2 * OS X and Netatalk interoperability VFS module for Samba-3.x
4 * Copyright (C) Ralph Boehme, 2013, 2014
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
21 #include "MacExtensions.h"
22 #include "smbd/smbd.h"
23 #include "system/filesys.h"
24 #include "lib/util/time.h"
25 #include "system/shmem.h"
26 #include "locking/proto.h"
27 #include "smbd/globals.h"
29 #include "libcli/security/security.h"
30 #include "../libcli/smb/smb2_create_ctx.h"
31 #include "lib/util/sys_rw.h"
32 #include "lib/util/tevent_ntstatus.h"
33 #include "lib/util/tevent_unix.h"
34 #include "offload_token.h"
35 #include "string_replace.h"
37 #include <gnutls/gnutls.h>
38 #include <gnutls/crypto.h>
41 * Enhanced OS X and Netatalk compatibility
42 * ========================================
44 * This modules takes advantage of vfs_streams_xattr and
45 * vfs_catia. VFS modules vfs_fruit and vfs_streams_xattr must be
46 * loaded in the correct order:
48 * vfs modules = catia fruit streams_xattr
50 * The module intercepts the OS X special streams "AFP_AfpInfo" and
51 * "AFP_Resource" and handles them in a special way. All other named
52 * streams are deferred to vfs_streams_xattr.
54 * The OS X client maps all NTFS illegal characters to the Unicode
55 * private range. This module optionally stores the charcters using
56 * their native ASCII encoding using vfs_catia. If you're not enabling
57 * this feature, you can skip catia from vfs modules.
59 * Finally, open modes are optionally checked against Netatalk AFP
62 * The "AFP_AfpInfo" named stream is a binary blob containing OS X
63 * extended metadata for files and directories. This module optionally
64 * reads and stores this metadata in a way compatible with Netatalk 3
65 * which stores the metadata in an EA "org.netatalk.metadata". Cf
66 * source3/include/MacExtensions.h for a description of the binary
69 * The "AFP_Resource" named stream may be arbitrarily large, thus it
70 * can't be stored in an xattr on most filesystem. ZFS on Solaris is
71 * the only available filesystem where xattrs can be of any size and
72 * the OS supports using the file APIs for xattrs.
74 * The AFP_Resource stream is stored in an AppleDouble file prepending
75 * "._" to the filename. On Solaris with ZFS the stream is optionally
76 * stored in an EA "org.netatalk.resource".
82 * The OS X SMB client sends xattrs as ADS too. For xattr interop with
83 * other protocols you may want to adjust the xattr names the VFS
84 * module vfs_streams_xattr uses for storing ADS's. This defaults to
85 * user.DosStream.ADS_NAME:$DATA and can be changed by specifying
86 * these module parameters:
88 * streams_xattr:prefix = user.
89 * streams_xattr:store_stream_type = false
95 * - log diagnostic if any needed VFS module is not loaded
96 * (eg with lp_vfs_objects())
100 static int vfs_fruit_debug_level = DBGC_VFS;
102 static struct global_fruit_config {
103 bool nego_aapl; /* client negotiated AAPL */
105 } global_fruit_config;
108 #define DBGC_CLASS vfs_fruit_debug_level
110 #define FRUIT_PARAM_TYPE_NAME "fruit"
111 #define ADOUBLE_NAME_PREFIX "._"
113 #define NETATALK_META_XATTR "org.netatalk.Metadata"
114 #define NETATALK_RSRC_XATTR "org.netatalk.ResourceFork"
116 #if defined(HAVE_ATTROPEN)
117 #define AFPINFO_EA_NETATALK NETATALK_META_XATTR
118 #define AFPRESOURCE_EA_NETATALK NETATALK_RSRC_XATTR
120 #define AFPINFO_EA_NETATALK "user." NETATALK_META_XATTR
121 #define AFPRESOURCE_EA_NETATALK "user." NETATALK_RSRC_XATTR
124 enum apple_fork {APPLE_FORK_DATA, APPLE_FORK_RSRC};
126 enum fruit_rsrc {FRUIT_RSRC_STREAM, FRUIT_RSRC_ADFILE, FRUIT_RSRC_XATTR};
127 enum fruit_meta {FRUIT_META_STREAM, FRUIT_META_NETATALK};
128 enum fruit_locking {FRUIT_LOCKING_NETATALK, FRUIT_LOCKING_NONE};
129 enum fruit_encoding {FRUIT_ENC_NATIVE, FRUIT_ENC_PRIVATE};
131 struct fruit_config_data {
132 enum fruit_rsrc rsrc;
133 enum fruit_meta meta;
134 enum fruit_locking locking;
135 enum fruit_encoding encoding;
136 bool use_aapl; /* config from smb.conf */
138 bool readdir_attr_enabled;
139 bool unix_info_enabled;
140 bool copyfile_enabled;
141 bool veto_appledouble;
143 bool aapl_zero_file_id;
146 off_t time_machine_max_size;
147 bool wipe_intentionally_left_blank_rfork;
148 bool delete_empty_adfiles;
151 * Additional options, all enabled by default,
152 * possibly useful for analyzing performance. The associated
153 * operations with each of them may be expensive, so having
154 * the chance to disable them individually gives a chance
155 * tweaking the setup for the particular usecase.
157 bool readdir_attr_rsize;
158 bool readdir_attr_finder_info;
159 bool readdir_attr_max_access;
162 static const struct enum_list fruit_rsrc[] = {
163 {FRUIT_RSRC_STREAM, "stream"}, /* pass on to vfs_streams_xattr */
164 {FRUIT_RSRC_ADFILE, "file"}, /* ._ AppleDouble file */
165 {FRUIT_RSRC_XATTR, "xattr"}, /* Netatalk compatible xattr (ZFS only) */
169 static const struct enum_list fruit_meta[] = {
170 {FRUIT_META_STREAM, "stream"}, /* pass on to vfs_streams_xattr */
171 {FRUIT_META_NETATALK, "netatalk"}, /* Netatalk compatible xattr */
175 static const struct enum_list fruit_locking[] = {
176 {FRUIT_LOCKING_NETATALK, "netatalk"}, /* synchronize locks with Netatalk */
177 {FRUIT_LOCKING_NONE, "none"},
181 static const struct enum_list fruit_encoding[] = {
182 {FRUIT_ENC_NATIVE, "native"}, /* map unicode private chars to ASCII */
183 {FRUIT_ENC_PRIVATE, "private"}, /* keep unicode private chars */
187 static const char *fruit_catia_maps =
188 "0x01:0xf001,0x02:0xf002,0x03:0xf003,0x04:0xf004,"
189 "0x05:0xf005,0x06:0xf006,0x07:0xf007,0x08:0xf008,"
190 "0x09:0xf009,0x0a:0xf00a,0x0b:0xf00b,0x0c:0xf00c,"
191 "0x0d:0xf00d,0x0e:0xf00e,0x0f:0xf00f,0x10:0xf010,"
192 "0x11:0xf011,0x12:0xf012,0x13:0xf013,0x14:0xf014,"
193 "0x15:0xf015,0x16:0xf016,0x17:0xf017,0x18:0xf018,"
194 "0x19:0xf019,0x1a:0xf01a,0x1b:0xf01b,0x1c:0xf01c,"
195 "0x1d:0xf01d,0x1e:0xf01e,0x1f:0xf01f,"
196 "0x22:0xf020,0x2a:0xf021,0x3a:0xf022,0x3c:0xf023,"
197 "0x3e:0xf024,0x3f:0xf025,0x5c:0xf026,0x7c:0xf027,"
200 /*****************************************************************************
201 * Defines, functions and data structures that deal with AppleDouble
202 *****************************************************************************/
205 * There are two AppleDouble blobs we deal with:
207 * - ADOUBLE_META - AppleDouble blob used by Netatalk for storing
208 * metadata in an xattr
210 * - ADOUBLE_RSRC - AppleDouble blob used by OS X and Netatalk in
213 typedef enum {ADOUBLE_META, ADOUBLE_RSRC} adouble_type_t;
216 #define AD_VERSION2 0x00020000
217 #define AD_VERSION AD_VERSION2
220 * AppleDouble entry IDs.
222 #define ADEID_DFORK 1
223 #define ADEID_RFORK 2
225 #define ADEID_COMMENT 4
226 #define ADEID_ICONBW 5
227 #define ADEID_ICONCOL 6
228 #define ADEID_FILEI 7
229 #define ADEID_FILEDATESI 8
230 #define ADEID_FINDERI 9
231 #define ADEID_MACFILEI 10
232 #define ADEID_PRODOSFILEI 11
233 #define ADEID_MSDOSFILEI 12
234 #define ADEID_SHORTNAME 13
235 #define ADEID_AFPFILEI 14
238 /* Private Netatalk entries */
239 #define ADEID_PRIVDEV 16
240 #define ADEID_PRIVINO 17
241 #define ADEID_PRIVSYN 18
242 #define ADEID_PRIVID 19
243 #define ADEID_MAX (ADEID_PRIVID + 1)
246 * These are the real ids for the private entries,
247 * as stored in the adouble file
249 #define AD_DEV 0x80444556
250 #define AD_INO 0x80494E4F
251 #define AD_SYN 0x8053594E
252 #define AD_ID 0x8053567E
254 /* Number of actually used entries */
255 #define ADEID_NUM_XATTR 8
256 #define ADEID_NUM_DOT_UND 2
257 #define ADEID_NUM_RSRC_XATTR 1
259 /* AppleDouble magic */
260 #define AD_APPLESINGLE_MAGIC 0x00051600
261 #define AD_APPLEDOUBLE_MAGIC 0x00051607
262 #define AD_MAGIC AD_APPLEDOUBLE_MAGIC
264 /* Sizes of relevant entry bits */
265 #define ADEDLEN_MAGIC 4
266 #define ADEDLEN_VERSION 4
267 #define ADEDLEN_FILLER 16
268 #define AD_FILLER_TAG "Netatalk " /* should be 16 bytes */
269 #define AD_FILLER_TAG_OSX "Mac OS X " /* should be 16 bytes */
270 #define ADEDLEN_NENTRIES 2
271 #define AD_HEADER_LEN (ADEDLEN_MAGIC + ADEDLEN_VERSION + \
272 ADEDLEN_FILLER + ADEDLEN_NENTRIES) /* 26 */
273 #define AD_ENTRY_LEN_EID 4
274 #define AD_ENTRY_LEN_OFF 4
275 #define AD_ENTRY_LEN_LEN 4
276 #define AD_ENTRY_LEN (AD_ENTRY_LEN_EID + AD_ENTRY_LEN_OFF + AD_ENTRY_LEN_LEN)
279 #define ADEDLEN_NAME 255
280 #define ADEDLEN_COMMENT 200
281 #define ADEDLEN_FILEI 16
282 #define ADEDLEN_FINDERI 32
283 #define ADEDLEN_FILEDATESI 16
284 #define ADEDLEN_SHORTNAME 12 /* length up to 8.3 */
285 #define ADEDLEN_AFPFILEI 4
286 #define ADEDLEN_MACFILEI 4
287 #define ADEDLEN_PRODOSFILEI 8
288 #define ADEDLEN_MSDOSFILEI 2
289 #define ADEDLEN_DID 4
290 #define ADEDLEN_PRIVDEV 8
291 #define ADEDLEN_PRIVINO 8
292 #define ADEDLEN_PRIVSYN 8
293 #define ADEDLEN_PRIVID 4
296 #define ADEDOFF_MAGIC 0
297 #define ADEDOFF_VERSION (ADEDOFF_MAGIC + ADEDLEN_MAGIC)
298 #define ADEDOFF_FILLER (ADEDOFF_VERSION + ADEDLEN_VERSION)
299 #define ADEDOFF_NENTRIES (ADEDOFF_FILLER + ADEDLEN_FILLER)
301 #define ADEDOFF_FINDERI_XATTR (AD_HEADER_LEN + \
302 (ADEID_NUM_XATTR * AD_ENTRY_LEN))
303 #define ADEDOFF_COMMENT_XATTR (ADEDOFF_FINDERI_XATTR + ADEDLEN_FINDERI)
304 #define ADEDOFF_FILEDATESI_XATTR (ADEDOFF_COMMENT_XATTR + ADEDLEN_COMMENT)
305 #define ADEDOFF_AFPFILEI_XATTR (ADEDOFF_FILEDATESI_XATTR + \
307 #define ADEDOFF_PRIVDEV_XATTR (ADEDOFF_AFPFILEI_XATTR + ADEDLEN_AFPFILEI)
308 #define ADEDOFF_PRIVINO_XATTR (ADEDOFF_PRIVDEV_XATTR + ADEDLEN_PRIVDEV)
309 #define ADEDOFF_PRIVSYN_XATTR (ADEDOFF_PRIVINO_XATTR + ADEDLEN_PRIVINO)
310 #define ADEDOFF_PRIVID_XATTR (ADEDOFF_PRIVSYN_XATTR + ADEDLEN_PRIVSYN)
312 #define ADEDOFF_FINDERI_DOT_UND (AD_HEADER_LEN + \
313 (ADEID_NUM_DOT_UND * AD_ENTRY_LEN))
314 #define ADEDOFF_RFORK_DOT_UND (ADEDOFF_FINDERI_DOT_UND + ADEDLEN_FINDERI)
316 #define AD_DATASZ_XATTR (AD_HEADER_LEN + \
317 (ADEID_NUM_XATTR * AD_ENTRY_LEN) + \
318 ADEDLEN_FINDERI + ADEDLEN_COMMENT + \
319 ADEDLEN_FILEDATESI + ADEDLEN_AFPFILEI + \
320 ADEDLEN_PRIVDEV + ADEDLEN_PRIVINO + \
321 ADEDLEN_PRIVSYN + ADEDLEN_PRIVID)
323 #if AD_DATASZ_XATTR != 402
324 #error bad size for AD_DATASZ_XATTR
327 #define AD_DATASZ_DOT_UND (AD_HEADER_LEN + \
328 (ADEID_NUM_DOT_UND * AD_ENTRY_LEN) + \
330 #if AD_DATASZ_DOT_UND != 82
331 #error bad size for AD_DATASZ_DOT_UND
335 * Sharemode locks fcntl() offsets
337 #if _FILE_OFFSET_BITS == 64 || defined(HAVE_LARGEFILE)
338 #define AD_FILELOCK_BASE (UINT64_C(0x7FFFFFFFFFFFFFFF) - 9)
340 #define AD_FILELOCK_BASE (UINT32_C(0x7FFFFFFF) - 9)
342 #define BYTELOCK_MAX (AD_FILELOCK_BASE - 1)
344 #define AD_FILELOCK_OPEN_WR (AD_FILELOCK_BASE + 0)
345 #define AD_FILELOCK_OPEN_RD (AD_FILELOCK_BASE + 1)
346 #define AD_FILELOCK_RSRC_OPEN_WR (AD_FILELOCK_BASE + 2)
347 #define AD_FILELOCK_RSRC_OPEN_RD (AD_FILELOCK_BASE + 3)
348 #define AD_FILELOCK_DENY_WR (AD_FILELOCK_BASE + 4)
349 #define AD_FILELOCK_DENY_RD (AD_FILELOCK_BASE + 5)
350 #define AD_FILELOCK_RSRC_DENY_WR (AD_FILELOCK_BASE + 6)
351 #define AD_FILELOCK_RSRC_DENY_RD (AD_FILELOCK_BASE + 7)
352 #define AD_FILELOCK_OPEN_NONE (AD_FILELOCK_BASE + 8)
353 #define AD_FILELOCK_RSRC_OPEN_NONE (AD_FILELOCK_BASE + 9)
355 /* Time stuff we overload the bits a little */
356 #define AD_DATE_CREATE 0
357 #define AD_DATE_MODIFY 4
358 #define AD_DATE_BACKUP 8
359 #define AD_DATE_ACCESS 12
360 #define AD_DATE_MASK (AD_DATE_CREATE | AD_DATE_MODIFY | \
361 AD_DATE_BACKUP | AD_DATE_ACCESS)
362 #define AD_DATE_UNIX (1 << 10)
363 #define AD_DATE_START 0x80000000
364 #define AD_DATE_DELTA 946684800
365 #define AD_DATE_FROM_UNIX(x) (htonl((x) - AD_DATE_DELTA))
366 #define AD_DATE_TO_UNIX(x) (ntohl(x) + AD_DATE_DELTA)
368 #define AD_XATTR_HDR_MAGIC 0x41545452 /* 'ATTR' */
369 #define AD_XATTR_MAX_ENTRIES 1024 /* Some arbitrarily enforced limit */
370 #define AD_XATTR_HDR_SIZE 36
371 #define AD_XATTR_MAX_HDR_SIZE 65536
373 /* Accessor macros */
374 #define ad_getentrylen(ad,eid) ((ad)->ad_eid[(eid)].ade_len)
375 #define ad_getentryoff(ad,eid) ((ad)->ad_eid[(eid)].ade_off)
376 #define ad_setentrylen(ad,eid,len) ((ad)->ad_eid[(eid)].ade_len = (len))
377 #define ad_setentryoff(ad,eid,off) ((ad)->ad_eid[(eid)].ade_off = (off))
380 * Both struct ad_xattr_header and struct ad_xattr_entry describe the in memory
381 * representation as well as the on-disk format.
383 * The ad_xattr_header follows the FinderInfo data in the FinderInfo entry if
384 * the length of the FinderInfo entry is larger then 32 bytes. It is then
385 * preceeded with 2 bytes padding.
387 * Cf: https://opensource.apple.com/source/xnu/xnu-4570.1.46/bsd/vfs/vfs_xattr.c
390 struct ad_xattr_header {
391 uint32_t adx_magic; /* ATTR_HDR_MAGIC */
392 uint32_t adx_debug_tag; /* for debugging == file id of owning file */
393 uint32_t adx_total_size; /* file offset of end of attribute header + entries + data */
394 uint32_t adx_data_start; /* file offset to attribute data area */
395 uint32_t adx_data_length; /* length of attribute data area */
396 uint32_t adx_reserved[3];
398 uint16_t adx_num_attrs;
401 /* On-disk entries are aligned on 4 byte boundaries */
402 struct ad_xattr_entry {
403 uint32_t adx_offset; /* file offset to data */
404 uint32_t adx_length; /* size of attribute data */
406 uint8_t adx_namelen; /* included the NULL terminator */
407 char *adx_name; /* NULL-terminated UTF-8 name */
416 files_struct *ad_fsp;
418 adouble_type_t ad_type;
421 uint8_t ad_filler[ADEDLEN_FILLER];
422 struct ad_entry ad_eid[ADEID_MAX];
424 struct ad_xattr_header adx_header;
425 struct ad_xattr_entry *adx_entries;
428 struct ad_entry_order {
429 uint32_t id, offset, len;
432 /* Netatalk AppleDouble metadata xattr */
434 struct ad_entry_order entry_order_meta_xattr[ADEID_NUM_XATTR + 1] = {
435 {ADEID_FINDERI, ADEDOFF_FINDERI_XATTR, ADEDLEN_FINDERI},
436 {ADEID_COMMENT, ADEDOFF_COMMENT_XATTR, 0},
437 {ADEID_FILEDATESI, ADEDOFF_FILEDATESI_XATTR, ADEDLEN_FILEDATESI},
438 {ADEID_AFPFILEI, ADEDOFF_AFPFILEI_XATTR, ADEDLEN_AFPFILEI},
439 {ADEID_PRIVDEV, ADEDOFF_PRIVDEV_XATTR, 0},
440 {ADEID_PRIVINO, ADEDOFF_PRIVINO_XATTR, 0},
441 {ADEID_PRIVSYN, ADEDOFF_PRIVSYN_XATTR, 0},
442 {ADEID_PRIVID, ADEDOFF_PRIVID_XATTR, 0},
446 /* AppleDouble resource fork file (the ones prefixed by "._") */
448 struct ad_entry_order entry_order_dot_und[ADEID_NUM_DOT_UND + 1] = {
449 {ADEID_FINDERI, ADEDOFF_FINDERI_DOT_UND, ADEDLEN_FINDERI},
450 {ADEID_RFORK, ADEDOFF_RFORK_DOT_UND, 0},
454 /* Conversion from enumerated id to on-disk AppleDouble id */
455 #define AD_EID_DISK(a) (set_eid[a])
456 static const uint32_t set_eid[] = {
457 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
458 AD_DEV, AD_INO, AD_SYN, AD_ID
461 static char empty_resourcefork[] = {
462 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
463 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
464 0x54, 0x68, 0x69, 0x73, 0x20, 0x72, 0x65, 0x73,
465 0x6F, 0x75, 0x72, 0x63, 0x65, 0x20, 0x66, 0x6F,
466 0x72, 0x6B, 0x20, 0x69, 0x6E, 0x74, 0x65, 0x6E,
467 0x74, 0x69, 0x6F, 0x6E, 0x61, 0x6C, 0x6C, 0x79,
468 0x20, 0x6C, 0x65, 0x66, 0x74, 0x20, 0x62, 0x6C,
469 0x61, 0x6E, 0x6B, 0x20, 0x20, 0x20, 0x00, 0x00,
470 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
471 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
472 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
473 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
474 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
475 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
476 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
477 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
478 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
479 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
480 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
481 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
482 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
483 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
484 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
485 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
486 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
487 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
488 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
489 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
490 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
491 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
492 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
493 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
494 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
495 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
496 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
497 0x00, 0x1C, 0x00, 0x1E, 0xFF, 0xFF
501 /* tcon config handle */
502 struct fruit_config_data *config;
504 /* Denote stream type, meta or rsrc */
507 /* Whether the create created the stream */
511 * AFP_AfpInfo stream created, but not written yet, thus still a fake
512 * pipe fd. This is set to true in fruit_open_meta if there was no
513 * exisiting stream but the caller requested O_CREAT. It is later set to
514 * false when we get a write on the stream that then does open and
523 * Forward declarations
525 static struct adouble *ad_init(TALLOC_CTX *ctx,
526 adouble_type_t type);
527 static struct adouble *ad_get(TALLOC_CTX *ctx,
528 vfs_handle_struct *handle,
529 const struct smb_filename *smb_fname,
530 adouble_type_t type);
531 static int ad_set(vfs_handle_struct *handle,
533 const struct smb_filename *smb_fname);
534 static int ad_fset(struct vfs_handle_struct *handle,
537 static int adouble_path(TALLOC_CTX *ctx,
538 const struct smb_filename *smb_fname__in,
539 struct smb_filename **ppsmb_fname_out);
540 static AfpInfo *afpinfo_new(TALLOC_CTX *ctx);
541 static ssize_t afpinfo_pack(const AfpInfo *ai, char *buf);
542 static AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data);
546 * Return a pointer to an AppleDouble entry
548 * Returns NULL if the entry is not present
550 static char *ad_get_entry(const struct adouble *ad, int eid)
552 off_t off = ad_getentryoff(ad, eid);
553 size_t len = ad_getentrylen(ad, eid);
555 if (off == 0 || len == 0) {
559 return ad->ad_data + off;
565 static int ad_getdate(const struct adouble *ad,
566 unsigned int dateoff,
569 bool xlate = (dateoff & AD_DATE_UNIX);
572 dateoff &= AD_DATE_MASK;
573 p = ad_get_entry(ad, ADEID_FILEDATESI);
578 if (dateoff > AD_DATE_ACCESS) {
582 memcpy(date, p + dateoff, sizeof(uint32_t));
585 *date = AD_DATE_TO_UNIX(*date);
593 static int ad_setdate(struct adouble *ad, unsigned int dateoff, uint32_t date)
595 bool xlate = (dateoff & AD_DATE_UNIX);
598 p = ad_get_entry(ad, ADEID_FILEDATESI);
603 dateoff &= AD_DATE_MASK;
605 date = AD_DATE_FROM_UNIX(date);
608 if (dateoff > AD_DATE_ACCESS) {
612 memcpy(p + dateoff, &date, sizeof(date));
619 * Map on-disk AppleDouble id to enumerated id
621 static uint32_t get_eid(uint32_t eid)
629 return ADEID_PRIVDEV;
631 return ADEID_PRIVINO;
633 return ADEID_PRIVSYN;
644 * Pack AppleDouble structure into data buffer
646 static bool ad_pack(struct adouble *ad)
653 bufsize = talloc_get_size(ad->ad_data);
654 if (bufsize < AD_DATASZ_DOT_UND) {
655 DBG_ERR("bad buffer size [0x%" PRIx32 "]\n", bufsize);
659 if (offset + ADEDLEN_MAGIC < offset ||
660 offset + ADEDLEN_MAGIC >= bufsize) {
663 RSIVAL(ad->ad_data, offset, ad->ad_magic);
664 offset += ADEDLEN_MAGIC;
666 if (offset + ADEDLEN_VERSION < offset ||
667 offset + ADEDLEN_VERSION >= bufsize) {
670 RSIVAL(ad->ad_data, offset, ad->ad_version);
671 offset += ADEDLEN_VERSION;
673 if (offset + ADEDLEN_FILLER < offset ||
674 offset + ADEDLEN_FILLER >= bufsize) {
677 if (ad->ad_type == ADOUBLE_RSRC) {
678 memcpy(ad->ad_data + offset, AD_FILLER_TAG, ADEDLEN_FILLER);
680 offset += ADEDLEN_FILLER;
682 if (offset + ADEDLEN_NENTRIES < offset ||
683 offset + ADEDLEN_NENTRIES >= bufsize) {
686 offset += ADEDLEN_NENTRIES;
688 for (eid = 0, nent = 0; eid < ADEID_MAX; eid++) {
689 if (ad->ad_eid[eid].ade_off == 0) {
691 * ade_off is also used as indicator whether a
692 * specific entry is used or not
697 if (offset + AD_ENTRY_LEN_EID < offset ||
698 offset + AD_ENTRY_LEN_EID >= bufsize) {
701 RSIVAL(ad->ad_data, offset, AD_EID_DISK(eid));
702 offset += AD_ENTRY_LEN_EID;
704 if (offset + AD_ENTRY_LEN_OFF < offset ||
705 offset + AD_ENTRY_LEN_OFF >= bufsize) {
708 RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_off);
709 offset += AD_ENTRY_LEN_OFF;
711 if (offset + AD_ENTRY_LEN_LEN < offset ||
712 offset + AD_ENTRY_LEN_LEN >= bufsize) {
715 RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_len);
716 offset += AD_ENTRY_LEN_LEN;
721 if (ADEDOFF_NENTRIES + 2 >= bufsize) {
724 RSSVAL(ad->ad_data, ADEDOFF_NENTRIES, nent);
729 static bool ad_unpack_xattrs(struct adouble *ad)
731 struct ad_xattr_header *h = &ad->adx_header;
732 const char *p = ad->ad_data;
736 if (ad_getentrylen(ad, ADEID_FINDERI) <= ADEDLEN_FINDERI) {
740 /* 2 bytes padding */
741 hoff = ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI + 2;
743 h->adx_magic = RIVAL(p, hoff + 0);
744 h->adx_debug_tag = RIVAL(p, hoff + 4); /* Not used -> not checked */
745 h->adx_total_size = RIVAL(p, hoff + 8);
746 h->adx_data_start = RIVAL(p, hoff + 12);
747 h->adx_data_length = RIVAL(p, hoff + 16);
748 h->adx_flags = RSVAL(p, hoff + 32); /* Not used -> not checked */
749 h->adx_num_attrs = RSVAL(p, hoff + 34);
751 if (h->adx_magic != AD_XATTR_HDR_MAGIC) {
752 DBG_ERR("Bad magic: 0x%" PRIx32 "\n", h->adx_magic);
756 if (h->adx_total_size > ad_getentryoff(ad, ADEID_RFORK)) {
757 DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size);
760 if (h->adx_total_size > AD_XATTR_MAX_HDR_SIZE) {
761 DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size);
765 if (h->adx_data_start < (hoff + AD_XATTR_HDR_SIZE)) {
766 DBG_ERR("Bad start: 0x%" PRIx32 "\n", h->adx_data_start);
770 if ((h->adx_data_start + h->adx_data_length) < h->adx_data_start) {
771 DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length);
774 if ((h->adx_data_start + h->adx_data_length) >
775 ad->adx_header.adx_total_size)
777 DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length);
781 if (h->adx_num_attrs > AD_XATTR_MAX_ENTRIES) {
782 DBG_ERR("Bad num xattrs: %" PRIu16 "\n", h->adx_num_attrs);
786 if (h->adx_num_attrs == 0) {
790 ad->adx_entries = talloc_zero_array(
791 ad, struct ad_xattr_entry, h->adx_num_attrs);
792 if (ad->adx_entries == NULL) {
796 hoff += AD_XATTR_HDR_SIZE;
798 for (i = 0; i < h->adx_num_attrs; i++) {
799 struct ad_xattr_entry *e = &ad->adx_entries[i];
801 hoff = (hoff + 3) & ~3;
803 e->adx_offset = RIVAL(p, hoff + 0);
804 e->adx_length = RIVAL(p, hoff + 4);
805 e->adx_flags = RSVAL(p, hoff + 8);
806 e->adx_namelen = *(p + hoff + 10);
808 if (e->adx_offset >= ad->adx_header.adx_total_size) {
809 DBG_ERR("Bad adx_offset: %" PRIx32 "\n",
814 if ((e->adx_offset + e->adx_length) < e->adx_offset) {
815 DBG_ERR("Bad adx_length: %" PRIx32 "\n",
820 if ((e->adx_offset + e->adx_length) >
821 ad->adx_header.adx_total_size)
823 DBG_ERR("Bad adx_length: %" PRIx32 "\n",
828 if (e->adx_namelen == 0) {
829 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
833 if ((hoff + 11 + e->adx_namelen) < hoff + 11) {
834 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
838 if ((hoff + 11 + e->adx_namelen) >
839 ad->adx_header.adx_data_start)
841 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
846 e->adx_name = talloc_strndup(ad->adx_entries,
849 if (e->adx_name == NULL) {
853 DBG_DEBUG("xattr [%s] offset [0x%x] size [0x%x]\n",
854 e->adx_name, e->adx_offset, e->adx_length);
855 dump_data(10, (uint8_t *)(ad->ad_data + e->adx_offset),
858 hoff += 11 + e->adx_namelen;
865 * Unpack an AppleDouble blob into a struct adoble
867 static bool ad_unpack(struct adouble *ad, const size_t nentries,
870 size_t bufsize = talloc_get_size(ad->ad_data);
872 uint32_t eid, len, off;
876 * The size of the buffer ad->ad_data is checked when read, so
877 * we wouldn't have to check our own offsets, a few extra
878 * checks won't hurt though. We have to check the offsets we
879 * read from the buffer anyway.
882 if (bufsize < (AD_HEADER_LEN + (AD_ENTRY_LEN * nentries))) {
883 DEBUG(1, ("bad size\n"));
887 ad->ad_magic = RIVAL(ad->ad_data, 0);
888 ad->ad_version = RIVAL(ad->ad_data, ADEDOFF_VERSION);
889 if ((ad->ad_magic != AD_MAGIC) || (ad->ad_version != AD_VERSION)) {
890 DEBUG(1, ("wrong magic or version\n"));
894 memcpy(ad->ad_filler, ad->ad_data + ADEDOFF_FILLER, ADEDLEN_FILLER);
896 adentries = RSVAL(ad->ad_data, ADEDOFF_NENTRIES);
897 if (adentries != nentries) {
898 DEBUG(1, ("invalid number of entries: %zu\n",
903 /* now, read in the entry bits */
904 for (i = 0; i < adentries; i++) {
905 eid = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN));
907 off = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 4);
908 len = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 8);
910 if (!eid || eid >= ADEID_MAX) {
911 DEBUG(1, ("bogus eid %d\n", eid));
916 * All entries other than the resource fork are
917 * expected to be read into the ad_data buffer, so
918 * ensure the specified offset is within that bound
920 if ((off > bufsize) && (eid != ADEID_RFORK)) {
921 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
927 * All entries besides FinderInfo and resource fork
928 * must fit into the buffer. FinderInfo is special as
929 * it may be larger then the default 32 bytes (if it
930 * contains marshalled xattrs), but we will fixup that
931 * in ad_convert(). And the resource fork is never
932 * accessed directly by the ad_data buf (also see
933 * comment above) anyway.
935 if ((eid != ADEID_RFORK) &&
936 (eid != ADEID_FINDERI) &&
937 ((off + len) > bufsize)) {
938 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
944 * That would be obviously broken
946 if (off > filesize) {
947 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
953 * Check for any entry that has its end beyond the
956 if (off + len < off) {
957 DEBUG(1, ("offset wrap in eid %d: off: %" PRIu32
958 ", len: %" PRIu32 "\n",
963 if (off + len > filesize) {
965 * If this is the resource fork entry, we fix
966 * up the length, for any other entry we bail
969 if (eid != ADEID_RFORK) {
970 DEBUG(1, ("bogus eid %d: off: %" PRIu32
971 ", len: %" PRIu32 "\n",
977 * Fixup the resource fork entry by limiting
978 * the size to entryoffset - filesize.
980 len = filesize - off;
981 DEBUG(1, ("Limiting ADEID_RFORK: off: %" PRIu32
982 ", len: %" PRIu32 "\n", off, len));
985 ad->ad_eid[eid].ade_off = off;
986 ad->ad_eid[eid].ade_len = len;
989 ok = ad_unpack_xattrs(ad);
997 static bool ad_convert_move_reso(vfs_handle_struct *handle,
999 const struct smb_filename *smb_fname)
1008 rforklen = ad_getentrylen(ad, ADEID_RFORK);
1009 if (rforklen == 0) {
1013 buf = talloc_size(ad, rforklen);
1016 * This allocates a buffer for reading the resource fork data in
1017 * one big swoop. Resource forks won't be larger then, say, 64
1018 * MB, I swear, so just doing the allocation with the talloc
1019 * limit as safeguard seems safe.
1021 DBG_ERR("Failed to allocate %zu bytes for rfork\n",
1026 rforkoff = ad_getentryoff(ad, ADEID_RFORK);
1028 n = SMB_VFS_PREAD(ad->ad_fsp, buf, rforklen, rforkoff);
1029 if (n != rforklen) {
1030 DBG_ERR("Reading %zu bytes from rfork [%s] failed: %s\n",
1031 rforklen, fsp_str_dbg(ad->ad_fsp), strerror(errno));
1035 rforkoff = ADEDOFF_RFORK_DOT_UND;
1037 n = SMB_VFS_PWRITE(ad->ad_fsp, buf, rforklen, rforkoff);
1038 if (n != rforklen) {
1039 DBG_ERR("Writing %zu bytes to rfork [%s] failed: %s\n",
1040 rforklen, fsp_str_dbg(ad->ad_fsp), strerror(errno));
1044 ad_setentryoff(ad, ADEID_RFORK, ADEDOFF_RFORK_DOT_UND);
1047 DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name);
1051 ret = ad_fset(handle, ad, ad->ad_fsp);
1053 DBG_ERR("ad_fset on [%s] failed\n", fsp_str_dbg(ad->ad_fsp));
1060 static bool ad_convert_xattr(vfs_handle_struct *handle,
1062 const struct smb_filename *smb_fname,
1063 bool *converted_xattr)
1065 static struct char_mappings **string_replace_cmaps = NULL;
1067 int saved_errno = 0;
1072 *converted_xattr = false;
1074 if (ad_getentrylen(ad, ADEID_FINDERI) == ADEDLEN_FINDERI) {
1078 if (string_replace_cmaps == NULL) {
1079 const char **mappings = NULL;
1081 mappings = str_list_make_v3_const(
1082 talloc_tos(), fruit_catia_maps, NULL);
1083 if (mappings == NULL) {
1086 string_replace_cmaps = string_replace_init_map(mappings);
1087 TALLOC_FREE(mappings);
1090 for (i = 0; i < ad->adx_header.adx_num_attrs; i++) {
1091 struct ad_xattr_entry *e = &ad->adx_entries[i];
1092 char *mapped_name = NULL;
1094 struct smb_filename *stream_name = NULL;
1095 files_struct *fsp = NULL;
1098 status = string_replace_allocate(handle->conn,
1100 string_replace_cmaps,
1103 vfs_translate_to_windows);
1104 if (!NT_STATUS_IS_OK(status) &&
1105 !NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))
1107 DBG_ERR("string_replace_allocate failed\n");
1113 mapped_name = talloc_asprintf(talloc_tos(), ":%s", tmp);
1115 if (mapped_name == NULL) {
1120 stream_name = synthetic_smb_fname(talloc_tos(),
1121 smb_fname->base_name,
1125 TALLOC_FREE(mapped_name);
1126 if (stream_name == NULL) {
1127 DBG_ERR("synthetic_smb_fname failed\n");
1132 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1134 status = SMB_VFS_CREATE_FILE(
1135 handle->conn, /* conn */
1137 0, /* root_dir_fid */
1138 stream_name, /* fname */
1139 FILE_GENERIC_WRITE, /* access_mask */
1140 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
1141 FILE_OPEN_IF, /* create_disposition */
1142 0, /* create_options */
1143 0, /* file_attributes */
1144 INTERNAL_OPEN_ONLY, /* oplock_request */
1146 0, /* allocation_size */
1147 0, /* private_flags */
1152 NULL, NULL); /* create context */
1153 TALLOC_FREE(stream_name);
1154 if (!NT_STATUS_IS_OK(status)) {
1155 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1160 nwritten = SMB_VFS_PWRITE(fsp,
1161 ad->ad_data + e->adx_offset,
1164 if (nwritten == -1) {
1165 DBG_ERR("SMB_VFS_PWRITE failed\n");
1166 saved_errno = errno;
1167 close_file(NULL, fsp, ERROR_CLOSE);
1168 errno = saved_errno;
1173 status = close_file(NULL, fsp, NORMAL_CLOSE);
1174 if (!NT_STATUS_IS_OK(status)) {
1181 ad_setentrylen(ad, ADEID_FINDERI, ADEDLEN_FINDERI);
1185 DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name);
1189 rc = ad_fset(handle, ad, ad->ad_fsp);
1191 DBG_ERR("ad_fset on [%s] failed: %s\n",
1192 fsp_str_dbg(ad->ad_fsp), strerror(errno));
1197 ok = ad_convert_move_reso(handle, ad, smb_fname);
1202 *converted_xattr = true;
1209 static bool ad_convert_finderinfo(vfs_handle_struct *handle,
1211 const struct smb_filename *smb_fname)
1216 struct smb_filename *stream_name = NULL;
1217 files_struct *fsp = NULL;
1221 int saved_errno = 0;
1224 cmp = memcmp(ad->ad_filler, AD_FILLER_TAG_OSX, ADEDLEN_FILLER);
1229 p_ad = ad_get_entry(ad, ADEID_FINDERI);
1234 ai = afpinfo_new(talloc_tos());
1239 memcpy(ai->afpi_FinderInfo, p_ad, ADEDLEN_FINDERI);
1241 aiblob = data_blob_talloc(talloc_tos(), NULL, AFP_INFO_SIZE);
1242 if (aiblob.data == NULL) {
1247 size = afpinfo_pack(ai, (char *)aiblob.data);
1249 if (size != AFP_INFO_SIZE) {
1253 stream_name = synthetic_smb_fname(talloc_tos(),
1254 smb_fname->base_name,
1258 if (stream_name == NULL) {
1259 data_blob_free(&aiblob);
1260 DBG_ERR("synthetic_smb_fname failed\n");
1264 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1266 status = SMB_VFS_CREATE_FILE(
1267 handle->conn, /* conn */
1269 0, /* root_dir_fid */
1270 stream_name, /* fname */
1271 FILE_GENERIC_WRITE, /* access_mask */
1272 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
1273 FILE_OPEN_IF, /* create_disposition */
1274 0, /* create_options */
1275 0, /* file_attributes */
1276 INTERNAL_OPEN_ONLY, /* oplock_request */
1278 0, /* allocation_size */
1279 0, /* private_flags */
1284 NULL, NULL); /* create context */
1285 TALLOC_FREE(stream_name);
1286 if (!NT_STATUS_IS_OK(status)) {
1287 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1291 nwritten = SMB_VFS_PWRITE(fsp,
1295 if (nwritten == -1) {
1296 DBG_ERR("SMB_VFS_PWRITE failed\n");
1297 saved_errno = errno;
1298 close_file(NULL, fsp, ERROR_CLOSE);
1299 errno = saved_errno;
1303 status = close_file(NULL, fsp, NORMAL_CLOSE);
1304 if (!NT_STATUS_IS_OK(status)) {
1312 static bool ad_convert_truncate(vfs_handle_struct *handle,
1314 const struct smb_filename *smb_fname)
1319 newlen = ADEDOFF_RFORK_DOT_UND + ad_getentrylen(ad, ADEID_RFORK);
1321 rc = SMB_VFS_FTRUNCATE(ad->ad_fsp, newlen);
1329 static bool ad_convert_blank_rfork(vfs_handle_struct *handle,
1333 struct fruit_config_data *config = NULL;
1334 size_t rforklen = sizeof(empty_resourcefork);
1343 SMB_VFS_HANDLE_GET_DATA(handle, config,
1344 struct fruit_config_data, return false);
1346 if (!config->wipe_intentionally_left_blank_rfork) {
1350 if (ad_getentrylen(ad, ADEID_RFORK) != rforklen) {
1354 nread = SMB_VFS_PREAD(ad->ad_fsp, buf, rforklen, ADEDOFF_RFORK_DOT_UND);
1355 if (nread != rforklen) {
1356 DBG_ERR("Reading %zu bytes from rfork [%s] failed: %s\n",
1357 rforklen, fsp_str_dbg(ad->ad_fsp), strerror(errno));
1361 cmp = memcmp(buf, empty_resourcefork, rforklen);
1366 ad_setentrylen(ad, ADEID_RFORK, 0);
1372 rc = ad_fset(handle, ad, ad->ad_fsp);
1374 DBG_ERR("ad_fset on [%s] failed\n", fsp_str_dbg(ad->ad_fsp));
1382 static bool ad_convert_delete_adfile(vfs_handle_struct *handle,
1384 const struct smb_filename *smb_fname)
1386 struct fruit_config_data *config = NULL;
1387 struct smb_filename *ad_name = NULL;
1390 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
1394 SMB_VFS_HANDLE_GET_DATA(handle, config,
1395 struct fruit_config_data, return false);
1397 if (!config->delete_empty_adfiles) {
1401 rc = adouble_path(talloc_tos(), smb_fname, &ad_name);
1406 rc = SMB_VFS_NEXT_UNLINK(handle, ad_name);
1408 DBG_ERR("Unlinking [%s] failed: %s\n",
1409 smb_fname_str_dbg(ad_name), strerror(errno));
1410 TALLOC_FREE(ad_name);
1414 DBG_WARNING("Unlinked [%s] after conversion\n", smb_fname_str_dbg(ad_name));
1415 TALLOC_FREE(ad_name);
1421 * Convert from Apple's ._ file to Netatalk
1423 * Apple's AppleDouble may contain a FinderInfo entry longer then 32
1424 * bytes containing packed xattrs.
1426 * @return -1 in case an error occurred, 0 if no conversion was done, 1
1429 static int ad_convert(struct vfs_handle_struct *handle,
1430 const struct smb_filename *smb_fname)
1432 struct adouble *ad = NULL;
1434 bool converted_xattr = false;
1438 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
1443 ok = ad_convert_xattr(handle, ad, smb_fname, &converted_xattr);
1449 ok = ad_convert_blank_rfork(handle, ad, &blank);
1455 if (converted_xattr || blank) {
1456 ok = ad_convert_truncate(handle, ad, smb_fname);
1463 ok = ad_convert_finderinfo(handle, ad, smb_fname);
1465 DBG_ERR("Failed to convert [%s]\n",
1466 smb_fname_str_dbg(smb_fname));
1471 ok = ad_convert_delete_adfile(handle, ad, smb_fname);
1484 * Read and parse Netatalk AppleDouble metadata xattr
1486 static ssize_t ad_read_meta(vfs_handle_struct *handle,
1488 const struct smb_filename *smb_fname)
1494 DEBUG(10, ("reading meta xattr for %s\n", smb_fname->base_name));
1496 ealen = SMB_VFS_GETXATTR(handle->conn, smb_fname,
1497 AFPINFO_EA_NETATALK, ad->ad_data,
1503 if (errno == ENOATTR) {
1509 DEBUG(2, ("error reading meta xattr: %s\n",
1515 if (ealen != AD_DATASZ_XATTR) {
1516 DEBUG(2, ("bad size %zd\n", ealen));
1522 /* Now parse entries */
1523 ok = ad_unpack(ad, ADEID_NUM_XATTR, AD_DATASZ_XATTR);
1525 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1531 if (!ad_getentryoff(ad, ADEID_FINDERI)
1532 || !ad_getentryoff(ad, ADEID_COMMENT)
1533 || !ad_getentryoff(ad, ADEID_FILEDATESI)
1534 || !ad_getentryoff(ad, ADEID_AFPFILEI)
1535 || !ad_getentryoff(ad, ADEID_PRIVDEV)
1536 || !ad_getentryoff(ad, ADEID_PRIVINO)
1537 || !ad_getentryoff(ad, ADEID_PRIVSYN)
1538 || !ad_getentryoff(ad, ADEID_PRIVID)) {
1539 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1546 DEBUG(10, ("reading meta xattr for %s, rc: %d\n",
1547 smb_fname->base_name, rc));
1551 if (errno == EINVAL) {
1553 (void)SMB_VFS_REMOVEXATTR(handle->conn,
1555 AFPINFO_EA_NETATALK);
1563 static int ad_open_rsrc(vfs_handle_struct *handle,
1564 const struct smb_filename *smb_fname,
1567 files_struct **_fsp)
1570 struct smb_filename *adp_smb_fname = NULL;
1571 files_struct *fsp = NULL;
1572 uint32_t access_mask;
1573 uint32_t share_access;
1574 uint32_t create_disposition;
1577 ret = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
1582 ret = SMB_VFS_STAT(handle->conn, adp_smb_fname);
1584 TALLOC_FREE(adp_smb_fname);
1588 access_mask = FILE_GENERIC_READ;
1589 share_access = FILE_SHARE_READ | FILE_SHARE_WRITE;
1590 create_disposition = FILE_OPEN;
1592 if (flags & O_RDWR) {
1593 access_mask |= FILE_GENERIC_WRITE;
1594 share_access &= ~FILE_SHARE_WRITE;
1597 status = SMB_VFS_CREATE_FILE(
1598 handle->conn, /* conn */
1600 0, /* root_dir_fid */
1605 0, /* create_options */
1606 0, /* file_attributes */
1607 INTERNAL_OPEN_ONLY, /* oplock_request */
1609 0, /* allocation_size */
1610 0, /* private_flags */
1615 NULL, NULL); /* create context */
1616 TALLOC_FREE(adp_smb_fname);
1617 if (!NT_STATUS_IS_OK(status)) {
1618 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1627 * Here's the deal: for ADOUBLE_META we can do without an fd as we can issue
1628 * path based xattr calls. For ADOUBLE_RSRC however we need a full-fledged fd
1629 * for file IO on the ._ file.
1631 static int ad_open(vfs_handle_struct *handle,
1634 const struct smb_filename *smb_fname,
1640 DBG_DEBUG("Path [%s] type [%s]\n", smb_fname->base_name,
1641 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
1643 if (ad->ad_type == ADOUBLE_META) {
1649 ad->ad_opened = false;
1653 ret = ad_open_rsrc(handle, smb_fname, flags, mode, &ad->ad_fsp);
1657 ad->ad_opened = true;
1659 DBG_DEBUG("Path [%s] type [%s]\n",
1660 smb_fname->base_name,
1661 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
1666 static ssize_t ad_read_rsrc_adouble(vfs_handle_struct *handle,
1668 const struct smb_filename *smb_fname)
1670 SMB_STRUCT_STAT sbuf;
1677 ret = sys_fstat(ad->ad_fsp->fh->fd, &sbuf, lp_fake_directory_create_times(
1678 SNUM(handle->conn)));
1684 * AppleDouble file header content and size, two cases:
1686 * - without xattrs it is exactly AD_DATASZ_DOT_UND (82) bytes large
1687 * - with embedded xattrs it can be larger, up to AD_XATTR_MAX_HDR_SIZE
1689 * Read as much as we can up to AD_XATTR_MAX_HDR_SIZE.
1691 size = sbuf.st_ex_size;
1692 if (size > talloc_array_length(ad->ad_data)) {
1693 if (size > AD_XATTR_MAX_HDR_SIZE) {
1694 size = AD_XATTR_MAX_HDR_SIZE;
1696 p_ad = talloc_realloc(ad, ad->ad_data, char, size);
1703 len = sys_pread(ad->ad_fsp->fh->fd, ad->ad_data,
1704 talloc_array_length(ad->ad_data), 0);
1705 if (len != talloc_array_length(ad->ad_data)) {
1706 DBG_NOTICE("%s %s: bad size: %zd\n",
1707 smb_fname->base_name, strerror(errno), len);
1711 /* Now parse entries */
1712 ok = ad_unpack(ad, ADEID_NUM_DOT_UND, sbuf.st_ex_size);
1714 DBG_ERR("invalid AppleDouble resource %s\n",
1715 smb_fname->base_name);
1720 if ((ad_getentryoff(ad, ADEID_FINDERI) != ADEDOFF_FINDERI_DOT_UND)
1721 || (ad_getentrylen(ad, ADEID_FINDERI) < ADEDLEN_FINDERI)
1722 || (ad_getentryoff(ad, ADEID_RFORK) < ADEDOFF_RFORK_DOT_UND)) {
1723 DBG_ERR("invalid AppleDouble resource %s\n",
1724 smb_fname->base_name);
1733 * Read and parse resource fork, either ._ AppleDouble file or xattr
1735 static ssize_t ad_read_rsrc(vfs_handle_struct *handle,
1737 const struct smb_filename *smb_fname)
1739 return ad_read_rsrc_adouble(handle, ad, smb_fname);
1743 * Read and unpack an AppleDouble metadata xattr or resource
1745 static ssize_t ad_read(vfs_handle_struct *handle,
1747 const struct smb_filename *smb_fname)
1749 switch (ad->ad_type) {
1751 return ad_read_meta(handle, ad, smb_fname);
1753 return ad_read_rsrc(handle, ad, smb_fname);
1759 static int adouble_destructor(struct adouble *ad)
1763 if (!ad->ad_opened) {
1767 SMB_ASSERT(ad->ad_fsp != NULL);
1769 status = close_file(NULL, ad->ad_fsp, NORMAL_CLOSE);
1770 if (!NT_STATUS_IS_OK(status)) {
1771 DBG_ERR("Closing [%s] failed: %s\n",
1772 fsp_str_dbg(ad->ad_fsp), nt_errstr(status));
1779 * Allocate a struct adouble without initialiing it
1781 * The struct is either hang of the fsp extension context or if fsp is
1784 * @param[in] ctx talloc context
1785 * @param[in] handle vfs handle
1786 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1788 * @return adouble handle
1790 static struct adouble *ad_alloc(TALLOC_CTX *ctx,
1791 adouble_type_t type)
1799 adsize = AD_DATASZ_XATTR;
1802 adsize = AD_DATASZ_DOT_UND;
1808 ad = talloc_zero(ctx, struct adouble);
1815 ad->ad_data = talloc_zero_array(ad, char, adsize);
1816 if (ad->ad_data == NULL) {
1823 ad->ad_magic = AD_MAGIC;
1824 ad->ad_version = AD_VERSION;
1826 talloc_set_destructor(ad, adouble_destructor);
1836 * Allocate and initialize a new struct adouble
1838 * @param[in] ctx talloc context
1839 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1841 * @return adouble handle, initialized
1843 static struct adouble *ad_init(TALLOC_CTX *ctx,
1844 adouble_type_t type)
1847 const struct ad_entry_order *eid;
1848 struct adouble *ad = NULL;
1849 time_t t = time(NULL);
1853 eid = entry_order_meta_xattr;
1856 eid = entry_order_dot_und;
1862 ad = ad_alloc(ctx, type);
1868 ad->ad_eid[eid->id].ade_off = eid->offset;
1869 ad->ad_eid[eid->id].ade_len = eid->len;
1873 /* put something sane in the date fields */
1874 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, t);
1875 ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, t);
1876 ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, t);
1877 ad_setdate(ad, AD_DATE_BACKUP, htonl(AD_DATE_START));
1885 static struct adouble *ad_get_internal(TALLOC_CTX *ctx,
1886 vfs_handle_struct *handle,
1888 const struct smb_filename *smb_fname,
1889 adouble_type_t type)
1893 struct adouble *ad = NULL;
1897 smb_fname = fsp->base_fsp->fsp_name;
1900 DEBUG(10, ("ad_get(%s) called for %s\n",
1901 type == ADOUBLE_META ? "meta" : "rsrc",
1902 smb_fname->base_name));
1904 ad = ad_alloc(ctx, type);
1910 /* Try rw first so we can use the fd in ad_convert() */
1913 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
1914 if (rc == -1 && ((errno == EROFS) || (errno == EACCES))) {
1916 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
1919 DBG_DEBUG("ad_open [%s] error [%s]\n",
1920 smb_fname->base_name, strerror(errno));
1925 len = ad_read(handle, ad, smb_fname);
1927 DEBUG(10, ("error reading AppleDouble for %s\n",
1928 smb_fname->base_name));
1934 DEBUG(10, ("ad_get(%s) for %s returning %d\n",
1935 type == ADOUBLE_META ? "meta" : "rsrc",
1936 smb_fname->base_name, rc));
1945 * Return AppleDouble data for a file
1947 * @param[in] ctx talloc context
1948 * @param[in] handle vfs handle
1949 * @param[in] smb_fname pathname to file or directory
1950 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1952 * @return talloced struct adouble or NULL on error
1954 static struct adouble *ad_get(TALLOC_CTX *ctx,
1955 vfs_handle_struct *handle,
1956 const struct smb_filename *smb_fname,
1957 adouble_type_t type)
1959 return ad_get_internal(ctx, handle, NULL, smb_fname, type);
1963 * Return AppleDouble data for a file
1965 * @param[in] ctx talloc context
1966 * @param[in] handle vfs handle
1967 * @param[in] fsp fsp to use for IO
1968 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1970 * @return talloced struct adouble or NULL on error
1972 static struct adouble *ad_fget(TALLOC_CTX *ctx, vfs_handle_struct *handle,
1973 files_struct *fsp, adouble_type_t type)
1975 return ad_get_internal(ctx, handle, fsp, NULL, type);
1979 * Set AppleDouble metadata on a file or directory
1981 * @param[in] ad adouble handle
1983 * @param[in] smb_fname pathname to file or directory
1985 * @return status code, 0 means success
1987 static int ad_set(vfs_handle_struct *handle,
1989 const struct smb_filename *smb_fname)
1994 DBG_DEBUG("Path [%s]\n", smb_fname->base_name);
1996 if (ad->ad_type != ADOUBLE_META) {
1997 DBG_ERR("ad_set on [%s] used with ADOUBLE_RSRC\n",
1998 smb_fname->base_name);
2007 ret = SMB_VFS_SETXATTR(handle->conn,
2009 AFPINFO_EA_NETATALK,
2011 AD_DATASZ_XATTR, 0);
2013 DBG_DEBUG("Path [%s] ret [%d]\n", smb_fname->base_name, ret);
2019 * Set AppleDouble metadata on a file or directory
2021 * @param[in] ad adouble handle
2022 * @param[in] fsp file handle
2024 * @return status code, 0 means success
2026 static int ad_fset(struct vfs_handle_struct *handle,
2034 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
2037 || (fsp->fh == NULL)
2038 || (fsp->fh->fd == -1))
2040 smb_panic("bad fsp");
2048 switch (ad->ad_type) {
2050 rc = SMB_VFS_NEXT_SETXATTR(handle,
2052 AFPINFO_EA_NETATALK,
2054 AD_DATASZ_XATTR, 0);
2058 len = SMB_VFS_NEXT_PWRITE(handle,
2063 if (len != AD_DATASZ_DOT_UND) {
2064 DBG_ERR("short write on %s: %zd", fsp_str_dbg(fsp), len);
2074 DBG_DEBUG("Path [%s] rc [%d]\n", fsp_str_dbg(fsp), rc);
2079 /*****************************************************************************
2081 *****************************************************************************/
2083 static bool is_afpinfo_stream(const struct smb_filename *smb_fname)
2085 if (strncasecmp_m(smb_fname->stream_name,
2086 AFPINFO_STREAM_NAME,
2087 strlen(AFPINFO_STREAM_NAME)) == 0) {
2093 static bool is_afpresource_stream(const struct smb_filename *smb_fname)
2095 if (strncasecmp_m(smb_fname->stream_name,
2096 AFPRESOURCE_STREAM_NAME,
2097 strlen(AFPRESOURCE_STREAM_NAME)) == 0) {
2104 * Test whether stream is an Apple stream.
2106 static bool is_apple_stream(const struct smb_filename *smb_fname)
2108 if (is_afpinfo_stream(smb_fname)) {
2111 if (is_afpresource_stream(smb_fname)) {
2117 static bool is_adouble_file(const char *path)
2119 const char *p = NULL;
2122 p = strrchr(path, '/');
2130 ADOUBLE_NAME_PREFIX,
2131 strlen(ADOUBLE_NAME_PREFIX));
2139 * Initialize config struct from our smb.conf config parameters
2141 static int init_fruit_config(vfs_handle_struct *handle)
2143 struct fruit_config_data *config;
2145 const char *tm_size_str = NULL;
2147 config = talloc_zero(handle->conn, struct fruit_config_data);
2149 DEBUG(1, ("talloc_zero() failed\n"));
2155 * Versions up to Samba 4.5.x had a spelling bug in the
2156 * fruit:resource option calling lp_parm_enum with
2157 * "res*s*ource" (ie two s).
2159 * In Samba 4.6 we accept both the wrong and the correct
2160 * spelling, in Samba 4.7 the bad spelling will be removed.
2162 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2163 "ressource", fruit_rsrc, FRUIT_RSRC_ADFILE);
2164 if (enumval == -1) {
2165 DEBUG(1, ("value for %s: resource type unknown\n",
2166 FRUIT_PARAM_TYPE_NAME));
2169 config->rsrc = (enum fruit_rsrc)enumval;
2171 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2172 "resource", fruit_rsrc, enumval);
2173 if (enumval == -1) {
2174 DEBUG(1, ("value for %s: resource type unknown\n",
2175 FRUIT_PARAM_TYPE_NAME));
2178 config->rsrc = (enum fruit_rsrc)enumval;
2180 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2181 "metadata", fruit_meta, FRUIT_META_NETATALK);
2182 if (enumval == -1) {
2183 DEBUG(1, ("value for %s: metadata type unknown\n",
2184 FRUIT_PARAM_TYPE_NAME));
2187 config->meta = (enum fruit_meta)enumval;
2189 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2190 "locking", fruit_locking, FRUIT_LOCKING_NONE);
2191 if (enumval == -1) {
2192 DEBUG(1, ("value for %s: locking type unknown\n",
2193 FRUIT_PARAM_TYPE_NAME));
2196 config->locking = (enum fruit_locking)enumval;
2198 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2199 "encoding", fruit_encoding, FRUIT_ENC_PRIVATE);
2200 if (enumval == -1) {
2201 DEBUG(1, ("value for %s: encoding type unknown\n",
2202 FRUIT_PARAM_TYPE_NAME));
2205 config->encoding = (enum fruit_encoding)enumval;
2207 if (config->rsrc == FRUIT_RSRC_ADFILE) {
2208 config->veto_appledouble = lp_parm_bool(SNUM(handle->conn),
2209 FRUIT_PARAM_TYPE_NAME,
2214 config->use_aapl = lp_parm_bool(
2215 -1, FRUIT_PARAM_TYPE_NAME, "aapl", true);
2217 config->time_machine = lp_parm_bool(
2218 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "time machine", false);
2220 config->unix_info_enabled = lp_parm_bool(
2221 -1, FRUIT_PARAM_TYPE_NAME, "nfs_aces", true);
2223 config->use_copyfile = lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME,
2226 config->posix_rename = lp_parm_bool(
2227 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "posix_rename", true);
2229 config->aapl_zero_file_id =
2230 lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME, "zero_file_id", true);
2232 config->readdir_attr_rsize = lp_parm_bool(
2233 SNUM(handle->conn), "readdir_attr", "aapl_rsize", true);
2235 config->readdir_attr_finder_info = lp_parm_bool(
2236 SNUM(handle->conn), "readdir_attr", "aapl_finder_info", true);
2238 config->readdir_attr_max_access = lp_parm_bool(
2239 SNUM(handle->conn), "readdir_attr", "aapl_max_access", true);
2241 config->model = lp_parm_const_string(
2242 -1, FRUIT_PARAM_TYPE_NAME, "model", "MacSamba");
2244 tm_size_str = lp_parm_const_string(
2245 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2246 "time machine max size", NULL);
2247 if (tm_size_str != NULL) {
2248 config->time_machine_max_size = conv_str_size(tm_size_str);
2251 config->wipe_intentionally_left_blank_rfork = lp_parm_bool(
2252 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2253 "wipe_intentionally_left_blank_rfork", false);
2255 config->delete_empty_adfiles = lp_parm_bool(
2256 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2257 "delete_empty_adfiles", false);
2259 SMB_VFS_HANDLE_SET_DATA(handle, config,
2260 NULL, struct fruit_config_data,
2267 * Prepend "._" to a basename
2268 * Return a new struct smb_filename with stream_name == NULL.
2270 static int adouble_path(TALLOC_CTX *ctx,
2271 const struct smb_filename *smb_fname_in,
2272 struct smb_filename **pp_smb_fname_out)
2276 struct smb_filename *smb_fname = cp_smb_filename(ctx,
2279 if (smb_fname == NULL) {
2283 /* We need streamname to be NULL */
2284 TALLOC_FREE(smb_fname->stream_name);
2286 /* And we're replacing base_name. */
2287 TALLOC_FREE(smb_fname->base_name);
2289 if (!parent_dirname(smb_fname, smb_fname_in->base_name,
2291 TALLOC_FREE(smb_fname);
2295 smb_fname->base_name = talloc_asprintf(smb_fname,
2296 "%s/._%s", parent, base);
2297 if (smb_fname->base_name == NULL) {
2298 TALLOC_FREE(smb_fname);
2302 *pp_smb_fname_out = smb_fname;
2308 * Allocate and initialize an AfpInfo struct
2310 static AfpInfo *afpinfo_new(TALLOC_CTX *ctx)
2312 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2316 ai->afpi_Signature = AFP_Signature;
2317 ai->afpi_Version = AFP_Version;
2318 ai->afpi_BackupTime = AD_DATE_START;
2323 * Pack an AfpInfo struct into a buffer
2325 * Buffer size must be at least AFP_INFO_SIZE
2326 * Returns size of packed buffer
2328 static ssize_t afpinfo_pack(const AfpInfo *ai, char *buf)
2330 memset(buf, 0, AFP_INFO_SIZE);
2332 RSIVAL(buf, 0, ai->afpi_Signature);
2333 RSIVAL(buf, 4, ai->afpi_Version);
2334 RSIVAL(buf, 12, ai->afpi_BackupTime);
2335 memcpy(buf + 16, ai->afpi_FinderInfo, sizeof(ai->afpi_FinderInfo));
2337 return AFP_INFO_SIZE;
2341 * Unpack a buffer into a AfpInfo structure
2343 * Buffer size must be at least AFP_INFO_SIZE
2344 * Returns allocated AfpInfo struct
2346 static AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data)
2348 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2353 ai->afpi_Signature = RIVAL(data, 0);
2354 ai->afpi_Version = RIVAL(data, 4);
2355 ai->afpi_BackupTime = RIVAL(data, 12);
2356 memcpy(ai->afpi_FinderInfo, (const char *)data + 16,
2357 sizeof(ai->afpi_FinderInfo));
2359 if (ai->afpi_Signature != AFP_Signature
2360 || ai->afpi_Version != AFP_Version) {
2361 DEBUG(1, ("Bad AfpInfo signature or version\n"));
2369 * Fake an inode number from the md5 hash of the (xattr) name
2371 static SMB_INO_T fruit_inode(const SMB_STRUCT_STAT *sbuf, const char *sname)
2373 gnutls_hash_hd_t hash_hnd = NULL;
2374 unsigned char hash[16];
2375 SMB_INO_T result = 0;
2379 DBG_DEBUG("fruit_inode called for %ju/%ju [%s]\n",
2380 (uintmax_t)sbuf->st_ex_dev,
2381 (uintmax_t)sbuf->st_ex_ino, sname);
2383 upper_sname = talloc_strdup_upper(talloc_tos(), sname);
2384 SMB_ASSERT(upper_sname != NULL);
2386 rc = gnutls_hash_init(&hash_hnd, GNUTLS_DIG_MD5);
2391 rc = gnutls_hash(hash_hnd, &(sbuf->st_ex_dev), sizeof(sbuf->st_ex_dev));
2393 gnutls_hash_deinit(hash_hnd, NULL);
2396 rc = gnutls_hash(hash_hnd,
2398 sizeof(sbuf->st_ex_ino));
2400 gnutls_hash_deinit(hash_hnd, NULL);
2403 rc = gnutls_hash(hash_hnd,
2405 talloc_get_size(upper_sname) - 1);
2407 gnutls_hash_deinit(hash_hnd, NULL);
2411 gnutls_hash_deinit(hash_hnd, hash);
2413 /* Hopefully all the variation is in the lower 4 (or 8) bytes! */
2414 memcpy(&result, hash, sizeof(result));
2417 DBG_DEBUG("fruit_inode \"%s\": ino=%ju\n",
2418 sname, (uintmax_t)result);
2421 TALLOC_FREE(upper_sname);
2426 static bool add_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
2427 struct stream_struct **streams,
2428 const char *name, off_t size,
2431 struct stream_struct *tmp;
2433 tmp = talloc_realloc(mem_ctx, *streams, struct stream_struct,
2439 tmp[*num_streams].name = talloc_asprintf(tmp, "%s:$DATA", name);
2440 if (tmp[*num_streams].name == NULL) {
2444 tmp[*num_streams].size = size;
2445 tmp[*num_streams].alloc_size = alloc_size;
2452 static bool filter_empty_rsrc_stream(unsigned int *num_streams,
2453 struct stream_struct **streams)
2455 struct stream_struct *tmp = *streams;
2458 if (*num_streams == 0) {
2462 for (i = 0; i < *num_streams; i++) {
2463 if (strequal_m(tmp[i].name, AFPRESOURCE_STREAM)) {
2468 if (i == *num_streams) {
2472 if (tmp[i].size > 0) {
2476 TALLOC_FREE(tmp[i].name);
2477 if (*num_streams - 1 > i) {
2478 memmove(&tmp[i], &tmp[i+1],
2479 (*num_streams - i - 1) * sizeof(struct stream_struct));
2486 static bool del_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
2487 struct stream_struct **streams,
2490 struct stream_struct *tmp = *streams;
2493 if (*num_streams == 0) {
2497 for (i = 0; i < *num_streams; i++) {
2498 if (strequal_m(tmp[i].name, name)) {
2503 if (i == *num_streams) {
2507 TALLOC_FREE(tmp[i].name);
2508 if (*num_streams - 1 > i) {
2509 memmove(&tmp[i], &tmp[i+1],
2510 (*num_streams - i - 1) * sizeof(struct stream_struct));
2517 static bool ad_empty_finderinfo(const struct adouble *ad)
2520 char emptybuf[ADEDLEN_FINDERI] = {0};
2523 fi = ad_get_entry(ad, ADEID_FINDERI);
2525 DBG_ERR("Missing FinderInfo in struct adouble [%p]\n", ad);
2529 cmp = memcmp(emptybuf, fi, ADEDLEN_FINDERI);
2533 static bool ai_empty_finderinfo(const AfpInfo *ai)
2536 char emptybuf[ADEDLEN_FINDERI] = {0};
2538 cmp = memcmp(emptybuf, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
2543 * Update btime with btime from Netatalk
2545 static void update_btime(vfs_handle_struct *handle,
2546 struct smb_filename *smb_fname)
2549 struct timespec creation_time = {0};
2551 struct fruit_config_data *config = NULL;
2553 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
2556 switch (config->meta) {
2557 case FRUIT_META_STREAM:
2559 case FRUIT_META_NETATALK:
2563 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
2567 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
2571 if (ad_getdate(ad, AD_DATE_UNIX | AD_DATE_CREATE, &t) != 0) {
2577 creation_time.tv_sec = convert_uint32_t_to_time_t(t);
2578 update_stat_ex_create_time(&smb_fname->st, creation_time);
2584 * Map an access mask to a Netatalk single byte byte range lock
2586 static off_t access_to_netatalk_brl(enum apple_fork fork_type,
2587 uint32_t access_mask)
2591 switch (access_mask) {
2592 case FILE_READ_DATA:
2593 offset = AD_FILELOCK_OPEN_RD;
2596 case FILE_WRITE_DATA:
2597 case FILE_APPEND_DATA:
2598 offset = AD_FILELOCK_OPEN_WR;
2602 offset = AD_FILELOCK_OPEN_NONE;
2606 if (fork_type == APPLE_FORK_RSRC) {
2607 if (offset == AD_FILELOCK_OPEN_NONE) {
2608 offset = AD_FILELOCK_RSRC_OPEN_NONE;
2618 * Map a deny mode to a Netatalk brl
2620 static off_t denymode_to_netatalk_brl(enum apple_fork fork_type,
2625 switch (deny_mode) {
2627 offset = AD_FILELOCK_DENY_RD;
2631 offset = AD_FILELOCK_DENY_WR;
2635 smb_panic("denymode_to_netatalk_brl: bad deny mode\n");
2638 if (fork_type == APPLE_FORK_RSRC) {
2646 * Call fcntl() with an exclusive F_GETLK request in order to
2647 * determine if there's an exisiting shared lock
2649 * @return true if the requested lock was found or any error occurred
2650 * false if the lock was not found
2652 static bool test_netatalk_lock(files_struct *fsp, off_t in_offset)
2655 off_t offset = in_offset;
2660 result = SMB_VFS_GETLOCK(fsp, &offset, &len, &type, &pid);
2661 if (result == false) {
2665 if (type != F_UNLCK) {
2672 static NTSTATUS fruit_check_access(vfs_handle_struct *handle,
2674 uint32_t access_mask,
2675 uint32_t share_mode)
2677 NTSTATUS status = NT_STATUS_OK;
2679 bool share_for_read = (share_mode & FILE_SHARE_READ);
2680 bool share_for_write = (share_mode & FILE_SHARE_WRITE);
2681 bool netatalk_already_open_for_reading = false;
2682 bool netatalk_already_open_for_writing = false;
2683 bool netatalk_already_open_with_deny_read = false;
2684 bool netatalk_already_open_with_deny_write = false;
2686 /* FIXME: hardcoded data fork, add resource fork */
2687 enum apple_fork fork_type = APPLE_FORK_DATA;
2689 DBG_DEBUG("fruit_check_access: %s, am: %s/%s, sm: 0x%x\n",
2691 access_mask & FILE_READ_DATA ? "READ" :"-",
2692 access_mask & FILE_WRITE_DATA ? "WRITE" : "-",
2695 if (fsp->fh->fd == -1) {
2696 return NT_STATUS_OK;
2699 /* Read NetATalk opens and deny modes on the file. */
2700 netatalk_already_open_for_reading = test_netatalk_lock(fsp,
2701 access_to_netatalk_brl(fork_type,
2704 netatalk_already_open_with_deny_read = test_netatalk_lock(fsp,
2705 denymode_to_netatalk_brl(fork_type,
2708 netatalk_already_open_for_writing = test_netatalk_lock(fsp,
2709 access_to_netatalk_brl(fork_type,
2712 netatalk_already_open_with_deny_write = test_netatalk_lock(fsp,
2713 denymode_to_netatalk_brl(fork_type,
2716 /* If there are any conflicts - sharing violation. */
2717 if ((access_mask & FILE_READ_DATA) &&
2718 netatalk_already_open_with_deny_read) {
2719 return NT_STATUS_SHARING_VIOLATION;
2722 if (!share_for_read &&
2723 netatalk_already_open_for_reading) {
2724 return NT_STATUS_SHARING_VIOLATION;
2727 if ((access_mask & FILE_WRITE_DATA) &&
2728 netatalk_already_open_with_deny_write) {
2729 return NT_STATUS_SHARING_VIOLATION;
2732 if (!share_for_write &&
2733 netatalk_already_open_for_writing) {
2734 return NT_STATUS_SHARING_VIOLATION;
2737 if (!(access_mask & FILE_READ_DATA)) {
2739 * Nothing we can do here, we need read access
2742 return NT_STATUS_OK;
2745 /* Set NetAtalk locks matching our access */
2746 if (access_mask & FILE_READ_DATA) {
2747 struct byte_range_lock *br_lck = NULL;
2749 off = access_to_netatalk_brl(fork_type, FILE_READ_DATA);
2751 handle->conn->sconn->msg_ctx, fsp,
2752 fsp->op->global->open_persistent_id, 1, off,
2753 READ_LOCK, POSIX_LOCK, false,
2756 TALLOC_FREE(br_lck);
2758 if (!NT_STATUS_IS_OK(status)) {
2763 if (!share_for_read) {
2764 struct byte_range_lock *br_lck = NULL;
2766 off = denymode_to_netatalk_brl(fork_type, DENY_READ);
2768 handle->conn->sconn->msg_ctx, fsp,
2769 fsp->op->global->open_persistent_id, 1, off,
2770 READ_LOCK, POSIX_LOCK, false,
2773 TALLOC_FREE(br_lck);
2775 if (!NT_STATUS_IS_OK(status)) {
2780 if (access_mask & FILE_WRITE_DATA) {
2781 struct byte_range_lock *br_lck = NULL;
2783 off = access_to_netatalk_brl(fork_type, FILE_WRITE_DATA);
2785 handle->conn->sconn->msg_ctx, fsp,
2786 fsp->op->global->open_persistent_id, 1, off,
2787 READ_LOCK, POSIX_LOCK, false,
2790 TALLOC_FREE(br_lck);
2792 if (!NT_STATUS_IS_OK(status)) {
2797 if (!share_for_write) {
2798 struct byte_range_lock *br_lck = NULL;
2800 off = denymode_to_netatalk_brl(fork_type, DENY_WRITE);
2802 handle->conn->sconn->msg_ctx, fsp,
2803 fsp->op->global->open_persistent_id, 1, off,
2804 READ_LOCK, POSIX_LOCK, false,
2807 TALLOC_FREE(br_lck);
2809 if (!NT_STATUS_IS_OK(status)) {
2814 return NT_STATUS_OK;
2817 static NTSTATUS check_aapl(vfs_handle_struct *handle,
2818 struct smb_request *req,
2819 const struct smb2_create_blobs *in_context_blobs,
2820 struct smb2_create_blobs *out_context_blobs)
2822 struct fruit_config_data *config;
2824 struct smb2_create_blob *aapl = NULL;
2828 DATA_BLOB blob = data_blob_talloc(req, NULL, 0);
2829 uint64_t req_bitmap, client_caps;
2830 uint64_t server_caps = SMB2_CRTCTX_AAPL_UNIX_BASED;
2834 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
2835 return NT_STATUS_UNSUCCESSFUL);
2837 if (!config->use_aapl
2838 || in_context_blobs == NULL
2839 || out_context_blobs == NULL) {
2840 return NT_STATUS_OK;
2843 aapl = smb2_create_blob_find(in_context_blobs,
2844 SMB2_CREATE_TAG_AAPL);
2846 return NT_STATUS_OK;
2849 if (aapl->data.length != 24) {
2850 DEBUG(1, ("unexpected AAPL ctxt length: %ju\n",
2851 (uintmax_t)aapl->data.length));
2852 return NT_STATUS_INVALID_PARAMETER;
2855 cmd = IVAL(aapl->data.data, 0);
2856 if (cmd != SMB2_CRTCTX_AAPL_SERVER_QUERY) {
2857 DEBUG(1, ("unsupported AAPL cmd: %d\n", cmd));
2858 return NT_STATUS_INVALID_PARAMETER;
2861 req_bitmap = BVAL(aapl->data.data, 8);
2862 client_caps = BVAL(aapl->data.data, 16);
2864 SIVAL(p, 0, SMB2_CRTCTX_AAPL_SERVER_QUERY);
2866 SBVAL(p, 8, req_bitmap);
2867 ok = data_blob_append(req, &blob, p, 16);
2869 return NT_STATUS_UNSUCCESSFUL;
2872 if (req_bitmap & SMB2_CRTCTX_AAPL_SERVER_CAPS) {
2873 if ((client_caps & SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR) &&
2874 (handle->conn->tcon->compat->fs_capabilities & FILE_NAMED_STREAMS)) {
2875 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR;
2876 config->readdir_attr_enabled = true;
2879 if (config->use_copyfile) {
2880 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_OSX_COPYFILE;
2881 config->copyfile_enabled = true;
2885 * The client doesn't set the flag, so we can't check
2886 * for it and just set it unconditionally
2888 if (config->unix_info_enabled) {
2889 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_NFS_ACE;
2892 SBVAL(p, 0, server_caps);
2893 ok = data_blob_append(req, &blob, p, 8);
2895 return NT_STATUS_UNSUCCESSFUL;
2899 if (req_bitmap & SMB2_CRTCTX_AAPL_VOLUME_CAPS) {
2900 int val = lp_case_sensitive(SNUM(handle->conn->tcon->compat));
2908 caps |= SMB2_CRTCTX_AAPL_CASE_SENSITIVE;
2915 if (config->time_machine) {
2916 caps |= SMB2_CRTCTX_AAPL_FULL_SYNC;
2921 ok = data_blob_append(req, &blob, p, 8);
2923 return NT_STATUS_UNSUCCESSFUL;
2927 if (req_bitmap & SMB2_CRTCTX_AAPL_MODEL_INFO) {
2928 ok = convert_string_talloc(req,
2929 CH_UNIX, CH_UTF16LE,
2930 config->model, strlen(config->model),
2933 return NT_STATUS_UNSUCCESSFUL;
2937 SIVAL(p + 4, 0, modellen);
2938 ok = data_blob_append(req, &blob, p, 8);
2941 return NT_STATUS_UNSUCCESSFUL;
2944 ok = data_blob_append(req, &blob, model, modellen);
2947 return NT_STATUS_UNSUCCESSFUL;
2951 status = smb2_create_blob_add(out_context_blobs,
2953 SMB2_CREATE_TAG_AAPL,
2955 if (NT_STATUS_IS_OK(status)) {
2956 global_fruit_config.nego_aapl = true;
2957 if (config->aapl_zero_file_id) {
2958 aapl_force_zero_file_id(handle->conn->sconn);
2965 static bool readdir_attr_meta_finderi_stream(
2966 struct vfs_handle_struct *handle,
2967 const struct smb_filename *smb_fname,
2970 struct smb_filename *stream_name = NULL;
2971 files_struct *fsp = NULL;
2976 uint8_t buf[AFP_INFO_SIZE];
2978 stream_name = synthetic_smb_fname(talloc_tos(),
2979 smb_fname->base_name,
2980 AFPINFO_STREAM_NAME,
2981 NULL, smb_fname->flags);
2982 if (stream_name == NULL) {
2986 ret = SMB_VFS_STAT(handle->conn, stream_name);
2991 status = SMB_VFS_CREATE_FILE(
2992 handle->conn, /* conn */
2994 0, /* root_dir_fid */
2995 stream_name, /* fname */
2996 FILE_READ_DATA, /* access_mask */
2997 (FILE_SHARE_READ | FILE_SHARE_WRITE | /* share_access */
2999 FILE_OPEN, /* create_disposition*/
3000 0, /* create_options */
3001 0, /* file_attributes */
3002 INTERNAL_OPEN_ONLY, /* oplock_request */
3004 0, /* allocation_size */
3005 0, /* private_flags */
3010 NULL, NULL); /* create context */
3012 TALLOC_FREE(stream_name);
3014 if (!NT_STATUS_IS_OK(status)) {
3018 nread = SMB_VFS_PREAD(fsp, &buf[0], AFP_INFO_SIZE, 0);
3019 if (nread != AFP_INFO_SIZE) {
3020 DBG_ERR("short read [%s] [%zd/%d]\n",
3021 smb_fname_str_dbg(stream_name), nread, AFP_INFO_SIZE);
3026 memcpy(&ai->afpi_FinderInfo[0], &buf[AFP_OFF_FinderInfo],
3033 close_file(NULL, fsp, NORMAL_CLOSE);
3039 static bool readdir_attr_meta_finderi_netatalk(
3040 struct vfs_handle_struct *handle,
3041 const struct smb_filename *smb_fname,
3044 struct adouble *ad = NULL;
3047 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
3052 p = ad_get_entry(ad, ADEID_FINDERI);
3054 DBG_ERR("No ADEID_FINDERI for [%s]\n", smb_fname->base_name);
3059 memcpy(&ai->afpi_FinderInfo[0], p, AFP_FinderSize);
3064 static bool readdir_attr_meta_finderi(struct vfs_handle_struct *handle,
3065 const struct smb_filename *smb_fname,
3066 struct readdir_attr_data *attr_data)
3068 struct fruit_config_data *config = NULL;
3069 uint32_t date_added;
3073 SMB_VFS_HANDLE_GET_DATA(handle, config,
3074 struct fruit_config_data,
3077 switch (config->meta) {
3078 case FRUIT_META_NETATALK:
3079 ok = readdir_attr_meta_finderi_netatalk(
3080 handle, smb_fname, &ai);
3083 case FRUIT_META_STREAM:
3084 ok = readdir_attr_meta_finderi_stream(
3085 handle, smb_fname, &ai);
3089 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3094 /* Don't bother with errors, it's likely ENOENT */
3098 if (S_ISREG(smb_fname->st.st_ex_mode)) {
3100 memcpy(&attr_data->attr_data.aapl.finder_info[0],
3101 &ai.afpi_FinderInfo[0], 4);
3103 /* finder_creator */
3104 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 4,
3105 &ai.afpi_FinderInfo[4], 4);
3109 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 8,
3110 &ai.afpi_FinderInfo[8], 2);
3112 /* finder_ext_flags */
3113 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 10,
3114 &ai.afpi_FinderInfo[24], 2);
3117 date_added = convert_time_t_to_uint32_t(
3118 smb_fname->st.st_ex_btime.tv_sec - AD_DATE_DELTA);
3120 RSIVAL(&attr_data->attr_data.aapl.finder_info[0], 12, date_added);
3125 static uint64_t readdir_attr_rfork_size_adouble(
3126 struct vfs_handle_struct *handle,
3127 const struct smb_filename *smb_fname)
3129 struct adouble *ad = NULL;
3130 uint64_t rfork_size;
3132 ad = ad_get(talloc_tos(), handle, smb_fname,
3138 rfork_size = ad_getentrylen(ad, ADEID_RFORK);
3144 static uint64_t readdir_attr_rfork_size_stream(
3145 struct vfs_handle_struct *handle,
3146 const struct smb_filename *smb_fname)
3148 struct smb_filename *stream_name = NULL;
3150 uint64_t rfork_size;
3152 stream_name = synthetic_smb_fname(talloc_tos(),
3153 smb_fname->base_name,
3154 AFPRESOURCE_STREAM_NAME,
3156 if (stream_name == NULL) {
3160 ret = SMB_VFS_STAT(handle->conn, stream_name);
3162 TALLOC_FREE(stream_name);
3166 rfork_size = stream_name->st.st_ex_size;
3167 TALLOC_FREE(stream_name);
3172 static uint64_t readdir_attr_rfork_size(struct vfs_handle_struct *handle,
3173 const struct smb_filename *smb_fname)
3175 struct fruit_config_data *config = NULL;
3176 uint64_t rfork_size;
3178 SMB_VFS_HANDLE_GET_DATA(handle, config,
3179 struct fruit_config_data,
3182 switch (config->rsrc) {
3183 case FRUIT_RSRC_ADFILE:
3184 rfork_size = readdir_attr_rfork_size_adouble(handle,
3188 case FRUIT_RSRC_XATTR:
3189 case FRUIT_RSRC_STREAM:
3190 rfork_size = readdir_attr_rfork_size_stream(handle,
3195 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3203 static NTSTATUS readdir_attr_macmeta(struct vfs_handle_struct *handle,
3204 const struct smb_filename *smb_fname,
3205 struct readdir_attr_data *attr_data)
3207 NTSTATUS status = NT_STATUS_OK;
3208 struct fruit_config_data *config = NULL;
3211 SMB_VFS_HANDLE_GET_DATA(handle, config,
3212 struct fruit_config_data,
3213 return NT_STATUS_UNSUCCESSFUL);
3216 /* Ensure we return a default value in the creation_date field */
3217 RSIVAL(&attr_data->attr_data.aapl.finder_info, 12, AD_DATE_START);
3220 * Resource fork length
3223 if (config->readdir_attr_rsize) {
3224 uint64_t rfork_size;
3226 rfork_size = readdir_attr_rfork_size(handle, smb_fname);
3227 attr_data->attr_data.aapl.rfork_size = rfork_size;
3234 if (config->readdir_attr_finder_info) {
3235 ok = readdir_attr_meta_finderi(handle, smb_fname, attr_data);
3237 status = NT_STATUS_INTERNAL_ERROR;
3244 static NTSTATUS remove_virtual_nfs_aces(struct security_descriptor *psd)
3249 if (psd->dacl == NULL) {
3250 return NT_STATUS_OK;
3253 for (i = 0; i < psd->dacl->num_aces; i++) {
3254 /* MS NFS style mode/uid/gid */
3255 int cmp = dom_sid_compare_domain(
3256 &global_sid_Unix_NFS,
3257 &psd->dacl->aces[i].trustee);
3259 /* Normal ACE entry. */
3264 * security_descriptor_dacl_del()
3265 * *must* return NT_STATUS_OK as we know
3266 * we have something to remove.
3269 status = security_descriptor_dacl_del(psd,
3270 &psd->dacl->aces[i].trustee);
3271 if (!NT_STATUS_IS_OK(status)) {
3272 DBG_WARNING("failed to remove MS NFS style ACE: %s\n",
3278 * security_descriptor_dacl_del() may delete more
3279 * then one entry subsequent to this one if the
3280 * SID matches, but we only need to ensure that
3281 * we stay looking at the same element in the array.
3285 return NT_STATUS_OK;
3288 /* Search MS NFS style ACE with UNIX mode */
3289 static NTSTATUS check_ms_nfs(vfs_handle_struct *handle,
3291 struct security_descriptor *psd,
3296 struct fruit_config_data *config = NULL;
3300 SMB_VFS_HANDLE_GET_DATA(handle, config,
3301 struct fruit_config_data,
3302 return NT_STATUS_UNSUCCESSFUL);
3304 if (!global_fruit_config.nego_aapl) {
3305 return NT_STATUS_OK;
3307 if (psd->dacl == NULL || !config->unix_info_enabled) {
3308 return NT_STATUS_OK;
3311 for (i = 0; i < psd->dacl->num_aces; i++) {
3312 if (dom_sid_compare_domain(
3313 &global_sid_Unix_NFS_Mode,
3314 &psd->dacl->aces[i].trustee) == 0) {
3315 *pmode = (mode_t)psd->dacl->aces[i].trustee.sub_auths[2];
3316 *pmode &= (S_IRWXU | S_IRWXG | S_IRWXO);
3319 DEBUG(10, ("MS NFS chmod request %s, %04o\n",
3320 fsp_str_dbg(fsp), (unsigned)(*pmode)));
3326 * Remove any incoming virtual ACE entries generated by
3327 * fruit_fget_nt_acl().
3330 return remove_virtual_nfs_aces(psd);
3333 /****************************************************************************
3335 ****************************************************************************/
3337 static int fruit_connect(vfs_handle_struct *handle,
3338 const char *service,
3342 char *list = NULL, *newlist = NULL;
3343 struct fruit_config_data *config;
3345 DEBUG(10, ("fruit_connect\n"));
3347 rc = SMB_VFS_NEXT_CONNECT(handle, service, user);
3352 rc = init_fruit_config(handle);
3357 SMB_VFS_HANDLE_GET_DATA(handle, config,
3358 struct fruit_config_data, return -1);
3360 if (config->veto_appledouble) {
3361 list = lp_veto_files(talloc_tos(), SNUM(handle->conn));
3364 if (strstr(list, "/" ADOUBLE_NAME_PREFIX "*/") == NULL) {
3365 newlist = talloc_asprintf(
3367 "%s/" ADOUBLE_NAME_PREFIX "*/",
3369 lp_do_parameter(SNUM(handle->conn),
3374 lp_do_parameter(SNUM(handle->conn),
3376 "/" ADOUBLE_NAME_PREFIX "*/");
3382 if (config->encoding == FRUIT_ENC_NATIVE) {
3383 lp_do_parameter(SNUM(handle->conn),
3388 if (config->time_machine) {
3389 DBG_NOTICE("Enabling durable handles for Time Machine "
3390 "support on [%s]\n", service);
3391 lp_do_parameter(SNUM(handle->conn), "durable handles", "yes");
3392 lp_do_parameter(SNUM(handle->conn), "kernel oplocks", "no");
3393 lp_do_parameter(SNUM(handle->conn), "kernel share modes", "no");
3394 if (!lp_strict_sync(SNUM(handle->conn))) {
3395 DBG_WARNING("Time Machine without strict sync is not "
3398 lp_do_parameter(SNUM(handle->conn), "posix locking", "no");
3404 static int fruit_fake_fd(void)
3411 * Return a valid fd, but ensure any attempt to use it returns
3412 * an error (EPIPE). Once we get a write on the handle, we open
3415 ret = pipe(pipe_fds);
3425 static int fruit_open_meta_stream(vfs_handle_struct *handle,
3426 struct smb_filename *smb_fname,
3431 struct fruit_config_data *config = NULL;
3432 struct fio *fio = NULL;
3433 int open_flags = flags & ~O_CREAT;
3436 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3438 SMB_VFS_HANDLE_GET_DATA(handle, config,
3439 struct fruit_config_data, return -1);
3441 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3442 fio->type = ADOUBLE_META;
3443 fio->config = config;
3445 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, open_flags, mode);
3450 if (!(flags & O_CREAT)) {
3451 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
3455 fd = fruit_fake_fd();
3457 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
3461 fio->fake_fd = true;
3468 static int fruit_open_meta_netatalk(vfs_handle_struct *handle,
3469 struct smb_filename *smb_fname,
3474 struct fruit_config_data *config = NULL;
3475 struct fio *fio = NULL;
3476 struct adouble *ad = NULL;
3477 bool meta_exists = false;
3480 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3482 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
3489 if (!meta_exists && !(flags & O_CREAT)) {
3494 fd = fruit_fake_fd();
3499 SMB_VFS_HANDLE_GET_DATA(handle, config,
3500 struct fruit_config_data, return -1);
3502 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3503 fio->type = ADOUBLE_META;
3504 fio->config = config;
3505 fio->fake_fd = true;
3512 static int fruit_open_meta(vfs_handle_struct *handle,
3513 struct smb_filename *smb_fname,
3514 files_struct *fsp, int flags, mode_t mode)
3517 struct fruit_config_data *config = NULL;
3519 DBG_DEBUG("path [%s]\n", smb_fname_str_dbg(smb_fname));
3521 SMB_VFS_HANDLE_GET_DATA(handle, config,
3522 struct fruit_config_data, return -1);
3524 switch (config->meta) {
3525 case FRUIT_META_STREAM:
3526 fd = fruit_open_meta_stream(handle, smb_fname,
3530 case FRUIT_META_NETATALK:
3531 fd = fruit_open_meta_netatalk(handle, smb_fname,
3536 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3540 DBG_DEBUG("path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3545 static int fruit_open_rsrc_adouble(vfs_handle_struct *handle,
3546 struct smb_filename *smb_fname,
3552 struct adouble *ad = NULL;
3553 struct smb_filename *smb_fname_base = NULL;
3554 struct fruit_config_data *config = NULL;
3557 SMB_VFS_HANDLE_GET_DATA(handle, config,
3558 struct fruit_config_data, return -1);
3560 if ((!(flags & O_CREAT)) &&
3561 S_ISDIR(fsp->base_fsp->fsp_name->st.st_ex_mode))
3563 /* sorry, but directories don't habe a resource fork */
3568 rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_base);
3573 /* We always need read/write access for the metadata header too */
3574 flags &= ~(O_RDONLY | O_WRONLY);
3577 hostfd = SMB_VFS_NEXT_OPEN(handle, smb_fname_base, fsp,
3584 if (flags & (O_CREAT | O_TRUNC)) {
3585 ad = ad_init(fsp, ADOUBLE_RSRC);
3591 fsp->fh->fd = hostfd;
3593 rc = ad_fset(handle, ad, fsp);
3604 TALLOC_FREE(smb_fname_base);
3606 DEBUG(10, ("fruit_open resource fork: rc=%d, fd=%d\n", rc, hostfd));
3608 int saved_errno = errno;
3611 * BUGBUGBUG -- we would need to call
3612 * fd_close_posix here, but we don't have a
3615 fsp->fh->fd = hostfd;
3619 errno = saved_errno;
3624 static int fruit_open_rsrc_xattr(vfs_handle_struct *handle,
3625 struct smb_filename *smb_fname,
3630 #ifdef HAVE_ATTROPEN
3633 fd = attropen(smb_fname->base_name,
3634 AFPRESOURCE_EA_NETATALK,
3649 static int fruit_open_rsrc(vfs_handle_struct *handle,
3650 struct smb_filename *smb_fname,
3651 files_struct *fsp, int flags, mode_t mode)
3654 struct fruit_config_data *config = NULL;
3655 struct fio *fio = NULL;
3657 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3659 SMB_VFS_HANDLE_GET_DATA(handle, config,
3660 struct fruit_config_data, return -1);
3662 switch (config->rsrc) {
3663 case FRUIT_RSRC_STREAM:
3664 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3667 case FRUIT_RSRC_ADFILE:
3668 fd = fruit_open_rsrc_adouble(handle, smb_fname,
3672 case FRUIT_RSRC_XATTR:
3673 fd = fruit_open_rsrc_xattr(handle, smb_fname,
3678 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3682 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3688 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3689 fio->type = ADOUBLE_RSRC;
3690 fio->config = config;
3695 static int fruit_open(vfs_handle_struct *handle,
3696 struct smb_filename *smb_fname,
3697 files_struct *fsp, int flags, mode_t mode)
3701 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3703 if (!is_ntfs_stream_smb_fname(smb_fname)) {
3704 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3707 if (is_afpinfo_stream(smb_fname)) {
3708 fd = fruit_open_meta(handle, smb_fname, fsp, flags, mode);
3709 } else if (is_afpresource_stream(smb_fname)) {
3710 fd = fruit_open_rsrc(handle, smb_fname, fsp, flags, mode);
3712 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3715 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3720 static int fruit_close_meta(vfs_handle_struct *handle,
3724 struct fruit_config_data *config = NULL;
3726 SMB_VFS_HANDLE_GET_DATA(handle, config,
3727 struct fruit_config_data, return -1);
3729 switch (config->meta) {
3730 case FRUIT_META_STREAM:
3731 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3734 case FRUIT_META_NETATALK:
3735 ret = close(fsp->fh->fd);
3740 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3748 static int fruit_close_rsrc(vfs_handle_struct *handle,
3752 struct fruit_config_data *config = NULL;
3754 SMB_VFS_HANDLE_GET_DATA(handle, config,
3755 struct fruit_config_data, return -1);
3757 switch (config->rsrc) {
3758 case FRUIT_RSRC_STREAM:
3759 case FRUIT_RSRC_ADFILE:
3760 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3763 case FRUIT_RSRC_XATTR:
3764 ret = close(fsp->fh->fd);
3769 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3776 static int fruit_close(vfs_handle_struct *handle,
3784 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(fsp->fsp_name), fd);
3786 if (!is_ntfs_stream_smb_fname(fsp->fsp_name)) {
3787 return SMB_VFS_NEXT_CLOSE(handle, fsp);
3790 if (is_afpinfo_stream(fsp->fsp_name)) {
3791 ret = fruit_close_meta(handle, fsp);
3792 } else if (is_afpresource_stream(fsp->fsp_name)) {
3793 ret = fruit_close_rsrc(handle, fsp);
3795 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3801 static int fruit_rename(struct vfs_handle_struct *handle,
3802 const struct smb_filename *smb_fname_src,
3803 const struct smb_filename *smb_fname_dst)
3806 struct fruit_config_data *config = NULL;
3807 struct smb_filename *src_adp_smb_fname = NULL;
3808 struct smb_filename *dst_adp_smb_fname = NULL;
3810 SMB_VFS_HANDLE_GET_DATA(handle, config,
3811 struct fruit_config_data, return -1);
3813 if (!VALID_STAT(smb_fname_src->st)) {
3814 DBG_ERR("Need valid stat for [%s]\n",
3815 smb_fname_str_dbg(smb_fname_src));
3819 rc = SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
3824 if ((config->rsrc != FRUIT_RSRC_ADFILE) ||
3825 (!S_ISREG(smb_fname_src->st.st_ex_mode)))
3830 rc = adouble_path(talloc_tos(), smb_fname_src, &src_adp_smb_fname);
3835 rc = adouble_path(talloc_tos(), smb_fname_dst, &dst_adp_smb_fname);
3840 DBG_DEBUG("%s -> %s\n",
3841 smb_fname_str_dbg(src_adp_smb_fname),
3842 smb_fname_str_dbg(dst_adp_smb_fname));
3844 rc = SMB_VFS_NEXT_RENAME(handle, src_adp_smb_fname, dst_adp_smb_fname);
3845 if (errno == ENOENT) {
3850 TALLOC_FREE(src_adp_smb_fname);
3851 TALLOC_FREE(dst_adp_smb_fname);
3855 static int fruit_unlink_meta_stream(vfs_handle_struct *handle,
3856 const struct smb_filename *smb_fname)
3858 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3861 static int fruit_unlink_meta_netatalk(vfs_handle_struct *handle,
3862 const struct smb_filename *smb_fname)
3864 return SMB_VFS_REMOVEXATTR(handle->conn,
3866 AFPINFO_EA_NETATALK);
3869 static int fruit_unlink_meta(vfs_handle_struct *handle,
3870 const struct smb_filename *smb_fname)
3872 struct fruit_config_data *config = NULL;
3875 SMB_VFS_HANDLE_GET_DATA(handle, config,
3876 struct fruit_config_data, return -1);
3878 switch (config->meta) {
3879 case FRUIT_META_STREAM:
3880 rc = fruit_unlink_meta_stream(handle, smb_fname);
3883 case FRUIT_META_NETATALK:
3884 rc = fruit_unlink_meta_netatalk(handle, smb_fname);
3888 DBG_ERR("Unsupported meta config [%d]\n", config->meta);
3895 static int fruit_unlink_rsrc_stream(vfs_handle_struct *handle,
3896 const struct smb_filename *smb_fname,
3901 if (!force_unlink) {
3902 struct smb_filename *smb_fname_cp = NULL;
3905 smb_fname_cp = cp_smb_filename(talloc_tos(), smb_fname);
3906 if (smb_fname_cp == NULL) {
3911 * 0 byte resource fork streams are not listed by
3912 * vfs_streaminfo, as a result stream cleanup/deletion of file
3913 * deletion doesn't remove the resourcefork stream.
3916 ret = SMB_VFS_NEXT_STAT(handle, smb_fname_cp);
3918 TALLOC_FREE(smb_fname_cp);
3919 DBG_ERR("stat [%s] failed [%s]\n",
3920 smb_fname_str_dbg(smb_fname_cp), strerror(errno));
3924 size = smb_fname_cp->st.st_ex_size;
3925 TALLOC_FREE(smb_fname_cp);
3928 /* OS X ignores resource fork stream delete requests */
3933 ret = SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3934 if ((ret != 0) && (errno == ENOENT) && force_unlink) {
3941 static int fruit_unlink_rsrc_adouble(vfs_handle_struct *handle,
3942 const struct smb_filename *smb_fname,
3946 struct adouble *ad = NULL;
3947 struct smb_filename *adp_smb_fname = NULL;
3949 if (!force_unlink) {
3950 ad = ad_get(talloc_tos(), handle, smb_fname,
3959 * 0 byte resource fork streams are not listed by
3960 * vfs_streaminfo, as a result stream cleanup/deletion of file
3961 * deletion doesn't remove the resourcefork stream.
3964 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
3965 /* OS X ignores resource fork stream delete requests */
3973 rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
3978 rc = SMB_VFS_NEXT_UNLINK(handle, adp_smb_fname);
3979 TALLOC_FREE(adp_smb_fname);
3980 if ((rc != 0) && (errno == ENOENT) && force_unlink) {
3987 static int fruit_unlink_rsrc_xattr(vfs_handle_struct *handle,
3988 const struct smb_filename *smb_fname,
3992 * OS X ignores resource fork stream delete requests, so nothing to do
3993 * here. Removing the file will remove the xattr anyway, so we don't
3994 * have to take care of removing 0 byte resource forks that could be
4000 static int fruit_unlink_rsrc(vfs_handle_struct *handle,
4001 const struct smb_filename *smb_fname,
4004 struct fruit_config_data *config = NULL;
4007 SMB_VFS_HANDLE_GET_DATA(handle, config,
4008 struct fruit_config_data, return -1);
4010 switch (config->rsrc) {
4011 case FRUIT_RSRC_STREAM:
4012 rc = fruit_unlink_rsrc_stream(handle, smb_fname, force_unlink);
4015 case FRUIT_RSRC_ADFILE:
4016 rc = fruit_unlink_rsrc_adouble(handle, smb_fname, force_unlink);
4019 case FRUIT_RSRC_XATTR:
4020 rc = fruit_unlink_rsrc_xattr(handle, smb_fname, force_unlink);
4024 DBG_ERR("Unsupported rsrc config [%d]\n", config->rsrc);
4031 static int fruit_unlink(vfs_handle_struct *handle,
4032 const struct smb_filename *smb_fname)
4035 struct fruit_config_data *config = NULL;
4036 struct smb_filename *rsrc_smb_fname = NULL;
4038 SMB_VFS_HANDLE_GET_DATA(handle, config,
4039 struct fruit_config_data, return -1);
4041 if (is_afpinfo_stream(smb_fname)) {
4042 return fruit_unlink_meta(handle, smb_fname);
4043 } else if (is_afpresource_stream(smb_fname)) {
4044 return fruit_unlink_rsrc(handle, smb_fname, false);
4045 } else if (is_ntfs_stream_smb_fname(smb_fname)) {
4046 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4047 } else if (is_adouble_file(smb_fname->base_name)) {
4048 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4052 * A request to delete the base file. Because 0 byte resource
4053 * fork streams are not listed by fruit_streaminfo,
4054 * delete_all_streams() can't remove 0 byte resource fork
4055 * streams, so we have to cleanup this here.
4057 rsrc_smb_fname = synthetic_smb_fname(talloc_tos(),
4058 smb_fname->base_name,
4059 AFPRESOURCE_STREAM_NAME,
4062 if (rsrc_smb_fname == NULL) {
4066 rc = fruit_unlink_rsrc(handle, rsrc_smb_fname, true);
4067 if ((rc != 0) && (errno != ENOENT)) {
4068 DBG_ERR("Forced unlink of [%s] failed [%s]\n",
4069 smb_fname_str_dbg(rsrc_smb_fname), strerror(errno));
4070 TALLOC_FREE(rsrc_smb_fname);
4073 TALLOC_FREE(rsrc_smb_fname);
4075 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4078 static int fruit_chmod(vfs_handle_struct *handle,
4079 const struct smb_filename *smb_fname,
4083 struct fruit_config_data *config = NULL;
4084 struct smb_filename *smb_fname_adp = NULL;
4086 rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname, mode);
4091 SMB_VFS_HANDLE_GET_DATA(handle, config,
4092 struct fruit_config_data, return -1);
4094 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4098 if (!VALID_STAT(smb_fname->st)) {
4102 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
4106 rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_adp);
4111 DEBUG(10, ("fruit_chmod: %s\n", smb_fname_adp->base_name));
4113 rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname_adp, mode);
4114 if (errno == ENOENT) {
4118 TALLOC_FREE(smb_fname_adp);
4122 static int fruit_chown(vfs_handle_struct *handle,
4123 const struct smb_filename *smb_fname,
4128 struct fruit_config_data *config = NULL;
4129 struct smb_filename *adp_smb_fname = NULL;
4131 rc = SMB_VFS_NEXT_CHOWN(handle, smb_fname, uid, gid);
4136 SMB_VFS_HANDLE_GET_DATA(handle, config,
4137 struct fruit_config_data, return -1);
4139 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4143 if (!VALID_STAT(smb_fname->st)) {
4147 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
4151 rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
4156 DEBUG(10, ("fruit_chown: %s\n", adp_smb_fname->base_name));
4158 rc = SMB_VFS_NEXT_CHOWN(handle, adp_smb_fname, uid, gid);
4159 if (errno == ENOENT) {
4164 TALLOC_FREE(adp_smb_fname);
4168 static int fruit_rmdir(struct vfs_handle_struct *handle,
4169 const struct smb_filename *smb_fname)
4173 struct fruit_config_data *config;
4175 SMB_VFS_HANDLE_GET_DATA(handle, config,
4176 struct fruit_config_data, return -1);
4178 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4183 * Due to there is no way to change bDeleteVetoFiles variable
4184 * from this module, need to clean up ourselves
4187 dh = SMB_VFS_OPENDIR(handle->conn, smb_fname, NULL, 0);
4192 while ((de = SMB_VFS_READDIR(handle->conn, dh, NULL)) != NULL) {
4193 struct adouble *ad = NULL;
4195 struct smb_filename *ad_smb_fname = NULL;
4198 if (!is_adouble_file(de->d_name)) {
4202 p = talloc_asprintf(talloc_tos(), "%s/%s",
4203 smb_fname->base_name, de->d_name);
4205 DBG_ERR("talloc_asprintf failed\n");
4209 ad_smb_fname = synthetic_smb_fname(talloc_tos(), p,
4213 if (ad_smb_fname == NULL) {
4214 DBG_ERR("synthetic_smb_fname failed\n");
4219 * Check whether it's a valid AppleDouble file, if
4220 * yes, delete it, ignore it otherwise.
4222 ad = ad_get(talloc_tos(), handle, ad_smb_fname, ADOUBLE_RSRC);
4224 TALLOC_FREE(ad_smb_fname);
4230 ret = SMB_VFS_NEXT_UNLINK(handle, ad_smb_fname);
4232 DBG_ERR("Deleting [%s] failed\n",
4233 smb_fname_str_dbg(ad_smb_fname));
4235 TALLOC_FREE(ad_smb_fname);
4240 SMB_VFS_CLOSEDIR(handle->conn, dh);
4242 return SMB_VFS_NEXT_RMDIR(handle, smb_fname);
4245 static ssize_t fruit_pread_meta_stream(vfs_handle_struct *handle,
4246 files_struct *fsp, void *data,
4247 size_t n, off_t offset)
4252 nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4253 if (nread == -1 || nread == n) {
4257 DBG_ERR("Removing [%s] after short read [%zd]\n",
4258 fsp_str_dbg(fsp), nread);
4260 ret = SMB_VFS_NEXT_UNLINK(handle, fsp->fsp_name);
4262 DBG_ERR("Removing [%s] failed\n", fsp_str_dbg(fsp));
4270 static ssize_t fruit_pread_meta_adouble(vfs_handle_struct *handle,
4271 files_struct *fsp, void *data,
4272 size_t n, off_t offset)
4275 struct adouble *ad = NULL;
4276 char afpinfo_buf[AFP_INFO_SIZE];
4280 ai = afpinfo_new(talloc_tos());
4285 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
4291 p = ad_get_entry(ad, ADEID_FINDERI);
4293 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
4298 memcpy(&ai->afpi_FinderInfo[0], p, ADEDLEN_FINDERI);
4300 nread = afpinfo_pack(ai, afpinfo_buf);
4301 if (nread != AFP_INFO_SIZE) {
4306 memcpy(data, afpinfo_buf, n);
4314 static ssize_t fruit_pread_meta(vfs_handle_struct *handle,
4315 files_struct *fsp, void *data,
4316 size_t n, off_t offset)
4318 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4323 * OS X has a off-by-1 error in the offset calculation, so we're
4324 * bug compatible here. It won't hurt, as any relevant real
4325 * world read requests from the AFP_AfpInfo stream will be
4326 * offset=0 n=60. offset is ignored anyway, see below.
4328 if ((offset < 0) || (offset >= AFP_INFO_SIZE + 1)) {
4333 DBG_ERR("Failed to fetch fsp extension");
4337 /* Yes, macOS always reads from offset 0 */
4339 to_return = MIN(n, AFP_INFO_SIZE);
4341 switch (fio->config->meta) {
4342 case FRUIT_META_STREAM:
4343 nread = fruit_pread_meta_stream(handle, fsp, data,
4347 case FRUIT_META_NETATALK:
4348 nread = fruit_pread_meta_adouble(handle, fsp, data,
4353 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
4357 if (nread == -1 && fio->created) {
4359 char afpinfo_buf[AFP_INFO_SIZE];
4361 ai = afpinfo_new(talloc_tos());
4366 nread = afpinfo_pack(ai, afpinfo_buf);
4368 if (nread != AFP_INFO_SIZE) {
4372 memcpy(data, afpinfo_buf, to_return);
4379 static ssize_t fruit_pread_rsrc_stream(vfs_handle_struct *handle,
4380 files_struct *fsp, void *data,
4381 size_t n, off_t offset)
4383 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4386 static ssize_t fruit_pread_rsrc_xattr(vfs_handle_struct *handle,
4387 files_struct *fsp, void *data,
4388 size_t n, off_t offset)
4390 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4393 static ssize_t fruit_pread_rsrc_adouble(vfs_handle_struct *handle,
4394 files_struct *fsp, void *data,
4395 size_t n, off_t offset)
4397 struct adouble *ad = NULL;
4400 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
4405 nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n,
4406 offset + ad_getentryoff(ad, ADEID_RFORK));
4412 static ssize_t fruit_pread_rsrc(vfs_handle_struct *handle,
4413 files_struct *fsp, void *data,
4414 size_t n, off_t offset)
4416 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4424 switch (fio->config->rsrc) {
4425 case FRUIT_RSRC_STREAM:
4426 nread = fruit_pread_rsrc_stream(handle, fsp, data, n, offset);
4429 case FRUIT_RSRC_ADFILE:
4430 nread = fruit_pread_rsrc_adouble(handle, fsp, data, n, offset);
4433 case FRUIT_RSRC_XATTR:
4434 nread = fruit_pread_rsrc_xattr(handle, fsp, data, n, offset);
4438 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
4445 static ssize_t fruit_pread(vfs_handle_struct *handle,
4446 files_struct *fsp, void *data,
4447 size_t n, off_t offset)
4449 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4452 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4453 fsp_str_dbg(fsp), (intmax_t)offset, n);
4456 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4459 if (fio->type == ADOUBLE_META) {
4460 nread = fruit_pread_meta(handle, fsp, data, n, offset);
4462 nread = fruit_pread_rsrc(handle, fsp, data, n, offset);
4465 DBG_DEBUG("Path [%s] nread [%zd]\n", fsp_str_dbg(fsp), nread);
4469 static bool fruit_must_handle_aio_stream(struct fio *fio)
4475 if (fio->type == ADOUBLE_META) {
4479 if ((fio->type == ADOUBLE_RSRC) &&
4480 (fio->config->rsrc == FRUIT_RSRC_ADFILE))
4488 struct fruit_pread_state {
4490 struct vfs_aio_state vfs_aio_state;
4493 static void fruit_pread_done(struct tevent_req *subreq);
4495 static struct tevent_req *fruit_pread_send(
4496 struct vfs_handle_struct *handle,
4497 TALLOC_CTX *mem_ctx,
4498 struct tevent_context *ev,
4499 struct files_struct *fsp,
4501 size_t n, off_t offset)
4503 struct tevent_req *req = NULL;
4504 struct tevent_req *subreq = NULL;
4505 struct fruit_pread_state *state = NULL;
4506 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4508 req = tevent_req_create(mem_ctx, &state,
4509 struct fruit_pread_state);
4514 if (fruit_must_handle_aio_stream(fio)) {
4515 state->nread = SMB_VFS_PREAD(fsp, data, n, offset);
4516 if (state->nread != n) {
4517 if (state->nread != -1) {
4520 tevent_req_error(req, errno);
4521 return tevent_req_post(req, ev);
4523 tevent_req_done(req);
4524 return tevent_req_post(req, ev);
4527 subreq = SMB_VFS_NEXT_PREAD_SEND(state, ev, handle, fsp,
4529 if (tevent_req_nomem(req, subreq)) {
4530 return tevent_req_post(req, ev);
4532 tevent_req_set_callback(subreq, fruit_pread_done, req);
4536 static void fruit_pread_done(struct tevent_req *subreq)
4538 struct tevent_req *req = tevent_req_callback_data(
4539 subreq, struct tevent_req);
4540 struct fruit_pread_state *state = tevent_req_data(
4541 req, struct fruit_pread_state);
4543 state->nread = SMB_VFS_PREAD_RECV(subreq, &state->vfs_aio_state);
4544 TALLOC_FREE(subreq);
4546 if (tevent_req_error(req, state->vfs_aio_state.error)) {
4549 tevent_req_done(req);
4552 static ssize_t fruit_pread_recv(struct tevent_req *req,
4553 struct vfs_aio_state *vfs_aio_state)
4555 struct fruit_pread_state *state = tevent_req_data(
4556 req, struct fruit_pread_state);
4558 if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
4562 *vfs_aio_state = state->vfs_aio_state;
4563 return state->nread;
4566 static ssize_t fruit_pwrite_meta_stream(vfs_handle_struct *handle,
4567 files_struct *fsp, const void *data,
4568 size_t n, off_t offset)
4570 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4576 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4577 fsp_str_dbg(fsp), (intmax_t)offset, n);
4586 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
4588 DBG_ERR("Close [%s] failed: %s\n",
4589 fsp_str_dbg(fsp), strerror(errno));
4594 fd = SMB_VFS_NEXT_OPEN(handle,
4600 DBG_ERR("On-demand create [%s] in write failed: %s\n",
4601 fsp_str_dbg(fsp), strerror(errno));
4605 fio->fake_fd = false;
4608 ai = afpinfo_unpack(talloc_tos(), data);
4613 if (ai_empty_finderinfo(ai)) {
4615 * Writing an all 0 blob to the metadata stream results in the
4616 * stream being removed on a macOS server. This ensures we
4617 * behave the same and it verified by the "delete AFP_AfpInfo by
4618 * writing all 0" test.
4620 ret = SMB_VFS_NEXT_FTRUNCATE(handle, fsp, 0);
4622 DBG_ERR("SMB_VFS_NEXT_FTRUNCATE on [%s] failed\n",
4627 ok = set_delete_on_close(
4630 handle->conn->session_info->security_token,
4631 handle->conn->session_info->unix_token);
4633 DBG_ERR("set_delete_on_close on [%s] failed\n",
4640 nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4641 if (nwritten != n) {
4648 static ssize_t fruit_pwrite_meta_netatalk(vfs_handle_struct *handle,
4649 files_struct *fsp, const void *data,
4650 size_t n, off_t offset)
4652 struct adouble *ad = NULL;
4658 ai = afpinfo_unpack(talloc_tos(), data);
4663 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
4665 ad = ad_init(talloc_tos(), ADOUBLE_META);
4670 p = ad_get_entry(ad, ADEID_FINDERI);
4672 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
4677 memcpy(p, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
4679 ret = ad_fset(handle, ad, fsp);
4681 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
4688 if (!ai_empty_finderinfo(ai)) {
4693 * Writing an all 0 blob to the metadata stream results in the stream
4694 * being removed on a macOS server. This ensures we behave the same and
4695 * it verified by the "delete AFP_AfpInfo by writing all 0" test.
4698 ok = set_delete_on_close(
4701 handle->conn->session_info->security_token,
4702 handle->conn->session_info->unix_token);
4704 DBG_ERR("set_delete_on_close on [%s] failed\n",
4712 static ssize_t fruit_pwrite_meta(vfs_handle_struct *handle,
4713 files_struct *fsp, const void *data,
4714 size_t n, off_t offset)
4716 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4718 uint8_t buf[AFP_INFO_SIZE];
4724 DBG_ERR("Failed to fetch fsp extension");
4733 if (offset != 0 && n < 60) {
4738 cmp = memcmp(data, "AFP", 3);
4744 if (n <= AFP_OFF_FinderInfo) {
4746 * Nothing to do here really, just return
4754 if (to_copy > AFP_INFO_SIZE) {
4755 to_copy = AFP_INFO_SIZE;
4757 memcpy(buf, data, to_copy);
4760 if (to_write != AFP_INFO_SIZE) {
4761 to_write = AFP_INFO_SIZE;
4764 switch (fio->config->meta) {
4765 case FRUIT_META_STREAM:
4766 nwritten = fruit_pwrite_meta_stream(handle,
4773 case FRUIT_META_NETATALK:
4774 nwritten = fruit_pwrite_meta_netatalk(handle,
4782 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
4786 if (nwritten != to_write) {
4791 * Return the requested amount, verified against macOS SMB server
4796 static ssize_t fruit_pwrite_rsrc_stream(vfs_handle_struct *handle,
4797 files_struct *fsp, const void *data,
4798 size_t n, off_t offset)
4800 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4803 static ssize_t fruit_pwrite_rsrc_xattr(vfs_handle_struct *handle,
4804 files_struct *fsp, const void *data,
4805 size_t n, off_t offset)
4807 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4810 static ssize_t fruit_pwrite_rsrc_adouble(vfs_handle_struct *handle,
4811 files_struct *fsp, const void *data,
4812 size_t n, off_t offset)
4814 struct adouble *ad = NULL;
4818 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
4820 DBG_ERR("ad_get [%s] failed\n", fsp_str_dbg(fsp));
4824 nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n,
4825 offset + ad_getentryoff(ad, ADEID_RFORK));
4826 if (nwritten != n) {
4827 DBG_ERR("Short write on [%s] [%zd/%zd]\n",
4828 fsp_str_dbg(fsp), nwritten, n);
4833 if ((n + offset) > ad_getentrylen(ad, ADEID_RFORK)) {
4834 ad_setentrylen(ad, ADEID_RFORK, n + offset);
4835 ret = ad_fset(handle, ad, fsp);
4837 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
4847 static ssize_t fruit_pwrite_rsrc(vfs_handle_struct *handle,
4848 files_struct *fsp, const void *data,
4849 size_t n, off_t offset)
4851 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4855 DBG_ERR("Failed to fetch fsp extension");
4859 switch (fio->config->rsrc) {
4860 case FRUIT_RSRC_STREAM:
4861 nwritten = fruit_pwrite_rsrc_stream(handle, fsp, data, n, offset);
4864 case FRUIT_RSRC_ADFILE:
4865 nwritten = fruit_pwrite_rsrc_adouble(handle, fsp, data, n, offset);
4868 case FRUIT_RSRC_XATTR:
4869 nwritten = fruit_pwrite_rsrc_xattr(handle, fsp, data, n, offset);
4873 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
4880 static ssize_t fruit_pwrite(vfs_handle_struct *handle,
4881 files_struct *fsp, const void *data,
4882 size_t n, off_t offset)
4884 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4887 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4888 fsp_str_dbg(fsp), (intmax_t)offset, n);
4891 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4894 if (fio->type == ADOUBLE_META) {
4895 nwritten = fruit_pwrite_meta(handle, fsp, data, n, offset);
4897 nwritten = fruit_pwrite_rsrc(handle, fsp, data, n, offset);
4900 DBG_DEBUG("Path [%s] nwritten=%zd\n", fsp_str_dbg(fsp), nwritten);
4904 struct fruit_pwrite_state {
4906 struct vfs_aio_state vfs_aio_state;
4909 static void fruit_pwrite_done(struct tevent_req *subreq);
4911 static struct tevent_req *fruit_pwrite_send(
4912 struct vfs_handle_struct *handle,
4913 TALLOC_CTX *mem_ctx,
4914 struct tevent_context *ev,
4915 struct files_struct *fsp,
4917 size_t n, off_t offset)
4919 struct tevent_req *req = NULL;
4920 struct tevent_req *subreq = NULL;
4921 struct fruit_pwrite_state *state = NULL;
4922 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4924 req = tevent_req_create(mem_ctx, &state,
4925 struct fruit_pwrite_state);
4930 if (fruit_must_handle_aio_stream(fio)) {
4931 state->nwritten = SMB_VFS_PWRITE(fsp, data, n, offset);
4932 if (state->nwritten != n) {
4933 if (state->nwritten != -1) {
4936 tevent_req_error(req, errno);
4937 return tevent_req_post(req, ev);
4939 tevent_req_done(req);
4940 return tevent_req_post(req, ev);
4943 subreq = SMB_VFS_NEXT_PWRITE_SEND(state, ev, handle, fsp,
4945 if (tevent_req_nomem(req, subreq)) {
4946 return tevent_req_post(req, ev);
4948 tevent_req_set_callback(subreq, fruit_pwrite_done, req);
4952 static void fruit_pwrite_done(struct tevent_req *subreq)
4954 struct tevent_req *req = tevent_req_callback_data(
4955 subreq, struct tevent_req);
4956 struct fruit_pwrite_state *state = tevent_req_data(
4957 req, struct fruit_pwrite_state);
4959 state->nwritten = SMB_VFS_PWRITE_RECV(subreq, &state->vfs_aio_state);
4960 TALLOC_FREE(subreq);
4962 if (tevent_req_error(req, state->vfs_aio_state.error)) {
4965 tevent_req_done(req);
4968 static ssize_t fruit_pwrite_recv(struct tevent_req *req,
4969 struct vfs_aio_state *vfs_aio_state)
4971 struct fruit_pwrite_state *state = tevent_req_data(
4972 req, struct fruit_pwrite_state);
4974 if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
4978 *vfs_aio_state = state->vfs_aio_state;
4979 return state->nwritten;
4983 * Helper to stat/lstat the base file of an smb_fname.
4985 static int fruit_stat_base(vfs_handle_struct *handle,
4986 struct smb_filename *smb_fname,
4989 char *tmp_stream_name;
4992 tmp_stream_name = smb_fname->stream_name;
4993 smb_fname->stream_name = NULL;
4995 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
4997 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
4999 smb_fname->stream_name = tmp_stream_name;
5001 DBG_DEBUG("fruit_stat_base [%s] dev [%ju] ino [%ju]\n",
5002 smb_fname->base_name,
5003 (uintmax_t)smb_fname->st.st_ex_dev,
5004 (uintmax_t)smb_fname->st.st_ex_ino);
5008 static int fruit_stat_meta_stream(vfs_handle_struct *handle,
5009 struct smb_filename *smb_fname,
5015 ret = fruit_stat_base(handle, smb_fname, false);
5020 ino = fruit_inode(&smb_fname->st, smb_fname->stream_name);
5023 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
5025 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5028 smb_fname->st.st_ex_ino = ino;
5033 static int fruit_stat_meta_netatalk(vfs_handle_struct *handle,
5034 struct smb_filename *smb_fname,
5037 struct adouble *ad = NULL;
5039 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5041 DBG_INFO("fruit_stat_meta %s: %s\n",
5042 smb_fname_str_dbg(smb_fname), strerror(errno));
5048 /* Populate the stat struct with info from the base file. */
5049 if (fruit_stat_base(handle, smb_fname, follow_links) == -1) {
5052 smb_fname->st.st_ex_size = AFP_INFO_SIZE;
5053 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5054 smb_fname->stream_name);
5058 static int fruit_stat_meta(vfs_handle_struct *handle,
5059 struct smb_filename *smb_fname,
5062 struct fruit_config_data *config = NULL;
5065 SMB_VFS_HANDLE_GET_DATA(handle, config,
5066 struct fruit_config_data, return -1);
5068 switch (config->meta) {
5069 case FRUIT_META_STREAM:
5070 ret = fruit_stat_meta_stream(handle, smb_fname, follow_links);
5073 case FRUIT_META_NETATALK:
5074 ret = fruit_stat_meta_netatalk(handle, smb_fname, follow_links);
5078 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
5085 static int fruit_stat_rsrc_netatalk(vfs_handle_struct *handle,
5086 struct smb_filename *smb_fname,
5089 struct adouble *ad = NULL;
5092 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
5098 /* Populate the stat struct with info from the base file. */
5099 ret = fruit_stat_base(handle, smb_fname, follow_links);
5105 smb_fname->st.st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
5106 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5107 smb_fname->stream_name);
5112 static int fruit_stat_rsrc_stream(vfs_handle_struct *handle,
5113 struct smb_filename *smb_fname,
5119 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
5121 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5127 static int fruit_stat_rsrc_xattr(vfs_handle_struct *handle,
5128 struct smb_filename *smb_fname,
5131 #ifdef HAVE_ATTROPEN
5135 /* Populate the stat struct with info from the base file. */
5136 ret = fruit_stat_base(handle, smb_fname, follow_links);
5141 fd = attropen(smb_fname->base_name,
5142 AFPRESOURCE_EA_NETATALK,
5148 ret = sys_fstat(fd, &smb_fname->st, false);
5151 DBG_ERR("fstat [%s:%s] failed\n", smb_fname->base_name,
5152 AFPRESOURCE_EA_NETATALK);
5158 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5159 smb_fname->stream_name);
5169 static int fruit_stat_rsrc(vfs_handle_struct *handle,
5170 struct smb_filename *smb_fname,
5173 struct fruit_config_data *config = NULL;
5176 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
5178 SMB_VFS_HANDLE_GET_DATA(handle, config,
5179 struct fruit_config_data, return -1);
5181 switch (config->rsrc) {
5182 case FRUIT_RSRC_STREAM:
5183 ret = fruit_stat_rsrc_stream(handle, smb_fname, follow_links);
5186 case FRUIT_RSRC_XATTR:
5187 ret = fruit_stat_rsrc_xattr(handle, smb_fname, follow_links);
5190 case FRUIT_RSRC_ADFILE:
5191 ret = fruit_stat_rsrc_netatalk(handle, smb_fname, follow_links);
5195 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
5202 static int fruit_stat(vfs_handle_struct *handle,
5203 struct smb_filename *smb_fname)
5207 DEBUG(10, ("fruit_stat called for %s\n",
5208 smb_fname_str_dbg(smb_fname)));
5210 if (!is_ntfs_stream_smb_fname(smb_fname)
5211 || is_ntfs_default_stream_smb_fname(smb_fname)) {
5212 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
5214 update_btime(handle, smb_fname);
5220 * Note if lp_posix_paths() is true, we can never
5221 * get here as is_ntfs_stream_smb_fname() is
5222 * always false. So we never need worry about
5223 * not following links here.
5226 if (is_afpinfo_stream(smb_fname)) {
5227 rc = fruit_stat_meta(handle, smb_fname, true);
5228 } else if (is_afpresource_stream(smb_fname)) {
5229 rc = fruit_stat_rsrc(handle, smb_fname, true);
5231 return SMB_VFS_NEXT_STAT(handle, smb_fname);
5235 update_btime(handle, smb_fname);
5236 smb_fname->st.st_ex_mode &= ~S_IFMT;
5237 smb_fname->st.st_ex_mode |= S_IFREG;
5238 smb_fname->st.st_ex_blocks =
5239 smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
5244 static int fruit_lstat(vfs_handle_struct *handle,
5245 struct smb_filename *smb_fname)
5249 DEBUG(10, ("fruit_lstat called for %s\n",
5250 smb_fname_str_dbg(smb_fname)));
5252 if (!is_ntfs_stream_smb_fname(smb_fname)
5253 || is_ntfs_default_stream_smb_fname(smb_fname)) {
5254 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5256 update_btime(handle, smb_fname);
5261 if (is_afpinfo_stream(smb_fname)) {
5262 rc = fruit_stat_meta(handle, smb_fname, false);
5263 } else if (is_afpresource_stream(smb_fname)) {
5264 rc = fruit_stat_rsrc(handle, smb_fname, false);
5266 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5270 update_btime(handle, smb_fname);
5271 smb_fname->st.st_ex_mode &= ~S_IFMT;
5272 smb_fname->st.st_ex_mode |= S_IFREG;
5273 smb_fname->st.st_ex_blocks =
5274 smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
5279 static int fruit_fstat_meta_stream(vfs_handle_struct *handle,
5281 SMB_STRUCT_STAT *sbuf)
5283 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5284 struct smb_filename smb_fname;
5293 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5298 *sbuf = fsp->base_fsp->fsp_name->st;
5299 sbuf->st_ex_size = AFP_INFO_SIZE;
5300 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5304 smb_fname = (struct smb_filename) {
5305 .base_name = fsp->fsp_name->base_name,
5308 ret = fruit_stat_base(handle, &smb_fname, false);
5312 *sbuf = smb_fname.st;
5314 ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5316 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5321 sbuf->st_ex_ino = ino;
5325 static int fruit_fstat_meta_netatalk(vfs_handle_struct *handle,
5327 SMB_STRUCT_STAT *sbuf)
5331 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5336 *sbuf = fsp->base_fsp->fsp_name->st;
5337 sbuf->st_ex_size = AFP_INFO_SIZE;
5338 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5343 static int fruit_fstat_meta(vfs_handle_struct *handle,
5345 SMB_STRUCT_STAT *sbuf,
5350 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
5352 switch (fio->config->meta) {
5353 case FRUIT_META_STREAM:
5354 ret = fruit_fstat_meta_stream(handle, fsp, sbuf);
5357 case FRUIT_META_NETATALK:
5358 ret = fruit_fstat_meta_netatalk(handle, fsp, sbuf);
5362 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
5366 DBG_DEBUG("Path [%s] ret [%d]\n", fsp_str_dbg(fsp), ret);
5370 static int fruit_fstat_rsrc_xattr(vfs_handle_struct *handle,
5372 SMB_STRUCT_STAT *sbuf)
5374 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5377 static int fruit_fstat_rsrc_stream(vfs_handle_struct *handle,
5379 SMB_STRUCT_STAT *sbuf)
5381 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5384 static int fruit_fstat_rsrc_adouble(vfs_handle_struct *handle,
5386 SMB_STRUCT_STAT *sbuf)
5388 struct adouble *ad = NULL;
5391 /* Populate the stat struct with info from the base file. */
5392 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5397 ad = ad_get(talloc_tos(), handle,
5398 fsp->base_fsp->fsp_name,
5401 DBG_ERR("ad_get [%s] failed [%s]\n",
5402 fsp_str_dbg(fsp), strerror(errno));
5406 *sbuf = fsp->base_fsp->fsp_name->st;
5407 sbuf->st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
5408 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5414 static int fruit_fstat_rsrc(vfs_handle_struct *handle, files_struct *fsp,
5415 SMB_STRUCT_STAT *sbuf, struct fio *fio)
5419 switch (fio->config->rsrc) {
5420 case FRUIT_RSRC_STREAM:
5421 ret = fruit_fstat_rsrc_stream(handle, fsp, sbuf);
5424 case FRUIT_RSRC_ADFILE:
5425 ret = fruit_fstat_rsrc_adouble(handle, fsp, sbuf);
5428 case FRUIT_RSRC_XATTR:
5429 ret = fruit_fstat_rsrc_xattr(handle, fsp, sbuf);
5433 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
5440 static int fruit_fstat(vfs_handle_struct *handle, files_struct *fsp,
5441 SMB_STRUCT_STAT *sbuf)
5443 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5447 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5450 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
5452 if (fio->type == ADOUBLE_META) {
5453 rc = fruit_fstat_meta(handle, fsp, sbuf, fio);
5455 rc = fruit_fstat_rsrc(handle, fsp, sbuf, fio);
5459 sbuf->st_ex_mode &= ~S_IFMT;
5460 sbuf->st_ex_mode |= S_IFREG;
5461 sbuf->st_ex_blocks = sbuf->st_ex_size / STAT_ST_BLOCKSIZE + 1;
5464 DBG_DEBUG("Path [%s] rc [%d] size [%"PRIdMAX"]\n",
5465 fsp_str_dbg(fsp), rc, (intmax_t)sbuf->st_ex_size);
5469 static NTSTATUS delete_invalid_meta_stream(
5470 vfs_handle_struct *handle,
5471 const struct smb_filename *smb_fname,
5472 TALLOC_CTX *mem_ctx,
5473 unsigned int *pnum_streams,
5474 struct stream_struct **pstreams,
5477 struct smb_filename *sname = NULL;
5481 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams, AFPINFO_STREAM);
5483 return NT_STATUS_INTERNAL_ERROR;
5487 return NT_STATUS_OK;
5490 sname = synthetic_smb_fname(talloc_tos(),
5491 smb_fname->base_name,
5492 AFPINFO_STREAM_NAME,
5494 if (sname == NULL) {
5495 return NT_STATUS_NO_MEMORY;
5498 ret = SMB_VFS_NEXT_UNLINK(handle, sname);
5501 DBG_ERR("Removing [%s] failed\n", smb_fname_str_dbg(sname));
5502 return map_nt_error_from_unix(errno);
5505 return NT_STATUS_OK;
5508 static NTSTATUS fruit_streaminfo_meta_stream(
5509 vfs_handle_struct *handle,
5510 struct files_struct *fsp,
5511 const struct smb_filename *smb_fname,
5512 TALLOC_CTX *mem_ctx,
5513 unsigned int *pnum_streams,
5514 struct stream_struct **pstreams)
5516 struct stream_struct *stream = *pstreams;
5517 unsigned int num_streams = *pnum_streams;
5520 for (i = 0; i < num_streams; i++) {
5521 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
5526 if (i == num_streams) {
5527 return NT_STATUS_OK;
5530 if (stream[i].size != AFP_INFO_SIZE) {
5531 DBG_ERR("Removing invalid AFPINFO_STREAM size [%jd] from [%s]\n",
5532 (intmax_t)stream[i].size, smb_fname_str_dbg(smb_fname));
5534 return delete_invalid_meta_stream(handle,
5543 return NT_STATUS_OK;
5546 static NTSTATUS fruit_streaminfo_meta_netatalk(
5547 vfs_handle_struct *handle,
5548 struct files_struct *fsp,
5549 const struct smb_filename *smb_fname,
5550 TALLOC_CTX *mem_ctx,
5551 unsigned int *pnum_streams,
5552 struct stream_struct **pstreams)
5554 struct stream_struct *stream = *pstreams;
5555 unsigned int num_streams = *pnum_streams;
5556 struct adouble *ad = NULL;
5561 /* Remove the Netatalk xattr from the list */
5562 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5563 ":" NETATALK_META_XATTR ":$DATA");
5565 return NT_STATUS_NO_MEMORY;
5569 * Check if there's a AFPINFO_STREAM from the VFS streams
5570 * backend and if yes, remove it from the list
5572 for (i = 0; i < num_streams; i++) {
5573 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
5578 if (i < num_streams) {
5579 DBG_WARNING("Unexpected AFPINFO_STREAM on [%s]\n",
5580 smb_fname_str_dbg(smb_fname));
5582 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5585 return NT_STATUS_INTERNAL_ERROR;
5589 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5591 return NT_STATUS_OK;
5594 is_fi_empty = ad_empty_finderinfo(ad);
5598 return NT_STATUS_OK;
5601 ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
5602 AFPINFO_STREAM_NAME, AFP_INFO_SIZE,
5603 smb_roundup(handle->conn, AFP_INFO_SIZE));
5605 return NT_STATUS_NO_MEMORY;
5608 return NT_STATUS_OK;
5611 static NTSTATUS fruit_streaminfo_meta(vfs_handle_struct *handle,
5612 struct files_struct *fsp,
5613 const struct smb_filename *smb_fname,
5614 TALLOC_CTX *mem_ctx,
5615 unsigned int *pnum_streams,
5616 struct stream_struct **pstreams)
5618 struct fruit_config_data *config = NULL;
5621 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5622 return NT_STATUS_INTERNAL_ERROR);
5624 switch (config->meta) {
5625 case FRUIT_META_NETATALK:
5626 status = fruit_streaminfo_meta_netatalk(handle, fsp, smb_fname,
5627 mem_ctx, pnum_streams,
5631 case FRUIT_META_STREAM:
5632 status = fruit_streaminfo_meta_stream(handle, fsp, smb_fname,
5633 mem_ctx, pnum_streams,
5638 return NT_STATUS_INTERNAL_ERROR;
5644 static NTSTATUS fruit_streaminfo_rsrc_stream(
5645 vfs_handle_struct *handle,
5646 struct files_struct *fsp,
5647 const struct smb_filename *smb_fname,
5648 TALLOC_CTX *mem_ctx,
5649 unsigned int *pnum_streams,
5650 struct stream_struct **pstreams)
5654 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
5656 DBG_ERR("Filtering resource stream failed\n");
5657 return NT_STATUS_INTERNAL_ERROR;
5659 return NT_STATUS_OK;
5662 static NTSTATUS fruit_streaminfo_rsrc_xattr(
5663 vfs_handle_struct *handle,
5664 struct files_struct *fsp,
5665 const struct smb_filename *smb_fname,
5666 TALLOC_CTX *mem_ctx,
5667 unsigned int *pnum_streams,
5668 struct stream_struct **pstreams)
5672 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
5674 DBG_ERR("Filtering resource stream failed\n");
5675 return NT_STATUS_INTERNAL_ERROR;
5677 return NT_STATUS_OK;
5680 static NTSTATUS fruit_streaminfo_rsrc_adouble(
5681 vfs_handle_struct *handle,
5682 struct files_struct *fsp,
5683 const struct smb_filename *smb_fname,
5684 TALLOC_CTX *mem_ctx,
5685 unsigned int *pnum_streams,
5686 struct stream_struct **pstreams)
5688 struct stream_struct *stream = *pstreams;
5689 unsigned int num_streams = *pnum_streams;
5690 struct adouble *ad = NULL;
5696 * Check if there's a AFPRESOURCE_STREAM from the VFS streams backend
5697 * and if yes, remove it from the list
5699 for (i = 0; i < num_streams; i++) {
5700 if (strequal_m(stream[i].name, AFPRESOURCE_STREAM)) {
5705 if (i < num_streams) {
5706 DBG_WARNING("Unexpected AFPRESOURCE_STREAM on [%s]\n",
5707 smb_fname_str_dbg(smb_fname));
5709 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5710 AFPRESOURCE_STREAM);
5712 return NT_STATUS_INTERNAL_ERROR;
5716 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
5718 return NT_STATUS_OK;
5721 rlen = ad_getentrylen(ad, ADEID_RFORK);
5725 return NT_STATUS_OK;
5728 ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
5729 AFPRESOURCE_STREAM_NAME, rlen,
5730 smb_roundup(handle->conn, rlen));
5732 return NT_STATUS_NO_MEMORY;
5735 return NT_STATUS_OK;
5738 static NTSTATUS fruit_streaminfo_rsrc(vfs_handle_struct *handle,
5739 struct files_struct *fsp,
5740 const struct smb_filename *smb_fname,
5741 TALLOC_CTX *mem_ctx,
5742 unsigned int *pnum_streams,
5743 struct stream_struct **pstreams)
5745 struct fruit_config_data *config = NULL;
5748 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5749 return NT_STATUS_INTERNAL_ERROR);
5751 switch (config->rsrc) {
5752 case FRUIT_RSRC_STREAM:
5753 status = fruit_streaminfo_rsrc_stream(handle, fsp, smb_fname,
5754 mem_ctx, pnum_streams,
5758 case FRUIT_RSRC_XATTR:
5759 status = fruit_streaminfo_rsrc_xattr(handle, fsp, smb_fname,
5760 mem_ctx, pnum_streams,
5764 case FRUIT_RSRC_ADFILE:
5765 status = fruit_streaminfo_rsrc_adouble(handle, fsp, smb_fname,
5766 mem_ctx, pnum_streams,
5771 return NT_STATUS_INTERNAL_ERROR;
5777 static void fruit_filter_empty_streams(unsigned int *pnum_streams,
5778 struct stream_struct **pstreams)
5780 unsigned num_streams = *pnum_streams;
5781 struct stream_struct *streams = *pstreams;
5784 if (!global_fruit_config.nego_aapl) {
5788 while (i < num_streams) {
5789 struct smb_filename smb_fname = (struct smb_filename) {
5790 .stream_name = streams[i].name,
5793 if (is_ntfs_default_stream_smb_fname(&smb_fname)
5794 || streams[i].size > 0)
5800 streams[i] = streams[num_streams - 1];
5804 *pnum_streams = num_streams;
5807 static NTSTATUS fruit_streaminfo(vfs_handle_struct *handle,
5808 struct files_struct *fsp,
5809 const struct smb_filename *smb_fname,
5810 TALLOC_CTX *mem_ctx,
5811 unsigned int *pnum_streams,
5812 struct stream_struct **pstreams)
5814 struct fruit_config_data *config = NULL;
5817 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5818 return NT_STATUS_UNSUCCESSFUL);
5820 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
5822 status = SMB_VFS_NEXT_STREAMINFO(handle, fsp, smb_fname, mem_ctx,
5823 pnum_streams, pstreams);
5824 if (!NT_STATUS_IS_OK(status)) {
5828 fruit_filter_empty_streams(pnum_streams, pstreams);
5830 status = fruit_streaminfo_meta(handle, fsp, smb_fname,
5831 mem_ctx, pnum_streams, pstreams);
5832 if (!NT_STATUS_IS_OK(status)) {
5836 status = fruit_streaminfo_rsrc(handle, fsp, smb_fname,
5837 mem_ctx, pnum_streams, pstreams);
5838 if (!NT_STATUS_IS_OK(status)) {
5842 return NT_STATUS_OK;
5845 static int fruit_ntimes(vfs_handle_struct *handle,
5846 const struct smb_filename *smb_fname,
5847 struct smb_file_time *ft)
5850 struct adouble *ad = NULL;
5851 struct fruit_config_data *config = NULL;
5853 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5856 if ((config->meta != FRUIT_META_NETATALK) ||
5857 null_timespec(ft->create_time))
5859 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
5862 DEBUG(10,("set btime for %s to %s\n", smb_fname_str_dbg(smb_fname),
5863 time_to_asc(convert_timespec_to_time_t(ft->create_time))));
5865 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5870 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX,
5871 convert_time_t_to_uint32_t(ft->create_time.tv_sec));
5873 rc = ad_set(handle, ad, smb_fname);
5879 DEBUG(1, ("fruit_ntimes: %s\n", smb_fname_str_dbg(smb_fname)));
5882 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
5885 static int fruit_fallocate(struct vfs_handle_struct *handle,
5886 struct files_struct *fsp,
5891 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5894 return SMB_VFS_NEXT_FALLOCATE(handle, fsp, mode, offset, len);
5897 /* Let the pwrite code path handle it. */
5902 static int fruit_ftruncate_rsrc_xattr(struct vfs_handle_struct *handle,
5903 struct files_struct *fsp,
5906 #ifdef HAVE_ATTROPEN
5907 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
5912 static int fruit_ftruncate_rsrc_adouble(struct vfs_handle_struct *handle,
5913 struct files_struct *fsp,
5917 struct adouble *ad = NULL;
5920 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
5922 DBG_DEBUG("ad_get [%s] failed [%s]\n",
5923 fsp_str_dbg(fsp), strerror(errno));
5927 ad_off = ad_getentryoff(ad, ADEID_RFORK);
5929 rc = ftruncate(fsp->fh->fd, offset + ad_off);
5935 ad_setentrylen(ad, ADEID_RFORK, offset);
5937 rc = ad_fset(handle, ad, fsp);
5939 DBG_ERR("ad_fset [%s] failed [%s]\n",
5940 fsp_str_dbg(fsp), strerror(errno));
5949 static int fruit_ftruncate_rsrc_stream(struct vfs_handle_struct *handle,
5950 struct files_struct *fsp,
5953 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
5956 static int fruit_ftruncate_rsrc(struct vfs_handle_struct *handle,
5957 struct files_struct *fsp,
5960 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5964 DBG_ERR("Failed to fetch fsp extension");
5968 switch (fio->config->rsrc) {
5969 case FRUIT_RSRC_XATTR:
5970 ret = fruit_ftruncate_rsrc_xattr(handle, fsp, offset);
5973 case FRUIT_RSRC_ADFILE:
5974 ret = fruit_ftruncate_rsrc_adouble(handle, fsp, offset);
5977 case FRUIT_RSRC_STREAM:
5978 ret = fruit_ftruncate_rsrc_stream(handle, fsp, offset);
5982 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
5990 static int fruit_ftruncate_meta(struct vfs_handle_struct *handle,
5991 struct files_struct *fsp,
5995 DBG_WARNING("ftruncate %s to %jd",
5996 fsp_str_dbg(fsp), (intmax_t)offset);
5997 /* OS X returns NT_STATUS_ALLOTTED_SPACE_EXCEEDED */
6002 /* OS X returns success but does nothing */
6003 DBG_INFO("ignoring ftruncate %s to %jd\n",
6004 fsp_str_dbg(fsp), (intmax_t)offset);
6008 static int fruit_ftruncate(struct vfs_handle_struct *handle,
6009 struct files_struct *fsp,
6012 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
6015 DBG_DEBUG("Path [%s] offset [%"PRIdMAX"]\n", fsp_str_dbg(fsp),
6019 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
6022 if (fio->type == ADOUBLE_META) {
6023 ret = fruit_ftruncate_meta(handle, fsp, offset);
6025 ret = fruit_ftruncate_rsrc(handle, fsp, offset);
6028 DBG_DEBUG("Path [%s] result [%d]\n", fsp_str_dbg(fsp), ret);
6032 static NTSTATUS fruit_create_file(vfs_handle_struct *handle,
6033 struct smb_request *req,
6034 uint16_t root_dir_fid,
6035 struct smb_filename *smb_fname,
6036 uint32_t access_mask,
6037 uint32_t share_access,
6038 uint32_t create_disposition,
6039 uint32_t create_options,
6040 uint32_t file_attributes,
6041 uint32_t oplock_request,
6042 struct smb2_lease *lease,
6043 uint64_t allocation_size,
6044 uint32_t private_flags,
6045 struct security_descriptor *sd,
6046 struct ea_list *ea_list,
6047 files_struct **result,
6049 const struct smb2_create_blobs *in_context_blobs,
6050 struct smb2_create_blobs *out_context_blobs)
6053 struct fruit_config_data *config = NULL;
6054 files_struct *fsp = NULL;
6055 struct fio *fio = NULL;
6056 bool internal_open = (oplock_request & INTERNAL_OPEN_ONLY);
6059 status = check_aapl(handle, req, in_context_blobs, out_context_blobs);
6060 if (!NT_STATUS_IS_OK(status)) {
6064 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
6065 return NT_STATUS_UNSUCCESSFUL);
6067 if (is_apple_stream(smb_fname) && !internal_open) {
6068 ret = ad_convert(handle, smb_fname);
6070 DBG_ERR("ad_convert() failed\n");
6071 return NT_STATUS_UNSUCCESSFUL;
6075 status = SMB_VFS_NEXT_CREATE_FILE(
6076 handle, req, root_dir_fid, smb_fname,
6077 access_mask, share_access,
6078 create_disposition, create_options,
6079 file_attributes, oplock_request,
6081 allocation_size, private_flags,
6082 sd, ea_list, result,
6083 pinfo, in_context_blobs, out_context_blobs);
6084 if (!NT_STATUS_IS_OK(status)) {
6090 if (global_fruit_config.nego_aapl) {
6091 if (config->posix_rename && fsp->is_directory) {
6093 * Enable POSIX directory rename behaviour
6095 fsp->posix_flags |= FSP_POSIX_FLAGS_RENAME;
6100 * If this is a plain open for existing files, opening an 0
6101 * byte size resource fork MUST fail with
6102 * NT_STATUS_OBJECT_NAME_NOT_FOUND.
6104 * Cf the vfs_fruit torture tests in test_rfork_create().
6106 if (global_fruit_config.nego_aapl &&
6107 create_disposition == FILE_OPEN &&
6108 smb_fname->st.st_ex_size == 0 &&
6109 is_ntfs_stream_smb_fname(smb_fname) &&
6110 !(is_ntfs_default_stream_smb_fname(smb_fname)))
6112 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
6116 fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
6117 if (fio != NULL && pinfo != NULL && *pinfo == FILE_WAS_CREATED) {
6118 fio->created = true;
6121 if (is_ntfs_stream_smb_fname(smb_fname)
6122 || fsp->is_directory) {
6126 if ((config->locking == FRUIT_LOCKING_NETATALK) &&
6129 status = fruit_check_access(
6133 if (!NT_STATUS_IS_OK(status)) {
6141 DEBUG(10, ("fruit_create_file: %s\n", nt_errstr(status)));
6144 close_file(req, fsp, ERROR_CLOSE);
6145 *result = fsp = NULL;
6151 static NTSTATUS fruit_readdir_attr(struct vfs_handle_struct *handle,
6152 const struct smb_filename *fname,
6153 TALLOC_CTX *mem_ctx,
6154 struct readdir_attr_data **pattr_data)
6156 struct fruit_config_data *config = NULL;
6157 struct readdir_attr_data *attr_data;
6161 SMB_VFS_HANDLE_GET_DATA(handle, config,
6162 struct fruit_config_data,
6163 return NT_STATUS_UNSUCCESSFUL);
6165 if (!global_fruit_config.nego_aapl) {
6166 return SMB_VFS_NEXT_READDIR_ATTR(handle, fname, mem_ctx, pattr_data);
6169 DEBUG(10, ("fruit_readdir_attr %s\n", fname->base_name));
6171 ret = ad_convert(handle, fname);
6173 DBG_ERR("ad_convert() failed\n");
6174 return NT_STATUS_UNSUCCESSFUL;
6177 *pattr_data = talloc_zero(mem_ctx, struct readdir_attr_data);
6178 if (*pattr_data == NULL) {
6179 return NT_STATUS_UNSUCCESSFUL;
6181 attr_data = *pattr_data;
6182 attr_data->type = RDATTR_AAPL;
6185 * Mac metadata: compressed FinderInfo, resource fork length
6188 status = readdir_attr_macmeta(handle, fname, attr_data);
6189 if (!NT_STATUS_IS_OK(status)) {
6191 * Error handling is tricky: if we return failure from
6192 * this function, the corresponding directory entry
6193 * will to be passed to the client, so we really just
6194 * want to error out on fatal errors.
6196 if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
6204 if (config->unix_info_enabled) {
6205 attr_data->attr_data.aapl.unix_mode = fname->st.st_ex_mode;
6211 if (!config->readdir_attr_max_access) {
6212 attr_data->attr_data.aapl.max_access = FILE_GENERIC_ALL;
6214 status = smbd_calculate_access_mask(
6218 SEC_FLAG_MAXIMUM_ALLOWED,
6219 &attr_data->attr_data.aapl.max_access);
6220 if (!NT_STATUS_IS_OK(status)) {
6225 return NT_STATUS_OK;
6228 DEBUG(1, ("fruit_readdir_attr %s, error: %s\n",
6229 fname->base_name, nt_errstr(status)));
6230 TALLOC_FREE(*pattr_data);
6234 static NTSTATUS fruit_fget_nt_acl(vfs_handle_struct *handle,
6236 uint32_t security_info,
6237 TALLOC_CTX *mem_ctx,
6238 struct security_descriptor **ppdesc)
6241 struct security_ace ace;
6243 struct fruit_config_data *config;
6245 SMB_VFS_HANDLE_GET_DATA(handle, config,
6246 struct fruit_config_data,
6247 return NT_STATUS_UNSUCCESSFUL);
6249 status = SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
6251 if (!NT_STATUS_IS_OK(status)) {
6256 * Add MS NFS style ACEs with uid, gid and mode
6258 if (!global_fruit_config.nego_aapl) {
6259 return NT_STATUS_OK;
6261 if (!config->unix_info_enabled) {
6262 return NT_STATUS_OK;
6265 /* First remove any existing ACE's with NFS style mode/uid/gid SIDs. */
6266 status = remove_virtual_nfs_aces(*ppdesc);
6267 if (!NT_STATUS_IS_OK(status)) {
6268 DBG_WARNING("failed to remove MS NFS style ACEs\n");
6272 /* MS NFS style mode */
6273 sid_compose(&sid, &global_sid_Unix_NFS_Mode, fsp->fsp_name->st.st_ex_mode);
6274 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6275 status = security_descriptor_dacl_add(*ppdesc, &ace);
6276 if (!NT_STATUS_IS_OK(status)) {
6277 DEBUG(1,("failed to add MS NFS style ACE\n"));
6281 /* MS NFS style uid */
6282 sid_compose(&sid, &global_sid_Unix_NFS_Users, fsp->fsp_name->st.st_ex_uid);
6283 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6284 status = security_descriptor_dacl_add(*ppdesc, &ace);
6285 if (!NT_STATUS_IS_OK(status)) {
6286 DEBUG(1,("failed to add MS NFS style ACE\n"));
6290 /* MS NFS style gid */
6291 sid_compose(&sid, &global_sid_Unix_NFS_Groups, fsp->fsp_name->st.st_ex_gid);
6292 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6293 status = security_descriptor_dacl_add(*ppdesc, &ace);
6294 if (!NT_STATUS_IS_OK(status)) {
6295 DEBUG(1,("failed to add MS NFS style ACE\n"));
6299 return NT_STATUS_OK;
6302 static NTSTATUS fruit_fset_nt_acl(vfs_handle_struct *handle,
6304 uint32_t security_info_sent,
6305 const struct security_descriptor *orig_psd)
6309 mode_t ms_nfs_mode = 0;
6311 struct security_descriptor *psd = NULL;
6312 uint32_t orig_num_aces = 0;
6314 if (orig_psd->dacl != NULL) {
6315 orig_num_aces = orig_psd->dacl->num_aces;
6318 psd = security_descriptor_copy(talloc_tos(), orig_psd);
6320 return NT_STATUS_NO_MEMORY;
6323 DBG_DEBUG("fruit_fset_nt_acl: %s\n", fsp_str_dbg(fsp));
6325 status = check_ms_nfs(handle, fsp, psd, &ms_nfs_mode, &do_chmod);
6326 if (!NT_STATUS_IS_OK(status)) {
6327 DEBUG(1, ("fruit_fset_nt_acl: check_ms_nfs failed%s\n", fsp_str_dbg(fsp)));
6333 * If only ms_nfs ACE entries were sent, ensure we set the DACL
6334 * sent/present flags correctly now we've removed them.
6337 if (orig_num_aces != 0) {
6339 * Are there any ACE's left ?
6341 if (psd->dacl->num_aces == 0) {
6342 /* No - clear the DACL sent/present flags. */
6343 security_info_sent &= ~SECINFO_DACL;
6344 psd->type &= ~SEC_DESC_DACL_PRESENT;
6348 status = SMB_VFS_NEXT_FSET_NT_ACL(handle, fsp, security_info_sent, psd);
6349 if (!NT_STATUS_IS_OK(status)) {
6350 DEBUG(1, ("fruit_fset_nt_acl: SMB_VFS_NEXT_FSET_NT_ACL failed%s\n", fsp_str_dbg(fsp)));
6356 if (fsp->fh->fd != -1) {
6357 result = SMB_VFS_FCHMOD(fsp, ms_nfs_mode);
6359 result = SMB_VFS_CHMOD(fsp->conn,
6365 DEBUG(1, ("chmod: %s, result: %d, %04o error %s\n", fsp_str_dbg(fsp),
6366 result, (unsigned)ms_nfs_mode,
6368 status = map_nt_error_from_unix(errno);
6375 return NT_STATUS_OK;
6378 static struct vfs_offload_ctx *fruit_offload_ctx;
6380 struct fruit_offload_read_state {
6381 struct vfs_handle_struct *handle;
6382 struct tevent_context *ev;
6388 static void fruit_offload_read_done(struct tevent_req *subreq);
6390 static struct tevent_req *fruit_offload_read_send(
6391 TALLOC_CTX *mem_ctx,
6392 struct tevent_context *ev,
6393 struct vfs_handle_struct *handle,
6400 struct tevent_req *req = NULL;
6401 struct tevent_req *subreq = NULL;
6402 struct fruit_offload_read_state *state = NULL;
6404 req = tevent_req_create(mem_ctx, &state,
6405 struct fruit_offload_read_state);
6409 *state = (struct fruit_offload_read_state) {
6416 subreq = SMB_VFS_NEXT_OFFLOAD_READ_SEND(mem_ctx, ev, handle, fsp,
6417 fsctl, ttl, offset, to_copy);
6418 if (tevent_req_nomem(subreq, req)) {
6419 return tevent_req_post(req, ev);
6421 tevent_req_set_callback(subreq, fruit_offload_read_done, req);
6425 static void fruit_offload_read_done(struct tevent_req *subreq)
6427 struct tevent_req *req = tevent_req_callback_data(
6428 subreq, struct tevent_req);
6429 struct fruit_offload_read_state *state = tevent_req_data(
6430 req, struct fruit_offload_read_state);
6433 status = SMB_VFS_NEXT_OFFLOAD_READ_RECV(subreq,
6437 TALLOC_FREE(subreq);
6438 if (tevent_req_nterror(req, status)) {
6442 if (state->fsctl != FSCTL_SRV_REQUEST_RESUME_KEY) {
6443 tevent_req_done(req);
6447 status = vfs_offload_token_ctx_init(state->fsp->conn->sconn->client,
6448 &fruit_offload_ctx);
6449 if (tevent_req_nterror(req, status)) {
6453 status = vfs_offload_token_db_store_fsp(fruit_offload_ctx,
6456 if (tevent_req_nterror(req, status)) {
6460 tevent_req_done(req);
6464 static NTSTATUS fruit_offload_read_recv(struct tevent_req *req,
6465 struct vfs_handle_struct *handle,
6466 TALLOC_CTX *mem_ctx,
6469 struct fruit_offload_read_state *state = tevent_req_data(
6470 req, struct fruit_offload_read_state);
6473 if (tevent_req_is_nterror(req, &status)) {
6474 tevent_req_received(req);
6478 token->length = state->token.length;
6479 token->data = talloc_move(mem_ctx, &state->token.data);
6481 tevent_req_received(req);
6482 return NT_STATUS_OK;
6485 struct fruit_offload_write_state {
6486 struct vfs_handle_struct *handle;
6488 struct files_struct *src_fsp;
6489 struct files_struct *dst_fsp;
6493 static void fruit_offload_write_done(struct tevent_req *subreq);
6494 static struct tevent_req *fruit_offload_write_send(struct vfs_handle_struct *handle,
6495 TALLOC_CTX *mem_ctx,
6496 struct tevent_context *ev,
6499 off_t transfer_offset,
6500 struct files_struct *dest_fsp,
6504 struct tevent_req *req, *subreq;
6505 struct fruit_offload_write_state *state;
6507 struct fruit_config_data *config;
6508 off_t src_off = transfer_offset;
6509 files_struct *src_fsp = NULL;
6510 off_t to_copy = num;
6511 bool copyfile_enabled = false;
6513 DEBUG(10,("soff: %ju, doff: %ju, len: %ju\n",
6514 (uintmax_t)src_off, (uintmax_t)dest_off, (uintmax_t)num));
6516 SMB_VFS_HANDLE_GET_DATA(handle, config,
6517 struct fruit_config_data,
6520 req = tevent_req_create(mem_ctx, &state,
6521 struct fruit_offload_write_state);
6525 state->handle = handle;
6526 state->dst_fsp = dest_fsp;
6529 case FSCTL_SRV_COPYCHUNK:
6530 case FSCTL_SRV_COPYCHUNK_WRITE:
6531 copyfile_enabled = config->copyfile_enabled;
6538 * Check if this a OS X copyfile style copychunk request with
6539 * a requested chunk count of 0 that was translated to a
6540 * offload_write_send VFS call overloading the parameters src_off
6541 * = dest_off = num = 0.
6543 if (copyfile_enabled && num == 0 && src_off == 0 && dest_off == 0) {
6544 status = vfs_offload_token_db_fetch_fsp(
6545 fruit_offload_ctx, token, &src_fsp);
6546 if (tevent_req_nterror(req, status)) {
6547 return tevent_req_post(req, ev);
6549 state->src_fsp = src_fsp;
6551 status = vfs_stat_fsp(src_fsp);
6552 if (tevent_req_nterror(req, status)) {
6553 return tevent_req_post(req, ev);
6556 to_copy = src_fsp->fsp_name->st.st_ex_size;
6557 state->is_copyfile = true;
6560 subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle,
6569 if (tevent_req_nomem(subreq, req)) {
6570 return tevent_req_post(req, ev);
6573 tevent_req_set_callback(subreq, fruit_offload_write_done, req);
6577 static void fruit_offload_write_done(struct tevent_req *subreq)
6579 struct tevent_req *req = tevent_req_callback_data(
6580 subreq, struct tevent_req);
6581 struct fruit_offload_write_state *state = tevent_req_data(
6582 req, struct fruit_offload_write_state);
6584 unsigned int num_streams = 0;
6585 struct stream_struct *streams = NULL;
6587 struct smb_filename *src_fname_tmp = NULL;
6588 struct smb_filename *dst_fname_tmp = NULL;
6590 status = SMB_VFS_NEXT_OFFLOAD_WRITE_RECV(state->handle,
6593 TALLOC_FREE(subreq);
6594 if (tevent_req_nterror(req, status)) {
6598 if (!state->is_copyfile) {
6599 tevent_req_done(req);
6604 * Now copy all remaining streams. We know the share supports
6605 * streams, because we're in vfs_fruit. We don't do this async
6606 * because streams are few and small.
6608 status = vfs_streaminfo(state->handle->conn, state->src_fsp,
6609 state->src_fsp->fsp_name,
6610 req, &num_streams, &streams);
6611 if (tevent_req_nterror(req, status)) {
6615 if (num_streams == 1) {
6616 /* There is always one stream, ::$DATA. */
6617 tevent_req_done(req);
6621 for (i = 0; i < num_streams; i++) {
6622 DEBUG(10, ("%s: stream: '%s'/%zu\n",
6623 __func__, streams[i].name, (size_t)streams[i].size));
6625 src_fname_tmp = synthetic_smb_fname(
6627 state->src_fsp->fsp_name->base_name,
6630 state->src_fsp->fsp_name->flags);
6631 if (tevent_req_nomem(src_fname_tmp, req)) {
6635 if (is_ntfs_default_stream_smb_fname(src_fname_tmp)) {
6636 TALLOC_FREE(src_fname_tmp);
6640 dst_fname_tmp = synthetic_smb_fname(
6642 state->dst_fsp->fsp_name->base_name,
6645 state->dst_fsp->fsp_name->flags);
6646 if (tevent_req_nomem(dst_fname_tmp, req)) {
6647 TALLOC_FREE(src_fname_tmp);
6651 status = copy_file(req,
6652 state->handle->conn,
6655 OPENX_FILE_CREATE_IF_NOT_EXIST,
6657 if (!NT_STATUS_IS_OK(status)) {
6658 DEBUG(1, ("%s: copy %s to %s failed: %s\n", __func__,
6659 smb_fname_str_dbg(src_fname_tmp),
6660 smb_fname_str_dbg(dst_fname_tmp),
6661 nt_errstr(status)));
6662 TALLOC_FREE(src_fname_tmp);
6663 TALLOC_FREE(dst_fname_tmp);
6664 tevent_req_nterror(req, status);
6668 TALLOC_FREE(src_fname_tmp);
6669 TALLOC_FREE(dst_fname_tmp);
6672 TALLOC_FREE(streams);
6673 TALLOC_FREE(src_fname_tmp);
6674 TALLOC_FREE(dst_fname_tmp);
6675 tevent_req_done(req);
6678 static NTSTATUS fruit_offload_write_recv(struct vfs_handle_struct *handle,
6679 struct tevent_req *req,
6682 struct fruit_offload_write_state *state = tevent_req_data(
6683 req, struct fruit_offload_write_state);
6686 if (tevent_req_is_nterror(req, &status)) {
6687 DEBUG(1, ("server side copy chunk failed: %s\n",
6688 nt_errstr(status)));
6690 tevent_req_received(req);
6694 *copied = state->copied;
6695 tevent_req_received(req);
6697 return NT_STATUS_OK;
6700 static char *fruit_get_bandsize_line(char **lines, int numlines)
6703 static bool re_initialized = false;
6707 if (!re_initialized) {
6708 ret = regcomp(&re, "^[[:blank:]]*<key>band-size</key>$", 0);
6712 re_initialized = true;
6715 for (i = 0; i < numlines; i++) {
6716 regmatch_t matches[1];
6718 ret = regexec(&re, lines[i], 1, matches, 0);
6721 * Check if the match was on the last line, sa we want
6722 * the subsequent line.
6724 if (i + 1 == numlines) {
6727 return lines[i + 1];
6729 if (ret != REG_NOMATCH) {
6737 static bool fruit_get_bandsize_from_line(char *line, size_t *_band_size)
6740 static bool re_initialized = false;
6741 regmatch_t matches[2];
6746 if (!re_initialized) {
6749 "<integer>\\([[:digit:]]*\\)</integer>$",
6754 re_initialized = true;
6757 ret = regexec(&re, line, 2, matches, 0);
6759 DBG_ERR("regex failed [%s]\n", line);
6763 line[matches[1].rm_eo] = '\0';
6765 ok = conv_str_u64(&line[matches[1].rm_so], &band_size);
6769 *_band_size = (size_t)band_size;
6774 * This reads and parses an Info.plist from a TM sparsebundle looking for the
6775 * "band-size" key and value.
6777 static bool fruit_get_bandsize(vfs_handle_struct *handle,
6781 #define INFO_PLIST_MAX_SIZE 64*1024
6783 struct smb_filename *smb_fname = NULL;
6784 files_struct *fsp = NULL;
6785 uint8_t *file_data = NULL;
6786 char **lines = NULL;
6787 char *band_size_line = NULL;
6788 size_t plist_file_size;
6795 plist = talloc_asprintf(talloc_tos(),
6797 handle->conn->connectpath,
6799 if (plist == NULL) {
6804 smb_fname = synthetic_smb_fname(talloc_tos(), plist, NULL, NULL, 0);
6805 if (smb_fname == NULL) {
6810 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
6812 DBG_INFO("Ignoring Sparsebundle without Info.plist [%s]\n", dir);
6817 plist_file_size = smb_fname->st.st_ex_size;
6819 if (plist_file_size > INFO_PLIST_MAX_SIZE) {
6820 DBG_INFO("%s is too large, ignoring\n", plist);
6825 status = SMB_VFS_NEXT_CREATE_FILE(
6828 0, /* root_dir_fid */
6829 smb_fname, /* fname */
6830 FILE_GENERIC_READ, /* access_mask */
6831 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
6832 FILE_OPEN, /* create_disposition */
6833 0, /* create_options */
6834 0, /* file_attributes */
6835 INTERNAL_OPEN_ONLY, /* oplock_request */
6837 0, /* allocation_size */
6838 0, /* private_flags */
6843 NULL, NULL); /* create context */
6844 if (!NT_STATUS_IS_OK(status)) {
6845 DBG_INFO("Opening [%s] failed [%s]\n",
6846 smb_fname_str_dbg(smb_fname), nt_errstr(status));
6851 file_data = talloc_array(talloc_tos(), uint8_t, plist_file_size);
6852 if (file_data == NULL) {
6857 nread = SMB_VFS_NEXT_PREAD(handle, fsp, file_data, plist_file_size, 0);
6858 if (nread != plist_file_size) {
6859 DBG_ERR("Short read on [%s]: %zu/%zd\n",
6860 fsp_str_dbg(fsp), nread, plist_file_size);
6866 status = close_file(NULL, fsp, NORMAL_CLOSE);
6868 if (!NT_STATUS_IS_OK(status)) {
6869 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
6874 lines = file_lines_parse((char *)file_data,
6878 if (lines == NULL) {
6883 band_size_line = fruit_get_bandsize_line(lines, numlines);
6884 if (band_size_line == NULL) {
6885 DBG_ERR("Didn't find band-size key in [%s]\n",
6886 smb_fname_str_dbg(smb_fname));
6891 ok = fruit_get_bandsize_from_line(band_size_line, band_size);
6893 DBG_ERR("fruit_get_bandsize_from_line failed\n");
6897 DBG_DEBUG("Parsed band-size [%zu] for [%s]\n", *band_size, plist);
6901 status = close_file(NULL, fsp, NORMAL_CLOSE);
6902 if (!NT_STATUS_IS_OK(status)) {
6903 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
6908 TALLOC_FREE(smb_fname);
6909 TALLOC_FREE(file_data);
6914 struct fruit_disk_free_state {
6918 static bool fruit_get_num_bands(vfs_handle_struct *handle,
6923 struct smb_filename *bands_dir = NULL;
6925 struct dirent *e = NULL;
6929 path = talloc_asprintf(talloc_tos(),
6931 handle->conn->connectpath,
6937 bands_dir = synthetic_smb_fname(talloc_tos(),
6943 if (bands_dir == NULL) {
6947 d = SMB_VFS_NEXT_OPENDIR(handle, bands_dir, NULL, 0);
6949 TALLOC_FREE(bands_dir);
6955 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
6957 e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
6959 if (ISDOT(e->d_name) || ISDOTDOT(e->d_name)) {
6965 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
6967 TALLOC_FREE(bands_dir);
6971 DBG_DEBUG("%zu bands in [%s]\n", nbands, smb_fname_str_dbg(bands_dir));
6973 TALLOC_FREE(bands_dir);
6979 static bool fruit_tmsize_do_dirent(vfs_handle_struct *handle,
6980 struct fruit_disk_free_state *state,
6985 size_t sparsebundle_strlen = strlen("sparsebundle");
6986 size_t bandsize = 0;
6990 p = strstr(e->d_name, "sparsebundle");
6995 if (p[sparsebundle_strlen] != '\0') {
6999 DBG_DEBUG("Processing sparsebundle [%s]\n", e->d_name);
7001 ok = fruit_get_bandsize(handle, e->d_name, &bandsize);
7004 * Beware of race conditions: this may be an uninitialized
7005 * Info.plist that a client is just creating. We don't want let
7006 * this to trigger complete failure.
7008 DBG_ERR("Processing sparsebundle [%s] failed\n", e->d_name);
7012 ok = fruit_get_num_bands(handle, e->d_name, &nbands);
7015 * Beware of race conditions: this may be a backup sparsebundle
7016 * in an early stage lacking a bands subdirectory. We don't want
7017 * let this to trigger complete failure.
7019 DBG_ERR("Processing sparsebundle [%s] failed\n", e->d_name);
7023 if (bandsize > SIZE_MAX/nbands) {
7024 DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
7028 tm_size = bandsize * nbands;
7030 if (state->total_size + tm_size < state->total_size) {
7031 DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
7036 state->total_size += tm_size;
7038 DBG_DEBUG("[%s] tm_size [%jd] total_size [%jd]\n",
7039 e->d_name, (intmax_t)tm_size, (intmax_t)state->total_size);
7045 * Calculate used size of a TimeMachine volume
7047 * This assumes that the volume is used only for TimeMachine.
7049 * - readdir(basedir of share), then
7050 * - for every element that matches regex "^\(.*\)\.sparsebundle$" :
7051 * - parse "\1.sparsebundle/Info.plist" and read the band-size XML key
7052 * - count band files in "\1.sparsebundle/bands/"
7053 * - calculate used size of all bands: band_count * band_size
7055 static uint64_t fruit_disk_free(vfs_handle_struct *handle,
7056 const struct smb_filename *smb_fname,
7061 struct fruit_config_data *config = NULL;
7062 struct fruit_disk_free_state state = {0};
7064 struct dirent *e = NULL;
7070 SMB_VFS_HANDLE_GET_DATA(handle, config,
7071 struct fruit_config_data,
7074 if (!config->time_machine ||
7075 config->time_machine_max_size == 0)
7077 return SMB_VFS_NEXT_DISK_FREE(handle,
7084 d = SMB_VFS_NEXT_OPENDIR(handle, smb_fname, NULL, 0);
7089 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
7091 e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
7093 ok = fruit_tmsize_do_dirent(handle, &state, e);
7095 SMB_VFS_NEXT_CLOSEDIR(handle, d);
7100 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
7105 dsize = config->time_machine_max_size / 512;
7106 dfree = dsize - (state.total_size / 512);
7107 if (dfree > dsize) {
7117 static struct vfs_fn_pointers vfs_fruit_fns = {
7118 .connect_fn = fruit_connect,
7119 .disk_free_fn = fruit_disk_free,
7121 /* File operations */
7122 .chmod_fn = fruit_chmod,
7123 .chown_fn = fruit_chown,
7124 .unlink_fn = fruit_unlink,
7125 .rename_fn = fruit_rename,
7126 .rmdir_fn = fruit_rmdir,
7127 .open_fn = fruit_open,
7128 .close_fn = fruit_close,
7129 .pread_fn = fruit_pread,
7130 .pwrite_fn = fruit_pwrite,
7131 .pread_send_fn = fruit_pread_send,
7132 .pread_recv_fn = fruit_pread_recv,
7133 .pwrite_send_fn = fruit_pwrite_send,
7134 .pwrite_recv_fn = fruit_pwrite_recv,
7135 .stat_fn = fruit_stat,
7136 .lstat_fn = fruit_lstat,
7137 .fstat_fn = fruit_fstat,
7138 .streaminfo_fn = fruit_streaminfo,
7139 .ntimes_fn = fruit_ntimes,
7140 .ftruncate_fn = fruit_ftruncate,
7141 .fallocate_fn = fruit_fallocate,
7142 .create_file_fn = fruit_create_file,
7143 .readdir_attr_fn = fruit_readdir_attr,
7144 .offload_read_send_fn = fruit_offload_read_send,
7145 .offload_read_recv_fn = fruit_offload_read_recv,
7146 .offload_write_send_fn = fruit_offload_write_send,
7147 .offload_write_recv_fn = fruit_offload_write_recv,
7149 /* NT ACL operations */
7150 .fget_nt_acl_fn = fruit_fget_nt_acl,
7151 .fset_nt_acl_fn = fruit_fset_nt_acl,
7155 NTSTATUS vfs_fruit_init(TALLOC_CTX *ctx)
7157 NTSTATUS ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "fruit",
7159 if (!NT_STATUS_IS_OK(ret)) {
7163 vfs_fruit_debug_level = debug_add_class("fruit");
7164 if (vfs_fruit_debug_level == -1) {
7165 vfs_fruit_debug_level = DBGC_VFS;
7166 DEBUG(0, ("%s: Couldn't register custom debugging class!\n",
7169 DEBUG(10, ("%s: Debug class number of '%s': %d\n",
7170 "vfs_fruit_init","fruit",vfs_fruit_debug_level));