2 * OS X and Netatalk interoperability VFS module for Samba-3.x
4 * Copyright (C) Ralph Boehme, 2013, 2014
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
21 #include "MacExtensions.h"
22 #include "smbd/smbd.h"
23 #include "system/filesys.h"
24 #include "lib/util/time.h"
25 #include "system/shmem.h"
26 #include "locking/proto.h"
27 #include "smbd/globals.h"
29 #include "libcli/security/security.h"
30 #include "../libcli/smb/smb2_create_ctx.h"
31 #include "lib/util/sys_rw.h"
32 #include "lib/util/tevent_ntstatus.h"
33 #include "lib/util/tevent_unix.h"
34 #include "offload_token.h"
35 #include "string_replace.h"
37 #include <gnutls/gnutls.h>
38 #include <gnutls/crypto.h>
41 * Enhanced OS X and Netatalk compatibility
42 * ========================================
44 * This modules takes advantage of vfs_streams_xattr and
45 * vfs_catia. VFS modules vfs_fruit and vfs_streams_xattr must be
46 * loaded in the correct order:
48 * vfs modules = catia fruit streams_xattr
50 * The module intercepts the OS X special streams "AFP_AfpInfo" and
51 * "AFP_Resource" and handles them in a special way. All other named
52 * streams are deferred to vfs_streams_xattr.
54 * The OS X client maps all NTFS illegal characters to the Unicode
55 * private range. This module optionally stores the charcters using
56 * their native ASCII encoding using vfs_catia. If you're not enabling
57 * this feature, you can skip catia from vfs modules.
59 * Finally, open modes are optionally checked against Netatalk AFP
62 * The "AFP_AfpInfo" named stream is a binary blob containing OS X
63 * extended metadata for files and directories. This module optionally
64 * reads and stores this metadata in a way compatible with Netatalk 3
65 * which stores the metadata in an EA "org.netatalk.metadata". Cf
66 * source3/include/MacExtensions.h for a description of the binary
69 * The "AFP_Resource" named stream may be arbitrarily large, thus it
70 * can't be stored in an xattr on most filesystem. ZFS on Solaris is
71 * the only available filesystem where xattrs can be of any size and
72 * the OS supports using the file APIs for xattrs.
74 * The AFP_Resource stream is stored in an AppleDouble file prepending
75 * "._" to the filename. On Solaris with ZFS the stream is optionally
76 * stored in an EA "org.netatalk.resource".
82 * The OS X SMB client sends xattrs as ADS too. For xattr interop with
83 * other protocols you may want to adjust the xattr names the VFS
84 * module vfs_streams_xattr uses for storing ADS's. This defaults to
85 * user.DosStream.ADS_NAME:$DATA and can be changed by specifying
86 * these module parameters:
88 * streams_xattr:prefix = user.
89 * streams_xattr:store_stream_type = false
95 * - log diagnostic if any needed VFS module is not loaded
96 * (eg with lp_vfs_objects())
100 static int vfs_fruit_debug_level = DBGC_VFS;
102 static struct global_fruit_config {
103 bool nego_aapl; /* client negotiated AAPL */
105 } global_fruit_config;
108 #define DBGC_CLASS vfs_fruit_debug_level
110 #define FRUIT_PARAM_TYPE_NAME "fruit"
111 #define ADOUBLE_NAME_PREFIX "._"
113 #define NETATALK_META_XATTR "org.netatalk.Metadata"
114 #define NETATALK_RSRC_XATTR "org.netatalk.ResourceFork"
116 #if defined(HAVE_ATTROPEN)
117 #define AFPINFO_EA_NETATALK NETATALK_META_XATTR
118 #define AFPRESOURCE_EA_NETATALK NETATALK_RSRC_XATTR
120 #define AFPINFO_EA_NETATALK "user." NETATALK_META_XATTR
121 #define AFPRESOURCE_EA_NETATALK "user." NETATALK_RSRC_XATTR
124 enum apple_fork {APPLE_FORK_DATA, APPLE_FORK_RSRC};
126 enum fruit_rsrc {FRUIT_RSRC_STREAM, FRUIT_RSRC_ADFILE, FRUIT_RSRC_XATTR};
127 enum fruit_meta {FRUIT_META_STREAM, FRUIT_META_NETATALK};
128 enum fruit_locking {FRUIT_LOCKING_NETATALK, FRUIT_LOCKING_NONE};
129 enum fruit_encoding {FRUIT_ENC_NATIVE, FRUIT_ENC_PRIVATE};
131 struct fruit_config_data {
132 enum fruit_rsrc rsrc;
133 enum fruit_meta meta;
134 enum fruit_locking locking;
135 enum fruit_encoding encoding;
136 bool use_aapl; /* config from smb.conf */
138 bool readdir_attr_enabled;
139 bool unix_info_enabled;
140 bool copyfile_enabled;
141 bool veto_appledouble;
143 bool aapl_zero_file_id;
146 off_t time_machine_max_size;
147 bool wipe_intentionally_left_blank_rfork;
148 bool delete_empty_adfiles;
151 * Additional options, all enabled by default,
152 * possibly useful for analyzing performance. The associated
153 * operations with each of them may be expensive, so having
154 * the chance to disable them individually gives a chance
155 * tweaking the setup for the particular usecase.
157 bool readdir_attr_rsize;
158 bool readdir_attr_finder_info;
159 bool readdir_attr_max_access;
162 static const struct enum_list fruit_rsrc[] = {
163 {FRUIT_RSRC_STREAM, "stream"}, /* pass on to vfs_streams_xattr */
164 {FRUIT_RSRC_ADFILE, "file"}, /* ._ AppleDouble file */
165 {FRUIT_RSRC_XATTR, "xattr"}, /* Netatalk compatible xattr (ZFS only) */
169 static const struct enum_list fruit_meta[] = {
170 {FRUIT_META_STREAM, "stream"}, /* pass on to vfs_streams_xattr */
171 {FRUIT_META_NETATALK, "netatalk"}, /* Netatalk compatible xattr */
175 static const struct enum_list fruit_locking[] = {
176 {FRUIT_LOCKING_NETATALK, "netatalk"}, /* synchronize locks with Netatalk */
177 {FRUIT_LOCKING_NONE, "none"},
181 static const struct enum_list fruit_encoding[] = {
182 {FRUIT_ENC_NATIVE, "native"}, /* map unicode private chars to ASCII */
183 {FRUIT_ENC_PRIVATE, "private"}, /* keep unicode private chars */
187 static const char *fruit_catia_maps =
188 "0x01:0xf001,0x02:0xf002,0x03:0xf003,0x04:0xf004,"
189 "0x05:0xf005,0x06:0xf006,0x07:0xf007,0x08:0xf008,"
190 "0x09:0xf009,0x0a:0xf00a,0x0b:0xf00b,0x0c:0xf00c,"
191 "0x0d:0xf00d,0x0e:0xf00e,0x0f:0xf00f,0x10:0xf010,"
192 "0x11:0xf011,0x12:0xf012,0x13:0xf013,0x14:0xf014,"
193 "0x15:0xf015,0x16:0xf016,0x17:0xf017,0x18:0xf018,"
194 "0x19:0xf019,0x1a:0xf01a,0x1b:0xf01b,0x1c:0xf01c,"
195 "0x1d:0xf01d,0x1e:0xf01e,0x1f:0xf01f,"
196 "0x22:0xf020,0x2a:0xf021,0x3a:0xf022,0x3c:0xf023,"
197 "0x3e:0xf024,0x3f:0xf025,0x5c:0xf026,0x7c:0xf027,"
200 /*****************************************************************************
201 * Defines, functions and data structures that deal with AppleDouble
202 *****************************************************************************/
205 * There are two AppleDouble blobs we deal with:
207 * - ADOUBLE_META - AppleDouble blob used by Netatalk for storing
208 * metadata in an xattr
210 * - ADOUBLE_RSRC - AppleDouble blob used by OS X and Netatalk in
213 typedef enum {ADOUBLE_META, ADOUBLE_RSRC} adouble_type_t;
216 #define AD_VERSION2 0x00020000
217 #define AD_VERSION AD_VERSION2
220 * AppleDouble entry IDs.
222 #define ADEID_DFORK 1
223 #define ADEID_RFORK 2
225 #define ADEID_COMMENT 4
226 #define ADEID_ICONBW 5
227 #define ADEID_ICONCOL 6
228 #define ADEID_FILEI 7
229 #define ADEID_FILEDATESI 8
230 #define ADEID_FINDERI 9
231 #define ADEID_MACFILEI 10
232 #define ADEID_PRODOSFILEI 11
233 #define ADEID_MSDOSFILEI 12
234 #define ADEID_SHORTNAME 13
235 #define ADEID_AFPFILEI 14
238 /* Private Netatalk entries */
239 #define ADEID_PRIVDEV 16
240 #define ADEID_PRIVINO 17
241 #define ADEID_PRIVSYN 18
242 #define ADEID_PRIVID 19
243 #define ADEID_MAX (ADEID_PRIVID + 1)
246 * These are the real ids for the private entries,
247 * as stored in the adouble file
249 #define AD_DEV 0x80444556
250 #define AD_INO 0x80494E4F
251 #define AD_SYN 0x8053594E
252 #define AD_ID 0x8053567E
254 /* Number of actually used entries */
255 #define ADEID_NUM_XATTR 8
256 #define ADEID_NUM_DOT_UND 2
257 #define ADEID_NUM_RSRC_XATTR 1
259 /* AppleDouble magic */
260 #define AD_APPLESINGLE_MAGIC 0x00051600
261 #define AD_APPLEDOUBLE_MAGIC 0x00051607
262 #define AD_MAGIC AD_APPLEDOUBLE_MAGIC
264 /* Sizes of relevant entry bits */
265 #define ADEDLEN_MAGIC 4
266 #define ADEDLEN_VERSION 4
267 #define ADEDLEN_FILLER 16
268 #define AD_FILLER_TAG "Netatalk " /* should be 16 bytes */
269 #define AD_FILLER_TAG_OSX "Mac OS X " /* should be 16 bytes */
270 #define ADEDLEN_NENTRIES 2
271 #define AD_HEADER_LEN (ADEDLEN_MAGIC + ADEDLEN_VERSION + \
272 ADEDLEN_FILLER + ADEDLEN_NENTRIES) /* 26 */
273 #define AD_ENTRY_LEN_EID 4
274 #define AD_ENTRY_LEN_OFF 4
275 #define AD_ENTRY_LEN_LEN 4
276 #define AD_ENTRY_LEN (AD_ENTRY_LEN_EID + AD_ENTRY_LEN_OFF + AD_ENTRY_LEN_LEN)
279 #define ADEDLEN_NAME 255
280 #define ADEDLEN_COMMENT 200
281 #define ADEDLEN_FILEI 16
282 #define ADEDLEN_FINDERI 32
283 #define ADEDLEN_FILEDATESI 16
284 #define ADEDLEN_SHORTNAME 12 /* length up to 8.3 */
285 #define ADEDLEN_AFPFILEI 4
286 #define ADEDLEN_MACFILEI 4
287 #define ADEDLEN_PRODOSFILEI 8
288 #define ADEDLEN_MSDOSFILEI 2
289 #define ADEDLEN_DID 4
290 #define ADEDLEN_PRIVDEV 8
291 #define ADEDLEN_PRIVINO 8
292 #define ADEDLEN_PRIVSYN 8
293 #define ADEDLEN_PRIVID 4
296 #define ADEDOFF_MAGIC 0
297 #define ADEDOFF_VERSION (ADEDOFF_MAGIC + ADEDLEN_MAGIC)
298 #define ADEDOFF_FILLER (ADEDOFF_VERSION + ADEDLEN_VERSION)
299 #define ADEDOFF_NENTRIES (ADEDOFF_FILLER + ADEDLEN_FILLER)
301 #define ADEDOFF_FINDERI_XATTR (AD_HEADER_LEN + \
302 (ADEID_NUM_XATTR * AD_ENTRY_LEN))
303 #define ADEDOFF_COMMENT_XATTR (ADEDOFF_FINDERI_XATTR + ADEDLEN_FINDERI)
304 #define ADEDOFF_FILEDATESI_XATTR (ADEDOFF_COMMENT_XATTR + ADEDLEN_COMMENT)
305 #define ADEDOFF_AFPFILEI_XATTR (ADEDOFF_FILEDATESI_XATTR + \
307 #define ADEDOFF_PRIVDEV_XATTR (ADEDOFF_AFPFILEI_XATTR + ADEDLEN_AFPFILEI)
308 #define ADEDOFF_PRIVINO_XATTR (ADEDOFF_PRIVDEV_XATTR + ADEDLEN_PRIVDEV)
309 #define ADEDOFF_PRIVSYN_XATTR (ADEDOFF_PRIVINO_XATTR + ADEDLEN_PRIVINO)
310 #define ADEDOFF_PRIVID_XATTR (ADEDOFF_PRIVSYN_XATTR + ADEDLEN_PRIVSYN)
312 #define ADEDOFF_FINDERI_DOT_UND (AD_HEADER_LEN + \
313 (ADEID_NUM_DOT_UND * AD_ENTRY_LEN))
314 #define ADEDOFF_RFORK_DOT_UND (ADEDOFF_FINDERI_DOT_UND + ADEDLEN_FINDERI)
316 #define AD_DATASZ_XATTR (AD_HEADER_LEN + \
317 (ADEID_NUM_XATTR * AD_ENTRY_LEN) + \
318 ADEDLEN_FINDERI + ADEDLEN_COMMENT + \
319 ADEDLEN_FILEDATESI + ADEDLEN_AFPFILEI + \
320 ADEDLEN_PRIVDEV + ADEDLEN_PRIVINO + \
321 ADEDLEN_PRIVSYN + ADEDLEN_PRIVID)
323 #if AD_DATASZ_XATTR != 402
324 #error bad size for AD_DATASZ_XATTR
327 #define AD_DATASZ_DOT_UND (AD_HEADER_LEN + \
328 (ADEID_NUM_DOT_UND * AD_ENTRY_LEN) + \
330 #if AD_DATASZ_DOT_UND != 82
331 #error bad size for AD_DATASZ_DOT_UND
335 * Sharemode locks fcntl() offsets
337 #if _FILE_OFFSET_BITS == 64 || defined(HAVE_LARGEFILE)
338 #define AD_FILELOCK_BASE (UINT64_C(0x7FFFFFFFFFFFFFFF) - 9)
340 #define AD_FILELOCK_BASE (UINT32_C(0x7FFFFFFF) - 9)
342 #define BYTELOCK_MAX (AD_FILELOCK_BASE - 1)
344 #define AD_FILELOCK_OPEN_WR (AD_FILELOCK_BASE + 0)
345 #define AD_FILELOCK_OPEN_RD (AD_FILELOCK_BASE + 1)
346 #define AD_FILELOCK_RSRC_OPEN_WR (AD_FILELOCK_BASE + 2)
347 #define AD_FILELOCK_RSRC_OPEN_RD (AD_FILELOCK_BASE + 3)
348 #define AD_FILELOCK_DENY_WR (AD_FILELOCK_BASE + 4)
349 #define AD_FILELOCK_DENY_RD (AD_FILELOCK_BASE + 5)
350 #define AD_FILELOCK_RSRC_DENY_WR (AD_FILELOCK_BASE + 6)
351 #define AD_FILELOCK_RSRC_DENY_RD (AD_FILELOCK_BASE + 7)
352 #define AD_FILELOCK_OPEN_NONE (AD_FILELOCK_BASE + 8)
353 #define AD_FILELOCK_RSRC_OPEN_NONE (AD_FILELOCK_BASE + 9)
355 /* Time stuff we overload the bits a little */
356 #define AD_DATE_CREATE 0
357 #define AD_DATE_MODIFY 4
358 #define AD_DATE_BACKUP 8
359 #define AD_DATE_ACCESS 12
360 #define AD_DATE_MASK (AD_DATE_CREATE | AD_DATE_MODIFY | \
361 AD_DATE_BACKUP | AD_DATE_ACCESS)
362 #define AD_DATE_UNIX (1 << 10)
363 #define AD_DATE_START 0x80000000
364 #define AD_DATE_DELTA 946684800
365 #define AD_DATE_FROM_UNIX(x) (htonl((x) - AD_DATE_DELTA))
366 #define AD_DATE_TO_UNIX(x) (ntohl(x) + AD_DATE_DELTA)
368 #define AD_XATTR_HDR_MAGIC 0x41545452 /* 'ATTR' */
369 #define AD_XATTR_MAX_ENTRIES 1024 /* Some arbitrarily enforced limit */
370 #define AD_XATTR_HDR_SIZE 36
371 #define AD_XATTR_MAX_HDR_SIZE 65536
373 /* Accessor macros */
374 #define ad_getentrylen(ad,eid) ((ad)->ad_eid[(eid)].ade_len)
375 #define ad_getentryoff(ad,eid) ((ad)->ad_eid[(eid)].ade_off)
376 #define ad_setentrylen(ad,eid,len) ((ad)->ad_eid[(eid)].ade_len = (len))
377 #define ad_setentryoff(ad,eid,off) ((ad)->ad_eid[(eid)].ade_off = (off))
380 * Both struct ad_xattr_header and struct ad_xattr_entry describe the in memory
381 * representation as well as the on-disk format.
383 * The ad_xattr_header follows the FinderInfo data in the FinderInfo entry if
384 * the length of the FinderInfo entry is larger then 32 bytes. It is then
385 * preceeded with 2 bytes padding.
387 * Cf: https://opensource.apple.com/source/xnu/xnu-4570.1.46/bsd/vfs/vfs_xattr.c
390 struct ad_xattr_header {
391 uint32_t adx_magic; /* ATTR_HDR_MAGIC */
392 uint32_t adx_debug_tag; /* for debugging == file id of owning file */
393 uint32_t adx_total_size; /* file offset of end of attribute header + entries + data */
394 uint32_t adx_data_start; /* file offset to attribute data area */
395 uint32_t adx_data_length; /* length of attribute data area */
396 uint32_t adx_reserved[3];
398 uint16_t adx_num_attrs;
401 /* On-disk entries are aligned on 4 byte boundaries */
402 struct ad_xattr_entry {
403 uint32_t adx_offset; /* file offset to data */
404 uint32_t adx_length; /* size of attribute data */
406 uint8_t adx_namelen; /* included the NULL terminator */
407 char *adx_name; /* NULL-terminated UTF-8 name */
418 adouble_type_t ad_type;
421 uint8_t ad_filler[ADEDLEN_FILLER];
422 struct ad_entry ad_eid[ADEID_MAX];
424 struct ad_xattr_header adx_header;
425 struct ad_xattr_entry *adx_entries;
428 struct ad_entry_order {
429 uint32_t id, offset, len;
432 /* Netatalk AppleDouble metadata xattr */
434 struct ad_entry_order entry_order_meta_xattr[ADEID_NUM_XATTR + 1] = {
435 {ADEID_FINDERI, ADEDOFF_FINDERI_XATTR, ADEDLEN_FINDERI},
436 {ADEID_COMMENT, ADEDOFF_COMMENT_XATTR, 0},
437 {ADEID_FILEDATESI, ADEDOFF_FILEDATESI_XATTR, ADEDLEN_FILEDATESI},
438 {ADEID_AFPFILEI, ADEDOFF_AFPFILEI_XATTR, ADEDLEN_AFPFILEI},
439 {ADEID_PRIVDEV, ADEDOFF_PRIVDEV_XATTR, 0},
440 {ADEID_PRIVINO, ADEDOFF_PRIVINO_XATTR, 0},
441 {ADEID_PRIVSYN, ADEDOFF_PRIVSYN_XATTR, 0},
442 {ADEID_PRIVID, ADEDOFF_PRIVID_XATTR, 0},
446 /* AppleDouble resource fork file (the ones prefixed by "._") */
448 struct ad_entry_order entry_order_dot_und[ADEID_NUM_DOT_UND + 1] = {
449 {ADEID_FINDERI, ADEDOFF_FINDERI_DOT_UND, ADEDLEN_FINDERI},
450 {ADEID_RFORK, ADEDOFF_RFORK_DOT_UND, 0},
455 * Fake AppleDouble entry oder for resource fork xattr. The xattr
456 * isn't an AppleDouble file, it simply contains the resource data,
457 * but in order to be able to use some API calls like ad_getentryoff()
458 * we build a fake/helper struct adouble with this entry order struct.
461 struct ad_entry_order entry_order_rsrc_xattr[ADEID_NUM_RSRC_XATTR + 1] = {
466 /* Conversion from enumerated id to on-disk AppleDouble id */
467 #define AD_EID_DISK(a) (set_eid[a])
468 static const uint32_t set_eid[] = {
469 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
470 AD_DEV, AD_INO, AD_SYN, AD_ID
473 static char empty_resourcefork[] = {
474 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
475 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
476 0x54, 0x68, 0x69, 0x73, 0x20, 0x72, 0x65, 0x73,
477 0x6F, 0x75, 0x72, 0x63, 0x65, 0x20, 0x66, 0x6F,
478 0x72, 0x6B, 0x20, 0x69, 0x6E, 0x74, 0x65, 0x6E,
479 0x74, 0x69, 0x6F, 0x6E, 0x61, 0x6C, 0x6C, 0x79,
480 0x20, 0x6C, 0x65, 0x66, 0x74, 0x20, 0x62, 0x6C,
481 0x61, 0x6E, 0x6B, 0x20, 0x20, 0x20, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
506 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
507 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
508 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
509 0x00, 0x1C, 0x00, 0x1E, 0xFF, 0xFF
513 /* tcon config handle */
514 struct fruit_config_data *config;
516 /* Denote stream type, meta or rsrc */
519 /* Whether the create created the stream */
523 * AFP_AfpInfo stream created, but not written yet, thus still a fake
524 * pipe fd. This is set to true in fruit_open_meta if there was no
525 * exisiting stream but the caller requested O_CREAT. It is later set to
526 * false when we get a write on the stream that then does open and
535 * Forward declarations
537 static struct adouble *ad_init(TALLOC_CTX *ctx, vfs_handle_struct *handle,
538 adouble_type_t type);
539 static struct adouble *ad_get(TALLOC_CTX *ctx,
540 vfs_handle_struct *handle,
541 const struct smb_filename *smb_fname,
542 adouble_type_t type);
543 static int ad_set(vfs_handle_struct *handle,
545 const struct smb_filename *smb_fname);
546 static int ad_fset(struct vfs_handle_struct *handle,
549 static int adouble_path(TALLOC_CTX *ctx,
550 const struct smb_filename *smb_fname__in,
551 struct smb_filename **ppsmb_fname_out);
552 static AfpInfo *afpinfo_new(TALLOC_CTX *ctx);
553 static ssize_t afpinfo_pack(const AfpInfo *ai, char *buf);
554 static AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data);
558 * Return a pointer to an AppleDouble entry
560 * Returns NULL if the entry is not present
562 static char *ad_get_entry(const struct adouble *ad, int eid)
564 off_t off = ad_getentryoff(ad, eid);
565 size_t len = ad_getentrylen(ad, eid);
567 if (off == 0 || len == 0) {
571 return ad->ad_data + off;
577 static int ad_getdate(const struct adouble *ad,
578 unsigned int dateoff,
581 bool xlate = (dateoff & AD_DATE_UNIX);
584 dateoff &= AD_DATE_MASK;
585 p = ad_get_entry(ad, ADEID_FILEDATESI);
590 if (dateoff > AD_DATE_ACCESS) {
594 memcpy(date, p + dateoff, sizeof(uint32_t));
597 *date = AD_DATE_TO_UNIX(*date);
605 static int ad_setdate(struct adouble *ad, unsigned int dateoff, uint32_t date)
607 bool xlate = (dateoff & AD_DATE_UNIX);
610 p = ad_get_entry(ad, ADEID_FILEDATESI);
615 dateoff &= AD_DATE_MASK;
617 date = AD_DATE_FROM_UNIX(date);
620 if (dateoff > AD_DATE_ACCESS) {
624 memcpy(p + dateoff, &date, sizeof(date));
631 * Map on-disk AppleDouble id to enumerated id
633 static uint32_t get_eid(uint32_t eid)
641 return ADEID_PRIVDEV;
643 return ADEID_PRIVINO;
645 return ADEID_PRIVSYN;
656 * Pack AppleDouble structure into data buffer
658 static bool ad_pack(struct adouble *ad)
665 bufsize = talloc_get_size(ad->ad_data);
666 if (bufsize < AD_DATASZ_DOT_UND) {
667 DBG_ERR("bad buffer size [0x%" PRIx32 "]\n", bufsize);
671 if (offset + ADEDLEN_MAGIC < offset ||
672 offset + ADEDLEN_MAGIC >= bufsize) {
675 RSIVAL(ad->ad_data, offset, ad->ad_magic);
676 offset += ADEDLEN_MAGIC;
678 if (offset + ADEDLEN_VERSION < offset ||
679 offset + ADEDLEN_VERSION >= bufsize) {
682 RSIVAL(ad->ad_data, offset, ad->ad_version);
683 offset += ADEDLEN_VERSION;
685 if (offset + ADEDLEN_FILLER < offset ||
686 offset + ADEDLEN_FILLER >= bufsize) {
689 if (ad->ad_type == ADOUBLE_RSRC) {
690 memcpy(ad->ad_data + offset, AD_FILLER_TAG, ADEDLEN_FILLER);
692 offset += ADEDLEN_FILLER;
694 if (offset + ADEDLEN_NENTRIES < offset ||
695 offset + ADEDLEN_NENTRIES >= bufsize) {
698 offset += ADEDLEN_NENTRIES;
700 for (eid = 0, nent = 0; eid < ADEID_MAX; eid++) {
701 if (ad->ad_eid[eid].ade_off == 0) {
703 * ade_off is also used as indicator whether a
704 * specific entry is used or not
709 if (offset + AD_ENTRY_LEN_EID < offset ||
710 offset + AD_ENTRY_LEN_EID >= bufsize) {
713 RSIVAL(ad->ad_data, offset, AD_EID_DISK(eid));
714 offset += AD_ENTRY_LEN_EID;
716 if (offset + AD_ENTRY_LEN_OFF < offset ||
717 offset + AD_ENTRY_LEN_OFF >= bufsize) {
720 RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_off);
721 offset += AD_ENTRY_LEN_OFF;
723 if (offset + AD_ENTRY_LEN_LEN < offset ||
724 offset + AD_ENTRY_LEN_LEN >= bufsize) {
727 RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_len);
728 offset += AD_ENTRY_LEN_LEN;
733 if (ADEDOFF_NENTRIES + 2 >= bufsize) {
736 RSSVAL(ad->ad_data, ADEDOFF_NENTRIES, nent);
741 static bool ad_unpack_xattrs(struct adouble *ad)
743 struct ad_xattr_header *h = &ad->adx_header;
744 const char *p = ad->ad_data;
748 if (ad_getentrylen(ad, ADEID_FINDERI) <= ADEDLEN_FINDERI) {
752 /* 2 bytes padding */
753 hoff = ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI + 2;
755 h->adx_magic = RIVAL(p, hoff + 0);
756 h->adx_debug_tag = RIVAL(p, hoff + 4); /* Not used -> not checked */
757 h->adx_total_size = RIVAL(p, hoff + 8);
758 h->adx_data_start = RIVAL(p, hoff + 12);
759 h->adx_data_length = RIVAL(p, hoff + 16);
760 h->adx_flags = RSVAL(p, hoff + 32); /* Not used -> not checked */
761 h->adx_num_attrs = RSVAL(p, hoff + 34);
763 if (h->adx_magic != AD_XATTR_HDR_MAGIC) {
764 DBG_ERR("Bad magic: 0x%" PRIx32 "\n", h->adx_magic);
768 if (h->adx_total_size > ad_getentryoff(ad, ADEID_RFORK)) {
769 DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size);
772 if (h->adx_total_size > AD_XATTR_MAX_HDR_SIZE) {
773 DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size);
777 if (h->adx_data_start < (hoff + AD_XATTR_HDR_SIZE)) {
778 DBG_ERR("Bad start: 0x%" PRIx32 "\n", h->adx_data_start);
782 if ((h->adx_data_start + h->adx_data_length) < h->adx_data_start) {
783 DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length);
786 if ((h->adx_data_start + h->adx_data_length) >
787 ad->adx_header.adx_total_size)
789 DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length);
793 if (h->adx_num_attrs > AD_XATTR_MAX_ENTRIES) {
794 DBG_ERR("Bad num xattrs: %" PRIu16 "\n", h->adx_num_attrs);
798 if (h->adx_num_attrs == 0) {
802 ad->adx_entries = talloc_zero_array(
803 ad, struct ad_xattr_entry, h->adx_num_attrs);
804 if (ad->adx_entries == NULL) {
808 hoff += AD_XATTR_HDR_SIZE;
810 for (i = 0; i < h->adx_num_attrs; i++) {
811 struct ad_xattr_entry *e = &ad->adx_entries[i];
813 hoff = (hoff + 3) & ~3;
815 e->adx_offset = RIVAL(p, hoff + 0);
816 e->adx_length = RIVAL(p, hoff + 4);
817 e->adx_flags = RSVAL(p, hoff + 8);
818 e->adx_namelen = *(p + hoff + 10);
820 if (e->adx_offset >= ad->adx_header.adx_total_size) {
821 DBG_ERR("Bad adx_offset: %" PRIx32 "\n",
826 if ((e->adx_offset + e->adx_length) < e->adx_offset) {
827 DBG_ERR("Bad adx_length: %" PRIx32 "\n",
832 if ((e->adx_offset + e->adx_length) >
833 ad->adx_header.adx_total_size)
835 DBG_ERR("Bad adx_length: %" PRIx32 "\n",
840 if (e->adx_namelen == 0) {
841 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
845 if ((hoff + 11 + e->adx_namelen) < hoff + 11) {
846 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
850 if ((hoff + 11 + e->adx_namelen) >
851 ad->adx_header.adx_data_start)
853 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
858 e->adx_name = talloc_strndup(ad->adx_entries,
861 if (e->adx_name == NULL) {
865 DBG_DEBUG("xattr [%s] offset [0x%x] size [0x%x]\n",
866 e->adx_name, e->adx_offset, e->adx_length);
867 dump_data(10, (uint8_t *)(ad->ad_data + e->adx_offset),
870 hoff += 11 + e->adx_namelen;
877 * Unpack an AppleDouble blob into a struct adoble
879 static bool ad_unpack(struct adouble *ad, const size_t nentries,
882 size_t bufsize = talloc_get_size(ad->ad_data);
884 uint32_t eid, len, off;
888 * The size of the buffer ad->ad_data is checked when read, so
889 * we wouldn't have to check our own offsets, a few extra
890 * checks won't hurt though. We have to check the offsets we
891 * read from the buffer anyway.
894 if (bufsize < (AD_HEADER_LEN + (AD_ENTRY_LEN * nentries))) {
895 DEBUG(1, ("bad size\n"));
899 ad->ad_magic = RIVAL(ad->ad_data, 0);
900 ad->ad_version = RIVAL(ad->ad_data, ADEDOFF_VERSION);
901 if ((ad->ad_magic != AD_MAGIC) || (ad->ad_version != AD_VERSION)) {
902 DEBUG(1, ("wrong magic or version\n"));
906 memcpy(ad->ad_filler, ad->ad_data + ADEDOFF_FILLER, ADEDLEN_FILLER);
908 adentries = RSVAL(ad->ad_data, ADEDOFF_NENTRIES);
909 if (adentries != nentries) {
910 DEBUG(1, ("invalid number of entries: %zu\n",
915 /* now, read in the entry bits */
916 for (i = 0; i < adentries; i++) {
917 eid = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN));
919 off = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 4);
920 len = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 8);
922 if (!eid || eid >= ADEID_MAX) {
923 DEBUG(1, ("bogus eid %d\n", eid));
928 * All entries other than the resource fork are
929 * expected to be read into the ad_data buffer, so
930 * ensure the specified offset is within that bound
932 if ((off > bufsize) && (eid != ADEID_RFORK)) {
933 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
939 * All entries besides FinderInfo and resource fork
940 * must fit into the buffer. FinderInfo is special as
941 * it may be larger then the default 32 bytes (if it
942 * contains marshalled xattrs), but we will fixup that
943 * in ad_convert(). And the resource fork is never
944 * accessed directly by the ad_data buf (also see
945 * comment above) anyway.
947 if ((eid != ADEID_RFORK) &&
948 (eid != ADEID_FINDERI) &&
949 ((off + len) > bufsize)) {
950 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
956 * That would be obviously broken
958 if (off > filesize) {
959 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
965 * Check for any entry that has its end beyond the
968 if (off + len < off) {
969 DEBUG(1, ("offset wrap in eid %d: off: %" PRIu32
970 ", len: %" PRIu32 "\n",
975 if (off + len > filesize) {
977 * If this is the resource fork entry, we fix
978 * up the length, for any other entry we bail
981 if (eid != ADEID_RFORK) {
982 DEBUG(1, ("bogus eid %d: off: %" PRIu32
983 ", len: %" PRIu32 "\n",
989 * Fixup the resource fork entry by limiting
990 * the size to entryoffset - filesize.
992 len = filesize - off;
993 DEBUG(1, ("Limiting ADEID_RFORK: off: %" PRIu32
994 ", len: %" PRIu32 "\n", off, len));
997 ad->ad_eid[eid].ade_off = off;
998 ad->ad_eid[eid].ade_len = len;
1001 ok = ad_unpack_xattrs(ad);
1009 static bool ad_convert_move_reso(struct adouble *ad,
1010 const struct smb_filename *smb_fname)
1012 char *map = MAP_FAILED;
1018 if (ad_getentrylen(ad, ADEID_RFORK) == 0) {
1022 maplen = ad_getentryoff(ad, ADEID_RFORK) +
1023 ad_getentrylen(ad, ADEID_RFORK);
1025 /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
1026 map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
1028 if (map == MAP_FAILED) {
1029 DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
1034 memmove(map + ADEDOFF_RFORK_DOT_UND,
1035 map + ad_getentryoff(ad, ADEID_RFORK),
1036 ad_getentrylen(ad, ADEID_RFORK));
1038 rc = munmap(map, maplen);
1040 DBG_ERR("munmap failed: %s\n", strerror(errno));
1044 ad_setentryoff(ad, ADEID_RFORK, ADEDOFF_RFORK_DOT_UND);
1048 DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name);
1052 len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
1053 if (len != AD_DATASZ_DOT_UND) {
1054 DBG_ERR("%s: bad size: %zd\n", smb_fname->base_name, len);
1061 static bool ad_convert_xattr(vfs_handle_struct *handle,
1063 const struct smb_filename *smb_fname,
1064 bool *converted_xattr)
1066 static struct char_mappings **string_replace_cmaps = NULL;
1067 char *map = MAP_FAILED;
1071 int saved_errno = 0;
1076 *converted_xattr = false;
1078 if (ad_getentrylen(ad, ADEID_FINDERI) == ADEDLEN_FINDERI) {
1082 if (string_replace_cmaps == NULL) {
1083 const char **mappings = NULL;
1085 mappings = str_list_make_v3_const(
1086 talloc_tos(), fruit_catia_maps, NULL);
1087 if (mappings == NULL) {
1090 string_replace_cmaps = string_replace_init_map(mappings);
1091 TALLOC_FREE(mappings);
1094 maplen = ad_getentryoff(ad, ADEID_RFORK) +
1095 ad_getentrylen(ad, ADEID_RFORK);
1097 /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
1098 map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
1100 if (map == MAP_FAILED) {
1101 DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
1105 for (i = 0; i < ad->adx_header.adx_num_attrs; i++) {
1106 struct ad_xattr_entry *e = &ad->adx_entries[i];
1107 char *mapped_name = NULL;
1109 struct smb_filename *stream_name = NULL;
1110 files_struct *fsp = NULL;
1113 status = string_replace_allocate(handle->conn,
1115 string_replace_cmaps,
1118 vfs_translate_to_windows);
1119 if (!NT_STATUS_IS_OK(status) &&
1120 !NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))
1122 DBG_ERR("string_replace_allocate failed\n");
1128 mapped_name = talloc_asprintf(talloc_tos(), ":%s", tmp);
1130 if (mapped_name == NULL) {
1135 stream_name = synthetic_smb_fname(talloc_tos(),
1136 smb_fname->base_name,
1140 TALLOC_FREE(mapped_name);
1141 if (stream_name == NULL) {
1142 DBG_ERR("synthetic_smb_fname failed\n");
1147 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1149 status = SMB_VFS_CREATE_FILE(
1150 handle->conn, /* conn */
1152 0, /* root_dir_fid */
1153 stream_name, /* fname */
1154 FILE_GENERIC_WRITE, /* access_mask */
1155 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
1156 FILE_OPEN_IF, /* create_disposition */
1157 0, /* create_options */
1158 0, /* file_attributes */
1159 INTERNAL_OPEN_ONLY, /* oplock_request */
1161 0, /* allocation_size */
1162 0, /* private_flags */
1167 NULL, NULL); /* create context */
1168 TALLOC_FREE(stream_name);
1169 if (!NT_STATUS_IS_OK(status)) {
1170 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1175 nwritten = SMB_VFS_PWRITE(fsp,
1176 map + e->adx_offset,
1179 if (nwritten == -1) {
1180 DBG_ERR("SMB_VFS_PWRITE failed\n");
1181 saved_errno = errno;
1182 close_file(NULL, fsp, ERROR_CLOSE);
1183 errno = saved_errno;
1188 status = close_file(NULL, fsp, NORMAL_CLOSE);
1189 if (!NT_STATUS_IS_OK(status)) {
1196 ad_setentrylen(ad, ADEID_FINDERI, ADEDLEN_FINDERI);
1200 DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name);
1204 len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
1205 if (len != AD_DATASZ_DOT_UND) {
1206 DBG_ERR("%s: bad size: %zd\n", smb_fname->base_name, len);
1211 ok = ad_convert_move_reso(ad, smb_fname);
1216 *converted_xattr = true;
1220 rc = munmap(map, maplen);
1222 DBG_ERR("munmap failed: %s\n", strerror(errno));
1229 static bool ad_convert_finderinfo(vfs_handle_struct *handle,
1231 const struct smb_filename *smb_fname)
1236 struct smb_filename *stream_name = NULL;
1237 files_struct *fsp = NULL;
1241 int saved_errno = 0;
1244 cmp = memcmp(ad->ad_filler, AD_FILLER_TAG_OSX, ADEDLEN_FILLER);
1249 p_ad = ad_get_entry(ad, ADEID_FINDERI);
1254 ai = afpinfo_new(talloc_tos());
1259 memcpy(ai->afpi_FinderInfo, p_ad, ADEDLEN_FINDERI);
1261 aiblob = data_blob_talloc(talloc_tos(), NULL, AFP_INFO_SIZE);
1262 if (aiblob.data == NULL) {
1267 size = afpinfo_pack(ai, (char *)aiblob.data);
1269 if (size != AFP_INFO_SIZE) {
1273 stream_name = synthetic_smb_fname(talloc_tos(),
1274 smb_fname->base_name,
1278 if (stream_name == NULL) {
1279 data_blob_free(&aiblob);
1280 DBG_ERR("synthetic_smb_fname failed\n");
1284 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1286 status = SMB_VFS_CREATE_FILE(
1287 handle->conn, /* conn */
1289 0, /* root_dir_fid */
1290 stream_name, /* fname */
1291 FILE_GENERIC_WRITE, /* access_mask */
1292 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
1293 FILE_OPEN_IF, /* create_disposition */
1294 0, /* create_options */
1295 0, /* file_attributes */
1296 INTERNAL_OPEN_ONLY, /* oplock_request */
1298 0, /* allocation_size */
1299 0, /* private_flags */
1304 NULL, NULL); /* create context */
1305 TALLOC_FREE(stream_name);
1306 if (!NT_STATUS_IS_OK(status)) {
1307 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1311 nwritten = SMB_VFS_PWRITE(fsp,
1315 if (nwritten == -1) {
1316 DBG_ERR("SMB_VFS_PWRITE failed\n");
1317 saved_errno = errno;
1318 close_file(NULL, fsp, ERROR_CLOSE);
1319 errno = saved_errno;
1323 status = close_file(NULL, fsp, NORMAL_CLOSE);
1324 if (!NT_STATUS_IS_OK(status)) {
1332 static bool ad_convert_truncate(struct adouble *ad,
1333 const struct smb_filename *smb_fname)
1338 * FIXME: direct ftruncate(), but we don't have a fsp for the
1341 rc = ftruncate(ad->ad_fd, ADEDOFF_RFORK_DOT_UND +
1342 ad_getentrylen(ad, ADEID_RFORK));
1350 static bool ad_convert_blank_rfork(vfs_handle_struct *handle,
1354 struct fruit_config_data *config = NULL;
1355 uint8_t *map = MAP_FAILED;
1364 SMB_VFS_HANDLE_GET_DATA(handle, config,
1365 struct fruit_config_data, return false);
1367 if (!config->wipe_intentionally_left_blank_rfork) {
1371 if (ad_getentrylen(ad, ADEID_RFORK) != sizeof(empty_resourcefork)) {
1375 maplen = ad_getentryoff(ad, ADEID_RFORK) +
1376 ad_getentrylen(ad, ADEID_RFORK);
1378 /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
1379 map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
1381 if (map == MAP_FAILED) {
1382 DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
1386 cmp = memcmp(map + ADEDOFF_RFORK_DOT_UND,
1388 sizeof(empty_resourcefork));
1389 rc = munmap(map, maplen);
1391 DBG_ERR("munmap failed: %s\n", strerror(errno));
1399 ad_setentrylen(ad, ADEID_RFORK, 0);
1406 len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
1407 if (len != AD_DATASZ_DOT_UND) {
1415 static bool ad_convert_delete_adfile(vfs_handle_struct *handle,
1417 const struct smb_filename *smb_fname)
1419 struct fruit_config_data *config = NULL;
1420 struct smb_filename *ad_name = NULL;
1423 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
1427 SMB_VFS_HANDLE_GET_DATA(handle, config,
1428 struct fruit_config_data, return false);
1430 if (!config->delete_empty_adfiles) {
1434 rc = adouble_path(talloc_tos(), smb_fname, &ad_name);
1439 rc = SMB_VFS_NEXT_UNLINK(handle, ad_name);
1441 DBG_ERR("Unlinking [%s] failed: %s\n",
1442 smb_fname_str_dbg(ad_name), strerror(errno));
1443 TALLOC_FREE(ad_name);
1447 DBG_WARNING("Unlinked [%s] after conversion\n", smb_fname_str_dbg(ad_name));
1448 TALLOC_FREE(ad_name);
1454 * Convert from Apple's ._ file to Netatalk
1456 * Apple's AppleDouble may contain a FinderInfo entry longer then 32
1457 * bytes containing packed xattrs.
1459 * @return -1 in case an error occurred, 0 if no conversion was done, 1
1462 static int ad_convert(struct vfs_handle_struct *handle,
1463 const struct smb_filename *smb_fname)
1465 struct adouble *ad = NULL;
1467 bool converted_xattr = false;
1471 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
1476 ok = ad_convert_xattr(handle, ad, smb_fname, &converted_xattr);
1482 ok = ad_convert_blank_rfork(handle, ad, &blank);
1488 if (converted_xattr || blank) {
1489 ok = ad_convert_truncate(ad, smb_fname);
1496 ok = ad_convert_finderinfo(handle, ad, smb_fname);
1498 DBG_ERR("Failed to convert [%s]\n",
1499 smb_fname_str_dbg(smb_fname));
1504 ok = ad_convert_delete_adfile(handle, ad, smb_fname);
1517 * Read and parse Netatalk AppleDouble metadata xattr
1519 static ssize_t ad_read_meta(vfs_handle_struct *handle,
1521 const struct smb_filename *smb_fname)
1527 DEBUG(10, ("reading meta xattr for %s\n", smb_fname->base_name));
1529 ealen = SMB_VFS_GETXATTR(handle->conn, smb_fname,
1530 AFPINFO_EA_NETATALK, ad->ad_data,
1536 if (errno == ENOATTR) {
1542 DEBUG(2, ("error reading meta xattr: %s\n",
1548 if (ealen != AD_DATASZ_XATTR) {
1549 DEBUG(2, ("bad size %zd\n", ealen));
1555 /* Now parse entries */
1556 ok = ad_unpack(ad, ADEID_NUM_XATTR, AD_DATASZ_XATTR);
1558 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1564 if (!ad_getentryoff(ad, ADEID_FINDERI)
1565 || !ad_getentryoff(ad, ADEID_COMMENT)
1566 || !ad_getentryoff(ad, ADEID_FILEDATESI)
1567 || !ad_getentryoff(ad, ADEID_AFPFILEI)
1568 || !ad_getentryoff(ad, ADEID_PRIVDEV)
1569 || !ad_getentryoff(ad, ADEID_PRIVINO)
1570 || !ad_getentryoff(ad, ADEID_PRIVSYN)
1571 || !ad_getentryoff(ad, ADEID_PRIVID)) {
1572 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1579 DEBUG(10, ("reading meta xattr for %s, rc: %d\n",
1580 smb_fname->base_name, rc));
1584 if (errno == EINVAL) {
1586 (void)SMB_VFS_REMOVEXATTR(handle->conn,
1588 AFPINFO_EA_NETATALK);
1596 static int ad_open_rsrc_xattr(const struct smb_filename *smb_fname,
1600 #ifdef HAVE_ATTROPEN
1601 /* FIXME: direct Solaris xattr syscall */
1602 return attropen(smb_fname->base_name,
1603 AFPRESOURCE_EA_NETATALK, flags, mode);
1610 static int ad_open_rsrc_adouble(const struct smb_filename *smb_fname,
1616 struct smb_filename *adp_smb_fname = NULL;
1618 ret = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
1623 fd = open(adp_smb_fname->base_name, flags, mode);
1624 TALLOC_FREE(adp_smb_fname);
1629 static int ad_open_rsrc(vfs_handle_struct *handle,
1630 const struct smb_filename *smb_fname,
1634 struct fruit_config_data *config = NULL;
1637 SMB_VFS_HANDLE_GET_DATA(handle, config,
1638 struct fruit_config_data, return -1);
1640 if (config->rsrc == FRUIT_RSRC_XATTR) {
1641 fd = ad_open_rsrc_xattr(smb_fname, flags, mode);
1643 fd = ad_open_rsrc_adouble(smb_fname, flags, mode);
1650 * Here's the deal: for ADOUBLE_META we can do without an fd as we can issue
1651 * path based xattr calls. For ADOUBLE_RSRC however we need a full-fledged fd
1652 * for file IO on the ._ file.
1654 static int ad_open(vfs_handle_struct *handle,
1657 const struct smb_filename *smb_fname,
1663 DBG_DEBUG("Path [%s] type [%s]\n", smb_fname->base_name,
1664 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
1666 if (ad->ad_type == ADOUBLE_META) {
1670 if ((fsp != NULL) && (fsp->fh != NULL) && (fsp->fh->fd != -1)) {
1671 ad->ad_fd = fsp->fh->fd;
1672 ad->ad_opened = false;
1676 fd = ad_open_rsrc(handle, smb_fname, flags, mode);
1680 ad->ad_opened = true;
1683 DBG_DEBUG("Path [%s] type [%s] fd [%d]\n",
1684 smb_fname->base_name,
1685 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc", fd);
1690 static ssize_t ad_read_rsrc_xattr(vfs_handle_struct *handle,
1696 /* FIXME: direct sys_fstat(), don't have an fsp */
1697 ret = sys_fstat(ad->ad_fd, &st,
1698 lp_fake_directory_create_times(
1699 SNUM(handle->conn)));
1704 ad_setentrylen(ad, ADEID_RFORK, st.st_ex_size);
1705 return st.st_ex_size;
1708 static ssize_t ad_read_rsrc_adouble(vfs_handle_struct *handle,
1710 const struct smb_filename *smb_fname)
1712 SMB_STRUCT_STAT sbuf;
1719 ret = sys_fstat(ad->ad_fd, &sbuf, lp_fake_directory_create_times(
1720 SNUM(handle->conn)));
1726 * AppleDouble file header content and size, two cases:
1728 * - without xattrs it is exactly AD_DATASZ_DOT_UND (82) bytes large
1729 * - with embedded xattrs it can be larger, up to AD_XATTR_MAX_HDR_SIZE
1731 * Read as much as we can up to AD_XATTR_MAX_HDR_SIZE.
1733 size = sbuf.st_ex_size;
1734 if (size > talloc_array_length(ad->ad_data)) {
1735 if (size > AD_XATTR_MAX_HDR_SIZE) {
1736 size = AD_XATTR_MAX_HDR_SIZE;
1738 p_ad = talloc_realloc(ad, ad->ad_data, char, size);
1745 len = sys_pread(ad->ad_fd, ad->ad_data,
1746 talloc_array_length(ad->ad_data), 0);
1747 if (len != talloc_array_length(ad->ad_data)) {
1748 DBG_NOTICE("%s %s: bad size: %zd\n",
1749 smb_fname->base_name, strerror(errno), len);
1753 /* Now parse entries */
1754 ok = ad_unpack(ad, ADEID_NUM_DOT_UND, sbuf.st_ex_size);
1756 DBG_ERR("invalid AppleDouble resource %s\n",
1757 smb_fname->base_name);
1762 if ((ad_getentryoff(ad, ADEID_FINDERI) != ADEDOFF_FINDERI_DOT_UND)
1763 || (ad_getentrylen(ad, ADEID_FINDERI) < ADEDLEN_FINDERI)
1764 || (ad_getentryoff(ad, ADEID_RFORK) < ADEDOFF_RFORK_DOT_UND)) {
1765 DBG_ERR("invalid AppleDouble resource %s\n",
1766 smb_fname->base_name);
1775 * Read and parse resource fork, either ._ AppleDouble file or xattr
1777 static ssize_t ad_read_rsrc(vfs_handle_struct *handle,
1779 const struct smb_filename *smb_fname)
1781 struct fruit_config_data *config = NULL;
1784 SMB_VFS_HANDLE_GET_DATA(handle, config,
1785 struct fruit_config_data, return -1);
1787 if (config->rsrc == FRUIT_RSRC_XATTR) {
1788 len = ad_read_rsrc_xattr(handle, ad);
1790 len = ad_read_rsrc_adouble(handle, ad, smb_fname);
1797 * Read and unpack an AppleDouble metadata xattr or resource
1799 static ssize_t ad_read(vfs_handle_struct *handle,
1801 const struct smb_filename *smb_fname)
1803 switch (ad->ad_type) {
1805 return ad_read_meta(handle, ad, smb_fname);
1807 return ad_read_rsrc(handle, ad, smb_fname);
1813 static int adouble_destructor(struct adouble *ad)
1815 if ((ad->ad_fd != -1) && ad->ad_opened) {
1823 * Allocate a struct adouble without initialiing it
1825 * The struct is either hang of the fsp extension context or if fsp is
1828 * @param[in] ctx talloc context
1829 * @param[in] handle vfs handle
1830 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1832 * @return adouble handle
1834 static struct adouble *ad_alloc(TALLOC_CTX *ctx, vfs_handle_struct *handle,
1835 adouble_type_t type)
1840 struct fruit_config_data *config;
1842 SMB_VFS_HANDLE_GET_DATA(handle, config,
1843 struct fruit_config_data, return NULL);
1847 adsize = AD_DATASZ_XATTR;
1850 if (config->rsrc == FRUIT_RSRC_ADFILE) {
1851 adsize = AD_DATASZ_DOT_UND;
1858 ad = talloc_zero(ctx, struct adouble);
1865 ad->ad_data = talloc_zero_array(ad, char, adsize);
1866 if (ad->ad_data == NULL) {
1873 ad->ad_magic = AD_MAGIC;
1874 ad->ad_version = AD_VERSION;
1877 talloc_set_destructor(ad, adouble_destructor);
1887 * Allocate and initialize a new struct adouble
1889 * @param[in] ctx talloc context
1890 * @param[in] handle vfs handle
1891 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1893 * @return adouble handle, initialized
1895 static struct adouble *ad_init(TALLOC_CTX *ctx, vfs_handle_struct *handle,
1896 adouble_type_t type)
1899 const struct ad_entry_order *eid;
1900 struct adouble *ad = NULL;
1901 struct fruit_config_data *config;
1902 time_t t = time(NULL);
1904 SMB_VFS_HANDLE_GET_DATA(handle, config,
1905 struct fruit_config_data, return NULL);
1909 eid = entry_order_meta_xattr;
1912 if (config->rsrc == FRUIT_RSRC_ADFILE) {
1913 eid = entry_order_dot_und;
1915 eid = entry_order_rsrc_xattr;
1922 ad = ad_alloc(ctx, handle, type);
1928 ad->ad_eid[eid->id].ade_off = eid->offset;
1929 ad->ad_eid[eid->id].ade_len = eid->len;
1933 /* put something sane in the date fields */
1934 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, t);
1935 ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, t);
1936 ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, t);
1937 ad_setdate(ad, AD_DATE_BACKUP, htonl(AD_DATE_START));
1945 static struct adouble *ad_get_internal(TALLOC_CTX *ctx,
1946 vfs_handle_struct *handle,
1948 const struct smb_filename *smb_fname,
1949 adouble_type_t type)
1953 struct adouble *ad = NULL;
1957 smb_fname = fsp->base_fsp->fsp_name;
1960 DEBUG(10, ("ad_get(%s) called for %s\n",
1961 type == ADOUBLE_META ? "meta" : "rsrc",
1962 smb_fname->base_name));
1964 ad = ad_alloc(ctx, handle, type);
1970 /* Try rw first so we can use the fd in ad_convert() */
1973 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
1974 if (rc == -1 && ((errno == EROFS) || (errno == EACCES))) {
1976 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
1979 DBG_DEBUG("ad_open [%s] error [%s]\n",
1980 smb_fname->base_name, strerror(errno));
1985 len = ad_read(handle, ad, smb_fname);
1987 DEBUG(10, ("error reading AppleDouble for %s\n",
1988 smb_fname->base_name));
1994 DEBUG(10, ("ad_get(%s) for %s returning %d\n",
1995 type == ADOUBLE_META ? "meta" : "rsrc",
1996 smb_fname->base_name, rc));
2005 * Return AppleDouble data for a file
2007 * @param[in] ctx talloc context
2008 * @param[in] handle vfs handle
2009 * @param[in] smb_fname pathname to file or directory
2010 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2012 * @return talloced struct adouble or NULL on error
2014 static struct adouble *ad_get(TALLOC_CTX *ctx,
2015 vfs_handle_struct *handle,
2016 const struct smb_filename *smb_fname,
2017 adouble_type_t type)
2019 return ad_get_internal(ctx, handle, NULL, smb_fname, type);
2023 * Return AppleDouble data for a file
2025 * @param[in] ctx talloc context
2026 * @param[in] handle vfs handle
2027 * @param[in] fsp fsp to use for IO
2028 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2030 * @return talloced struct adouble or NULL on error
2032 static struct adouble *ad_fget(TALLOC_CTX *ctx, vfs_handle_struct *handle,
2033 files_struct *fsp, adouble_type_t type)
2035 return ad_get_internal(ctx, handle, fsp, NULL, type);
2039 * Set AppleDouble metadata on a file or directory
2041 * @param[in] ad adouble handle
2043 * @param[in] smb_fname pathname to file or directory
2045 * @return status code, 0 means success
2047 static int ad_set(vfs_handle_struct *handle,
2049 const struct smb_filename *smb_fname)
2054 DBG_DEBUG("Path [%s]\n", smb_fname->base_name);
2056 if (ad->ad_type != ADOUBLE_META) {
2057 DBG_ERR("ad_set on [%s] used with ADOUBLE_RSRC\n",
2058 smb_fname->base_name);
2067 ret = SMB_VFS_SETXATTR(handle->conn,
2069 AFPINFO_EA_NETATALK,
2071 AD_DATASZ_XATTR, 0);
2073 DBG_DEBUG("Path [%s] ret [%d]\n", smb_fname->base_name, ret);
2079 * Set AppleDouble metadata on a file or directory
2081 * @param[in] ad adouble handle
2082 * @param[in] fsp file handle
2084 * @return status code, 0 means success
2086 static int ad_fset(struct vfs_handle_struct *handle,
2094 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
2097 || (fsp->fh == NULL)
2098 || (fsp->fh->fd == -1))
2100 smb_panic("bad fsp");
2108 switch (ad->ad_type) {
2110 rc = SMB_VFS_NEXT_SETXATTR(handle,
2112 AFPINFO_EA_NETATALK,
2114 AD_DATASZ_XATTR, 0);
2118 len = SMB_VFS_NEXT_PWRITE(handle,
2123 if (len != AD_DATASZ_DOT_UND) {
2124 DBG_ERR("short write on %s: %zd", fsp_str_dbg(fsp), len);
2134 DBG_DEBUG("Path [%s] rc [%d]\n", fsp_str_dbg(fsp), rc);
2139 /*****************************************************************************
2141 *****************************************************************************/
2143 static bool is_afpinfo_stream(const struct smb_filename *smb_fname)
2145 if (strncasecmp_m(smb_fname->stream_name,
2146 AFPINFO_STREAM_NAME,
2147 strlen(AFPINFO_STREAM_NAME)) == 0) {
2153 static bool is_afpresource_stream(const struct smb_filename *smb_fname)
2155 if (strncasecmp_m(smb_fname->stream_name,
2156 AFPRESOURCE_STREAM_NAME,
2157 strlen(AFPRESOURCE_STREAM_NAME)) == 0) {
2164 * Test whether stream is an Apple stream.
2166 static bool is_apple_stream(const struct smb_filename *smb_fname)
2168 if (is_afpinfo_stream(smb_fname)) {
2171 if (is_afpresource_stream(smb_fname)) {
2177 static bool is_adouble_file(const char *path)
2179 const char *p = NULL;
2182 p = strrchr(path, '/');
2190 ADOUBLE_NAME_PREFIX,
2191 strlen(ADOUBLE_NAME_PREFIX));
2199 * Initialize config struct from our smb.conf config parameters
2201 static int init_fruit_config(vfs_handle_struct *handle)
2203 struct fruit_config_data *config;
2205 const char *tm_size_str = NULL;
2207 config = talloc_zero(handle->conn, struct fruit_config_data);
2209 DEBUG(1, ("talloc_zero() failed\n"));
2215 * Versions up to Samba 4.5.x had a spelling bug in the
2216 * fruit:resource option calling lp_parm_enum with
2217 * "res*s*ource" (ie two s).
2219 * In Samba 4.6 we accept both the wrong and the correct
2220 * spelling, in Samba 4.7 the bad spelling will be removed.
2222 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2223 "ressource", fruit_rsrc, FRUIT_RSRC_ADFILE);
2224 if (enumval == -1) {
2225 DEBUG(1, ("value for %s: resource type unknown\n",
2226 FRUIT_PARAM_TYPE_NAME));
2229 config->rsrc = (enum fruit_rsrc)enumval;
2231 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2232 "resource", fruit_rsrc, enumval);
2233 if (enumval == -1) {
2234 DEBUG(1, ("value for %s: resource type unknown\n",
2235 FRUIT_PARAM_TYPE_NAME));
2238 config->rsrc = (enum fruit_rsrc)enumval;
2240 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2241 "metadata", fruit_meta, FRUIT_META_NETATALK);
2242 if (enumval == -1) {
2243 DEBUG(1, ("value for %s: metadata type unknown\n",
2244 FRUIT_PARAM_TYPE_NAME));
2247 config->meta = (enum fruit_meta)enumval;
2249 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2250 "locking", fruit_locking, FRUIT_LOCKING_NONE);
2251 if (enumval == -1) {
2252 DEBUG(1, ("value for %s: locking type unknown\n",
2253 FRUIT_PARAM_TYPE_NAME));
2256 config->locking = (enum fruit_locking)enumval;
2258 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2259 "encoding", fruit_encoding, FRUIT_ENC_PRIVATE);
2260 if (enumval == -1) {
2261 DEBUG(1, ("value for %s: encoding type unknown\n",
2262 FRUIT_PARAM_TYPE_NAME));
2265 config->encoding = (enum fruit_encoding)enumval;
2267 if (config->rsrc == FRUIT_RSRC_ADFILE) {
2268 config->veto_appledouble = lp_parm_bool(SNUM(handle->conn),
2269 FRUIT_PARAM_TYPE_NAME,
2274 config->use_aapl = lp_parm_bool(
2275 -1, FRUIT_PARAM_TYPE_NAME, "aapl", true);
2277 config->time_machine = lp_parm_bool(
2278 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "time machine", false);
2280 config->unix_info_enabled = lp_parm_bool(
2281 -1, FRUIT_PARAM_TYPE_NAME, "nfs_aces", true);
2283 config->use_copyfile = lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME,
2286 config->posix_rename = lp_parm_bool(
2287 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "posix_rename", true);
2289 config->aapl_zero_file_id =
2290 lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME, "zero_file_id", true);
2292 config->readdir_attr_rsize = lp_parm_bool(
2293 SNUM(handle->conn), "readdir_attr", "aapl_rsize", true);
2295 config->readdir_attr_finder_info = lp_parm_bool(
2296 SNUM(handle->conn), "readdir_attr", "aapl_finder_info", true);
2298 config->readdir_attr_max_access = lp_parm_bool(
2299 SNUM(handle->conn), "readdir_attr", "aapl_max_access", true);
2301 config->model = lp_parm_const_string(
2302 -1, FRUIT_PARAM_TYPE_NAME, "model", "MacSamba");
2304 tm_size_str = lp_parm_const_string(
2305 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2306 "time machine max size", NULL);
2307 if (tm_size_str != NULL) {
2308 config->time_machine_max_size = conv_str_size(tm_size_str);
2311 config->wipe_intentionally_left_blank_rfork = lp_parm_bool(
2312 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2313 "wipe_intentionally_left_blank_rfork", false);
2315 config->delete_empty_adfiles = lp_parm_bool(
2316 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2317 "delete_empty_adfiles", false);
2319 SMB_VFS_HANDLE_SET_DATA(handle, config,
2320 NULL, struct fruit_config_data,
2327 * Prepend "._" to a basename
2328 * Return a new struct smb_filename with stream_name == NULL.
2330 static int adouble_path(TALLOC_CTX *ctx,
2331 const struct smb_filename *smb_fname_in,
2332 struct smb_filename **pp_smb_fname_out)
2336 struct smb_filename *smb_fname = cp_smb_filename(ctx,
2339 if (smb_fname == NULL) {
2343 /* We need streamname to be NULL */
2344 TALLOC_FREE(smb_fname->stream_name);
2346 /* And we're replacing base_name. */
2347 TALLOC_FREE(smb_fname->base_name);
2349 if (!parent_dirname(smb_fname, smb_fname_in->base_name,
2351 TALLOC_FREE(smb_fname);
2355 smb_fname->base_name = talloc_asprintf(smb_fname,
2356 "%s/._%s", parent, base);
2357 if (smb_fname->base_name == NULL) {
2358 TALLOC_FREE(smb_fname);
2362 *pp_smb_fname_out = smb_fname;
2368 * Allocate and initialize an AfpInfo struct
2370 static AfpInfo *afpinfo_new(TALLOC_CTX *ctx)
2372 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2376 ai->afpi_Signature = AFP_Signature;
2377 ai->afpi_Version = AFP_Version;
2378 ai->afpi_BackupTime = AD_DATE_START;
2383 * Pack an AfpInfo struct into a buffer
2385 * Buffer size must be at least AFP_INFO_SIZE
2386 * Returns size of packed buffer
2388 static ssize_t afpinfo_pack(const AfpInfo *ai, char *buf)
2390 memset(buf, 0, AFP_INFO_SIZE);
2392 RSIVAL(buf, 0, ai->afpi_Signature);
2393 RSIVAL(buf, 4, ai->afpi_Version);
2394 RSIVAL(buf, 12, ai->afpi_BackupTime);
2395 memcpy(buf + 16, ai->afpi_FinderInfo, sizeof(ai->afpi_FinderInfo));
2397 return AFP_INFO_SIZE;
2401 * Unpack a buffer into a AfpInfo structure
2403 * Buffer size must be at least AFP_INFO_SIZE
2404 * Returns allocated AfpInfo struct
2406 static AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data)
2408 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2413 ai->afpi_Signature = RIVAL(data, 0);
2414 ai->afpi_Version = RIVAL(data, 4);
2415 ai->afpi_BackupTime = RIVAL(data, 12);
2416 memcpy(ai->afpi_FinderInfo, (const char *)data + 16,
2417 sizeof(ai->afpi_FinderInfo));
2419 if (ai->afpi_Signature != AFP_Signature
2420 || ai->afpi_Version != AFP_Version) {
2421 DEBUG(1, ("Bad AfpInfo signature or version\n"));
2429 * Fake an inode number from the md5 hash of the (xattr) name
2431 static SMB_INO_T fruit_inode(const SMB_STRUCT_STAT *sbuf, const char *sname)
2433 gnutls_hash_hd_t hash_hnd = NULL;
2434 unsigned char hash[16];
2435 SMB_INO_T result = 0;
2439 DBG_DEBUG("fruit_inode called for %ju/%ju [%s]\n",
2440 (uintmax_t)sbuf->st_ex_dev,
2441 (uintmax_t)sbuf->st_ex_ino, sname);
2443 upper_sname = talloc_strdup_upper(talloc_tos(), sname);
2444 SMB_ASSERT(upper_sname != NULL);
2446 rc = gnutls_hash_init(&hash_hnd, GNUTLS_DIG_MD5);
2451 rc = gnutls_hash(hash_hnd, &(sbuf->st_ex_dev), sizeof(sbuf->st_ex_dev));
2453 gnutls_hash_deinit(hash_hnd, NULL);
2456 rc = gnutls_hash(hash_hnd,
2458 sizeof(sbuf->st_ex_ino));
2460 gnutls_hash_deinit(hash_hnd, NULL);
2463 rc = gnutls_hash(hash_hnd,
2465 talloc_get_size(upper_sname) - 1);
2467 gnutls_hash_deinit(hash_hnd, NULL);
2471 gnutls_hash_deinit(hash_hnd, hash);
2473 /* Hopefully all the variation is in the lower 4 (or 8) bytes! */
2474 memcpy(&result, hash, sizeof(result));
2477 DBG_DEBUG("fruit_inode \"%s\": ino=%ju\n",
2478 sname, (uintmax_t)result);
2481 TALLOC_FREE(upper_sname);
2486 static bool add_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
2487 struct stream_struct **streams,
2488 const char *name, off_t size,
2491 struct stream_struct *tmp;
2493 tmp = talloc_realloc(mem_ctx, *streams, struct stream_struct,
2499 tmp[*num_streams].name = talloc_asprintf(tmp, "%s:$DATA", name);
2500 if (tmp[*num_streams].name == NULL) {
2504 tmp[*num_streams].size = size;
2505 tmp[*num_streams].alloc_size = alloc_size;
2512 static bool filter_empty_rsrc_stream(unsigned int *num_streams,
2513 struct stream_struct **streams)
2515 struct stream_struct *tmp = *streams;
2518 if (*num_streams == 0) {
2522 for (i = 0; i < *num_streams; i++) {
2523 if (strequal_m(tmp[i].name, AFPRESOURCE_STREAM)) {
2528 if (i == *num_streams) {
2532 if (tmp[i].size > 0) {
2536 TALLOC_FREE(tmp[i].name);
2537 if (*num_streams - 1 > i) {
2538 memmove(&tmp[i], &tmp[i+1],
2539 (*num_streams - i - 1) * sizeof(struct stream_struct));
2546 static bool del_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
2547 struct stream_struct **streams,
2550 struct stream_struct *tmp = *streams;
2553 if (*num_streams == 0) {
2557 for (i = 0; i < *num_streams; i++) {
2558 if (strequal_m(tmp[i].name, name)) {
2563 if (i == *num_streams) {
2567 TALLOC_FREE(tmp[i].name);
2568 if (*num_streams - 1 > i) {
2569 memmove(&tmp[i], &tmp[i+1],
2570 (*num_streams - i - 1) * sizeof(struct stream_struct));
2577 static bool ad_empty_finderinfo(const struct adouble *ad)
2580 char emptybuf[ADEDLEN_FINDERI] = {0};
2583 fi = ad_get_entry(ad, ADEID_FINDERI);
2585 DBG_ERR("Missing FinderInfo in struct adouble [%p]\n", ad);
2589 cmp = memcmp(emptybuf, fi, ADEDLEN_FINDERI);
2593 static bool ai_empty_finderinfo(const AfpInfo *ai)
2596 char emptybuf[ADEDLEN_FINDERI] = {0};
2598 cmp = memcmp(emptybuf, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
2603 * Update btime with btime from Netatalk
2605 static void update_btime(vfs_handle_struct *handle,
2606 struct smb_filename *smb_fname)
2609 struct timespec creation_time = {0};
2611 struct fruit_config_data *config = NULL;
2613 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
2616 switch (config->meta) {
2617 case FRUIT_META_STREAM:
2619 case FRUIT_META_NETATALK:
2623 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
2627 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
2631 if (ad_getdate(ad, AD_DATE_UNIX | AD_DATE_CREATE, &t) != 0) {
2637 creation_time.tv_sec = convert_uint32_t_to_time_t(t);
2638 update_stat_ex_create_time(&smb_fname->st, creation_time);
2644 * Map an access mask to a Netatalk single byte byte range lock
2646 static off_t access_to_netatalk_brl(enum apple_fork fork_type,
2647 uint32_t access_mask)
2651 switch (access_mask) {
2652 case FILE_READ_DATA:
2653 offset = AD_FILELOCK_OPEN_RD;
2656 case FILE_WRITE_DATA:
2657 case FILE_APPEND_DATA:
2658 offset = AD_FILELOCK_OPEN_WR;
2662 offset = AD_FILELOCK_OPEN_NONE;
2666 if (fork_type == APPLE_FORK_RSRC) {
2667 if (offset == AD_FILELOCK_OPEN_NONE) {
2668 offset = AD_FILELOCK_RSRC_OPEN_NONE;
2678 * Map a deny mode to a Netatalk brl
2680 static off_t denymode_to_netatalk_brl(enum apple_fork fork_type,
2685 switch (deny_mode) {
2687 offset = AD_FILELOCK_DENY_RD;
2691 offset = AD_FILELOCK_DENY_WR;
2695 smb_panic("denymode_to_netatalk_brl: bad deny mode\n");
2698 if (fork_type == APPLE_FORK_RSRC) {
2706 * Call fcntl() with an exclusive F_GETLK request in order to
2707 * determine if there's an exisiting shared lock
2709 * @return true if the requested lock was found or any error occurred
2710 * false if the lock was not found
2712 static bool test_netatalk_lock(files_struct *fsp, off_t in_offset)
2715 off_t offset = in_offset;
2720 result = SMB_VFS_GETLOCK(fsp, &offset, &len, &type, &pid);
2721 if (result == false) {
2725 if (type != F_UNLCK) {
2732 static NTSTATUS fruit_check_access(vfs_handle_struct *handle,
2734 uint32_t access_mask,
2735 uint32_t share_mode)
2737 NTSTATUS status = NT_STATUS_OK;
2739 bool share_for_read = (share_mode & FILE_SHARE_READ);
2740 bool share_for_write = (share_mode & FILE_SHARE_WRITE);
2741 bool netatalk_already_open_for_reading = false;
2742 bool netatalk_already_open_for_writing = false;
2743 bool netatalk_already_open_with_deny_read = false;
2744 bool netatalk_already_open_with_deny_write = false;
2746 /* FIXME: hardcoded data fork, add resource fork */
2747 enum apple_fork fork_type = APPLE_FORK_DATA;
2749 DBG_DEBUG("fruit_check_access: %s, am: %s/%s, sm: 0x%x\n",
2751 access_mask & FILE_READ_DATA ? "READ" :"-",
2752 access_mask & FILE_WRITE_DATA ? "WRITE" : "-",
2755 if (fsp->fh->fd == -1) {
2756 return NT_STATUS_OK;
2759 /* Read NetATalk opens and deny modes on the file. */
2760 netatalk_already_open_for_reading = test_netatalk_lock(fsp,
2761 access_to_netatalk_brl(fork_type,
2764 netatalk_already_open_with_deny_read = test_netatalk_lock(fsp,
2765 denymode_to_netatalk_brl(fork_type,
2768 netatalk_already_open_for_writing = test_netatalk_lock(fsp,
2769 access_to_netatalk_brl(fork_type,
2772 netatalk_already_open_with_deny_write = test_netatalk_lock(fsp,
2773 denymode_to_netatalk_brl(fork_type,
2776 /* If there are any conflicts - sharing violation. */
2777 if ((access_mask & FILE_READ_DATA) &&
2778 netatalk_already_open_with_deny_read) {
2779 return NT_STATUS_SHARING_VIOLATION;
2782 if (!share_for_read &&
2783 netatalk_already_open_for_reading) {
2784 return NT_STATUS_SHARING_VIOLATION;
2787 if ((access_mask & FILE_WRITE_DATA) &&
2788 netatalk_already_open_with_deny_write) {
2789 return NT_STATUS_SHARING_VIOLATION;
2792 if (!share_for_write &&
2793 netatalk_already_open_for_writing) {
2794 return NT_STATUS_SHARING_VIOLATION;
2797 if (!(access_mask & FILE_READ_DATA)) {
2799 * Nothing we can do here, we need read access
2802 return NT_STATUS_OK;
2805 /* Set NetAtalk locks matching our access */
2806 if (access_mask & FILE_READ_DATA) {
2807 struct byte_range_lock *br_lck = NULL;
2809 off = access_to_netatalk_brl(fork_type, FILE_READ_DATA);
2811 handle->conn->sconn->msg_ctx, fsp,
2812 fsp->op->global->open_persistent_id, 1, off,
2813 READ_LOCK, POSIX_LOCK, false,
2816 TALLOC_FREE(br_lck);
2818 if (!NT_STATUS_IS_OK(status)) {
2823 if (!share_for_read) {
2824 struct byte_range_lock *br_lck = NULL;
2826 off = denymode_to_netatalk_brl(fork_type, DENY_READ);
2828 handle->conn->sconn->msg_ctx, fsp,
2829 fsp->op->global->open_persistent_id, 1, off,
2830 READ_LOCK, POSIX_LOCK, false,
2833 TALLOC_FREE(br_lck);
2835 if (!NT_STATUS_IS_OK(status)) {
2840 if (access_mask & FILE_WRITE_DATA) {
2841 struct byte_range_lock *br_lck = NULL;
2843 off = access_to_netatalk_brl(fork_type, FILE_WRITE_DATA);
2845 handle->conn->sconn->msg_ctx, fsp,
2846 fsp->op->global->open_persistent_id, 1, off,
2847 READ_LOCK, POSIX_LOCK, false,
2850 TALLOC_FREE(br_lck);
2852 if (!NT_STATUS_IS_OK(status)) {
2857 if (!share_for_write) {
2858 struct byte_range_lock *br_lck = NULL;
2860 off = denymode_to_netatalk_brl(fork_type, DENY_WRITE);
2862 handle->conn->sconn->msg_ctx, fsp,
2863 fsp->op->global->open_persistent_id, 1, off,
2864 READ_LOCK, POSIX_LOCK, false,
2867 TALLOC_FREE(br_lck);
2869 if (!NT_STATUS_IS_OK(status)) {
2874 return NT_STATUS_OK;
2877 static NTSTATUS check_aapl(vfs_handle_struct *handle,
2878 struct smb_request *req,
2879 const struct smb2_create_blobs *in_context_blobs,
2880 struct smb2_create_blobs *out_context_blobs)
2882 struct fruit_config_data *config;
2884 struct smb2_create_blob *aapl = NULL;
2888 DATA_BLOB blob = data_blob_talloc(req, NULL, 0);
2889 uint64_t req_bitmap, client_caps;
2890 uint64_t server_caps = SMB2_CRTCTX_AAPL_UNIX_BASED;
2894 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
2895 return NT_STATUS_UNSUCCESSFUL);
2897 if (!config->use_aapl
2898 || in_context_blobs == NULL
2899 || out_context_blobs == NULL) {
2900 return NT_STATUS_OK;
2903 aapl = smb2_create_blob_find(in_context_blobs,
2904 SMB2_CREATE_TAG_AAPL);
2906 return NT_STATUS_OK;
2909 if (aapl->data.length != 24) {
2910 DEBUG(1, ("unexpected AAPL ctxt length: %ju\n",
2911 (uintmax_t)aapl->data.length));
2912 return NT_STATUS_INVALID_PARAMETER;
2915 cmd = IVAL(aapl->data.data, 0);
2916 if (cmd != SMB2_CRTCTX_AAPL_SERVER_QUERY) {
2917 DEBUG(1, ("unsupported AAPL cmd: %d\n", cmd));
2918 return NT_STATUS_INVALID_PARAMETER;
2921 req_bitmap = BVAL(aapl->data.data, 8);
2922 client_caps = BVAL(aapl->data.data, 16);
2924 SIVAL(p, 0, SMB2_CRTCTX_AAPL_SERVER_QUERY);
2926 SBVAL(p, 8, req_bitmap);
2927 ok = data_blob_append(req, &blob, p, 16);
2929 return NT_STATUS_UNSUCCESSFUL;
2932 if (req_bitmap & SMB2_CRTCTX_AAPL_SERVER_CAPS) {
2933 if ((client_caps & SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR) &&
2934 (handle->conn->tcon->compat->fs_capabilities & FILE_NAMED_STREAMS)) {
2935 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR;
2936 config->readdir_attr_enabled = true;
2939 if (config->use_copyfile) {
2940 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_OSX_COPYFILE;
2941 config->copyfile_enabled = true;
2945 * The client doesn't set the flag, so we can't check
2946 * for it and just set it unconditionally
2948 if (config->unix_info_enabled) {
2949 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_NFS_ACE;
2952 SBVAL(p, 0, server_caps);
2953 ok = data_blob_append(req, &blob, p, 8);
2955 return NT_STATUS_UNSUCCESSFUL;
2959 if (req_bitmap & SMB2_CRTCTX_AAPL_VOLUME_CAPS) {
2960 int val = lp_case_sensitive(SNUM(handle->conn->tcon->compat));
2968 caps |= SMB2_CRTCTX_AAPL_CASE_SENSITIVE;
2975 if (config->time_machine) {
2976 caps |= SMB2_CRTCTX_AAPL_FULL_SYNC;
2981 ok = data_blob_append(req, &blob, p, 8);
2983 return NT_STATUS_UNSUCCESSFUL;
2987 if (req_bitmap & SMB2_CRTCTX_AAPL_MODEL_INFO) {
2988 ok = convert_string_talloc(req,
2989 CH_UNIX, CH_UTF16LE,
2990 config->model, strlen(config->model),
2993 return NT_STATUS_UNSUCCESSFUL;
2997 SIVAL(p + 4, 0, modellen);
2998 ok = data_blob_append(req, &blob, p, 8);
3001 return NT_STATUS_UNSUCCESSFUL;
3004 ok = data_blob_append(req, &blob, model, modellen);
3007 return NT_STATUS_UNSUCCESSFUL;
3011 status = smb2_create_blob_add(out_context_blobs,
3013 SMB2_CREATE_TAG_AAPL,
3015 if (NT_STATUS_IS_OK(status)) {
3016 global_fruit_config.nego_aapl = true;
3017 if (config->aapl_zero_file_id) {
3018 aapl_force_zero_file_id(handle->conn->sconn);
3025 static bool readdir_attr_meta_finderi_stream(
3026 struct vfs_handle_struct *handle,
3027 const struct smb_filename *smb_fname,
3030 struct smb_filename *stream_name = NULL;
3031 files_struct *fsp = NULL;
3036 uint8_t buf[AFP_INFO_SIZE];
3038 stream_name = synthetic_smb_fname(talloc_tos(),
3039 smb_fname->base_name,
3040 AFPINFO_STREAM_NAME,
3041 NULL, smb_fname->flags);
3042 if (stream_name == NULL) {
3046 ret = SMB_VFS_STAT(handle->conn, stream_name);
3051 status = SMB_VFS_CREATE_FILE(
3052 handle->conn, /* conn */
3054 0, /* root_dir_fid */
3055 stream_name, /* fname */
3056 FILE_READ_DATA, /* access_mask */
3057 (FILE_SHARE_READ | FILE_SHARE_WRITE | /* share_access */
3059 FILE_OPEN, /* create_disposition*/
3060 0, /* create_options */
3061 0, /* file_attributes */
3062 INTERNAL_OPEN_ONLY, /* oplock_request */
3064 0, /* allocation_size */
3065 0, /* private_flags */
3070 NULL, NULL); /* create context */
3072 TALLOC_FREE(stream_name);
3074 if (!NT_STATUS_IS_OK(status)) {
3078 nread = SMB_VFS_PREAD(fsp, &buf[0], AFP_INFO_SIZE, 0);
3079 if (nread != AFP_INFO_SIZE) {
3080 DBG_ERR("short read [%s] [%zd/%d]\n",
3081 smb_fname_str_dbg(stream_name), nread, AFP_INFO_SIZE);
3086 memcpy(&ai->afpi_FinderInfo[0], &buf[AFP_OFF_FinderInfo],
3093 close_file(NULL, fsp, NORMAL_CLOSE);
3099 static bool readdir_attr_meta_finderi_netatalk(
3100 struct vfs_handle_struct *handle,
3101 const struct smb_filename *smb_fname,
3104 struct adouble *ad = NULL;
3107 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
3112 p = ad_get_entry(ad, ADEID_FINDERI);
3114 DBG_ERR("No ADEID_FINDERI for [%s]\n", smb_fname->base_name);
3119 memcpy(&ai->afpi_FinderInfo[0], p, AFP_FinderSize);
3124 static bool readdir_attr_meta_finderi(struct vfs_handle_struct *handle,
3125 const struct smb_filename *smb_fname,
3126 struct readdir_attr_data *attr_data)
3128 struct fruit_config_data *config = NULL;
3129 uint32_t date_added;
3133 SMB_VFS_HANDLE_GET_DATA(handle, config,
3134 struct fruit_config_data,
3137 switch (config->meta) {
3138 case FRUIT_META_NETATALK:
3139 ok = readdir_attr_meta_finderi_netatalk(
3140 handle, smb_fname, &ai);
3143 case FRUIT_META_STREAM:
3144 ok = readdir_attr_meta_finderi_stream(
3145 handle, smb_fname, &ai);
3149 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3154 /* Don't bother with errors, it's likely ENOENT */
3158 if (S_ISREG(smb_fname->st.st_ex_mode)) {
3160 memcpy(&attr_data->attr_data.aapl.finder_info[0],
3161 &ai.afpi_FinderInfo[0], 4);
3163 /* finder_creator */
3164 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 4,
3165 &ai.afpi_FinderInfo[4], 4);
3169 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 8,
3170 &ai.afpi_FinderInfo[8], 2);
3172 /* finder_ext_flags */
3173 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 10,
3174 &ai.afpi_FinderInfo[24], 2);
3177 date_added = convert_time_t_to_uint32_t(
3178 smb_fname->st.st_ex_btime.tv_sec - AD_DATE_DELTA);
3180 RSIVAL(&attr_data->attr_data.aapl.finder_info[0], 12, date_added);
3185 static uint64_t readdir_attr_rfork_size_adouble(
3186 struct vfs_handle_struct *handle,
3187 const struct smb_filename *smb_fname)
3189 struct adouble *ad = NULL;
3190 uint64_t rfork_size;
3192 ad = ad_get(talloc_tos(), handle, smb_fname,
3198 rfork_size = ad_getentrylen(ad, ADEID_RFORK);
3204 static uint64_t readdir_attr_rfork_size_stream(
3205 struct vfs_handle_struct *handle,
3206 const struct smb_filename *smb_fname)
3208 struct smb_filename *stream_name = NULL;
3210 uint64_t rfork_size;
3212 stream_name = synthetic_smb_fname(talloc_tos(),
3213 smb_fname->base_name,
3214 AFPRESOURCE_STREAM_NAME,
3216 if (stream_name == NULL) {
3220 ret = SMB_VFS_STAT(handle->conn, stream_name);
3222 TALLOC_FREE(stream_name);
3226 rfork_size = stream_name->st.st_ex_size;
3227 TALLOC_FREE(stream_name);
3232 static uint64_t readdir_attr_rfork_size(struct vfs_handle_struct *handle,
3233 const struct smb_filename *smb_fname)
3235 struct fruit_config_data *config = NULL;
3236 uint64_t rfork_size;
3238 SMB_VFS_HANDLE_GET_DATA(handle, config,
3239 struct fruit_config_data,
3242 switch (config->rsrc) {
3243 case FRUIT_RSRC_ADFILE:
3244 case FRUIT_RSRC_XATTR:
3245 rfork_size = readdir_attr_rfork_size_adouble(handle,
3249 case FRUIT_META_STREAM:
3250 rfork_size = readdir_attr_rfork_size_stream(handle,
3255 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3263 static NTSTATUS readdir_attr_macmeta(struct vfs_handle_struct *handle,
3264 const struct smb_filename *smb_fname,
3265 struct readdir_attr_data *attr_data)
3267 NTSTATUS status = NT_STATUS_OK;
3268 struct fruit_config_data *config = NULL;
3271 SMB_VFS_HANDLE_GET_DATA(handle, config,
3272 struct fruit_config_data,
3273 return NT_STATUS_UNSUCCESSFUL);
3276 /* Ensure we return a default value in the creation_date field */
3277 RSIVAL(&attr_data->attr_data.aapl.finder_info, 12, AD_DATE_START);
3280 * Resource fork length
3283 if (config->readdir_attr_rsize) {
3284 uint64_t rfork_size;
3286 rfork_size = readdir_attr_rfork_size(handle, smb_fname);
3287 attr_data->attr_data.aapl.rfork_size = rfork_size;
3294 if (config->readdir_attr_finder_info) {
3295 ok = readdir_attr_meta_finderi(handle, smb_fname, attr_data);
3297 status = NT_STATUS_INTERNAL_ERROR;
3304 static NTSTATUS remove_virtual_nfs_aces(struct security_descriptor *psd)
3309 if (psd->dacl == NULL) {
3310 return NT_STATUS_OK;
3313 for (i = 0; i < psd->dacl->num_aces; i++) {
3314 /* MS NFS style mode/uid/gid */
3315 int cmp = dom_sid_compare_domain(
3316 &global_sid_Unix_NFS,
3317 &psd->dacl->aces[i].trustee);
3319 /* Normal ACE entry. */
3324 * security_descriptor_dacl_del()
3325 * *must* return NT_STATUS_OK as we know
3326 * we have something to remove.
3329 status = security_descriptor_dacl_del(psd,
3330 &psd->dacl->aces[i].trustee);
3331 if (!NT_STATUS_IS_OK(status)) {
3332 DBG_WARNING("failed to remove MS NFS style ACE: %s\n",
3338 * security_descriptor_dacl_del() may delete more
3339 * then one entry subsequent to this one if the
3340 * SID matches, but we only need to ensure that
3341 * we stay looking at the same element in the array.
3345 return NT_STATUS_OK;
3348 /* Search MS NFS style ACE with UNIX mode */
3349 static NTSTATUS check_ms_nfs(vfs_handle_struct *handle,
3351 struct security_descriptor *psd,
3356 struct fruit_config_data *config = NULL;
3360 SMB_VFS_HANDLE_GET_DATA(handle, config,
3361 struct fruit_config_data,
3362 return NT_STATUS_UNSUCCESSFUL);
3364 if (!global_fruit_config.nego_aapl) {
3365 return NT_STATUS_OK;
3367 if (psd->dacl == NULL || !config->unix_info_enabled) {
3368 return NT_STATUS_OK;
3371 for (i = 0; i < psd->dacl->num_aces; i++) {
3372 if (dom_sid_compare_domain(
3373 &global_sid_Unix_NFS_Mode,
3374 &psd->dacl->aces[i].trustee) == 0) {
3375 *pmode = (mode_t)psd->dacl->aces[i].trustee.sub_auths[2];
3376 *pmode &= (S_IRWXU | S_IRWXG | S_IRWXO);
3379 DEBUG(10, ("MS NFS chmod request %s, %04o\n",
3380 fsp_str_dbg(fsp), (unsigned)(*pmode)));
3386 * Remove any incoming virtual ACE entries generated by
3387 * fruit_fget_nt_acl().
3390 return remove_virtual_nfs_aces(psd);
3393 /****************************************************************************
3395 ****************************************************************************/
3397 static int fruit_connect(vfs_handle_struct *handle,
3398 const char *service,
3402 char *list = NULL, *newlist = NULL;
3403 struct fruit_config_data *config;
3405 DEBUG(10, ("fruit_connect\n"));
3407 rc = SMB_VFS_NEXT_CONNECT(handle, service, user);
3412 rc = init_fruit_config(handle);
3417 SMB_VFS_HANDLE_GET_DATA(handle, config,
3418 struct fruit_config_data, return -1);
3420 if (config->veto_appledouble) {
3421 list = lp_veto_files(talloc_tos(), SNUM(handle->conn));
3424 if (strstr(list, "/" ADOUBLE_NAME_PREFIX "*/") == NULL) {
3425 newlist = talloc_asprintf(
3427 "%s/" ADOUBLE_NAME_PREFIX "*/",
3429 lp_do_parameter(SNUM(handle->conn),
3434 lp_do_parameter(SNUM(handle->conn),
3436 "/" ADOUBLE_NAME_PREFIX "*/");
3442 if (config->encoding == FRUIT_ENC_NATIVE) {
3443 lp_do_parameter(SNUM(handle->conn),
3448 if (config->time_machine) {
3449 DBG_NOTICE("Enabling durable handles for Time Machine "
3450 "support on [%s]\n", service);
3451 lp_do_parameter(SNUM(handle->conn), "durable handles", "yes");
3452 lp_do_parameter(SNUM(handle->conn), "kernel oplocks", "no");
3453 lp_do_parameter(SNUM(handle->conn), "kernel share modes", "no");
3454 if (!lp_strict_sync(SNUM(handle->conn))) {
3455 DBG_WARNING("Time Machine without strict sync is not "
3458 lp_do_parameter(SNUM(handle->conn), "posix locking", "no");
3464 static int fruit_fake_fd(void)
3471 * Return a valid fd, but ensure any attempt to use it returns
3472 * an error (EPIPE). Once we get a write on the handle, we open
3475 ret = pipe(pipe_fds);
3485 static int fruit_open_meta_stream(vfs_handle_struct *handle,
3486 struct smb_filename *smb_fname,
3491 struct fruit_config_data *config = NULL;
3492 struct fio *fio = NULL;
3493 int open_flags = flags & ~O_CREAT;
3496 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3498 SMB_VFS_HANDLE_GET_DATA(handle, config,
3499 struct fruit_config_data, return -1);
3501 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3502 fio->type = ADOUBLE_META;
3503 fio->config = config;
3505 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, open_flags, mode);
3510 if (!(flags & O_CREAT)) {
3511 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
3515 fd = fruit_fake_fd();
3517 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
3521 fio->fake_fd = true;
3528 static int fruit_open_meta_netatalk(vfs_handle_struct *handle,
3529 struct smb_filename *smb_fname,
3534 struct fruit_config_data *config = NULL;
3535 struct fio *fio = NULL;
3536 struct adouble *ad = NULL;
3537 bool meta_exists = false;
3540 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3542 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
3549 if (!meta_exists && !(flags & O_CREAT)) {
3554 fd = fruit_fake_fd();
3559 SMB_VFS_HANDLE_GET_DATA(handle, config,
3560 struct fruit_config_data, return -1);
3562 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3563 fio->type = ADOUBLE_META;
3564 fio->config = config;
3565 fio->fake_fd = true;
3572 static int fruit_open_meta(vfs_handle_struct *handle,
3573 struct smb_filename *smb_fname,
3574 files_struct *fsp, int flags, mode_t mode)
3577 struct fruit_config_data *config = NULL;
3579 DBG_DEBUG("path [%s]\n", smb_fname_str_dbg(smb_fname));
3581 SMB_VFS_HANDLE_GET_DATA(handle, config,
3582 struct fruit_config_data, return -1);
3584 switch (config->meta) {
3585 case FRUIT_META_STREAM:
3586 fd = fruit_open_meta_stream(handle, smb_fname,
3590 case FRUIT_META_NETATALK:
3591 fd = fruit_open_meta_netatalk(handle, smb_fname,
3596 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3600 DBG_DEBUG("path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3605 static int fruit_open_rsrc_adouble(vfs_handle_struct *handle,
3606 struct smb_filename *smb_fname,
3612 struct adouble *ad = NULL;
3613 struct smb_filename *smb_fname_base = NULL;
3614 struct fruit_config_data *config = NULL;
3617 SMB_VFS_HANDLE_GET_DATA(handle, config,
3618 struct fruit_config_data, return -1);
3620 if ((!(flags & O_CREAT)) &&
3621 S_ISDIR(fsp->base_fsp->fsp_name->st.st_ex_mode))
3623 /* sorry, but directories don't habe a resource fork */
3628 rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_base);
3633 /* We always need read/write access for the metadata header too */
3634 flags &= ~(O_RDONLY | O_WRONLY);
3637 hostfd = SMB_VFS_NEXT_OPEN(handle, smb_fname_base, fsp,
3644 if (flags & (O_CREAT | O_TRUNC)) {
3645 ad = ad_init(fsp, handle, ADOUBLE_RSRC);
3651 fsp->fh->fd = hostfd;
3653 rc = ad_fset(handle, ad, fsp);
3664 TALLOC_FREE(smb_fname_base);
3666 DEBUG(10, ("fruit_open resource fork: rc=%d, fd=%d\n", rc, hostfd));
3668 int saved_errno = errno;
3671 * BUGBUGBUG -- we would need to call
3672 * fd_close_posix here, but we don't have a
3675 fsp->fh->fd = hostfd;
3679 errno = saved_errno;
3684 static int fruit_open_rsrc_xattr(vfs_handle_struct *handle,
3685 struct smb_filename *smb_fname,
3690 #ifdef HAVE_ATTROPEN
3693 fd = attropen(smb_fname->base_name,
3694 AFPRESOURCE_EA_NETATALK,
3709 static int fruit_open_rsrc(vfs_handle_struct *handle,
3710 struct smb_filename *smb_fname,
3711 files_struct *fsp, int flags, mode_t mode)
3714 struct fruit_config_data *config = NULL;
3715 struct fio *fio = NULL;
3717 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3719 SMB_VFS_HANDLE_GET_DATA(handle, config,
3720 struct fruit_config_data, return -1);
3722 switch (config->rsrc) {
3723 case FRUIT_RSRC_STREAM:
3724 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3727 case FRUIT_RSRC_ADFILE:
3728 fd = fruit_open_rsrc_adouble(handle, smb_fname,
3732 case FRUIT_RSRC_XATTR:
3733 fd = fruit_open_rsrc_xattr(handle, smb_fname,
3738 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3742 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3748 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3749 fio->type = ADOUBLE_RSRC;
3750 fio->config = config;
3755 static int fruit_open(vfs_handle_struct *handle,
3756 struct smb_filename *smb_fname,
3757 files_struct *fsp, int flags, mode_t mode)
3761 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3763 if (!is_ntfs_stream_smb_fname(smb_fname)) {
3764 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3767 if (is_afpinfo_stream(smb_fname)) {
3768 fd = fruit_open_meta(handle, smb_fname, fsp, flags, mode);
3769 } else if (is_afpresource_stream(smb_fname)) {
3770 fd = fruit_open_rsrc(handle, smb_fname, fsp, flags, mode);
3772 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3775 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3780 static int fruit_close_meta(vfs_handle_struct *handle,
3784 struct fruit_config_data *config = NULL;
3786 SMB_VFS_HANDLE_GET_DATA(handle, config,
3787 struct fruit_config_data, return -1);
3789 switch (config->meta) {
3790 case FRUIT_META_STREAM:
3791 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3794 case FRUIT_META_NETATALK:
3795 ret = close(fsp->fh->fd);
3800 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3808 static int fruit_close_rsrc(vfs_handle_struct *handle,
3812 struct fruit_config_data *config = NULL;
3814 SMB_VFS_HANDLE_GET_DATA(handle, config,
3815 struct fruit_config_data, return -1);
3817 switch (config->rsrc) {
3818 case FRUIT_RSRC_STREAM:
3819 case FRUIT_RSRC_ADFILE:
3820 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3823 case FRUIT_RSRC_XATTR:
3824 ret = close(fsp->fh->fd);
3829 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3836 static int fruit_close(vfs_handle_struct *handle,
3844 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(fsp->fsp_name), fd);
3846 if (!is_ntfs_stream_smb_fname(fsp->fsp_name)) {
3847 return SMB_VFS_NEXT_CLOSE(handle, fsp);
3850 if (is_afpinfo_stream(fsp->fsp_name)) {
3851 ret = fruit_close_meta(handle, fsp);
3852 } else if (is_afpresource_stream(fsp->fsp_name)) {
3853 ret = fruit_close_rsrc(handle, fsp);
3855 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3861 static int fruit_rename(struct vfs_handle_struct *handle,
3862 const struct smb_filename *smb_fname_src,
3863 const struct smb_filename *smb_fname_dst)
3866 struct fruit_config_data *config = NULL;
3867 struct smb_filename *src_adp_smb_fname = NULL;
3868 struct smb_filename *dst_adp_smb_fname = NULL;
3870 SMB_VFS_HANDLE_GET_DATA(handle, config,
3871 struct fruit_config_data, return -1);
3873 if (!VALID_STAT(smb_fname_src->st)) {
3874 DBG_ERR("Need valid stat for [%s]\n",
3875 smb_fname_str_dbg(smb_fname_src));
3879 rc = SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
3884 if ((config->rsrc != FRUIT_RSRC_ADFILE) ||
3885 (!S_ISREG(smb_fname_src->st.st_ex_mode)))
3890 rc = adouble_path(talloc_tos(), smb_fname_src, &src_adp_smb_fname);
3895 rc = adouble_path(talloc_tos(), smb_fname_dst, &dst_adp_smb_fname);
3900 DBG_DEBUG("%s -> %s\n",
3901 smb_fname_str_dbg(src_adp_smb_fname),
3902 smb_fname_str_dbg(dst_adp_smb_fname));
3904 rc = SMB_VFS_NEXT_RENAME(handle, src_adp_smb_fname, dst_adp_smb_fname);
3905 if (errno == ENOENT) {
3910 TALLOC_FREE(src_adp_smb_fname);
3911 TALLOC_FREE(dst_adp_smb_fname);
3915 static int fruit_unlink_meta_stream(vfs_handle_struct *handle,
3916 const struct smb_filename *smb_fname)
3918 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3921 static int fruit_unlink_meta_netatalk(vfs_handle_struct *handle,
3922 const struct smb_filename *smb_fname)
3924 return SMB_VFS_REMOVEXATTR(handle->conn,
3926 AFPINFO_EA_NETATALK);
3929 static int fruit_unlink_meta(vfs_handle_struct *handle,
3930 const struct smb_filename *smb_fname)
3932 struct fruit_config_data *config = NULL;
3935 SMB_VFS_HANDLE_GET_DATA(handle, config,
3936 struct fruit_config_data, return -1);
3938 switch (config->meta) {
3939 case FRUIT_META_STREAM:
3940 rc = fruit_unlink_meta_stream(handle, smb_fname);
3943 case FRUIT_META_NETATALK:
3944 rc = fruit_unlink_meta_netatalk(handle, smb_fname);
3948 DBG_ERR("Unsupported meta config [%d]\n", config->meta);
3955 static int fruit_unlink_rsrc_stream(vfs_handle_struct *handle,
3956 const struct smb_filename *smb_fname,
3961 if (!force_unlink) {
3962 struct smb_filename *smb_fname_cp = NULL;
3965 smb_fname_cp = cp_smb_filename(talloc_tos(), smb_fname);
3966 if (smb_fname_cp == NULL) {
3971 * 0 byte resource fork streams are not listed by
3972 * vfs_streaminfo, as a result stream cleanup/deletion of file
3973 * deletion doesn't remove the resourcefork stream.
3976 ret = SMB_VFS_NEXT_STAT(handle, smb_fname_cp);
3978 TALLOC_FREE(smb_fname_cp);
3979 DBG_ERR("stat [%s] failed [%s]\n",
3980 smb_fname_str_dbg(smb_fname_cp), strerror(errno));
3984 size = smb_fname_cp->st.st_ex_size;
3985 TALLOC_FREE(smb_fname_cp);
3988 /* OS X ignores resource fork stream delete requests */
3993 ret = SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3994 if ((ret != 0) && (errno == ENOENT) && force_unlink) {
4001 static int fruit_unlink_rsrc_adouble(vfs_handle_struct *handle,
4002 const struct smb_filename *smb_fname,
4006 struct adouble *ad = NULL;
4007 struct smb_filename *adp_smb_fname = NULL;
4009 if (!force_unlink) {
4010 ad = ad_get(talloc_tos(), handle, smb_fname,
4019 * 0 byte resource fork streams are not listed by
4020 * vfs_streaminfo, as a result stream cleanup/deletion of file
4021 * deletion doesn't remove the resourcefork stream.
4024 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
4025 /* OS X ignores resource fork stream delete requests */
4033 rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
4038 rc = SMB_VFS_NEXT_UNLINK(handle, adp_smb_fname);
4039 TALLOC_FREE(adp_smb_fname);
4040 if ((rc != 0) && (errno == ENOENT) && force_unlink) {
4047 static int fruit_unlink_rsrc_xattr(vfs_handle_struct *handle,
4048 const struct smb_filename *smb_fname,
4052 * OS X ignores resource fork stream delete requests, so nothing to do
4053 * here. Removing the file will remove the xattr anyway, so we don't
4054 * have to take care of removing 0 byte resource forks that could be
4060 static int fruit_unlink_rsrc(vfs_handle_struct *handle,
4061 const struct smb_filename *smb_fname,
4064 struct fruit_config_data *config = NULL;
4067 SMB_VFS_HANDLE_GET_DATA(handle, config,
4068 struct fruit_config_data, return -1);
4070 switch (config->rsrc) {
4071 case FRUIT_RSRC_STREAM:
4072 rc = fruit_unlink_rsrc_stream(handle, smb_fname, force_unlink);
4075 case FRUIT_RSRC_ADFILE:
4076 rc = fruit_unlink_rsrc_adouble(handle, smb_fname, force_unlink);
4079 case FRUIT_RSRC_XATTR:
4080 rc = fruit_unlink_rsrc_xattr(handle, smb_fname, force_unlink);
4084 DBG_ERR("Unsupported rsrc config [%d]\n", config->rsrc);
4091 static int fruit_unlink(vfs_handle_struct *handle,
4092 const struct smb_filename *smb_fname)
4095 struct fruit_config_data *config = NULL;
4096 struct smb_filename *rsrc_smb_fname = NULL;
4098 SMB_VFS_HANDLE_GET_DATA(handle, config,
4099 struct fruit_config_data, return -1);
4101 if (is_afpinfo_stream(smb_fname)) {
4102 return fruit_unlink_meta(handle, smb_fname);
4103 } else if (is_afpresource_stream(smb_fname)) {
4104 return fruit_unlink_rsrc(handle, smb_fname, false);
4105 } if (is_ntfs_stream_smb_fname(smb_fname)) {
4106 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4110 * A request to delete the base file. Because 0 byte resource
4111 * fork streams are not listed by fruit_streaminfo,
4112 * delete_all_streams() can't remove 0 byte resource fork
4113 * streams, so we have to cleanup this here.
4115 rsrc_smb_fname = synthetic_smb_fname(talloc_tos(),
4116 smb_fname->base_name,
4117 AFPRESOURCE_STREAM_NAME,
4120 if (rsrc_smb_fname == NULL) {
4124 rc = fruit_unlink_rsrc(handle, rsrc_smb_fname, true);
4125 if ((rc != 0) && (errno != ENOENT)) {
4126 DBG_ERR("Forced unlink of [%s] failed [%s]\n",
4127 smb_fname_str_dbg(rsrc_smb_fname), strerror(errno));
4128 TALLOC_FREE(rsrc_smb_fname);
4131 TALLOC_FREE(rsrc_smb_fname);
4133 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4136 static int fruit_chmod(vfs_handle_struct *handle,
4137 const struct smb_filename *smb_fname,
4141 struct fruit_config_data *config = NULL;
4142 struct smb_filename *smb_fname_adp = NULL;
4144 rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname, mode);
4149 SMB_VFS_HANDLE_GET_DATA(handle, config,
4150 struct fruit_config_data, return -1);
4152 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4156 if (!VALID_STAT(smb_fname->st)) {
4160 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
4164 rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_adp);
4169 DEBUG(10, ("fruit_chmod: %s\n", smb_fname_adp->base_name));
4171 rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname_adp, mode);
4172 if (errno == ENOENT) {
4176 TALLOC_FREE(smb_fname_adp);
4180 static int fruit_chown(vfs_handle_struct *handle,
4181 const struct smb_filename *smb_fname,
4186 struct fruit_config_data *config = NULL;
4187 struct smb_filename *adp_smb_fname = NULL;
4189 rc = SMB_VFS_NEXT_CHOWN(handle, smb_fname, uid, gid);
4194 SMB_VFS_HANDLE_GET_DATA(handle, config,
4195 struct fruit_config_data, return -1);
4197 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4201 if (!VALID_STAT(smb_fname->st)) {
4205 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
4209 rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
4214 DEBUG(10, ("fruit_chown: %s\n", adp_smb_fname->base_name));
4216 rc = SMB_VFS_NEXT_CHOWN(handle, adp_smb_fname, uid, gid);
4217 if (errno == ENOENT) {
4222 TALLOC_FREE(adp_smb_fname);
4226 static int fruit_rmdir(struct vfs_handle_struct *handle,
4227 const struct smb_filename *smb_fname)
4231 struct fruit_config_data *config;
4233 SMB_VFS_HANDLE_GET_DATA(handle, config,
4234 struct fruit_config_data, return -1);
4236 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4241 * Due to there is no way to change bDeleteVetoFiles variable
4242 * from this module, need to clean up ourselves
4245 dh = SMB_VFS_OPENDIR(handle->conn, smb_fname, NULL, 0);
4250 while ((de = SMB_VFS_READDIR(handle->conn, dh, NULL)) != NULL) {
4251 struct adouble *ad = NULL;
4253 struct smb_filename *ad_smb_fname = NULL;
4256 if (!is_adouble_file(de->d_name)) {
4260 p = talloc_asprintf(talloc_tos(), "%s/%s",
4261 smb_fname->base_name, de->d_name);
4263 DBG_ERR("talloc_asprintf failed\n");
4267 ad_smb_fname = synthetic_smb_fname(talloc_tos(), p,
4271 if (ad_smb_fname == NULL) {
4272 DBG_ERR("synthetic_smb_fname failed\n");
4277 * Check whether it's a valid AppleDouble file, if
4278 * yes, delete it, ignore it otherwise.
4280 ad = ad_get(talloc_tos(), handle, ad_smb_fname, ADOUBLE_RSRC);
4282 TALLOC_FREE(ad_smb_fname);
4288 ret = SMB_VFS_NEXT_UNLINK(handle, ad_smb_fname);
4290 DBG_ERR("Deleting [%s] failed\n",
4291 smb_fname_str_dbg(ad_smb_fname));
4293 TALLOC_FREE(ad_smb_fname);
4298 SMB_VFS_CLOSEDIR(handle->conn, dh);
4300 return SMB_VFS_NEXT_RMDIR(handle, smb_fname);
4303 static ssize_t fruit_pread_meta_stream(vfs_handle_struct *handle,
4304 files_struct *fsp, void *data,
4305 size_t n, off_t offset)
4310 nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4311 if (nread == -1 || nread == n) {
4315 DBG_ERR("Removing [%s] after short read [%zd]\n",
4316 fsp_str_dbg(fsp), nread);
4318 ret = SMB_VFS_NEXT_UNLINK(handle, fsp->fsp_name);
4320 DBG_ERR("Removing [%s] failed\n", fsp_str_dbg(fsp));
4328 static ssize_t fruit_pread_meta_adouble(vfs_handle_struct *handle,
4329 files_struct *fsp, void *data,
4330 size_t n, off_t offset)
4333 struct adouble *ad = NULL;
4334 char afpinfo_buf[AFP_INFO_SIZE];
4338 ai = afpinfo_new(talloc_tos());
4343 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
4349 p = ad_get_entry(ad, ADEID_FINDERI);
4351 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
4356 memcpy(&ai->afpi_FinderInfo[0], p, ADEDLEN_FINDERI);
4358 nread = afpinfo_pack(ai, afpinfo_buf);
4359 if (nread != AFP_INFO_SIZE) {
4364 memcpy(data, afpinfo_buf, n);
4372 static ssize_t fruit_pread_meta(vfs_handle_struct *handle,
4373 files_struct *fsp, void *data,
4374 size_t n, off_t offset)
4376 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4381 * OS X has a off-by-1 error in the offset calculation, so we're
4382 * bug compatible here. It won't hurt, as any relevant real
4383 * world read requests from the AFP_AfpInfo stream will be
4384 * offset=0 n=60. offset is ignored anyway, see below.
4386 if ((offset < 0) || (offset >= AFP_INFO_SIZE + 1)) {
4391 DBG_ERR("Failed to fetch fsp extension");
4395 /* Yes, macOS always reads from offset 0 */
4397 to_return = MIN(n, AFP_INFO_SIZE);
4399 switch (fio->config->meta) {
4400 case FRUIT_META_STREAM:
4401 nread = fruit_pread_meta_stream(handle, fsp, data,
4405 case FRUIT_META_NETATALK:
4406 nread = fruit_pread_meta_adouble(handle, fsp, data,
4411 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
4415 if (nread == -1 && fio->created) {
4417 char afpinfo_buf[AFP_INFO_SIZE];
4419 ai = afpinfo_new(talloc_tos());
4424 nread = afpinfo_pack(ai, afpinfo_buf);
4426 if (nread != AFP_INFO_SIZE) {
4430 memcpy(data, afpinfo_buf, to_return);
4437 static ssize_t fruit_pread_rsrc_stream(vfs_handle_struct *handle,
4438 files_struct *fsp, void *data,
4439 size_t n, off_t offset)
4441 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4444 static ssize_t fruit_pread_rsrc_xattr(vfs_handle_struct *handle,
4445 files_struct *fsp, void *data,
4446 size_t n, off_t offset)
4448 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4451 static ssize_t fruit_pread_rsrc_adouble(vfs_handle_struct *handle,
4452 files_struct *fsp, void *data,
4453 size_t n, off_t offset)
4455 struct adouble *ad = NULL;
4458 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
4463 nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n,
4464 offset + ad_getentryoff(ad, ADEID_RFORK));
4470 static ssize_t fruit_pread_rsrc(vfs_handle_struct *handle,
4471 files_struct *fsp, void *data,
4472 size_t n, off_t offset)
4474 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4482 switch (fio->config->rsrc) {
4483 case FRUIT_RSRC_STREAM:
4484 nread = fruit_pread_rsrc_stream(handle, fsp, data, n, offset);
4487 case FRUIT_RSRC_ADFILE:
4488 nread = fruit_pread_rsrc_adouble(handle, fsp, data, n, offset);
4491 case FRUIT_RSRC_XATTR:
4492 nread = fruit_pread_rsrc_xattr(handle, fsp, data, n, offset);
4496 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
4503 static ssize_t fruit_pread(vfs_handle_struct *handle,
4504 files_struct *fsp, void *data,
4505 size_t n, off_t offset)
4507 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4510 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4511 fsp_str_dbg(fsp), (intmax_t)offset, n);
4514 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4517 if (fio->type == ADOUBLE_META) {
4518 nread = fruit_pread_meta(handle, fsp, data, n, offset);
4520 nread = fruit_pread_rsrc(handle, fsp, data, n, offset);
4523 DBG_DEBUG("Path [%s] nread [%zd]\n", fsp_str_dbg(fsp), nread);
4527 static bool fruit_must_handle_aio_stream(struct fio *fio)
4533 if (fio->type == ADOUBLE_META) {
4537 if ((fio->type == ADOUBLE_RSRC) &&
4538 (fio->config->rsrc == FRUIT_RSRC_ADFILE))
4546 struct fruit_pread_state {
4548 struct vfs_aio_state vfs_aio_state;
4551 static void fruit_pread_done(struct tevent_req *subreq);
4553 static struct tevent_req *fruit_pread_send(
4554 struct vfs_handle_struct *handle,
4555 TALLOC_CTX *mem_ctx,
4556 struct tevent_context *ev,
4557 struct files_struct *fsp,
4559 size_t n, off_t offset)
4561 struct tevent_req *req = NULL;
4562 struct tevent_req *subreq = NULL;
4563 struct fruit_pread_state *state = NULL;
4564 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4566 req = tevent_req_create(mem_ctx, &state,
4567 struct fruit_pread_state);
4572 if (fruit_must_handle_aio_stream(fio)) {
4573 state->nread = SMB_VFS_PREAD(fsp, data, n, offset);
4574 if (state->nread != n) {
4575 if (state->nread != -1) {
4578 tevent_req_error(req, errno);
4579 return tevent_req_post(req, ev);
4581 tevent_req_done(req);
4582 return tevent_req_post(req, ev);
4585 subreq = SMB_VFS_NEXT_PREAD_SEND(state, ev, handle, fsp,
4587 if (tevent_req_nomem(req, subreq)) {
4588 return tevent_req_post(req, ev);
4590 tevent_req_set_callback(subreq, fruit_pread_done, req);
4594 static void fruit_pread_done(struct tevent_req *subreq)
4596 struct tevent_req *req = tevent_req_callback_data(
4597 subreq, struct tevent_req);
4598 struct fruit_pread_state *state = tevent_req_data(
4599 req, struct fruit_pread_state);
4601 state->nread = SMB_VFS_PREAD_RECV(subreq, &state->vfs_aio_state);
4602 TALLOC_FREE(subreq);
4604 if (tevent_req_error(req, state->vfs_aio_state.error)) {
4607 tevent_req_done(req);
4610 static ssize_t fruit_pread_recv(struct tevent_req *req,
4611 struct vfs_aio_state *vfs_aio_state)
4613 struct fruit_pread_state *state = tevent_req_data(
4614 req, struct fruit_pread_state);
4616 if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
4620 *vfs_aio_state = state->vfs_aio_state;
4621 return state->nread;
4624 static ssize_t fruit_pwrite_meta_stream(vfs_handle_struct *handle,
4625 files_struct *fsp, const void *data,
4626 size_t n, off_t offset)
4628 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4634 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4635 fsp_str_dbg(fsp), (intmax_t)offset, n);
4644 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
4646 DBG_ERR("Close [%s] failed: %s\n",
4647 fsp_str_dbg(fsp), strerror(errno));
4652 fd = SMB_VFS_NEXT_OPEN(handle,
4658 DBG_ERR("On-demand create [%s] in write failed: %s\n",
4659 fsp_str_dbg(fsp), strerror(errno));
4663 fio->fake_fd = false;
4666 ai = afpinfo_unpack(talloc_tos(), data);
4671 if (ai_empty_finderinfo(ai)) {
4673 * Writing an all 0 blob to the metadata stream results in the
4674 * stream being removed on a macOS server. This ensures we
4675 * behave the same and it verified by the "delete AFP_AfpInfo by
4676 * writing all 0" test.
4678 ret = SMB_VFS_NEXT_FTRUNCATE(handle, fsp, 0);
4680 DBG_ERR("SMB_VFS_NEXT_FTRUNCATE on [%s] failed\n",
4685 ok = set_delete_on_close(
4688 handle->conn->session_info->security_token,
4689 handle->conn->session_info->unix_token);
4691 DBG_ERR("set_delete_on_close on [%s] failed\n",
4698 nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4699 if (nwritten != n) {
4706 static ssize_t fruit_pwrite_meta_netatalk(vfs_handle_struct *handle,
4707 files_struct *fsp, const void *data,
4708 size_t n, off_t offset)
4710 struct adouble *ad = NULL;
4716 ai = afpinfo_unpack(talloc_tos(), data);
4721 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
4723 ad = ad_init(talloc_tos(), handle, ADOUBLE_META);
4728 p = ad_get_entry(ad, ADEID_FINDERI);
4730 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
4735 memcpy(p, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
4737 ret = ad_fset(handle, ad, fsp);
4739 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
4746 if (!ai_empty_finderinfo(ai)) {
4751 * Writing an all 0 blob to the metadata stream results in the stream
4752 * being removed on a macOS server. This ensures we behave the same and
4753 * it verified by the "delete AFP_AfpInfo by writing all 0" test.
4756 ok = set_delete_on_close(
4759 handle->conn->session_info->security_token,
4760 handle->conn->session_info->unix_token);
4762 DBG_ERR("set_delete_on_close on [%s] failed\n",
4770 static ssize_t fruit_pwrite_meta(vfs_handle_struct *handle,
4771 files_struct *fsp, const void *data,
4772 size_t n, off_t offset)
4774 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4776 uint8_t buf[AFP_INFO_SIZE];
4782 DBG_ERR("Failed to fetch fsp extension");
4791 if (offset != 0 && n < 60) {
4796 cmp = memcmp(data, "AFP", 3);
4802 if (n <= AFP_OFF_FinderInfo) {
4804 * Nothing to do here really, just return
4812 if (to_copy > AFP_INFO_SIZE) {
4813 to_copy = AFP_INFO_SIZE;
4815 memcpy(buf, data, to_copy);
4818 if (to_write != AFP_INFO_SIZE) {
4819 to_write = AFP_INFO_SIZE;
4822 switch (fio->config->meta) {
4823 case FRUIT_META_STREAM:
4824 nwritten = fruit_pwrite_meta_stream(handle,
4831 case FRUIT_META_NETATALK:
4832 nwritten = fruit_pwrite_meta_netatalk(handle,
4840 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
4844 if (nwritten != to_write) {
4849 * Return the requested amount, verified against macOS SMB server
4854 static ssize_t fruit_pwrite_rsrc_stream(vfs_handle_struct *handle,
4855 files_struct *fsp, const void *data,
4856 size_t n, off_t offset)
4858 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4861 static ssize_t fruit_pwrite_rsrc_xattr(vfs_handle_struct *handle,
4862 files_struct *fsp, const void *data,
4863 size_t n, off_t offset)
4865 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4868 static ssize_t fruit_pwrite_rsrc_adouble(vfs_handle_struct *handle,
4869 files_struct *fsp, const void *data,
4870 size_t n, off_t offset)
4872 struct adouble *ad = NULL;
4876 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
4878 DBG_ERR("ad_get [%s] failed\n", fsp_str_dbg(fsp));
4882 nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n,
4883 offset + ad_getentryoff(ad, ADEID_RFORK));
4884 if (nwritten != n) {
4885 DBG_ERR("Short write on [%s] [%zd/%zd]\n",
4886 fsp_str_dbg(fsp), nwritten, n);
4891 if ((n + offset) > ad_getentrylen(ad, ADEID_RFORK)) {
4892 ad_setentrylen(ad, ADEID_RFORK, n + offset);
4893 ret = ad_fset(handle, ad, fsp);
4895 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
4905 static ssize_t fruit_pwrite_rsrc(vfs_handle_struct *handle,
4906 files_struct *fsp, const void *data,
4907 size_t n, off_t offset)
4909 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4913 DBG_ERR("Failed to fetch fsp extension");
4917 switch (fio->config->rsrc) {
4918 case FRUIT_RSRC_STREAM:
4919 nwritten = fruit_pwrite_rsrc_stream(handle, fsp, data, n, offset);
4922 case FRUIT_RSRC_ADFILE:
4923 nwritten = fruit_pwrite_rsrc_adouble(handle, fsp, data, n, offset);
4926 case FRUIT_RSRC_XATTR:
4927 nwritten = fruit_pwrite_rsrc_xattr(handle, fsp, data, n, offset);
4931 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
4938 static ssize_t fruit_pwrite(vfs_handle_struct *handle,
4939 files_struct *fsp, const void *data,
4940 size_t n, off_t offset)
4942 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4945 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4946 fsp_str_dbg(fsp), (intmax_t)offset, n);
4949 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4952 if (fio->type == ADOUBLE_META) {
4953 nwritten = fruit_pwrite_meta(handle, fsp, data, n, offset);
4955 nwritten = fruit_pwrite_rsrc(handle, fsp, data, n, offset);
4958 DBG_DEBUG("Path [%s] nwritten=%zd\n", fsp_str_dbg(fsp), nwritten);
4962 struct fruit_pwrite_state {
4964 struct vfs_aio_state vfs_aio_state;
4967 static void fruit_pwrite_done(struct tevent_req *subreq);
4969 static struct tevent_req *fruit_pwrite_send(
4970 struct vfs_handle_struct *handle,
4971 TALLOC_CTX *mem_ctx,
4972 struct tevent_context *ev,
4973 struct files_struct *fsp,
4975 size_t n, off_t offset)
4977 struct tevent_req *req = NULL;
4978 struct tevent_req *subreq = NULL;
4979 struct fruit_pwrite_state *state = NULL;
4980 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4982 req = tevent_req_create(mem_ctx, &state,
4983 struct fruit_pwrite_state);
4988 if (fruit_must_handle_aio_stream(fio)) {
4989 state->nwritten = SMB_VFS_PWRITE(fsp, data, n, offset);
4990 if (state->nwritten != n) {
4991 if (state->nwritten != -1) {
4994 tevent_req_error(req, errno);
4995 return tevent_req_post(req, ev);
4997 tevent_req_done(req);
4998 return tevent_req_post(req, ev);
5001 subreq = SMB_VFS_NEXT_PWRITE_SEND(state, ev, handle, fsp,
5003 if (tevent_req_nomem(req, subreq)) {
5004 return tevent_req_post(req, ev);
5006 tevent_req_set_callback(subreq, fruit_pwrite_done, req);
5010 static void fruit_pwrite_done(struct tevent_req *subreq)
5012 struct tevent_req *req = tevent_req_callback_data(
5013 subreq, struct tevent_req);
5014 struct fruit_pwrite_state *state = tevent_req_data(
5015 req, struct fruit_pwrite_state);
5017 state->nwritten = SMB_VFS_PWRITE_RECV(subreq, &state->vfs_aio_state);
5018 TALLOC_FREE(subreq);
5020 if (tevent_req_error(req, state->vfs_aio_state.error)) {
5023 tevent_req_done(req);
5026 static ssize_t fruit_pwrite_recv(struct tevent_req *req,
5027 struct vfs_aio_state *vfs_aio_state)
5029 struct fruit_pwrite_state *state = tevent_req_data(
5030 req, struct fruit_pwrite_state);
5032 if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
5036 *vfs_aio_state = state->vfs_aio_state;
5037 return state->nwritten;
5041 * Helper to stat/lstat the base file of an smb_fname.
5043 static int fruit_stat_base(vfs_handle_struct *handle,
5044 struct smb_filename *smb_fname,
5047 char *tmp_stream_name;
5050 tmp_stream_name = smb_fname->stream_name;
5051 smb_fname->stream_name = NULL;
5053 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
5055 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5057 smb_fname->stream_name = tmp_stream_name;
5059 DBG_DEBUG("fruit_stat_base [%s] dev [%ju] ino [%ju]\n",
5060 smb_fname->base_name,
5061 (uintmax_t)smb_fname->st.st_ex_dev,
5062 (uintmax_t)smb_fname->st.st_ex_ino);
5066 static int fruit_stat_meta_stream(vfs_handle_struct *handle,
5067 struct smb_filename *smb_fname,
5073 ret = fruit_stat_base(handle, smb_fname, false);
5078 ino = fruit_inode(&smb_fname->st, smb_fname->stream_name);
5081 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
5083 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5086 smb_fname->st.st_ex_ino = ino;
5091 static int fruit_stat_meta_netatalk(vfs_handle_struct *handle,
5092 struct smb_filename *smb_fname,
5095 struct adouble *ad = NULL;
5097 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5099 DBG_INFO("fruit_stat_meta %s: %s\n",
5100 smb_fname_str_dbg(smb_fname), strerror(errno));
5106 /* Populate the stat struct with info from the base file. */
5107 if (fruit_stat_base(handle, smb_fname, follow_links) == -1) {
5110 smb_fname->st.st_ex_size = AFP_INFO_SIZE;
5111 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5112 smb_fname->stream_name);
5116 static int fruit_stat_meta(vfs_handle_struct *handle,
5117 struct smb_filename *smb_fname,
5120 struct fruit_config_data *config = NULL;
5123 SMB_VFS_HANDLE_GET_DATA(handle, config,
5124 struct fruit_config_data, return -1);
5126 switch (config->meta) {
5127 case FRUIT_META_STREAM:
5128 ret = fruit_stat_meta_stream(handle, smb_fname, follow_links);
5131 case FRUIT_META_NETATALK:
5132 ret = fruit_stat_meta_netatalk(handle, smb_fname, follow_links);
5136 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
5143 static int fruit_stat_rsrc_netatalk(vfs_handle_struct *handle,
5144 struct smb_filename *smb_fname,
5147 struct adouble *ad = NULL;
5150 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
5156 /* Populate the stat struct with info from the base file. */
5157 ret = fruit_stat_base(handle, smb_fname, follow_links);
5163 smb_fname->st.st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
5164 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5165 smb_fname->stream_name);
5170 static int fruit_stat_rsrc_stream(vfs_handle_struct *handle,
5171 struct smb_filename *smb_fname,
5177 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
5179 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5185 static int fruit_stat_rsrc_xattr(vfs_handle_struct *handle,
5186 struct smb_filename *smb_fname,
5189 #ifdef HAVE_ATTROPEN
5193 /* Populate the stat struct with info from the base file. */
5194 ret = fruit_stat_base(handle, smb_fname, follow_links);
5199 fd = attropen(smb_fname->base_name,
5200 AFPRESOURCE_EA_NETATALK,
5206 ret = sys_fstat(fd, &smb_fname->st, false);
5209 DBG_ERR("fstat [%s:%s] failed\n", smb_fname->base_name,
5210 AFPRESOURCE_EA_NETATALK);
5216 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5217 smb_fname->stream_name);
5227 static int fruit_stat_rsrc(vfs_handle_struct *handle,
5228 struct smb_filename *smb_fname,
5231 struct fruit_config_data *config = NULL;
5234 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
5236 SMB_VFS_HANDLE_GET_DATA(handle, config,
5237 struct fruit_config_data, return -1);
5239 switch (config->rsrc) {
5240 case FRUIT_RSRC_STREAM:
5241 ret = fruit_stat_rsrc_stream(handle, smb_fname, follow_links);
5244 case FRUIT_RSRC_XATTR:
5245 ret = fruit_stat_rsrc_xattr(handle, smb_fname, follow_links);
5248 case FRUIT_RSRC_ADFILE:
5249 ret = fruit_stat_rsrc_netatalk(handle, smb_fname, follow_links);
5253 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
5260 static int fruit_stat(vfs_handle_struct *handle,
5261 struct smb_filename *smb_fname)
5265 DEBUG(10, ("fruit_stat called for %s\n",
5266 smb_fname_str_dbg(smb_fname)));
5268 if (!is_ntfs_stream_smb_fname(smb_fname)
5269 || is_ntfs_default_stream_smb_fname(smb_fname)) {
5270 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
5272 update_btime(handle, smb_fname);
5278 * Note if lp_posix_paths() is true, we can never
5279 * get here as is_ntfs_stream_smb_fname() is
5280 * always false. So we never need worry about
5281 * not following links here.
5284 if (is_afpinfo_stream(smb_fname)) {
5285 rc = fruit_stat_meta(handle, smb_fname, true);
5286 } else if (is_afpresource_stream(smb_fname)) {
5287 rc = fruit_stat_rsrc(handle, smb_fname, true);
5289 return SMB_VFS_NEXT_STAT(handle, smb_fname);
5293 update_btime(handle, smb_fname);
5294 smb_fname->st.st_ex_mode &= ~S_IFMT;
5295 smb_fname->st.st_ex_mode |= S_IFREG;
5296 smb_fname->st.st_ex_blocks =
5297 smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
5302 static int fruit_lstat(vfs_handle_struct *handle,
5303 struct smb_filename *smb_fname)
5307 DEBUG(10, ("fruit_lstat called for %s\n",
5308 smb_fname_str_dbg(smb_fname)));
5310 if (!is_ntfs_stream_smb_fname(smb_fname)
5311 || is_ntfs_default_stream_smb_fname(smb_fname)) {
5312 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5314 update_btime(handle, smb_fname);
5319 if (is_afpinfo_stream(smb_fname)) {
5320 rc = fruit_stat_meta(handle, smb_fname, false);
5321 } else if (is_afpresource_stream(smb_fname)) {
5322 rc = fruit_stat_rsrc(handle, smb_fname, false);
5324 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5328 update_btime(handle, smb_fname);
5329 smb_fname->st.st_ex_mode &= ~S_IFMT;
5330 smb_fname->st.st_ex_mode |= S_IFREG;
5331 smb_fname->st.st_ex_blocks =
5332 smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
5337 static int fruit_fstat_meta_stream(vfs_handle_struct *handle,
5339 SMB_STRUCT_STAT *sbuf)
5341 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5342 struct smb_filename smb_fname;
5351 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5356 *sbuf = fsp->base_fsp->fsp_name->st;
5357 sbuf->st_ex_size = AFP_INFO_SIZE;
5358 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5362 smb_fname = (struct smb_filename) {
5363 .base_name = fsp->fsp_name->base_name,
5366 ret = fruit_stat_base(handle, &smb_fname, false);
5370 *sbuf = smb_fname.st;
5372 ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5374 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5379 sbuf->st_ex_ino = ino;
5383 static int fruit_fstat_meta_netatalk(vfs_handle_struct *handle,
5385 SMB_STRUCT_STAT *sbuf)
5389 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5394 *sbuf = fsp->base_fsp->fsp_name->st;
5395 sbuf->st_ex_size = AFP_INFO_SIZE;
5396 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5401 static int fruit_fstat_meta(vfs_handle_struct *handle,
5403 SMB_STRUCT_STAT *sbuf,
5408 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
5410 switch (fio->config->meta) {
5411 case FRUIT_META_STREAM:
5412 ret = fruit_fstat_meta_stream(handle, fsp, sbuf);
5415 case FRUIT_META_NETATALK:
5416 ret = fruit_fstat_meta_netatalk(handle, fsp, sbuf);
5420 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
5424 DBG_DEBUG("Path [%s] ret [%d]\n", fsp_str_dbg(fsp), ret);
5428 static int fruit_fstat_rsrc_xattr(vfs_handle_struct *handle,
5430 SMB_STRUCT_STAT *sbuf)
5432 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5435 static int fruit_fstat_rsrc_stream(vfs_handle_struct *handle,
5437 SMB_STRUCT_STAT *sbuf)
5439 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5442 static int fruit_fstat_rsrc_adouble(vfs_handle_struct *handle,
5444 SMB_STRUCT_STAT *sbuf)
5446 struct adouble *ad = NULL;
5449 /* Populate the stat struct with info from the base file. */
5450 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5455 ad = ad_get(talloc_tos(), handle,
5456 fsp->base_fsp->fsp_name,
5459 DBG_ERR("ad_get [%s] failed [%s]\n",
5460 fsp_str_dbg(fsp), strerror(errno));
5464 *sbuf = fsp->base_fsp->fsp_name->st;
5465 sbuf->st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
5466 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5472 static int fruit_fstat_rsrc(vfs_handle_struct *handle, files_struct *fsp,
5473 SMB_STRUCT_STAT *sbuf, struct fio *fio)
5477 switch (fio->config->rsrc) {
5478 case FRUIT_RSRC_STREAM:
5479 ret = fruit_fstat_rsrc_stream(handle, fsp, sbuf);
5482 case FRUIT_RSRC_ADFILE:
5483 ret = fruit_fstat_rsrc_adouble(handle, fsp, sbuf);
5486 case FRUIT_RSRC_XATTR:
5487 ret = fruit_fstat_rsrc_xattr(handle, fsp, sbuf);
5491 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
5498 static int fruit_fstat(vfs_handle_struct *handle, files_struct *fsp,
5499 SMB_STRUCT_STAT *sbuf)
5501 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5505 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5508 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
5510 if (fio->type == ADOUBLE_META) {
5511 rc = fruit_fstat_meta(handle, fsp, sbuf, fio);
5513 rc = fruit_fstat_rsrc(handle, fsp, sbuf, fio);
5517 sbuf->st_ex_mode &= ~S_IFMT;
5518 sbuf->st_ex_mode |= S_IFREG;
5519 sbuf->st_ex_blocks = sbuf->st_ex_size / STAT_ST_BLOCKSIZE + 1;
5522 DBG_DEBUG("Path [%s] rc [%d] size [%"PRIdMAX"]\n",
5523 fsp_str_dbg(fsp), rc, (intmax_t)sbuf->st_ex_size);
5527 static NTSTATUS delete_invalid_meta_stream(
5528 vfs_handle_struct *handle,
5529 const struct smb_filename *smb_fname,
5530 TALLOC_CTX *mem_ctx,
5531 unsigned int *pnum_streams,
5532 struct stream_struct **pstreams,
5535 struct smb_filename *sname = NULL;
5539 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams, AFPINFO_STREAM);
5541 return NT_STATUS_INTERNAL_ERROR;
5545 return NT_STATUS_OK;
5548 sname = synthetic_smb_fname(talloc_tos(),
5549 smb_fname->base_name,
5550 AFPINFO_STREAM_NAME,
5552 if (sname == NULL) {
5553 return NT_STATUS_NO_MEMORY;
5556 ret = SMB_VFS_NEXT_UNLINK(handle, sname);
5559 DBG_ERR("Removing [%s] failed\n", smb_fname_str_dbg(sname));
5560 return map_nt_error_from_unix(errno);
5563 return NT_STATUS_OK;
5566 static NTSTATUS fruit_streaminfo_meta_stream(
5567 vfs_handle_struct *handle,
5568 struct files_struct *fsp,
5569 const struct smb_filename *smb_fname,
5570 TALLOC_CTX *mem_ctx,
5571 unsigned int *pnum_streams,
5572 struct stream_struct **pstreams)
5574 struct stream_struct *stream = *pstreams;
5575 unsigned int num_streams = *pnum_streams;
5578 for (i = 0; i < num_streams; i++) {
5579 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
5584 if (i == num_streams) {
5585 return NT_STATUS_OK;
5588 if (stream[i].size != AFP_INFO_SIZE) {
5589 DBG_ERR("Removing invalid AFPINFO_STREAM size [%jd] from [%s]\n",
5590 (intmax_t)stream[i].size, smb_fname_str_dbg(smb_fname));
5592 return delete_invalid_meta_stream(handle,
5601 return NT_STATUS_OK;
5604 static NTSTATUS fruit_streaminfo_meta_netatalk(
5605 vfs_handle_struct *handle,
5606 struct files_struct *fsp,
5607 const struct smb_filename *smb_fname,
5608 TALLOC_CTX *mem_ctx,
5609 unsigned int *pnum_streams,
5610 struct stream_struct **pstreams)
5612 struct stream_struct *stream = *pstreams;
5613 unsigned int num_streams = *pnum_streams;
5614 struct adouble *ad = NULL;
5619 /* Remove the Netatalk xattr from the list */
5620 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5621 ":" NETATALK_META_XATTR ":$DATA");
5623 return NT_STATUS_NO_MEMORY;
5627 * Check if there's a AFPINFO_STREAM from the VFS streams
5628 * backend and if yes, remove it from the list
5630 for (i = 0; i < num_streams; i++) {
5631 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
5636 if (i < num_streams) {
5637 DBG_WARNING("Unexpected AFPINFO_STREAM on [%s]\n",
5638 smb_fname_str_dbg(smb_fname));
5640 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5643 return NT_STATUS_INTERNAL_ERROR;
5647 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5649 return NT_STATUS_OK;
5652 is_fi_empty = ad_empty_finderinfo(ad);
5656 return NT_STATUS_OK;
5659 ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
5660 AFPINFO_STREAM_NAME, AFP_INFO_SIZE,
5661 smb_roundup(handle->conn, AFP_INFO_SIZE));
5663 return NT_STATUS_NO_MEMORY;
5666 return NT_STATUS_OK;
5669 static NTSTATUS fruit_streaminfo_meta(vfs_handle_struct *handle,
5670 struct files_struct *fsp,
5671 const struct smb_filename *smb_fname,
5672 TALLOC_CTX *mem_ctx,
5673 unsigned int *pnum_streams,
5674 struct stream_struct **pstreams)
5676 struct fruit_config_data *config = NULL;
5679 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5680 return NT_STATUS_INTERNAL_ERROR);
5682 switch (config->meta) {
5683 case FRUIT_META_NETATALK:
5684 status = fruit_streaminfo_meta_netatalk(handle, fsp, smb_fname,
5685 mem_ctx, pnum_streams,
5689 case FRUIT_META_STREAM:
5690 status = fruit_streaminfo_meta_stream(handle, fsp, smb_fname,
5691 mem_ctx, pnum_streams,
5696 return NT_STATUS_INTERNAL_ERROR;
5702 static NTSTATUS fruit_streaminfo_rsrc_stream(
5703 vfs_handle_struct *handle,
5704 struct files_struct *fsp,
5705 const struct smb_filename *smb_fname,
5706 TALLOC_CTX *mem_ctx,
5707 unsigned int *pnum_streams,
5708 struct stream_struct **pstreams)
5712 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
5714 DBG_ERR("Filtering resource stream failed\n");
5715 return NT_STATUS_INTERNAL_ERROR;
5717 return NT_STATUS_OK;
5720 static NTSTATUS fruit_streaminfo_rsrc_xattr(
5721 vfs_handle_struct *handle,
5722 struct files_struct *fsp,
5723 const struct smb_filename *smb_fname,
5724 TALLOC_CTX *mem_ctx,
5725 unsigned int *pnum_streams,
5726 struct stream_struct **pstreams)
5730 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
5732 DBG_ERR("Filtering resource stream failed\n");
5733 return NT_STATUS_INTERNAL_ERROR;
5735 return NT_STATUS_OK;
5738 static NTSTATUS fruit_streaminfo_rsrc_adouble(
5739 vfs_handle_struct *handle,
5740 struct files_struct *fsp,
5741 const struct smb_filename *smb_fname,
5742 TALLOC_CTX *mem_ctx,
5743 unsigned int *pnum_streams,
5744 struct stream_struct **pstreams)
5746 struct stream_struct *stream = *pstreams;
5747 unsigned int num_streams = *pnum_streams;
5748 struct adouble *ad = NULL;
5754 * Check if there's a AFPRESOURCE_STREAM from the VFS streams backend
5755 * and if yes, remove it from the list
5757 for (i = 0; i < num_streams; i++) {
5758 if (strequal_m(stream[i].name, AFPRESOURCE_STREAM)) {
5763 if (i < num_streams) {
5764 DBG_WARNING("Unexpected AFPRESOURCE_STREAM on [%s]\n",
5765 smb_fname_str_dbg(smb_fname));
5767 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5768 AFPRESOURCE_STREAM);
5770 return NT_STATUS_INTERNAL_ERROR;
5774 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
5776 return NT_STATUS_OK;
5779 rlen = ad_getentrylen(ad, ADEID_RFORK);
5783 return NT_STATUS_OK;
5786 ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
5787 AFPRESOURCE_STREAM_NAME, rlen,
5788 smb_roundup(handle->conn, rlen));
5790 return NT_STATUS_NO_MEMORY;
5793 return NT_STATUS_OK;
5796 static NTSTATUS fruit_streaminfo_rsrc(vfs_handle_struct *handle,
5797 struct files_struct *fsp,
5798 const struct smb_filename *smb_fname,
5799 TALLOC_CTX *mem_ctx,
5800 unsigned int *pnum_streams,
5801 struct stream_struct **pstreams)
5803 struct fruit_config_data *config = NULL;
5806 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5807 return NT_STATUS_INTERNAL_ERROR);
5809 switch (config->rsrc) {
5810 case FRUIT_RSRC_STREAM:
5811 status = fruit_streaminfo_rsrc_stream(handle, fsp, smb_fname,
5812 mem_ctx, pnum_streams,
5816 case FRUIT_RSRC_XATTR:
5817 status = fruit_streaminfo_rsrc_xattr(handle, fsp, smb_fname,
5818 mem_ctx, pnum_streams,
5822 case FRUIT_RSRC_ADFILE:
5823 status = fruit_streaminfo_rsrc_adouble(handle, fsp, smb_fname,
5824 mem_ctx, pnum_streams,
5829 return NT_STATUS_INTERNAL_ERROR;
5835 static void fruit_filter_empty_streams(unsigned int *pnum_streams,
5836 struct stream_struct **pstreams)
5838 unsigned num_streams = *pnum_streams;
5839 struct stream_struct *streams = *pstreams;
5842 if (!global_fruit_config.nego_aapl) {
5846 while (i < num_streams) {
5847 struct smb_filename smb_fname = (struct smb_filename) {
5848 .stream_name = streams[i].name,
5851 if (is_ntfs_default_stream_smb_fname(&smb_fname)
5852 || streams[i].size > 0)
5858 streams[i] = streams[num_streams - 1];
5862 *pnum_streams = num_streams;
5865 static NTSTATUS fruit_streaminfo(vfs_handle_struct *handle,
5866 struct files_struct *fsp,
5867 const struct smb_filename *smb_fname,
5868 TALLOC_CTX *mem_ctx,
5869 unsigned int *pnum_streams,
5870 struct stream_struct **pstreams)
5872 struct fruit_config_data *config = NULL;
5875 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5876 return NT_STATUS_UNSUCCESSFUL);
5878 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
5880 status = SMB_VFS_NEXT_STREAMINFO(handle, fsp, smb_fname, mem_ctx,
5881 pnum_streams, pstreams);
5882 if (!NT_STATUS_IS_OK(status)) {
5886 fruit_filter_empty_streams(pnum_streams, pstreams);
5888 status = fruit_streaminfo_meta(handle, fsp, smb_fname,
5889 mem_ctx, pnum_streams, pstreams);
5890 if (!NT_STATUS_IS_OK(status)) {
5894 status = fruit_streaminfo_rsrc(handle, fsp, smb_fname,
5895 mem_ctx, pnum_streams, pstreams);
5896 if (!NT_STATUS_IS_OK(status)) {
5900 return NT_STATUS_OK;
5903 static int fruit_ntimes(vfs_handle_struct *handle,
5904 const struct smb_filename *smb_fname,
5905 struct smb_file_time *ft)
5908 struct adouble *ad = NULL;
5909 struct fruit_config_data *config = NULL;
5911 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5914 if ((config->meta != FRUIT_META_NETATALK) ||
5915 null_timespec(ft->create_time))
5917 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
5920 DEBUG(10,("set btime for %s to %s\n", smb_fname_str_dbg(smb_fname),
5921 time_to_asc(convert_timespec_to_time_t(ft->create_time))));
5923 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5928 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX,
5929 convert_time_t_to_uint32_t(ft->create_time.tv_sec));
5931 rc = ad_set(handle, ad, smb_fname);
5937 DEBUG(1, ("fruit_ntimes: %s\n", smb_fname_str_dbg(smb_fname)));
5940 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
5943 static int fruit_fallocate(struct vfs_handle_struct *handle,
5944 struct files_struct *fsp,
5949 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5952 return SMB_VFS_NEXT_FALLOCATE(handle, fsp, mode, offset, len);
5955 /* Let the pwrite code path handle it. */
5960 static int fruit_ftruncate_rsrc_xattr(struct vfs_handle_struct *handle,
5961 struct files_struct *fsp,
5964 #ifdef HAVE_ATTROPEN
5965 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
5970 static int fruit_ftruncate_rsrc_adouble(struct vfs_handle_struct *handle,
5971 struct files_struct *fsp,
5975 struct adouble *ad = NULL;
5978 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
5980 DBG_DEBUG("ad_get [%s] failed [%s]\n",
5981 fsp_str_dbg(fsp), strerror(errno));
5985 ad_off = ad_getentryoff(ad, ADEID_RFORK);
5987 rc = ftruncate(fsp->fh->fd, offset + ad_off);
5993 ad_setentrylen(ad, ADEID_RFORK, offset);
5995 rc = ad_fset(handle, ad, fsp);
5997 DBG_ERR("ad_fset [%s] failed [%s]\n",
5998 fsp_str_dbg(fsp), strerror(errno));
6007 static int fruit_ftruncate_rsrc_stream(struct vfs_handle_struct *handle,
6008 struct files_struct *fsp,
6011 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
6014 static int fruit_ftruncate_rsrc(struct vfs_handle_struct *handle,
6015 struct files_struct *fsp,
6018 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
6022 DBG_ERR("Failed to fetch fsp extension");
6026 switch (fio->config->rsrc) {
6027 case FRUIT_RSRC_XATTR:
6028 ret = fruit_ftruncate_rsrc_xattr(handle, fsp, offset);
6031 case FRUIT_RSRC_ADFILE:
6032 ret = fruit_ftruncate_rsrc_adouble(handle, fsp, offset);
6035 case FRUIT_RSRC_STREAM:
6036 ret = fruit_ftruncate_rsrc_stream(handle, fsp, offset);
6040 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
6048 static int fruit_ftruncate_meta(struct vfs_handle_struct *handle,
6049 struct files_struct *fsp,
6053 DBG_WARNING("ftruncate %s to %jd",
6054 fsp_str_dbg(fsp), (intmax_t)offset);
6055 /* OS X returns NT_STATUS_ALLOTTED_SPACE_EXCEEDED */
6060 /* OS X returns success but does nothing */
6061 DBG_INFO("ignoring ftruncate %s to %jd\n",
6062 fsp_str_dbg(fsp), (intmax_t)offset);
6066 static int fruit_ftruncate(struct vfs_handle_struct *handle,
6067 struct files_struct *fsp,
6070 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
6073 DBG_DEBUG("Path [%s] offset [%"PRIdMAX"]\n", fsp_str_dbg(fsp),
6077 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
6080 if (fio->type == ADOUBLE_META) {
6081 ret = fruit_ftruncate_meta(handle, fsp, offset);
6083 ret = fruit_ftruncate_rsrc(handle, fsp, offset);
6086 DBG_DEBUG("Path [%s] result [%d]\n", fsp_str_dbg(fsp), ret);
6090 static NTSTATUS fruit_create_file(vfs_handle_struct *handle,
6091 struct smb_request *req,
6092 uint16_t root_dir_fid,
6093 struct smb_filename *smb_fname,
6094 uint32_t access_mask,
6095 uint32_t share_access,
6096 uint32_t create_disposition,
6097 uint32_t create_options,
6098 uint32_t file_attributes,
6099 uint32_t oplock_request,
6100 struct smb2_lease *lease,
6101 uint64_t allocation_size,
6102 uint32_t private_flags,
6103 struct security_descriptor *sd,
6104 struct ea_list *ea_list,
6105 files_struct **result,
6107 const struct smb2_create_blobs *in_context_blobs,
6108 struct smb2_create_blobs *out_context_blobs)
6111 struct fruit_config_data *config = NULL;
6112 files_struct *fsp = NULL;
6113 struct fio *fio = NULL;
6114 bool internal_open = (oplock_request & INTERNAL_OPEN_ONLY);
6117 status = check_aapl(handle, req, in_context_blobs, out_context_blobs);
6118 if (!NT_STATUS_IS_OK(status)) {
6122 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
6123 return NT_STATUS_UNSUCCESSFUL);
6125 if (is_apple_stream(smb_fname) && !internal_open) {
6126 ret = ad_convert(handle, smb_fname);
6128 DBG_ERR("ad_convert() failed\n");
6129 return NT_STATUS_UNSUCCESSFUL;
6133 status = SMB_VFS_NEXT_CREATE_FILE(
6134 handle, req, root_dir_fid, smb_fname,
6135 access_mask, share_access,
6136 create_disposition, create_options,
6137 file_attributes, oplock_request,
6139 allocation_size, private_flags,
6140 sd, ea_list, result,
6141 pinfo, in_context_blobs, out_context_blobs);
6142 if (!NT_STATUS_IS_OK(status)) {
6148 if (global_fruit_config.nego_aapl) {
6149 if (config->posix_rename && fsp->is_directory) {
6151 * Enable POSIX directory rename behaviour
6153 fsp->posix_flags |= FSP_POSIX_FLAGS_RENAME;
6158 * If this is a plain open for existing files, opening an 0
6159 * byte size resource fork MUST fail with
6160 * NT_STATUS_OBJECT_NAME_NOT_FOUND.
6162 * Cf the vfs_fruit torture tests in test_rfork_create().
6164 if (global_fruit_config.nego_aapl &&
6165 create_disposition == FILE_OPEN &&
6166 smb_fname->st.st_ex_size == 0 &&
6167 is_ntfs_stream_smb_fname(smb_fname) &&
6168 !(is_ntfs_default_stream_smb_fname(smb_fname)))
6170 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
6174 fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
6175 if (fio != NULL && pinfo != NULL && *pinfo == FILE_WAS_CREATED) {
6176 fio->created = true;
6179 if (is_ntfs_stream_smb_fname(smb_fname)
6180 || fsp->is_directory) {
6184 if (config->locking == FRUIT_LOCKING_NETATALK) {
6185 status = fruit_check_access(
6189 if (!NT_STATUS_IS_OK(status)) {
6197 DEBUG(10, ("fruit_create_file: %s\n", nt_errstr(status)));
6200 close_file(req, fsp, ERROR_CLOSE);
6201 *result = fsp = NULL;
6207 static NTSTATUS fruit_readdir_attr(struct vfs_handle_struct *handle,
6208 const struct smb_filename *fname,
6209 TALLOC_CTX *mem_ctx,
6210 struct readdir_attr_data **pattr_data)
6212 struct fruit_config_data *config = NULL;
6213 struct readdir_attr_data *attr_data;
6217 SMB_VFS_HANDLE_GET_DATA(handle, config,
6218 struct fruit_config_data,
6219 return NT_STATUS_UNSUCCESSFUL);
6221 if (!global_fruit_config.nego_aapl) {
6222 return SMB_VFS_NEXT_READDIR_ATTR(handle, fname, mem_ctx, pattr_data);
6225 DEBUG(10, ("fruit_readdir_attr %s\n", fname->base_name));
6227 ret = ad_convert(handle, fname);
6229 DBG_ERR("ad_convert() failed\n");
6230 return NT_STATUS_UNSUCCESSFUL;
6233 *pattr_data = talloc_zero(mem_ctx, struct readdir_attr_data);
6234 if (*pattr_data == NULL) {
6235 return NT_STATUS_UNSUCCESSFUL;
6237 attr_data = *pattr_data;
6238 attr_data->type = RDATTR_AAPL;
6241 * Mac metadata: compressed FinderInfo, resource fork length
6244 status = readdir_attr_macmeta(handle, fname, attr_data);
6245 if (!NT_STATUS_IS_OK(status)) {
6247 * Error handling is tricky: if we return failure from
6248 * this function, the corresponding directory entry
6249 * will to be passed to the client, so we really just
6250 * want to error out on fatal errors.
6252 if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
6260 if (config->unix_info_enabled) {
6261 attr_data->attr_data.aapl.unix_mode = fname->st.st_ex_mode;
6267 if (!config->readdir_attr_max_access) {
6268 attr_data->attr_data.aapl.max_access = FILE_GENERIC_ALL;
6270 status = smbd_calculate_access_mask(
6274 SEC_FLAG_MAXIMUM_ALLOWED,
6275 &attr_data->attr_data.aapl.max_access);
6276 if (!NT_STATUS_IS_OK(status)) {
6281 return NT_STATUS_OK;
6284 DEBUG(1, ("fruit_readdir_attr %s, error: %s\n",
6285 fname->base_name, nt_errstr(status)));
6286 TALLOC_FREE(*pattr_data);
6290 static NTSTATUS fruit_fget_nt_acl(vfs_handle_struct *handle,
6292 uint32_t security_info,
6293 TALLOC_CTX *mem_ctx,
6294 struct security_descriptor **ppdesc)
6297 struct security_ace ace;
6299 struct fruit_config_data *config;
6301 SMB_VFS_HANDLE_GET_DATA(handle, config,
6302 struct fruit_config_data,
6303 return NT_STATUS_UNSUCCESSFUL);
6305 status = SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
6307 if (!NT_STATUS_IS_OK(status)) {
6312 * Add MS NFS style ACEs with uid, gid and mode
6314 if (!global_fruit_config.nego_aapl) {
6315 return NT_STATUS_OK;
6317 if (!config->unix_info_enabled) {
6318 return NT_STATUS_OK;
6321 /* First remove any existing ACE's with NFS style mode/uid/gid SIDs. */
6322 status = remove_virtual_nfs_aces(*ppdesc);
6323 if (!NT_STATUS_IS_OK(status)) {
6324 DBG_WARNING("failed to remove MS NFS style ACEs\n");
6328 /* MS NFS style mode */
6329 sid_compose(&sid, &global_sid_Unix_NFS_Mode, fsp->fsp_name->st.st_ex_mode);
6330 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6331 status = security_descriptor_dacl_add(*ppdesc, &ace);
6332 if (!NT_STATUS_IS_OK(status)) {
6333 DEBUG(1,("failed to add MS NFS style ACE\n"));
6337 /* MS NFS style uid */
6338 sid_compose(&sid, &global_sid_Unix_NFS_Users, fsp->fsp_name->st.st_ex_uid);
6339 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6340 status = security_descriptor_dacl_add(*ppdesc, &ace);
6341 if (!NT_STATUS_IS_OK(status)) {
6342 DEBUG(1,("failed to add MS NFS style ACE\n"));
6346 /* MS NFS style gid */
6347 sid_compose(&sid, &global_sid_Unix_NFS_Groups, fsp->fsp_name->st.st_ex_gid);
6348 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6349 status = security_descriptor_dacl_add(*ppdesc, &ace);
6350 if (!NT_STATUS_IS_OK(status)) {
6351 DEBUG(1,("failed to add MS NFS style ACE\n"));
6355 return NT_STATUS_OK;
6358 static NTSTATUS fruit_fset_nt_acl(vfs_handle_struct *handle,
6360 uint32_t security_info_sent,
6361 const struct security_descriptor *orig_psd)
6365 mode_t ms_nfs_mode = 0;
6367 struct security_descriptor *psd = NULL;
6368 uint32_t orig_num_aces = 0;
6370 if (orig_psd->dacl != NULL) {
6371 orig_num_aces = orig_psd->dacl->num_aces;
6374 psd = security_descriptor_copy(talloc_tos(), orig_psd);
6376 return NT_STATUS_NO_MEMORY;
6379 DBG_DEBUG("fruit_fset_nt_acl: %s\n", fsp_str_dbg(fsp));
6381 status = check_ms_nfs(handle, fsp, psd, &ms_nfs_mode, &do_chmod);
6382 if (!NT_STATUS_IS_OK(status)) {
6383 DEBUG(1, ("fruit_fset_nt_acl: check_ms_nfs failed%s\n", fsp_str_dbg(fsp)));
6389 * If only ms_nfs ACE entries were sent, ensure we set the DACL
6390 * sent/present flags correctly now we've removed them.
6393 if (orig_num_aces != 0) {
6395 * Are there any ACE's left ?
6397 if (psd->dacl->num_aces == 0) {
6398 /* No - clear the DACL sent/present flags. */
6399 security_info_sent &= ~SECINFO_DACL;
6400 psd->type &= ~SEC_DESC_DACL_PRESENT;
6404 status = SMB_VFS_NEXT_FSET_NT_ACL(handle, fsp, security_info_sent, psd);
6405 if (!NT_STATUS_IS_OK(status)) {
6406 DEBUG(1, ("fruit_fset_nt_acl: SMB_VFS_NEXT_FSET_NT_ACL failed%s\n", fsp_str_dbg(fsp)));
6412 if (fsp->fh->fd != -1) {
6413 result = SMB_VFS_FCHMOD(fsp, ms_nfs_mode);
6415 result = SMB_VFS_CHMOD(fsp->conn,
6421 DEBUG(1, ("chmod: %s, result: %d, %04o error %s\n", fsp_str_dbg(fsp),
6422 result, (unsigned)ms_nfs_mode,
6424 status = map_nt_error_from_unix(errno);
6431 return NT_STATUS_OK;
6434 static struct vfs_offload_ctx *fruit_offload_ctx;
6436 struct fruit_offload_read_state {
6437 struct vfs_handle_struct *handle;
6438 struct tevent_context *ev;
6444 static void fruit_offload_read_done(struct tevent_req *subreq);
6446 static struct tevent_req *fruit_offload_read_send(
6447 TALLOC_CTX *mem_ctx,
6448 struct tevent_context *ev,
6449 struct vfs_handle_struct *handle,
6456 struct tevent_req *req = NULL;
6457 struct tevent_req *subreq = NULL;
6458 struct fruit_offload_read_state *state = NULL;
6460 req = tevent_req_create(mem_ctx, &state,
6461 struct fruit_offload_read_state);
6465 *state = (struct fruit_offload_read_state) {
6472 subreq = SMB_VFS_NEXT_OFFLOAD_READ_SEND(mem_ctx, ev, handle, fsp,
6473 fsctl, ttl, offset, to_copy);
6474 if (tevent_req_nomem(subreq, req)) {
6475 return tevent_req_post(req, ev);
6477 tevent_req_set_callback(subreq, fruit_offload_read_done, req);
6481 static void fruit_offload_read_done(struct tevent_req *subreq)
6483 struct tevent_req *req = tevent_req_callback_data(
6484 subreq, struct tevent_req);
6485 struct fruit_offload_read_state *state = tevent_req_data(
6486 req, struct fruit_offload_read_state);
6489 status = SMB_VFS_NEXT_OFFLOAD_READ_RECV(subreq,
6493 TALLOC_FREE(subreq);
6494 if (tevent_req_nterror(req, status)) {
6498 if (state->fsctl != FSCTL_SRV_REQUEST_RESUME_KEY) {
6499 tevent_req_done(req);
6503 status = vfs_offload_token_ctx_init(state->fsp->conn->sconn->client,
6504 &fruit_offload_ctx);
6505 if (tevent_req_nterror(req, status)) {
6509 status = vfs_offload_token_db_store_fsp(fruit_offload_ctx,
6512 if (tevent_req_nterror(req, status)) {
6516 tevent_req_done(req);
6520 static NTSTATUS fruit_offload_read_recv(struct tevent_req *req,
6521 struct vfs_handle_struct *handle,
6522 TALLOC_CTX *mem_ctx,
6525 struct fruit_offload_read_state *state = tevent_req_data(
6526 req, struct fruit_offload_read_state);
6529 if (tevent_req_is_nterror(req, &status)) {
6530 tevent_req_received(req);
6534 token->length = state->token.length;
6535 token->data = talloc_move(mem_ctx, &state->token.data);
6537 tevent_req_received(req);
6538 return NT_STATUS_OK;
6541 struct fruit_offload_write_state {
6542 struct vfs_handle_struct *handle;
6544 struct files_struct *src_fsp;
6545 struct files_struct *dst_fsp;
6549 static void fruit_offload_write_done(struct tevent_req *subreq);
6550 static struct tevent_req *fruit_offload_write_send(struct vfs_handle_struct *handle,
6551 TALLOC_CTX *mem_ctx,
6552 struct tevent_context *ev,
6555 off_t transfer_offset,
6556 struct files_struct *dest_fsp,
6560 struct tevent_req *req, *subreq;
6561 struct fruit_offload_write_state *state;
6563 struct fruit_config_data *config;
6564 off_t src_off = transfer_offset;
6565 files_struct *src_fsp = NULL;
6566 off_t to_copy = num;
6567 bool copyfile_enabled = false;
6569 DEBUG(10,("soff: %ju, doff: %ju, len: %ju\n",
6570 (uintmax_t)src_off, (uintmax_t)dest_off, (uintmax_t)num));
6572 SMB_VFS_HANDLE_GET_DATA(handle, config,
6573 struct fruit_config_data,
6576 req = tevent_req_create(mem_ctx, &state,
6577 struct fruit_offload_write_state);
6581 state->handle = handle;
6582 state->dst_fsp = dest_fsp;
6585 case FSCTL_SRV_COPYCHUNK:
6586 case FSCTL_SRV_COPYCHUNK_WRITE:
6587 copyfile_enabled = config->copyfile_enabled;
6594 * Check if this a OS X copyfile style copychunk request with
6595 * a requested chunk count of 0 that was translated to a
6596 * offload_write_send VFS call overloading the parameters src_off
6597 * = dest_off = num = 0.
6599 if (copyfile_enabled && num == 0 && src_off == 0 && dest_off == 0) {
6600 status = vfs_offload_token_db_fetch_fsp(
6601 fruit_offload_ctx, token, &src_fsp);
6602 if (tevent_req_nterror(req, status)) {
6603 return tevent_req_post(req, ev);
6605 state->src_fsp = src_fsp;
6607 status = vfs_stat_fsp(src_fsp);
6608 if (tevent_req_nterror(req, status)) {
6609 return tevent_req_post(req, ev);
6612 to_copy = src_fsp->fsp_name->st.st_ex_size;
6613 state->is_copyfile = true;
6616 subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle,
6625 if (tevent_req_nomem(subreq, req)) {
6626 return tevent_req_post(req, ev);
6629 tevent_req_set_callback(subreq, fruit_offload_write_done, req);
6633 static void fruit_offload_write_done(struct tevent_req *subreq)
6635 struct tevent_req *req = tevent_req_callback_data(
6636 subreq, struct tevent_req);
6637 struct fruit_offload_write_state *state = tevent_req_data(
6638 req, struct fruit_offload_write_state);
6640 unsigned int num_streams = 0;
6641 struct stream_struct *streams = NULL;
6643 struct smb_filename *src_fname_tmp = NULL;
6644 struct smb_filename *dst_fname_tmp = NULL;
6646 status = SMB_VFS_NEXT_OFFLOAD_WRITE_RECV(state->handle,
6649 TALLOC_FREE(subreq);
6650 if (tevent_req_nterror(req, status)) {
6654 if (!state->is_copyfile) {
6655 tevent_req_done(req);
6660 * Now copy all remaining streams. We know the share supports
6661 * streams, because we're in vfs_fruit. We don't do this async
6662 * because streams are few and small.
6664 status = vfs_streaminfo(state->handle->conn, state->src_fsp,
6665 state->src_fsp->fsp_name,
6666 req, &num_streams, &streams);
6667 if (tevent_req_nterror(req, status)) {
6671 if (num_streams == 1) {
6672 /* There is always one stream, ::$DATA. */
6673 tevent_req_done(req);
6677 for (i = 0; i < num_streams; i++) {
6678 DEBUG(10, ("%s: stream: '%s'/%zu\n",
6679 __func__, streams[i].name, (size_t)streams[i].size));
6681 src_fname_tmp = synthetic_smb_fname(
6683 state->src_fsp->fsp_name->base_name,
6686 state->src_fsp->fsp_name->flags);
6687 if (tevent_req_nomem(src_fname_tmp, req)) {
6691 if (is_ntfs_default_stream_smb_fname(src_fname_tmp)) {
6692 TALLOC_FREE(src_fname_tmp);
6696 dst_fname_tmp = synthetic_smb_fname(
6698 state->dst_fsp->fsp_name->base_name,
6701 state->dst_fsp->fsp_name->flags);
6702 if (tevent_req_nomem(dst_fname_tmp, req)) {
6703 TALLOC_FREE(src_fname_tmp);
6707 status = copy_file(req,
6708 state->handle->conn,
6711 OPENX_FILE_CREATE_IF_NOT_EXIST,
6713 if (!NT_STATUS_IS_OK(status)) {
6714 DEBUG(1, ("%s: copy %s to %s failed: %s\n", __func__,
6715 smb_fname_str_dbg(src_fname_tmp),
6716 smb_fname_str_dbg(dst_fname_tmp),
6717 nt_errstr(status)));
6718 TALLOC_FREE(src_fname_tmp);
6719 TALLOC_FREE(dst_fname_tmp);
6720 tevent_req_nterror(req, status);
6724 TALLOC_FREE(src_fname_tmp);
6725 TALLOC_FREE(dst_fname_tmp);
6728 TALLOC_FREE(streams);
6729 TALLOC_FREE(src_fname_tmp);
6730 TALLOC_FREE(dst_fname_tmp);
6731 tevent_req_done(req);
6734 static NTSTATUS fruit_offload_write_recv(struct vfs_handle_struct *handle,
6735 struct tevent_req *req,
6738 struct fruit_offload_write_state *state = tevent_req_data(
6739 req, struct fruit_offload_write_state);
6742 if (tevent_req_is_nterror(req, &status)) {
6743 DEBUG(1, ("server side copy chunk failed: %s\n",
6744 nt_errstr(status)));
6746 tevent_req_received(req);
6750 *copied = state->copied;
6751 tevent_req_received(req);
6753 return NT_STATUS_OK;
6756 static char *fruit_get_bandsize_line(char **lines, int numlines)
6759 static bool re_initialized = false;
6763 if (!re_initialized) {
6764 ret = regcomp(&re, "^[[:blank:]]*<key>band-size</key>$", 0);
6768 re_initialized = true;
6771 for (i = 0; i < numlines; i++) {
6772 regmatch_t matches[1];
6774 ret = regexec(&re, lines[i], 1, matches, 0);
6777 * Check if the match was on the last line, sa we want
6778 * the subsequent line.
6780 if (i + 1 == numlines) {
6783 return lines[i + 1];
6785 if (ret != REG_NOMATCH) {
6793 static bool fruit_get_bandsize_from_line(char *line, size_t *_band_size)
6796 static bool re_initialized = false;
6797 regmatch_t matches[2];
6802 if (!re_initialized) {
6805 "<integer>\\([[:digit:]]*\\)</integer>$",
6810 re_initialized = true;
6813 ret = regexec(&re, line, 2, matches, 0);
6815 DBG_ERR("regex failed [%s]\n", line);
6819 line[matches[1].rm_eo] = '\0';
6821 ok = conv_str_u64(&line[matches[1].rm_so], &band_size);
6825 *_band_size = (size_t)band_size;
6830 * This reads and parses an Info.plist from a TM sparsebundle looking for the
6831 * "band-size" key and value.
6833 static bool fruit_get_bandsize(vfs_handle_struct *handle,
6837 #define INFO_PLIST_MAX_SIZE 64*1024
6839 struct smb_filename *smb_fname = NULL;
6840 files_struct *fsp = NULL;
6841 uint8_t *file_data = NULL;
6842 char **lines = NULL;
6843 char *band_size_line = NULL;
6844 size_t plist_file_size;
6851 plist = talloc_asprintf(talloc_tos(),
6853 handle->conn->connectpath,
6855 if (plist == NULL) {
6860 smb_fname = synthetic_smb_fname(talloc_tos(), plist, NULL, NULL, 0);
6861 if (smb_fname == NULL) {
6866 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
6868 DBG_INFO("Ignoring Sparsebundle without Info.plist [%s]\n", dir);
6873 plist_file_size = smb_fname->st.st_ex_size;
6875 if (plist_file_size > INFO_PLIST_MAX_SIZE) {
6876 DBG_INFO("%s is too large, ignoring\n", plist);
6881 status = SMB_VFS_NEXT_CREATE_FILE(
6884 0, /* root_dir_fid */
6885 smb_fname, /* fname */
6886 FILE_GENERIC_READ, /* access_mask */
6887 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
6888 FILE_OPEN, /* create_disposition */
6889 0, /* create_options */
6890 0, /* file_attributes */
6891 INTERNAL_OPEN_ONLY, /* oplock_request */
6893 0, /* allocation_size */
6894 0, /* private_flags */
6899 NULL, NULL); /* create context */
6900 if (!NT_STATUS_IS_OK(status)) {
6901 DBG_INFO("Opening [%s] failed [%s]\n",
6902 smb_fname_str_dbg(smb_fname), nt_errstr(status));
6907 file_data = talloc_array(talloc_tos(), uint8_t, plist_file_size);
6908 if (file_data == NULL) {
6913 nread = SMB_VFS_NEXT_PREAD(handle, fsp, file_data, plist_file_size, 0);
6914 if (nread != plist_file_size) {
6915 DBG_ERR("Short read on [%s]: %zu/%zd\n",
6916 fsp_str_dbg(fsp), nread, plist_file_size);
6922 status = close_file(NULL, fsp, NORMAL_CLOSE);
6924 if (!NT_STATUS_IS_OK(status)) {
6925 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
6930 lines = file_lines_parse((char *)file_data,
6934 if (lines == NULL) {
6939 band_size_line = fruit_get_bandsize_line(lines, numlines);
6940 if (band_size_line == NULL) {
6941 DBG_ERR("Didn't find band-size key in [%s]\n",
6942 smb_fname_str_dbg(smb_fname));
6947 ok = fruit_get_bandsize_from_line(band_size_line, band_size);
6949 DBG_ERR("fruit_get_bandsize_from_line failed\n");
6953 DBG_DEBUG("Parsed band-size [%zu] for [%s]\n", *band_size, plist);
6957 status = close_file(NULL, fsp, NORMAL_CLOSE);
6958 if (!NT_STATUS_IS_OK(status)) {
6959 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
6964 TALLOC_FREE(smb_fname);
6965 TALLOC_FREE(file_data);
6970 struct fruit_disk_free_state {
6974 static bool fruit_get_num_bands(vfs_handle_struct *handle,
6979 struct smb_filename *bands_dir = NULL;
6981 struct dirent *e = NULL;
6985 path = talloc_asprintf(talloc_tos(),
6987 handle->conn->connectpath,
6993 bands_dir = synthetic_smb_fname(talloc_tos(),
6999 if (bands_dir == NULL) {
7003 d = SMB_VFS_NEXT_OPENDIR(handle, bands_dir, NULL, 0);
7005 TALLOC_FREE(bands_dir);
7011 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
7013 e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
7015 if (ISDOT(e->d_name) || ISDOTDOT(e->d_name)) {
7021 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
7023 TALLOC_FREE(bands_dir);
7027 DBG_DEBUG("%zu bands in [%s]\n", nbands, smb_fname_str_dbg(bands_dir));
7029 TALLOC_FREE(bands_dir);
7035 static bool fruit_tmsize_do_dirent(vfs_handle_struct *handle,
7036 struct fruit_disk_free_state *state,
7041 size_t sparsebundle_strlen = strlen("sparsebundle");
7042 size_t bandsize = 0;
7046 p = strstr(e->d_name, "sparsebundle");
7051 if (p[sparsebundle_strlen] != '\0') {
7055 DBG_DEBUG("Processing sparsebundle [%s]\n", e->d_name);
7057 ok = fruit_get_bandsize(handle, e->d_name, &bandsize);
7060 * Beware of race conditions: this may be an uninitialized
7061 * Info.plist that a client is just creating. We don't want let
7062 * this to trigger complete failure.
7064 DBG_ERR("Processing sparsebundle [%s] failed\n", e->d_name);
7068 ok = fruit_get_num_bands(handle, e->d_name, &nbands);
7071 * Beware of race conditions: this may be a backup sparsebundle
7072 * in an early stage lacking a bands subdirectory. We don't want
7073 * let this to trigger complete failure.
7075 DBG_ERR("Processing sparsebundle [%s] failed\n", e->d_name);
7079 if (bandsize > SIZE_MAX/nbands) {
7080 DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
7084 tm_size = bandsize * nbands;
7086 if (state->total_size + tm_size < state->total_size) {
7087 DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
7092 state->total_size += tm_size;
7094 DBG_DEBUG("[%s] tm_size [%jd] total_size [%jd]\n",
7095 e->d_name, (intmax_t)tm_size, (intmax_t)state->total_size);
7101 * Calculate used size of a TimeMachine volume
7103 * This assumes that the volume is used only for TimeMachine.
7105 * - readdir(basedir of share), then
7106 * - for every element that matches regex "^\(.*\)\.sparsebundle$" :
7107 * - parse "\1.sparsebundle/Info.plist" and read the band-size XML key
7108 * - count band files in "\1.sparsebundle/bands/"
7109 * - calculate used size of all bands: band_count * band_size
7111 static uint64_t fruit_disk_free(vfs_handle_struct *handle,
7112 const struct smb_filename *smb_fname,
7117 struct fruit_config_data *config = NULL;
7118 struct fruit_disk_free_state state = {0};
7120 struct dirent *e = NULL;
7126 SMB_VFS_HANDLE_GET_DATA(handle, config,
7127 struct fruit_config_data,
7130 if (!config->time_machine ||
7131 config->time_machine_max_size == 0)
7133 return SMB_VFS_NEXT_DISK_FREE(handle,
7140 d = SMB_VFS_NEXT_OPENDIR(handle, smb_fname, NULL, 0);
7145 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
7147 e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
7149 ok = fruit_tmsize_do_dirent(handle, &state, e);
7151 SMB_VFS_NEXT_CLOSEDIR(handle, d);
7156 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
7161 dsize = config->time_machine_max_size / 512;
7162 dfree = dsize - (state.total_size / 512);
7163 if (dfree > dsize) {
7173 static struct vfs_fn_pointers vfs_fruit_fns = {
7174 .connect_fn = fruit_connect,
7175 .disk_free_fn = fruit_disk_free,
7177 /* File operations */
7178 .chmod_fn = fruit_chmod,
7179 .chown_fn = fruit_chown,
7180 .unlink_fn = fruit_unlink,
7181 .rename_fn = fruit_rename,
7182 .rmdir_fn = fruit_rmdir,
7183 .open_fn = fruit_open,
7184 .close_fn = fruit_close,
7185 .pread_fn = fruit_pread,
7186 .pwrite_fn = fruit_pwrite,
7187 .pread_send_fn = fruit_pread_send,
7188 .pread_recv_fn = fruit_pread_recv,
7189 .pwrite_send_fn = fruit_pwrite_send,
7190 .pwrite_recv_fn = fruit_pwrite_recv,
7191 .stat_fn = fruit_stat,
7192 .lstat_fn = fruit_lstat,
7193 .fstat_fn = fruit_fstat,
7194 .streaminfo_fn = fruit_streaminfo,
7195 .ntimes_fn = fruit_ntimes,
7196 .ftruncate_fn = fruit_ftruncate,
7197 .fallocate_fn = fruit_fallocate,
7198 .create_file_fn = fruit_create_file,
7199 .readdir_attr_fn = fruit_readdir_attr,
7200 .offload_read_send_fn = fruit_offload_read_send,
7201 .offload_read_recv_fn = fruit_offload_read_recv,
7202 .offload_write_send_fn = fruit_offload_write_send,
7203 .offload_write_recv_fn = fruit_offload_write_recv,
7205 /* NT ACL operations */
7206 .fget_nt_acl_fn = fruit_fget_nt_acl,
7207 .fset_nt_acl_fn = fruit_fset_nt_acl,
7211 NTSTATUS vfs_fruit_init(TALLOC_CTX *ctx)
7213 NTSTATUS ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "fruit",
7215 if (!NT_STATUS_IS_OK(ret)) {
7219 vfs_fruit_debug_level = debug_add_class("fruit");
7220 if (vfs_fruit_debug_level == -1) {
7221 vfs_fruit_debug_level = DBGC_VFS;
7222 DEBUG(0, ("%s: Couldn't register custom debugging class!\n",
7225 DEBUG(10, ("%s: Debug class number of '%s': %d\n",
7226 "vfs_fruit_init","fruit",vfs_fruit_debug_level));