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 "../lib/crypto/md5.h"
26 #include "system/shmem.h"
27 #include "locking/proto.h"
28 #include "smbd/globals.h"
30 #include "libcli/security/security.h"
31 #include "../libcli/smb/smb2_create_ctx.h"
32 #include "lib/util/sys_rw.h"
33 #include "lib/util/tevent_ntstatus.h"
34 #include "lib/util/tevent_unix.h"
35 #include "offload_token.h"
36 #include "string_replace.h"
39 * Enhanced OS X and Netatalk compatibility
40 * ========================================
42 * This modules takes advantage of vfs_streams_xattr and
43 * vfs_catia. VFS modules vfs_fruit and vfs_streams_xattr must be
44 * loaded in the correct order:
46 * vfs modules = catia fruit streams_xattr
48 * The module intercepts the OS X special streams "AFP_AfpInfo" and
49 * "AFP_Resource" and handles them in a special way. All other named
50 * streams are deferred to vfs_streams_xattr.
52 * The OS X client maps all NTFS illegal characters to the Unicode
53 * private range. This module optionally stores the charcters using
54 * their native ASCII encoding using vfs_catia. If you're not enabling
55 * this feature, you can skip catia from vfs modules.
57 * Finally, open modes are optionally checked against Netatalk AFP
60 * The "AFP_AfpInfo" named stream is a binary blob containing OS X
61 * extended metadata for files and directories. This module optionally
62 * reads and stores this metadata in a way compatible with Netatalk 3
63 * which stores the metadata in an EA "org.netatalk.metadata". Cf
64 * source3/include/MacExtensions.h for a description of the binary
67 * The "AFP_Resource" named stream may be arbitrarily large, thus it
68 * can't be stored in an xattr on most filesystem. ZFS on Solaris is
69 * the only available filesystem where xattrs can be of any size and
70 * the OS supports using the file APIs for xattrs.
72 * The AFP_Resource stream is stored in an AppleDouble file prepending
73 * "._" to the filename. On Solaris with ZFS the stream is optionally
74 * stored in an EA "org.netatalk.resource".
80 * The OS X SMB client sends xattrs as ADS too. For xattr interop with
81 * other protocols you may want to adjust the xattr names the VFS
82 * module vfs_streams_xattr uses for storing ADS's. This defaults to
83 * user.DosStream.ADS_NAME:$DATA and can be changed by specifying
84 * these module parameters:
86 * streams_xattr:prefix = user.
87 * streams_xattr:store_stream_type = false
93 * - log diagnostic if any needed VFS module is not loaded
94 * (eg with lp_vfs_objects())
98 static int vfs_fruit_debug_level = DBGC_VFS;
100 static struct global_fruit_config {
101 bool nego_aapl; /* client negotiated AAPL */
103 } global_fruit_config;
106 #define DBGC_CLASS vfs_fruit_debug_level
108 #define FRUIT_PARAM_TYPE_NAME "fruit"
109 #define ADOUBLE_NAME_PREFIX "._"
111 #define NETATALK_META_XATTR "org.netatalk.Metadata"
112 #define NETATALK_RSRC_XATTR "org.netatalk.ResourceFork"
114 #if defined(HAVE_ATTROPEN)
115 #define AFPINFO_EA_NETATALK NETATALK_META_XATTR
116 #define AFPRESOURCE_EA_NETATALK NETATALK_RSRC_XATTR
118 #define AFPINFO_EA_NETATALK "user." NETATALK_META_XATTR
119 #define AFPRESOURCE_EA_NETATALK "user." NETATALK_RSRC_XATTR
122 enum apple_fork {APPLE_FORK_DATA, APPLE_FORK_RSRC};
124 enum fruit_rsrc {FRUIT_RSRC_STREAM, FRUIT_RSRC_ADFILE, FRUIT_RSRC_XATTR};
125 enum fruit_meta {FRUIT_META_STREAM, FRUIT_META_NETATALK};
126 enum fruit_locking {FRUIT_LOCKING_NETATALK, FRUIT_LOCKING_NONE};
127 enum fruit_encoding {FRUIT_ENC_NATIVE, FRUIT_ENC_PRIVATE};
129 struct fruit_config_data {
130 enum fruit_rsrc rsrc;
131 enum fruit_meta meta;
132 enum fruit_locking locking;
133 enum fruit_encoding encoding;
134 bool use_aapl; /* config from smb.conf */
136 bool readdir_attr_enabled;
137 bool unix_info_enabled;
138 bool copyfile_enabled;
139 bool veto_appledouble;
141 bool aapl_zero_file_id;
144 off_t time_machine_max_size;
145 bool wipe_intentionally_left_blank_rfork;
148 * Additional options, all enabled by default,
149 * possibly useful for analyzing performance. The associated
150 * operations with each of them may be expensive, so having
151 * the chance to disable them individually gives a chance
152 * tweaking the setup for the particular usecase.
154 bool readdir_attr_rsize;
155 bool readdir_attr_finder_info;
156 bool readdir_attr_max_access;
159 static const struct enum_list fruit_rsrc[] = {
160 {FRUIT_RSRC_STREAM, "stream"}, /* pass on to vfs_streams_xattr */
161 {FRUIT_RSRC_ADFILE, "file"}, /* ._ AppleDouble file */
162 {FRUIT_RSRC_XATTR, "xattr"}, /* Netatalk compatible xattr (ZFS only) */
166 static const struct enum_list fruit_meta[] = {
167 {FRUIT_META_STREAM, "stream"}, /* pass on to vfs_streams_xattr */
168 {FRUIT_META_NETATALK, "netatalk"}, /* Netatalk compatible xattr */
172 static const struct enum_list fruit_locking[] = {
173 {FRUIT_LOCKING_NETATALK, "netatalk"}, /* synchronize locks with Netatalk */
174 {FRUIT_LOCKING_NONE, "none"},
178 static const struct enum_list fruit_encoding[] = {
179 {FRUIT_ENC_NATIVE, "native"}, /* map unicode private chars to ASCII */
180 {FRUIT_ENC_PRIVATE, "private"}, /* keep unicode private chars */
184 static const char *fruit_catia_maps =
185 "0x01:0xf001,0x02:0xf002,0x03:0xf003,0x04:0xf004,"
186 "0x05:0xf005,0x06:0xf006,0x07:0xf007,0x08:0xf008,"
187 "0x09:0xf009,0x0a:0xf00a,0x0b:0xf00b,0x0c:0xf00c,"
188 "0x0d:0xf00d,0x0e:0xf00e,0x0f:0xf00f,0x10:0xf010,"
189 "0x11:0xf011,0x12:0xf012,0x13:0xf013,0x14:0xf014,"
190 "0x15:0xf015,0x16:0xf016,0x17:0xf017,0x18:0xf018,"
191 "0x19:0xf019,0x1a:0xf01a,0x1b:0xf01b,0x1c:0xf01c,"
192 "0x1d:0xf01d,0x1e:0xf01e,0x1f:0xf01f,"
193 "0x22:0xf020,0x2a:0xf021,0x3a:0xf022,0x3c:0xf023,"
194 "0x3e:0xf024,0x3f:0xf025,0x5c:0xf026,0x7c:0xf027,"
197 /*****************************************************************************
198 * Defines, functions and data structures that deal with AppleDouble
199 *****************************************************************************/
202 * There are two AppleDouble blobs we deal with:
204 * - ADOUBLE_META - AppleDouble blob used by Netatalk for storing
205 * metadata in an xattr
207 * - ADOUBLE_RSRC - AppleDouble blob used by OS X and Netatalk in
210 typedef enum {ADOUBLE_META, ADOUBLE_RSRC} adouble_type_t;
213 #define AD_VERSION2 0x00020000
214 #define AD_VERSION AD_VERSION2
217 * AppleDouble entry IDs.
219 #define ADEID_DFORK 1
220 #define ADEID_RFORK 2
222 #define ADEID_COMMENT 4
223 #define ADEID_ICONBW 5
224 #define ADEID_ICONCOL 6
225 #define ADEID_FILEI 7
226 #define ADEID_FILEDATESI 8
227 #define ADEID_FINDERI 9
228 #define ADEID_MACFILEI 10
229 #define ADEID_PRODOSFILEI 11
230 #define ADEID_MSDOSFILEI 12
231 #define ADEID_SHORTNAME 13
232 #define ADEID_AFPFILEI 14
235 /* Private Netatalk entries */
236 #define ADEID_PRIVDEV 16
237 #define ADEID_PRIVINO 17
238 #define ADEID_PRIVSYN 18
239 #define ADEID_PRIVID 19
240 #define ADEID_MAX (ADEID_PRIVID + 1)
243 * These are the real ids for the private entries,
244 * as stored in the adouble file
246 #define AD_DEV 0x80444556
247 #define AD_INO 0x80494E4F
248 #define AD_SYN 0x8053594E
249 #define AD_ID 0x8053567E
251 /* Number of actually used entries */
252 #define ADEID_NUM_XATTR 8
253 #define ADEID_NUM_DOT_UND 2
254 #define ADEID_NUM_RSRC_XATTR 1
256 /* AppleDouble magic */
257 #define AD_APPLESINGLE_MAGIC 0x00051600
258 #define AD_APPLEDOUBLE_MAGIC 0x00051607
259 #define AD_MAGIC AD_APPLEDOUBLE_MAGIC
261 /* Sizes of relevant entry bits */
262 #define ADEDLEN_MAGIC 4
263 #define ADEDLEN_VERSION 4
264 #define ADEDLEN_FILLER 16
265 #define AD_FILLER_TAG "Netatalk " /* should be 16 bytes */
266 #define AD_FILLER_TAG_OSX "Mac OS X " /* should be 16 bytes */
267 #define ADEDLEN_NENTRIES 2
268 #define AD_HEADER_LEN (ADEDLEN_MAGIC + ADEDLEN_VERSION + \
269 ADEDLEN_FILLER + ADEDLEN_NENTRIES) /* 26 */
270 #define AD_ENTRY_LEN_EID 4
271 #define AD_ENTRY_LEN_OFF 4
272 #define AD_ENTRY_LEN_LEN 4
273 #define AD_ENTRY_LEN (AD_ENTRY_LEN_EID + AD_ENTRY_LEN_OFF + AD_ENTRY_LEN_LEN)
276 #define ADEDLEN_NAME 255
277 #define ADEDLEN_COMMENT 200
278 #define ADEDLEN_FILEI 16
279 #define ADEDLEN_FINDERI 32
280 #define ADEDLEN_FILEDATESI 16
281 #define ADEDLEN_SHORTNAME 12 /* length up to 8.3 */
282 #define ADEDLEN_AFPFILEI 4
283 #define ADEDLEN_MACFILEI 4
284 #define ADEDLEN_PRODOSFILEI 8
285 #define ADEDLEN_MSDOSFILEI 2
286 #define ADEDLEN_DID 4
287 #define ADEDLEN_PRIVDEV 8
288 #define ADEDLEN_PRIVINO 8
289 #define ADEDLEN_PRIVSYN 8
290 #define ADEDLEN_PRIVID 4
293 #define ADEDOFF_MAGIC 0
294 #define ADEDOFF_VERSION (ADEDOFF_MAGIC + ADEDLEN_MAGIC)
295 #define ADEDOFF_FILLER (ADEDOFF_VERSION + ADEDLEN_VERSION)
296 #define ADEDOFF_NENTRIES (ADEDOFF_FILLER + ADEDLEN_FILLER)
298 #define ADEDOFF_FINDERI_XATTR (AD_HEADER_LEN + \
299 (ADEID_NUM_XATTR * AD_ENTRY_LEN))
300 #define ADEDOFF_COMMENT_XATTR (ADEDOFF_FINDERI_XATTR + ADEDLEN_FINDERI)
301 #define ADEDOFF_FILEDATESI_XATTR (ADEDOFF_COMMENT_XATTR + ADEDLEN_COMMENT)
302 #define ADEDOFF_AFPFILEI_XATTR (ADEDOFF_FILEDATESI_XATTR + \
304 #define ADEDOFF_PRIVDEV_XATTR (ADEDOFF_AFPFILEI_XATTR + ADEDLEN_AFPFILEI)
305 #define ADEDOFF_PRIVINO_XATTR (ADEDOFF_PRIVDEV_XATTR + ADEDLEN_PRIVDEV)
306 #define ADEDOFF_PRIVSYN_XATTR (ADEDOFF_PRIVINO_XATTR + ADEDLEN_PRIVINO)
307 #define ADEDOFF_PRIVID_XATTR (ADEDOFF_PRIVSYN_XATTR + ADEDLEN_PRIVSYN)
309 #define ADEDOFF_FINDERI_DOT_UND (AD_HEADER_LEN + \
310 (ADEID_NUM_DOT_UND * AD_ENTRY_LEN))
311 #define ADEDOFF_RFORK_DOT_UND (ADEDOFF_FINDERI_DOT_UND + ADEDLEN_FINDERI)
313 #define AD_DATASZ_XATTR (AD_HEADER_LEN + \
314 (ADEID_NUM_XATTR * AD_ENTRY_LEN) + \
315 ADEDLEN_FINDERI + ADEDLEN_COMMENT + \
316 ADEDLEN_FILEDATESI + ADEDLEN_AFPFILEI + \
317 ADEDLEN_PRIVDEV + ADEDLEN_PRIVINO + \
318 ADEDLEN_PRIVSYN + ADEDLEN_PRIVID)
320 #if AD_DATASZ_XATTR != 402
321 #error bad size for AD_DATASZ_XATTR
324 #define AD_DATASZ_DOT_UND (AD_HEADER_LEN + \
325 (ADEID_NUM_DOT_UND * AD_ENTRY_LEN) + \
327 #if AD_DATASZ_DOT_UND != 82
328 #error bad size for AD_DATASZ_DOT_UND
332 * Sharemode locks fcntl() offsets
334 #if _FILE_OFFSET_BITS == 64 || defined(HAVE_LARGEFILE)
335 #define AD_FILELOCK_BASE (UINT64_C(0x7FFFFFFFFFFFFFFF) - 9)
337 #define AD_FILELOCK_BASE (UINT32_C(0x7FFFFFFF) - 9)
339 #define BYTELOCK_MAX (AD_FILELOCK_BASE - 1)
341 #define AD_FILELOCK_OPEN_WR (AD_FILELOCK_BASE + 0)
342 #define AD_FILELOCK_OPEN_RD (AD_FILELOCK_BASE + 1)
343 #define AD_FILELOCK_RSRC_OPEN_WR (AD_FILELOCK_BASE + 2)
344 #define AD_FILELOCK_RSRC_OPEN_RD (AD_FILELOCK_BASE + 3)
345 #define AD_FILELOCK_DENY_WR (AD_FILELOCK_BASE + 4)
346 #define AD_FILELOCK_DENY_RD (AD_FILELOCK_BASE + 5)
347 #define AD_FILELOCK_RSRC_DENY_WR (AD_FILELOCK_BASE + 6)
348 #define AD_FILELOCK_RSRC_DENY_RD (AD_FILELOCK_BASE + 7)
349 #define AD_FILELOCK_OPEN_NONE (AD_FILELOCK_BASE + 8)
350 #define AD_FILELOCK_RSRC_OPEN_NONE (AD_FILELOCK_BASE + 9)
352 /* Time stuff we overload the bits a little */
353 #define AD_DATE_CREATE 0
354 #define AD_DATE_MODIFY 4
355 #define AD_DATE_BACKUP 8
356 #define AD_DATE_ACCESS 12
357 #define AD_DATE_MASK (AD_DATE_CREATE | AD_DATE_MODIFY | \
358 AD_DATE_BACKUP | AD_DATE_ACCESS)
359 #define AD_DATE_UNIX (1 << 10)
360 #define AD_DATE_START 0x80000000
361 #define AD_DATE_DELTA 946684800
362 #define AD_DATE_FROM_UNIX(x) (htonl((x) - AD_DATE_DELTA))
363 #define AD_DATE_TO_UNIX(x) (ntohl(x) + AD_DATE_DELTA)
365 #define AD_XATTR_HDR_MAGIC 0x41545452 /* 'ATTR' */
366 #define AD_XATTR_MAX_ENTRIES 1024 /* Some arbitrarily enforced limit */
367 #define AD_XATTR_HDR_SIZE 36
368 #define AD_XATTR_MAX_HDR_SIZE 65536
370 /* Accessor macros */
371 #define ad_getentrylen(ad,eid) ((ad)->ad_eid[(eid)].ade_len)
372 #define ad_getentryoff(ad,eid) ((ad)->ad_eid[(eid)].ade_off)
373 #define ad_setentrylen(ad,eid,len) ((ad)->ad_eid[(eid)].ade_len = (len))
374 #define ad_setentryoff(ad,eid,off) ((ad)->ad_eid[(eid)].ade_off = (off))
377 * Both struct ad_xattr_header and struct ad_xattr_entry describe the in memory
378 * representation as well as the on-disk format.
380 * The ad_xattr_header follows the FinderInfo data in the FinderInfo entry if
381 * the length of the FinderInfo entry is larger then 32 bytes. It is then
382 * preceeded with 2 bytes padding.
384 * Cf: https://opensource.apple.com/source/xnu/xnu-4570.1.46/bsd/vfs/vfs_xattr.c
387 struct ad_xattr_header {
388 uint32_t adx_magic; /* ATTR_HDR_MAGIC */
389 uint32_t adx_debug_tag; /* for debugging == file id of owning file */
390 uint32_t adx_total_size; /* file offset of end of attribute header + entries + data */
391 uint32_t adx_data_start; /* file offset to attribute data area */
392 uint32_t adx_data_length; /* length of attribute data area */
393 uint32_t adx_reserved[3];
395 uint16_t adx_num_attrs;
398 /* On-disk entries are aligned on 4 byte boundaries */
399 struct ad_xattr_entry {
400 uint32_t adx_offset; /* file offset to data */
401 uint32_t adx_length; /* size of attribute data */
403 uint8_t adx_namelen; /* included the NULL terminator */
404 char *adx_name; /* NULL-terminated UTF-8 name */
413 vfs_handle_struct *ad_handle;
416 adouble_type_t ad_type;
419 uint8_t ad_filler[ADEDLEN_FILLER];
420 struct ad_entry ad_eid[ADEID_MAX];
422 struct ad_xattr_header adx_header;
423 struct ad_xattr_entry *adx_entries;
426 struct ad_entry_order {
427 uint32_t id, offset, len;
430 /* Netatalk AppleDouble metadata xattr */
432 struct ad_entry_order entry_order_meta_xattr[ADEID_NUM_XATTR + 1] = {
433 {ADEID_FINDERI, ADEDOFF_FINDERI_XATTR, ADEDLEN_FINDERI},
434 {ADEID_COMMENT, ADEDOFF_COMMENT_XATTR, 0},
435 {ADEID_FILEDATESI, ADEDOFF_FILEDATESI_XATTR, ADEDLEN_FILEDATESI},
436 {ADEID_AFPFILEI, ADEDOFF_AFPFILEI_XATTR, ADEDLEN_AFPFILEI},
437 {ADEID_PRIVDEV, ADEDOFF_PRIVDEV_XATTR, 0},
438 {ADEID_PRIVINO, ADEDOFF_PRIVINO_XATTR, 0},
439 {ADEID_PRIVSYN, ADEDOFF_PRIVSYN_XATTR, 0},
440 {ADEID_PRIVID, ADEDOFF_PRIVID_XATTR, 0},
444 /* AppleDouble resource fork file (the ones prefixed by "._") */
446 struct ad_entry_order entry_order_dot_und[ADEID_NUM_DOT_UND + 1] = {
447 {ADEID_FINDERI, ADEDOFF_FINDERI_DOT_UND, ADEDLEN_FINDERI},
448 {ADEID_RFORK, ADEDOFF_RFORK_DOT_UND, 0},
453 * Fake AppleDouble entry oder for resource fork xattr. The xattr
454 * isn't an AppleDouble file, it simply contains the resource data,
455 * but in order to be able to use some API calls like ad_getentryoff()
456 * we build a fake/helper struct adouble with this entry order struct.
459 struct ad_entry_order entry_order_rsrc_xattr[ADEID_NUM_RSRC_XATTR + 1] = {
464 /* Conversion from enumerated id to on-disk AppleDouble id */
465 #define AD_EID_DISK(a) (set_eid[a])
466 static const uint32_t set_eid[] = {
467 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
468 AD_DEV, AD_INO, AD_SYN, AD_ID
471 static char empty_resourcefork[] = {
472 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
473 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
474 0x54, 0x68, 0x69, 0x73, 0x20, 0x72, 0x65, 0x73,
475 0x6F, 0x75, 0x72, 0x63, 0x65, 0x20, 0x66, 0x6F,
476 0x72, 0x6B, 0x20, 0x69, 0x6E, 0x74, 0x65, 0x6E,
477 0x74, 0x69, 0x6F, 0x6E, 0x61, 0x6C, 0x6C, 0x79,
478 0x20, 0x6C, 0x65, 0x66, 0x74, 0x20, 0x62, 0x6C,
479 0x61, 0x6E, 0x6B, 0x20, 0x20, 0x20, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
495 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
496 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
497 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
498 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
499 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
500 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
501 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
502 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
503 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
504 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
505 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
506 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
507 0x00, 0x1C, 0x00, 0x1E, 0xFF, 0xFF
511 /* tcon config handle */
512 struct fruit_config_data *config;
514 /* Denote stream type, meta or rsrc */
519 * Forward declarations
521 static struct adouble *ad_init(TALLOC_CTX *ctx, vfs_handle_struct *handle,
522 adouble_type_t type);
523 static int ad_set(struct adouble *ad, const struct smb_filename *smb_fname);
524 static int ad_fset(struct adouble *ad, files_struct *fsp);
525 static int adouble_path(TALLOC_CTX *ctx,
526 const struct smb_filename *smb_fname__in,
527 struct smb_filename **ppsmb_fname_out);
528 static AfpInfo *afpinfo_new(TALLOC_CTX *ctx);
529 static ssize_t afpinfo_pack(const AfpInfo *ai, char *buf);
530 static AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data);
534 * Return a pointer to an AppleDouble entry
536 * Returns NULL if the entry is not present
538 static char *ad_get_entry(const struct adouble *ad, int eid)
540 off_t off = ad_getentryoff(ad, eid);
541 size_t len = ad_getentrylen(ad, eid);
543 if (off == 0 || len == 0) {
547 return ad->ad_data + off;
553 static int ad_getdate(const struct adouble *ad,
554 unsigned int dateoff,
557 bool xlate = (dateoff & AD_DATE_UNIX);
560 dateoff &= AD_DATE_MASK;
561 p = ad_get_entry(ad, ADEID_FILEDATESI);
566 if (dateoff > AD_DATE_ACCESS) {
570 memcpy(date, p + dateoff, sizeof(uint32_t));
573 *date = AD_DATE_TO_UNIX(*date);
581 static int ad_setdate(struct adouble *ad, unsigned int dateoff, uint32_t date)
583 bool xlate = (dateoff & AD_DATE_UNIX);
586 p = ad_get_entry(ad, ADEID_FILEDATESI);
591 dateoff &= AD_DATE_MASK;
593 date = AD_DATE_FROM_UNIX(date);
596 if (dateoff > AD_DATE_ACCESS) {
600 memcpy(p + dateoff, &date, sizeof(date));
607 * Map on-disk AppleDouble id to enumerated id
609 static uint32_t get_eid(uint32_t eid)
617 return ADEID_PRIVDEV;
619 return ADEID_PRIVINO;
621 return ADEID_PRIVSYN;
632 * Pack AppleDouble structure into data buffer
634 static bool ad_pack(struct adouble *ad)
641 bufsize = talloc_get_size(ad->ad_data);
642 if (bufsize < AD_DATASZ_DOT_UND) {
643 DBG_ERR("bad buffer size [0x%" PRIx32 "]\n", bufsize);
647 if (offset + ADEDLEN_MAGIC < offset ||
648 offset + ADEDLEN_MAGIC >= bufsize) {
651 RSIVAL(ad->ad_data, offset, ad->ad_magic);
652 offset += ADEDLEN_MAGIC;
654 if (offset + ADEDLEN_VERSION < offset ||
655 offset + ADEDLEN_VERSION >= bufsize) {
658 RSIVAL(ad->ad_data, offset, ad->ad_version);
659 offset += ADEDLEN_VERSION;
661 if (offset + ADEDLEN_FILLER < offset ||
662 offset + ADEDLEN_FILLER >= bufsize) {
665 if (ad->ad_type == ADOUBLE_RSRC) {
666 memcpy(ad->ad_data + offset, AD_FILLER_TAG, ADEDLEN_FILLER);
668 offset += ADEDLEN_FILLER;
670 if (offset + ADEDLEN_NENTRIES < offset ||
671 offset + ADEDLEN_NENTRIES >= bufsize) {
674 offset += ADEDLEN_NENTRIES;
676 for (eid = 0, nent = 0; eid < ADEID_MAX; eid++) {
677 if (ad->ad_eid[eid].ade_off == 0) {
679 * ade_off is also used as indicator whether a
680 * specific entry is used or not
685 if (offset + AD_ENTRY_LEN_EID < offset ||
686 offset + AD_ENTRY_LEN_EID >= bufsize) {
689 RSIVAL(ad->ad_data, offset, AD_EID_DISK(eid));
690 offset += AD_ENTRY_LEN_EID;
692 if (offset + AD_ENTRY_LEN_OFF < offset ||
693 offset + AD_ENTRY_LEN_OFF >= bufsize) {
696 RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_off);
697 offset += AD_ENTRY_LEN_OFF;
699 if (offset + AD_ENTRY_LEN_LEN < offset ||
700 offset + AD_ENTRY_LEN_LEN >= bufsize) {
703 RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_len);
704 offset += AD_ENTRY_LEN_LEN;
709 if (ADEDOFF_NENTRIES + 2 >= bufsize) {
712 RSSVAL(ad->ad_data, ADEDOFF_NENTRIES, nent);
717 static bool ad_unpack_xattrs(struct adouble *ad)
719 struct ad_xattr_header *h = &ad->adx_header;
720 const char *p = ad->ad_data;
724 if (ad_getentrylen(ad, ADEID_FINDERI) <= ADEDLEN_FINDERI) {
728 /* 2 bytes padding */
729 hoff = ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI + 2;
731 h->adx_magic = RIVAL(p, hoff + 0);
732 h->adx_debug_tag = RIVAL(p, hoff + 4); /* Not used -> not checked */
733 h->adx_total_size = RIVAL(p, hoff + 8);
734 h->adx_data_start = RIVAL(p, hoff + 12);
735 h->adx_data_length = RIVAL(p, hoff + 16);
736 h->adx_flags = RSVAL(p, hoff + 32); /* Not used -> not checked */
737 h->adx_num_attrs = RSVAL(p, hoff + 34);
739 if (h->adx_magic != AD_XATTR_HDR_MAGIC) {
740 DBG_ERR("Bad magic: 0x%" PRIx32 "\n", h->adx_magic);
744 if (h->adx_total_size > ad_getentryoff(ad, ADEID_RFORK)) {
745 DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size);
748 if (h->adx_total_size > AD_XATTR_MAX_HDR_SIZE) {
749 DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size);
753 if (h->adx_data_start < (hoff + AD_XATTR_HDR_SIZE)) {
754 DBG_ERR("Bad start: 0x%" PRIx32 "\n", h->adx_data_start);
758 if ((h->adx_data_start + h->adx_data_length) < h->adx_data_start) {
759 DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length);
762 if ((h->adx_data_start + h->adx_data_length) >
763 ad->adx_header.adx_total_size)
765 DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length);
769 if (h->adx_num_attrs > AD_XATTR_MAX_ENTRIES) {
770 DBG_ERR("Bad num xattrs: %" PRIu16 "\n", h->adx_num_attrs);
774 if (h->adx_num_attrs == 0) {
778 ad->adx_entries = talloc_zero_array(
779 ad, struct ad_xattr_entry, h->adx_num_attrs);
780 if (ad->adx_entries == NULL) {
784 hoff += AD_XATTR_HDR_SIZE;
786 for (i = 0; i < h->adx_num_attrs; i++) {
787 struct ad_xattr_entry *e = &ad->adx_entries[i];
789 hoff = (hoff + 3) & ~3;
791 e->adx_offset = RIVAL(p, hoff + 0);
792 e->adx_length = RIVAL(p, hoff + 4);
793 e->adx_flags = RSVAL(p, hoff + 8);
794 e->adx_namelen = *(p + hoff + 10);
796 if (e->adx_offset >= ad->adx_header.adx_total_size) {
797 DBG_ERR("Bad adx_offset: %" PRIx32 "\n",
802 if ((e->adx_offset + e->adx_length) < e->adx_offset) {
803 DBG_ERR("Bad adx_length: %" PRIx32 "\n",
808 if ((e->adx_offset + e->adx_length) >
809 ad->adx_header.adx_total_size)
811 DBG_ERR("Bad adx_length: %" PRIx32 "\n",
816 if (e->adx_namelen == 0) {
817 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
821 if ((hoff + 11 + e->adx_namelen) < hoff + 11) {
822 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
826 if ((hoff + 11 + e->adx_namelen) >
827 ad->adx_header.adx_data_start)
829 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
834 e->adx_name = talloc_strndup(ad->adx_entries,
837 if (e->adx_name == NULL) {
841 DBG_DEBUG("xattr [%s] offset [0x%x] size [0x%x]\n",
842 e->adx_name, e->adx_offset, e->adx_length);
843 dump_data(10, (uint8_t *)(ad->ad_data + e->adx_offset),
846 hoff += 11 + e->adx_namelen;
853 * Unpack an AppleDouble blob into a struct adoble
855 static bool ad_unpack(struct adouble *ad, const size_t nentries,
858 size_t bufsize = talloc_get_size(ad->ad_data);
860 uint32_t eid, len, off;
864 * The size of the buffer ad->ad_data is checked when read, so
865 * we wouldn't have to check our own offsets, a few extra
866 * checks won't hurt though. We have to check the offsets we
867 * read from the buffer anyway.
870 if (bufsize < (AD_HEADER_LEN + (AD_ENTRY_LEN * nentries))) {
871 DEBUG(1, ("bad size\n"));
875 ad->ad_magic = RIVAL(ad->ad_data, 0);
876 ad->ad_version = RIVAL(ad->ad_data, ADEDOFF_VERSION);
877 if ((ad->ad_magic != AD_MAGIC) || (ad->ad_version != AD_VERSION)) {
878 DEBUG(1, ("wrong magic or version\n"));
882 memcpy(ad->ad_filler, ad->ad_data + ADEDOFF_FILLER, ADEDLEN_FILLER);
884 adentries = RSVAL(ad->ad_data, ADEDOFF_NENTRIES);
885 if (adentries != nentries) {
886 DEBUG(1, ("invalid number of entries: %zu\n",
891 /* now, read in the entry bits */
892 for (i = 0; i < adentries; i++) {
893 eid = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN));
895 off = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 4);
896 len = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 8);
898 if (!eid || eid >= ADEID_MAX) {
899 DEBUG(1, ("bogus eid %d\n", eid));
904 * All entries other than the resource fork are
905 * expected to be read into the ad_data buffer, so
906 * ensure the specified offset is within that bound
908 if ((off > bufsize) && (eid != ADEID_RFORK)) {
909 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
915 * All entries besides FinderInfo and resource fork
916 * must fit into the buffer. FinderInfo is special as
917 * it may be larger then the default 32 bytes (if it
918 * contains marshalled xattrs), but we will fixup that
919 * in ad_convert(). And the resource fork is never
920 * accessed directly by the ad_data buf (also see
921 * comment above) anyway.
923 if ((eid != ADEID_RFORK) &&
924 (eid != ADEID_FINDERI) &&
925 ((off + len) > bufsize)) {
926 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
932 * That would be obviously broken
934 if (off > filesize) {
935 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
941 * Check for any entry that has its end beyond the
944 if (off + len < off) {
945 DEBUG(1, ("offset wrap in eid %d: off: %" PRIu32
946 ", len: %" PRIu32 "\n",
951 if (off + len > filesize) {
953 * If this is the resource fork entry, we fix
954 * up the length, for any other entry we bail
957 if (eid != ADEID_RFORK) {
958 DEBUG(1, ("bogus eid %d: off: %" PRIu32
959 ", len: %" PRIu32 "\n",
965 * Fixup the resource fork entry by limiting
966 * the size to entryoffset - filesize.
968 len = filesize - off;
969 DEBUG(1, ("Limiting ADEID_RFORK: off: %" PRIu32
970 ", len: %" PRIu32 "\n", off, len));
973 ad->ad_eid[eid].ade_off = off;
974 ad->ad_eid[eid].ade_len = len;
977 ok = ad_unpack_xattrs(ad);
985 static bool ad_convert_move_reso(struct adouble *ad,
986 const struct smb_filename *smb_fname)
988 char *map = MAP_FAILED;
994 if (ad_getentrylen(ad, ADEID_RFORK) == 0) {
998 maplen = ad_getentryoff(ad, ADEID_RFORK) +
999 ad_getentrylen(ad, ADEID_RFORK);
1001 /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
1002 map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
1004 if (map == MAP_FAILED) {
1005 DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
1010 memmove(map + ADEDOFF_RFORK_DOT_UND,
1011 map + ad_getentryoff(ad, ADEID_RFORK),
1012 ad_getentrylen(ad, ADEID_RFORK));
1014 rc = munmap(map, maplen);
1016 DBG_ERR("munmap failed: %s\n", strerror(errno));
1020 ad_setentryoff(ad, ADEID_RFORK, ADEDOFF_RFORK_DOT_UND);
1024 DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name);
1028 len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
1029 if (len != AD_DATASZ_DOT_UND) {
1030 DBG_ERR("%s: bad size: %zd\n", smb_fname->base_name, len);
1037 static bool ad_convert_xattr(struct adouble *ad,
1038 const struct smb_filename *smb_fname,
1039 bool *converted_xattr)
1041 static struct char_mappings **string_replace_cmaps = NULL;
1042 char *map = MAP_FAILED;
1046 int saved_errno = 0;
1051 *converted_xattr = false;
1053 if (ad_getentrylen(ad, ADEID_FINDERI) == ADEDLEN_FINDERI) {
1057 if (string_replace_cmaps == NULL) {
1058 const char **mappings = NULL;
1060 mappings = str_list_make_v3_const(
1061 talloc_tos(), fruit_catia_maps, NULL);
1062 if (mappings == NULL) {
1065 string_replace_cmaps = string_replace_init_map(mappings);
1066 TALLOC_FREE(mappings);
1069 maplen = ad_getentryoff(ad, ADEID_RFORK) +
1070 ad_getentrylen(ad, ADEID_RFORK);
1072 /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
1073 map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
1075 if (map == MAP_FAILED) {
1076 DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
1080 for (i = 0; i < ad->adx_header.adx_num_attrs; i++) {
1081 struct ad_xattr_entry *e = &ad->adx_entries[i];
1082 char *mapped_name = NULL;
1084 struct smb_filename *stream_name = NULL;
1085 files_struct *fsp = NULL;
1088 status = string_replace_allocate(ad->ad_handle->conn,
1090 string_replace_cmaps,
1093 vfs_translate_to_windows);
1094 if (!NT_STATUS_IS_OK(status) &&
1095 !NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))
1097 DBG_ERR("string_replace_allocate failed\n");
1103 mapped_name = talloc_asprintf(talloc_tos(), ":%s", tmp);
1105 if (mapped_name == NULL) {
1110 stream_name = synthetic_smb_fname(talloc_tos(),
1111 smb_fname->base_name,
1115 TALLOC_FREE(mapped_name);
1116 if (stream_name == NULL) {
1117 DBG_ERR("synthetic_smb_fname failed\n");
1122 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1124 status = SMB_VFS_CREATE_FILE(
1125 ad->ad_handle->conn, /* conn */
1127 0, /* root_dir_fid */
1128 stream_name, /* fname */
1129 FILE_GENERIC_WRITE, /* access_mask */
1130 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
1131 FILE_OPEN_IF, /* create_disposition */
1132 0, /* create_options */
1133 0, /* file_attributes */
1134 INTERNAL_OPEN_ONLY, /* oplock_request */
1136 0, /* allocation_size */
1137 0, /* private_flags */
1142 NULL, NULL); /* create context */
1143 TALLOC_FREE(stream_name);
1144 if (!NT_STATUS_IS_OK(status)) {
1145 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1150 nwritten = SMB_VFS_PWRITE(fsp,
1151 map + e->adx_offset,
1154 if (nwritten == -1) {
1155 DBG_ERR("SMB_VFS_PWRITE failed\n");
1156 saved_errno = errno;
1157 close_file(NULL, fsp, ERROR_CLOSE);
1158 errno = saved_errno;
1163 status = close_file(NULL, fsp, NORMAL_CLOSE);
1164 if (!NT_STATUS_IS_OK(status)) {
1171 ad_setentrylen(ad, ADEID_FINDERI, ADEDLEN_FINDERI);
1175 DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name);
1179 len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
1180 if (len != AD_DATASZ_DOT_UND) {
1181 DBG_ERR("%s: bad size: %zd\n", smb_fname->base_name, len);
1186 ok = ad_convert_move_reso(ad, smb_fname);
1191 *converted_xattr = true;
1195 rc = munmap(map, maplen);
1197 DBG_ERR("munmap failed: %s\n", strerror(errno));
1204 static bool ad_convert_finderinfo(struct adouble *ad,
1205 const struct smb_filename *smb_fname)
1210 struct smb_filename *stream_name = NULL;
1211 files_struct *fsp = NULL;
1215 int saved_errno = 0;
1218 cmp = memcmp(ad->ad_filler, AD_FILLER_TAG_OSX, ADEDLEN_FILLER);
1223 p_ad = ad_get_entry(ad, ADEID_FINDERI);
1228 ai = afpinfo_new(talloc_tos());
1233 memcpy(ai->afpi_FinderInfo, p_ad, ADEDLEN_FINDERI);
1235 aiblob = data_blob_talloc(talloc_tos(), NULL, AFP_INFO_SIZE);
1236 if (aiblob.data == NULL) {
1241 size = afpinfo_pack(ai, (char *)aiblob.data);
1243 if (size != AFP_INFO_SIZE) {
1247 stream_name = synthetic_smb_fname(talloc_tos(),
1248 smb_fname->base_name,
1252 if (stream_name == NULL) {
1253 data_blob_free(&aiblob);
1254 DBG_ERR("synthetic_smb_fname failed\n");
1258 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1260 status = SMB_VFS_CREATE_FILE(
1261 ad->ad_handle->conn, /* conn */
1263 0, /* root_dir_fid */
1264 stream_name, /* fname */
1265 FILE_GENERIC_WRITE, /* access_mask */
1266 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
1267 FILE_OPEN_IF, /* create_disposition */
1268 0, /* create_options */
1269 0, /* file_attributes */
1270 INTERNAL_OPEN_ONLY, /* oplock_request */
1272 0, /* allocation_size */
1273 0, /* private_flags */
1278 NULL, NULL); /* create context */
1279 TALLOC_FREE(stream_name);
1280 if (!NT_STATUS_IS_OK(status)) {
1281 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1285 nwritten = SMB_VFS_PWRITE(fsp,
1289 if (nwritten == -1) {
1290 DBG_ERR("SMB_VFS_PWRITE failed\n");
1291 saved_errno = errno;
1292 close_file(NULL, fsp, ERROR_CLOSE);
1293 errno = saved_errno;
1297 status = close_file(NULL, fsp, NORMAL_CLOSE);
1298 if (!NT_STATUS_IS_OK(status)) {
1306 static bool ad_convert_truncate(struct adouble *ad,
1307 const struct smb_filename *smb_fname)
1312 * FIXME: direct ftruncate(), but we don't have a fsp for the
1315 rc = ftruncate(ad->ad_fd, ADEDOFF_RFORK_DOT_UND +
1316 ad_getentrylen(ad, ADEID_RFORK));
1324 static bool ad_convert_blank_rfork(struct adouble *ad,
1327 struct fruit_config_data *config = NULL;
1328 uint8_t *map = MAP_FAILED;
1337 SMB_VFS_HANDLE_GET_DATA(ad->ad_handle, config,
1338 struct fruit_config_data, return false);
1340 if (!config->wipe_intentionally_left_blank_rfork) {
1344 if (ad_getentrylen(ad, ADEID_RFORK) != sizeof(empty_resourcefork)) {
1348 maplen = ad_getentryoff(ad, ADEID_RFORK) +
1349 ad_getentrylen(ad, ADEID_RFORK);
1351 /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
1352 map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
1354 if (map == MAP_FAILED) {
1355 DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
1359 cmp = memcmp(map + ADEDOFF_RFORK_DOT_UND,
1361 sizeof(empty_resourcefork));
1362 rc = munmap(map, maplen);
1364 DBG_ERR("munmap failed: %s\n", strerror(errno));
1372 ad_setentrylen(ad, ADEID_RFORK, 0);
1379 len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
1380 if (len != AD_DATASZ_DOT_UND) {
1389 * Convert from Apple's ._ file to Netatalk
1391 * Apple's AppleDouble may contain a FinderInfo entry longer then 32
1392 * bytes containing packed xattrs.
1394 * @return -1 in case an error occurred, 0 if no conversion was done, 1
1397 static int ad_convert(struct adouble *ad,
1398 const struct smb_filename *smb_fname)
1401 bool converted_xattr = false;
1404 ok = ad_convert_xattr(ad, smb_fname, &converted_xattr);
1409 ok = ad_convert_blank_rfork(ad, &blank);
1414 if (converted_xattr || blank) {
1415 ok = ad_convert_truncate(ad, smb_fname);
1421 ok = ad_convert_finderinfo(ad, smb_fname);
1423 DBG_ERR("Failed to convert [%s]\n",
1424 smb_fname_str_dbg(smb_fname));
1432 * Read and parse Netatalk AppleDouble metadata xattr
1434 static ssize_t ad_read_meta(struct adouble *ad,
1435 const struct smb_filename *smb_fname)
1441 DEBUG(10, ("reading meta xattr for %s\n", smb_fname->base_name));
1443 ealen = SMB_VFS_GETXATTR(ad->ad_handle->conn, smb_fname,
1444 AFPINFO_EA_NETATALK, ad->ad_data,
1450 if (errno == ENOATTR) {
1456 DEBUG(2, ("error reading meta xattr: %s\n",
1462 if (ealen != AD_DATASZ_XATTR) {
1463 DEBUG(2, ("bad size %zd\n", ealen));
1469 /* Now parse entries */
1470 ok = ad_unpack(ad, ADEID_NUM_XATTR, AD_DATASZ_XATTR);
1472 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1478 if (!ad_getentryoff(ad, ADEID_FINDERI)
1479 || !ad_getentryoff(ad, ADEID_COMMENT)
1480 || !ad_getentryoff(ad, ADEID_FILEDATESI)
1481 || !ad_getentryoff(ad, ADEID_AFPFILEI)
1482 || !ad_getentryoff(ad, ADEID_PRIVDEV)
1483 || !ad_getentryoff(ad, ADEID_PRIVINO)
1484 || !ad_getentryoff(ad, ADEID_PRIVSYN)
1485 || !ad_getentryoff(ad, ADEID_PRIVID)) {
1486 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1493 DEBUG(10, ("reading meta xattr for %s, rc: %d\n",
1494 smb_fname->base_name, rc));
1498 if (errno == EINVAL) {
1500 removexattr(smb_fname->base_name, AFPINFO_EA_NETATALK);
1508 static int ad_open_rsrc_xattr(const struct smb_filename *smb_fname,
1512 #ifdef HAVE_ATTROPEN
1513 /* FIXME: direct Solaris xattr syscall */
1514 return attropen(smb_fname->base_name,
1515 AFPRESOURCE_EA_NETATALK, flags, mode);
1522 static int ad_open_rsrc_adouble(const struct smb_filename *smb_fname,
1528 struct smb_filename *adp_smb_fname = NULL;
1530 ret = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
1535 fd = open(adp_smb_fname->base_name, flags, mode);
1536 TALLOC_FREE(adp_smb_fname);
1541 static int ad_open_rsrc(vfs_handle_struct *handle,
1542 const struct smb_filename *smb_fname,
1546 struct fruit_config_data *config = NULL;
1549 SMB_VFS_HANDLE_GET_DATA(handle, config,
1550 struct fruit_config_data, return -1);
1552 if (config->rsrc == FRUIT_RSRC_XATTR) {
1553 fd = ad_open_rsrc_xattr(smb_fname, flags, mode);
1555 fd = ad_open_rsrc_adouble(smb_fname, flags, mode);
1562 * Here's the deal: for ADOUBLE_META we can do without an fd as we can issue
1563 * path based xattr calls. For ADOUBLE_RSRC however we need a full-fledged fd
1564 * for file IO on the ._ file.
1566 static int ad_open(vfs_handle_struct *handle,
1569 const struct smb_filename *smb_fname,
1575 DBG_DEBUG("Path [%s] type [%s]\n", smb_fname->base_name,
1576 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
1578 if (ad->ad_type == ADOUBLE_META) {
1582 if ((fsp != NULL) && (fsp->fh != NULL) && (fsp->fh->fd != -1)) {
1583 ad->ad_fd = fsp->fh->fd;
1584 ad->ad_opened = false;
1588 fd = ad_open_rsrc(handle, smb_fname, flags, mode);
1592 ad->ad_opened = true;
1595 DBG_DEBUG("Path [%s] type [%s] fd [%d]\n",
1596 smb_fname->base_name,
1597 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc", fd);
1602 static ssize_t ad_read_rsrc_xattr(struct adouble *ad)
1607 /* FIXME: direct sys_fstat(), don't have an fsp */
1608 ret = sys_fstat(ad->ad_fd, &st,
1609 lp_fake_directory_create_times(
1610 SNUM(ad->ad_handle->conn)));
1615 ad_setentrylen(ad, ADEID_RFORK, st.st_ex_size);
1616 return st.st_ex_size;
1619 static ssize_t ad_read_rsrc_adouble(struct adouble *ad,
1620 const struct smb_filename *smb_fname)
1622 SMB_STRUCT_STAT sbuf;
1629 ret = sys_fstat(ad->ad_fd, &sbuf, lp_fake_directory_create_times(
1630 SNUM(ad->ad_handle->conn)));
1636 * AppleDouble file header content and size, two cases:
1638 * - without xattrs it is exactly AD_DATASZ_DOT_UND (82) bytes large
1639 * - with embedded xattrs it can be larger, up to AD_XATTR_MAX_HDR_SIZE
1641 * Read as much as we can up to AD_XATTR_MAX_HDR_SIZE.
1643 size = sbuf.st_ex_size;
1644 if (size > talloc_array_length(ad->ad_data)) {
1645 if (size > AD_XATTR_MAX_HDR_SIZE) {
1646 size = AD_XATTR_MAX_HDR_SIZE;
1648 p_ad = talloc_realloc(ad, ad->ad_data, char, size);
1655 len = sys_pread(ad->ad_fd, ad->ad_data,
1656 talloc_array_length(ad->ad_data), 0);
1657 if (len != talloc_array_length(ad->ad_data)) {
1658 DBG_NOTICE("%s %s: bad size: %zd\n",
1659 smb_fname->base_name, strerror(errno), len);
1663 /* Now parse entries */
1664 ok = ad_unpack(ad, ADEID_NUM_DOT_UND, sbuf.st_ex_size);
1666 DBG_ERR("invalid AppleDouble resource %s\n",
1667 smb_fname->base_name);
1672 if ((ad_getentryoff(ad, ADEID_FINDERI) != ADEDOFF_FINDERI_DOT_UND)
1673 || (ad_getentrylen(ad, ADEID_FINDERI) < ADEDLEN_FINDERI)
1674 || (ad_getentryoff(ad, ADEID_RFORK) < ADEDOFF_RFORK_DOT_UND)) {
1675 DBG_ERR("invalid AppleDouble resource %s\n",
1676 smb_fname->base_name);
1682 * Try to fixup AppleDouble files created by OS X with xattrs
1683 * appended to the ADEID_FINDERI entry.
1686 ret = ad_convert(ad, smb_fname);
1688 DBG_WARNING("Failed to convert [%s]\n", smb_fname->base_name);
1696 * Read and parse resource fork, either ._ AppleDouble file or xattr
1698 static ssize_t ad_read_rsrc(struct adouble *ad,
1699 const struct smb_filename *smb_fname)
1701 struct fruit_config_data *config = NULL;
1704 SMB_VFS_HANDLE_GET_DATA(ad->ad_handle, config,
1705 struct fruit_config_data, return -1);
1707 if (config->rsrc == FRUIT_RSRC_XATTR) {
1708 len = ad_read_rsrc_xattr(ad);
1710 len = ad_read_rsrc_adouble(ad, smb_fname);
1717 * Read and unpack an AppleDouble metadata xattr or resource
1719 static ssize_t ad_read(struct adouble *ad, const struct smb_filename *smb_fname)
1721 switch (ad->ad_type) {
1723 return ad_read_meta(ad, smb_fname);
1725 return ad_read_rsrc(ad, smb_fname);
1731 static int adouble_destructor(struct adouble *ad)
1733 if ((ad->ad_fd != -1) && ad->ad_opened) {
1741 * Allocate a struct adouble without initialiing it
1743 * The struct is either hang of the fsp extension context or if fsp is
1746 * @param[in] ctx talloc context
1747 * @param[in] handle vfs handle
1748 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1750 * @return adouble handle
1752 static struct adouble *ad_alloc(TALLOC_CTX *ctx, vfs_handle_struct *handle,
1753 adouble_type_t type)
1758 struct fruit_config_data *config;
1760 SMB_VFS_HANDLE_GET_DATA(handle, config,
1761 struct fruit_config_data, return NULL);
1765 adsize = AD_DATASZ_XATTR;
1768 if (config->rsrc == FRUIT_RSRC_ADFILE) {
1769 adsize = AD_DATASZ_DOT_UND;
1776 ad = talloc_zero(ctx, struct adouble);
1783 ad->ad_data = talloc_zero_array(ad, char, adsize);
1784 if (ad->ad_data == NULL) {
1790 ad->ad_handle = handle;
1792 ad->ad_magic = AD_MAGIC;
1793 ad->ad_version = AD_VERSION;
1796 talloc_set_destructor(ad, adouble_destructor);
1806 * Allocate and initialize a new struct adouble
1808 * @param[in] ctx talloc context
1809 * @param[in] handle vfs handle
1810 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1812 * @return adouble handle, initialized
1814 static struct adouble *ad_init(TALLOC_CTX *ctx, vfs_handle_struct *handle,
1815 adouble_type_t type)
1818 const struct ad_entry_order *eid;
1819 struct adouble *ad = NULL;
1820 struct fruit_config_data *config;
1821 time_t t = time(NULL);
1823 SMB_VFS_HANDLE_GET_DATA(handle, config,
1824 struct fruit_config_data, return NULL);
1828 eid = entry_order_meta_xattr;
1831 if (config->rsrc == FRUIT_RSRC_ADFILE) {
1832 eid = entry_order_dot_und;
1834 eid = entry_order_rsrc_xattr;
1841 ad = ad_alloc(ctx, handle, type);
1847 ad->ad_eid[eid->id].ade_off = eid->offset;
1848 ad->ad_eid[eid->id].ade_len = eid->len;
1852 /* put something sane in the date fields */
1853 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, t);
1854 ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, t);
1855 ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, t);
1856 ad_setdate(ad, AD_DATE_BACKUP, htonl(AD_DATE_START));
1864 static struct adouble *ad_get_internal(TALLOC_CTX *ctx,
1865 vfs_handle_struct *handle,
1867 const struct smb_filename *smb_fname,
1868 adouble_type_t type)
1872 struct adouble *ad = NULL;
1876 smb_fname = fsp->base_fsp->fsp_name;
1879 DEBUG(10, ("ad_get(%s) called for %s\n",
1880 type == ADOUBLE_META ? "meta" : "rsrc",
1881 smb_fname->base_name));
1883 ad = ad_alloc(ctx, handle, type);
1889 /* Try rw first so we can use the fd in ad_convert() */
1892 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
1893 if (rc == -1 && ((errno == EROFS) || (errno == EACCES))) {
1895 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
1898 DBG_DEBUG("ad_open [%s] error [%s]\n",
1899 smb_fname->base_name, strerror(errno));
1904 len = ad_read(ad, smb_fname);
1906 DEBUG(10, ("error reading AppleDouble for %s\n",
1907 smb_fname->base_name));
1913 DEBUG(10, ("ad_get(%s) for %s returning %d\n",
1914 type == ADOUBLE_META ? "meta" : "rsrc",
1915 smb_fname->base_name, rc));
1924 * Return AppleDouble data for a file
1926 * @param[in] ctx talloc context
1927 * @param[in] handle vfs handle
1928 * @param[in] smb_fname pathname to file or directory
1929 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1931 * @return talloced struct adouble or NULL on error
1933 static struct adouble *ad_get(TALLOC_CTX *ctx,
1934 vfs_handle_struct *handle,
1935 const struct smb_filename *smb_fname,
1936 adouble_type_t type)
1938 return ad_get_internal(ctx, handle, NULL, smb_fname, type);
1942 * Return AppleDouble data for a file
1944 * @param[in] ctx talloc context
1945 * @param[in] handle vfs handle
1946 * @param[in] fsp fsp to use for IO
1947 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1949 * @return talloced struct adouble or NULL on error
1951 static struct adouble *ad_fget(TALLOC_CTX *ctx, vfs_handle_struct *handle,
1952 files_struct *fsp, adouble_type_t type)
1954 return ad_get_internal(ctx, handle, fsp, NULL, type);
1958 * Set AppleDouble metadata on a file or directory
1960 * @param[in] ad adouble handle
1962 * @param[in] smb_fname pathname to file or directory
1964 * @return status code, 0 means success
1966 static int ad_set(struct adouble *ad, const struct smb_filename *smb_fname)
1971 DBG_DEBUG("Path [%s]\n", smb_fname->base_name);
1973 if (ad->ad_type != ADOUBLE_META) {
1974 DBG_ERR("ad_set on [%s] used with ADOUBLE_RSRC\n",
1975 smb_fname->base_name);
1984 ret = SMB_VFS_SETXATTR(ad->ad_handle->conn,
1986 AFPINFO_EA_NETATALK,
1988 AD_DATASZ_XATTR, 0);
1990 DBG_DEBUG("Path [%s] ret [%d]\n", smb_fname->base_name, ret);
1996 * Set AppleDouble metadata on a file or directory
1998 * @param[in] ad adouble handle
1999 * @param[in] fsp file handle
2001 * @return status code, 0 means success
2003 static int ad_fset(struct adouble *ad, files_struct *fsp)
2009 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
2012 || (fsp->fh == NULL)
2013 || (fsp->fh->fd == -1))
2015 smb_panic("bad fsp");
2023 switch (ad->ad_type) {
2025 rc = SMB_VFS_NEXT_SETXATTR(ad->ad_handle,
2027 AFPINFO_EA_NETATALK,
2029 AD_DATASZ_XATTR, 0);
2033 len = SMB_VFS_NEXT_PWRITE(ad->ad_handle,
2038 if (len != AD_DATASZ_DOT_UND) {
2039 DBG_ERR("short write on %s: %zd", fsp_str_dbg(fsp), len);
2049 DBG_DEBUG("Path [%s] rc [%d]\n", fsp_str_dbg(fsp), rc);
2054 /*****************************************************************************
2056 *****************************************************************************/
2058 static bool is_afpinfo_stream(const struct smb_filename *smb_fname)
2060 if (strncasecmp_m(smb_fname->stream_name,
2061 AFPINFO_STREAM_NAME,
2062 strlen(AFPINFO_STREAM_NAME)) == 0) {
2068 static bool is_afpresource_stream(const struct smb_filename *smb_fname)
2070 if (strncasecmp_m(smb_fname->stream_name,
2071 AFPRESOURCE_STREAM_NAME,
2072 strlen(AFPRESOURCE_STREAM_NAME)) == 0) {
2079 * Test whether stream is an Apple stream, not used atm
2082 static bool is_apple_stream(const struct smb_filename *smb_fname)
2084 if (is_afpinfo_stream(smb_fname)) {
2087 if (is_afpresource_stream(smb_fname)) {
2095 * Initialize config struct from our smb.conf config parameters
2097 static int init_fruit_config(vfs_handle_struct *handle)
2099 struct fruit_config_data *config;
2101 const char *tm_size_str = NULL;
2103 config = talloc_zero(handle->conn, struct fruit_config_data);
2105 DEBUG(1, ("talloc_zero() failed\n"));
2111 * Versions up to Samba 4.5.x had a spelling bug in the
2112 * fruit:resource option calling lp_parm_enum with
2113 * "res*s*ource" (ie two s).
2115 * In Samba 4.6 we accept both the wrong and the correct
2116 * spelling, in Samba 4.7 the bad spelling will be removed.
2118 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2119 "ressource", fruit_rsrc, FRUIT_RSRC_ADFILE);
2120 if (enumval == -1) {
2121 DEBUG(1, ("value for %s: resource type unknown\n",
2122 FRUIT_PARAM_TYPE_NAME));
2125 config->rsrc = (enum fruit_rsrc)enumval;
2127 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2128 "resource", fruit_rsrc, enumval);
2129 if (enumval == -1) {
2130 DEBUG(1, ("value for %s: resource type unknown\n",
2131 FRUIT_PARAM_TYPE_NAME));
2134 config->rsrc = (enum fruit_rsrc)enumval;
2136 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2137 "metadata", fruit_meta, FRUIT_META_NETATALK);
2138 if (enumval == -1) {
2139 DEBUG(1, ("value for %s: metadata type unknown\n",
2140 FRUIT_PARAM_TYPE_NAME));
2143 config->meta = (enum fruit_meta)enumval;
2145 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2146 "locking", fruit_locking, FRUIT_LOCKING_NONE);
2147 if (enumval == -1) {
2148 DEBUG(1, ("value for %s: locking type unknown\n",
2149 FRUIT_PARAM_TYPE_NAME));
2152 config->locking = (enum fruit_locking)enumval;
2154 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2155 "encoding", fruit_encoding, FRUIT_ENC_PRIVATE);
2156 if (enumval == -1) {
2157 DEBUG(1, ("value for %s: encoding type unknown\n",
2158 FRUIT_PARAM_TYPE_NAME));
2161 config->encoding = (enum fruit_encoding)enumval;
2163 if (config->rsrc == FRUIT_RSRC_ADFILE) {
2164 config->veto_appledouble = lp_parm_bool(SNUM(handle->conn),
2165 FRUIT_PARAM_TYPE_NAME,
2170 config->use_aapl = lp_parm_bool(
2171 -1, FRUIT_PARAM_TYPE_NAME, "aapl", true);
2173 config->time_machine = lp_parm_bool(
2174 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "time machine", false);
2176 config->unix_info_enabled = lp_parm_bool(
2177 -1, FRUIT_PARAM_TYPE_NAME, "nfs_aces", true);
2179 config->use_copyfile = lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME,
2182 config->posix_rename = lp_parm_bool(
2183 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "posix_rename", true);
2185 config->aapl_zero_file_id =
2186 lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME, "zero_file_id", true);
2188 config->readdir_attr_rsize = lp_parm_bool(
2189 SNUM(handle->conn), "readdir_attr", "aapl_rsize", true);
2191 config->readdir_attr_finder_info = lp_parm_bool(
2192 SNUM(handle->conn), "readdir_attr", "aapl_finder_info", true);
2194 config->readdir_attr_max_access = lp_parm_bool(
2195 SNUM(handle->conn), "readdir_attr", "aapl_max_access", true);
2197 config->model = lp_parm_const_string(
2198 -1, FRUIT_PARAM_TYPE_NAME, "model", "MacSamba");
2200 tm_size_str = lp_parm_const_string(
2201 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2202 "time machine max size", NULL);
2203 if (tm_size_str != NULL) {
2204 config->time_machine_max_size = conv_str_size(tm_size_str);
2207 config->wipe_intentionally_left_blank_rfork = lp_parm_bool(
2208 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2209 "wipe_intentionally_left_blank_rfork", false);
2211 SMB_VFS_HANDLE_SET_DATA(handle, config,
2212 NULL, struct fruit_config_data,
2219 * Prepend "._" to a basename
2220 * Return a new struct smb_filename with stream_name == NULL.
2222 static int adouble_path(TALLOC_CTX *ctx,
2223 const struct smb_filename *smb_fname_in,
2224 struct smb_filename **pp_smb_fname_out)
2228 struct smb_filename *smb_fname = cp_smb_filename(ctx,
2231 if (smb_fname == NULL) {
2235 /* We need streamname to be NULL */
2236 TALLOC_FREE(smb_fname->stream_name);
2238 /* And we're replacing base_name. */
2239 TALLOC_FREE(smb_fname->base_name);
2241 if (!parent_dirname(smb_fname, smb_fname_in->base_name,
2243 TALLOC_FREE(smb_fname);
2247 smb_fname->base_name = talloc_asprintf(smb_fname,
2248 "%s/._%s", parent, base);
2249 if (smb_fname->base_name == NULL) {
2250 TALLOC_FREE(smb_fname);
2254 *pp_smb_fname_out = smb_fname;
2260 * Allocate and initialize an AfpInfo struct
2262 static AfpInfo *afpinfo_new(TALLOC_CTX *ctx)
2264 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2268 ai->afpi_Signature = AFP_Signature;
2269 ai->afpi_Version = AFP_Version;
2270 ai->afpi_BackupTime = AD_DATE_START;
2275 * Pack an AfpInfo struct into a buffer
2277 * Buffer size must be at least AFP_INFO_SIZE
2278 * Returns size of packed buffer
2280 static ssize_t afpinfo_pack(const AfpInfo *ai, char *buf)
2282 memset(buf, 0, AFP_INFO_SIZE);
2284 RSIVAL(buf, 0, ai->afpi_Signature);
2285 RSIVAL(buf, 4, ai->afpi_Version);
2286 RSIVAL(buf, 12, ai->afpi_BackupTime);
2287 memcpy(buf + 16, ai->afpi_FinderInfo, sizeof(ai->afpi_FinderInfo));
2289 return AFP_INFO_SIZE;
2293 * Unpack a buffer into a AfpInfo structure
2295 * Buffer size must be at least AFP_INFO_SIZE
2296 * Returns allocated AfpInfo struct
2298 static AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data)
2300 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2305 ai->afpi_Signature = RIVAL(data, 0);
2306 ai->afpi_Version = RIVAL(data, 4);
2307 ai->afpi_BackupTime = RIVAL(data, 12);
2308 memcpy(ai->afpi_FinderInfo, (const char *)data + 16,
2309 sizeof(ai->afpi_FinderInfo));
2311 if (ai->afpi_Signature != AFP_Signature
2312 || ai->afpi_Version != AFP_Version) {
2313 DEBUG(1, ("Bad AfpInfo signature or version\n"));
2321 * Fake an inode number from the md5 hash of the (xattr) name
2323 static SMB_INO_T fruit_inode(const SMB_STRUCT_STAT *sbuf, const char *sname)
2326 unsigned char hash[16];
2330 upper_sname = talloc_strdup_upper(talloc_tos(), sname);
2331 SMB_ASSERT(upper_sname != NULL);
2334 MD5Update(&ctx, (const unsigned char *)&(sbuf->st_ex_dev),
2335 sizeof(sbuf->st_ex_dev));
2336 MD5Update(&ctx, (const unsigned char *)&(sbuf->st_ex_ino),
2337 sizeof(sbuf->st_ex_ino));
2338 MD5Update(&ctx, (unsigned char *)upper_sname,
2339 talloc_get_size(upper_sname)-1);
2340 MD5Final(hash, &ctx);
2342 TALLOC_FREE(upper_sname);
2344 /* Hopefully all the variation is in the lower 4 (or 8) bytes! */
2345 memcpy(&result, hash, sizeof(result));
2347 DEBUG(10, ("fruit_inode \"%s\": ino=0x%llu\n",
2348 sname, (unsigned long long)result));
2353 static bool add_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
2354 struct stream_struct **streams,
2355 const char *name, off_t size,
2358 struct stream_struct *tmp;
2360 tmp = talloc_realloc(mem_ctx, *streams, struct stream_struct,
2366 tmp[*num_streams].name = talloc_asprintf(tmp, "%s:$DATA", name);
2367 if (tmp[*num_streams].name == NULL) {
2371 tmp[*num_streams].size = size;
2372 tmp[*num_streams].alloc_size = alloc_size;
2379 static bool filter_empty_rsrc_stream(unsigned int *num_streams,
2380 struct stream_struct **streams)
2382 struct stream_struct *tmp = *streams;
2385 if (*num_streams == 0) {
2389 for (i = 0; i < *num_streams; i++) {
2390 if (strequal_m(tmp[i].name, AFPRESOURCE_STREAM)) {
2395 if (i == *num_streams) {
2399 if (tmp[i].size > 0) {
2403 TALLOC_FREE(tmp[i].name);
2404 if (*num_streams - 1 > i) {
2405 memmove(&tmp[i], &tmp[i+1],
2406 (*num_streams - i - 1) * sizeof(struct stream_struct));
2413 static bool del_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
2414 struct stream_struct **streams,
2417 struct stream_struct *tmp = *streams;
2420 if (*num_streams == 0) {
2424 for (i = 0; i < *num_streams; i++) {
2425 if (strequal_m(tmp[i].name, name)) {
2430 if (i == *num_streams) {
2434 TALLOC_FREE(tmp[i].name);
2435 if (*num_streams - 1 > i) {
2436 memmove(&tmp[i], &tmp[i+1],
2437 (*num_streams - i - 1) * sizeof(struct stream_struct));
2444 static bool ad_empty_finderinfo(const struct adouble *ad)
2447 char emptybuf[ADEDLEN_FINDERI] = {0};
2450 fi = ad_get_entry(ad, ADEID_FINDERI);
2452 DBG_ERR("Missing FinderInfo in struct adouble [%p]\n", ad);
2456 cmp = memcmp(emptybuf, fi, ADEDLEN_FINDERI);
2460 static bool ai_empty_finderinfo(const AfpInfo *ai)
2463 char emptybuf[ADEDLEN_FINDERI] = {0};
2465 cmp = memcmp(emptybuf, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
2470 * Update btime with btime from Netatalk
2472 static void update_btime(vfs_handle_struct *handle,
2473 struct smb_filename *smb_fname)
2476 struct timespec creation_time = {0};
2478 struct fruit_config_data *config = NULL;
2480 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
2483 switch (config->meta) {
2484 case FRUIT_META_STREAM:
2486 case FRUIT_META_NETATALK:
2490 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
2494 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
2498 if (ad_getdate(ad, AD_DATE_UNIX | AD_DATE_CREATE, &t) != 0) {
2504 creation_time.tv_sec = convert_uint32_t_to_time_t(t);
2505 update_stat_ex_create_time(&smb_fname->st, creation_time);
2511 * Map an access mask to a Netatalk single byte byte range lock
2513 static off_t access_to_netatalk_brl(enum apple_fork fork_type,
2514 uint32_t access_mask)
2518 switch (access_mask) {
2519 case FILE_READ_DATA:
2520 offset = AD_FILELOCK_OPEN_RD;
2523 case FILE_WRITE_DATA:
2524 case FILE_APPEND_DATA:
2525 offset = AD_FILELOCK_OPEN_WR;
2529 offset = AD_FILELOCK_OPEN_NONE;
2533 if (fork_type == APPLE_FORK_RSRC) {
2534 if (offset == AD_FILELOCK_OPEN_NONE) {
2535 offset = AD_FILELOCK_RSRC_OPEN_NONE;
2545 * Map a deny mode to a Netatalk brl
2547 static off_t denymode_to_netatalk_brl(enum apple_fork fork_type,
2552 switch (deny_mode) {
2554 offset = AD_FILELOCK_DENY_RD;
2558 offset = AD_FILELOCK_DENY_WR;
2562 smb_panic("denymode_to_netatalk_brl: bad deny mode\n");
2565 if (fork_type == APPLE_FORK_RSRC) {
2573 * Call fcntl() with an exclusive F_GETLK request in order to
2574 * determine if there's an exisiting shared lock
2576 * @return true if the requested lock was found or any error occurred
2577 * false if the lock was not found
2579 static bool test_netatalk_lock(files_struct *fsp, off_t in_offset)
2582 off_t offset = in_offset;
2587 result = SMB_VFS_GETLOCK(fsp, &offset, &len, &type, &pid);
2588 if (result == false) {
2592 if (type != F_UNLCK) {
2599 static NTSTATUS fruit_check_access(vfs_handle_struct *handle,
2601 uint32_t access_mask,
2604 NTSTATUS status = NT_STATUS_OK;
2605 bool open_for_reading, open_for_writing, deny_read, deny_write;
2607 bool have_read = false;
2610 /* FIXME: hardcoded data fork, add resource fork */
2611 enum apple_fork fork_type = APPLE_FORK_DATA;
2613 DEBUG(10, ("fruit_check_access: %s, am: %s/%s, dm: %s/%s\n",
2615 access_mask & FILE_READ_DATA ? "READ" :"-",
2616 access_mask & FILE_WRITE_DATA ? "WRITE" : "-",
2617 deny_mode & DENY_READ ? "DENY_READ" : "-",
2618 deny_mode & DENY_WRITE ? "DENY_WRITE" : "-"));
2620 if (fsp->fh->fd == -1) {
2621 return NT_STATUS_OK;
2624 flags = fcntl(fsp->fh->fd, F_GETFL);
2626 DBG_ERR("fcntl get flags [%s] fd [%d] failed [%s]\n",
2627 fsp_str_dbg(fsp), fsp->fh->fd, strerror(errno));
2628 return map_nt_error_from_unix(errno);
2631 if (flags & (O_RDONLY|O_RDWR)) {
2633 * Applying fcntl read locks requires an fd opened for
2634 * reading. This means we won't be applying locks for
2635 * files openend write-only, but what can we do...
2641 * Check read access and deny read mode
2643 if ((access_mask & FILE_READ_DATA) || (deny_mode & DENY_READ)) {
2645 open_for_reading = test_netatalk_lock(
2646 fsp, access_to_netatalk_brl(fork_type, FILE_READ_DATA));
2648 deny_read = test_netatalk_lock(
2649 fsp, denymode_to_netatalk_brl(fork_type, DENY_READ));
2651 DEBUG(10, ("read: %s, deny_write: %s\n",
2652 open_for_reading == true ? "yes" : "no",
2653 deny_read == true ? "yes" : "no"));
2655 if (((access_mask & FILE_READ_DATA) && deny_read)
2656 || ((deny_mode & DENY_READ) && open_for_reading)) {
2657 return NT_STATUS_SHARING_VIOLATION;
2661 if ((access_mask & FILE_READ_DATA) && have_read) {
2662 struct byte_range_lock *br_lck = NULL;
2664 off = access_to_netatalk_brl(fork_type, FILE_READ_DATA);
2666 handle->conn->sconn->msg_ctx, fsp,
2667 fsp->op->global->open_persistent_id, 1, off,
2668 READ_LOCK, POSIX_LOCK, false,
2671 TALLOC_FREE(br_lck);
2673 if (!NT_STATUS_IS_OK(status)) {
2678 if ((deny_mode & DENY_READ) && have_read) {
2679 struct byte_range_lock *br_lck = NULL;
2681 off = denymode_to_netatalk_brl(fork_type, DENY_READ);
2683 handle->conn->sconn->msg_ctx, fsp,
2684 fsp->op->global->open_persistent_id, 1, off,
2685 READ_LOCK, POSIX_LOCK, false,
2688 TALLOC_FREE(br_lck);
2690 if (!NT_STATUS_IS_OK(status)) {
2697 * Check write access and deny write mode
2699 if ((access_mask & FILE_WRITE_DATA) || (deny_mode & DENY_WRITE)) {
2701 open_for_writing = test_netatalk_lock(
2702 fsp, access_to_netatalk_brl(fork_type, FILE_WRITE_DATA));
2704 deny_write = test_netatalk_lock(
2705 fsp, denymode_to_netatalk_brl(fork_type, DENY_WRITE));
2707 DEBUG(10, ("write: %s, deny_write: %s\n",
2708 open_for_writing == true ? "yes" : "no",
2709 deny_write == true ? "yes" : "no"));
2711 if (((access_mask & FILE_WRITE_DATA) && deny_write)
2712 || ((deny_mode & DENY_WRITE) && open_for_writing)) {
2713 return NT_STATUS_SHARING_VIOLATION;
2717 if ((access_mask & FILE_WRITE_DATA) && have_read) {
2718 struct byte_range_lock *br_lck = NULL;
2720 off = access_to_netatalk_brl(fork_type, FILE_WRITE_DATA);
2722 handle->conn->sconn->msg_ctx, fsp,
2723 fsp->op->global->open_persistent_id, 1, off,
2724 READ_LOCK, POSIX_LOCK, false,
2727 TALLOC_FREE(br_lck);
2729 if (!NT_STATUS_IS_OK(status)) {
2733 if ((deny_mode & DENY_WRITE) && have_read) {
2734 struct byte_range_lock *br_lck = NULL;
2736 off = denymode_to_netatalk_brl(fork_type, DENY_WRITE);
2738 handle->conn->sconn->msg_ctx, fsp,
2739 fsp->op->global->open_persistent_id, 1, off,
2740 READ_LOCK, POSIX_LOCK, false,
2743 TALLOC_FREE(br_lck);
2745 if (!NT_STATUS_IS_OK(status)) {
2754 static NTSTATUS check_aapl(vfs_handle_struct *handle,
2755 struct smb_request *req,
2756 const struct smb2_create_blobs *in_context_blobs,
2757 struct smb2_create_blobs *out_context_blobs)
2759 struct fruit_config_data *config;
2761 struct smb2_create_blob *aapl = NULL;
2765 DATA_BLOB blob = data_blob_talloc(req, NULL, 0);
2766 uint64_t req_bitmap, client_caps;
2767 uint64_t server_caps = SMB2_CRTCTX_AAPL_UNIX_BASED;
2771 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
2772 return NT_STATUS_UNSUCCESSFUL);
2774 if (!config->use_aapl
2775 || in_context_blobs == NULL
2776 || out_context_blobs == NULL) {
2777 return NT_STATUS_OK;
2780 aapl = smb2_create_blob_find(in_context_blobs,
2781 SMB2_CREATE_TAG_AAPL);
2783 return NT_STATUS_OK;
2786 if (aapl->data.length != 24) {
2787 DEBUG(1, ("unexpected AAPL ctxt length: %ju\n",
2788 (uintmax_t)aapl->data.length));
2789 return NT_STATUS_INVALID_PARAMETER;
2792 cmd = IVAL(aapl->data.data, 0);
2793 if (cmd != SMB2_CRTCTX_AAPL_SERVER_QUERY) {
2794 DEBUG(1, ("unsupported AAPL cmd: %d\n", cmd));
2795 return NT_STATUS_INVALID_PARAMETER;
2798 req_bitmap = BVAL(aapl->data.data, 8);
2799 client_caps = BVAL(aapl->data.data, 16);
2801 SIVAL(p, 0, SMB2_CRTCTX_AAPL_SERVER_QUERY);
2803 SBVAL(p, 8, req_bitmap);
2804 ok = data_blob_append(req, &blob, p, 16);
2806 return NT_STATUS_UNSUCCESSFUL;
2809 if (req_bitmap & SMB2_CRTCTX_AAPL_SERVER_CAPS) {
2810 if ((client_caps & SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR) &&
2811 (handle->conn->tcon->compat->fs_capabilities & FILE_NAMED_STREAMS)) {
2812 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR;
2813 config->readdir_attr_enabled = true;
2816 if (config->use_copyfile) {
2817 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_OSX_COPYFILE;
2818 config->copyfile_enabled = true;
2822 * The client doesn't set the flag, so we can't check
2823 * for it and just set it unconditionally
2825 if (config->unix_info_enabled) {
2826 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_NFS_ACE;
2829 SBVAL(p, 0, server_caps);
2830 ok = data_blob_append(req, &blob, p, 8);
2832 return NT_STATUS_UNSUCCESSFUL;
2836 if (req_bitmap & SMB2_CRTCTX_AAPL_VOLUME_CAPS) {
2837 int val = lp_case_sensitive(SNUM(handle->conn->tcon->compat));
2845 caps |= SMB2_CRTCTX_AAPL_CASE_SENSITIVE;
2852 if (config->time_machine) {
2853 caps |= SMB2_CRTCTX_AAPL_FULL_SYNC;
2858 ok = data_blob_append(req, &blob, p, 8);
2860 return NT_STATUS_UNSUCCESSFUL;
2864 if (req_bitmap & SMB2_CRTCTX_AAPL_MODEL_INFO) {
2865 ok = convert_string_talloc(req,
2866 CH_UNIX, CH_UTF16LE,
2867 config->model, strlen(config->model),
2870 return NT_STATUS_UNSUCCESSFUL;
2874 SIVAL(p + 4, 0, modellen);
2875 ok = data_blob_append(req, &blob, p, 8);
2878 return NT_STATUS_UNSUCCESSFUL;
2881 ok = data_blob_append(req, &blob, model, modellen);
2884 return NT_STATUS_UNSUCCESSFUL;
2888 status = smb2_create_blob_add(out_context_blobs,
2890 SMB2_CREATE_TAG_AAPL,
2892 if (NT_STATUS_IS_OK(status)) {
2893 global_fruit_config.nego_aapl = true;
2894 if (config->aapl_zero_file_id) {
2895 aapl_force_zero_file_id(handle->conn->sconn);
2902 static bool readdir_attr_meta_finderi_stream(
2903 struct vfs_handle_struct *handle,
2904 const struct smb_filename *smb_fname,
2907 struct smb_filename *stream_name = NULL;
2908 files_struct *fsp = NULL;
2913 uint8_t buf[AFP_INFO_SIZE];
2915 stream_name = synthetic_smb_fname(talloc_tos(),
2916 smb_fname->base_name,
2917 AFPINFO_STREAM_NAME,
2918 NULL, smb_fname->flags);
2919 if (stream_name == NULL) {
2923 ret = SMB_VFS_STAT(handle->conn, stream_name);
2928 status = SMB_VFS_CREATE_FILE(
2929 handle->conn, /* conn */
2931 0, /* root_dir_fid */
2932 stream_name, /* fname */
2933 FILE_READ_DATA, /* access_mask */
2934 (FILE_SHARE_READ | FILE_SHARE_WRITE | /* share_access */
2936 FILE_OPEN, /* create_disposition*/
2937 0, /* create_options */
2938 0, /* file_attributes */
2939 INTERNAL_OPEN_ONLY, /* oplock_request */
2941 0, /* allocation_size */
2942 0, /* private_flags */
2947 NULL, NULL); /* create context */
2949 TALLOC_FREE(stream_name);
2951 if (!NT_STATUS_IS_OK(status)) {
2955 nread = SMB_VFS_PREAD(fsp, &buf[0], AFP_INFO_SIZE, 0);
2956 if (nread != AFP_INFO_SIZE) {
2957 DBG_ERR("short read [%s] [%zd/%d]\n",
2958 smb_fname_str_dbg(stream_name), nread, AFP_INFO_SIZE);
2963 memcpy(&ai->afpi_FinderInfo[0], &buf[AFP_OFF_FinderInfo],
2970 close_file(NULL, fsp, NORMAL_CLOSE);
2976 static bool readdir_attr_meta_finderi_netatalk(
2977 struct vfs_handle_struct *handle,
2978 const struct smb_filename *smb_fname,
2981 struct adouble *ad = NULL;
2984 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
2989 p = ad_get_entry(ad, ADEID_FINDERI);
2991 DBG_ERR("No ADEID_FINDERI for [%s]\n", smb_fname->base_name);
2996 memcpy(&ai->afpi_FinderInfo[0], p, AFP_FinderSize);
3001 static bool readdir_attr_meta_finderi(struct vfs_handle_struct *handle,
3002 const struct smb_filename *smb_fname,
3003 struct readdir_attr_data *attr_data)
3005 struct fruit_config_data *config = NULL;
3006 uint32_t date_added;
3010 SMB_VFS_HANDLE_GET_DATA(handle, config,
3011 struct fruit_config_data,
3014 switch (config->meta) {
3015 case FRUIT_META_NETATALK:
3016 ok = readdir_attr_meta_finderi_netatalk(
3017 handle, smb_fname, &ai);
3020 case FRUIT_META_STREAM:
3021 ok = readdir_attr_meta_finderi_stream(
3022 handle, smb_fname, &ai);
3026 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3031 /* Don't bother with errors, it's likely ENOENT */
3035 if (S_ISREG(smb_fname->st.st_ex_mode)) {
3037 memcpy(&attr_data->attr_data.aapl.finder_info[0],
3038 &ai.afpi_FinderInfo[0], 4);
3040 /* finder_creator */
3041 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 4,
3042 &ai.afpi_FinderInfo[4], 4);
3046 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 8,
3047 &ai.afpi_FinderInfo[8], 2);
3049 /* finder_ext_flags */
3050 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 10,
3051 &ai.afpi_FinderInfo[24], 2);
3054 date_added = convert_time_t_to_uint32_t(
3055 smb_fname->st.st_ex_btime.tv_sec - AD_DATE_DELTA);
3057 RSIVAL(&attr_data->attr_data.aapl.finder_info[0], 12, date_added);
3062 static uint64_t readdir_attr_rfork_size_adouble(
3063 struct vfs_handle_struct *handle,
3064 const struct smb_filename *smb_fname)
3066 struct adouble *ad = NULL;
3067 uint64_t rfork_size;
3069 ad = ad_get(talloc_tos(), handle, smb_fname,
3075 rfork_size = ad_getentrylen(ad, ADEID_RFORK);
3081 static uint64_t readdir_attr_rfork_size_stream(
3082 struct vfs_handle_struct *handle,
3083 const struct smb_filename *smb_fname)
3085 struct smb_filename *stream_name = NULL;
3087 uint64_t rfork_size;
3089 stream_name = synthetic_smb_fname(talloc_tos(),
3090 smb_fname->base_name,
3091 AFPRESOURCE_STREAM_NAME,
3093 if (stream_name == NULL) {
3097 ret = SMB_VFS_STAT(handle->conn, stream_name);
3099 TALLOC_FREE(stream_name);
3103 rfork_size = stream_name->st.st_ex_size;
3104 TALLOC_FREE(stream_name);
3109 static uint64_t readdir_attr_rfork_size(struct vfs_handle_struct *handle,
3110 const struct smb_filename *smb_fname)
3112 struct fruit_config_data *config = NULL;
3113 uint64_t rfork_size;
3115 SMB_VFS_HANDLE_GET_DATA(handle, config,
3116 struct fruit_config_data,
3119 switch (config->rsrc) {
3120 case FRUIT_RSRC_ADFILE:
3121 case FRUIT_RSRC_XATTR:
3122 rfork_size = readdir_attr_rfork_size_adouble(handle,
3126 case FRUIT_META_STREAM:
3127 rfork_size = readdir_attr_rfork_size_stream(handle,
3132 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3140 static NTSTATUS readdir_attr_macmeta(struct vfs_handle_struct *handle,
3141 const struct smb_filename *smb_fname,
3142 struct readdir_attr_data *attr_data)
3144 NTSTATUS status = NT_STATUS_OK;
3145 struct fruit_config_data *config = NULL;
3148 SMB_VFS_HANDLE_GET_DATA(handle, config,
3149 struct fruit_config_data,
3150 return NT_STATUS_UNSUCCESSFUL);
3153 /* Ensure we return a default value in the creation_date field */
3154 RSIVAL(&attr_data->attr_data.aapl.finder_info, 12, AD_DATE_START);
3157 * Resource fork length
3160 if (config->readdir_attr_rsize) {
3161 uint64_t rfork_size;
3163 rfork_size = readdir_attr_rfork_size(handle, smb_fname);
3164 attr_data->attr_data.aapl.rfork_size = rfork_size;
3171 if (config->readdir_attr_finder_info) {
3172 ok = readdir_attr_meta_finderi(handle, smb_fname, attr_data);
3174 status = NT_STATUS_INTERNAL_ERROR;
3181 static NTSTATUS remove_virtual_nfs_aces(struct security_descriptor *psd)
3186 if (psd->dacl == NULL) {
3187 return NT_STATUS_OK;
3190 for (i = 0; i < psd->dacl->num_aces; i++) {
3191 /* MS NFS style mode/uid/gid */
3192 int cmp = dom_sid_compare_domain(
3193 &global_sid_Unix_NFS,
3194 &psd->dacl->aces[i].trustee);
3196 /* Normal ACE entry. */
3201 * security_descriptor_dacl_del()
3202 * *must* return NT_STATUS_OK as we know
3203 * we have something to remove.
3206 status = security_descriptor_dacl_del(psd,
3207 &psd->dacl->aces[i].trustee);
3208 if (!NT_STATUS_IS_OK(status)) {
3209 DBG_WARNING("failed to remove MS NFS style ACE: %s\n",
3215 * security_descriptor_dacl_del() may delete more
3216 * then one entry subsequent to this one if the
3217 * SID matches, but we only need to ensure that
3218 * we stay looking at the same element in the array.
3222 return NT_STATUS_OK;
3225 /* Search MS NFS style ACE with UNIX mode */
3226 static NTSTATUS check_ms_nfs(vfs_handle_struct *handle,
3228 struct security_descriptor *psd,
3233 struct fruit_config_data *config = NULL;
3237 SMB_VFS_HANDLE_GET_DATA(handle, config,
3238 struct fruit_config_data,
3239 return NT_STATUS_UNSUCCESSFUL);
3241 if (!global_fruit_config.nego_aapl) {
3242 return NT_STATUS_OK;
3244 if (psd->dacl == NULL || !config->unix_info_enabled) {
3245 return NT_STATUS_OK;
3248 for (i = 0; i < psd->dacl->num_aces; i++) {
3249 if (dom_sid_compare_domain(
3250 &global_sid_Unix_NFS_Mode,
3251 &psd->dacl->aces[i].trustee) == 0) {
3252 *pmode = (mode_t)psd->dacl->aces[i].trustee.sub_auths[2];
3253 *pmode &= (S_IRWXU | S_IRWXG | S_IRWXO);
3256 DEBUG(10, ("MS NFS chmod request %s, %04o\n",
3257 fsp_str_dbg(fsp), (unsigned)(*pmode)));
3263 * Remove any incoming virtual ACE entries generated by
3264 * fruit_fget_nt_acl().
3267 return remove_virtual_nfs_aces(psd);
3270 /****************************************************************************
3272 ****************************************************************************/
3274 static int fruit_connect(vfs_handle_struct *handle,
3275 const char *service,
3279 char *list = NULL, *newlist = NULL;
3280 struct fruit_config_data *config;
3282 DEBUG(10, ("fruit_connect\n"));
3284 rc = SMB_VFS_NEXT_CONNECT(handle, service, user);
3289 rc = init_fruit_config(handle);
3294 SMB_VFS_HANDLE_GET_DATA(handle, config,
3295 struct fruit_config_data, return -1);
3297 if (config->veto_appledouble) {
3298 list = lp_veto_files(talloc_tos(), SNUM(handle->conn));
3301 if (strstr(list, "/" ADOUBLE_NAME_PREFIX "*/") == NULL) {
3302 newlist = talloc_asprintf(
3304 "%s/" ADOUBLE_NAME_PREFIX "*/",
3306 lp_do_parameter(SNUM(handle->conn),
3311 lp_do_parameter(SNUM(handle->conn),
3313 "/" ADOUBLE_NAME_PREFIX "*/");
3319 if (config->encoding == FRUIT_ENC_NATIVE) {
3320 lp_do_parameter(SNUM(handle->conn),
3325 if (config->time_machine) {
3326 DBG_NOTICE("Enabling durable handles for Time Machine "
3327 "support on [%s]\n", service);
3328 lp_do_parameter(SNUM(handle->conn), "durable handles", "yes");
3329 lp_do_parameter(SNUM(handle->conn), "kernel oplocks", "no");
3330 lp_do_parameter(SNUM(handle->conn), "kernel share modes", "no");
3331 if (!lp_strict_sync(SNUM(handle->conn))) {
3332 DBG_WARNING("Time Machine without strict sync is not "
3335 lp_do_parameter(SNUM(handle->conn), "posix locking", "no");
3341 static int fruit_open_meta_stream(vfs_handle_struct *handle,
3342 struct smb_filename *smb_fname,
3348 char afpinfo_buf[AFP_INFO_SIZE];
3349 ssize_t len, written;
3353 hostfd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3358 if (!(flags & (O_CREAT | O_TRUNC))) {
3362 ai = afpinfo_new(talloc_tos());
3368 len = afpinfo_pack(ai, afpinfo_buf);
3369 if (len != AFP_INFO_SIZE) {
3374 /* Set fd, needed in SMB_VFS_NEXT_PWRITE() */
3375 fsp->fh->fd = hostfd;
3377 written = SMB_VFS_NEXT_PWRITE(handle, fsp, afpinfo_buf,
3380 if (written != AFP_INFO_SIZE) {
3381 DBG_ERR("bad write [%zd/%d]\n", written, AFP_INFO_SIZE);
3389 DBG_DEBUG("rc=%d, fd=%d\n", rc, hostfd);
3392 int saved_errno = errno;
3394 fsp->fh->fd = hostfd;
3395 SMB_VFS_NEXT_CLOSE(handle, fsp);
3398 errno = saved_errno;
3403 static int fruit_open_meta_netatalk(vfs_handle_struct *handle,
3404 struct smb_filename *smb_fname,
3411 struct adouble *ad = NULL;
3414 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3417 * Return a valid fd, but ensure any attempt to use it returns an error
3418 * (EPIPE). All operations on the smb_fname or the fsp will use path
3428 if (flags & (O_CREAT | O_TRUNC)) {
3430 * The attribute does not exist or needs to be truncated,
3431 * create an AppleDouble EA
3433 ad = ad_init(fsp, handle, ADOUBLE_META);
3439 rc = ad_set(ad, fsp->fsp_name);
3449 DEBUG(10, ("fruit_open meta rc=%d, fd=%d\n", rc, fakefd));
3451 int saved_errno = errno;
3456 errno = saved_errno;
3461 static int fruit_open_meta(vfs_handle_struct *handle,
3462 struct smb_filename *smb_fname,
3463 files_struct *fsp, int flags, mode_t mode)
3466 struct fruit_config_data *config = NULL;
3467 struct fio *fio = NULL;
3469 DBG_DEBUG("path [%s]\n", smb_fname_str_dbg(smb_fname));
3471 SMB_VFS_HANDLE_GET_DATA(handle, config,
3472 struct fruit_config_data, return -1);
3474 switch (config->meta) {
3475 case FRUIT_META_STREAM:
3476 fd = fruit_open_meta_stream(handle, smb_fname,
3480 case FRUIT_META_NETATALK:
3481 fd = fruit_open_meta_netatalk(handle, smb_fname,
3486 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3490 DBG_DEBUG("path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3496 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3497 fio->type = ADOUBLE_META;
3498 fio->config = config;
3503 static int fruit_open_rsrc_adouble(vfs_handle_struct *handle,
3504 struct smb_filename *smb_fname,
3510 struct adouble *ad = NULL;
3511 struct smb_filename *smb_fname_base = NULL;
3512 struct fruit_config_data *config = NULL;
3515 SMB_VFS_HANDLE_GET_DATA(handle, config,
3516 struct fruit_config_data, return -1);
3518 if ((!(flags & O_CREAT)) &&
3519 S_ISDIR(fsp->base_fsp->fsp_name->st.st_ex_mode))
3521 /* sorry, but directories don't habe a resource fork */
3526 rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_base);
3531 /* Sanitize flags */
3532 if (flags & O_WRONLY) {
3533 /* We always need read access for the metadata header too */
3538 hostfd = SMB_VFS_NEXT_OPEN(handle, smb_fname_base, fsp,
3545 if (flags & (O_CREAT | O_TRUNC)) {
3546 ad = ad_init(fsp, handle, ADOUBLE_RSRC);
3552 fsp->fh->fd = hostfd;
3554 rc = ad_fset(ad, fsp);
3565 TALLOC_FREE(smb_fname_base);
3567 DEBUG(10, ("fruit_open resource fork: rc=%d, fd=%d\n", rc, hostfd));
3569 int saved_errno = errno;
3572 * BUGBUGBUG -- we would need to call
3573 * fd_close_posix here, but we don't have a
3576 fsp->fh->fd = hostfd;
3580 errno = saved_errno;
3585 static int fruit_open_rsrc_xattr(vfs_handle_struct *handle,
3586 struct smb_filename *smb_fname,
3591 #ifdef HAVE_ATTROPEN
3594 fd = attropen(smb_fname->base_name,
3595 AFPRESOURCE_EA_NETATALK,
3610 static int fruit_open_rsrc(vfs_handle_struct *handle,
3611 struct smb_filename *smb_fname,
3612 files_struct *fsp, int flags, mode_t mode)
3615 struct fruit_config_data *config = NULL;
3616 struct fio *fio = NULL;
3618 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3620 SMB_VFS_HANDLE_GET_DATA(handle, config,
3621 struct fruit_config_data, return -1);
3623 if (((flags & O_ACCMODE) == O_RDONLY)
3624 && (flags & O_CREAT)
3625 && !VALID_STAT(fsp->fsp_name->st))
3628 * This means the stream doesn't exist. macOS SMB server fails
3629 * this with NT_STATUS_OBJECT_NAME_NOT_FOUND, so must we. Cf bug
3630 * 12565 and the test for this combination in
3631 * test_rfork_create().
3637 switch (config->rsrc) {
3638 case FRUIT_RSRC_STREAM:
3639 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3642 case FRUIT_RSRC_ADFILE:
3643 fd = fruit_open_rsrc_adouble(handle, smb_fname,
3647 case FRUIT_RSRC_XATTR:
3648 fd = fruit_open_rsrc_xattr(handle, smb_fname,
3653 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3657 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3663 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3664 fio->type = ADOUBLE_RSRC;
3665 fio->config = config;
3670 static int fruit_open(vfs_handle_struct *handle,
3671 struct smb_filename *smb_fname,
3672 files_struct *fsp, int flags, mode_t mode)
3676 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3678 if (!is_ntfs_stream_smb_fname(smb_fname)) {
3679 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3682 if (is_afpinfo_stream(smb_fname)) {
3683 fd = fruit_open_meta(handle, smb_fname, fsp, flags, mode);
3684 } else if (is_afpresource_stream(smb_fname)) {
3685 fd = fruit_open_rsrc(handle, smb_fname, fsp, flags, mode);
3687 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3690 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3695 static int fruit_rename(struct vfs_handle_struct *handle,
3696 const struct smb_filename *smb_fname_src,
3697 const struct smb_filename *smb_fname_dst)
3700 struct fruit_config_data *config = NULL;
3701 struct smb_filename *src_adp_smb_fname = NULL;
3702 struct smb_filename *dst_adp_smb_fname = NULL;
3704 SMB_VFS_HANDLE_GET_DATA(handle, config,
3705 struct fruit_config_data, return -1);
3707 if (!VALID_STAT(smb_fname_src->st)) {
3708 DBG_ERR("Need valid stat for [%s]\n",
3709 smb_fname_str_dbg(smb_fname_src));
3713 rc = SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
3718 if ((config->rsrc != FRUIT_RSRC_ADFILE) ||
3719 (!S_ISREG(smb_fname_src->st.st_ex_mode)))
3724 rc = adouble_path(talloc_tos(), smb_fname_src, &src_adp_smb_fname);
3729 rc = adouble_path(talloc_tos(), smb_fname_dst, &dst_adp_smb_fname);
3734 DBG_DEBUG("%s -> %s\n",
3735 smb_fname_str_dbg(src_adp_smb_fname),
3736 smb_fname_str_dbg(dst_adp_smb_fname));
3738 rc = SMB_VFS_NEXT_RENAME(handle, src_adp_smb_fname, dst_adp_smb_fname);
3739 if (errno == ENOENT) {
3744 TALLOC_FREE(src_adp_smb_fname);
3745 TALLOC_FREE(dst_adp_smb_fname);
3749 static int fruit_unlink_meta_stream(vfs_handle_struct *handle,
3750 const struct smb_filename *smb_fname)
3752 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3755 static int fruit_unlink_meta_netatalk(vfs_handle_struct *handle,
3756 const struct smb_filename *smb_fname)
3758 return SMB_VFS_REMOVEXATTR(handle->conn,
3760 AFPINFO_EA_NETATALK);
3763 static int fruit_unlink_meta(vfs_handle_struct *handle,
3764 const struct smb_filename *smb_fname)
3766 struct fruit_config_data *config = NULL;
3769 SMB_VFS_HANDLE_GET_DATA(handle, config,
3770 struct fruit_config_data, return -1);
3772 switch (config->meta) {
3773 case FRUIT_META_STREAM:
3774 rc = fruit_unlink_meta_stream(handle, smb_fname);
3777 case FRUIT_META_NETATALK:
3778 rc = fruit_unlink_meta_netatalk(handle, smb_fname);
3782 DBG_ERR("Unsupported meta config [%d]\n", config->meta);
3789 static int fruit_unlink_rsrc_stream(vfs_handle_struct *handle,
3790 const struct smb_filename *smb_fname,
3795 if (!force_unlink) {
3796 struct smb_filename *smb_fname_cp = NULL;
3799 smb_fname_cp = cp_smb_filename(talloc_tos(), smb_fname);
3800 if (smb_fname_cp == NULL) {
3805 * 0 byte resource fork streams are not listed by
3806 * vfs_streaminfo, as a result stream cleanup/deletion of file
3807 * deletion doesn't remove the resourcefork stream.
3810 ret = SMB_VFS_NEXT_STAT(handle, smb_fname_cp);
3812 TALLOC_FREE(smb_fname_cp);
3813 DBG_ERR("stat [%s] failed [%s]\n",
3814 smb_fname_str_dbg(smb_fname_cp), strerror(errno));
3818 size = smb_fname_cp->st.st_ex_size;
3819 TALLOC_FREE(smb_fname_cp);
3822 /* OS X ignores resource fork stream delete requests */
3827 ret = SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3828 if ((ret != 0) && (errno == ENOENT) && force_unlink) {
3835 static int fruit_unlink_rsrc_adouble(vfs_handle_struct *handle,
3836 const struct smb_filename *smb_fname,
3840 struct adouble *ad = NULL;
3841 struct smb_filename *adp_smb_fname = NULL;
3843 if (!force_unlink) {
3844 ad = ad_get(talloc_tos(), handle, smb_fname,
3853 * 0 byte resource fork streams are not listed by
3854 * vfs_streaminfo, as a result stream cleanup/deletion of file
3855 * deletion doesn't remove the resourcefork stream.
3858 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
3859 /* OS X ignores resource fork stream delete requests */
3867 rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
3872 rc = SMB_VFS_NEXT_UNLINK(handle, adp_smb_fname);
3873 TALLOC_FREE(adp_smb_fname);
3874 if ((rc != 0) && (errno == ENOENT) && force_unlink) {
3881 static int fruit_unlink_rsrc_xattr(vfs_handle_struct *handle,
3882 const struct smb_filename *smb_fname,
3886 * OS X ignores resource fork stream delete requests, so nothing to do
3887 * here. Removing the file will remove the xattr anyway, so we don't
3888 * have to take care of removing 0 byte resource forks that could be
3894 static int fruit_unlink_rsrc(vfs_handle_struct *handle,
3895 const struct smb_filename *smb_fname,
3898 struct fruit_config_data *config = NULL;
3901 SMB_VFS_HANDLE_GET_DATA(handle, config,
3902 struct fruit_config_data, return -1);
3904 switch (config->rsrc) {
3905 case FRUIT_RSRC_STREAM:
3906 rc = fruit_unlink_rsrc_stream(handle, smb_fname, force_unlink);
3909 case FRUIT_RSRC_ADFILE:
3910 rc = fruit_unlink_rsrc_adouble(handle, smb_fname, force_unlink);
3913 case FRUIT_RSRC_XATTR:
3914 rc = fruit_unlink_rsrc_xattr(handle, smb_fname, force_unlink);
3918 DBG_ERR("Unsupported rsrc config [%d]\n", config->rsrc);
3925 static int fruit_unlink(vfs_handle_struct *handle,
3926 const struct smb_filename *smb_fname)
3929 struct fruit_config_data *config = NULL;
3930 struct smb_filename *rsrc_smb_fname = NULL;
3932 SMB_VFS_HANDLE_GET_DATA(handle, config,
3933 struct fruit_config_data, return -1);
3935 if (is_afpinfo_stream(smb_fname)) {
3936 return fruit_unlink_meta(handle, smb_fname);
3937 } else if (is_afpresource_stream(smb_fname)) {
3938 return fruit_unlink_rsrc(handle, smb_fname, false);
3939 } if (is_ntfs_stream_smb_fname(smb_fname)) {
3940 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3944 * A request to delete the base file. Because 0 byte resource
3945 * fork streams are not listed by fruit_streaminfo,
3946 * delete_all_streams() can't remove 0 byte resource fork
3947 * streams, so we have to cleanup this here.
3949 rsrc_smb_fname = synthetic_smb_fname(talloc_tos(),
3950 smb_fname->base_name,
3951 AFPRESOURCE_STREAM_NAME,
3954 if (rsrc_smb_fname == NULL) {
3958 rc = fruit_unlink_rsrc(handle, rsrc_smb_fname, true);
3959 if ((rc != 0) && (errno != ENOENT)) {
3960 DBG_ERR("Forced unlink of [%s] failed [%s]\n",
3961 smb_fname_str_dbg(rsrc_smb_fname), strerror(errno));
3962 TALLOC_FREE(rsrc_smb_fname);
3965 TALLOC_FREE(rsrc_smb_fname);
3967 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3970 static int fruit_chmod(vfs_handle_struct *handle,
3971 const struct smb_filename *smb_fname,
3975 struct fruit_config_data *config = NULL;
3976 struct smb_filename *smb_fname_adp = NULL;
3978 rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname, mode);
3983 SMB_VFS_HANDLE_GET_DATA(handle, config,
3984 struct fruit_config_data, return -1);
3986 if (config->rsrc != FRUIT_RSRC_ADFILE) {
3990 if (!VALID_STAT(smb_fname->st)) {
3994 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
3998 rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_adp);
4003 DEBUG(10, ("fruit_chmod: %s\n", smb_fname_adp->base_name));
4005 rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname_adp, mode);
4006 if (errno == ENOENT) {
4010 TALLOC_FREE(smb_fname_adp);
4014 static int fruit_chown(vfs_handle_struct *handle,
4015 const struct smb_filename *smb_fname,
4020 struct fruit_config_data *config = NULL;
4021 struct smb_filename *adp_smb_fname = NULL;
4023 rc = SMB_VFS_NEXT_CHOWN(handle, smb_fname, uid, gid);
4028 SMB_VFS_HANDLE_GET_DATA(handle, config,
4029 struct fruit_config_data, return -1);
4031 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4035 if (!VALID_STAT(smb_fname->st)) {
4039 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
4043 rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
4048 DEBUG(10, ("fruit_chown: %s\n", adp_smb_fname->base_name));
4050 rc = SMB_VFS_NEXT_CHOWN(handle, adp_smb_fname, uid, gid);
4051 if (errno == ENOENT) {
4056 TALLOC_FREE(adp_smb_fname);
4060 static int fruit_rmdir(struct vfs_handle_struct *handle,
4061 const struct smb_filename *smb_fname)
4065 struct fruit_config_data *config;
4067 SMB_VFS_HANDLE_GET_DATA(handle, config,
4068 struct fruit_config_data, return -1);
4070 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4075 * Due to there is no way to change bDeleteVetoFiles variable
4076 * from this module, need to clean up ourselves
4079 dh = SMB_VFS_OPENDIR(handle->conn, smb_fname, NULL, 0);
4084 while ((de = SMB_VFS_READDIR(handle->conn, dh, NULL)) != NULL) {
4086 struct adouble *ad = NULL;
4088 struct smb_filename *ad_smb_fname = NULL;
4091 match = strncmp(de->d_name,
4092 ADOUBLE_NAME_PREFIX,
4093 strlen(ADOUBLE_NAME_PREFIX));
4098 p = talloc_asprintf(talloc_tos(), "%s/%s",
4099 smb_fname->base_name, de->d_name);
4101 DBG_ERR("talloc_asprintf failed\n");
4105 ad_smb_fname = synthetic_smb_fname(talloc_tos(), p,
4109 if (ad_smb_fname == NULL) {
4110 DBG_ERR("synthetic_smb_fname failed\n");
4115 * Check whether it's a valid AppleDouble file, if
4116 * yes, delete it, ignore it otherwise.
4118 ad = ad_get(talloc_tos(), handle, ad_smb_fname, ADOUBLE_RSRC);
4120 TALLOC_FREE(ad_smb_fname);
4126 ret = SMB_VFS_NEXT_UNLINK(handle, ad_smb_fname);
4128 DBG_ERR("Deleting [%s] failed\n",
4129 smb_fname_str_dbg(ad_smb_fname));
4131 TALLOC_FREE(ad_smb_fname);
4136 SMB_VFS_CLOSEDIR(handle->conn, dh);
4138 return SMB_VFS_NEXT_RMDIR(handle, smb_fname);
4141 static ssize_t fruit_pread_meta_stream(vfs_handle_struct *handle,
4142 files_struct *fsp, void *data,
4143 size_t n, off_t offset)
4148 nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4154 DBG_ERR("Removing [%s] after short read [%zd]\n",
4155 fsp_str_dbg(fsp), nread);
4157 ret = SMB_VFS_NEXT_UNLINK(handle, fsp->fsp_name);
4159 DBG_ERR("Removing [%s] failed\n", fsp_str_dbg(fsp));
4167 static ssize_t fruit_pread_meta_adouble(vfs_handle_struct *handle,
4168 files_struct *fsp, void *data,
4169 size_t n, off_t offset)
4172 struct adouble *ad = NULL;
4173 char afpinfo_buf[AFP_INFO_SIZE];
4177 ai = afpinfo_new(talloc_tos());
4182 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
4188 p = ad_get_entry(ad, ADEID_FINDERI);
4190 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
4195 memcpy(&ai->afpi_FinderInfo[0], p, ADEDLEN_FINDERI);
4197 nread = afpinfo_pack(ai, afpinfo_buf);
4198 if (nread != AFP_INFO_SIZE) {
4203 memcpy(data, afpinfo_buf, n);
4211 static ssize_t fruit_pread_meta(vfs_handle_struct *handle,
4212 files_struct *fsp, void *data,
4213 size_t n, off_t offset)
4215 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4220 * OS X has a off-by-1 error in the offset calculation, so we're
4221 * bug compatible here. It won't hurt, as any relevant real
4222 * world read requests from the AFP_AfpInfo stream will be
4223 * offset=0 n=60. offset is ignored anyway, see below.
4225 if ((offset < 0) || (offset >= AFP_INFO_SIZE + 1)) {
4230 DBG_ERR("Failed to fetch fsp extension");
4234 /* Yes, macOS always reads from offset 0 */
4236 to_return = MIN(n, AFP_INFO_SIZE);
4238 switch (fio->config->meta) {
4239 case FRUIT_META_STREAM:
4240 nread = fruit_pread_meta_stream(handle, fsp, data,
4244 case FRUIT_META_NETATALK:
4245 nread = fruit_pread_meta_adouble(handle, fsp, data,
4250 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
4257 static ssize_t fruit_pread_rsrc_stream(vfs_handle_struct *handle,
4258 files_struct *fsp, void *data,
4259 size_t n, off_t offset)
4261 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4264 static ssize_t fruit_pread_rsrc_xattr(vfs_handle_struct *handle,
4265 files_struct *fsp, void *data,
4266 size_t n, off_t offset)
4268 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4271 static ssize_t fruit_pread_rsrc_adouble(vfs_handle_struct *handle,
4272 files_struct *fsp, void *data,
4273 size_t n, off_t offset)
4275 struct adouble *ad = NULL;
4278 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
4283 nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n,
4284 offset + ad_getentryoff(ad, ADEID_RFORK));
4290 static ssize_t fruit_pread_rsrc(vfs_handle_struct *handle,
4291 files_struct *fsp, void *data,
4292 size_t n, off_t offset)
4294 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4302 switch (fio->config->rsrc) {
4303 case FRUIT_RSRC_STREAM:
4304 nread = fruit_pread_rsrc_stream(handle, fsp, data, n, offset);
4307 case FRUIT_RSRC_ADFILE:
4308 nread = fruit_pread_rsrc_adouble(handle, fsp, data, n, offset);
4311 case FRUIT_RSRC_XATTR:
4312 nread = fruit_pread_rsrc_xattr(handle, fsp, data, n, offset);
4316 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
4323 static ssize_t fruit_pread(vfs_handle_struct *handle,
4324 files_struct *fsp, void *data,
4325 size_t n, off_t offset)
4327 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4330 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4331 fsp_str_dbg(fsp), (intmax_t)offset, n);
4334 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4337 if (fio->type == ADOUBLE_META) {
4338 nread = fruit_pread_meta(handle, fsp, data, n, offset);
4340 nread = fruit_pread_rsrc(handle, fsp, data, n, offset);
4343 DBG_DEBUG("Path [%s] nread [%zd]\n", fsp_str_dbg(fsp), nread);
4347 static bool fruit_must_handle_aio_stream(struct fio *fio)
4353 if ((fio->type == ADOUBLE_META) &&
4354 (fio->config->meta == FRUIT_META_NETATALK))
4359 if ((fio->type == ADOUBLE_RSRC) &&
4360 (fio->config->rsrc == FRUIT_RSRC_ADFILE))
4368 struct fruit_pread_state {
4370 struct vfs_aio_state vfs_aio_state;
4373 static void fruit_pread_done(struct tevent_req *subreq);
4375 static struct tevent_req *fruit_pread_send(
4376 struct vfs_handle_struct *handle,
4377 TALLOC_CTX *mem_ctx,
4378 struct tevent_context *ev,
4379 struct files_struct *fsp,
4381 size_t n, off_t offset)
4383 struct tevent_req *req = NULL;
4384 struct tevent_req *subreq = NULL;
4385 struct fruit_pread_state *state = NULL;
4386 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4388 req = tevent_req_create(mem_ctx, &state,
4389 struct fruit_pread_state);
4394 if (fruit_must_handle_aio_stream(fio)) {
4395 state->nread = SMB_VFS_PREAD(fsp, data, n, offset);
4396 if (state->nread != n) {
4397 if (state->nread != -1) {
4400 tevent_req_error(req, errno);
4401 return tevent_req_post(req, ev);
4403 tevent_req_done(req);
4404 return tevent_req_post(req, ev);
4407 subreq = SMB_VFS_NEXT_PREAD_SEND(state, ev, handle, fsp,
4409 if (tevent_req_nomem(req, subreq)) {
4410 return tevent_req_post(req, ev);
4412 tevent_req_set_callback(subreq, fruit_pread_done, req);
4416 static void fruit_pread_done(struct tevent_req *subreq)
4418 struct tevent_req *req = tevent_req_callback_data(
4419 subreq, struct tevent_req);
4420 struct fruit_pread_state *state = tevent_req_data(
4421 req, struct fruit_pread_state);
4423 state->nread = SMB_VFS_PREAD_RECV(subreq, &state->vfs_aio_state);
4424 TALLOC_FREE(subreq);
4426 if (tevent_req_error(req, state->vfs_aio_state.error)) {
4429 tevent_req_done(req);
4432 static ssize_t fruit_pread_recv(struct tevent_req *req,
4433 struct vfs_aio_state *vfs_aio_state)
4435 struct fruit_pread_state *state = tevent_req_data(
4436 req, struct fruit_pread_state);
4438 if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
4442 *vfs_aio_state = state->vfs_aio_state;
4443 return state->nread;
4446 static ssize_t fruit_pwrite_meta_stream(vfs_handle_struct *handle,
4447 files_struct *fsp, const void *data,
4448 size_t n, off_t offset)
4454 ai = afpinfo_unpack(talloc_tos(), data);
4459 nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4460 if (nwritten != n) {
4464 if (!ai_empty_finderinfo(ai)) {
4468 ok = set_delete_on_close(
4471 handle->conn->session_info->security_token,
4472 handle->conn->session_info->unix_token);
4474 DBG_ERR("set_delete_on_close on [%s] failed\n",
4482 static ssize_t fruit_pwrite_meta_netatalk(vfs_handle_struct *handle,
4483 files_struct *fsp, const void *data,
4484 size_t n, off_t offset)
4486 struct adouble *ad = NULL;
4492 ai = afpinfo_unpack(talloc_tos(), data);
4497 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
4499 ad = ad_init(talloc_tos(), handle, ADOUBLE_META);
4504 p = ad_get_entry(ad, ADEID_FINDERI);
4506 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
4511 memcpy(p, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
4513 ret = ad_fset(ad, fsp);
4515 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
4522 if (!ai_empty_finderinfo(ai)) {
4526 ok = set_delete_on_close(
4529 handle->conn->session_info->security_token,
4530 handle->conn->session_info->unix_token);
4532 DBG_ERR("set_delete_on_close on [%s] failed\n",
4540 static ssize_t fruit_pwrite_meta(vfs_handle_struct *handle,
4541 files_struct *fsp, const void *data,
4542 size_t n, off_t offset)
4544 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4548 * Writing an all 0 blob to the metadata stream
4549 * results in the stream being removed on a macOS
4550 * server. This ensures we behave the same and it
4551 * verified by the "delete AFP_AfpInfo by writing all
4554 if (n != AFP_INFO_SIZE || offset != 0) {
4555 DBG_ERR("unexpected offset=%jd or size=%jd\n",
4556 (intmax_t)offset, (intmax_t)n);
4561 DBG_ERR("Failed to fetch fsp extension");
4565 switch (fio->config->meta) {
4566 case FRUIT_META_STREAM:
4567 nwritten = fruit_pwrite_meta_stream(handle, fsp, data,
4571 case FRUIT_META_NETATALK:
4572 nwritten = fruit_pwrite_meta_netatalk(handle, fsp, data,
4577 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
4584 static ssize_t fruit_pwrite_rsrc_stream(vfs_handle_struct *handle,
4585 files_struct *fsp, const void *data,
4586 size_t n, off_t offset)
4588 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4591 static ssize_t fruit_pwrite_rsrc_xattr(vfs_handle_struct *handle,
4592 files_struct *fsp, const void *data,
4593 size_t n, off_t offset)
4595 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4598 static ssize_t fruit_pwrite_rsrc_adouble(vfs_handle_struct *handle,
4599 files_struct *fsp, const void *data,
4600 size_t n, off_t offset)
4602 struct adouble *ad = NULL;
4606 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
4608 DBG_ERR("ad_get [%s] failed\n", fsp_str_dbg(fsp));
4612 nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n,
4613 offset + ad_getentryoff(ad, ADEID_RFORK));
4614 if (nwritten != n) {
4615 DBG_ERR("Short write on [%s] [%zd/%zd]\n",
4616 fsp_str_dbg(fsp), nwritten, n);
4621 if ((n + offset) > ad_getentrylen(ad, ADEID_RFORK)) {
4622 ad_setentrylen(ad, ADEID_RFORK, n + offset);
4623 ret = ad_fset(ad, fsp);
4625 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
4635 static ssize_t fruit_pwrite_rsrc(vfs_handle_struct *handle,
4636 files_struct *fsp, const void *data,
4637 size_t n, off_t offset)
4639 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4643 DBG_ERR("Failed to fetch fsp extension");
4647 switch (fio->config->rsrc) {
4648 case FRUIT_RSRC_STREAM:
4649 nwritten = fruit_pwrite_rsrc_stream(handle, fsp, data, n, offset);
4652 case FRUIT_RSRC_ADFILE:
4653 nwritten = fruit_pwrite_rsrc_adouble(handle, fsp, data, n, offset);
4656 case FRUIT_RSRC_XATTR:
4657 nwritten = fruit_pwrite_rsrc_xattr(handle, fsp, data, n, offset);
4661 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
4668 static ssize_t fruit_pwrite(vfs_handle_struct *handle,
4669 files_struct *fsp, const void *data,
4670 size_t n, off_t offset)
4672 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4675 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4676 fsp_str_dbg(fsp), (intmax_t)offset, n);
4679 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4682 if (fio->type == ADOUBLE_META) {
4683 nwritten = fruit_pwrite_meta(handle, fsp, data, n, offset);
4685 nwritten = fruit_pwrite_rsrc(handle, fsp, data, n, offset);
4688 DBG_DEBUG("Path [%s] nwritten=%zd\n", fsp_str_dbg(fsp), nwritten);
4692 struct fruit_pwrite_state {
4694 struct vfs_aio_state vfs_aio_state;
4697 static void fruit_pwrite_done(struct tevent_req *subreq);
4699 static struct tevent_req *fruit_pwrite_send(
4700 struct vfs_handle_struct *handle,
4701 TALLOC_CTX *mem_ctx,
4702 struct tevent_context *ev,
4703 struct files_struct *fsp,
4705 size_t n, off_t offset)
4707 struct tevent_req *req = NULL;
4708 struct tevent_req *subreq = NULL;
4709 struct fruit_pwrite_state *state = NULL;
4710 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4712 req = tevent_req_create(mem_ctx, &state,
4713 struct fruit_pwrite_state);
4718 if (fruit_must_handle_aio_stream(fio)) {
4719 state->nwritten = SMB_VFS_PWRITE(fsp, data, n, offset);
4720 if (state->nwritten != n) {
4721 if (state->nwritten != -1) {
4724 tevent_req_error(req, errno);
4725 return tevent_req_post(req, ev);
4727 tevent_req_done(req);
4728 return tevent_req_post(req, ev);
4731 subreq = SMB_VFS_NEXT_PWRITE_SEND(state, ev, handle, fsp,
4733 if (tevent_req_nomem(req, subreq)) {
4734 return tevent_req_post(req, ev);
4736 tevent_req_set_callback(subreq, fruit_pwrite_done, req);
4740 static void fruit_pwrite_done(struct tevent_req *subreq)
4742 struct tevent_req *req = tevent_req_callback_data(
4743 subreq, struct tevent_req);
4744 struct fruit_pwrite_state *state = tevent_req_data(
4745 req, struct fruit_pwrite_state);
4747 state->nwritten = SMB_VFS_PWRITE_RECV(subreq, &state->vfs_aio_state);
4748 TALLOC_FREE(subreq);
4750 if (tevent_req_error(req, state->vfs_aio_state.error)) {
4753 tevent_req_done(req);
4756 static ssize_t fruit_pwrite_recv(struct tevent_req *req,
4757 struct vfs_aio_state *vfs_aio_state)
4759 struct fruit_pwrite_state *state = tevent_req_data(
4760 req, struct fruit_pwrite_state);
4762 if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
4766 *vfs_aio_state = state->vfs_aio_state;
4767 return state->nwritten;
4771 * Helper to stat/lstat the base file of an smb_fname.
4773 static int fruit_stat_base(vfs_handle_struct *handle,
4774 struct smb_filename *smb_fname,
4777 char *tmp_stream_name;
4780 tmp_stream_name = smb_fname->stream_name;
4781 smb_fname->stream_name = NULL;
4783 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
4785 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
4787 smb_fname->stream_name = tmp_stream_name;
4791 static int fruit_stat_meta_stream(vfs_handle_struct *handle,
4792 struct smb_filename *smb_fname,
4798 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
4800 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
4806 static int fruit_stat_meta_netatalk(vfs_handle_struct *handle,
4807 struct smb_filename *smb_fname,
4810 struct adouble *ad = NULL;
4812 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
4814 DBG_INFO("fruit_stat_meta %s: %s\n",
4815 smb_fname_str_dbg(smb_fname), strerror(errno));
4821 /* Populate the stat struct with info from the base file. */
4822 if (fruit_stat_base(handle, smb_fname, follow_links) == -1) {
4825 smb_fname->st.st_ex_size = AFP_INFO_SIZE;
4826 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
4827 smb_fname->stream_name);
4831 static int fruit_stat_meta(vfs_handle_struct *handle,
4832 struct smb_filename *smb_fname,
4835 struct fruit_config_data *config = NULL;
4838 SMB_VFS_HANDLE_GET_DATA(handle, config,
4839 struct fruit_config_data, return -1);
4841 switch (config->meta) {
4842 case FRUIT_META_STREAM:
4843 ret = fruit_stat_meta_stream(handle, smb_fname, follow_links);
4846 case FRUIT_META_NETATALK:
4847 ret = fruit_stat_meta_netatalk(handle, smb_fname, follow_links);
4851 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
4858 static int fruit_stat_rsrc_netatalk(vfs_handle_struct *handle,
4859 struct smb_filename *smb_fname,
4862 struct adouble *ad = NULL;
4865 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
4871 /* Populate the stat struct with info from the base file. */
4872 ret = fruit_stat_base(handle, smb_fname, follow_links);
4878 smb_fname->st.st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
4879 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
4880 smb_fname->stream_name);
4885 static int fruit_stat_rsrc_stream(vfs_handle_struct *handle,
4886 struct smb_filename *smb_fname,
4892 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
4894 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
4900 static int fruit_stat_rsrc_xattr(vfs_handle_struct *handle,
4901 struct smb_filename *smb_fname,
4904 #ifdef HAVE_ATTROPEN
4908 /* Populate the stat struct with info from the base file. */
4909 ret = fruit_stat_base(handle, smb_fname, follow_links);
4914 fd = attropen(smb_fname->base_name,
4915 AFPRESOURCE_EA_NETATALK,
4921 ret = sys_fstat(fd, &smb_fname->st, false);
4924 DBG_ERR("fstat [%s:%s] failed\n", smb_fname->base_name,
4925 AFPRESOURCE_EA_NETATALK);
4931 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
4932 smb_fname->stream_name);
4942 static int fruit_stat_rsrc(vfs_handle_struct *handle,
4943 struct smb_filename *smb_fname,
4946 struct fruit_config_data *config = NULL;
4949 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
4951 SMB_VFS_HANDLE_GET_DATA(handle, config,
4952 struct fruit_config_data, return -1);
4954 switch (config->rsrc) {
4955 case FRUIT_RSRC_STREAM:
4956 ret = fruit_stat_rsrc_stream(handle, smb_fname, follow_links);
4959 case FRUIT_RSRC_XATTR:
4960 ret = fruit_stat_rsrc_xattr(handle, smb_fname, follow_links);
4963 case FRUIT_RSRC_ADFILE:
4964 ret = fruit_stat_rsrc_netatalk(handle, smb_fname, follow_links);
4968 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
4975 static int fruit_stat(vfs_handle_struct *handle,
4976 struct smb_filename *smb_fname)
4980 DEBUG(10, ("fruit_stat called for %s\n",
4981 smb_fname_str_dbg(smb_fname)));
4983 if (!is_ntfs_stream_smb_fname(smb_fname)
4984 || is_ntfs_default_stream_smb_fname(smb_fname)) {
4985 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
4987 update_btime(handle, smb_fname);
4993 * Note if lp_posix_paths() is true, we can never
4994 * get here as is_ntfs_stream_smb_fname() is
4995 * always false. So we never need worry about
4996 * not following links here.
4999 if (is_afpinfo_stream(smb_fname)) {
5000 rc = fruit_stat_meta(handle, smb_fname, true);
5001 } else if (is_afpresource_stream(smb_fname)) {
5002 rc = fruit_stat_rsrc(handle, smb_fname, true);
5004 return SMB_VFS_NEXT_STAT(handle, smb_fname);
5008 update_btime(handle, smb_fname);
5009 smb_fname->st.st_ex_mode &= ~S_IFMT;
5010 smb_fname->st.st_ex_mode |= S_IFREG;
5011 smb_fname->st.st_ex_blocks =
5012 smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
5017 static int fruit_lstat(vfs_handle_struct *handle,
5018 struct smb_filename *smb_fname)
5022 DEBUG(10, ("fruit_lstat called for %s\n",
5023 smb_fname_str_dbg(smb_fname)));
5025 if (!is_ntfs_stream_smb_fname(smb_fname)
5026 || is_ntfs_default_stream_smb_fname(smb_fname)) {
5027 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5029 update_btime(handle, smb_fname);
5034 if (is_afpinfo_stream(smb_fname)) {
5035 rc = fruit_stat_meta(handle, smb_fname, false);
5036 } else if (is_afpresource_stream(smb_fname)) {
5037 rc = fruit_stat_rsrc(handle, smb_fname, false);
5039 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5043 update_btime(handle, smb_fname);
5044 smb_fname->st.st_ex_mode &= ~S_IFMT;
5045 smb_fname->st.st_ex_mode |= S_IFREG;
5046 smb_fname->st.st_ex_blocks =
5047 smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
5052 static int fruit_fstat_meta_stream(vfs_handle_struct *handle,
5054 SMB_STRUCT_STAT *sbuf)
5056 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5059 static int fruit_fstat_meta_netatalk(vfs_handle_struct *handle,
5061 SMB_STRUCT_STAT *sbuf)
5065 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5070 *sbuf = fsp->base_fsp->fsp_name->st;
5071 sbuf->st_ex_size = AFP_INFO_SIZE;
5072 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5077 static int fruit_fstat_meta(vfs_handle_struct *handle,
5079 SMB_STRUCT_STAT *sbuf,
5084 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
5086 switch (fio->config->meta) {
5087 case FRUIT_META_STREAM:
5088 ret = fruit_fstat_meta_stream(handle, fsp, sbuf);
5091 case FRUIT_META_NETATALK:
5092 ret = fruit_fstat_meta_netatalk(handle, fsp, sbuf);
5096 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
5100 DBG_DEBUG("Path [%s] ret [%d]\n", fsp_str_dbg(fsp), ret);
5104 static int fruit_fstat_rsrc_xattr(vfs_handle_struct *handle,
5106 SMB_STRUCT_STAT *sbuf)
5108 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5111 static int fruit_fstat_rsrc_stream(vfs_handle_struct *handle,
5113 SMB_STRUCT_STAT *sbuf)
5115 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5118 static int fruit_fstat_rsrc_adouble(vfs_handle_struct *handle,
5120 SMB_STRUCT_STAT *sbuf)
5122 struct adouble *ad = NULL;
5125 /* Populate the stat struct with info from the base file. */
5126 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5131 ad = ad_get(talloc_tos(), handle,
5132 fsp->base_fsp->fsp_name,
5135 DBG_ERR("ad_get [%s] failed [%s]\n",
5136 fsp_str_dbg(fsp), strerror(errno));
5140 *sbuf = fsp->base_fsp->fsp_name->st;
5141 sbuf->st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
5142 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5148 static int fruit_fstat_rsrc(vfs_handle_struct *handle, files_struct *fsp,
5149 SMB_STRUCT_STAT *sbuf, struct fio *fio)
5153 switch (fio->config->rsrc) {
5154 case FRUIT_RSRC_STREAM:
5155 ret = fruit_fstat_rsrc_stream(handle, fsp, sbuf);
5158 case FRUIT_RSRC_ADFILE:
5159 ret = fruit_fstat_rsrc_adouble(handle, fsp, sbuf);
5162 case FRUIT_RSRC_XATTR:
5163 ret = fruit_fstat_rsrc_xattr(handle, fsp, sbuf);
5167 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
5174 static int fruit_fstat(vfs_handle_struct *handle, files_struct *fsp,
5175 SMB_STRUCT_STAT *sbuf)
5177 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5181 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5184 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
5186 if (fio->type == ADOUBLE_META) {
5187 rc = fruit_fstat_meta(handle, fsp, sbuf, fio);
5189 rc = fruit_fstat_rsrc(handle, fsp, sbuf, fio);
5193 sbuf->st_ex_mode &= ~S_IFMT;
5194 sbuf->st_ex_mode |= S_IFREG;
5195 sbuf->st_ex_blocks = sbuf->st_ex_size / STAT_ST_BLOCKSIZE + 1;
5198 DBG_DEBUG("Path [%s] rc [%d] size [%"PRIdMAX"]\n",
5199 fsp_str_dbg(fsp), rc, (intmax_t)sbuf->st_ex_size);
5203 static NTSTATUS delete_invalid_meta_stream(
5204 vfs_handle_struct *handle,
5205 const struct smb_filename *smb_fname,
5206 TALLOC_CTX *mem_ctx,
5207 unsigned int *pnum_streams,
5208 struct stream_struct **pstreams)
5210 struct smb_filename *sname = NULL;
5214 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams, AFPINFO_STREAM);
5216 return NT_STATUS_INTERNAL_ERROR;
5219 sname = synthetic_smb_fname(talloc_tos(),
5220 smb_fname->base_name,
5221 AFPINFO_STREAM_NAME,
5223 if (sname == NULL) {
5224 return NT_STATUS_NO_MEMORY;
5227 ret = SMB_VFS_NEXT_UNLINK(handle, sname);
5230 DBG_ERR("Removing [%s] failed\n", smb_fname_str_dbg(sname));
5231 return map_nt_error_from_unix(errno);
5234 return NT_STATUS_OK;
5237 static NTSTATUS fruit_streaminfo_meta_stream(
5238 vfs_handle_struct *handle,
5239 struct files_struct *fsp,
5240 const struct smb_filename *smb_fname,
5241 TALLOC_CTX *mem_ctx,
5242 unsigned int *pnum_streams,
5243 struct stream_struct **pstreams)
5245 struct stream_struct *stream = *pstreams;
5246 unsigned int num_streams = *pnum_streams;
5247 struct smb_filename *sname = NULL;
5248 char *full_name = NULL;
5250 struct share_mode_lock *lck = NULL;
5251 struct file_id id = {0};
5252 bool delete_on_close_set;
5258 for (i = 0; i < num_streams; i++) {
5259 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
5264 if (i == num_streams) {
5265 return NT_STATUS_OK;
5268 if (stream[i].size != AFP_INFO_SIZE) {
5269 DBG_ERR("Removing invalid AFPINFO_STREAM size [%jd] from [%s]\n",
5270 (intmax_t)stream[i].size, smb_fname_str_dbg(smb_fname));
5272 return delete_invalid_meta_stream(handle, smb_fname, mem_ctx,
5273 pnum_streams, pstreams);
5277 * Now check if there's a delete-on-close pending on the stream. If so,
5278 * hide the stream. This behaviour was verified against a macOS 10.12
5282 sname = synthetic_smb_fname(talloc_tos(),
5283 smb_fname->base_name,
5284 AFPINFO_STREAM_NAME,
5286 if (sname == NULL) {
5287 status = NT_STATUS_NO_MEMORY;
5291 ret = SMB_VFS_NEXT_STAT(handle, sname);
5293 status = map_nt_error_from_unix(errno);
5297 id = SMB_VFS_NEXT_FILE_ID_CREATE(handle, &sname->st);
5299 lck = get_existing_share_mode_lock(talloc_tos(), id);
5301 status = NT_STATUS_OK;
5305 full_name = talloc_asprintf(talloc_tos(),
5309 if (full_name == NULL) {
5310 status = NT_STATUS_NO_MEMORY;
5314 status = file_name_hash(handle->conn, full_name, &name_hash);
5315 if (!NT_STATUS_IS_OK(status)) {
5319 delete_on_close_set = is_delete_on_close_set(lck, name_hash);
5320 if (delete_on_close_set) {
5321 ok = del_fruit_stream(mem_ctx,
5326 status = NT_STATUS_INTERNAL_ERROR;
5331 status = NT_STATUS_OK;
5336 TALLOC_FREE(full_name);
5340 static NTSTATUS fruit_streaminfo_meta_netatalk(
5341 vfs_handle_struct *handle,
5342 struct files_struct *fsp,
5343 const struct smb_filename *smb_fname,
5344 TALLOC_CTX *mem_ctx,
5345 unsigned int *pnum_streams,
5346 struct stream_struct **pstreams)
5348 struct stream_struct *stream = *pstreams;
5349 unsigned int num_streams = *pnum_streams;
5350 struct adouble *ad = NULL;
5355 /* Remove the Netatalk xattr from the list */
5356 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5357 ":" NETATALK_META_XATTR ":$DATA");
5359 return NT_STATUS_NO_MEMORY;
5363 * Check if there's a AFPINFO_STREAM from the VFS streams
5364 * backend and if yes, remove it from the list
5366 for (i = 0; i < num_streams; i++) {
5367 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
5372 if (i < num_streams) {
5373 DBG_WARNING("Unexpected AFPINFO_STREAM on [%s]\n",
5374 smb_fname_str_dbg(smb_fname));
5376 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5379 return NT_STATUS_INTERNAL_ERROR;
5383 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5385 return NT_STATUS_OK;
5388 is_fi_empty = ad_empty_finderinfo(ad);
5392 return NT_STATUS_OK;
5395 ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
5396 AFPINFO_STREAM_NAME, AFP_INFO_SIZE,
5397 smb_roundup(handle->conn, AFP_INFO_SIZE));
5399 return NT_STATUS_NO_MEMORY;
5402 return NT_STATUS_OK;
5405 static NTSTATUS fruit_streaminfo_meta(vfs_handle_struct *handle,
5406 struct files_struct *fsp,
5407 const struct smb_filename *smb_fname,
5408 TALLOC_CTX *mem_ctx,
5409 unsigned int *pnum_streams,
5410 struct stream_struct **pstreams)
5412 struct fruit_config_data *config = NULL;
5415 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5416 return NT_STATUS_INTERNAL_ERROR);
5418 switch (config->meta) {
5419 case FRUIT_META_NETATALK:
5420 status = fruit_streaminfo_meta_netatalk(handle, fsp, smb_fname,
5421 mem_ctx, pnum_streams,
5425 case FRUIT_META_STREAM:
5426 status = fruit_streaminfo_meta_stream(handle, fsp, smb_fname,
5427 mem_ctx, pnum_streams,
5432 return NT_STATUS_INTERNAL_ERROR;
5438 static NTSTATUS fruit_streaminfo_rsrc_stream(
5439 vfs_handle_struct *handle,
5440 struct files_struct *fsp,
5441 const struct smb_filename *smb_fname,
5442 TALLOC_CTX *mem_ctx,
5443 unsigned int *pnum_streams,
5444 struct stream_struct **pstreams)
5448 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
5450 DBG_ERR("Filtering resource stream failed\n");
5451 return NT_STATUS_INTERNAL_ERROR;
5453 return NT_STATUS_OK;
5456 static NTSTATUS fruit_streaminfo_rsrc_xattr(
5457 vfs_handle_struct *handle,
5458 struct files_struct *fsp,
5459 const struct smb_filename *smb_fname,
5460 TALLOC_CTX *mem_ctx,
5461 unsigned int *pnum_streams,
5462 struct stream_struct **pstreams)
5466 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
5468 DBG_ERR("Filtering resource stream failed\n");
5469 return NT_STATUS_INTERNAL_ERROR;
5471 return NT_STATUS_OK;
5474 static NTSTATUS fruit_streaminfo_rsrc_adouble(
5475 vfs_handle_struct *handle,
5476 struct files_struct *fsp,
5477 const struct smb_filename *smb_fname,
5478 TALLOC_CTX *mem_ctx,
5479 unsigned int *pnum_streams,
5480 struct stream_struct **pstreams)
5482 struct stream_struct *stream = *pstreams;
5483 unsigned int num_streams = *pnum_streams;
5484 struct adouble *ad = NULL;
5490 * Check if there's a AFPRESOURCE_STREAM from the VFS streams backend
5491 * and if yes, remove it from the list
5493 for (i = 0; i < num_streams; i++) {
5494 if (strequal_m(stream[i].name, AFPRESOURCE_STREAM)) {
5499 if (i < num_streams) {
5500 DBG_WARNING("Unexpected AFPRESOURCE_STREAM on [%s]\n",
5501 smb_fname_str_dbg(smb_fname));
5503 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5504 AFPRESOURCE_STREAM);
5506 return NT_STATUS_INTERNAL_ERROR;
5510 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
5512 return NT_STATUS_OK;
5515 rlen = ad_getentrylen(ad, ADEID_RFORK);
5519 return NT_STATUS_OK;
5522 ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
5523 AFPRESOURCE_STREAM_NAME, rlen,
5524 smb_roundup(handle->conn, rlen));
5526 return NT_STATUS_NO_MEMORY;
5529 return NT_STATUS_OK;
5532 static NTSTATUS fruit_streaminfo_rsrc(vfs_handle_struct *handle,
5533 struct files_struct *fsp,
5534 const struct smb_filename *smb_fname,
5535 TALLOC_CTX *mem_ctx,
5536 unsigned int *pnum_streams,
5537 struct stream_struct **pstreams)
5539 struct fruit_config_data *config = NULL;
5542 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5543 return NT_STATUS_INTERNAL_ERROR);
5545 switch (config->rsrc) {
5546 case FRUIT_RSRC_STREAM:
5547 status = fruit_streaminfo_rsrc_stream(handle, fsp, smb_fname,
5548 mem_ctx, pnum_streams,
5552 case FRUIT_RSRC_XATTR:
5553 status = fruit_streaminfo_rsrc_xattr(handle, fsp, smb_fname,
5554 mem_ctx, pnum_streams,
5558 case FRUIT_RSRC_ADFILE:
5559 status = fruit_streaminfo_rsrc_adouble(handle, fsp, smb_fname,
5560 mem_ctx, pnum_streams,
5565 return NT_STATUS_INTERNAL_ERROR;
5571 static NTSTATUS fruit_streaminfo(vfs_handle_struct *handle,
5572 struct files_struct *fsp,
5573 const struct smb_filename *smb_fname,
5574 TALLOC_CTX *mem_ctx,
5575 unsigned int *pnum_streams,
5576 struct stream_struct **pstreams)
5578 struct fruit_config_data *config = NULL;
5581 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5582 return NT_STATUS_UNSUCCESSFUL);
5584 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
5586 status = SMB_VFS_NEXT_STREAMINFO(handle, fsp, smb_fname, mem_ctx,
5587 pnum_streams, pstreams);
5588 if (!NT_STATUS_IS_OK(status)) {
5592 status = fruit_streaminfo_meta(handle, fsp, smb_fname,
5593 mem_ctx, pnum_streams, pstreams);
5594 if (!NT_STATUS_IS_OK(status)) {
5598 status = fruit_streaminfo_rsrc(handle, fsp, smb_fname,
5599 mem_ctx, pnum_streams, pstreams);
5600 if (!NT_STATUS_IS_OK(status)) {
5604 return NT_STATUS_OK;
5607 static int fruit_ntimes(vfs_handle_struct *handle,
5608 const struct smb_filename *smb_fname,
5609 struct smb_file_time *ft)
5612 struct adouble *ad = NULL;
5613 struct fruit_config_data *config = NULL;
5615 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5618 if ((config->meta != FRUIT_META_NETATALK) ||
5619 null_timespec(ft->create_time))
5621 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
5624 DEBUG(10,("set btime for %s to %s\n", smb_fname_str_dbg(smb_fname),
5625 time_to_asc(convert_timespec_to_time_t(ft->create_time))));
5627 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5632 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX,
5633 convert_time_t_to_uint32_t(ft->create_time.tv_sec));
5635 rc = ad_set(ad, smb_fname);
5641 DEBUG(1, ("fruit_ntimes: %s\n", smb_fname_str_dbg(smb_fname)));
5644 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
5647 static int fruit_fallocate(struct vfs_handle_struct *handle,
5648 struct files_struct *fsp,
5653 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5656 return SMB_VFS_NEXT_FALLOCATE(handle, fsp, mode, offset, len);
5659 /* Let the pwrite code path handle it. */
5664 static int fruit_ftruncate_rsrc_xattr(struct vfs_handle_struct *handle,
5665 struct files_struct *fsp,
5669 return SMB_VFS_FREMOVEXATTR(fsp, AFPRESOURCE_EA_NETATALK);
5672 #ifdef HAVE_ATTROPEN
5673 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
5678 static int fruit_ftruncate_rsrc_adouble(struct vfs_handle_struct *handle,
5679 struct files_struct *fsp,
5683 struct adouble *ad = NULL;
5686 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
5688 DBG_DEBUG("ad_get [%s] failed [%s]\n",
5689 fsp_str_dbg(fsp), strerror(errno));
5693 ad_off = ad_getentryoff(ad, ADEID_RFORK);
5695 rc = ftruncate(fsp->fh->fd, offset + ad_off);
5701 ad_setentrylen(ad, ADEID_RFORK, offset);
5703 rc = ad_fset(ad, fsp);
5705 DBG_ERR("ad_fset [%s] failed [%s]\n",
5706 fsp_str_dbg(fsp), strerror(errno));
5715 static int fruit_ftruncate_rsrc_stream(struct vfs_handle_struct *handle,
5716 struct files_struct *fsp,
5720 return SMB_VFS_NEXT_UNLINK(handle, fsp->fsp_name);
5723 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
5726 static int fruit_ftruncate_rsrc(struct vfs_handle_struct *handle,
5727 struct files_struct *fsp,
5730 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5734 DBG_ERR("Failed to fetch fsp extension");
5738 switch (fio->config->rsrc) {
5739 case FRUIT_RSRC_XATTR:
5740 ret = fruit_ftruncate_rsrc_xattr(handle, fsp, offset);
5743 case FRUIT_RSRC_ADFILE:
5744 ret = fruit_ftruncate_rsrc_adouble(handle, fsp, offset);
5747 case FRUIT_RSRC_STREAM:
5748 ret = fruit_ftruncate_rsrc_stream(handle, fsp, offset);
5752 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
5760 static int fruit_ftruncate_meta(struct vfs_handle_struct *handle,
5761 struct files_struct *fsp,
5765 DBG_WARNING("ftruncate %s to %jd",
5766 fsp_str_dbg(fsp), (intmax_t)offset);
5767 /* OS X returns NT_STATUS_ALLOTTED_SPACE_EXCEEDED */
5772 /* OS X returns success but does nothing */
5773 DBG_INFO("ignoring ftruncate %s to %jd\n",
5774 fsp_str_dbg(fsp), (intmax_t)offset);
5778 static int fruit_ftruncate(struct vfs_handle_struct *handle,
5779 struct files_struct *fsp,
5782 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5785 DBG_DEBUG("Path [%s] offset [%"PRIdMAX"]\n", fsp_str_dbg(fsp),
5790 global_fruit_config.nego_aapl &&
5791 is_ntfs_stream_smb_fname(fsp->fsp_name) &&
5792 !is_ntfs_default_stream_smb_fname(fsp->fsp_name))
5794 return SMB_VFS_NEXT_UNLINK(handle, fsp->fsp_name);
5796 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
5799 if (fio->type == ADOUBLE_META) {
5800 ret = fruit_ftruncate_meta(handle, fsp, offset);
5802 ret = fruit_ftruncate_rsrc(handle, fsp, offset);
5805 DBG_DEBUG("Path [%s] result [%d]\n", fsp_str_dbg(fsp), ret);
5809 static NTSTATUS fruit_create_file(vfs_handle_struct *handle,
5810 struct smb_request *req,
5811 uint16_t root_dir_fid,
5812 struct smb_filename *smb_fname,
5813 uint32_t access_mask,
5814 uint32_t share_access,
5815 uint32_t create_disposition,
5816 uint32_t create_options,
5817 uint32_t file_attributes,
5818 uint32_t oplock_request,
5819 struct smb2_lease *lease,
5820 uint64_t allocation_size,
5821 uint32_t private_flags,
5822 struct security_descriptor *sd,
5823 struct ea_list *ea_list,
5824 files_struct **result,
5826 const struct smb2_create_blobs *in_context_blobs,
5827 struct smb2_create_blobs *out_context_blobs)
5830 struct fruit_config_data *config = NULL;
5831 files_struct *fsp = NULL;
5833 status = check_aapl(handle, req, in_context_blobs, out_context_blobs);
5834 if (!NT_STATUS_IS_OK(status)) {
5838 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5839 return NT_STATUS_UNSUCCESSFUL);
5841 status = SMB_VFS_NEXT_CREATE_FILE(
5842 handle, req, root_dir_fid, smb_fname,
5843 access_mask, share_access,
5844 create_disposition, create_options,
5845 file_attributes, oplock_request,
5847 allocation_size, private_flags,
5848 sd, ea_list, result,
5849 pinfo, in_context_blobs, out_context_blobs);
5850 if (!NT_STATUS_IS_OK(status)) {
5856 if (global_fruit_config.nego_aapl) {
5857 if (config->posix_rename && fsp->is_directory) {
5859 * Enable POSIX directory rename behaviour
5861 fsp->posix_flags |= FSP_POSIX_FLAGS_RENAME;
5866 * If this is a plain open for existing files, opening an 0
5867 * byte size resource fork MUST fail with
5868 * NT_STATUS_OBJECT_NAME_NOT_FOUND.
5870 * Cf the vfs_fruit torture tests in test_rfork_create().
5872 if (is_afpresource_stream(fsp->fsp_name) &&
5873 create_disposition == FILE_OPEN)
5875 if (fsp->fsp_name->st.st_ex_size == 0) {
5876 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
5881 if (is_ntfs_stream_smb_fname(smb_fname)
5882 || fsp->is_directory) {
5886 if (config->locking == FRUIT_LOCKING_NETATALK) {
5887 status = fruit_check_access(
5890 map_share_mode_to_deny_mode(share_access, 0));
5891 if (!NT_STATUS_IS_OK(status)) {
5899 DEBUG(10, ("fruit_create_file: %s\n", nt_errstr(status)));
5902 close_file(req, fsp, ERROR_CLOSE);
5903 *result = fsp = NULL;
5909 static NTSTATUS fruit_readdir_attr(struct vfs_handle_struct *handle,
5910 const struct smb_filename *fname,
5911 TALLOC_CTX *mem_ctx,
5912 struct readdir_attr_data **pattr_data)
5914 struct fruit_config_data *config = NULL;
5915 struct readdir_attr_data *attr_data;
5918 SMB_VFS_HANDLE_GET_DATA(handle, config,
5919 struct fruit_config_data,
5920 return NT_STATUS_UNSUCCESSFUL);
5922 if (!global_fruit_config.nego_aapl) {
5923 return SMB_VFS_NEXT_READDIR_ATTR(handle, fname, mem_ctx, pattr_data);
5926 DEBUG(10, ("fruit_readdir_attr %s\n", fname->base_name));
5928 *pattr_data = talloc_zero(mem_ctx, struct readdir_attr_data);
5929 if (*pattr_data == NULL) {
5930 return NT_STATUS_UNSUCCESSFUL;
5932 attr_data = *pattr_data;
5933 attr_data->type = RDATTR_AAPL;
5936 * Mac metadata: compressed FinderInfo, resource fork length
5939 status = readdir_attr_macmeta(handle, fname, attr_data);
5940 if (!NT_STATUS_IS_OK(status)) {
5942 * Error handling is tricky: if we return failure from
5943 * this function, the corresponding directory entry
5944 * will to be passed to the client, so we really just
5945 * want to error out on fatal errors.
5947 if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
5955 if (config->unix_info_enabled) {
5956 attr_data->attr_data.aapl.unix_mode = fname->st.st_ex_mode;
5962 if (!config->readdir_attr_max_access) {
5963 attr_data->attr_data.aapl.max_access = FILE_GENERIC_ALL;
5965 status = smbd_calculate_access_mask(
5969 SEC_FLAG_MAXIMUM_ALLOWED,
5970 &attr_data->attr_data.aapl.max_access);
5971 if (!NT_STATUS_IS_OK(status)) {
5976 return NT_STATUS_OK;
5979 DEBUG(1, ("fruit_readdir_attr %s, error: %s\n",
5980 fname->base_name, nt_errstr(status)));
5981 TALLOC_FREE(*pattr_data);
5985 static NTSTATUS fruit_fget_nt_acl(vfs_handle_struct *handle,
5987 uint32_t security_info,
5988 TALLOC_CTX *mem_ctx,
5989 struct security_descriptor **ppdesc)
5992 struct security_ace ace;
5994 struct fruit_config_data *config;
5996 SMB_VFS_HANDLE_GET_DATA(handle, config,
5997 struct fruit_config_data,
5998 return NT_STATUS_UNSUCCESSFUL);
6000 status = SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
6002 if (!NT_STATUS_IS_OK(status)) {
6007 * Add MS NFS style ACEs with uid, gid and mode
6009 if (!global_fruit_config.nego_aapl) {
6010 return NT_STATUS_OK;
6012 if (!config->unix_info_enabled) {
6013 return NT_STATUS_OK;
6016 /* First remove any existing ACE's with NFS style mode/uid/gid SIDs. */
6017 status = remove_virtual_nfs_aces(*ppdesc);
6018 if (!NT_STATUS_IS_OK(status)) {
6019 DBG_WARNING("failed to remove MS NFS style ACEs\n");
6023 /* MS NFS style mode */
6024 sid_compose(&sid, &global_sid_Unix_NFS_Mode, fsp->fsp_name->st.st_ex_mode);
6025 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6026 status = security_descriptor_dacl_add(*ppdesc, &ace);
6027 if (!NT_STATUS_IS_OK(status)) {
6028 DEBUG(1,("failed to add MS NFS style ACE\n"));
6032 /* MS NFS style uid */
6033 sid_compose(&sid, &global_sid_Unix_NFS_Users, fsp->fsp_name->st.st_ex_uid);
6034 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6035 status = security_descriptor_dacl_add(*ppdesc, &ace);
6036 if (!NT_STATUS_IS_OK(status)) {
6037 DEBUG(1,("failed to add MS NFS style ACE\n"));
6041 /* MS NFS style gid */
6042 sid_compose(&sid, &global_sid_Unix_NFS_Groups, fsp->fsp_name->st.st_ex_gid);
6043 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6044 status = security_descriptor_dacl_add(*ppdesc, &ace);
6045 if (!NT_STATUS_IS_OK(status)) {
6046 DEBUG(1,("failed to add MS NFS style ACE\n"));
6050 return NT_STATUS_OK;
6053 static NTSTATUS fruit_fset_nt_acl(vfs_handle_struct *handle,
6055 uint32_t security_info_sent,
6056 const struct security_descriptor *orig_psd)
6060 mode_t ms_nfs_mode = 0;
6062 struct security_descriptor *psd = NULL;
6063 uint32_t orig_num_aces = 0;
6065 if (orig_psd->dacl != NULL) {
6066 orig_num_aces = orig_psd->dacl->num_aces;
6069 psd = security_descriptor_copy(talloc_tos(), orig_psd);
6071 return NT_STATUS_NO_MEMORY;
6074 DBG_DEBUG("fruit_fset_nt_acl: %s\n", fsp_str_dbg(fsp));
6076 status = check_ms_nfs(handle, fsp, psd, &ms_nfs_mode, &do_chmod);
6077 if (!NT_STATUS_IS_OK(status)) {
6078 DEBUG(1, ("fruit_fset_nt_acl: check_ms_nfs failed%s\n", fsp_str_dbg(fsp)));
6084 * If only ms_nfs ACE entries were sent, ensure we set the DACL
6085 * sent/present flags correctly now we've removed them.
6088 if (orig_num_aces != 0) {
6090 * Are there any ACE's left ?
6092 if (psd->dacl->num_aces == 0) {
6093 /* No - clear the DACL sent/present flags. */
6094 security_info_sent &= ~SECINFO_DACL;
6095 psd->type &= ~SEC_DESC_DACL_PRESENT;
6099 status = SMB_VFS_NEXT_FSET_NT_ACL(handle, fsp, security_info_sent, psd);
6100 if (!NT_STATUS_IS_OK(status)) {
6101 DEBUG(1, ("fruit_fset_nt_acl: SMB_VFS_NEXT_FSET_NT_ACL failed%s\n", fsp_str_dbg(fsp)));
6107 if (fsp->fh->fd != -1) {
6108 result = SMB_VFS_FCHMOD(fsp, ms_nfs_mode);
6110 result = SMB_VFS_CHMOD(fsp->conn,
6116 DEBUG(1, ("chmod: %s, result: %d, %04o error %s\n", fsp_str_dbg(fsp),
6117 result, (unsigned)ms_nfs_mode,
6119 status = map_nt_error_from_unix(errno);
6126 return NT_STATUS_OK;
6129 static struct vfs_offload_ctx *fruit_offload_ctx;
6131 struct fruit_offload_read_state {
6132 struct vfs_handle_struct *handle;
6133 struct tevent_context *ev;
6139 static void fruit_offload_read_done(struct tevent_req *subreq);
6141 static struct tevent_req *fruit_offload_read_send(
6142 TALLOC_CTX *mem_ctx,
6143 struct tevent_context *ev,
6144 struct vfs_handle_struct *handle,
6151 struct tevent_req *req = NULL;
6152 struct tevent_req *subreq = NULL;
6153 struct fruit_offload_read_state *state = NULL;
6155 req = tevent_req_create(mem_ctx, &state,
6156 struct fruit_offload_read_state);
6160 *state = (struct fruit_offload_read_state) {
6167 subreq = SMB_VFS_NEXT_OFFLOAD_READ_SEND(mem_ctx, ev, handle, fsp,
6168 fsctl, ttl, offset, to_copy);
6169 if (tevent_req_nomem(subreq, req)) {
6170 return tevent_req_post(req, ev);
6172 tevent_req_set_callback(subreq, fruit_offload_read_done, req);
6176 static void fruit_offload_read_done(struct tevent_req *subreq)
6178 struct tevent_req *req = tevent_req_callback_data(
6179 subreq, struct tevent_req);
6180 struct fruit_offload_read_state *state = tevent_req_data(
6181 req, struct fruit_offload_read_state);
6184 status = SMB_VFS_NEXT_OFFLOAD_READ_RECV(subreq,
6188 TALLOC_FREE(subreq);
6189 if (tevent_req_nterror(req, status)) {
6193 if (state->fsctl != FSCTL_SRV_REQUEST_RESUME_KEY) {
6194 tevent_req_done(req);
6198 status = vfs_offload_token_ctx_init(state->fsp->conn->sconn->client,
6199 &fruit_offload_ctx);
6200 if (tevent_req_nterror(req, status)) {
6204 status = vfs_offload_token_db_store_fsp(fruit_offload_ctx,
6207 if (tevent_req_nterror(req, status)) {
6211 tevent_req_done(req);
6215 static NTSTATUS fruit_offload_read_recv(struct tevent_req *req,
6216 struct vfs_handle_struct *handle,
6217 TALLOC_CTX *mem_ctx,
6220 struct fruit_offload_read_state *state = tevent_req_data(
6221 req, struct fruit_offload_read_state);
6224 if (tevent_req_is_nterror(req, &status)) {
6225 tevent_req_received(req);
6229 token->length = state->token.length;
6230 token->data = talloc_move(mem_ctx, &state->token.data);
6232 tevent_req_received(req);
6233 return NT_STATUS_OK;
6236 struct fruit_offload_write_state {
6237 struct vfs_handle_struct *handle;
6239 struct files_struct *src_fsp;
6240 struct files_struct *dst_fsp;
6244 static void fruit_offload_write_done(struct tevent_req *subreq);
6245 static struct tevent_req *fruit_offload_write_send(struct vfs_handle_struct *handle,
6246 TALLOC_CTX *mem_ctx,
6247 struct tevent_context *ev,
6250 off_t transfer_offset,
6251 struct files_struct *dest_fsp,
6255 struct tevent_req *req, *subreq;
6256 struct fruit_offload_write_state *state;
6258 struct fruit_config_data *config;
6259 off_t src_off = transfer_offset;
6260 files_struct *src_fsp = NULL;
6261 off_t to_copy = num;
6262 bool copyfile_enabled = false;
6264 DEBUG(10,("soff: %ju, doff: %ju, len: %ju\n",
6265 (uintmax_t)src_off, (uintmax_t)dest_off, (uintmax_t)num));
6267 SMB_VFS_HANDLE_GET_DATA(handle, config,
6268 struct fruit_config_data,
6271 req = tevent_req_create(mem_ctx, &state,
6272 struct fruit_offload_write_state);
6276 state->handle = handle;
6277 state->dst_fsp = dest_fsp;
6280 case FSCTL_SRV_COPYCHUNK:
6281 case FSCTL_SRV_COPYCHUNK_WRITE:
6282 copyfile_enabled = config->copyfile_enabled;
6289 * Check if this a OS X copyfile style copychunk request with
6290 * a requested chunk count of 0 that was translated to a
6291 * offload_write_send VFS call overloading the parameters src_off
6292 * = dest_off = num = 0.
6294 if (copyfile_enabled && num == 0 && src_off == 0 && dest_off == 0) {
6295 status = vfs_offload_token_db_fetch_fsp(
6296 fruit_offload_ctx, token, &src_fsp);
6297 if (tevent_req_nterror(req, status)) {
6298 return tevent_req_post(req, ev);
6300 state->src_fsp = src_fsp;
6302 status = vfs_stat_fsp(src_fsp);
6303 if (tevent_req_nterror(req, status)) {
6304 return tevent_req_post(req, ev);
6307 to_copy = src_fsp->fsp_name->st.st_ex_size;
6308 state->is_copyfile = true;
6311 subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle,
6320 if (tevent_req_nomem(subreq, req)) {
6321 return tevent_req_post(req, ev);
6324 tevent_req_set_callback(subreq, fruit_offload_write_done, req);
6328 static void fruit_offload_write_done(struct tevent_req *subreq)
6330 struct tevent_req *req = tevent_req_callback_data(
6331 subreq, struct tevent_req);
6332 struct fruit_offload_write_state *state = tevent_req_data(
6333 req, struct fruit_offload_write_state);
6335 unsigned int num_streams = 0;
6336 struct stream_struct *streams = NULL;
6338 struct smb_filename *src_fname_tmp = NULL;
6339 struct smb_filename *dst_fname_tmp = NULL;
6341 status = SMB_VFS_NEXT_OFFLOAD_WRITE_RECV(state->handle,
6344 TALLOC_FREE(subreq);
6345 if (tevent_req_nterror(req, status)) {
6349 if (!state->is_copyfile) {
6350 tevent_req_done(req);
6355 * Now copy all remaining streams. We know the share supports
6356 * streams, because we're in vfs_fruit. We don't do this async
6357 * because streams are few and small.
6359 status = vfs_streaminfo(state->handle->conn, state->src_fsp,
6360 state->src_fsp->fsp_name,
6361 req, &num_streams, &streams);
6362 if (tevent_req_nterror(req, status)) {
6366 if (num_streams == 1) {
6367 /* There is always one stream, ::$DATA. */
6368 tevent_req_done(req);
6372 for (i = 0; i < num_streams; i++) {
6373 DEBUG(10, ("%s: stream: '%s'/%zu\n",
6374 __func__, streams[i].name, (size_t)streams[i].size));
6376 src_fname_tmp = synthetic_smb_fname(
6378 state->src_fsp->fsp_name->base_name,
6381 state->src_fsp->fsp_name->flags);
6382 if (tevent_req_nomem(src_fname_tmp, req)) {
6386 if (is_ntfs_default_stream_smb_fname(src_fname_tmp)) {
6387 TALLOC_FREE(src_fname_tmp);
6391 dst_fname_tmp = synthetic_smb_fname(
6393 state->dst_fsp->fsp_name->base_name,
6396 state->dst_fsp->fsp_name->flags);
6397 if (tevent_req_nomem(dst_fname_tmp, req)) {
6398 TALLOC_FREE(src_fname_tmp);
6402 status = copy_file(req,
6403 state->handle->conn,
6406 OPENX_FILE_CREATE_IF_NOT_EXIST,
6408 if (!NT_STATUS_IS_OK(status)) {
6409 DEBUG(1, ("%s: copy %s to %s failed: %s\n", __func__,
6410 smb_fname_str_dbg(src_fname_tmp),
6411 smb_fname_str_dbg(dst_fname_tmp),
6412 nt_errstr(status)));
6413 TALLOC_FREE(src_fname_tmp);
6414 TALLOC_FREE(dst_fname_tmp);
6415 tevent_req_nterror(req, status);
6419 TALLOC_FREE(src_fname_tmp);
6420 TALLOC_FREE(dst_fname_tmp);
6423 TALLOC_FREE(streams);
6424 TALLOC_FREE(src_fname_tmp);
6425 TALLOC_FREE(dst_fname_tmp);
6426 tevent_req_done(req);
6429 static NTSTATUS fruit_offload_write_recv(struct vfs_handle_struct *handle,
6430 struct tevent_req *req,
6433 struct fruit_offload_write_state *state = tevent_req_data(
6434 req, struct fruit_offload_write_state);
6437 if (tevent_req_is_nterror(req, &status)) {
6438 DEBUG(1, ("server side copy chunk failed: %s\n",
6439 nt_errstr(status)));
6441 tevent_req_received(req);
6445 *copied = state->copied;
6446 tevent_req_received(req);
6448 return NT_STATUS_OK;
6451 static char *fruit_get_bandsize_line(char **lines, int numlines)
6454 static bool re_initialized = false;
6458 if (!re_initialized) {
6459 ret = regcomp(&re, "^[[:blank:]]*<key>band-size</key>$", 0);
6463 re_initialized = true;
6466 for (i = 0; i < numlines; i++) {
6467 regmatch_t matches[1];
6469 ret = regexec(&re, lines[i], 1, matches, 0);
6472 * Check if the match was on the last line, sa we want
6473 * the subsequent line.
6475 if (i + 1 == numlines) {
6478 return lines[i + 1];
6480 if (ret != REG_NOMATCH) {
6488 static bool fruit_get_bandsize_from_line(char *line, size_t *_band_size)
6491 static bool re_initialized = false;
6492 regmatch_t matches[2];
6497 if (!re_initialized) {
6500 "<integer>\\([[:digit:]]*\\)</integer>$",
6505 re_initialized = true;
6508 ret = regexec(&re, line, 2, matches, 0);
6510 DBG_ERR("regex failed [%s]\n", line);
6514 line[matches[1].rm_eo] = '\0';
6516 ok = conv_str_u64(&line[matches[1].rm_so], &band_size);
6520 *_band_size = (size_t)band_size;
6525 * This reads and parses an Info.plist from a TM sparsebundle looking for the
6526 * "band-size" key and value.
6528 static bool fruit_get_bandsize(vfs_handle_struct *handle,
6532 #define INFO_PLIST_MAX_SIZE 64*1024
6534 struct smb_filename *smb_fname = NULL;
6535 files_struct *fsp = NULL;
6536 uint8_t *file_data = NULL;
6537 char **lines = NULL;
6538 char *band_size_line = NULL;
6539 size_t plist_file_size;
6546 plist = talloc_asprintf(talloc_tos(),
6548 handle->conn->connectpath,
6550 if (plist == NULL) {
6555 smb_fname = synthetic_smb_fname(talloc_tos(), plist, NULL, NULL, 0);
6556 if (smb_fname == NULL) {
6561 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
6563 DBG_INFO("Ignoring Sparsebundle without Info.plist [%s]\n", dir);
6568 plist_file_size = smb_fname->st.st_ex_size;
6570 if (plist_file_size > INFO_PLIST_MAX_SIZE) {
6571 DBG_INFO("%s is too large, ignoring\n", plist);
6576 status = SMB_VFS_NEXT_CREATE_FILE(
6579 0, /* root_dir_fid */
6580 smb_fname, /* fname */
6581 FILE_GENERIC_READ, /* access_mask */
6582 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
6583 FILE_OPEN, /* create_disposition */
6584 0, /* create_options */
6585 0, /* file_attributes */
6586 INTERNAL_OPEN_ONLY, /* oplock_request */
6588 0, /* allocation_size */
6589 0, /* private_flags */
6594 NULL, NULL); /* create context */
6595 if (!NT_STATUS_IS_OK(status)) {
6596 DBG_INFO("Opening [%s] failed [%s]\n",
6597 smb_fname_str_dbg(smb_fname), nt_errstr(status));
6602 file_data = talloc_array(talloc_tos(), uint8_t, plist_file_size);
6603 if (file_data == NULL) {
6608 nread = SMB_VFS_NEXT_PREAD(handle, fsp, file_data, plist_file_size, 0);
6609 if (nread != plist_file_size) {
6610 DBG_ERR("Short read on [%s]: %zu/%zd\n",
6611 fsp_str_dbg(fsp), nread, plist_file_size);
6617 status = close_file(NULL, fsp, NORMAL_CLOSE);
6619 if (!NT_STATUS_IS_OK(status)) {
6620 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
6625 lines = file_lines_parse((char *)file_data,
6629 if (lines == NULL) {
6634 band_size_line = fruit_get_bandsize_line(lines, numlines);
6635 if (band_size_line == NULL) {
6636 DBG_ERR("Didn't find band-size key in [%s]\n",
6637 smb_fname_str_dbg(smb_fname));
6642 ok = fruit_get_bandsize_from_line(band_size_line, band_size);
6644 DBG_ERR("fruit_get_bandsize_from_line failed\n");
6648 DBG_DEBUG("Parsed band-size [%zu] for [%s]\n", *band_size, plist);
6652 status = close_file(NULL, fsp, NORMAL_CLOSE);
6653 if (!NT_STATUS_IS_OK(status)) {
6654 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
6659 TALLOC_FREE(smb_fname);
6660 TALLOC_FREE(file_data);
6665 struct fruit_disk_free_state {
6669 static bool fruit_get_num_bands(vfs_handle_struct *handle,
6674 struct smb_filename *bands_dir = NULL;
6676 struct dirent *e = NULL;
6680 path = talloc_asprintf(talloc_tos(),
6682 handle->conn->connectpath,
6688 bands_dir = synthetic_smb_fname(talloc_tos(),
6694 if (bands_dir == NULL) {
6698 d = SMB_VFS_NEXT_OPENDIR(handle, bands_dir, NULL, 0);
6700 TALLOC_FREE(bands_dir);
6706 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
6708 e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
6710 if (ISDOT(e->d_name) || ISDOTDOT(e->d_name)) {
6716 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
6718 TALLOC_FREE(bands_dir);
6722 DBG_DEBUG("%zu bands in [%s]\n", nbands, smb_fname_str_dbg(bands_dir));
6724 TALLOC_FREE(bands_dir);
6730 static bool fruit_tmsize_do_dirent(vfs_handle_struct *handle,
6731 struct fruit_disk_free_state *state,
6736 size_t sparsebundle_strlen = strlen("sparsebundle");
6737 size_t bandsize = 0;
6741 p = strstr(e->d_name, "sparsebundle");
6746 if (p[sparsebundle_strlen] != '\0') {
6750 DBG_DEBUG("Processing sparsebundle [%s]\n", e->d_name);
6752 ok = fruit_get_bandsize(handle, e->d_name, &bandsize);
6755 * Beware of race conditions: this may be an uninitialized
6756 * Info.plist that a client is just creating. We don't want let
6757 * this to trigger complete failure.
6759 DBG_ERR("Processing sparsebundle [%s] failed\n", e->d_name);
6763 ok = fruit_get_num_bands(handle, e->d_name, &nbands);
6766 * Beware of race conditions: this may be a backup sparsebundle
6767 * in an early stage lacking a bands subdirectory. We don't want
6768 * let this to trigger complete failure.
6770 DBG_ERR("Processing sparsebundle [%s] failed\n", e->d_name);
6774 if (bandsize > SIZE_MAX/nbands) {
6775 DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
6779 tm_size = bandsize * nbands;
6781 if (state->total_size + tm_size < state->total_size) {
6782 DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
6787 state->total_size += tm_size;
6789 DBG_DEBUG("[%s] tm_size [%jd] total_size [%jd]\n",
6790 e->d_name, (intmax_t)tm_size, (intmax_t)state->total_size);
6796 * Calculate used size of a TimeMachine volume
6798 * This assumes that the volume is used only for TimeMachine.
6800 * - readdir(basedir of share), then
6801 * - for every element that matches regex "^\(.*\)\.sparsebundle$" :
6802 * - parse "\1.sparsebundle/Info.plist" and read the band-size XML key
6803 * - count band files in "\1.sparsebundle/bands/"
6804 * - calculate used size of all bands: band_count * band_size
6806 static uint64_t fruit_disk_free(vfs_handle_struct *handle,
6807 const struct smb_filename *smb_fname,
6812 struct fruit_config_data *config = NULL;
6813 struct fruit_disk_free_state state = {0};
6815 struct dirent *e = NULL;
6821 SMB_VFS_HANDLE_GET_DATA(handle, config,
6822 struct fruit_config_data,
6825 if (!config->time_machine ||
6826 config->time_machine_max_size == 0)
6828 return SMB_VFS_NEXT_DISK_FREE(handle,
6835 d = SMB_VFS_NEXT_OPENDIR(handle, smb_fname, NULL, 0);
6840 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
6842 e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
6844 ok = fruit_tmsize_do_dirent(handle, &state, e);
6846 SMB_VFS_NEXT_CLOSEDIR(handle, d);
6851 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
6856 dsize = config->time_machine_max_size / 512;
6857 dfree = dsize - (state.total_size / 512);
6858 if (dfree > dsize) {
6868 static struct vfs_fn_pointers vfs_fruit_fns = {
6869 .connect_fn = fruit_connect,
6870 .disk_free_fn = fruit_disk_free,
6872 /* File operations */
6873 .chmod_fn = fruit_chmod,
6874 .chown_fn = fruit_chown,
6875 .unlink_fn = fruit_unlink,
6876 .rename_fn = fruit_rename,
6877 .rmdir_fn = fruit_rmdir,
6878 .open_fn = fruit_open,
6879 .pread_fn = fruit_pread,
6880 .pwrite_fn = fruit_pwrite,
6881 .pread_send_fn = fruit_pread_send,
6882 .pread_recv_fn = fruit_pread_recv,
6883 .pwrite_send_fn = fruit_pwrite_send,
6884 .pwrite_recv_fn = fruit_pwrite_recv,
6885 .stat_fn = fruit_stat,
6886 .lstat_fn = fruit_lstat,
6887 .fstat_fn = fruit_fstat,
6888 .streaminfo_fn = fruit_streaminfo,
6889 .ntimes_fn = fruit_ntimes,
6890 .ftruncate_fn = fruit_ftruncate,
6891 .fallocate_fn = fruit_fallocate,
6892 .create_file_fn = fruit_create_file,
6893 .readdir_attr_fn = fruit_readdir_attr,
6894 .offload_read_send_fn = fruit_offload_read_send,
6895 .offload_read_recv_fn = fruit_offload_read_recv,
6896 .offload_write_send_fn = fruit_offload_write_send,
6897 .offload_write_recv_fn = fruit_offload_write_recv,
6899 /* NT ACL operations */
6900 .fget_nt_acl_fn = fruit_fget_nt_acl,
6901 .fset_nt_acl_fn = fruit_fset_nt_acl,
6905 NTSTATUS vfs_fruit_init(TALLOC_CTX *ctx)
6907 NTSTATUS ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "fruit",
6909 if (!NT_STATUS_IS_OK(ret)) {
6913 vfs_fruit_debug_level = debug_add_class("fruit");
6914 if (vfs_fruit_debug_level == -1) {
6915 vfs_fruit_debug_level = DBGC_VFS;
6916 DEBUG(0, ("%s: Couldn't register custom debugging class!\n",
6919 DEBUG(10, ("%s: Debug class number of '%s': %d\n",
6920 "vfs_fruit_init","fruit",vfs_fruit_debug_level));