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;
146 bool delete_empty_adfiles;
149 * Additional options, all enabled by default,
150 * possibly useful for analyzing performance. The associated
151 * operations with each of them may be expensive, so having
152 * the chance to disable them individually gives a chance
153 * tweaking the setup for the particular usecase.
155 bool readdir_attr_rsize;
156 bool readdir_attr_finder_info;
157 bool readdir_attr_max_access;
160 static const struct enum_list fruit_rsrc[] = {
161 {FRUIT_RSRC_STREAM, "stream"}, /* pass on to vfs_streams_xattr */
162 {FRUIT_RSRC_ADFILE, "file"}, /* ._ AppleDouble file */
163 {FRUIT_RSRC_XATTR, "xattr"}, /* Netatalk compatible xattr (ZFS only) */
167 static const struct enum_list fruit_meta[] = {
168 {FRUIT_META_STREAM, "stream"}, /* pass on to vfs_streams_xattr */
169 {FRUIT_META_NETATALK, "netatalk"}, /* Netatalk compatible xattr */
173 static const struct enum_list fruit_locking[] = {
174 {FRUIT_LOCKING_NETATALK, "netatalk"}, /* synchronize locks with Netatalk */
175 {FRUIT_LOCKING_NONE, "none"},
179 static const struct enum_list fruit_encoding[] = {
180 {FRUIT_ENC_NATIVE, "native"}, /* map unicode private chars to ASCII */
181 {FRUIT_ENC_PRIVATE, "private"}, /* keep unicode private chars */
185 static const char *fruit_catia_maps =
186 "0x01:0xf001,0x02:0xf002,0x03:0xf003,0x04:0xf004,"
187 "0x05:0xf005,0x06:0xf006,0x07:0xf007,0x08:0xf008,"
188 "0x09:0xf009,0x0a:0xf00a,0x0b:0xf00b,0x0c:0xf00c,"
189 "0x0d:0xf00d,0x0e:0xf00e,0x0f:0xf00f,0x10:0xf010,"
190 "0x11:0xf011,0x12:0xf012,0x13:0xf013,0x14:0xf014,"
191 "0x15:0xf015,0x16:0xf016,0x17:0xf017,0x18:0xf018,"
192 "0x19:0xf019,0x1a:0xf01a,0x1b:0xf01b,0x1c:0xf01c,"
193 "0x1d:0xf01d,0x1e:0xf01e,0x1f:0xf01f,"
194 "0x22:0xf020,0x2a:0xf021,0x3a:0xf022,0x3c:0xf023,"
195 "0x3e:0xf024,0x3f:0xf025,0x5c:0xf026,0x7c:0xf027,"
198 /*****************************************************************************
199 * Defines, functions and data structures that deal with AppleDouble
200 *****************************************************************************/
203 * There are two AppleDouble blobs we deal with:
205 * - ADOUBLE_META - AppleDouble blob used by Netatalk for storing
206 * metadata in an xattr
208 * - ADOUBLE_RSRC - AppleDouble blob used by OS X and Netatalk in
211 typedef enum {ADOUBLE_META, ADOUBLE_RSRC} adouble_type_t;
214 #define AD_VERSION2 0x00020000
215 #define AD_VERSION AD_VERSION2
218 * AppleDouble entry IDs.
220 #define ADEID_DFORK 1
221 #define ADEID_RFORK 2
223 #define ADEID_COMMENT 4
224 #define ADEID_ICONBW 5
225 #define ADEID_ICONCOL 6
226 #define ADEID_FILEI 7
227 #define ADEID_FILEDATESI 8
228 #define ADEID_FINDERI 9
229 #define ADEID_MACFILEI 10
230 #define ADEID_PRODOSFILEI 11
231 #define ADEID_MSDOSFILEI 12
232 #define ADEID_SHORTNAME 13
233 #define ADEID_AFPFILEI 14
236 /* Private Netatalk entries */
237 #define ADEID_PRIVDEV 16
238 #define ADEID_PRIVINO 17
239 #define ADEID_PRIVSYN 18
240 #define ADEID_PRIVID 19
241 #define ADEID_MAX (ADEID_PRIVID + 1)
244 * These are the real ids for the private entries,
245 * as stored in the adouble file
247 #define AD_DEV 0x80444556
248 #define AD_INO 0x80494E4F
249 #define AD_SYN 0x8053594E
250 #define AD_ID 0x8053567E
252 /* Number of actually used entries */
253 #define ADEID_NUM_XATTR 8
254 #define ADEID_NUM_DOT_UND 2
255 #define ADEID_NUM_RSRC_XATTR 1
257 /* AppleDouble magic */
258 #define AD_APPLESINGLE_MAGIC 0x00051600
259 #define AD_APPLEDOUBLE_MAGIC 0x00051607
260 #define AD_MAGIC AD_APPLEDOUBLE_MAGIC
262 /* Sizes of relevant entry bits */
263 #define ADEDLEN_MAGIC 4
264 #define ADEDLEN_VERSION 4
265 #define ADEDLEN_FILLER 16
266 #define AD_FILLER_TAG "Netatalk " /* should be 16 bytes */
267 #define AD_FILLER_TAG_OSX "Mac OS X " /* should be 16 bytes */
268 #define ADEDLEN_NENTRIES 2
269 #define AD_HEADER_LEN (ADEDLEN_MAGIC + ADEDLEN_VERSION + \
270 ADEDLEN_FILLER + ADEDLEN_NENTRIES) /* 26 */
271 #define AD_ENTRY_LEN_EID 4
272 #define AD_ENTRY_LEN_OFF 4
273 #define AD_ENTRY_LEN_LEN 4
274 #define AD_ENTRY_LEN (AD_ENTRY_LEN_EID + AD_ENTRY_LEN_OFF + AD_ENTRY_LEN_LEN)
277 #define ADEDLEN_NAME 255
278 #define ADEDLEN_COMMENT 200
279 #define ADEDLEN_FILEI 16
280 #define ADEDLEN_FINDERI 32
281 #define ADEDLEN_FILEDATESI 16
282 #define ADEDLEN_SHORTNAME 12 /* length up to 8.3 */
283 #define ADEDLEN_AFPFILEI 4
284 #define ADEDLEN_MACFILEI 4
285 #define ADEDLEN_PRODOSFILEI 8
286 #define ADEDLEN_MSDOSFILEI 2
287 #define ADEDLEN_DID 4
288 #define ADEDLEN_PRIVDEV 8
289 #define ADEDLEN_PRIVINO 8
290 #define ADEDLEN_PRIVSYN 8
291 #define ADEDLEN_PRIVID 4
294 #define ADEDOFF_MAGIC 0
295 #define ADEDOFF_VERSION (ADEDOFF_MAGIC + ADEDLEN_MAGIC)
296 #define ADEDOFF_FILLER (ADEDOFF_VERSION + ADEDLEN_VERSION)
297 #define ADEDOFF_NENTRIES (ADEDOFF_FILLER + ADEDLEN_FILLER)
299 #define ADEDOFF_FINDERI_XATTR (AD_HEADER_LEN + \
300 (ADEID_NUM_XATTR * AD_ENTRY_LEN))
301 #define ADEDOFF_COMMENT_XATTR (ADEDOFF_FINDERI_XATTR + ADEDLEN_FINDERI)
302 #define ADEDOFF_FILEDATESI_XATTR (ADEDOFF_COMMENT_XATTR + ADEDLEN_COMMENT)
303 #define ADEDOFF_AFPFILEI_XATTR (ADEDOFF_FILEDATESI_XATTR + \
305 #define ADEDOFF_PRIVDEV_XATTR (ADEDOFF_AFPFILEI_XATTR + ADEDLEN_AFPFILEI)
306 #define ADEDOFF_PRIVINO_XATTR (ADEDOFF_PRIVDEV_XATTR + ADEDLEN_PRIVDEV)
307 #define ADEDOFF_PRIVSYN_XATTR (ADEDOFF_PRIVINO_XATTR + ADEDLEN_PRIVINO)
308 #define ADEDOFF_PRIVID_XATTR (ADEDOFF_PRIVSYN_XATTR + ADEDLEN_PRIVSYN)
310 #define ADEDOFF_FINDERI_DOT_UND (AD_HEADER_LEN + \
311 (ADEID_NUM_DOT_UND * AD_ENTRY_LEN))
312 #define ADEDOFF_RFORK_DOT_UND (ADEDOFF_FINDERI_DOT_UND + ADEDLEN_FINDERI)
314 #define AD_DATASZ_XATTR (AD_HEADER_LEN + \
315 (ADEID_NUM_XATTR * AD_ENTRY_LEN) + \
316 ADEDLEN_FINDERI + ADEDLEN_COMMENT + \
317 ADEDLEN_FILEDATESI + ADEDLEN_AFPFILEI + \
318 ADEDLEN_PRIVDEV + ADEDLEN_PRIVINO + \
319 ADEDLEN_PRIVSYN + ADEDLEN_PRIVID)
321 #if AD_DATASZ_XATTR != 402
322 #error bad size for AD_DATASZ_XATTR
325 #define AD_DATASZ_DOT_UND (AD_HEADER_LEN + \
326 (ADEID_NUM_DOT_UND * AD_ENTRY_LEN) + \
328 #if AD_DATASZ_DOT_UND != 82
329 #error bad size for AD_DATASZ_DOT_UND
333 * Sharemode locks fcntl() offsets
335 #if _FILE_OFFSET_BITS == 64 || defined(HAVE_LARGEFILE)
336 #define AD_FILELOCK_BASE (UINT64_C(0x7FFFFFFFFFFFFFFF) - 9)
338 #define AD_FILELOCK_BASE (UINT32_C(0x7FFFFFFF) - 9)
340 #define BYTELOCK_MAX (AD_FILELOCK_BASE - 1)
342 #define AD_FILELOCK_OPEN_WR (AD_FILELOCK_BASE + 0)
343 #define AD_FILELOCK_OPEN_RD (AD_FILELOCK_BASE + 1)
344 #define AD_FILELOCK_RSRC_OPEN_WR (AD_FILELOCK_BASE + 2)
345 #define AD_FILELOCK_RSRC_OPEN_RD (AD_FILELOCK_BASE + 3)
346 #define AD_FILELOCK_DENY_WR (AD_FILELOCK_BASE + 4)
347 #define AD_FILELOCK_DENY_RD (AD_FILELOCK_BASE + 5)
348 #define AD_FILELOCK_RSRC_DENY_WR (AD_FILELOCK_BASE + 6)
349 #define AD_FILELOCK_RSRC_DENY_RD (AD_FILELOCK_BASE + 7)
350 #define AD_FILELOCK_OPEN_NONE (AD_FILELOCK_BASE + 8)
351 #define AD_FILELOCK_RSRC_OPEN_NONE (AD_FILELOCK_BASE + 9)
353 /* Time stuff we overload the bits a little */
354 #define AD_DATE_CREATE 0
355 #define AD_DATE_MODIFY 4
356 #define AD_DATE_BACKUP 8
357 #define AD_DATE_ACCESS 12
358 #define AD_DATE_MASK (AD_DATE_CREATE | AD_DATE_MODIFY | \
359 AD_DATE_BACKUP | AD_DATE_ACCESS)
360 #define AD_DATE_UNIX (1 << 10)
361 #define AD_DATE_START 0x80000000
362 #define AD_DATE_DELTA 946684800
363 #define AD_DATE_FROM_UNIX(x) (htonl((x) - AD_DATE_DELTA))
364 #define AD_DATE_TO_UNIX(x) (ntohl(x) + AD_DATE_DELTA)
366 #define AD_XATTR_HDR_MAGIC 0x41545452 /* 'ATTR' */
367 #define AD_XATTR_MAX_ENTRIES 1024 /* Some arbitrarily enforced limit */
368 #define AD_XATTR_HDR_SIZE 36
369 #define AD_XATTR_MAX_HDR_SIZE 65536
371 /* Accessor macros */
372 #define ad_getentrylen(ad,eid) ((ad)->ad_eid[(eid)].ade_len)
373 #define ad_getentryoff(ad,eid) ((ad)->ad_eid[(eid)].ade_off)
374 #define ad_setentrylen(ad,eid,len) ((ad)->ad_eid[(eid)].ade_len = (len))
375 #define ad_setentryoff(ad,eid,off) ((ad)->ad_eid[(eid)].ade_off = (off))
378 * Both struct ad_xattr_header and struct ad_xattr_entry describe the in memory
379 * representation as well as the on-disk format.
381 * The ad_xattr_header follows the FinderInfo data in the FinderInfo entry if
382 * the length of the FinderInfo entry is larger then 32 bytes. It is then
383 * preceeded with 2 bytes padding.
385 * Cf: https://opensource.apple.com/source/xnu/xnu-4570.1.46/bsd/vfs/vfs_xattr.c
388 struct ad_xattr_header {
389 uint32_t adx_magic; /* ATTR_HDR_MAGIC */
390 uint32_t adx_debug_tag; /* for debugging == file id of owning file */
391 uint32_t adx_total_size; /* file offset of end of attribute header + entries + data */
392 uint32_t adx_data_start; /* file offset to attribute data area */
393 uint32_t adx_data_length; /* length of attribute data area */
394 uint32_t adx_reserved[3];
396 uint16_t adx_num_attrs;
399 /* On-disk entries are aligned on 4 byte boundaries */
400 struct ad_xattr_entry {
401 uint32_t adx_offset; /* file offset to data */
402 uint32_t adx_length; /* size of attribute data */
404 uint8_t adx_namelen; /* included the NULL terminator */
405 char *adx_name; /* NULL-terminated UTF-8 name */
414 vfs_handle_struct *ad_handle;
417 adouble_type_t ad_type;
420 uint8_t ad_filler[ADEDLEN_FILLER];
421 struct ad_entry ad_eid[ADEID_MAX];
423 struct ad_xattr_header adx_header;
424 struct ad_xattr_entry *adx_entries;
427 struct ad_entry_order {
428 uint32_t id, offset, len;
431 /* Netatalk AppleDouble metadata xattr */
433 struct ad_entry_order entry_order_meta_xattr[ADEID_NUM_XATTR + 1] = {
434 {ADEID_FINDERI, ADEDOFF_FINDERI_XATTR, ADEDLEN_FINDERI},
435 {ADEID_COMMENT, ADEDOFF_COMMENT_XATTR, 0},
436 {ADEID_FILEDATESI, ADEDOFF_FILEDATESI_XATTR, ADEDLEN_FILEDATESI},
437 {ADEID_AFPFILEI, ADEDOFF_AFPFILEI_XATTR, ADEDLEN_AFPFILEI},
438 {ADEID_PRIVDEV, ADEDOFF_PRIVDEV_XATTR, 0},
439 {ADEID_PRIVINO, ADEDOFF_PRIVINO_XATTR, 0},
440 {ADEID_PRIVSYN, ADEDOFF_PRIVSYN_XATTR, 0},
441 {ADEID_PRIVID, ADEDOFF_PRIVID_XATTR, 0},
445 /* AppleDouble resource fork file (the ones prefixed by "._") */
447 struct ad_entry_order entry_order_dot_und[ADEID_NUM_DOT_UND + 1] = {
448 {ADEID_FINDERI, ADEDOFF_FINDERI_DOT_UND, ADEDLEN_FINDERI},
449 {ADEID_RFORK, ADEDOFF_RFORK_DOT_UND, 0},
454 * Fake AppleDouble entry oder for resource fork xattr. The xattr
455 * isn't an AppleDouble file, it simply contains the resource data,
456 * but in order to be able to use some API calls like ad_getentryoff()
457 * we build a fake/helper struct adouble with this entry order struct.
460 struct ad_entry_order entry_order_rsrc_xattr[ADEID_NUM_RSRC_XATTR + 1] = {
465 /* Conversion from enumerated id to on-disk AppleDouble id */
466 #define AD_EID_DISK(a) (set_eid[a])
467 static const uint32_t set_eid[] = {
468 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
469 AD_DEV, AD_INO, AD_SYN, AD_ID
472 static char empty_resourcefork[] = {
473 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
474 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
475 0x54, 0x68, 0x69, 0x73, 0x20, 0x72, 0x65, 0x73,
476 0x6F, 0x75, 0x72, 0x63, 0x65, 0x20, 0x66, 0x6F,
477 0x72, 0x6B, 0x20, 0x69, 0x6E, 0x74, 0x65, 0x6E,
478 0x74, 0x69, 0x6F, 0x6E, 0x61, 0x6C, 0x6C, 0x79,
479 0x20, 0x6C, 0x65, 0x66, 0x74, 0x20, 0x62, 0x6C,
480 0x61, 0x6E, 0x6B, 0x20, 0x20, 0x20, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
505 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
506 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
507 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
508 0x00, 0x1C, 0x00, 0x1E, 0xFF, 0xFF
512 /* tcon config handle */
513 struct fruit_config_data *config;
515 /* Denote stream type, meta or rsrc */
518 /* Whether the create created the stream */
522 * AFP_AfpInfo stream created, but not written yet, thus still a fake
523 * pipe fd. This is set to true in fruit_open_meta if there was no
524 * exisiting stream but the caller requested O_CREAT. It is later set to
525 * false when we get a write on the stream that then does open and
534 * Forward declarations
536 static struct adouble *ad_init(TALLOC_CTX *ctx, vfs_handle_struct *handle,
537 adouble_type_t type);
538 static int ad_set(struct adouble *ad, const struct smb_filename *smb_fname);
539 static int ad_fset(struct adouble *ad, files_struct *fsp);
540 static int adouble_path(TALLOC_CTX *ctx,
541 const struct smb_filename *smb_fname__in,
542 struct smb_filename **ppsmb_fname_out);
543 static AfpInfo *afpinfo_new(TALLOC_CTX *ctx);
544 static ssize_t afpinfo_pack(const AfpInfo *ai, char *buf);
545 static AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data);
549 * Return a pointer to an AppleDouble entry
551 * Returns NULL if the entry is not present
553 static char *ad_get_entry(const struct adouble *ad, int eid)
555 off_t off = ad_getentryoff(ad, eid);
556 size_t len = ad_getentrylen(ad, eid);
558 if (off == 0 || len == 0) {
562 return ad->ad_data + off;
568 static int ad_getdate(const struct adouble *ad,
569 unsigned int dateoff,
572 bool xlate = (dateoff & AD_DATE_UNIX);
575 dateoff &= AD_DATE_MASK;
576 p = ad_get_entry(ad, ADEID_FILEDATESI);
581 if (dateoff > AD_DATE_ACCESS) {
585 memcpy(date, p + dateoff, sizeof(uint32_t));
588 *date = AD_DATE_TO_UNIX(*date);
596 static int ad_setdate(struct adouble *ad, unsigned int dateoff, uint32_t date)
598 bool xlate = (dateoff & AD_DATE_UNIX);
601 p = ad_get_entry(ad, ADEID_FILEDATESI);
606 dateoff &= AD_DATE_MASK;
608 date = AD_DATE_FROM_UNIX(date);
611 if (dateoff > AD_DATE_ACCESS) {
615 memcpy(p + dateoff, &date, sizeof(date));
622 * Map on-disk AppleDouble id to enumerated id
624 static uint32_t get_eid(uint32_t eid)
632 return ADEID_PRIVDEV;
634 return ADEID_PRIVINO;
636 return ADEID_PRIVSYN;
647 * Pack AppleDouble structure into data buffer
649 static bool ad_pack(struct adouble *ad)
656 bufsize = talloc_get_size(ad->ad_data);
657 if (bufsize < AD_DATASZ_DOT_UND) {
658 DBG_ERR("bad buffer size [0x%" PRIx32 "]\n", bufsize);
662 if (offset + ADEDLEN_MAGIC < offset ||
663 offset + ADEDLEN_MAGIC >= bufsize) {
666 RSIVAL(ad->ad_data, offset, ad->ad_magic);
667 offset += ADEDLEN_MAGIC;
669 if (offset + ADEDLEN_VERSION < offset ||
670 offset + ADEDLEN_VERSION >= bufsize) {
673 RSIVAL(ad->ad_data, offset, ad->ad_version);
674 offset += ADEDLEN_VERSION;
676 if (offset + ADEDLEN_FILLER < offset ||
677 offset + ADEDLEN_FILLER >= bufsize) {
680 if (ad->ad_type == ADOUBLE_RSRC) {
681 memcpy(ad->ad_data + offset, AD_FILLER_TAG, ADEDLEN_FILLER);
683 offset += ADEDLEN_FILLER;
685 if (offset + ADEDLEN_NENTRIES < offset ||
686 offset + ADEDLEN_NENTRIES >= bufsize) {
689 offset += ADEDLEN_NENTRIES;
691 for (eid = 0, nent = 0; eid < ADEID_MAX; eid++) {
692 if (ad->ad_eid[eid].ade_off == 0) {
694 * ade_off is also used as indicator whether a
695 * specific entry is used or not
700 if (offset + AD_ENTRY_LEN_EID < offset ||
701 offset + AD_ENTRY_LEN_EID >= bufsize) {
704 RSIVAL(ad->ad_data, offset, AD_EID_DISK(eid));
705 offset += AD_ENTRY_LEN_EID;
707 if (offset + AD_ENTRY_LEN_OFF < offset ||
708 offset + AD_ENTRY_LEN_OFF >= bufsize) {
711 RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_off);
712 offset += AD_ENTRY_LEN_OFF;
714 if (offset + AD_ENTRY_LEN_LEN < offset ||
715 offset + AD_ENTRY_LEN_LEN >= bufsize) {
718 RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_len);
719 offset += AD_ENTRY_LEN_LEN;
724 if (ADEDOFF_NENTRIES + 2 >= bufsize) {
727 RSSVAL(ad->ad_data, ADEDOFF_NENTRIES, nent);
732 static bool ad_unpack_xattrs(struct adouble *ad)
734 struct ad_xattr_header *h = &ad->adx_header;
735 const char *p = ad->ad_data;
739 if (ad_getentrylen(ad, ADEID_FINDERI) <= ADEDLEN_FINDERI) {
743 /* 2 bytes padding */
744 hoff = ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI + 2;
746 h->adx_magic = RIVAL(p, hoff + 0);
747 h->adx_debug_tag = RIVAL(p, hoff + 4); /* Not used -> not checked */
748 h->adx_total_size = RIVAL(p, hoff + 8);
749 h->adx_data_start = RIVAL(p, hoff + 12);
750 h->adx_data_length = RIVAL(p, hoff + 16);
751 h->adx_flags = RSVAL(p, hoff + 32); /* Not used -> not checked */
752 h->adx_num_attrs = RSVAL(p, hoff + 34);
754 if (h->adx_magic != AD_XATTR_HDR_MAGIC) {
755 DBG_ERR("Bad magic: 0x%" PRIx32 "\n", h->adx_magic);
759 if (h->adx_total_size > ad_getentryoff(ad, ADEID_RFORK)) {
760 DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size);
763 if (h->adx_total_size > AD_XATTR_MAX_HDR_SIZE) {
764 DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size);
768 if (h->adx_data_start < (hoff + AD_XATTR_HDR_SIZE)) {
769 DBG_ERR("Bad start: 0x%" PRIx32 "\n", h->adx_data_start);
773 if ((h->adx_data_start + h->adx_data_length) < h->adx_data_start) {
774 DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length);
777 if ((h->adx_data_start + h->adx_data_length) >
778 ad->adx_header.adx_total_size)
780 DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length);
784 if (h->adx_num_attrs > AD_XATTR_MAX_ENTRIES) {
785 DBG_ERR("Bad num xattrs: %" PRIu16 "\n", h->adx_num_attrs);
789 if (h->adx_num_attrs == 0) {
793 ad->adx_entries = talloc_zero_array(
794 ad, struct ad_xattr_entry, h->adx_num_attrs);
795 if (ad->adx_entries == NULL) {
799 hoff += AD_XATTR_HDR_SIZE;
801 for (i = 0; i < h->adx_num_attrs; i++) {
802 struct ad_xattr_entry *e = &ad->adx_entries[i];
804 hoff = (hoff + 3) & ~3;
806 e->adx_offset = RIVAL(p, hoff + 0);
807 e->adx_length = RIVAL(p, hoff + 4);
808 e->adx_flags = RSVAL(p, hoff + 8);
809 e->adx_namelen = *(p + hoff + 10);
811 if (e->adx_offset >= ad->adx_header.adx_total_size) {
812 DBG_ERR("Bad adx_offset: %" PRIx32 "\n",
817 if ((e->adx_offset + e->adx_length) < e->adx_offset) {
818 DBG_ERR("Bad adx_length: %" PRIx32 "\n",
823 if ((e->adx_offset + e->adx_length) >
824 ad->adx_header.adx_total_size)
826 DBG_ERR("Bad adx_length: %" PRIx32 "\n",
831 if (e->adx_namelen == 0) {
832 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
836 if ((hoff + 11 + e->adx_namelen) < hoff + 11) {
837 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
841 if ((hoff + 11 + e->adx_namelen) >
842 ad->adx_header.adx_data_start)
844 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
849 e->adx_name = talloc_strndup(ad->adx_entries,
852 if (e->adx_name == NULL) {
856 DBG_DEBUG("xattr [%s] offset [0x%x] size [0x%x]\n",
857 e->adx_name, e->adx_offset, e->adx_length);
858 dump_data(10, (uint8_t *)(ad->ad_data + e->adx_offset),
861 hoff += 11 + e->adx_namelen;
868 * Unpack an AppleDouble blob into a struct adoble
870 static bool ad_unpack(struct adouble *ad, const size_t nentries,
873 size_t bufsize = talloc_get_size(ad->ad_data);
875 uint32_t eid, len, off;
879 * The size of the buffer ad->ad_data is checked when read, so
880 * we wouldn't have to check our own offsets, a few extra
881 * checks won't hurt though. We have to check the offsets we
882 * read from the buffer anyway.
885 if (bufsize < (AD_HEADER_LEN + (AD_ENTRY_LEN * nentries))) {
886 DEBUG(1, ("bad size\n"));
890 ad->ad_magic = RIVAL(ad->ad_data, 0);
891 ad->ad_version = RIVAL(ad->ad_data, ADEDOFF_VERSION);
892 if ((ad->ad_magic != AD_MAGIC) || (ad->ad_version != AD_VERSION)) {
893 DEBUG(1, ("wrong magic or version\n"));
897 memcpy(ad->ad_filler, ad->ad_data + ADEDOFF_FILLER, ADEDLEN_FILLER);
899 adentries = RSVAL(ad->ad_data, ADEDOFF_NENTRIES);
900 if (adentries != nentries) {
901 DEBUG(1, ("invalid number of entries: %zu\n",
906 /* now, read in the entry bits */
907 for (i = 0; i < adentries; i++) {
908 eid = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN));
910 off = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 4);
911 len = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 8);
913 if (!eid || eid >= ADEID_MAX) {
914 DEBUG(1, ("bogus eid %d\n", eid));
919 * All entries other than the resource fork are
920 * expected to be read into the ad_data buffer, so
921 * ensure the specified offset is within that bound
923 if ((off > bufsize) && (eid != ADEID_RFORK)) {
924 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
930 * All entries besides FinderInfo and resource fork
931 * must fit into the buffer. FinderInfo is special as
932 * it may be larger then the default 32 bytes (if it
933 * contains marshalled xattrs), but we will fixup that
934 * in ad_convert(). And the resource fork is never
935 * accessed directly by the ad_data buf (also see
936 * comment above) anyway.
938 if ((eid != ADEID_RFORK) &&
939 (eid != ADEID_FINDERI) &&
940 ((off + len) > bufsize)) {
941 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
947 * That would be obviously broken
949 if (off > filesize) {
950 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
956 * Check for any entry that has its end beyond the
959 if (off + len < off) {
960 DEBUG(1, ("offset wrap in eid %d: off: %" PRIu32
961 ", len: %" PRIu32 "\n",
966 if (off + len > filesize) {
968 * If this is the resource fork entry, we fix
969 * up the length, for any other entry we bail
972 if (eid != ADEID_RFORK) {
973 DEBUG(1, ("bogus eid %d: off: %" PRIu32
974 ", len: %" PRIu32 "\n",
980 * Fixup the resource fork entry by limiting
981 * the size to entryoffset - filesize.
983 len = filesize - off;
984 DEBUG(1, ("Limiting ADEID_RFORK: off: %" PRIu32
985 ", len: %" PRIu32 "\n", off, len));
988 ad->ad_eid[eid].ade_off = off;
989 ad->ad_eid[eid].ade_len = len;
992 ok = ad_unpack_xattrs(ad);
1000 static bool ad_convert_move_reso(struct adouble *ad,
1001 const struct smb_filename *smb_fname)
1003 char *map = MAP_FAILED;
1009 if (ad_getentrylen(ad, ADEID_RFORK) == 0) {
1013 maplen = ad_getentryoff(ad, ADEID_RFORK) +
1014 ad_getentrylen(ad, ADEID_RFORK);
1016 /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
1017 map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
1019 if (map == MAP_FAILED) {
1020 DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
1025 memmove(map + ADEDOFF_RFORK_DOT_UND,
1026 map + ad_getentryoff(ad, ADEID_RFORK),
1027 ad_getentrylen(ad, ADEID_RFORK));
1029 rc = munmap(map, maplen);
1031 DBG_ERR("munmap failed: %s\n", strerror(errno));
1035 ad_setentryoff(ad, ADEID_RFORK, ADEDOFF_RFORK_DOT_UND);
1039 DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name);
1043 len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
1044 if (len != AD_DATASZ_DOT_UND) {
1045 DBG_ERR("%s: bad size: %zd\n", smb_fname->base_name, len);
1052 static bool ad_convert_xattr(struct adouble *ad,
1053 const struct smb_filename *smb_fname,
1054 bool *converted_xattr)
1056 static struct char_mappings **string_replace_cmaps = NULL;
1057 char *map = MAP_FAILED;
1061 int saved_errno = 0;
1066 *converted_xattr = false;
1068 if (ad_getentrylen(ad, ADEID_FINDERI) == ADEDLEN_FINDERI) {
1072 if (string_replace_cmaps == NULL) {
1073 const char **mappings = NULL;
1075 mappings = str_list_make_v3_const(
1076 talloc_tos(), fruit_catia_maps, NULL);
1077 if (mappings == NULL) {
1080 string_replace_cmaps = string_replace_init_map(mappings);
1081 TALLOC_FREE(mappings);
1084 maplen = ad_getentryoff(ad, ADEID_RFORK) +
1085 ad_getentrylen(ad, ADEID_RFORK);
1087 /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
1088 map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
1090 if (map == MAP_FAILED) {
1091 DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
1095 for (i = 0; i < ad->adx_header.adx_num_attrs; i++) {
1096 struct ad_xattr_entry *e = &ad->adx_entries[i];
1097 char *mapped_name = NULL;
1099 struct smb_filename *stream_name = NULL;
1100 files_struct *fsp = NULL;
1103 status = string_replace_allocate(ad->ad_handle->conn,
1105 string_replace_cmaps,
1108 vfs_translate_to_windows);
1109 if (!NT_STATUS_IS_OK(status) &&
1110 !NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))
1112 DBG_ERR("string_replace_allocate failed\n");
1118 mapped_name = talloc_asprintf(talloc_tos(), ":%s", tmp);
1120 if (mapped_name == NULL) {
1125 stream_name = synthetic_smb_fname(talloc_tos(),
1126 smb_fname->base_name,
1130 TALLOC_FREE(mapped_name);
1131 if (stream_name == NULL) {
1132 DBG_ERR("synthetic_smb_fname failed\n");
1137 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1139 status = SMB_VFS_CREATE_FILE(
1140 ad->ad_handle->conn, /* conn */
1142 0, /* root_dir_fid */
1143 stream_name, /* fname */
1144 FILE_GENERIC_WRITE, /* access_mask */
1145 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
1146 FILE_OPEN_IF, /* create_disposition */
1147 0, /* create_options */
1148 0, /* file_attributes */
1149 INTERNAL_OPEN_ONLY, /* oplock_request */
1151 0, /* allocation_size */
1152 0, /* private_flags */
1157 NULL, NULL); /* create context */
1158 TALLOC_FREE(stream_name);
1159 if (!NT_STATUS_IS_OK(status)) {
1160 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1165 nwritten = SMB_VFS_PWRITE(fsp,
1166 map + e->adx_offset,
1169 if (nwritten == -1) {
1170 DBG_ERR("SMB_VFS_PWRITE failed\n");
1171 saved_errno = errno;
1172 close_file(NULL, fsp, ERROR_CLOSE);
1173 errno = saved_errno;
1178 status = close_file(NULL, fsp, NORMAL_CLOSE);
1179 if (!NT_STATUS_IS_OK(status)) {
1186 ad_setentrylen(ad, ADEID_FINDERI, ADEDLEN_FINDERI);
1190 DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name);
1194 len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
1195 if (len != AD_DATASZ_DOT_UND) {
1196 DBG_ERR("%s: bad size: %zd\n", smb_fname->base_name, len);
1201 ok = ad_convert_move_reso(ad, smb_fname);
1206 *converted_xattr = true;
1210 rc = munmap(map, maplen);
1212 DBG_ERR("munmap failed: %s\n", strerror(errno));
1219 static bool ad_convert_finderinfo(struct adouble *ad,
1220 const struct smb_filename *smb_fname)
1225 struct smb_filename *stream_name = NULL;
1226 files_struct *fsp = NULL;
1230 int saved_errno = 0;
1233 cmp = memcmp(ad->ad_filler, AD_FILLER_TAG_OSX, ADEDLEN_FILLER);
1238 p_ad = ad_get_entry(ad, ADEID_FINDERI);
1243 ai = afpinfo_new(talloc_tos());
1248 memcpy(ai->afpi_FinderInfo, p_ad, ADEDLEN_FINDERI);
1250 aiblob = data_blob_talloc(talloc_tos(), NULL, AFP_INFO_SIZE);
1251 if (aiblob.data == NULL) {
1256 size = afpinfo_pack(ai, (char *)aiblob.data);
1258 if (size != AFP_INFO_SIZE) {
1262 stream_name = synthetic_smb_fname(talloc_tos(),
1263 smb_fname->base_name,
1267 if (stream_name == NULL) {
1268 data_blob_free(&aiblob);
1269 DBG_ERR("synthetic_smb_fname failed\n");
1273 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1275 status = SMB_VFS_CREATE_FILE(
1276 ad->ad_handle->conn, /* conn */
1278 0, /* root_dir_fid */
1279 stream_name, /* fname */
1280 FILE_GENERIC_WRITE, /* access_mask */
1281 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
1282 FILE_OPEN_IF, /* create_disposition */
1283 0, /* create_options */
1284 0, /* file_attributes */
1285 INTERNAL_OPEN_ONLY, /* oplock_request */
1287 0, /* allocation_size */
1288 0, /* private_flags */
1293 NULL, NULL); /* create context */
1294 TALLOC_FREE(stream_name);
1295 if (!NT_STATUS_IS_OK(status)) {
1296 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1300 nwritten = SMB_VFS_PWRITE(fsp,
1304 if (nwritten == -1) {
1305 DBG_ERR("SMB_VFS_PWRITE failed\n");
1306 saved_errno = errno;
1307 close_file(NULL, fsp, ERROR_CLOSE);
1308 errno = saved_errno;
1312 status = close_file(NULL, fsp, NORMAL_CLOSE);
1313 if (!NT_STATUS_IS_OK(status)) {
1321 static bool ad_convert_truncate(struct adouble *ad,
1322 const struct smb_filename *smb_fname)
1327 * FIXME: direct ftruncate(), but we don't have a fsp for the
1330 rc = ftruncate(ad->ad_fd, ADEDOFF_RFORK_DOT_UND +
1331 ad_getentrylen(ad, ADEID_RFORK));
1339 static bool ad_convert_blank_rfork(struct adouble *ad,
1342 struct fruit_config_data *config = NULL;
1343 uint8_t *map = MAP_FAILED;
1352 SMB_VFS_HANDLE_GET_DATA(ad->ad_handle, config,
1353 struct fruit_config_data, return false);
1355 if (!config->wipe_intentionally_left_blank_rfork) {
1359 if (ad_getentrylen(ad, ADEID_RFORK) != sizeof(empty_resourcefork)) {
1363 maplen = ad_getentryoff(ad, ADEID_RFORK) +
1364 ad_getentrylen(ad, ADEID_RFORK);
1366 /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
1367 map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
1369 if (map == MAP_FAILED) {
1370 DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
1374 cmp = memcmp(map + ADEDOFF_RFORK_DOT_UND,
1376 sizeof(empty_resourcefork));
1377 rc = munmap(map, maplen);
1379 DBG_ERR("munmap failed: %s\n", strerror(errno));
1387 ad_setentrylen(ad, ADEID_RFORK, 0);
1394 len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
1395 if (len != AD_DATASZ_DOT_UND) {
1403 static bool ad_convert_delete_adfile(struct adouble *ad,
1404 const struct smb_filename *smb_fname)
1406 struct fruit_config_data *config = NULL;
1407 struct smb_filename *ad_name = NULL;
1410 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
1414 SMB_VFS_HANDLE_GET_DATA(ad->ad_handle, config,
1415 struct fruit_config_data, return false);
1417 if (!config->delete_empty_adfiles) {
1421 rc = adouble_path(talloc_tos(), smb_fname, &ad_name);
1426 rc = SMB_VFS_NEXT_UNLINK(ad->ad_handle, ad_name);
1428 DBG_ERR("Unlinking [%s] failed: %s\n",
1429 smb_fname_str_dbg(ad_name), strerror(errno));
1430 TALLOC_FREE(ad_name);
1434 DBG_WARNING("Unlinked [%s] after conversion\n", smb_fname_str_dbg(ad_name));
1435 TALLOC_FREE(ad_name);
1441 * Convert from Apple's ._ file to Netatalk
1443 * Apple's AppleDouble may contain a FinderInfo entry longer then 32
1444 * bytes containing packed xattrs.
1446 * @return -1 in case an error occurred, 0 if no conversion was done, 1
1449 static int ad_convert(struct adouble *ad,
1450 const struct smb_filename *smb_fname)
1453 bool converted_xattr = false;
1456 ok = ad_convert_xattr(ad, smb_fname, &converted_xattr);
1461 ok = ad_convert_blank_rfork(ad, &blank);
1466 if (converted_xattr || blank) {
1467 ok = ad_convert_truncate(ad, smb_fname);
1473 ok = ad_convert_finderinfo(ad, smb_fname);
1475 DBG_ERR("Failed to convert [%s]\n",
1476 smb_fname_str_dbg(smb_fname));
1480 ok = ad_convert_delete_adfile(ad, smb_fname);
1489 * Read and parse Netatalk AppleDouble metadata xattr
1491 static ssize_t ad_read_meta(struct adouble *ad,
1492 const struct smb_filename *smb_fname)
1498 DEBUG(10, ("reading meta xattr for %s\n", smb_fname->base_name));
1500 ealen = SMB_VFS_GETXATTR(ad->ad_handle->conn, smb_fname,
1501 AFPINFO_EA_NETATALK, ad->ad_data,
1507 if (errno == ENOATTR) {
1513 DEBUG(2, ("error reading meta xattr: %s\n",
1519 if (ealen != AD_DATASZ_XATTR) {
1520 DEBUG(2, ("bad size %zd\n", ealen));
1526 /* Now parse entries */
1527 ok = ad_unpack(ad, ADEID_NUM_XATTR, AD_DATASZ_XATTR);
1529 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1535 if (!ad_getentryoff(ad, ADEID_FINDERI)
1536 || !ad_getentryoff(ad, ADEID_COMMENT)
1537 || !ad_getentryoff(ad, ADEID_FILEDATESI)
1538 || !ad_getentryoff(ad, ADEID_AFPFILEI)
1539 || !ad_getentryoff(ad, ADEID_PRIVDEV)
1540 || !ad_getentryoff(ad, ADEID_PRIVINO)
1541 || !ad_getentryoff(ad, ADEID_PRIVSYN)
1542 || !ad_getentryoff(ad, ADEID_PRIVID)) {
1543 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1550 DEBUG(10, ("reading meta xattr for %s, rc: %d\n",
1551 smb_fname->base_name, rc));
1555 if (errno == EINVAL) {
1557 removexattr(smb_fname->base_name, AFPINFO_EA_NETATALK);
1565 static int ad_open_rsrc_xattr(const struct smb_filename *smb_fname,
1569 #ifdef HAVE_ATTROPEN
1570 /* FIXME: direct Solaris xattr syscall */
1571 return attropen(smb_fname->base_name,
1572 AFPRESOURCE_EA_NETATALK, flags, mode);
1579 static int ad_open_rsrc_adouble(const struct smb_filename *smb_fname,
1585 struct smb_filename *adp_smb_fname = NULL;
1587 ret = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
1592 fd = open(adp_smb_fname->base_name, flags, mode);
1593 TALLOC_FREE(adp_smb_fname);
1598 static int ad_open_rsrc(vfs_handle_struct *handle,
1599 const struct smb_filename *smb_fname,
1603 struct fruit_config_data *config = NULL;
1606 SMB_VFS_HANDLE_GET_DATA(handle, config,
1607 struct fruit_config_data, return -1);
1609 if (config->rsrc == FRUIT_RSRC_XATTR) {
1610 fd = ad_open_rsrc_xattr(smb_fname, flags, mode);
1612 fd = ad_open_rsrc_adouble(smb_fname, flags, mode);
1619 * Here's the deal: for ADOUBLE_META we can do without an fd as we can issue
1620 * path based xattr calls. For ADOUBLE_RSRC however we need a full-fledged fd
1621 * for file IO on the ._ file.
1623 static int ad_open(vfs_handle_struct *handle,
1626 const struct smb_filename *smb_fname,
1632 DBG_DEBUG("Path [%s] type [%s]\n", smb_fname->base_name,
1633 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
1635 if (ad->ad_type == ADOUBLE_META) {
1639 if ((fsp != NULL) && (fsp->fh != NULL) && (fsp->fh->fd != -1)) {
1640 ad->ad_fd = fsp->fh->fd;
1641 ad->ad_opened = false;
1645 fd = ad_open_rsrc(handle, smb_fname, flags, mode);
1649 ad->ad_opened = true;
1652 DBG_DEBUG("Path [%s] type [%s] fd [%d]\n",
1653 smb_fname->base_name,
1654 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc", fd);
1659 static ssize_t ad_read_rsrc_xattr(struct adouble *ad)
1664 /* FIXME: direct sys_fstat(), don't have an fsp */
1665 ret = sys_fstat(ad->ad_fd, &st,
1666 lp_fake_directory_create_times(
1667 SNUM(ad->ad_handle->conn)));
1672 ad_setentrylen(ad, ADEID_RFORK, st.st_ex_size);
1673 return st.st_ex_size;
1676 static ssize_t ad_read_rsrc_adouble(struct adouble *ad,
1677 const struct smb_filename *smb_fname)
1679 SMB_STRUCT_STAT sbuf;
1686 ret = sys_fstat(ad->ad_fd, &sbuf, lp_fake_directory_create_times(
1687 SNUM(ad->ad_handle->conn)));
1693 * AppleDouble file header content and size, two cases:
1695 * - without xattrs it is exactly AD_DATASZ_DOT_UND (82) bytes large
1696 * - with embedded xattrs it can be larger, up to AD_XATTR_MAX_HDR_SIZE
1698 * Read as much as we can up to AD_XATTR_MAX_HDR_SIZE.
1700 size = sbuf.st_ex_size;
1701 if (size > talloc_array_length(ad->ad_data)) {
1702 if (size > AD_XATTR_MAX_HDR_SIZE) {
1703 size = AD_XATTR_MAX_HDR_SIZE;
1705 p_ad = talloc_realloc(ad, ad->ad_data, char, size);
1712 len = sys_pread(ad->ad_fd, ad->ad_data,
1713 talloc_array_length(ad->ad_data), 0);
1714 if (len != talloc_array_length(ad->ad_data)) {
1715 DBG_NOTICE("%s %s: bad size: %zd\n",
1716 smb_fname->base_name, strerror(errno), len);
1720 /* Now parse entries */
1721 ok = ad_unpack(ad, ADEID_NUM_DOT_UND, sbuf.st_ex_size);
1723 DBG_ERR("invalid AppleDouble resource %s\n",
1724 smb_fname->base_name);
1729 if ((ad_getentryoff(ad, ADEID_FINDERI) != ADEDOFF_FINDERI_DOT_UND)
1730 || (ad_getentrylen(ad, ADEID_FINDERI) < ADEDLEN_FINDERI)
1731 || (ad_getentryoff(ad, ADEID_RFORK) < ADEDOFF_RFORK_DOT_UND)) {
1732 DBG_ERR("invalid AppleDouble resource %s\n",
1733 smb_fname->base_name);
1739 * Try to fixup AppleDouble files created by OS X with xattrs
1740 * appended to the ADEID_FINDERI entry.
1743 ret = ad_convert(ad, smb_fname);
1745 DBG_WARNING("Failed to convert [%s]\n", smb_fname->base_name);
1753 * Read and parse resource fork, either ._ AppleDouble file or xattr
1755 static ssize_t ad_read_rsrc(struct adouble *ad,
1756 const struct smb_filename *smb_fname)
1758 struct fruit_config_data *config = NULL;
1761 SMB_VFS_HANDLE_GET_DATA(ad->ad_handle, config,
1762 struct fruit_config_data, return -1);
1764 if (config->rsrc == FRUIT_RSRC_XATTR) {
1765 len = ad_read_rsrc_xattr(ad);
1767 len = ad_read_rsrc_adouble(ad, smb_fname);
1774 * Read and unpack an AppleDouble metadata xattr or resource
1776 static ssize_t ad_read(struct adouble *ad, const struct smb_filename *smb_fname)
1778 switch (ad->ad_type) {
1780 return ad_read_meta(ad, smb_fname);
1782 return ad_read_rsrc(ad, smb_fname);
1788 static int adouble_destructor(struct adouble *ad)
1790 if ((ad->ad_fd != -1) && ad->ad_opened) {
1798 * Allocate a struct adouble without initialiing it
1800 * The struct is either hang of the fsp extension context or if fsp is
1803 * @param[in] ctx talloc context
1804 * @param[in] handle vfs handle
1805 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1807 * @return adouble handle
1809 static struct adouble *ad_alloc(TALLOC_CTX *ctx, vfs_handle_struct *handle,
1810 adouble_type_t type)
1815 struct fruit_config_data *config;
1817 SMB_VFS_HANDLE_GET_DATA(handle, config,
1818 struct fruit_config_data, return NULL);
1822 adsize = AD_DATASZ_XATTR;
1825 if (config->rsrc == FRUIT_RSRC_ADFILE) {
1826 adsize = AD_DATASZ_DOT_UND;
1833 ad = talloc_zero(ctx, struct adouble);
1840 ad->ad_data = talloc_zero_array(ad, char, adsize);
1841 if (ad->ad_data == NULL) {
1847 ad->ad_handle = handle;
1849 ad->ad_magic = AD_MAGIC;
1850 ad->ad_version = AD_VERSION;
1853 talloc_set_destructor(ad, adouble_destructor);
1863 * Allocate and initialize a new struct adouble
1865 * @param[in] ctx talloc context
1866 * @param[in] handle vfs handle
1867 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1869 * @return adouble handle, initialized
1871 static struct adouble *ad_init(TALLOC_CTX *ctx, vfs_handle_struct *handle,
1872 adouble_type_t type)
1875 const struct ad_entry_order *eid;
1876 struct adouble *ad = NULL;
1877 struct fruit_config_data *config;
1878 time_t t = time(NULL);
1880 SMB_VFS_HANDLE_GET_DATA(handle, config,
1881 struct fruit_config_data, return NULL);
1885 eid = entry_order_meta_xattr;
1888 if (config->rsrc == FRUIT_RSRC_ADFILE) {
1889 eid = entry_order_dot_und;
1891 eid = entry_order_rsrc_xattr;
1898 ad = ad_alloc(ctx, handle, type);
1904 ad->ad_eid[eid->id].ade_off = eid->offset;
1905 ad->ad_eid[eid->id].ade_len = eid->len;
1909 /* put something sane in the date fields */
1910 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, t);
1911 ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, t);
1912 ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, t);
1913 ad_setdate(ad, AD_DATE_BACKUP, htonl(AD_DATE_START));
1921 static struct adouble *ad_get_internal(TALLOC_CTX *ctx,
1922 vfs_handle_struct *handle,
1924 const struct smb_filename *smb_fname,
1925 adouble_type_t type)
1929 struct adouble *ad = NULL;
1933 smb_fname = fsp->base_fsp->fsp_name;
1936 DEBUG(10, ("ad_get(%s) called for %s\n",
1937 type == ADOUBLE_META ? "meta" : "rsrc",
1938 smb_fname->base_name));
1940 ad = ad_alloc(ctx, handle, type);
1946 /* Try rw first so we can use the fd in ad_convert() */
1949 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
1950 if (rc == -1 && ((errno == EROFS) || (errno == EACCES))) {
1952 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
1955 DBG_DEBUG("ad_open [%s] error [%s]\n",
1956 smb_fname->base_name, strerror(errno));
1961 len = ad_read(ad, smb_fname);
1963 DEBUG(10, ("error reading AppleDouble for %s\n",
1964 smb_fname->base_name));
1970 DEBUG(10, ("ad_get(%s) for %s returning %d\n",
1971 type == ADOUBLE_META ? "meta" : "rsrc",
1972 smb_fname->base_name, rc));
1981 * Return AppleDouble data for a file
1983 * @param[in] ctx talloc context
1984 * @param[in] handle vfs handle
1985 * @param[in] smb_fname pathname to file or directory
1986 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1988 * @return talloced struct adouble or NULL on error
1990 static struct adouble *ad_get(TALLOC_CTX *ctx,
1991 vfs_handle_struct *handle,
1992 const struct smb_filename *smb_fname,
1993 adouble_type_t type)
1995 return ad_get_internal(ctx, handle, NULL, smb_fname, type);
1999 * Return AppleDouble data for a file
2001 * @param[in] ctx talloc context
2002 * @param[in] handle vfs handle
2003 * @param[in] fsp fsp to use for IO
2004 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2006 * @return talloced struct adouble or NULL on error
2008 static struct adouble *ad_fget(TALLOC_CTX *ctx, vfs_handle_struct *handle,
2009 files_struct *fsp, adouble_type_t type)
2011 return ad_get_internal(ctx, handle, fsp, NULL, type);
2015 * Set AppleDouble metadata on a file or directory
2017 * @param[in] ad adouble handle
2019 * @param[in] smb_fname pathname to file or directory
2021 * @return status code, 0 means success
2023 static int ad_set(struct adouble *ad, const struct smb_filename *smb_fname)
2028 DBG_DEBUG("Path [%s]\n", smb_fname->base_name);
2030 if (ad->ad_type != ADOUBLE_META) {
2031 DBG_ERR("ad_set on [%s] used with ADOUBLE_RSRC\n",
2032 smb_fname->base_name);
2041 ret = SMB_VFS_SETXATTR(ad->ad_handle->conn,
2043 AFPINFO_EA_NETATALK,
2045 AD_DATASZ_XATTR, 0);
2047 DBG_DEBUG("Path [%s] ret [%d]\n", smb_fname->base_name, ret);
2053 * Set AppleDouble metadata on a file or directory
2055 * @param[in] ad adouble handle
2056 * @param[in] fsp file handle
2058 * @return status code, 0 means success
2060 static int ad_fset(struct adouble *ad, files_struct *fsp)
2066 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
2069 || (fsp->fh == NULL)
2070 || (fsp->fh->fd == -1))
2072 smb_panic("bad fsp");
2080 switch (ad->ad_type) {
2082 rc = SMB_VFS_NEXT_SETXATTR(ad->ad_handle,
2084 AFPINFO_EA_NETATALK,
2086 AD_DATASZ_XATTR, 0);
2090 len = SMB_VFS_NEXT_PWRITE(ad->ad_handle,
2095 if (len != AD_DATASZ_DOT_UND) {
2096 DBG_ERR("short write on %s: %zd", fsp_str_dbg(fsp), len);
2106 DBG_DEBUG("Path [%s] rc [%d]\n", fsp_str_dbg(fsp), rc);
2111 /*****************************************************************************
2113 *****************************************************************************/
2115 static bool is_afpinfo_stream(const struct smb_filename *smb_fname)
2117 if (strncasecmp_m(smb_fname->stream_name,
2118 AFPINFO_STREAM_NAME,
2119 strlen(AFPINFO_STREAM_NAME)) == 0) {
2125 static bool is_afpresource_stream(const struct smb_filename *smb_fname)
2127 if (strncasecmp_m(smb_fname->stream_name,
2128 AFPRESOURCE_STREAM_NAME,
2129 strlen(AFPRESOURCE_STREAM_NAME)) == 0) {
2136 * Test whether stream is an Apple stream, not used atm
2139 static bool is_apple_stream(const struct smb_filename *smb_fname)
2141 if (is_afpinfo_stream(smb_fname)) {
2144 if (is_afpresource_stream(smb_fname)) {
2152 * Initialize config struct from our smb.conf config parameters
2154 static int init_fruit_config(vfs_handle_struct *handle)
2156 struct fruit_config_data *config;
2158 const char *tm_size_str = NULL;
2160 config = talloc_zero(handle->conn, struct fruit_config_data);
2162 DEBUG(1, ("talloc_zero() failed\n"));
2168 * Versions up to Samba 4.5.x had a spelling bug in the
2169 * fruit:resource option calling lp_parm_enum with
2170 * "res*s*ource" (ie two s).
2172 * In Samba 4.6 we accept both the wrong and the correct
2173 * spelling, in Samba 4.7 the bad spelling will be removed.
2175 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2176 "ressource", fruit_rsrc, FRUIT_RSRC_ADFILE);
2177 if (enumval == -1) {
2178 DEBUG(1, ("value for %s: resource type unknown\n",
2179 FRUIT_PARAM_TYPE_NAME));
2182 config->rsrc = (enum fruit_rsrc)enumval;
2184 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2185 "resource", fruit_rsrc, enumval);
2186 if (enumval == -1) {
2187 DEBUG(1, ("value for %s: resource type unknown\n",
2188 FRUIT_PARAM_TYPE_NAME));
2191 config->rsrc = (enum fruit_rsrc)enumval;
2193 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2194 "metadata", fruit_meta, FRUIT_META_NETATALK);
2195 if (enumval == -1) {
2196 DEBUG(1, ("value for %s: metadata type unknown\n",
2197 FRUIT_PARAM_TYPE_NAME));
2200 config->meta = (enum fruit_meta)enumval;
2202 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2203 "locking", fruit_locking, FRUIT_LOCKING_NONE);
2204 if (enumval == -1) {
2205 DEBUG(1, ("value for %s: locking type unknown\n",
2206 FRUIT_PARAM_TYPE_NAME));
2209 config->locking = (enum fruit_locking)enumval;
2211 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2212 "encoding", fruit_encoding, FRUIT_ENC_PRIVATE);
2213 if (enumval == -1) {
2214 DEBUG(1, ("value for %s: encoding type unknown\n",
2215 FRUIT_PARAM_TYPE_NAME));
2218 config->encoding = (enum fruit_encoding)enumval;
2220 if (config->rsrc == FRUIT_RSRC_ADFILE) {
2221 config->veto_appledouble = lp_parm_bool(SNUM(handle->conn),
2222 FRUIT_PARAM_TYPE_NAME,
2227 config->use_aapl = lp_parm_bool(
2228 -1, FRUIT_PARAM_TYPE_NAME, "aapl", true);
2230 config->time_machine = lp_parm_bool(
2231 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "time machine", false);
2233 config->unix_info_enabled = lp_parm_bool(
2234 -1, FRUIT_PARAM_TYPE_NAME, "nfs_aces", true);
2236 config->use_copyfile = lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME,
2239 config->posix_rename = lp_parm_bool(
2240 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "posix_rename", true);
2242 config->aapl_zero_file_id =
2243 lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME, "zero_file_id", true);
2245 config->readdir_attr_rsize = lp_parm_bool(
2246 SNUM(handle->conn), "readdir_attr", "aapl_rsize", true);
2248 config->readdir_attr_finder_info = lp_parm_bool(
2249 SNUM(handle->conn), "readdir_attr", "aapl_finder_info", true);
2251 config->readdir_attr_max_access = lp_parm_bool(
2252 SNUM(handle->conn), "readdir_attr", "aapl_max_access", true);
2254 config->model = lp_parm_const_string(
2255 -1, FRUIT_PARAM_TYPE_NAME, "model", "MacSamba");
2257 tm_size_str = lp_parm_const_string(
2258 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2259 "time machine max size", NULL);
2260 if (tm_size_str != NULL) {
2261 config->time_machine_max_size = conv_str_size(tm_size_str);
2264 config->wipe_intentionally_left_blank_rfork = lp_parm_bool(
2265 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2266 "wipe_intentionally_left_blank_rfork", false);
2268 config->delete_empty_adfiles = lp_parm_bool(
2269 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2270 "delete_empty_adfiles", false);
2272 SMB_VFS_HANDLE_SET_DATA(handle, config,
2273 NULL, struct fruit_config_data,
2280 * Prepend "._" to a basename
2281 * Return a new struct smb_filename with stream_name == NULL.
2283 static int adouble_path(TALLOC_CTX *ctx,
2284 const struct smb_filename *smb_fname_in,
2285 struct smb_filename **pp_smb_fname_out)
2289 struct smb_filename *smb_fname = cp_smb_filename(ctx,
2292 if (smb_fname == NULL) {
2296 /* We need streamname to be NULL */
2297 TALLOC_FREE(smb_fname->stream_name);
2299 /* And we're replacing base_name. */
2300 TALLOC_FREE(smb_fname->base_name);
2302 if (!parent_dirname(smb_fname, smb_fname_in->base_name,
2304 TALLOC_FREE(smb_fname);
2308 smb_fname->base_name = talloc_asprintf(smb_fname,
2309 "%s/._%s", parent, base);
2310 if (smb_fname->base_name == NULL) {
2311 TALLOC_FREE(smb_fname);
2315 *pp_smb_fname_out = smb_fname;
2321 * Allocate and initialize an AfpInfo struct
2323 static AfpInfo *afpinfo_new(TALLOC_CTX *ctx)
2325 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2329 ai->afpi_Signature = AFP_Signature;
2330 ai->afpi_Version = AFP_Version;
2331 ai->afpi_BackupTime = AD_DATE_START;
2336 * Pack an AfpInfo struct into a buffer
2338 * Buffer size must be at least AFP_INFO_SIZE
2339 * Returns size of packed buffer
2341 static ssize_t afpinfo_pack(const AfpInfo *ai, char *buf)
2343 memset(buf, 0, AFP_INFO_SIZE);
2345 RSIVAL(buf, 0, ai->afpi_Signature);
2346 RSIVAL(buf, 4, ai->afpi_Version);
2347 RSIVAL(buf, 12, ai->afpi_BackupTime);
2348 memcpy(buf + 16, ai->afpi_FinderInfo, sizeof(ai->afpi_FinderInfo));
2350 return AFP_INFO_SIZE;
2354 * Unpack a buffer into a AfpInfo structure
2356 * Buffer size must be at least AFP_INFO_SIZE
2357 * Returns allocated AfpInfo struct
2359 static AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data)
2361 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2366 ai->afpi_Signature = RIVAL(data, 0);
2367 ai->afpi_Version = RIVAL(data, 4);
2368 ai->afpi_BackupTime = RIVAL(data, 12);
2369 memcpy(ai->afpi_FinderInfo, (const char *)data + 16,
2370 sizeof(ai->afpi_FinderInfo));
2372 if (ai->afpi_Signature != AFP_Signature
2373 || ai->afpi_Version != AFP_Version) {
2374 DEBUG(1, ("Bad AfpInfo signature or version\n"));
2382 * Fake an inode number from the md5 hash of the (xattr) name
2384 static SMB_INO_T fruit_inode(const SMB_STRUCT_STAT *sbuf, const char *sname)
2387 unsigned char hash[16];
2391 DBG_DEBUG("fruit_inode called for %ju/%ju [%s]\n",
2392 (uintmax_t)sbuf->st_ex_dev,
2393 (uintmax_t)sbuf->st_ex_ino, sname);
2395 upper_sname = talloc_strdup_upper(talloc_tos(), sname);
2396 SMB_ASSERT(upper_sname != NULL);
2399 MD5Update(&ctx, (const unsigned char *)&(sbuf->st_ex_dev),
2400 sizeof(sbuf->st_ex_dev));
2401 MD5Update(&ctx, (const unsigned char *)&(sbuf->st_ex_ino),
2402 sizeof(sbuf->st_ex_ino));
2403 MD5Update(&ctx, (unsigned char *)upper_sname,
2404 talloc_get_size(upper_sname)-1);
2405 MD5Final(hash, &ctx);
2407 TALLOC_FREE(upper_sname);
2409 /* Hopefully all the variation is in the lower 4 (or 8) bytes! */
2410 memcpy(&result, hash, sizeof(result));
2412 DBG_DEBUG("fruit_inode \"%s\": ino=%ju\n",
2413 sname, (uintmax_t)result);
2418 static bool add_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
2419 struct stream_struct **streams,
2420 const char *name, off_t size,
2423 struct stream_struct *tmp;
2425 tmp = talloc_realloc(mem_ctx, *streams, struct stream_struct,
2431 tmp[*num_streams].name = talloc_asprintf(tmp, "%s:$DATA", name);
2432 if (tmp[*num_streams].name == NULL) {
2436 tmp[*num_streams].size = size;
2437 tmp[*num_streams].alloc_size = alloc_size;
2444 static bool filter_empty_rsrc_stream(unsigned int *num_streams,
2445 struct stream_struct **streams)
2447 struct stream_struct *tmp = *streams;
2450 if (*num_streams == 0) {
2454 for (i = 0; i < *num_streams; i++) {
2455 if (strequal_m(tmp[i].name, AFPRESOURCE_STREAM)) {
2460 if (i == *num_streams) {
2464 if (tmp[i].size > 0) {
2468 TALLOC_FREE(tmp[i].name);
2469 if (*num_streams - 1 > i) {
2470 memmove(&tmp[i], &tmp[i+1],
2471 (*num_streams - i - 1) * sizeof(struct stream_struct));
2478 static bool del_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
2479 struct stream_struct **streams,
2482 struct stream_struct *tmp = *streams;
2485 if (*num_streams == 0) {
2489 for (i = 0; i < *num_streams; i++) {
2490 if (strequal_m(tmp[i].name, name)) {
2495 if (i == *num_streams) {
2499 TALLOC_FREE(tmp[i].name);
2500 if (*num_streams - 1 > i) {
2501 memmove(&tmp[i], &tmp[i+1],
2502 (*num_streams - i - 1) * sizeof(struct stream_struct));
2509 static bool ad_empty_finderinfo(const struct adouble *ad)
2512 char emptybuf[ADEDLEN_FINDERI] = {0};
2515 fi = ad_get_entry(ad, ADEID_FINDERI);
2517 DBG_ERR("Missing FinderInfo in struct adouble [%p]\n", ad);
2521 cmp = memcmp(emptybuf, fi, ADEDLEN_FINDERI);
2525 static bool ai_empty_finderinfo(const AfpInfo *ai)
2528 char emptybuf[ADEDLEN_FINDERI] = {0};
2530 cmp = memcmp(emptybuf, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
2535 * Update btime with btime from Netatalk
2537 static void update_btime(vfs_handle_struct *handle,
2538 struct smb_filename *smb_fname)
2541 struct timespec creation_time = {0};
2543 struct fruit_config_data *config = NULL;
2545 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
2548 switch (config->meta) {
2549 case FRUIT_META_STREAM:
2551 case FRUIT_META_NETATALK:
2555 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
2559 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
2563 if (ad_getdate(ad, AD_DATE_UNIX | AD_DATE_CREATE, &t) != 0) {
2569 creation_time.tv_sec = convert_uint32_t_to_time_t(t);
2570 update_stat_ex_create_time(&smb_fname->st, creation_time);
2576 * Map an access mask to a Netatalk single byte byte range lock
2578 static off_t access_to_netatalk_brl(enum apple_fork fork_type,
2579 uint32_t access_mask)
2583 switch (access_mask) {
2584 case FILE_READ_DATA:
2585 offset = AD_FILELOCK_OPEN_RD;
2588 case FILE_WRITE_DATA:
2589 case FILE_APPEND_DATA:
2590 offset = AD_FILELOCK_OPEN_WR;
2594 offset = AD_FILELOCK_OPEN_NONE;
2598 if (fork_type == APPLE_FORK_RSRC) {
2599 if (offset == AD_FILELOCK_OPEN_NONE) {
2600 offset = AD_FILELOCK_RSRC_OPEN_NONE;
2610 * Map a deny mode to a Netatalk brl
2612 static off_t denymode_to_netatalk_brl(enum apple_fork fork_type,
2617 switch (deny_mode) {
2619 offset = AD_FILELOCK_DENY_RD;
2623 offset = AD_FILELOCK_DENY_WR;
2627 smb_panic("denymode_to_netatalk_brl: bad deny mode\n");
2630 if (fork_type == APPLE_FORK_RSRC) {
2638 * Call fcntl() with an exclusive F_GETLK request in order to
2639 * determine if there's an exisiting shared lock
2641 * @return true if the requested lock was found or any error occurred
2642 * false if the lock was not found
2644 static bool test_netatalk_lock(files_struct *fsp, off_t in_offset)
2647 off_t offset = in_offset;
2652 result = SMB_VFS_GETLOCK(fsp, &offset, &len, &type, &pid);
2653 if (result == false) {
2657 if (type != F_UNLCK) {
2664 static NTSTATUS fruit_check_access(vfs_handle_struct *handle,
2666 uint32_t access_mask,
2669 NTSTATUS status = NT_STATUS_OK;
2670 bool open_for_reading, open_for_writing, deny_read, deny_write;
2672 bool have_read = false;
2675 /* FIXME: hardcoded data fork, add resource fork */
2676 enum apple_fork fork_type = APPLE_FORK_DATA;
2678 DEBUG(10, ("fruit_check_access: %s, am: %s/%s, dm: %s/%s\n",
2680 access_mask & FILE_READ_DATA ? "READ" :"-",
2681 access_mask & FILE_WRITE_DATA ? "WRITE" : "-",
2682 deny_mode & DENY_READ ? "DENY_READ" : "-",
2683 deny_mode & DENY_WRITE ? "DENY_WRITE" : "-"));
2685 if (fsp->fh->fd == -1) {
2686 return NT_STATUS_OK;
2689 flags = fcntl(fsp->fh->fd, F_GETFL);
2691 DBG_ERR("fcntl get flags [%s] fd [%d] failed [%s]\n",
2692 fsp_str_dbg(fsp), fsp->fh->fd, strerror(errno));
2693 return map_nt_error_from_unix(errno);
2696 if (flags & (O_RDONLY|O_RDWR)) {
2698 * Applying fcntl read locks requires an fd opened for
2699 * reading. This means we won't be applying locks for
2700 * files openend write-only, but what can we do...
2706 * Check read access and deny read mode
2708 if ((access_mask & FILE_READ_DATA) || (deny_mode & DENY_READ)) {
2710 open_for_reading = test_netatalk_lock(
2711 fsp, access_to_netatalk_brl(fork_type, FILE_READ_DATA));
2713 deny_read = test_netatalk_lock(
2714 fsp, denymode_to_netatalk_brl(fork_type, DENY_READ));
2716 DEBUG(10, ("read: %s, deny_write: %s\n",
2717 open_for_reading == true ? "yes" : "no",
2718 deny_read == true ? "yes" : "no"));
2720 if (((access_mask & FILE_READ_DATA) && deny_read)
2721 || ((deny_mode & DENY_READ) && open_for_reading)) {
2722 return NT_STATUS_SHARING_VIOLATION;
2726 if ((access_mask & FILE_READ_DATA) && have_read) {
2727 struct byte_range_lock *br_lck = NULL;
2729 off = access_to_netatalk_brl(fork_type, FILE_READ_DATA);
2731 handle->conn->sconn->msg_ctx, fsp,
2732 fsp->op->global->open_persistent_id, 1, off,
2733 READ_LOCK, POSIX_LOCK, false,
2736 TALLOC_FREE(br_lck);
2738 if (!NT_STATUS_IS_OK(status)) {
2743 if ((deny_mode & DENY_READ) && have_read) {
2744 struct byte_range_lock *br_lck = NULL;
2746 off = denymode_to_netatalk_brl(fork_type, DENY_READ);
2748 handle->conn->sconn->msg_ctx, fsp,
2749 fsp->op->global->open_persistent_id, 1, off,
2750 READ_LOCK, POSIX_LOCK, false,
2753 TALLOC_FREE(br_lck);
2755 if (!NT_STATUS_IS_OK(status)) {
2762 * Check write access and deny write mode
2764 if ((access_mask & FILE_WRITE_DATA) || (deny_mode & DENY_WRITE)) {
2766 open_for_writing = test_netatalk_lock(
2767 fsp, access_to_netatalk_brl(fork_type, FILE_WRITE_DATA));
2769 deny_write = test_netatalk_lock(
2770 fsp, denymode_to_netatalk_brl(fork_type, DENY_WRITE));
2772 DEBUG(10, ("write: %s, deny_write: %s\n",
2773 open_for_writing == true ? "yes" : "no",
2774 deny_write == true ? "yes" : "no"));
2776 if (((access_mask & FILE_WRITE_DATA) && deny_write)
2777 || ((deny_mode & DENY_WRITE) && open_for_writing)) {
2778 return NT_STATUS_SHARING_VIOLATION;
2782 if ((access_mask & FILE_WRITE_DATA) && have_read) {
2783 struct byte_range_lock *br_lck = NULL;
2785 off = access_to_netatalk_brl(fork_type, FILE_WRITE_DATA);
2787 handle->conn->sconn->msg_ctx, fsp,
2788 fsp->op->global->open_persistent_id, 1, off,
2789 READ_LOCK, POSIX_LOCK, false,
2792 TALLOC_FREE(br_lck);
2794 if (!NT_STATUS_IS_OK(status)) {
2798 if ((deny_mode & DENY_WRITE) && have_read) {
2799 struct byte_range_lock *br_lck = NULL;
2801 off = denymode_to_netatalk_brl(fork_type, DENY_WRITE);
2803 handle->conn->sconn->msg_ctx, fsp,
2804 fsp->op->global->open_persistent_id, 1, off,
2805 READ_LOCK, POSIX_LOCK, false,
2808 TALLOC_FREE(br_lck);
2810 if (!NT_STATUS_IS_OK(status)) {
2819 static NTSTATUS check_aapl(vfs_handle_struct *handle,
2820 struct smb_request *req,
2821 const struct smb2_create_blobs *in_context_blobs,
2822 struct smb2_create_blobs *out_context_blobs)
2824 struct fruit_config_data *config;
2826 struct smb2_create_blob *aapl = NULL;
2830 DATA_BLOB blob = data_blob_talloc(req, NULL, 0);
2831 uint64_t req_bitmap, client_caps;
2832 uint64_t server_caps = SMB2_CRTCTX_AAPL_UNIX_BASED;
2836 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
2837 return NT_STATUS_UNSUCCESSFUL);
2839 if (!config->use_aapl
2840 || in_context_blobs == NULL
2841 || out_context_blobs == NULL) {
2842 return NT_STATUS_OK;
2845 aapl = smb2_create_blob_find(in_context_blobs,
2846 SMB2_CREATE_TAG_AAPL);
2848 return NT_STATUS_OK;
2851 if (aapl->data.length != 24) {
2852 DEBUG(1, ("unexpected AAPL ctxt length: %ju\n",
2853 (uintmax_t)aapl->data.length));
2854 return NT_STATUS_INVALID_PARAMETER;
2857 cmd = IVAL(aapl->data.data, 0);
2858 if (cmd != SMB2_CRTCTX_AAPL_SERVER_QUERY) {
2859 DEBUG(1, ("unsupported AAPL cmd: %d\n", cmd));
2860 return NT_STATUS_INVALID_PARAMETER;
2863 req_bitmap = BVAL(aapl->data.data, 8);
2864 client_caps = BVAL(aapl->data.data, 16);
2866 SIVAL(p, 0, SMB2_CRTCTX_AAPL_SERVER_QUERY);
2868 SBVAL(p, 8, req_bitmap);
2869 ok = data_blob_append(req, &blob, p, 16);
2871 return NT_STATUS_UNSUCCESSFUL;
2874 if (req_bitmap & SMB2_CRTCTX_AAPL_SERVER_CAPS) {
2875 if ((client_caps & SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR) &&
2876 (handle->conn->tcon->compat->fs_capabilities & FILE_NAMED_STREAMS)) {
2877 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR;
2878 config->readdir_attr_enabled = true;
2881 if (config->use_copyfile) {
2882 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_OSX_COPYFILE;
2883 config->copyfile_enabled = true;
2887 * The client doesn't set the flag, so we can't check
2888 * for it and just set it unconditionally
2890 if (config->unix_info_enabled) {
2891 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_NFS_ACE;
2894 SBVAL(p, 0, server_caps);
2895 ok = data_blob_append(req, &blob, p, 8);
2897 return NT_STATUS_UNSUCCESSFUL;
2901 if (req_bitmap & SMB2_CRTCTX_AAPL_VOLUME_CAPS) {
2902 int val = lp_case_sensitive(SNUM(handle->conn->tcon->compat));
2910 caps |= SMB2_CRTCTX_AAPL_CASE_SENSITIVE;
2917 if (config->time_machine) {
2918 caps |= SMB2_CRTCTX_AAPL_FULL_SYNC;
2923 ok = data_blob_append(req, &blob, p, 8);
2925 return NT_STATUS_UNSUCCESSFUL;
2929 if (req_bitmap & SMB2_CRTCTX_AAPL_MODEL_INFO) {
2930 ok = convert_string_talloc(req,
2931 CH_UNIX, CH_UTF16LE,
2932 config->model, strlen(config->model),
2935 return NT_STATUS_UNSUCCESSFUL;
2939 SIVAL(p + 4, 0, modellen);
2940 ok = data_blob_append(req, &blob, p, 8);
2943 return NT_STATUS_UNSUCCESSFUL;
2946 ok = data_blob_append(req, &blob, model, modellen);
2949 return NT_STATUS_UNSUCCESSFUL;
2953 status = smb2_create_blob_add(out_context_blobs,
2955 SMB2_CREATE_TAG_AAPL,
2957 if (NT_STATUS_IS_OK(status)) {
2958 global_fruit_config.nego_aapl = true;
2959 if (config->aapl_zero_file_id) {
2960 aapl_force_zero_file_id(handle->conn->sconn);
2967 static bool readdir_attr_meta_finderi_stream(
2968 struct vfs_handle_struct *handle,
2969 const struct smb_filename *smb_fname,
2972 struct smb_filename *stream_name = NULL;
2973 files_struct *fsp = NULL;
2978 uint8_t buf[AFP_INFO_SIZE];
2980 stream_name = synthetic_smb_fname(talloc_tos(),
2981 smb_fname->base_name,
2982 AFPINFO_STREAM_NAME,
2983 NULL, smb_fname->flags);
2984 if (stream_name == NULL) {
2988 ret = SMB_VFS_STAT(handle->conn, stream_name);
2993 status = SMB_VFS_CREATE_FILE(
2994 handle->conn, /* conn */
2996 0, /* root_dir_fid */
2997 stream_name, /* fname */
2998 FILE_READ_DATA, /* access_mask */
2999 (FILE_SHARE_READ | FILE_SHARE_WRITE | /* share_access */
3001 FILE_OPEN, /* create_disposition*/
3002 0, /* create_options */
3003 0, /* file_attributes */
3004 INTERNAL_OPEN_ONLY, /* oplock_request */
3006 0, /* allocation_size */
3007 0, /* private_flags */
3012 NULL, NULL); /* create context */
3014 TALLOC_FREE(stream_name);
3016 if (!NT_STATUS_IS_OK(status)) {
3020 nread = SMB_VFS_PREAD(fsp, &buf[0], AFP_INFO_SIZE, 0);
3021 if (nread != AFP_INFO_SIZE) {
3022 DBG_ERR("short read [%s] [%zd/%d]\n",
3023 smb_fname_str_dbg(stream_name), nread, AFP_INFO_SIZE);
3028 memcpy(&ai->afpi_FinderInfo[0], &buf[AFP_OFF_FinderInfo],
3035 close_file(NULL, fsp, NORMAL_CLOSE);
3041 static bool readdir_attr_meta_finderi_netatalk(
3042 struct vfs_handle_struct *handle,
3043 const struct smb_filename *smb_fname,
3046 struct adouble *ad = NULL;
3049 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
3054 p = ad_get_entry(ad, ADEID_FINDERI);
3056 DBG_ERR("No ADEID_FINDERI for [%s]\n", smb_fname->base_name);
3061 memcpy(&ai->afpi_FinderInfo[0], p, AFP_FinderSize);
3066 static bool readdir_attr_meta_finderi(struct vfs_handle_struct *handle,
3067 const struct smb_filename *smb_fname,
3068 struct readdir_attr_data *attr_data)
3070 struct fruit_config_data *config = NULL;
3071 uint32_t date_added;
3075 SMB_VFS_HANDLE_GET_DATA(handle, config,
3076 struct fruit_config_data,
3079 switch (config->meta) {
3080 case FRUIT_META_NETATALK:
3081 ok = readdir_attr_meta_finderi_netatalk(
3082 handle, smb_fname, &ai);
3085 case FRUIT_META_STREAM:
3086 ok = readdir_attr_meta_finderi_stream(
3087 handle, smb_fname, &ai);
3091 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3096 /* Don't bother with errors, it's likely ENOENT */
3100 if (S_ISREG(smb_fname->st.st_ex_mode)) {
3102 memcpy(&attr_data->attr_data.aapl.finder_info[0],
3103 &ai.afpi_FinderInfo[0], 4);
3105 /* finder_creator */
3106 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 4,
3107 &ai.afpi_FinderInfo[4], 4);
3111 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 8,
3112 &ai.afpi_FinderInfo[8], 2);
3114 /* finder_ext_flags */
3115 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 10,
3116 &ai.afpi_FinderInfo[24], 2);
3119 date_added = convert_time_t_to_uint32_t(
3120 smb_fname->st.st_ex_btime.tv_sec - AD_DATE_DELTA);
3122 RSIVAL(&attr_data->attr_data.aapl.finder_info[0], 12, date_added);
3127 static uint64_t readdir_attr_rfork_size_adouble(
3128 struct vfs_handle_struct *handle,
3129 const struct smb_filename *smb_fname)
3131 struct adouble *ad = NULL;
3132 uint64_t rfork_size;
3134 ad = ad_get(talloc_tos(), handle, smb_fname,
3140 rfork_size = ad_getentrylen(ad, ADEID_RFORK);
3146 static uint64_t readdir_attr_rfork_size_stream(
3147 struct vfs_handle_struct *handle,
3148 const struct smb_filename *smb_fname)
3150 struct smb_filename *stream_name = NULL;
3152 uint64_t rfork_size;
3154 stream_name = synthetic_smb_fname(talloc_tos(),
3155 smb_fname->base_name,
3156 AFPRESOURCE_STREAM_NAME,
3158 if (stream_name == NULL) {
3162 ret = SMB_VFS_STAT(handle->conn, stream_name);
3164 TALLOC_FREE(stream_name);
3168 rfork_size = stream_name->st.st_ex_size;
3169 TALLOC_FREE(stream_name);
3174 static uint64_t readdir_attr_rfork_size(struct vfs_handle_struct *handle,
3175 const struct smb_filename *smb_fname)
3177 struct fruit_config_data *config = NULL;
3178 uint64_t rfork_size;
3180 SMB_VFS_HANDLE_GET_DATA(handle, config,
3181 struct fruit_config_data,
3184 switch (config->rsrc) {
3185 case FRUIT_RSRC_ADFILE:
3186 case FRUIT_RSRC_XATTR:
3187 rfork_size = readdir_attr_rfork_size_adouble(handle,
3191 case FRUIT_META_STREAM:
3192 rfork_size = readdir_attr_rfork_size_stream(handle,
3197 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3205 static NTSTATUS readdir_attr_macmeta(struct vfs_handle_struct *handle,
3206 const struct smb_filename *smb_fname,
3207 struct readdir_attr_data *attr_data)
3209 NTSTATUS status = NT_STATUS_OK;
3210 struct fruit_config_data *config = NULL;
3213 SMB_VFS_HANDLE_GET_DATA(handle, config,
3214 struct fruit_config_data,
3215 return NT_STATUS_UNSUCCESSFUL);
3218 /* Ensure we return a default value in the creation_date field */
3219 RSIVAL(&attr_data->attr_data.aapl.finder_info, 12, AD_DATE_START);
3222 * Resource fork length
3225 if (config->readdir_attr_rsize) {
3226 uint64_t rfork_size;
3228 rfork_size = readdir_attr_rfork_size(handle, smb_fname);
3229 attr_data->attr_data.aapl.rfork_size = rfork_size;
3236 if (config->readdir_attr_finder_info) {
3237 ok = readdir_attr_meta_finderi(handle, smb_fname, attr_data);
3239 status = NT_STATUS_INTERNAL_ERROR;
3246 static NTSTATUS remove_virtual_nfs_aces(struct security_descriptor *psd)
3251 if (psd->dacl == NULL) {
3252 return NT_STATUS_OK;
3255 for (i = 0; i < psd->dacl->num_aces; i++) {
3256 /* MS NFS style mode/uid/gid */
3257 int cmp = dom_sid_compare_domain(
3258 &global_sid_Unix_NFS,
3259 &psd->dacl->aces[i].trustee);
3261 /* Normal ACE entry. */
3266 * security_descriptor_dacl_del()
3267 * *must* return NT_STATUS_OK as we know
3268 * we have something to remove.
3271 status = security_descriptor_dacl_del(psd,
3272 &psd->dacl->aces[i].trustee);
3273 if (!NT_STATUS_IS_OK(status)) {
3274 DBG_WARNING("failed to remove MS NFS style ACE: %s\n",
3280 * security_descriptor_dacl_del() may delete more
3281 * then one entry subsequent to this one if the
3282 * SID matches, but we only need to ensure that
3283 * we stay looking at the same element in the array.
3287 return NT_STATUS_OK;
3290 /* Search MS NFS style ACE with UNIX mode */
3291 static NTSTATUS check_ms_nfs(vfs_handle_struct *handle,
3293 struct security_descriptor *psd,
3298 struct fruit_config_data *config = NULL;
3302 SMB_VFS_HANDLE_GET_DATA(handle, config,
3303 struct fruit_config_data,
3304 return NT_STATUS_UNSUCCESSFUL);
3306 if (!global_fruit_config.nego_aapl) {
3307 return NT_STATUS_OK;
3309 if (psd->dacl == NULL || !config->unix_info_enabled) {
3310 return NT_STATUS_OK;
3313 for (i = 0; i < psd->dacl->num_aces; i++) {
3314 if (dom_sid_compare_domain(
3315 &global_sid_Unix_NFS_Mode,
3316 &psd->dacl->aces[i].trustee) == 0) {
3317 *pmode = (mode_t)psd->dacl->aces[i].trustee.sub_auths[2];
3318 *pmode &= (S_IRWXU | S_IRWXG | S_IRWXO);
3321 DEBUG(10, ("MS NFS chmod request %s, %04o\n",
3322 fsp_str_dbg(fsp), (unsigned)(*pmode)));
3328 * Remove any incoming virtual ACE entries generated by
3329 * fruit_fget_nt_acl().
3332 return remove_virtual_nfs_aces(psd);
3335 /****************************************************************************
3337 ****************************************************************************/
3339 static int fruit_connect(vfs_handle_struct *handle,
3340 const char *service,
3344 char *list = NULL, *newlist = NULL;
3345 struct fruit_config_data *config;
3347 DEBUG(10, ("fruit_connect\n"));
3349 rc = SMB_VFS_NEXT_CONNECT(handle, service, user);
3354 rc = init_fruit_config(handle);
3359 SMB_VFS_HANDLE_GET_DATA(handle, config,
3360 struct fruit_config_data, return -1);
3362 if (config->veto_appledouble) {
3363 list = lp_veto_files(talloc_tos(), SNUM(handle->conn));
3366 if (strstr(list, "/" ADOUBLE_NAME_PREFIX "*/") == NULL) {
3367 newlist = talloc_asprintf(
3369 "%s/" ADOUBLE_NAME_PREFIX "*/",
3371 lp_do_parameter(SNUM(handle->conn),
3376 lp_do_parameter(SNUM(handle->conn),
3378 "/" ADOUBLE_NAME_PREFIX "*/");
3384 if (config->encoding == FRUIT_ENC_NATIVE) {
3385 lp_do_parameter(SNUM(handle->conn),
3390 if (config->time_machine) {
3391 DBG_NOTICE("Enabling durable handles for Time Machine "
3392 "support on [%s]\n", service);
3393 lp_do_parameter(SNUM(handle->conn), "durable handles", "yes");
3394 lp_do_parameter(SNUM(handle->conn), "kernel oplocks", "no");
3395 lp_do_parameter(SNUM(handle->conn), "kernel share modes", "no");
3396 if (!lp_strict_sync(SNUM(handle->conn))) {
3397 DBG_WARNING("Time Machine without strict sync is not "
3400 lp_do_parameter(SNUM(handle->conn), "posix locking", "no");
3406 static int fruit_fake_fd(void)
3413 * Return a valid fd, but ensure any attempt to use it returns
3414 * an error (EPIPE). Once we get a write on the handle, we open
3417 ret = pipe(pipe_fds);
3427 static int fruit_open_meta_stream(vfs_handle_struct *handle,
3428 struct smb_filename *smb_fname,
3433 struct fruit_config_data *config = NULL;
3434 struct fio *fio = NULL;
3435 int open_flags = flags & ~O_CREAT;
3438 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3440 SMB_VFS_HANDLE_GET_DATA(handle, config,
3441 struct fruit_config_data, return -1);
3443 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3444 fio->type = ADOUBLE_META;
3445 fio->config = config;
3447 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, open_flags, mode);
3452 if (!(flags & O_CREAT)) {
3453 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
3457 fd = fruit_fake_fd();
3459 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
3463 fio->fake_fd = true;
3470 static int fruit_open_meta_netatalk(vfs_handle_struct *handle,
3471 struct smb_filename *smb_fname,
3476 struct fruit_config_data *config = NULL;
3477 struct fio *fio = NULL;
3478 struct adouble *ad = NULL;
3479 bool meta_exists = false;
3482 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3484 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
3491 if (!meta_exists && !(flags & O_CREAT)) {
3496 fd = fruit_fake_fd();
3501 SMB_VFS_HANDLE_GET_DATA(handle, config,
3502 struct fruit_config_data, return -1);
3504 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3505 fio->type = ADOUBLE_META;
3506 fio->config = config;
3507 fio->fake_fd = true;
3514 static int fruit_open_meta(vfs_handle_struct *handle,
3515 struct smb_filename *smb_fname,
3516 files_struct *fsp, int flags, mode_t mode)
3519 struct fruit_config_data *config = NULL;
3521 DBG_DEBUG("path [%s]\n", smb_fname_str_dbg(smb_fname));
3523 SMB_VFS_HANDLE_GET_DATA(handle, config,
3524 struct fruit_config_data, return -1);
3526 switch (config->meta) {
3527 case FRUIT_META_STREAM:
3528 fd = fruit_open_meta_stream(handle, smb_fname,
3532 case FRUIT_META_NETATALK:
3533 fd = fruit_open_meta_netatalk(handle, smb_fname,
3538 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3542 DBG_DEBUG("path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3547 static int fruit_open_rsrc_adouble(vfs_handle_struct *handle,
3548 struct smb_filename *smb_fname,
3554 struct adouble *ad = NULL;
3555 struct smb_filename *smb_fname_base = NULL;
3556 struct fruit_config_data *config = NULL;
3559 SMB_VFS_HANDLE_GET_DATA(handle, config,
3560 struct fruit_config_data, return -1);
3562 if ((!(flags & O_CREAT)) &&
3563 S_ISDIR(fsp->base_fsp->fsp_name->st.st_ex_mode))
3565 /* sorry, but directories don't habe a resource fork */
3570 rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_base);
3575 /* We always need read/write access for the metadata header too */
3576 flags &= ~(O_RDONLY | O_WRONLY);
3579 hostfd = SMB_VFS_NEXT_OPEN(handle, smb_fname_base, fsp,
3586 if (flags & (O_CREAT | O_TRUNC)) {
3587 ad = ad_init(fsp, handle, ADOUBLE_RSRC);
3593 fsp->fh->fd = hostfd;
3595 rc = ad_fset(ad, fsp);
3606 TALLOC_FREE(smb_fname_base);
3608 DEBUG(10, ("fruit_open resource fork: rc=%d, fd=%d\n", rc, hostfd));
3610 int saved_errno = errno;
3613 * BUGBUGBUG -- we would need to call
3614 * fd_close_posix here, but we don't have a
3617 fsp->fh->fd = hostfd;
3621 errno = saved_errno;
3626 static int fruit_open_rsrc_xattr(vfs_handle_struct *handle,
3627 struct smb_filename *smb_fname,
3632 #ifdef HAVE_ATTROPEN
3635 fd = attropen(smb_fname->base_name,
3636 AFPRESOURCE_EA_NETATALK,
3651 static int fruit_open_rsrc(vfs_handle_struct *handle,
3652 struct smb_filename *smb_fname,
3653 files_struct *fsp, int flags, mode_t mode)
3656 struct fruit_config_data *config = NULL;
3657 struct fio *fio = NULL;
3659 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3661 SMB_VFS_HANDLE_GET_DATA(handle, config,
3662 struct fruit_config_data, return -1);
3664 switch (config->rsrc) {
3665 case FRUIT_RSRC_STREAM:
3666 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3669 case FRUIT_RSRC_ADFILE:
3670 fd = fruit_open_rsrc_adouble(handle, smb_fname,
3674 case FRUIT_RSRC_XATTR:
3675 fd = fruit_open_rsrc_xattr(handle, smb_fname,
3680 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3684 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3690 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3691 fio->type = ADOUBLE_RSRC;
3692 fio->config = config;
3697 static int fruit_open(vfs_handle_struct *handle,
3698 struct smb_filename *smb_fname,
3699 files_struct *fsp, int flags, mode_t mode)
3703 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3705 if (!is_ntfs_stream_smb_fname(smb_fname)) {
3706 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3709 if (is_afpinfo_stream(smb_fname)) {
3710 fd = fruit_open_meta(handle, smb_fname, fsp, flags, mode);
3711 } else if (is_afpresource_stream(smb_fname)) {
3712 fd = fruit_open_rsrc(handle, smb_fname, fsp, flags, mode);
3714 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3717 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3722 static int fruit_close_meta(vfs_handle_struct *handle,
3726 struct fruit_config_data *config = NULL;
3728 SMB_VFS_HANDLE_GET_DATA(handle, config,
3729 struct fruit_config_data, return -1);
3731 switch (config->meta) {
3732 case FRUIT_META_STREAM:
3733 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3736 case FRUIT_META_NETATALK:
3737 ret = close(fsp->fh->fd);
3742 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3750 static int fruit_close_rsrc(vfs_handle_struct *handle,
3754 struct fruit_config_data *config = NULL;
3756 SMB_VFS_HANDLE_GET_DATA(handle, config,
3757 struct fruit_config_data, return -1);
3759 switch (config->rsrc) {
3760 case FRUIT_RSRC_STREAM:
3761 case FRUIT_RSRC_ADFILE:
3762 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3765 case FRUIT_RSRC_XATTR:
3766 ret = close(fsp->fh->fd);
3771 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3778 static int fruit_close(vfs_handle_struct *handle,
3786 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(fsp->fsp_name), fd);
3788 if (!is_ntfs_stream_smb_fname(fsp->fsp_name)) {
3789 return SMB_VFS_NEXT_CLOSE(handle, fsp);
3792 if (is_afpinfo_stream(fsp->fsp_name)) {
3793 ret = fruit_close_meta(handle, fsp);
3794 } else if (is_afpresource_stream(fsp->fsp_name)) {
3795 ret = fruit_close_rsrc(handle, fsp);
3797 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3803 static int fruit_rename(struct vfs_handle_struct *handle,
3804 const struct smb_filename *smb_fname_src,
3805 const struct smb_filename *smb_fname_dst)
3808 struct fruit_config_data *config = NULL;
3809 struct smb_filename *src_adp_smb_fname = NULL;
3810 struct smb_filename *dst_adp_smb_fname = NULL;
3812 SMB_VFS_HANDLE_GET_DATA(handle, config,
3813 struct fruit_config_data, return -1);
3815 if (!VALID_STAT(smb_fname_src->st)) {
3816 DBG_ERR("Need valid stat for [%s]\n",
3817 smb_fname_str_dbg(smb_fname_src));
3821 rc = SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
3826 if ((config->rsrc != FRUIT_RSRC_ADFILE) ||
3827 (!S_ISREG(smb_fname_src->st.st_ex_mode)))
3832 rc = adouble_path(talloc_tos(), smb_fname_src, &src_adp_smb_fname);
3837 rc = adouble_path(talloc_tos(), smb_fname_dst, &dst_adp_smb_fname);
3842 DBG_DEBUG("%s -> %s\n",
3843 smb_fname_str_dbg(src_adp_smb_fname),
3844 smb_fname_str_dbg(dst_adp_smb_fname));
3846 rc = SMB_VFS_NEXT_RENAME(handle, src_adp_smb_fname, dst_adp_smb_fname);
3847 if (errno == ENOENT) {
3852 TALLOC_FREE(src_adp_smb_fname);
3853 TALLOC_FREE(dst_adp_smb_fname);
3857 static int fruit_unlink_meta_stream(vfs_handle_struct *handle,
3858 const struct smb_filename *smb_fname)
3860 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3863 static int fruit_unlink_meta_netatalk(vfs_handle_struct *handle,
3864 const struct smb_filename *smb_fname)
3866 return SMB_VFS_REMOVEXATTR(handle->conn,
3868 AFPINFO_EA_NETATALK);
3871 static int fruit_unlink_meta(vfs_handle_struct *handle,
3872 const struct smb_filename *smb_fname)
3874 struct fruit_config_data *config = NULL;
3877 SMB_VFS_HANDLE_GET_DATA(handle, config,
3878 struct fruit_config_data, return -1);
3880 switch (config->meta) {
3881 case FRUIT_META_STREAM:
3882 rc = fruit_unlink_meta_stream(handle, smb_fname);
3885 case FRUIT_META_NETATALK:
3886 rc = fruit_unlink_meta_netatalk(handle, smb_fname);
3890 DBG_ERR("Unsupported meta config [%d]\n", config->meta);
3897 static int fruit_unlink_rsrc_stream(vfs_handle_struct *handle,
3898 const struct smb_filename *smb_fname,
3903 if (!force_unlink) {
3904 struct smb_filename *smb_fname_cp = NULL;
3907 smb_fname_cp = cp_smb_filename(talloc_tos(), smb_fname);
3908 if (smb_fname_cp == NULL) {
3913 * 0 byte resource fork streams are not listed by
3914 * vfs_streaminfo, as a result stream cleanup/deletion of file
3915 * deletion doesn't remove the resourcefork stream.
3918 ret = SMB_VFS_NEXT_STAT(handle, smb_fname_cp);
3920 TALLOC_FREE(smb_fname_cp);
3921 DBG_ERR("stat [%s] failed [%s]\n",
3922 smb_fname_str_dbg(smb_fname_cp), strerror(errno));
3926 size = smb_fname_cp->st.st_ex_size;
3927 TALLOC_FREE(smb_fname_cp);
3930 /* OS X ignores resource fork stream delete requests */
3935 ret = SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3936 if ((ret != 0) && (errno == ENOENT) && force_unlink) {
3943 static int fruit_unlink_rsrc_adouble(vfs_handle_struct *handle,
3944 const struct smb_filename *smb_fname,
3948 struct adouble *ad = NULL;
3949 struct smb_filename *adp_smb_fname = NULL;
3951 if (!force_unlink) {
3952 ad = ad_get(talloc_tos(), handle, smb_fname,
3961 * 0 byte resource fork streams are not listed by
3962 * vfs_streaminfo, as a result stream cleanup/deletion of file
3963 * deletion doesn't remove the resourcefork stream.
3966 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
3967 /* OS X ignores resource fork stream delete requests */
3975 rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
3980 rc = SMB_VFS_NEXT_UNLINK(handle, adp_smb_fname);
3981 TALLOC_FREE(adp_smb_fname);
3982 if ((rc != 0) && (errno == ENOENT) && force_unlink) {
3989 static int fruit_unlink_rsrc_xattr(vfs_handle_struct *handle,
3990 const struct smb_filename *smb_fname,
3994 * OS X ignores resource fork stream delete requests, so nothing to do
3995 * here. Removing the file will remove the xattr anyway, so we don't
3996 * have to take care of removing 0 byte resource forks that could be
4002 static int fruit_unlink_rsrc(vfs_handle_struct *handle,
4003 const struct smb_filename *smb_fname,
4006 struct fruit_config_data *config = NULL;
4009 SMB_VFS_HANDLE_GET_DATA(handle, config,
4010 struct fruit_config_data, return -1);
4012 switch (config->rsrc) {
4013 case FRUIT_RSRC_STREAM:
4014 rc = fruit_unlink_rsrc_stream(handle, smb_fname, force_unlink);
4017 case FRUIT_RSRC_ADFILE:
4018 rc = fruit_unlink_rsrc_adouble(handle, smb_fname, force_unlink);
4021 case FRUIT_RSRC_XATTR:
4022 rc = fruit_unlink_rsrc_xattr(handle, smb_fname, force_unlink);
4026 DBG_ERR("Unsupported rsrc config [%d]\n", config->rsrc);
4033 static int fruit_unlink(vfs_handle_struct *handle,
4034 const struct smb_filename *smb_fname)
4037 struct fruit_config_data *config = NULL;
4038 struct smb_filename *rsrc_smb_fname = NULL;
4040 SMB_VFS_HANDLE_GET_DATA(handle, config,
4041 struct fruit_config_data, return -1);
4043 if (is_afpinfo_stream(smb_fname)) {
4044 return fruit_unlink_meta(handle, smb_fname);
4045 } else if (is_afpresource_stream(smb_fname)) {
4046 return fruit_unlink_rsrc(handle, smb_fname, false);
4047 } if (is_ntfs_stream_smb_fname(smb_fname)) {
4048 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4052 * A request to delete the base file. Because 0 byte resource
4053 * fork streams are not listed by fruit_streaminfo,
4054 * delete_all_streams() can't remove 0 byte resource fork
4055 * streams, so we have to cleanup this here.
4057 rsrc_smb_fname = synthetic_smb_fname(talloc_tos(),
4058 smb_fname->base_name,
4059 AFPRESOURCE_STREAM_NAME,
4062 if (rsrc_smb_fname == NULL) {
4066 rc = fruit_unlink_rsrc(handle, rsrc_smb_fname, true);
4067 if ((rc != 0) && (errno != ENOENT)) {
4068 DBG_ERR("Forced unlink of [%s] failed [%s]\n",
4069 smb_fname_str_dbg(rsrc_smb_fname), strerror(errno));
4070 TALLOC_FREE(rsrc_smb_fname);
4073 TALLOC_FREE(rsrc_smb_fname);
4075 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4078 static int fruit_chmod(vfs_handle_struct *handle,
4079 const struct smb_filename *smb_fname,
4083 struct fruit_config_data *config = NULL;
4084 struct smb_filename *smb_fname_adp = NULL;
4086 rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname, mode);
4091 SMB_VFS_HANDLE_GET_DATA(handle, config,
4092 struct fruit_config_data, return -1);
4094 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4098 if (!VALID_STAT(smb_fname->st)) {
4102 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
4106 rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_adp);
4111 DEBUG(10, ("fruit_chmod: %s\n", smb_fname_adp->base_name));
4113 rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname_adp, mode);
4114 if (errno == ENOENT) {
4118 TALLOC_FREE(smb_fname_adp);
4122 static int fruit_chown(vfs_handle_struct *handle,
4123 const struct smb_filename *smb_fname,
4128 struct fruit_config_data *config = NULL;
4129 struct smb_filename *adp_smb_fname = NULL;
4131 rc = SMB_VFS_NEXT_CHOWN(handle, smb_fname, uid, gid);
4136 SMB_VFS_HANDLE_GET_DATA(handle, config,
4137 struct fruit_config_data, return -1);
4139 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4143 if (!VALID_STAT(smb_fname->st)) {
4147 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
4151 rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
4156 DEBUG(10, ("fruit_chown: %s\n", adp_smb_fname->base_name));
4158 rc = SMB_VFS_NEXT_CHOWN(handle, adp_smb_fname, uid, gid);
4159 if (errno == ENOENT) {
4164 TALLOC_FREE(adp_smb_fname);
4168 static int fruit_rmdir(struct vfs_handle_struct *handle,
4169 const struct smb_filename *smb_fname)
4173 struct fruit_config_data *config;
4175 SMB_VFS_HANDLE_GET_DATA(handle, config,
4176 struct fruit_config_data, return -1);
4178 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4183 * Due to there is no way to change bDeleteVetoFiles variable
4184 * from this module, need to clean up ourselves
4187 dh = SMB_VFS_OPENDIR(handle->conn, smb_fname, NULL, 0);
4192 while ((de = SMB_VFS_READDIR(handle->conn, dh, NULL)) != NULL) {
4194 struct adouble *ad = NULL;
4196 struct smb_filename *ad_smb_fname = NULL;
4199 match = strncmp(de->d_name,
4200 ADOUBLE_NAME_PREFIX,
4201 strlen(ADOUBLE_NAME_PREFIX));
4206 p = talloc_asprintf(talloc_tos(), "%s/%s",
4207 smb_fname->base_name, de->d_name);
4209 DBG_ERR("talloc_asprintf failed\n");
4213 ad_smb_fname = synthetic_smb_fname(talloc_tos(), p,
4217 if (ad_smb_fname == NULL) {
4218 DBG_ERR("synthetic_smb_fname failed\n");
4223 * Check whether it's a valid AppleDouble file, if
4224 * yes, delete it, ignore it otherwise.
4226 ad = ad_get(talloc_tos(), handle, ad_smb_fname, ADOUBLE_RSRC);
4228 TALLOC_FREE(ad_smb_fname);
4234 ret = SMB_VFS_NEXT_UNLINK(handle, ad_smb_fname);
4236 DBG_ERR("Deleting [%s] failed\n",
4237 smb_fname_str_dbg(ad_smb_fname));
4239 TALLOC_FREE(ad_smb_fname);
4244 SMB_VFS_CLOSEDIR(handle->conn, dh);
4246 return SMB_VFS_NEXT_RMDIR(handle, smb_fname);
4249 static ssize_t fruit_pread_meta_stream(vfs_handle_struct *handle,
4250 files_struct *fsp, void *data,
4251 size_t n, off_t offset)
4256 nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4257 if (nread == -1 || nread == n) {
4261 DBG_ERR("Removing [%s] after short read [%zd]\n",
4262 fsp_str_dbg(fsp), nread);
4264 ret = SMB_VFS_NEXT_UNLINK(handle, fsp->fsp_name);
4266 DBG_ERR("Removing [%s] failed\n", fsp_str_dbg(fsp));
4274 static ssize_t fruit_pread_meta_adouble(vfs_handle_struct *handle,
4275 files_struct *fsp, void *data,
4276 size_t n, off_t offset)
4279 struct adouble *ad = NULL;
4280 char afpinfo_buf[AFP_INFO_SIZE];
4284 ai = afpinfo_new(talloc_tos());
4289 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
4295 p = ad_get_entry(ad, ADEID_FINDERI);
4297 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
4302 memcpy(&ai->afpi_FinderInfo[0], p, ADEDLEN_FINDERI);
4304 nread = afpinfo_pack(ai, afpinfo_buf);
4305 if (nread != AFP_INFO_SIZE) {
4310 memcpy(data, afpinfo_buf, n);
4318 static ssize_t fruit_pread_meta(vfs_handle_struct *handle,
4319 files_struct *fsp, void *data,
4320 size_t n, off_t offset)
4322 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4327 * OS X has a off-by-1 error in the offset calculation, so we're
4328 * bug compatible here. It won't hurt, as any relevant real
4329 * world read requests from the AFP_AfpInfo stream will be
4330 * offset=0 n=60. offset is ignored anyway, see below.
4332 if ((offset < 0) || (offset >= AFP_INFO_SIZE + 1)) {
4337 DBG_ERR("Failed to fetch fsp extension");
4341 /* Yes, macOS always reads from offset 0 */
4343 to_return = MIN(n, AFP_INFO_SIZE);
4345 switch (fio->config->meta) {
4346 case FRUIT_META_STREAM:
4347 nread = fruit_pread_meta_stream(handle, fsp, data,
4351 case FRUIT_META_NETATALK:
4352 nread = fruit_pread_meta_adouble(handle, fsp, data,
4357 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
4361 if (nread == -1 && fio->created) {
4363 char afpinfo_buf[AFP_INFO_SIZE];
4365 ai = afpinfo_new(talloc_tos());
4370 nread = afpinfo_pack(ai, afpinfo_buf);
4372 if (nread != AFP_INFO_SIZE) {
4376 memcpy(data, afpinfo_buf, to_return);
4383 static ssize_t fruit_pread_rsrc_stream(vfs_handle_struct *handle,
4384 files_struct *fsp, void *data,
4385 size_t n, off_t offset)
4387 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4390 static ssize_t fruit_pread_rsrc_xattr(vfs_handle_struct *handle,
4391 files_struct *fsp, void *data,
4392 size_t n, off_t offset)
4394 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4397 static ssize_t fruit_pread_rsrc_adouble(vfs_handle_struct *handle,
4398 files_struct *fsp, void *data,
4399 size_t n, off_t offset)
4401 struct adouble *ad = NULL;
4404 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
4409 nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n,
4410 offset + ad_getentryoff(ad, ADEID_RFORK));
4416 static ssize_t fruit_pread_rsrc(vfs_handle_struct *handle,
4417 files_struct *fsp, void *data,
4418 size_t n, off_t offset)
4420 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4428 switch (fio->config->rsrc) {
4429 case FRUIT_RSRC_STREAM:
4430 nread = fruit_pread_rsrc_stream(handle, fsp, data, n, offset);
4433 case FRUIT_RSRC_ADFILE:
4434 nread = fruit_pread_rsrc_adouble(handle, fsp, data, n, offset);
4437 case FRUIT_RSRC_XATTR:
4438 nread = fruit_pread_rsrc_xattr(handle, fsp, data, n, offset);
4442 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
4449 static ssize_t fruit_pread(vfs_handle_struct *handle,
4450 files_struct *fsp, void *data,
4451 size_t n, off_t offset)
4453 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4456 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4457 fsp_str_dbg(fsp), (intmax_t)offset, n);
4460 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4463 if (fio->type == ADOUBLE_META) {
4464 nread = fruit_pread_meta(handle, fsp, data, n, offset);
4466 nread = fruit_pread_rsrc(handle, fsp, data, n, offset);
4469 DBG_DEBUG("Path [%s] nread [%zd]\n", fsp_str_dbg(fsp), nread);
4473 static bool fruit_must_handle_aio_stream(struct fio *fio)
4479 if (fio->type == ADOUBLE_META) {
4483 if ((fio->type == ADOUBLE_RSRC) &&
4484 (fio->config->rsrc == FRUIT_RSRC_ADFILE))
4492 struct fruit_pread_state {
4494 struct vfs_aio_state vfs_aio_state;
4497 static void fruit_pread_done(struct tevent_req *subreq);
4499 static struct tevent_req *fruit_pread_send(
4500 struct vfs_handle_struct *handle,
4501 TALLOC_CTX *mem_ctx,
4502 struct tevent_context *ev,
4503 struct files_struct *fsp,
4505 size_t n, off_t offset)
4507 struct tevent_req *req = NULL;
4508 struct tevent_req *subreq = NULL;
4509 struct fruit_pread_state *state = NULL;
4510 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4512 req = tevent_req_create(mem_ctx, &state,
4513 struct fruit_pread_state);
4518 if (fruit_must_handle_aio_stream(fio)) {
4519 state->nread = SMB_VFS_PREAD(fsp, data, n, offset);
4520 if (state->nread != n) {
4521 if (state->nread != -1) {
4524 tevent_req_error(req, errno);
4525 return tevent_req_post(req, ev);
4527 tevent_req_done(req);
4528 return tevent_req_post(req, ev);
4531 subreq = SMB_VFS_NEXT_PREAD_SEND(state, ev, handle, fsp,
4533 if (tevent_req_nomem(req, subreq)) {
4534 return tevent_req_post(req, ev);
4536 tevent_req_set_callback(subreq, fruit_pread_done, req);
4540 static void fruit_pread_done(struct tevent_req *subreq)
4542 struct tevent_req *req = tevent_req_callback_data(
4543 subreq, struct tevent_req);
4544 struct fruit_pread_state *state = tevent_req_data(
4545 req, struct fruit_pread_state);
4547 state->nread = SMB_VFS_PREAD_RECV(subreq, &state->vfs_aio_state);
4548 TALLOC_FREE(subreq);
4550 if (tevent_req_error(req, state->vfs_aio_state.error)) {
4553 tevent_req_done(req);
4556 static ssize_t fruit_pread_recv(struct tevent_req *req,
4557 struct vfs_aio_state *vfs_aio_state)
4559 struct fruit_pread_state *state = tevent_req_data(
4560 req, struct fruit_pread_state);
4562 if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
4566 *vfs_aio_state = state->vfs_aio_state;
4567 return state->nread;
4570 static ssize_t fruit_pwrite_meta_stream(vfs_handle_struct *handle,
4571 files_struct *fsp, const void *data,
4572 size_t n, off_t offset)
4574 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4580 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4581 fsp_str_dbg(fsp), (intmax_t)offset, n);
4590 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
4592 DBG_ERR("Close [%s] failed: %s\n",
4593 fsp_str_dbg(fsp), strerror(errno));
4598 fd = SMB_VFS_NEXT_OPEN(handle,
4604 DBG_ERR("On-demand create [%s] in write failed: %s\n",
4605 fsp_str_dbg(fsp), strerror(errno));
4609 fio->fake_fd = false;
4612 ai = afpinfo_unpack(talloc_tos(), data);
4617 if (ai_empty_finderinfo(ai)) {
4619 * Writing an all 0 blob to the metadata stream results in the
4620 * stream being removed on a macOS server. This ensures we
4621 * behave the same and it verified by the "delete AFP_AfpInfo by
4622 * writing all 0" test.
4624 ret = SMB_VFS_NEXT_FTRUNCATE(handle, fsp, 0);
4626 DBG_ERR("SMB_VFS_NEXT_FTRUNCATE on [%s] failed\n",
4631 ok = set_delete_on_close(
4634 handle->conn->session_info->security_token,
4635 handle->conn->session_info->unix_token);
4637 DBG_ERR("set_delete_on_close on [%s] failed\n",
4644 nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4645 if (nwritten != n) {
4652 static ssize_t fruit_pwrite_meta_netatalk(vfs_handle_struct *handle,
4653 files_struct *fsp, const void *data,
4654 size_t n, off_t offset)
4656 struct adouble *ad = NULL;
4662 ai = afpinfo_unpack(talloc_tos(), data);
4667 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
4669 ad = ad_init(talloc_tos(), handle, ADOUBLE_META);
4674 p = ad_get_entry(ad, ADEID_FINDERI);
4676 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
4681 memcpy(p, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
4683 ret = ad_fset(ad, fsp);
4685 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
4692 if (!ai_empty_finderinfo(ai)) {
4697 * Writing an all 0 blob to the metadata stream results in the stream
4698 * being removed on a macOS server. This ensures we behave the same and
4699 * it verified by the "delete AFP_AfpInfo by writing all 0" test.
4702 ok = set_delete_on_close(
4705 handle->conn->session_info->security_token,
4706 handle->conn->session_info->unix_token);
4708 DBG_ERR("set_delete_on_close on [%s] failed\n",
4716 static ssize_t fruit_pwrite_meta(vfs_handle_struct *handle,
4717 files_struct *fsp, const void *data,
4718 size_t n, off_t offset)
4720 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4722 uint8_t buf[AFP_INFO_SIZE];
4728 DBG_ERR("Failed to fetch fsp extension");
4737 if (offset != 0 && n < 60) {
4742 cmp = memcmp(data, "AFP", 3);
4748 if (n <= AFP_OFF_FinderInfo) {
4750 * Nothing to do here really, just return
4758 if (to_copy > AFP_INFO_SIZE) {
4759 to_copy = AFP_INFO_SIZE;
4761 memcpy(buf, data, to_copy);
4764 if (to_write != AFP_INFO_SIZE) {
4765 to_write = AFP_INFO_SIZE;
4768 switch (fio->config->meta) {
4769 case FRUIT_META_STREAM:
4770 nwritten = fruit_pwrite_meta_stream(handle,
4777 case FRUIT_META_NETATALK:
4778 nwritten = fruit_pwrite_meta_netatalk(handle,
4786 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
4790 if (nwritten != to_write) {
4795 * Return the requested amount, verified against macOS SMB server
4800 static ssize_t fruit_pwrite_rsrc_stream(vfs_handle_struct *handle,
4801 files_struct *fsp, const void *data,
4802 size_t n, off_t offset)
4804 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4807 static ssize_t fruit_pwrite_rsrc_xattr(vfs_handle_struct *handle,
4808 files_struct *fsp, const void *data,
4809 size_t n, off_t offset)
4811 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4814 static ssize_t fruit_pwrite_rsrc_adouble(vfs_handle_struct *handle,
4815 files_struct *fsp, const void *data,
4816 size_t n, off_t offset)
4818 struct adouble *ad = NULL;
4822 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
4824 DBG_ERR("ad_get [%s] failed\n", fsp_str_dbg(fsp));
4828 nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n,
4829 offset + ad_getentryoff(ad, ADEID_RFORK));
4830 if (nwritten != n) {
4831 DBG_ERR("Short write on [%s] [%zd/%zd]\n",
4832 fsp_str_dbg(fsp), nwritten, n);
4837 if ((n + offset) > ad_getentrylen(ad, ADEID_RFORK)) {
4838 ad_setentrylen(ad, ADEID_RFORK, n + offset);
4839 ret = ad_fset(ad, fsp);
4841 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
4851 static ssize_t fruit_pwrite_rsrc(vfs_handle_struct *handle,
4852 files_struct *fsp, const void *data,
4853 size_t n, off_t offset)
4855 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4859 DBG_ERR("Failed to fetch fsp extension");
4863 switch (fio->config->rsrc) {
4864 case FRUIT_RSRC_STREAM:
4865 nwritten = fruit_pwrite_rsrc_stream(handle, fsp, data, n, offset);
4868 case FRUIT_RSRC_ADFILE:
4869 nwritten = fruit_pwrite_rsrc_adouble(handle, fsp, data, n, offset);
4872 case FRUIT_RSRC_XATTR:
4873 nwritten = fruit_pwrite_rsrc_xattr(handle, fsp, data, n, offset);
4877 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
4884 static ssize_t fruit_pwrite(vfs_handle_struct *handle,
4885 files_struct *fsp, const void *data,
4886 size_t n, off_t offset)
4888 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4891 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4892 fsp_str_dbg(fsp), (intmax_t)offset, n);
4895 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4898 if (fio->type == ADOUBLE_META) {
4899 nwritten = fruit_pwrite_meta(handle, fsp, data, n, offset);
4901 nwritten = fruit_pwrite_rsrc(handle, fsp, data, n, offset);
4904 DBG_DEBUG("Path [%s] nwritten=%zd\n", fsp_str_dbg(fsp), nwritten);
4908 struct fruit_pwrite_state {
4910 struct vfs_aio_state vfs_aio_state;
4913 static void fruit_pwrite_done(struct tevent_req *subreq);
4915 static struct tevent_req *fruit_pwrite_send(
4916 struct vfs_handle_struct *handle,
4917 TALLOC_CTX *mem_ctx,
4918 struct tevent_context *ev,
4919 struct files_struct *fsp,
4921 size_t n, off_t offset)
4923 struct tevent_req *req = NULL;
4924 struct tevent_req *subreq = NULL;
4925 struct fruit_pwrite_state *state = NULL;
4926 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4928 req = tevent_req_create(mem_ctx, &state,
4929 struct fruit_pwrite_state);
4934 if (fruit_must_handle_aio_stream(fio)) {
4935 state->nwritten = SMB_VFS_PWRITE(fsp, data, n, offset);
4936 if (state->nwritten != n) {
4937 if (state->nwritten != -1) {
4940 tevent_req_error(req, errno);
4941 return tevent_req_post(req, ev);
4943 tevent_req_done(req);
4944 return tevent_req_post(req, ev);
4947 subreq = SMB_VFS_NEXT_PWRITE_SEND(state, ev, handle, fsp,
4949 if (tevent_req_nomem(req, subreq)) {
4950 return tevent_req_post(req, ev);
4952 tevent_req_set_callback(subreq, fruit_pwrite_done, req);
4956 static void fruit_pwrite_done(struct tevent_req *subreq)
4958 struct tevent_req *req = tevent_req_callback_data(
4959 subreq, struct tevent_req);
4960 struct fruit_pwrite_state *state = tevent_req_data(
4961 req, struct fruit_pwrite_state);
4963 state->nwritten = SMB_VFS_PWRITE_RECV(subreq, &state->vfs_aio_state);
4964 TALLOC_FREE(subreq);
4966 if (tevent_req_error(req, state->vfs_aio_state.error)) {
4969 tevent_req_done(req);
4972 static ssize_t fruit_pwrite_recv(struct tevent_req *req,
4973 struct vfs_aio_state *vfs_aio_state)
4975 struct fruit_pwrite_state *state = tevent_req_data(
4976 req, struct fruit_pwrite_state);
4978 if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
4982 *vfs_aio_state = state->vfs_aio_state;
4983 return state->nwritten;
4987 * Helper to stat/lstat the base file of an smb_fname.
4989 static int fruit_stat_base(vfs_handle_struct *handle,
4990 struct smb_filename *smb_fname,
4993 char *tmp_stream_name;
4996 tmp_stream_name = smb_fname->stream_name;
4997 smb_fname->stream_name = NULL;
4999 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
5001 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5003 smb_fname->stream_name = tmp_stream_name;
5005 DBG_DEBUG("fruit_stat_base [%s] dev [%ju] ino [%ju]\n",
5006 smb_fname->base_name,
5007 (uintmax_t)smb_fname->st.st_ex_dev,
5008 (uintmax_t)smb_fname->st.st_ex_ino);
5012 static int fruit_stat_meta_stream(vfs_handle_struct *handle,
5013 struct smb_filename *smb_fname,
5019 ret = fruit_stat_base(handle, smb_fname, false);
5024 ino = fruit_inode(&smb_fname->st, smb_fname->stream_name);
5027 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
5029 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5032 smb_fname->st.st_ex_ino = ino;
5037 static int fruit_stat_meta_netatalk(vfs_handle_struct *handle,
5038 struct smb_filename *smb_fname,
5041 struct adouble *ad = NULL;
5043 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5045 DBG_INFO("fruit_stat_meta %s: %s\n",
5046 smb_fname_str_dbg(smb_fname), strerror(errno));
5052 /* Populate the stat struct with info from the base file. */
5053 if (fruit_stat_base(handle, smb_fname, follow_links) == -1) {
5056 smb_fname->st.st_ex_size = AFP_INFO_SIZE;
5057 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5058 smb_fname->stream_name);
5062 static int fruit_stat_meta(vfs_handle_struct *handle,
5063 struct smb_filename *smb_fname,
5066 struct fruit_config_data *config = NULL;
5069 SMB_VFS_HANDLE_GET_DATA(handle, config,
5070 struct fruit_config_data, return -1);
5072 switch (config->meta) {
5073 case FRUIT_META_STREAM:
5074 ret = fruit_stat_meta_stream(handle, smb_fname, follow_links);
5077 case FRUIT_META_NETATALK:
5078 ret = fruit_stat_meta_netatalk(handle, smb_fname, follow_links);
5082 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
5089 static int fruit_stat_rsrc_netatalk(vfs_handle_struct *handle,
5090 struct smb_filename *smb_fname,
5093 struct adouble *ad = NULL;
5096 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
5102 /* Populate the stat struct with info from the base file. */
5103 ret = fruit_stat_base(handle, smb_fname, follow_links);
5109 smb_fname->st.st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
5110 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5111 smb_fname->stream_name);
5116 static int fruit_stat_rsrc_stream(vfs_handle_struct *handle,
5117 struct smb_filename *smb_fname,
5123 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
5125 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5131 static int fruit_stat_rsrc_xattr(vfs_handle_struct *handle,
5132 struct smb_filename *smb_fname,
5135 #ifdef HAVE_ATTROPEN
5139 /* Populate the stat struct with info from the base file. */
5140 ret = fruit_stat_base(handle, smb_fname, follow_links);
5145 fd = attropen(smb_fname->base_name,
5146 AFPRESOURCE_EA_NETATALK,
5152 ret = sys_fstat(fd, &smb_fname->st, false);
5155 DBG_ERR("fstat [%s:%s] failed\n", smb_fname->base_name,
5156 AFPRESOURCE_EA_NETATALK);
5162 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5163 smb_fname->stream_name);
5173 static int fruit_stat_rsrc(vfs_handle_struct *handle,
5174 struct smb_filename *smb_fname,
5177 struct fruit_config_data *config = NULL;
5180 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
5182 SMB_VFS_HANDLE_GET_DATA(handle, config,
5183 struct fruit_config_data, return -1);
5185 switch (config->rsrc) {
5186 case FRUIT_RSRC_STREAM:
5187 ret = fruit_stat_rsrc_stream(handle, smb_fname, follow_links);
5190 case FRUIT_RSRC_XATTR:
5191 ret = fruit_stat_rsrc_xattr(handle, smb_fname, follow_links);
5194 case FRUIT_RSRC_ADFILE:
5195 ret = fruit_stat_rsrc_netatalk(handle, smb_fname, follow_links);
5199 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
5206 static int fruit_stat(vfs_handle_struct *handle,
5207 struct smb_filename *smb_fname)
5211 DEBUG(10, ("fruit_stat called for %s\n",
5212 smb_fname_str_dbg(smb_fname)));
5214 if (!is_ntfs_stream_smb_fname(smb_fname)
5215 || is_ntfs_default_stream_smb_fname(smb_fname)) {
5216 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
5218 update_btime(handle, smb_fname);
5224 * Note if lp_posix_paths() is true, we can never
5225 * get here as is_ntfs_stream_smb_fname() is
5226 * always false. So we never need worry about
5227 * not following links here.
5230 if (is_afpinfo_stream(smb_fname)) {
5231 rc = fruit_stat_meta(handle, smb_fname, true);
5232 } else if (is_afpresource_stream(smb_fname)) {
5233 rc = fruit_stat_rsrc(handle, smb_fname, true);
5235 return SMB_VFS_NEXT_STAT(handle, smb_fname);
5239 update_btime(handle, smb_fname);
5240 smb_fname->st.st_ex_mode &= ~S_IFMT;
5241 smb_fname->st.st_ex_mode |= S_IFREG;
5242 smb_fname->st.st_ex_blocks =
5243 smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
5248 static int fruit_lstat(vfs_handle_struct *handle,
5249 struct smb_filename *smb_fname)
5253 DEBUG(10, ("fruit_lstat called for %s\n",
5254 smb_fname_str_dbg(smb_fname)));
5256 if (!is_ntfs_stream_smb_fname(smb_fname)
5257 || is_ntfs_default_stream_smb_fname(smb_fname)) {
5258 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5260 update_btime(handle, smb_fname);
5265 if (is_afpinfo_stream(smb_fname)) {
5266 rc = fruit_stat_meta(handle, smb_fname, false);
5267 } else if (is_afpresource_stream(smb_fname)) {
5268 rc = fruit_stat_rsrc(handle, smb_fname, false);
5270 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5274 update_btime(handle, smb_fname);
5275 smb_fname->st.st_ex_mode &= ~S_IFMT;
5276 smb_fname->st.st_ex_mode |= S_IFREG;
5277 smb_fname->st.st_ex_blocks =
5278 smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
5283 static int fruit_fstat_meta_stream(vfs_handle_struct *handle,
5285 SMB_STRUCT_STAT *sbuf)
5287 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5288 struct smb_filename smb_fname;
5297 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5302 *sbuf = fsp->base_fsp->fsp_name->st;
5303 sbuf->st_ex_size = AFP_INFO_SIZE;
5304 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5308 smb_fname = (struct smb_filename) {
5309 .base_name = fsp->fsp_name->base_name,
5312 ret = fruit_stat_base(handle, &smb_fname, false);
5316 *sbuf = smb_fname.st;
5318 ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5320 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5325 sbuf->st_ex_ino = ino;
5329 static int fruit_fstat_meta_netatalk(vfs_handle_struct *handle,
5331 SMB_STRUCT_STAT *sbuf)
5335 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5340 *sbuf = fsp->base_fsp->fsp_name->st;
5341 sbuf->st_ex_size = AFP_INFO_SIZE;
5342 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5347 static int fruit_fstat_meta(vfs_handle_struct *handle,
5349 SMB_STRUCT_STAT *sbuf,
5354 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
5356 switch (fio->config->meta) {
5357 case FRUIT_META_STREAM:
5358 ret = fruit_fstat_meta_stream(handle, fsp, sbuf);
5361 case FRUIT_META_NETATALK:
5362 ret = fruit_fstat_meta_netatalk(handle, fsp, sbuf);
5366 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
5370 DBG_DEBUG("Path [%s] ret [%d]\n", fsp_str_dbg(fsp), ret);
5374 static int fruit_fstat_rsrc_xattr(vfs_handle_struct *handle,
5376 SMB_STRUCT_STAT *sbuf)
5378 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5381 static int fruit_fstat_rsrc_stream(vfs_handle_struct *handle,
5383 SMB_STRUCT_STAT *sbuf)
5385 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5388 static int fruit_fstat_rsrc_adouble(vfs_handle_struct *handle,
5390 SMB_STRUCT_STAT *sbuf)
5392 struct adouble *ad = NULL;
5395 /* Populate the stat struct with info from the base file. */
5396 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5401 ad = ad_get(talloc_tos(), handle,
5402 fsp->base_fsp->fsp_name,
5405 DBG_ERR("ad_get [%s] failed [%s]\n",
5406 fsp_str_dbg(fsp), strerror(errno));
5410 *sbuf = fsp->base_fsp->fsp_name->st;
5411 sbuf->st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
5412 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5418 static int fruit_fstat_rsrc(vfs_handle_struct *handle, files_struct *fsp,
5419 SMB_STRUCT_STAT *sbuf, struct fio *fio)
5423 switch (fio->config->rsrc) {
5424 case FRUIT_RSRC_STREAM:
5425 ret = fruit_fstat_rsrc_stream(handle, fsp, sbuf);
5428 case FRUIT_RSRC_ADFILE:
5429 ret = fruit_fstat_rsrc_adouble(handle, fsp, sbuf);
5432 case FRUIT_RSRC_XATTR:
5433 ret = fruit_fstat_rsrc_xattr(handle, fsp, sbuf);
5437 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
5444 static int fruit_fstat(vfs_handle_struct *handle, files_struct *fsp,
5445 SMB_STRUCT_STAT *sbuf)
5447 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5451 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5454 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
5456 if (fio->type == ADOUBLE_META) {
5457 rc = fruit_fstat_meta(handle, fsp, sbuf, fio);
5459 rc = fruit_fstat_rsrc(handle, fsp, sbuf, fio);
5463 sbuf->st_ex_mode &= ~S_IFMT;
5464 sbuf->st_ex_mode |= S_IFREG;
5465 sbuf->st_ex_blocks = sbuf->st_ex_size / STAT_ST_BLOCKSIZE + 1;
5468 DBG_DEBUG("Path [%s] rc [%d] size [%"PRIdMAX"]\n",
5469 fsp_str_dbg(fsp), rc, (intmax_t)sbuf->st_ex_size);
5473 static NTSTATUS delete_invalid_meta_stream(
5474 vfs_handle_struct *handle,
5475 const struct smb_filename *smb_fname,
5476 TALLOC_CTX *mem_ctx,
5477 unsigned int *pnum_streams,
5478 struct stream_struct **pstreams,
5481 struct smb_filename *sname = NULL;
5485 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams, AFPINFO_STREAM);
5487 return NT_STATUS_INTERNAL_ERROR;
5491 return NT_STATUS_OK;
5494 sname = synthetic_smb_fname(talloc_tos(),
5495 smb_fname->base_name,
5496 AFPINFO_STREAM_NAME,
5498 if (sname == NULL) {
5499 return NT_STATUS_NO_MEMORY;
5502 ret = SMB_VFS_NEXT_UNLINK(handle, sname);
5505 DBG_ERR("Removing [%s] failed\n", smb_fname_str_dbg(sname));
5506 return map_nt_error_from_unix(errno);
5509 return NT_STATUS_OK;
5512 static NTSTATUS fruit_streaminfo_meta_stream(
5513 vfs_handle_struct *handle,
5514 struct files_struct *fsp,
5515 const struct smb_filename *smb_fname,
5516 TALLOC_CTX *mem_ctx,
5517 unsigned int *pnum_streams,
5518 struct stream_struct **pstreams)
5520 struct stream_struct *stream = *pstreams;
5521 unsigned int num_streams = *pnum_streams;
5524 for (i = 0; i < num_streams; i++) {
5525 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
5530 if (i == num_streams) {
5531 return NT_STATUS_OK;
5534 if (stream[i].size != AFP_INFO_SIZE) {
5535 DBG_ERR("Removing invalid AFPINFO_STREAM size [%jd] from [%s]\n",
5536 (intmax_t)stream[i].size, smb_fname_str_dbg(smb_fname));
5538 return delete_invalid_meta_stream(handle,
5547 return NT_STATUS_OK;
5550 static NTSTATUS fruit_streaminfo_meta_netatalk(
5551 vfs_handle_struct *handle,
5552 struct files_struct *fsp,
5553 const struct smb_filename *smb_fname,
5554 TALLOC_CTX *mem_ctx,
5555 unsigned int *pnum_streams,
5556 struct stream_struct **pstreams)
5558 struct stream_struct *stream = *pstreams;
5559 unsigned int num_streams = *pnum_streams;
5560 struct adouble *ad = NULL;
5565 /* Remove the Netatalk xattr from the list */
5566 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5567 ":" NETATALK_META_XATTR ":$DATA");
5569 return NT_STATUS_NO_MEMORY;
5573 * Check if there's a AFPINFO_STREAM from the VFS streams
5574 * backend and if yes, remove it from the list
5576 for (i = 0; i < num_streams; i++) {
5577 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
5582 if (i < num_streams) {
5583 DBG_WARNING("Unexpected AFPINFO_STREAM on [%s]\n",
5584 smb_fname_str_dbg(smb_fname));
5586 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5589 return NT_STATUS_INTERNAL_ERROR;
5593 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5595 return NT_STATUS_OK;
5598 is_fi_empty = ad_empty_finderinfo(ad);
5602 return NT_STATUS_OK;
5605 ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
5606 AFPINFO_STREAM_NAME, AFP_INFO_SIZE,
5607 smb_roundup(handle->conn, AFP_INFO_SIZE));
5609 return NT_STATUS_NO_MEMORY;
5612 return NT_STATUS_OK;
5615 static NTSTATUS fruit_streaminfo_meta(vfs_handle_struct *handle,
5616 struct files_struct *fsp,
5617 const struct smb_filename *smb_fname,
5618 TALLOC_CTX *mem_ctx,
5619 unsigned int *pnum_streams,
5620 struct stream_struct **pstreams)
5622 struct fruit_config_data *config = NULL;
5625 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5626 return NT_STATUS_INTERNAL_ERROR);
5628 switch (config->meta) {
5629 case FRUIT_META_NETATALK:
5630 status = fruit_streaminfo_meta_netatalk(handle, fsp, smb_fname,
5631 mem_ctx, pnum_streams,
5635 case FRUIT_META_STREAM:
5636 status = fruit_streaminfo_meta_stream(handle, fsp, smb_fname,
5637 mem_ctx, pnum_streams,
5642 return NT_STATUS_INTERNAL_ERROR;
5648 static NTSTATUS fruit_streaminfo_rsrc_stream(
5649 vfs_handle_struct *handle,
5650 struct files_struct *fsp,
5651 const struct smb_filename *smb_fname,
5652 TALLOC_CTX *mem_ctx,
5653 unsigned int *pnum_streams,
5654 struct stream_struct **pstreams)
5658 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
5660 DBG_ERR("Filtering resource stream failed\n");
5661 return NT_STATUS_INTERNAL_ERROR;
5663 return NT_STATUS_OK;
5666 static NTSTATUS fruit_streaminfo_rsrc_xattr(
5667 vfs_handle_struct *handle,
5668 struct files_struct *fsp,
5669 const struct smb_filename *smb_fname,
5670 TALLOC_CTX *mem_ctx,
5671 unsigned int *pnum_streams,
5672 struct stream_struct **pstreams)
5676 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
5678 DBG_ERR("Filtering resource stream failed\n");
5679 return NT_STATUS_INTERNAL_ERROR;
5681 return NT_STATUS_OK;
5684 static NTSTATUS fruit_streaminfo_rsrc_adouble(
5685 vfs_handle_struct *handle,
5686 struct files_struct *fsp,
5687 const struct smb_filename *smb_fname,
5688 TALLOC_CTX *mem_ctx,
5689 unsigned int *pnum_streams,
5690 struct stream_struct **pstreams)
5692 struct stream_struct *stream = *pstreams;
5693 unsigned int num_streams = *pnum_streams;
5694 struct adouble *ad = NULL;
5700 * Check if there's a AFPRESOURCE_STREAM from the VFS streams backend
5701 * and if yes, remove it from the list
5703 for (i = 0; i < num_streams; i++) {
5704 if (strequal_m(stream[i].name, AFPRESOURCE_STREAM)) {
5709 if (i < num_streams) {
5710 DBG_WARNING("Unexpected AFPRESOURCE_STREAM on [%s]\n",
5711 smb_fname_str_dbg(smb_fname));
5713 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5714 AFPRESOURCE_STREAM);
5716 return NT_STATUS_INTERNAL_ERROR;
5720 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
5722 return NT_STATUS_OK;
5725 rlen = ad_getentrylen(ad, ADEID_RFORK);
5729 return NT_STATUS_OK;
5732 ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
5733 AFPRESOURCE_STREAM_NAME, rlen,
5734 smb_roundup(handle->conn, rlen));
5736 return NT_STATUS_NO_MEMORY;
5739 return NT_STATUS_OK;
5742 static NTSTATUS fruit_streaminfo_rsrc(vfs_handle_struct *handle,
5743 struct files_struct *fsp,
5744 const struct smb_filename *smb_fname,
5745 TALLOC_CTX *mem_ctx,
5746 unsigned int *pnum_streams,
5747 struct stream_struct **pstreams)
5749 struct fruit_config_data *config = NULL;
5752 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5753 return NT_STATUS_INTERNAL_ERROR);
5755 switch (config->rsrc) {
5756 case FRUIT_RSRC_STREAM:
5757 status = fruit_streaminfo_rsrc_stream(handle, fsp, smb_fname,
5758 mem_ctx, pnum_streams,
5762 case FRUIT_RSRC_XATTR:
5763 status = fruit_streaminfo_rsrc_xattr(handle, fsp, smb_fname,
5764 mem_ctx, pnum_streams,
5768 case FRUIT_RSRC_ADFILE:
5769 status = fruit_streaminfo_rsrc_adouble(handle, fsp, smb_fname,
5770 mem_ctx, pnum_streams,
5775 return NT_STATUS_INTERNAL_ERROR;
5781 static void fruit_filter_empty_streams(unsigned int *pnum_streams,
5782 struct stream_struct **pstreams)
5784 unsigned num_streams = *pnum_streams;
5785 struct stream_struct *streams = *pstreams;
5788 if (!global_fruit_config.nego_aapl) {
5792 while (i < num_streams) {
5793 struct smb_filename smb_fname = (struct smb_filename) {
5794 .stream_name = streams[i].name,
5797 if (is_ntfs_default_stream_smb_fname(&smb_fname)
5798 || streams[i].size > 0)
5804 streams[i] = streams[num_streams - 1];
5808 *pnum_streams = num_streams;
5811 static NTSTATUS fruit_streaminfo(vfs_handle_struct *handle,
5812 struct files_struct *fsp,
5813 const struct smb_filename *smb_fname,
5814 TALLOC_CTX *mem_ctx,
5815 unsigned int *pnum_streams,
5816 struct stream_struct **pstreams)
5818 struct fruit_config_data *config = NULL;
5821 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5822 return NT_STATUS_UNSUCCESSFUL);
5824 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
5826 status = SMB_VFS_NEXT_STREAMINFO(handle, fsp, smb_fname, mem_ctx,
5827 pnum_streams, pstreams);
5828 if (!NT_STATUS_IS_OK(status)) {
5832 fruit_filter_empty_streams(pnum_streams, pstreams);
5834 status = fruit_streaminfo_meta(handle, fsp, smb_fname,
5835 mem_ctx, pnum_streams, pstreams);
5836 if (!NT_STATUS_IS_OK(status)) {
5840 status = fruit_streaminfo_rsrc(handle, fsp, smb_fname,
5841 mem_ctx, pnum_streams, pstreams);
5842 if (!NT_STATUS_IS_OK(status)) {
5846 return NT_STATUS_OK;
5849 static int fruit_ntimes(vfs_handle_struct *handle,
5850 const struct smb_filename *smb_fname,
5851 struct smb_file_time *ft)
5854 struct adouble *ad = NULL;
5855 struct fruit_config_data *config = NULL;
5857 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5860 if ((config->meta != FRUIT_META_NETATALK) ||
5861 null_timespec(ft->create_time))
5863 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
5866 DEBUG(10,("set btime for %s to %s\n", smb_fname_str_dbg(smb_fname),
5867 time_to_asc(convert_timespec_to_time_t(ft->create_time))));
5869 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5874 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX,
5875 convert_time_t_to_uint32_t(ft->create_time.tv_sec));
5877 rc = ad_set(ad, smb_fname);
5883 DEBUG(1, ("fruit_ntimes: %s\n", smb_fname_str_dbg(smb_fname)));
5886 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
5889 static int fruit_fallocate(struct vfs_handle_struct *handle,
5890 struct files_struct *fsp,
5895 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5898 return SMB_VFS_NEXT_FALLOCATE(handle, fsp, mode, offset, len);
5901 /* Let the pwrite code path handle it. */
5906 static int fruit_ftruncate_rsrc_xattr(struct vfs_handle_struct *handle,
5907 struct files_struct *fsp,
5910 #ifdef HAVE_ATTROPEN
5911 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
5916 static int fruit_ftruncate_rsrc_adouble(struct vfs_handle_struct *handle,
5917 struct files_struct *fsp,
5921 struct adouble *ad = NULL;
5924 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
5926 DBG_DEBUG("ad_get [%s] failed [%s]\n",
5927 fsp_str_dbg(fsp), strerror(errno));
5931 ad_off = ad_getentryoff(ad, ADEID_RFORK);
5933 rc = ftruncate(fsp->fh->fd, offset + ad_off);
5939 ad_setentrylen(ad, ADEID_RFORK, offset);
5941 rc = ad_fset(ad, fsp);
5943 DBG_ERR("ad_fset [%s] failed [%s]\n",
5944 fsp_str_dbg(fsp), strerror(errno));
5953 static int fruit_ftruncate_rsrc_stream(struct vfs_handle_struct *handle,
5954 struct files_struct *fsp,
5957 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
5960 static int fruit_ftruncate_rsrc(struct vfs_handle_struct *handle,
5961 struct files_struct *fsp,
5964 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5968 DBG_ERR("Failed to fetch fsp extension");
5972 switch (fio->config->rsrc) {
5973 case FRUIT_RSRC_XATTR:
5974 ret = fruit_ftruncate_rsrc_xattr(handle, fsp, offset);
5977 case FRUIT_RSRC_ADFILE:
5978 ret = fruit_ftruncate_rsrc_adouble(handle, fsp, offset);
5981 case FRUIT_RSRC_STREAM:
5982 ret = fruit_ftruncate_rsrc_stream(handle, fsp, offset);
5986 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
5994 static int fruit_ftruncate_meta(struct vfs_handle_struct *handle,
5995 struct files_struct *fsp,
5999 DBG_WARNING("ftruncate %s to %jd",
6000 fsp_str_dbg(fsp), (intmax_t)offset);
6001 /* OS X returns NT_STATUS_ALLOTTED_SPACE_EXCEEDED */
6006 /* OS X returns success but does nothing */
6007 DBG_INFO("ignoring ftruncate %s to %jd\n",
6008 fsp_str_dbg(fsp), (intmax_t)offset);
6012 static int fruit_ftruncate(struct vfs_handle_struct *handle,
6013 struct files_struct *fsp,
6016 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
6019 DBG_DEBUG("Path [%s] offset [%"PRIdMAX"]\n", fsp_str_dbg(fsp),
6023 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
6026 if (fio->type == ADOUBLE_META) {
6027 ret = fruit_ftruncate_meta(handle, fsp, offset);
6029 ret = fruit_ftruncate_rsrc(handle, fsp, offset);
6032 DBG_DEBUG("Path [%s] result [%d]\n", fsp_str_dbg(fsp), ret);
6036 static NTSTATUS fruit_create_file(vfs_handle_struct *handle,
6037 struct smb_request *req,
6038 uint16_t root_dir_fid,
6039 struct smb_filename *smb_fname,
6040 uint32_t access_mask,
6041 uint32_t share_access,
6042 uint32_t create_disposition,
6043 uint32_t create_options,
6044 uint32_t file_attributes,
6045 uint32_t oplock_request,
6046 struct smb2_lease *lease,
6047 uint64_t allocation_size,
6048 uint32_t private_flags,
6049 struct security_descriptor *sd,
6050 struct ea_list *ea_list,
6051 files_struct **result,
6053 const struct smb2_create_blobs *in_context_blobs,
6054 struct smb2_create_blobs *out_context_blobs)
6057 struct fruit_config_data *config = NULL;
6058 files_struct *fsp = NULL;
6059 struct fio *fio = NULL;
6061 status = check_aapl(handle, req, in_context_blobs, out_context_blobs);
6062 if (!NT_STATUS_IS_OK(status)) {
6066 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
6067 return NT_STATUS_UNSUCCESSFUL);
6069 status = SMB_VFS_NEXT_CREATE_FILE(
6070 handle, req, root_dir_fid, smb_fname,
6071 access_mask, share_access,
6072 create_disposition, create_options,
6073 file_attributes, oplock_request,
6075 allocation_size, private_flags,
6076 sd, ea_list, result,
6077 pinfo, in_context_blobs, out_context_blobs);
6078 if (!NT_STATUS_IS_OK(status)) {
6084 if (global_fruit_config.nego_aapl) {
6085 if (config->posix_rename && fsp->is_directory) {
6087 * Enable POSIX directory rename behaviour
6089 fsp->posix_flags |= FSP_POSIX_FLAGS_RENAME;
6094 * If this is a plain open for existing files, opening an 0
6095 * byte size resource fork MUST fail with
6096 * NT_STATUS_OBJECT_NAME_NOT_FOUND.
6098 * Cf the vfs_fruit torture tests in test_rfork_create().
6100 if (global_fruit_config.nego_aapl &&
6101 create_disposition == FILE_OPEN &&
6102 smb_fname->st.st_ex_size == 0 &&
6103 is_ntfs_stream_smb_fname(smb_fname) &&
6104 !(is_ntfs_default_stream_smb_fname(smb_fname)))
6106 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
6110 fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
6111 if (fio != NULL && pinfo != NULL && *pinfo == FILE_WAS_CREATED) {
6112 fio->created = true;
6115 if (is_ntfs_stream_smb_fname(smb_fname)
6116 || fsp->is_directory) {
6120 if (config->locking == FRUIT_LOCKING_NETATALK) {
6121 status = fruit_check_access(
6124 map_share_mode_to_deny_mode(share_access, 0));
6125 if (!NT_STATUS_IS_OK(status)) {
6133 DEBUG(10, ("fruit_create_file: %s\n", nt_errstr(status)));
6136 close_file(req, fsp, ERROR_CLOSE);
6137 *result = fsp = NULL;
6143 static NTSTATUS fruit_readdir_attr(struct vfs_handle_struct *handle,
6144 const struct smb_filename *fname,
6145 TALLOC_CTX *mem_ctx,
6146 struct readdir_attr_data **pattr_data)
6148 struct fruit_config_data *config = NULL;
6149 struct readdir_attr_data *attr_data;
6152 SMB_VFS_HANDLE_GET_DATA(handle, config,
6153 struct fruit_config_data,
6154 return NT_STATUS_UNSUCCESSFUL);
6156 if (!global_fruit_config.nego_aapl) {
6157 return SMB_VFS_NEXT_READDIR_ATTR(handle, fname, mem_ctx, pattr_data);
6160 DEBUG(10, ("fruit_readdir_attr %s\n", fname->base_name));
6162 *pattr_data = talloc_zero(mem_ctx, struct readdir_attr_data);
6163 if (*pattr_data == NULL) {
6164 return NT_STATUS_UNSUCCESSFUL;
6166 attr_data = *pattr_data;
6167 attr_data->type = RDATTR_AAPL;
6170 * Mac metadata: compressed FinderInfo, resource fork length
6173 status = readdir_attr_macmeta(handle, fname, attr_data);
6174 if (!NT_STATUS_IS_OK(status)) {
6176 * Error handling is tricky: if we return failure from
6177 * this function, the corresponding directory entry
6178 * will to be passed to the client, so we really just
6179 * want to error out on fatal errors.
6181 if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
6189 if (config->unix_info_enabled) {
6190 attr_data->attr_data.aapl.unix_mode = fname->st.st_ex_mode;
6196 if (!config->readdir_attr_max_access) {
6197 attr_data->attr_data.aapl.max_access = FILE_GENERIC_ALL;
6199 status = smbd_calculate_access_mask(
6203 SEC_FLAG_MAXIMUM_ALLOWED,
6204 &attr_data->attr_data.aapl.max_access);
6205 if (!NT_STATUS_IS_OK(status)) {
6210 return NT_STATUS_OK;
6213 DEBUG(1, ("fruit_readdir_attr %s, error: %s\n",
6214 fname->base_name, nt_errstr(status)));
6215 TALLOC_FREE(*pattr_data);
6219 static NTSTATUS fruit_fget_nt_acl(vfs_handle_struct *handle,
6221 uint32_t security_info,
6222 TALLOC_CTX *mem_ctx,
6223 struct security_descriptor **ppdesc)
6226 struct security_ace ace;
6228 struct fruit_config_data *config;
6230 SMB_VFS_HANDLE_GET_DATA(handle, config,
6231 struct fruit_config_data,
6232 return NT_STATUS_UNSUCCESSFUL);
6234 status = SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
6236 if (!NT_STATUS_IS_OK(status)) {
6241 * Add MS NFS style ACEs with uid, gid and mode
6243 if (!global_fruit_config.nego_aapl) {
6244 return NT_STATUS_OK;
6246 if (!config->unix_info_enabled) {
6247 return NT_STATUS_OK;
6250 /* First remove any existing ACE's with NFS style mode/uid/gid SIDs. */
6251 status = remove_virtual_nfs_aces(*ppdesc);
6252 if (!NT_STATUS_IS_OK(status)) {
6253 DBG_WARNING("failed to remove MS NFS style ACEs\n");
6257 /* MS NFS style mode */
6258 sid_compose(&sid, &global_sid_Unix_NFS_Mode, fsp->fsp_name->st.st_ex_mode);
6259 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6260 status = security_descriptor_dacl_add(*ppdesc, &ace);
6261 if (!NT_STATUS_IS_OK(status)) {
6262 DEBUG(1,("failed to add MS NFS style ACE\n"));
6266 /* MS NFS style uid */
6267 sid_compose(&sid, &global_sid_Unix_NFS_Users, fsp->fsp_name->st.st_ex_uid);
6268 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6269 status = security_descriptor_dacl_add(*ppdesc, &ace);
6270 if (!NT_STATUS_IS_OK(status)) {
6271 DEBUG(1,("failed to add MS NFS style ACE\n"));
6275 /* MS NFS style gid */
6276 sid_compose(&sid, &global_sid_Unix_NFS_Groups, fsp->fsp_name->st.st_ex_gid);
6277 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6278 status = security_descriptor_dacl_add(*ppdesc, &ace);
6279 if (!NT_STATUS_IS_OK(status)) {
6280 DEBUG(1,("failed to add MS NFS style ACE\n"));
6284 return NT_STATUS_OK;
6287 static NTSTATUS fruit_fset_nt_acl(vfs_handle_struct *handle,
6289 uint32_t security_info_sent,
6290 const struct security_descriptor *orig_psd)
6294 mode_t ms_nfs_mode = 0;
6296 struct security_descriptor *psd = NULL;
6297 uint32_t orig_num_aces = 0;
6299 if (orig_psd->dacl != NULL) {
6300 orig_num_aces = orig_psd->dacl->num_aces;
6303 psd = security_descriptor_copy(talloc_tos(), orig_psd);
6305 return NT_STATUS_NO_MEMORY;
6308 DBG_DEBUG("fruit_fset_nt_acl: %s\n", fsp_str_dbg(fsp));
6310 status = check_ms_nfs(handle, fsp, psd, &ms_nfs_mode, &do_chmod);
6311 if (!NT_STATUS_IS_OK(status)) {
6312 DEBUG(1, ("fruit_fset_nt_acl: check_ms_nfs failed%s\n", fsp_str_dbg(fsp)));
6318 * If only ms_nfs ACE entries were sent, ensure we set the DACL
6319 * sent/present flags correctly now we've removed them.
6322 if (orig_num_aces != 0) {
6324 * Are there any ACE's left ?
6326 if (psd->dacl->num_aces == 0) {
6327 /* No - clear the DACL sent/present flags. */
6328 security_info_sent &= ~SECINFO_DACL;
6329 psd->type &= ~SEC_DESC_DACL_PRESENT;
6333 status = SMB_VFS_NEXT_FSET_NT_ACL(handle, fsp, security_info_sent, psd);
6334 if (!NT_STATUS_IS_OK(status)) {
6335 DEBUG(1, ("fruit_fset_nt_acl: SMB_VFS_NEXT_FSET_NT_ACL failed%s\n", fsp_str_dbg(fsp)));
6341 if (fsp->fh->fd != -1) {
6342 result = SMB_VFS_FCHMOD(fsp, ms_nfs_mode);
6344 result = SMB_VFS_CHMOD(fsp->conn,
6350 DEBUG(1, ("chmod: %s, result: %d, %04o error %s\n", fsp_str_dbg(fsp),
6351 result, (unsigned)ms_nfs_mode,
6353 status = map_nt_error_from_unix(errno);
6360 return NT_STATUS_OK;
6363 static struct vfs_offload_ctx *fruit_offload_ctx;
6365 struct fruit_offload_read_state {
6366 struct vfs_handle_struct *handle;
6367 struct tevent_context *ev;
6373 static void fruit_offload_read_done(struct tevent_req *subreq);
6375 static struct tevent_req *fruit_offload_read_send(
6376 TALLOC_CTX *mem_ctx,
6377 struct tevent_context *ev,
6378 struct vfs_handle_struct *handle,
6385 struct tevent_req *req = NULL;
6386 struct tevent_req *subreq = NULL;
6387 struct fruit_offload_read_state *state = NULL;
6389 req = tevent_req_create(mem_ctx, &state,
6390 struct fruit_offload_read_state);
6394 *state = (struct fruit_offload_read_state) {
6401 subreq = SMB_VFS_NEXT_OFFLOAD_READ_SEND(mem_ctx, ev, handle, fsp,
6402 fsctl, ttl, offset, to_copy);
6403 if (tevent_req_nomem(subreq, req)) {
6404 return tevent_req_post(req, ev);
6406 tevent_req_set_callback(subreq, fruit_offload_read_done, req);
6410 static void fruit_offload_read_done(struct tevent_req *subreq)
6412 struct tevent_req *req = tevent_req_callback_data(
6413 subreq, struct tevent_req);
6414 struct fruit_offload_read_state *state = tevent_req_data(
6415 req, struct fruit_offload_read_state);
6418 status = SMB_VFS_NEXT_OFFLOAD_READ_RECV(subreq,
6422 TALLOC_FREE(subreq);
6423 if (tevent_req_nterror(req, status)) {
6427 if (state->fsctl != FSCTL_SRV_REQUEST_RESUME_KEY) {
6428 tevent_req_done(req);
6432 status = vfs_offload_token_ctx_init(state->fsp->conn->sconn->client,
6433 &fruit_offload_ctx);
6434 if (tevent_req_nterror(req, status)) {
6438 status = vfs_offload_token_db_store_fsp(fruit_offload_ctx,
6441 if (tevent_req_nterror(req, status)) {
6445 tevent_req_done(req);
6449 static NTSTATUS fruit_offload_read_recv(struct tevent_req *req,
6450 struct vfs_handle_struct *handle,
6451 TALLOC_CTX *mem_ctx,
6454 struct fruit_offload_read_state *state = tevent_req_data(
6455 req, struct fruit_offload_read_state);
6458 if (tevent_req_is_nterror(req, &status)) {
6459 tevent_req_received(req);
6463 token->length = state->token.length;
6464 token->data = talloc_move(mem_ctx, &state->token.data);
6466 tevent_req_received(req);
6467 return NT_STATUS_OK;
6470 struct fruit_offload_write_state {
6471 struct vfs_handle_struct *handle;
6473 struct files_struct *src_fsp;
6474 struct files_struct *dst_fsp;
6478 static void fruit_offload_write_done(struct tevent_req *subreq);
6479 static struct tevent_req *fruit_offload_write_send(struct vfs_handle_struct *handle,
6480 TALLOC_CTX *mem_ctx,
6481 struct tevent_context *ev,
6484 off_t transfer_offset,
6485 struct files_struct *dest_fsp,
6489 struct tevent_req *req, *subreq;
6490 struct fruit_offload_write_state *state;
6492 struct fruit_config_data *config;
6493 off_t src_off = transfer_offset;
6494 files_struct *src_fsp = NULL;
6495 off_t to_copy = num;
6496 bool copyfile_enabled = false;
6498 DEBUG(10,("soff: %ju, doff: %ju, len: %ju\n",
6499 (uintmax_t)src_off, (uintmax_t)dest_off, (uintmax_t)num));
6501 SMB_VFS_HANDLE_GET_DATA(handle, config,
6502 struct fruit_config_data,
6505 req = tevent_req_create(mem_ctx, &state,
6506 struct fruit_offload_write_state);
6510 state->handle = handle;
6511 state->dst_fsp = dest_fsp;
6514 case FSCTL_SRV_COPYCHUNK:
6515 case FSCTL_SRV_COPYCHUNK_WRITE:
6516 copyfile_enabled = config->copyfile_enabled;
6523 * Check if this a OS X copyfile style copychunk request with
6524 * a requested chunk count of 0 that was translated to a
6525 * offload_write_send VFS call overloading the parameters src_off
6526 * = dest_off = num = 0.
6528 if (copyfile_enabled && num == 0 && src_off == 0 && dest_off == 0) {
6529 status = vfs_offload_token_db_fetch_fsp(
6530 fruit_offload_ctx, token, &src_fsp);
6531 if (tevent_req_nterror(req, status)) {
6532 return tevent_req_post(req, ev);
6534 state->src_fsp = src_fsp;
6536 status = vfs_stat_fsp(src_fsp);
6537 if (tevent_req_nterror(req, status)) {
6538 return tevent_req_post(req, ev);
6541 to_copy = src_fsp->fsp_name->st.st_ex_size;
6542 state->is_copyfile = true;
6545 subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle,
6554 if (tevent_req_nomem(subreq, req)) {
6555 return tevent_req_post(req, ev);
6558 tevent_req_set_callback(subreq, fruit_offload_write_done, req);
6562 static void fruit_offload_write_done(struct tevent_req *subreq)
6564 struct tevent_req *req = tevent_req_callback_data(
6565 subreq, struct tevent_req);
6566 struct fruit_offload_write_state *state = tevent_req_data(
6567 req, struct fruit_offload_write_state);
6569 unsigned int num_streams = 0;
6570 struct stream_struct *streams = NULL;
6572 struct smb_filename *src_fname_tmp = NULL;
6573 struct smb_filename *dst_fname_tmp = NULL;
6575 status = SMB_VFS_NEXT_OFFLOAD_WRITE_RECV(state->handle,
6578 TALLOC_FREE(subreq);
6579 if (tevent_req_nterror(req, status)) {
6583 if (!state->is_copyfile) {
6584 tevent_req_done(req);
6589 * Now copy all remaining streams. We know the share supports
6590 * streams, because we're in vfs_fruit. We don't do this async
6591 * because streams are few and small.
6593 status = vfs_streaminfo(state->handle->conn, state->src_fsp,
6594 state->src_fsp->fsp_name,
6595 req, &num_streams, &streams);
6596 if (tevent_req_nterror(req, status)) {
6600 if (num_streams == 1) {
6601 /* There is always one stream, ::$DATA. */
6602 tevent_req_done(req);
6606 for (i = 0; i < num_streams; i++) {
6607 DEBUG(10, ("%s: stream: '%s'/%zu\n",
6608 __func__, streams[i].name, (size_t)streams[i].size));
6610 src_fname_tmp = synthetic_smb_fname(
6612 state->src_fsp->fsp_name->base_name,
6615 state->src_fsp->fsp_name->flags);
6616 if (tevent_req_nomem(src_fname_tmp, req)) {
6620 if (is_ntfs_default_stream_smb_fname(src_fname_tmp)) {
6621 TALLOC_FREE(src_fname_tmp);
6625 dst_fname_tmp = synthetic_smb_fname(
6627 state->dst_fsp->fsp_name->base_name,
6630 state->dst_fsp->fsp_name->flags);
6631 if (tevent_req_nomem(dst_fname_tmp, req)) {
6632 TALLOC_FREE(src_fname_tmp);
6636 status = copy_file(req,
6637 state->handle->conn,
6640 OPENX_FILE_CREATE_IF_NOT_EXIST,
6642 if (!NT_STATUS_IS_OK(status)) {
6643 DEBUG(1, ("%s: copy %s to %s failed: %s\n", __func__,
6644 smb_fname_str_dbg(src_fname_tmp),
6645 smb_fname_str_dbg(dst_fname_tmp),
6646 nt_errstr(status)));
6647 TALLOC_FREE(src_fname_tmp);
6648 TALLOC_FREE(dst_fname_tmp);
6649 tevent_req_nterror(req, status);
6653 TALLOC_FREE(src_fname_tmp);
6654 TALLOC_FREE(dst_fname_tmp);
6657 TALLOC_FREE(streams);
6658 TALLOC_FREE(src_fname_tmp);
6659 TALLOC_FREE(dst_fname_tmp);
6660 tevent_req_done(req);
6663 static NTSTATUS fruit_offload_write_recv(struct vfs_handle_struct *handle,
6664 struct tevent_req *req,
6667 struct fruit_offload_write_state *state = tevent_req_data(
6668 req, struct fruit_offload_write_state);
6671 if (tevent_req_is_nterror(req, &status)) {
6672 DEBUG(1, ("server side copy chunk failed: %s\n",
6673 nt_errstr(status)));
6675 tevent_req_received(req);
6679 *copied = state->copied;
6680 tevent_req_received(req);
6682 return NT_STATUS_OK;
6685 static char *fruit_get_bandsize_line(char **lines, int numlines)
6688 static bool re_initialized = false;
6692 if (!re_initialized) {
6693 ret = regcomp(&re, "^[[:blank:]]*<key>band-size</key>$", 0);
6697 re_initialized = true;
6700 for (i = 0; i < numlines; i++) {
6701 regmatch_t matches[1];
6703 ret = regexec(&re, lines[i], 1, matches, 0);
6706 * Check if the match was on the last line, sa we want
6707 * the subsequent line.
6709 if (i + 1 == numlines) {
6712 return lines[i + 1];
6714 if (ret != REG_NOMATCH) {
6722 static bool fruit_get_bandsize_from_line(char *line, size_t *_band_size)
6725 static bool re_initialized = false;
6726 regmatch_t matches[2];
6731 if (!re_initialized) {
6734 "<integer>\\([[:digit:]]*\\)</integer>$",
6739 re_initialized = true;
6742 ret = regexec(&re, line, 2, matches, 0);
6744 DBG_ERR("regex failed [%s]\n", line);
6748 line[matches[1].rm_eo] = '\0';
6750 ok = conv_str_u64(&line[matches[1].rm_so], &band_size);
6754 *_band_size = (size_t)band_size;
6759 * This reads and parses an Info.plist from a TM sparsebundle looking for the
6760 * "band-size" key and value.
6762 static bool fruit_get_bandsize(vfs_handle_struct *handle,
6766 #define INFO_PLIST_MAX_SIZE 64*1024
6768 struct smb_filename *smb_fname = NULL;
6769 files_struct *fsp = NULL;
6770 uint8_t *file_data = NULL;
6771 char **lines = NULL;
6772 char *band_size_line = NULL;
6773 size_t plist_file_size;
6780 plist = talloc_asprintf(talloc_tos(),
6782 handle->conn->connectpath,
6784 if (plist == NULL) {
6789 smb_fname = synthetic_smb_fname(talloc_tos(), plist, NULL, NULL, 0);
6790 if (smb_fname == NULL) {
6795 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
6797 DBG_INFO("Ignoring Sparsebundle without Info.plist [%s]\n", dir);
6802 plist_file_size = smb_fname->st.st_ex_size;
6804 if (plist_file_size > INFO_PLIST_MAX_SIZE) {
6805 DBG_INFO("%s is too large, ignoring\n", plist);
6810 status = SMB_VFS_NEXT_CREATE_FILE(
6813 0, /* root_dir_fid */
6814 smb_fname, /* fname */
6815 FILE_GENERIC_READ, /* access_mask */
6816 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
6817 FILE_OPEN, /* create_disposition */
6818 0, /* create_options */
6819 0, /* file_attributes */
6820 INTERNAL_OPEN_ONLY, /* oplock_request */
6822 0, /* allocation_size */
6823 0, /* private_flags */
6828 NULL, NULL); /* create context */
6829 if (!NT_STATUS_IS_OK(status)) {
6830 DBG_INFO("Opening [%s] failed [%s]\n",
6831 smb_fname_str_dbg(smb_fname), nt_errstr(status));
6836 file_data = talloc_array(talloc_tos(), uint8_t, plist_file_size);
6837 if (file_data == NULL) {
6842 nread = SMB_VFS_NEXT_PREAD(handle, fsp, file_data, plist_file_size, 0);
6843 if (nread != plist_file_size) {
6844 DBG_ERR("Short read on [%s]: %zu/%zd\n",
6845 fsp_str_dbg(fsp), nread, plist_file_size);
6851 status = close_file(NULL, fsp, NORMAL_CLOSE);
6853 if (!NT_STATUS_IS_OK(status)) {
6854 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
6859 lines = file_lines_parse((char *)file_data,
6863 if (lines == NULL) {
6868 band_size_line = fruit_get_bandsize_line(lines, numlines);
6869 if (band_size_line == NULL) {
6870 DBG_ERR("Didn't find band-size key in [%s]\n",
6871 smb_fname_str_dbg(smb_fname));
6876 ok = fruit_get_bandsize_from_line(band_size_line, band_size);
6878 DBG_ERR("fruit_get_bandsize_from_line failed\n");
6882 DBG_DEBUG("Parsed band-size [%zu] for [%s]\n", *band_size, plist);
6886 status = close_file(NULL, fsp, NORMAL_CLOSE);
6887 if (!NT_STATUS_IS_OK(status)) {
6888 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
6893 TALLOC_FREE(smb_fname);
6894 TALLOC_FREE(file_data);
6899 struct fruit_disk_free_state {
6903 static bool fruit_get_num_bands(vfs_handle_struct *handle,
6908 struct smb_filename *bands_dir = NULL;
6910 struct dirent *e = NULL;
6914 path = talloc_asprintf(talloc_tos(),
6916 handle->conn->connectpath,
6922 bands_dir = synthetic_smb_fname(talloc_tos(),
6928 if (bands_dir == NULL) {
6932 d = SMB_VFS_NEXT_OPENDIR(handle, bands_dir, NULL, 0);
6934 TALLOC_FREE(bands_dir);
6940 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
6942 e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
6944 if (ISDOT(e->d_name) || ISDOTDOT(e->d_name)) {
6950 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
6952 TALLOC_FREE(bands_dir);
6956 DBG_DEBUG("%zu bands in [%s]\n", nbands, smb_fname_str_dbg(bands_dir));
6958 TALLOC_FREE(bands_dir);
6964 static bool fruit_tmsize_do_dirent(vfs_handle_struct *handle,
6965 struct fruit_disk_free_state *state,
6970 size_t sparsebundle_strlen = strlen("sparsebundle");
6971 size_t bandsize = 0;
6975 p = strstr(e->d_name, "sparsebundle");
6980 if (p[sparsebundle_strlen] != '\0') {
6984 DBG_DEBUG("Processing sparsebundle [%s]\n", e->d_name);
6986 ok = fruit_get_bandsize(handle, e->d_name, &bandsize);
6989 * Beware of race conditions: this may be an uninitialized
6990 * Info.plist that a client is just creating. We don't want let
6991 * this to trigger complete failure.
6993 DBG_ERR("Processing sparsebundle [%s] failed\n", e->d_name);
6997 ok = fruit_get_num_bands(handle, e->d_name, &nbands);
7000 * Beware of race conditions: this may be a backup sparsebundle
7001 * in an early stage lacking a bands subdirectory. We don't want
7002 * let this to trigger complete failure.
7004 DBG_ERR("Processing sparsebundle [%s] failed\n", e->d_name);
7008 if (bandsize > SIZE_MAX/nbands) {
7009 DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
7013 tm_size = bandsize * nbands;
7015 if (state->total_size + tm_size < state->total_size) {
7016 DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
7021 state->total_size += tm_size;
7023 DBG_DEBUG("[%s] tm_size [%jd] total_size [%jd]\n",
7024 e->d_name, (intmax_t)tm_size, (intmax_t)state->total_size);
7030 * Calculate used size of a TimeMachine volume
7032 * This assumes that the volume is used only for TimeMachine.
7034 * - readdir(basedir of share), then
7035 * - for every element that matches regex "^\(.*\)\.sparsebundle$" :
7036 * - parse "\1.sparsebundle/Info.plist" and read the band-size XML key
7037 * - count band files in "\1.sparsebundle/bands/"
7038 * - calculate used size of all bands: band_count * band_size
7040 static uint64_t fruit_disk_free(vfs_handle_struct *handle,
7041 const struct smb_filename *smb_fname,
7046 struct fruit_config_data *config = NULL;
7047 struct fruit_disk_free_state state = {0};
7049 struct dirent *e = NULL;
7055 SMB_VFS_HANDLE_GET_DATA(handle, config,
7056 struct fruit_config_data,
7059 if (!config->time_machine ||
7060 config->time_machine_max_size == 0)
7062 return SMB_VFS_NEXT_DISK_FREE(handle,
7069 d = SMB_VFS_NEXT_OPENDIR(handle, smb_fname, NULL, 0);
7074 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
7076 e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
7078 ok = fruit_tmsize_do_dirent(handle, &state, e);
7080 SMB_VFS_NEXT_CLOSEDIR(handle, d);
7085 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
7090 dsize = config->time_machine_max_size / 512;
7091 dfree = dsize - (state.total_size / 512);
7092 if (dfree > dsize) {
7102 static struct vfs_fn_pointers vfs_fruit_fns = {
7103 .connect_fn = fruit_connect,
7104 .disk_free_fn = fruit_disk_free,
7106 /* File operations */
7107 .chmod_fn = fruit_chmod,
7108 .chown_fn = fruit_chown,
7109 .unlink_fn = fruit_unlink,
7110 .rename_fn = fruit_rename,
7111 .rmdir_fn = fruit_rmdir,
7112 .open_fn = fruit_open,
7113 .close_fn = fruit_close,
7114 .pread_fn = fruit_pread,
7115 .pwrite_fn = fruit_pwrite,
7116 .pread_send_fn = fruit_pread_send,
7117 .pread_recv_fn = fruit_pread_recv,
7118 .pwrite_send_fn = fruit_pwrite_send,
7119 .pwrite_recv_fn = fruit_pwrite_recv,
7120 .stat_fn = fruit_stat,
7121 .lstat_fn = fruit_lstat,
7122 .fstat_fn = fruit_fstat,
7123 .streaminfo_fn = fruit_streaminfo,
7124 .ntimes_fn = fruit_ntimes,
7125 .ftruncate_fn = fruit_ftruncate,
7126 .fallocate_fn = fruit_fallocate,
7127 .create_file_fn = fruit_create_file,
7128 .readdir_attr_fn = fruit_readdir_attr,
7129 .offload_read_send_fn = fruit_offload_read_send,
7130 .offload_read_recv_fn = fruit_offload_read_recv,
7131 .offload_write_send_fn = fruit_offload_write_send,
7132 .offload_write_recv_fn = fruit_offload_write_recv,
7134 /* NT ACL operations */
7135 .fget_nt_acl_fn = fruit_fget_nt_acl,
7136 .fset_nt_acl_fn = fruit_fset_nt_acl,
7140 NTSTATUS vfs_fruit_init(TALLOC_CTX *ctx)
7142 NTSTATUS ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "fruit",
7144 if (!NT_STATUS_IS_OK(ret)) {
7148 vfs_fruit_debug_level = debug_add_class("fruit");
7149 if (vfs_fruit_debug_level == -1) {
7150 vfs_fruit_debug_level = DBGC_VFS;
7151 DEBUG(0, ("%s: Couldn't register custom debugging class!\n",
7154 DEBUG(10, ("%s: Debug class number of '%s': %d\n",
7155 "vfs_fruit_init","fruit",vfs_fruit_debug_level));