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 } else if (is_ntfs_stream_smb_fname(smb_fname)) {
4106 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4107 } else if (is_adouble_file(smb_fname->base_name)) {
4108 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4112 * A request to delete the base file. Because 0 byte resource
4113 * fork streams are not listed by fruit_streaminfo,
4114 * delete_all_streams() can't remove 0 byte resource fork
4115 * streams, so we have to cleanup this here.
4117 rsrc_smb_fname = synthetic_smb_fname(talloc_tos(),
4118 smb_fname->base_name,
4119 AFPRESOURCE_STREAM_NAME,
4122 if (rsrc_smb_fname == NULL) {
4126 rc = fruit_unlink_rsrc(handle, rsrc_smb_fname, true);
4127 if ((rc != 0) && (errno != ENOENT)) {
4128 DBG_ERR("Forced unlink of [%s] failed [%s]\n",
4129 smb_fname_str_dbg(rsrc_smb_fname), strerror(errno));
4130 TALLOC_FREE(rsrc_smb_fname);
4133 TALLOC_FREE(rsrc_smb_fname);
4135 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4138 static int fruit_chmod(vfs_handle_struct *handle,
4139 const struct smb_filename *smb_fname,
4143 struct fruit_config_data *config = NULL;
4144 struct smb_filename *smb_fname_adp = NULL;
4146 rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname, mode);
4151 SMB_VFS_HANDLE_GET_DATA(handle, config,
4152 struct fruit_config_data, return -1);
4154 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4158 if (!VALID_STAT(smb_fname->st)) {
4162 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
4166 rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_adp);
4171 DEBUG(10, ("fruit_chmod: %s\n", smb_fname_adp->base_name));
4173 rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname_adp, mode);
4174 if (errno == ENOENT) {
4178 TALLOC_FREE(smb_fname_adp);
4182 static int fruit_chown(vfs_handle_struct *handle,
4183 const struct smb_filename *smb_fname,
4188 struct fruit_config_data *config = NULL;
4189 struct smb_filename *adp_smb_fname = NULL;
4191 rc = SMB_VFS_NEXT_CHOWN(handle, smb_fname, uid, gid);
4196 SMB_VFS_HANDLE_GET_DATA(handle, config,
4197 struct fruit_config_data, return -1);
4199 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4203 if (!VALID_STAT(smb_fname->st)) {
4207 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
4211 rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
4216 DEBUG(10, ("fruit_chown: %s\n", adp_smb_fname->base_name));
4218 rc = SMB_VFS_NEXT_CHOWN(handle, adp_smb_fname, uid, gid);
4219 if (errno == ENOENT) {
4224 TALLOC_FREE(adp_smb_fname);
4228 static int fruit_rmdir(struct vfs_handle_struct *handle,
4229 const struct smb_filename *smb_fname)
4233 struct fruit_config_data *config;
4235 SMB_VFS_HANDLE_GET_DATA(handle, config,
4236 struct fruit_config_data, return -1);
4238 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4243 * Due to there is no way to change bDeleteVetoFiles variable
4244 * from this module, need to clean up ourselves
4247 dh = SMB_VFS_OPENDIR(handle->conn, smb_fname, NULL, 0);
4252 while ((de = SMB_VFS_READDIR(handle->conn, dh, NULL)) != NULL) {
4253 struct adouble *ad = NULL;
4255 struct smb_filename *ad_smb_fname = NULL;
4258 if (!is_adouble_file(de->d_name)) {
4262 p = talloc_asprintf(talloc_tos(), "%s/%s",
4263 smb_fname->base_name, de->d_name);
4265 DBG_ERR("talloc_asprintf failed\n");
4269 ad_smb_fname = synthetic_smb_fname(talloc_tos(), p,
4273 if (ad_smb_fname == NULL) {
4274 DBG_ERR("synthetic_smb_fname failed\n");
4279 * Check whether it's a valid AppleDouble file, if
4280 * yes, delete it, ignore it otherwise.
4282 ad = ad_get(talloc_tos(), handle, ad_smb_fname, ADOUBLE_RSRC);
4284 TALLOC_FREE(ad_smb_fname);
4290 ret = SMB_VFS_NEXT_UNLINK(handle, ad_smb_fname);
4292 DBG_ERR("Deleting [%s] failed\n",
4293 smb_fname_str_dbg(ad_smb_fname));
4295 TALLOC_FREE(ad_smb_fname);
4300 SMB_VFS_CLOSEDIR(handle->conn, dh);
4302 return SMB_VFS_NEXT_RMDIR(handle, smb_fname);
4305 static ssize_t fruit_pread_meta_stream(vfs_handle_struct *handle,
4306 files_struct *fsp, void *data,
4307 size_t n, off_t offset)
4312 nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4313 if (nread == -1 || nread == n) {
4317 DBG_ERR("Removing [%s] after short read [%zd]\n",
4318 fsp_str_dbg(fsp), nread);
4320 ret = SMB_VFS_NEXT_UNLINK(handle, fsp->fsp_name);
4322 DBG_ERR("Removing [%s] failed\n", fsp_str_dbg(fsp));
4330 static ssize_t fruit_pread_meta_adouble(vfs_handle_struct *handle,
4331 files_struct *fsp, void *data,
4332 size_t n, off_t offset)
4335 struct adouble *ad = NULL;
4336 char afpinfo_buf[AFP_INFO_SIZE];
4340 ai = afpinfo_new(talloc_tos());
4345 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
4351 p = ad_get_entry(ad, ADEID_FINDERI);
4353 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
4358 memcpy(&ai->afpi_FinderInfo[0], p, ADEDLEN_FINDERI);
4360 nread = afpinfo_pack(ai, afpinfo_buf);
4361 if (nread != AFP_INFO_SIZE) {
4366 memcpy(data, afpinfo_buf, n);
4374 static ssize_t fruit_pread_meta(vfs_handle_struct *handle,
4375 files_struct *fsp, void *data,
4376 size_t n, off_t offset)
4378 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4383 * OS X has a off-by-1 error in the offset calculation, so we're
4384 * bug compatible here. It won't hurt, as any relevant real
4385 * world read requests from the AFP_AfpInfo stream will be
4386 * offset=0 n=60. offset is ignored anyway, see below.
4388 if ((offset < 0) || (offset >= AFP_INFO_SIZE + 1)) {
4393 DBG_ERR("Failed to fetch fsp extension");
4397 /* Yes, macOS always reads from offset 0 */
4399 to_return = MIN(n, AFP_INFO_SIZE);
4401 switch (fio->config->meta) {
4402 case FRUIT_META_STREAM:
4403 nread = fruit_pread_meta_stream(handle, fsp, data,
4407 case FRUIT_META_NETATALK:
4408 nread = fruit_pread_meta_adouble(handle, fsp, data,
4413 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
4417 if (nread == -1 && fio->created) {
4419 char afpinfo_buf[AFP_INFO_SIZE];
4421 ai = afpinfo_new(talloc_tos());
4426 nread = afpinfo_pack(ai, afpinfo_buf);
4428 if (nread != AFP_INFO_SIZE) {
4432 memcpy(data, afpinfo_buf, to_return);
4439 static ssize_t fruit_pread_rsrc_stream(vfs_handle_struct *handle,
4440 files_struct *fsp, void *data,
4441 size_t n, off_t offset)
4443 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4446 static ssize_t fruit_pread_rsrc_xattr(vfs_handle_struct *handle,
4447 files_struct *fsp, void *data,
4448 size_t n, off_t offset)
4450 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4453 static ssize_t fruit_pread_rsrc_adouble(vfs_handle_struct *handle,
4454 files_struct *fsp, void *data,
4455 size_t n, off_t offset)
4457 struct adouble *ad = NULL;
4460 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
4465 nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n,
4466 offset + ad_getentryoff(ad, ADEID_RFORK));
4472 static ssize_t fruit_pread_rsrc(vfs_handle_struct *handle,
4473 files_struct *fsp, void *data,
4474 size_t n, off_t offset)
4476 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4484 switch (fio->config->rsrc) {
4485 case FRUIT_RSRC_STREAM:
4486 nread = fruit_pread_rsrc_stream(handle, fsp, data, n, offset);
4489 case FRUIT_RSRC_ADFILE:
4490 nread = fruit_pread_rsrc_adouble(handle, fsp, data, n, offset);
4493 case FRUIT_RSRC_XATTR:
4494 nread = fruit_pread_rsrc_xattr(handle, fsp, data, n, offset);
4498 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
4505 static ssize_t fruit_pread(vfs_handle_struct *handle,
4506 files_struct *fsp, void *data,
4507 size_t n, off_t offset)
4509 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4512 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4513 fsp_str_dbg(fsp), (intmax_t)offset, n);
4516 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4519 if (fio->type == ADOUBLE_META) {
4520 nread = fruit_pread_meta(handle, fsp, data, n, offset);
4522 nread = fruit_pread_rsrc(handle, fsp, data, n, offset);
4525 DBG_DEBUG("Path [%s] nread [%zd]\n", fsp_str_dbg(fsp), nread);
4529 static bool fruit_must_handle_aio_stream(struct fio *fio)
4535 if (fio->type == ADOUBLE_META) {
4539 if ((fio->type == ADOUBLE_RSRC) &&
4540 (fio->config->rsrc == FRUIT_RSRC_ADFILE))
4548 struct fruit_pread_state {
4550 struct vfs_aio_state vfs_aio_state;
4553 static void fruit_pread_done(struct tevent_req *subreq);
4555 static struct tevent_req *fruit_pread_send(
4556 struct vfs_handle_struct *handle,
4557 TALLOC_CTX *mem_ctx,
4558 struct tevent_context *ev,
4559 struct files_struct *fsp,
4561 size_t n, off_t offset)
4563 struct tevent_req *req = NULL;
4564 struct tevent_req *subreq = NULL;
4565 struct fruit_pread_state *state = NULL;
4566 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4568 req = tevent_req_create(mem_ctx, &state,
4569 struct fruit_pread_state);
4574 if (fruit_must_handle_aio_stream(fio)) {
4575 state->nread = SMB_VFS_PREAD(fsp, data, n, offset);
4576 if (state->nread != n) {
4577 if (state->nread != -1) {
4580 tevent_req_error(req, errno);
4581 return tevent_req_post(req, ev);
4583 tevent_req_done(req);
4584 return tevent_req_post(req, ev);
4587 subreq = SMB_VFS_NEXT_PREAD_SEND(state, ev, handle, fsp,
4589 if (tevent_req_nomem(req, subreq)) {
4590 return tevent_req_post(req, ev);
4592 tevent_req_set_callback(subreq, fruit_pread_done, req);
4596 static void fruit_pread_done(struct tevent_req *subreq)
4598 struct tevent_req *req = tevent_req_callback_data(
4599 subreq, struct tevent_req);
4600 struct fruit_pread_state *state = tevent_req_data(
4601 req, struct fruit_pread_state);
4603 state->nread = SMB_VFS_PREAD_RECV(subreq, &state->vfs_aio_state);
4604 TALLOC_FREE(subreq);
4606 if (tevent_req_error(req, state->vfs_aio_state.error)) {
4609 tevent_req_done(req);
4612 static ssize_t fruit_pread_recv(struct tevent_req *req,
4613 struct vfs_aio_state *vfs_aio_state)
4615 struct fruit_pread_state *state = tevent_req_data(
4616 req, struct fruit_pread_state);
4618 if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
4622 *vfs_aio_state = state->vfs_aio_state;
4623 return state->nread;
4626 static ssize_t fruit_pwrite_meta_stream(vfs_handle_struct *handle,
4627 files_struct *fsp, const void *data,
4628 size_t n, off_t offset)
4630 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4636 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4637 fsp_str_dbg(fsp), (intmax_t)offset, n);
4646 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
4648 DBG_ERR("Close [%s] failed: %s\n",
4649 fsp_str_dbg(fsp), strerror(errno));
4654 fd = SMB_VFS_NEXT_OPEN(handle,
4660 DBG_ERR("On-demand create [%s] in write failed: %s\n",
4661 fsp_str_dbg(fsp), strerror(errno));
4665 fio->fake_fd = false;
4668 ai = afpinfo_unpack(talloc_tos(), data);
4673 if (ai_empty_finderinfo(ai)) {
4675 * Writing an all 0 blob to the metadata stream results in the
4676 * stream being removed on a macOS server. This ensures we
4677 * behave the same and it verified by the "delete AFP_AfpInfo by
4678 * writing all 0" test.
4680 ret = SMB_VFS_NEXT_FTRUNCATE(handle, fsp, 0);
4682 DBG_ERR("SMB_VFS_NEXT_FTRUNCATE on [%s] failed\n",
4687 ok = set_delete_on_close(
4690 handle->conn->session_info->security_token,
4691 handle->conn->session_info->unix_token);
4693 DBG_ERR("set_delete_on_close on [%s] failed\n",
4700 nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4701 if (nwritten != n) {
4708 static ssize_t fruit_pwrite_meta_netatalk(vfs_handle_struct *handle,
4709 files_struct *fsp, const void *data,
4710 size_t n, off_t offset)
4712 struct adouble *ad = NULL;
4718 ai = afpinfo_unpack(talloc_tos(), data);
4723 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
4725 ad = ad_init(talloc_tos(), handle, ADOUBLE_META);
4730 p = ad_get_entry(ad, ADEID_FINDERI);
4732 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
4737 memcpy(p, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
4739 ret = ad_fset(handle, ad, fsp);
4741 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
4748 if (!ai_empty_finderinfo(ai)) {
4753 * Writing an all 0 blob to the metadata stream results in the stream
4754 * being removed on a macOS server. This ensures we behave the same and
4755 * it verified by the "delete AFP_AfpInfo by writing all 0" test.
4758 ok = set_delete_on_close(
4761 handle->conn->session_info->security_token,
4762 handle->conn->session_info->unix_token);
4764 DBG_ERR("set_delete_on_close on [%s] failed\n",
4772 static ssize_t fruit_pwrite_meta(vfs_handle_struct *handle,
4773 files_struct *fsp, const void *data,
4774 size_t n, off_t offset)
4776 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4778 uint8_t buf[AFP_INFO_SIZE];
4784 DBG_ERR("Failed to fetch fsp extension");
4793 if (offset != 0 && n < 60) {
4798 cmp = memcmp(data, "AFP", 3);
4804 if (n <= AFP_OFF_FinderInfo) {
4806 * Nothing to do here really, just return
4814 if (to_copy > AFP_INFO_SIZE) {
4815 to_copy = AFP_INFO_SIZE;
4817 memcpy(buf, data, to_copy);
4820 if (to_write != AFP_INFO_SIZE) {
4821 to_write = AFP_INFO_SIZE;
4824 switch (fio->config->meta) {
4825 case FRUIT_META_STREAM:
4826 nwritten = fruit_pwrite_meta_stream(handle,
4833 case FRUIT_META_NETATALK:
4834 nwritten = fruit_pwrite_meta_netatalk(handle,
4842 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
4846 if (nwritten != to_write) {
4851 * Return the requested amount, verified against macOS SMB server
4856 static ssize_t fruit_pwrite_rsrc_stream(vfs_handle_struct *handle,
4857 files_struct *fsp, const void *data,
4858 size_t n, off_t offset)
4860 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4863 static ssize_t fruit_pwrite_rsrc_xattr(vfs_handle_struct *handle,
4864 files_struct *fsp, const void *data,
4865 size_t n, off_t offset)
4867 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4870 static ssize_t fruit_pwrite_rsrc_adouble(vfs_handle_struct *handle,
4871 files_struct *fsp, const void *data,
4872 size_t n, off_t offset)
4874 struct adouble *ad = NULL;
4878 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
4880 DBG_ERR("ad_get [%s] failed\n", fsp_str_dbg(fsp));
4884 nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n,
4885 offset + ad_getentryoff(ad, ADEID_RFORK));
4886 if (nwritten != n) {
4887 DBG_ERR("Short write on [%s] [%zd/%zd]\n",
4888 fsp_str_dbg(fsp), nwritten, n);
4893 if ((n + offset) > ad_getentrylen(ad, ADEID_RFORK)) {
4894 ad_setentrylen(ad, ADEID_RFORK, n + offset);
4895 ret = ad_fset(handle, ad, fsp);
4897 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
4907 static ssize_t fruit_pwrite_rsrc(vfs_handle_struct *handle,
4908 files_struct *fsp, const void *data,
4909 size_t n, off_t offset)
4911 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4915 DBG_ERR("Failed to fetch fsp extension");
4919 switch (fio->config->rsrc) {
4920 case FRUIT_RSRC_STREAM:
4921 nwritten = fruit_pwrite_rsrc_stream(handle, fsp, data, n, offset);
4924 case FRUIT_RSRC_ADFILE:
4925 nwritten = fruit_pwrite_rsrc_adouble(handle, fsp, data, n, offset);
4928 case FRUIT_RSRC_XATTR:
4929 nwritten = fruit_pwrite_rsrc_xattr(handle, fsp, data, n, offset);
4933 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
4940 static ssize_t fruit_pwrite(vfs_handle_struct *handle,
4941 files_struct *fsp, const void *data,
4942 size_t n, off_t offset)
4944 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4947 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4948 fsp_str_dbg(fsp), (intmax_t)offset, n);
4951 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4954 if (fio->type == ADOUBLE_META) {
4955 nwritten = fruit_pwrite_meta(handle, fsp, data, n, offset);
4957 nwritten = fruit_pwrite_rsrc(handle, fsp, data, n, offset);
4960 DBG_DEBUG("Path [%s] nwritten=%zd\n", fsp_str_dbg(fsp), nwritten);
4964 struct fruit_pwrite_state {
4966 struct vfs_aio_state vfs_aio_state;
4969 static void fruit_pwrite_done(struct tevent_req *subreq);
4971 static struct tevent_req *fruit_pwrite_send(
4972 struct vfs_handle_struct *handle,
4973 TALLOC_CTX *mem_ctx,
4974 struct tevent_context *ev,
4975 struct files_struct *fsp,
4977 size_t n, off_t offset)
4979 struct tevent_req *req = NULL;
4980 struct tevent_req *subreq = NULL;
4981 struct fruit_pwrite_state *state = NULL;
4982 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4984 req = tevent_req_create(mem_ctx, &state,
4985 struct fruit_pwrite_state);
4990 if (fruit_must_handle_aio_stream(fio)) {
4991 state->nwritten = SMB_VFS_PWRITE(fsp, data, n, offset);
4992 if (state->nwritten != n) {
4993 if (state->nwritten != -1) {
4996 tevent_req_error(req, errno);
4997 return tevent_req_post(req, ev);
4999 tevent_req_done(req);
5000 return tevent_req_post(req, ev);
5003 subreq = SMB_VFS_NEXT_PWRITE_SEND(state, ev, handle, fsp,
5005 if (tevent_req_nomem(req, subreq)) {
5006 return tevent_req_post(req, ev);
5008 tevent_req_set_callback(subreq, fruit_pwrite_done, req);
5012 static void fruit_pwrite_done(struct tevent_req *subreq)
5014 struct tevent_req *req = tevent_req_callback_data(
5015 subreq, struct tevent_req);
5016 struct fruit_pwrite_state *state = tevent_req_data(
5017 req, struct fruit_pwrite_state);
5019 state->nwritten = SMB_VFS_PWRITE_RECV(subreq, &state->vfs_aio_state);
5020 TALLOC_FREE(subreq);
5022 if (tevent_req_error(req, state->vfs_aio_state.error)) {
5025 tevent_req_done(req);
5028 static ssize_t fruit_pwrite_recv(struct tevent_req *req,
5029 struct vfs_aio_state *vfs_aio_state)
5031 struct fruit_pwrite_state *state = tevent_req_data(
5032 req, struct fruit_pwrite_state);
5034 if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
5038 *vfs_aio_state = state->vfs_aio_state;
5039 return state->nwritten;
5043 * Helper to stat/lstat the base file of an smb_fname.
5045 static int fruit_stat_base(vfs_handle_struct *handle,
5046 struct smb_filename *smb_fname,
5049 char *tmp_stream_name;
5052 tmp_stream_name = smb_fname->stream_name;
5053 smb_fname->stream_name = NULL;
5055 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
5057 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5059 smb_fname->stream_name = tmp_stream_name;
5061 DBG_DEBUG("fruit_stat_base [%s] dev [%ju] ino [%ju]\n",
5062 smb_fname->base_name,
5063 (uintmax_t)smb_fname->st.st_ex_dev,
5064 (uintmax_t)smb_fname->st.st_ex_ino);
5068 static int fruit_stat_meta_stream(vfs_handle_struct *handle,
5069 struct smb_filename *smb_fname,
5075 ret = fruit_stat_base(handle, smb_fname, false);
5080 ino = fruit_inode(&smb_fname->st, smb_fname->stream_name);
5083 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
5085 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5088 smb_fname->st.st_ex_ino = ino;
5093 static int fruit_stat_meta_netatalk(vfs_handle_struct *handle,
5094 struct smb_filename *smb_fname,
5097 struct adouble *ad = NULL;
5099 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5101 DBG_INFO("fruit_stat_meta %s: %s\n",
5102 smb_fname_str_dbg(smb_fname), strerror(errno));
5108 /* Populate the stat struct with info from the base file. */
5109 if (fruit_stat_base(handle, smb_fname, follow_links) == -1) {
5112 smb_fname->st.st_ex_size = AFP_INFO_SIZE;
5113 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5114 smb_fname->stream_name);
5118 static int fruit_stat_meta(vfs_handle_struct *handle,
5119 struct smb_filename *smb_fname,
5122 struct fruit_config_data *config = NULL;
5125 SMB_VFS_HANDLE_GET_DATA(handle, config,
5126 struct fruit_config_data, return -1);
5128 switch (config->meta) {
5129 case FRUIT_META_STREAM:
5130 ret = fruit_stat_meta_stream(handle, smb_fname, follow_links);
5133 case FRUIT_META_NETATALK:
5134 ret = fruit_stat_meta_netatalk(handle, smb_fname, follow_links);
5138 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
5145 static int fruit_stat_rsrc_netatalk(vfs_handle_struct *handle,
5146 struct smb_filename *smb_fname,
5149 struct adouble *ad = NULL;
5152 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
5158 /* Populate the stat struct with info from the base file. */
5159 ret = fruit_stat_base(handle, smb_fname, follow_links);
5165 smb_fname->st.st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
5166 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5167 smb_fname->stream_name);
5172 static int fruit_stat_rsrc_stream(vfs_handle_struct *handle,
5173 struct smb_filename *smb_fname,
5179 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
5181 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5187 static int fruit_stat_rsrc_xattr(vfs_handle_struct *handle,
5188 struct smb_filename *smb_fname,
5191 #ifdef HAVE_ATTROPEN
5195 /* Populate the stat struct with info from the base file. */
5196 ret = fruit_stat_base(handle, smb_fname, follow_links);
5201 fd = attropen(smb_fname->base_name,
5202 AFPRESOURCE_EA_NETATALK,
5208 ret = sys_fstat(fd, &smb_fname->st, false);
5211 DBG_ERR("fstat [%s:%s] failed\n", smb_fname->base_name,
5212 AFPRESOURCE_EA_NETATALK);
5218 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5219 smb_fname->stream_name);
5229 static int fruit_stat_rsrc(vfs_handle_struct *handle,
5230 struct smb_filename *smb_fname,
5233 struct fruit_config_data *config = NULL;
5236 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
5238 SMB_VFS_HANDLE_GET_DATA(handle, config,
5239 struct fruit_config_data, return -1);
5241 switch (config->rsrc) {
5242 case FRUIT_RSRC_STREAM:
5243 ret = fruit_stat_rsrc_stream(handle, smb_fname, follow_links);
5246 case FRUIT_RSRC_XATTR:
5247 ret = fruit_stat_rsrc_xattr(handle, smb_fname, follow_links);
5250 case FRUIT_RSRC_ADFILE:
5251 ret = fruit_stat_rsrc_netatalk(handle, smb_fname, follow_links);
5255 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
5262 static int fruit_stat(vfs_handle_struct *handle,
5263 struct smb_filename *smb_fname)
5267 DEBUG(10, ("fruit_stat called for %s\n",
5268 smb_fname_str_dbg(smb_fname)));
5270 if (!is_ntfs_stream_smb_fname(smb_fname)
5271 || is_ntfs_default_stream_smb_fname(smb_fname)) {
5272 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
5274 update_btime(handle, smb_fname);
5280 * Note if lp_posix_paths() is true, we can never
5281 * get here as is_ntfs_stream_smb_fname() is
5282 * always false. So we never need worry about
5283 * not following links here.
5286 if (is_afpinfo_stream(smb_fname)) {
5287 rc = fruit_stat_meta(handle, smb_fname, true);
5288 } else if (is_afpresource_stream(smb_fname)) {
5289 rc = fruit_stat_rsrc(handle, smb_fname, true);
5291 return SMB_VFS_NEXT_STAT(handle, smb_fname);
5295 update_btime(handle, smb_fname);
5296 smb_fname->st.st_ex_mode &= ~S_IFMT;
5297 smb_fname->st.st_ex_mode |= S_IFREG;
5298 smb_fname->st.st_ex_blocks =
5299 smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
5304 static int fruit_lstat(vfs_handle_struct *handle,
5305 struct smb_filename *smb_fname)
5309 DEBUG(10, ("fruit_lstat called for %s\n",
5310 smb_fname_str_dbg(smb_fname)));
5312 if (!is_ntfs_stream_smb_fname(smb_fname)
5313 || is_ntfs_default_stream_smb_fname(smb_fname)) {
5314 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5316 update_btime(handle, smb_fname);
5321 if (is_afpinfo_stream(smb_fname)) {
5322 rc = fruit_stat_meta(handle, smb_fname, false);
5323 } else if (is_afpresource_stream(smb_fname)) {
5324 rc = fruit_stat_rsrc(handle, smb_fname, false);
5326 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5330 update_btime(handle, smb_fname);
5331 smb_fname->st.st_ex_mode &= ~S_IFMT;
5332 smb_fname->st.st_ex_mode |= S_IFREG;
5333 smb_fname->st.st_ex_blocks =
5334 smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
5339 static int fruit_fstat_meta_stream(vfs_handle_struct *handle,
5341 SMB_STRUCT_STAT *sbuf)
5343 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5344 struct smb_filename smb_fname;
5353 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5358 *sbuf = fsp->base_fsp->fsp_name->st;
5359 sbuf->st_ex_size = AFP_INFO_SIZE;
5360 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5364 smb_fname = (struct smb_filename) {
5365 .base_name = fsp->fsp_name->base_name,
5368 ret = fruit_stat_base(handle, &smb_fname, false);
5372 *sbuf = smb_fname.st;
5374 ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5376 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5381 sbuf->st_ex_ino = ino;
5385 static int fruit_fstat_meta_netatalk(vfs_handle_struct *handle,
5387 SMB_STRUCT_STAT *sbuf)
5391 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5396 *sbuf = fsp->base_fsp->fsp_name->st;
5397 sbuf->st_ex_size = AFP_INFO_SIZE;
5398 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5403 static int fruit_fstat_meta(vfs_handle_struct *handle,
5405 SMB_STRUCT_STAT *sbuf,
5410 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
5412 switch (fio->config->meta) {
5413 case FRUIT_META_STREAM:
5414 ret = fruit_fstat_meta_stream(handle, fsp, sbuf);
5417 case FRUIT_META_NETATALK:
5418 ret = fruit_fstat_meta_netatalk(handle, fsp, sbuf);
5422 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
5426 DBG_DEBUG("Path [%s] ret [%d]\n", fsp_str_dbg(fsp), ret);
5430 static int fruit_fstat_rsrc_xattr(vfs_handle_struct *handle,
5432 SMB_STRUCT_STAT *sbuf)
5434 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5437 static int fruit_fstat_rsrc_stream(vfs_handle_struct *handle,
5439 SMB_STRUCT_STAT *sbuf)
5441 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5444 static int fruit_fstat_rsrc_adouble(vfs_handle_struct *handle,
5446 SMB_STRUCT_STAT *sbuf)
5448 struct adouble *ad = NULL;
5451 /* Populate the stat struct with info from the base file. */
5452 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5457 ad = ad_get(talloc_tos(), handle,
5458 fsp->base_fsp->fsp_name,
5461 DBG_ERR("ad_get [%s] failed [%s]\n",
5462 fsp_str_dbg(fsp), strerror(errno));
5466 *sbuf = fsp->base_fsp->fsp_name->st;
5467 sbuf->st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
5468 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5474 static int fruit_fstat_rsrc(vfs_handle_struct *handle, files_struct *fsp,
5475 SMB_STRUCT_STAT *sbuf, struct fio *fio)
5479 switch (fio->config->rsrc) {
5480 case FRUIT_RSRC_STREAM:
5481 ret = fruit_fstat_rsrc_stream(handle, fsp, sbuf);
5484 case FRUIT_RSRC_ADFILE:
5485 ret = fruit_fstat_rsrc_adouble(handle, fsp, sbuf);
5488 case FRUIT_RSRC_XATTR:
5489 ret = fruit_fstat_rsrc_xattr(handle, fsp, sbuf);
5493 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
5500 static int fruit_fstat(vfs_handle_struct *handle, files_struct *fsp,
5501 SMB_STRUCT_STAT *sbuf)
5503 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5507 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5510 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
5512 if (fio->type == ADOUBLE_META) {
5513 rc = fruit_fstat_meta(handle, fsp, sbuf, fio);
5515 rc = fruit_fstat_rsrc(handle, fsp, sbuf, fio);
5519 sbuf->st_ex_mode &= ~S_IFMT;
5520 sbuf->st_ex_mode |= S_IFREG;
5521 sbuf->st_ex_blocks = sbuf->st_ex_size / STAT_ST_BLOCKSIZE + 1;
5524 DBG_DEBUG("Path [%s] rc [%d] size [%"PRIdMAX"]\n",
5525 fsp_str_dbg(fsp), rc, (intmax_t)sbuf->st_ex_size);
5529 static NTSTATUS delete_invalid_meta_stream(
5530 vfs_handle_struct *handle,
5531 const struct smb_filename *smb_fname,
5532 TALLOC_CTX *mem_ctx,
5533 unsigned int *pnum_streams,
5534 struct stream_struct **pstreams,
5537 struct smb_filename *sname = NULL;
5541 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams, AFPINFO_STREAM);
5543 return NT_STATUS_INTERNAL_ERROR;
5547 return NT_STATUS_OK;
5550 sname = synthetic_smb_fname(talloc_tos(),
5551 smb_fname->base_name,
5552 AFPINFO_STREAM_NAME,
5554 if (sname == NULL) {
5555 return NT_STATUS_NO_MEMORY;
5558 ret = SMB_VFS_NEXT_UNLINK(handle, sname);
5561 DBG_ERR("Removing [%s] failed\n", smb_fname_str_dbg(sname));
5562 return map_nt_error_from_unix(errno);
5565 return NT_STATUS_OK;
5568 static NTSTATUS fruit_streaminfo_meta_stream(
5569 vfs_handle_struct *handle,
5570 struct files_struct *fsp,
5571 const struct smb_filename *smb_fname,
5572 TALLOC_CTX *mem_ctx,
5573 unsigned int *pnum_streams,
5574 struct stream_struct **pstreams)
5576 struct stream_struct *stream = *pstreams;
5577 unsigned int num_streams = *pnum_streams;
5580 for (i = 0; i < num_streams; i++) {
5581 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
5586 if (i == num_streams) {
5587 return NT_STATUS_OK;
5590 if (stream[i].size != AFP_INFO_SIZE) {
5591 DBG_ERR("Removing invalid AFPINFO_STREAM size [%jd] from [%s]\n",
5592 (intmax_t)stream[i].size, smb_fname_str_dbg(smb_fname));
5594 return delete_invalid_meta_stream(handle,
5603 return NT_STATUS_OK;
5606 static NTSTATUS fruit_streaminfo_meta_netatalk(
5607 vfs_handle_struct *handle,
5608 struct files_struct *fsp,
5609 const struct smb_filename *smb_fname,
5610 TALLOC_CTX *mem_ctx,
5611 unsigned int *pnum_streams,
5612 struct stream_struct **pstreams)
5614 struct stream_struct *stream = *pstreams;
5615 unsigned int num_streams = *pnum_streams;
5616 struct adouble *ad = NULL;
5621 /* Remove the Netatalk xattr from the list */
5622 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5623 ":" NETATALK_META_XATTR ":$DATA");
5625 return NT_STATUS_NO_MEMORY;
5629 * Check if there's a AFPINFO_STREAM from the VFS streams
5630 * backend and if yes, remove it from the list
5632 for (i = 0; i < num_streams; i++) {
5633 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
5638 if (i < num_streams) {
5639 DBG_WARNING("Unexpected AFPINFO_STREAM on [%s]\n",
5640 smb_fname_str_dbg(smb_fname));
5642 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5645 return NT_STATUS_INTERNAL_ERROR;
5649 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5651 return NT_STATUS_OK;
5654 is_fi_empty = ad_empty_finderinfo(ad);
5658 return NT_STATUS_OK;
5661 ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
5662 AFPINFO_STREAM_NAME, AFP_INFO_SIZE,
5663 smb_roundup(handle->conn, AFP_INFO_SIZE));
5665 return NT_STATUS_NO_MEMORY;
5668 return NT_STATUS_OK;
5671 static NTSTATUS fruit_streaminfo_meta(vfs_handle_struct *handle,
5672 struct files_struct *fsp,
5673 const struct smb_filename *smb_fname,
5674 TALLOC_CTX *mem_ctx,
5675 unsigned int *pnum_streams,
5676 struct stream_struct **pstreams)
5678 struct fruit_config_data *config = NULL;
5681 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5682 return NT_STATUS_INTERNAL_ERROR);
5684 switch (config->meta) {
5685 case FRUIT_META_NETATALK:
5686 status = fruit_streaminfo_meta_netatalk(handle, fsp, smb_fname,
5687 mem_ctx, pnum_streams,
5691 case FRUIT_META_STREAM:
5692 status = fruit_streaminfo_meta_stream(handle, fsp, smb_fname,
5693 mem_ctx, pnum_streams,
5698 return NT_STATUS_INTERNAL_ERROR;
5704 static NTSTATUS fruit_streaminfo_rsrc_stream(
5705 vfs_handle_struct *handle,
5706 struct files_struct *fsp,
5707 const struct smb_filename *smb_fname,
5708 TALLOC_CTX *mem_ctx,
5709 unsigned int *pnum_streams,
5710 struct stream_struct **pstreams)
5714 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
5716 DBG_ERR("Filtering resource stream failed\n");
5717 return NT_STATUS_INTERNAL_ERROR;
5719 return NT_STATUS_OK;
5722 static NTSTATUS fruit_streaminfo_rsrc_xattr(
5723 vfs_handle_struct *handle,
5724 struct files_struct *fsp,
5725 const struct smb_filename *smb_fname,
5726 TALLOC_CTX *mem_ctx,
5727 unsigned int *pnum_streams,
5728 struct stream_struct **pstreams)
5732 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
5734 DBG_ERR("Filtering resource stream failed\n");
5735 return NT_STATUS_INTERNAL_ERROR;
5737 return NT_STATUS_OK;
5740 static NTSTATUS fruit_streaminfo_rsrc_adouble(
5741 vfs_handle_struct *handle,
5742 struct files_struct *fsp,
5743 const struct smb_filename *smb_fname,
5744 TALLOC_CTX *mem_ctx,
5745 unsigned int *pnum_streams,
5746 struct stream_struct **pstreams)
5748 struct stream_struct *stream = *pstreams;
5749 unsigned int num_streams = *pnum_streams;
5750 struct adouble *ad = NULL;
5756 * Check if there's a AFPRESOURCE_STREAM from the VFS streams backend
5757 * and if yes, remove it from the list
5759 for (i = 0; i < num_streams; i++) {
5760 if (strequal_m(stream[i].name, AFPRESOURCE_STREAM)) {
5765 if (i < num_streams) {
5766 DBG_WARNING("Unexpected AFPRESOURCE_STREAM on [%s]\n",
5767 smb_fname_str_dbg(smb_fname));
5769 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5770 AFPRESOURCE_STREAM);
5772 return NT_STATUS_INTERNAL_ERROR;
5776 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
5778 return NT_STATUS_OK;
5781 rlen = ad_getentrylen(ad, ADEID_RFORK);
5785 return NT_STATUS_OK;
5788 ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
5789 AFPRESOURCE_STREAM_NAME, rlen,
5790 smb_roundup(handle->conn, rlen));
5792 return NT_STATUS_NO_MEMORY;
5795 return NT_STATUS_OK;
5798 static NTSTATUS fruit_streaminfo_rsrc(vfs_handle_struct *handle,
5799 struct files_struct *fsp,
5800 const struct smb_filename *smb_fname,
5801 TALLOC_CTX *mem_ctx,
5802 unsigned int *pnum_streams,
5803 struct stream_struct **pstreams)
5805 struct fruit_config_data *config = NULL;
5808 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5809 return NT_STATUS_INTERNAL_ERROR);
5811 switch (config->rsrc) {
5812 case FRUIT_RSRC_STREAM:
5813 status = fruit_streaminfo_rsrc_stream(handle, fsp, smb_fname,
5814 mem_ctx, pnum_streams,
5818 case FRUIT_RSRC_XATTR:
5819 status = fruit_streaminfo_rsrc_xattr(handle, fsp, smb_fname,
5820 mem_ctx, pnum_streams,
5824 case FRUIT_RSRC_ADFILE:
5825 status = fruit_streaminfo_rsrc_adouble(handle, fsp, smb_fname,
5826 mem_ctx, pnum_streams,
5831 return NT_STATUS_INTERNAL_ERROR;
5837 static void fruit_filter_empty_streams(unsigned int *pnum_streams,
5838 struct stream_struct **pstreams)
5840 unsigned num_streams = *pnum_streams;
5841 struct stream_struct *streams = *pstreams;
5844 if (!global_fruit_config.nego_aapl) {
5848 while (i < num_streams) {
5849 struct smb_filename smb_fname = (struct smb_filename) {
5850 .stream_name = streams[i].name,
5853 if (is_ntfs_default_stream_smb_fname(&smb_fname)
5854 || streams[i].size > 0)
5860 streams[i] = streams[num_streams - 1];
5864 *pnum_streams = num_streams;
5867 static NTSTATUS fruit_streaminfo(vfs_handle_struct *handle,
5868 struct files_struct *fsp,
5869 const struct smb_filename *smb_fname,
5870 TALLOC_CTX *mem_ctx,
5871 unsigned int *pnum_streams,
5872 struct stream_struct **pstreams)
5874 struct fruit_config_data *config = NULL;
5877 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5878 return NT_STATUS_UNSUCCESSFUL);
5880 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
5882 status = SMB_VFS_NEXT_STREAMINFO(handle, fsp, smb_fname, mem_ctx,
5883 pnum_streams, pstreams);
5884 if (!NT_STATUS_IS_OK(status)) {
5888 fruit_filter_empty_streams(pnum_streams, pstreams);
5890 status = fruit_streaminfo_meta(handle, fsp, smb_fname,
5891 mem_ctx, pnum_streams, pstreams);
5892 if (!NT_STATUS_IS_OK(status)) {
5896 status = fruit_streaminfo_rsrc(handle, fsp, smb_fname,
5897 mem_ctx, pnum_streams, pstreams);
5898 if (!NT_STATUS_IS_OK(status)) {
5902 return NT_STATUS_OK;
5905 static int fruit_ntimes(vfs_handle_struct *handle,
5906 const struct smb_filename *smb_fname,
5907 struct smb_file_time *ft)
5910 struct adouble *ad = NULL;
5911 struct fruit_config_data *config = NULL;
5913 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5916 if ((config->meta != FRUIT_META_NETATALK) ||
5917 null_timespec(ft->create_time))
5919 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
5922 DEBUG(10,("set btime for %s to %s\n", smb_fname_str_dbg(smb_fname),
5923 time_to_asc(convert_timespec_to_time_t(ft->create_time))));
5925 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5930 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX,
5931 convert_time_t_to_uint32_t(ft->create_time.tv_sec));
5933 rc = ad_set(handle, ad, smb_fname);
5939 DEBUG(1, ("fruit_ntimes: %s\n", smb_fname_str_dbg(smb_fname)));
5942 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
5945 static int fruit_fallocate(struct vfs_handle_struct *handle,
5946 struct files_struct *fsp,
5951 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5954 return SMB_VFS_NEXT_FALLOCATE(handle, fsp, mode, offset, len);
5957 /* Let the pwrite code path handle it. */
5962 static int fruit_ftruncate_rsrc_xattr(struct vfs_handle_struct *handle,
5963 struct files_struct *fsp,
5966 #ifdef HAVE_ATTROPEN
5967 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
5972 static int fruit_ftruncate_rsrc_adouble(struct vfs_handle_struct *handle,
5973 struct files_struct *fsp,
5977 struct adouble *ad = NULL;
5980 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
5982 DBG_DEBUG("ad_get [%s] failed [%s]\n",
5983 fsp_str_dbg(fsp), strerror(errno));
5987 ad_off = ad_getentryoff(ad, ADEID_RFORK);
5989 rc = ftruncate(fsp->fh->fd, offset + ad_off);
5995 ad_setentrylen(ad, ADEID_RFORK, offset);
5997 rc = ad_fset(handle, ad, fsp);
5999 DBG_ERR("ad_fset [%s] failed [%s]\n",
6000 fsp_str_dbg(fsp), strerror(errno));
6009 static int fruit_ftruncate_rsrc_stream(struct vfs_handle_struct *handle,
6010 struct files_struct *fsp,
6013 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
6016 static int fruit_ftruncate_rsrc(struct vfs_handle_struct *handle,
6017 struct files_struct *fsp,
6020 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
6024 DBG_ERR("Failed to fetch fsp extension");
6028 switch (fio->config->rsrc) {
6029 case FRUIT_RSRC_XATTR:
6030 ret = fruit_ftruncate_rsrc_xattr(handle, fsp, offset);
6033 case FRUIT_RSRC_ADFILE:
6034 ret = fruit_ftruncate_rsrc_adouble(handle, fsp, offset);
6037 case FRUIT_RSRC_STREAM:
6038 ret = fruit_ftruncate_rsrc_stream(handle, fsp, offset);
6042 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
6050 static int fruit_ftruncate_meta(struct vfs_handle_struct *handle,
6051 struct files_struct *fsp,
6055 DBG_WARNING("ftruncate %s to %jd",
6056 fsp_str_dbg(fsp), (intmax_t)offset);
6057 /* OS X returns NT_STATUS_ALLOTTED_SPACE_EXCEEDED */
6062 /* OS X returns success but does nothing */
6063 DBG_INFO("ignoring ftruncate %s to %jd\n",
6064 fsp_str_dbg(fsp), (intmax_t)offset);
6068 static int fruit_ftruncate(struct vfs_handle_struct *handle,
6069 struct files_struct *fsp,
6072 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
6075 DBG_DEBUG("Path [%s] offset [%"PRIdMAX"]\n", fsp_str_dbg(fsp),
6079 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
6082 if (fio->type == ADOUBLE_META) {
6083 ret = fruit_ftruncate_meta(handle, fsp, offset);
6085 ret = fruit_ftruncate_rsrc(handle, fsp, offset);
6088 DBG_DEBUG("Path [%s] result [%d]\n", fsp_str_dbg(fsp), ret);
6092 static NTSTATUS fruit_create_file(vfs_handle_struct *handle,
6093 struct smb_request *req,
6094 uint16_t root_dir_fid,
6095 struct smb_filename *smb_fname,
6096 uint32_t access_mask,
6097 uint32_t share_access,
6098 uint32_t create_disposition,
6099 uint32_t create_options,
6100 uint32_t file_attributes,
6101 uint32_t oplock_request,
6102 struct smb2_lease *lease,
6103 uint64_t allocation_size,
6104 uint32_t private_flags,
6105 struct security_descriptor *sd,
6106 struct ea_list *ea_list,
6107 files_struct **result,
6109 const struct smb2_create_blobs *in_context_blobs,
6110 struct smb2_create_blobs *out_context_blobs)
6113 struct fruit_config_data *config = NULL;
6114 files_struct *fsp = NULL;
6115 struct fio *fio = NULL;
6116 bool internal_open = (oplock_request & INTERNAL_OPEN_ONLY);
6119 status = check_aapl(handle, req, in_context_blobs, out_context_blobs);
6120 if (!NT_STATUS_IS_OK(status)) {
6124 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
6125 return NT_STATUS_UNSUCCESSFUL);
6127 if (is_apple_stream(smb_fname) && !internal_open) {
6128 ret = ad_convert(handle, smb_fname);
6130 DBG_ERR("ad_convert() failed\n");
6131 return NT_STATUS_UNSUCCESSFUL;
6135 status = SMB_VFS_NEXT_CREATE_FILE(
6136 handle, req, root_dir_fid, smb_fname,
6137 access_mask, share_access,
6138 create_disposition, create_options,
6139 file_attributes, oplock_request,
6141 allocation_size, private_flags,
6142 sd, ea_list, result,
6143 pinfo, in_context_blobs, out_context_blobs);
6144 if (!NT_STATUS_IS_OK(status)) {
6150 if (global_fruit_config.nego_aapl) {
6151 if (config->posix_rename && fsp->is_directory) {
6153 * Enable POSIX directory rename behaviour
6155 fsp->posix_flags |= FSP_POSIX_FLAGS_RENAME;
6160 * If this is a plain open for existing files, opening an 0
6161 * byte size resource fork MUST fail with
6162 * NT_STATUS_OBJECT_NAME_NOT_FOUND.
6164 * Cf the vfs_fruit torture tests in test_rfork_create().
6166 if (global_fruit_config.nego_aapl &&
6167 create_disposition == FILE_OPEN &&
6168 smb_fname->st.st_ex_size == 0 &&
6169 is_ntfs_stream_smb_fname(smb_fname) &&
6170 !(is_ntfs_default_stream_smb_fname(smb_fname)))
6172 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
6176 fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
6177 if (fio != NULL && pinfo != NULL && *pinfo == FILE_WAS_CREATED) {
6178 fio->created = true;
6181 if (is_ntfs_stream_smb_fname(smb_fname)
6182 || fsp->is_directory) {
6186 if (config->locking == FRUIT_LOCKING_NETATALK) {
6187 status = fruit_check_access(
6191 if (!NT_STATUS_IS_OK(status)) {
6199 DEBUG(10, ("fruit_create_file: %s\n", nt_errstr(status)));
6202 close_file(req, fsp, ERROR_CLOSE);
6203 *result = fsp = NULL;
6209 static NTSTATUS fruit_readdir_attr(struct vfs_handle_struct *handle,
6210 const struct smb_filename *fname,
6211 TALLOC_CTX *mem_ctx,
6212 struct readdir_attr_data **pattr_data)
6214 struct fruit_config_data *config = NULL;
6215 struct readdir_attr_data *attr_data;
6219 SMB_VFS_HANDLE_GET_DATA(handle, config,
6220 struct fruit_config_data,
6221 return NT_STATUS_UNSUCCESSFUL);
6223 if (!global_fruit_config.nego_aapl) {
6224 return SMB_VFS_NEXT_READDIR_ATTR(handle, fname, mem_ctx, pattr_data);
6227 DEBUG(10, ("fruit_readdir_attr %s\n", fname->base_name));
6229 ret = ad_convert(handle, fname);
6231 DBG_ERR("ad_convert() failed\n");
6232 return NT_STATUS_UNSUCCESSFUL;
6235 *pattr_data = talloc_zero(mem_ctx, struct readdir_attr_data);
6236 if (*pattr_data == NULL) {
6237 return NT_STATUS_UNSUCCESSFUL;
6239 attr_data = *pattr_data;
6240 attr_data->type = RDATTR_AAPL;
6243 * Mac metadata: compressed FinderInfo, resource fork length
6246 status = readdir_attr_macmeta(handle, fname, attr_data);
6247 if (!NT_STATUS_IS_OK(status)) {
6249 * Error handling is tricky: if we return failure from
6250 * this function, the corresponding directory entry
6251 * will to be passed to the client, so we really just
6252 * want to error out on fatal errors.
6254 if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
6262 if (config->unix_info_enabled) {
6263 attr_data->attr_data.aapl.unix_mode = fname->st.st_ex_mode;
6269 if (!config->readdir_attr_max_access) {
6270 attr_data->attr_data.aapl.max_access = FILE_GENERIC_ALL;
6272 status = smbd_calculate_access_mask(
6276 SEC_FLAG_MAXIMUM_ALLOWED,
6277 &attr_data->attr_data.aapl.max_access);
6278 if (!NT_STATUS_IS_OK(status)) {
6283 return NT_STATUS_OK;
6286 DEBUG(1, ("fruit_readdir_attr %s, error: %s\n",
6287 fname->base_name, nt_errstr(status)));
6288 TALLOC_FREE(*pattr_data);
6292 static NTSTATUS fruit_fget_nt_acl(vfs_handle_struct *handle,
6294 uint32_t security_info,
6295 TALLOC_CTX *mem_ctx,
6296 struct security_descriptor **ppdesc)
6299 struct security_ace ace;
6301 struct fruit_config_data *config;
6303 SMB_VFS_HANDLE_GET_DATA(handle, config,
6304 struct fruit_config_data,
6305 return NT_STATUS_UNSUCCESSFUL);
6307 status = SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
6309 if (!NT_STATUS_IS_OK(status)) {
6314 * Add MS NFS style ACEs with uid, gid and mode
6316 if (!global_fruit_config.nego_aapl) {
6317 return NT_STATUS_OK;
6319 if (!config->unix_info_enabled) {
6320 return NT_STATUS_OK;
6323 /* First remove any existing ACE's with NFS style mode/uid/gid SIDs. */
6324 status = remove_virtual_nfs_aces(*ppdesc);
6325 if (!NT_STATUS_IS_OK(status)) {
6326 DBG_WARNING("failed to remove MS NFS style ACEs\n");
6330 /* MS NFS style mode */
6331 sid_compose(&sid, &global_sid_Unix_NFS_Mode, fsp->fsp_name->st.st_ex_mode);
6332 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6333 status = security_descriptor_dacl_add(*ppdesc, &ace);
6334 if (!NT_STATUS_IS_OK(status)) {
6335 DEBUG(1,("failed to add MS NFS style ACE\n"));
6339 /* MS NFS style uid */
6340 sid_compose(&sid, &global_sid_Unix_NFS_Users, fsp->fsp_name->st.st_ex_uid);
6341 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6342 status = security_descriptor_dacl_add(*ppdesc, &ace);
6343 if (!NT_STATUS_IS_OK(status)) {
6344 DEBUG(1,("failed to add MS NFS style ACE\n"));
6348 /* MS NFS style gid */
6349 sid_compose(&sid, &global_sid_Unix_NFS_Groups, fsp->fsp_name->st.st_ex_gid);
6350 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6351 status = security_descriptor_dacl_add(*ppdesc, &ace);
6352 if (!NT_STATUS_IS_OK(status)) {
6353 DEBUG(1,("failed to add MS NFS style ACE\n"));
6357 return NT_STATUS_OK;
6360 static NTSTATUS fruit_fset_nt_acl(vfs_handle_struct *handle,
6362 uint32_t security_info_sent,
6363 const struct security_descriptor *orig_psd)
6367 mode_t ms_nfs_mode = 0;
6369 struct security_descriptor *psd = NULL;
6370 uint32_t orig_num_aces = 0;
6372 if (orig_psd->dacl != NULL) {
6373 orig_num_aces = orig_psd->dacl->num_aces;
6376 psd = security_descriptor_copy(talloc_tos(), orig_psd);
6378 return NT_STATUS_NO_MEMORY;
6381 DBG_DEBUG("fruit_fset_nt_acl: %s\n", fsp_str_dbg(fsp));
6383 status = check_ms_nfs(handle, fsp, psd, &ms_nfs_mode, &do_chmod);
6384 if (!NT_STATUS_IS_OK(status)) {
6385 DEBUG(1, ("fruit_fset_nt_acl: check_ms_nfs failed%s\n", fsp_str_dbg(fsp)));
6391 * If only ms_nfs ACE entries were sent, ensure we set the DACL
6392 * sent/present flags correctly now we've removed them.
6395 if (orig_num_aces != 0) {
6397 * Are there any ACE's left ?
6399 if (psd->dacl->num_aces == 0) {
6400 /* No - clear the DACL sent/present flags. */
6401 security_info_sent &= ~SECINFO_DACL;
6402 psd->type &= ~SEC_DESC_DACL_PRESENT;
6406 status = SMB_VFS_NEXT_FSET_NT_ACL(handle, fsp, security_info_sent, psd);
6407 if (!NT_STATUS_IS_OK(status)) {
6408 DEBUG(1, ("fruit_fset_nt_acl: SMB_VFS_NEXT_FSET_NT_ACL failed%s\n", fsp_str_dbg(fsp)));
6414 if (fsp->fh->fd != -1) {
6415 result = SMB_VFS_FCHMOD(fsp, ms_nfs_mode);
6417 result = SMB_VFS_CHMOD(fsp->conn,
6423 DEBUG(1, ("chmod: %s, result: %d, %04o error %s\n", fsp_str_dbg(fsp),
6424 result, (unsigned)ms_nfs_mode,
6426 status = map_nt_error_from_unix(errno);
6433 return NT_STATUS_OK;
6436 static struct vfs_offload_ctx *fruit_offload_ctx;
6438 struct fruit_offload_read_state {
6439 struct vfs_handle_struct *handle;
6440 struct tevent_context *ev;
6446 static void fruit_offload_read_done(struct tevent_req *subreq);
6448 static struct tevent_req *fruit_offload_read_send(
6449 TALLOC_CTX *mem_ctx,
6450 struct tevent_context *ev,
6451 struct vfs_handle_struct *handle,
6458 struct tevent_req *req = NULL;
6459 struct tevent_req *subreq = NULL;
6460 struct fruit_offload_read_state *state = NULL;
6462 req = tevent_req_create(mem_ctx, &state,
6463 struct fruit_offload_read_state);
6467 *state = (struct fruit_offload_read_state) {
6474 subreq = SMB_VFS_NEXT_OFFLOAD_READ_SEND(mem_ctx, ev, handle, fsp,
6475 fsctl, ttl, offset, to_copy);
6476 if (tevent_req_nomem(subreq, req)) {
6477 return tevent_req_post(req, ev);
6479 tevent_req_set_callback(subreq, fruit_offload_read_done, req);
6483 static void fruit_offload_read_done(struct tevent_req *subreq)
6485 struct tevent_req *req = tevent_req_callback_data(
6486 subreq, struct tevent_req);
6487 struct fruit_offload_read_state *state = tevent_req_data(
6488 req, struct fruit_offload_read_state);
6491 status = SMB_VFS_NEXT_OFFLOAD_READ_RECV(subreq,
6495 TALLOC_FREE(subreq);
6496 if (tevent_req_nterror(req, status)) {
6500 if (state->fsctl != FSCTL_SRV_REQUEST_RESUME_KEY) {
6501 tevent_req_done(req);
6505 status = vfs_offload_token_ctx_init(state->fsp->conn->sconn->client,
6506 &fruit_offload_ctx);
6507 if (tevent_req_nterror(req, status)) {
6511 status = vfs_offload_token_db_store_fsp(fruit_offload_ctx,
6514 if (tevent_req_nterror(req, status)) {
6518 tevent_req_done(req);
6522 static NTSTATUS fruit_offload_read_recv(struct tevent_req *req,
6523 struct vfs_handle_struct *handle,
6524 TALLOC_CTX *mem_ctx,
6527 struct fruit_offload_read_state *state = tevent_req_data(
6528 req, struct fruit_offload_read_state);
6531 if (tevent_req_is_nterror(req, &status)) {
6532 tevent_req_received(req);
6536 token->length = state->token.length;
6537 token->data = talloc_move(mem_ctx, &state->token.data);
6539 tevent_req_received(req);
6540 return NT_STATUS_OK;
6543 struct fruit_offload_write_state {
6544 struct vfs_handle_struct *handle;
6546 struct files_struct *src_fsp;
6547 struct files_struct *dst_fsp;
6551 static void fruit_offload_write_done(struct tevent_req *subreq);
6552 static struct tevent_req *fruit_offload_write_send(struct vfs_handle_struct *handle,
6553 TALLOC_CTX *mem_ctx,
6554 struct tevent_context *ev,
6557 off_t transfer_offset,
6558 struct files_struct *dest_fsp,
6562 struct tevent_req *req, *subreq;
6563 struct fruit_offload_write_state *state;
6565 struct fruit_config_data *config;
6566 off_t src_off = transfer_offset;
6567 files_struct *src_fsp = NULL;
6568 off_t to_copy = num;
6569 bool copyfile_enabled = false;
6571 DEBUG(10,("soff: %ju, doff: %ju, len: %ju\n",
6572 (uintmax_t)src_off, (uintmax_t)dest_off, (uintmax_t)num));
6574 SMB_VFS_HANDLE_GET_DATA(handle, config,
6575 struct fruit_config_data,
6578 req = tevent_req_create(mem_ctx, &state,
6579 struct fruit_offload_write_state);
6583 state->handle = handle;
6584 state->dst_fsp = dest_fsp;
6587 case FSCTL_SRV_COPYCHUNK:
6588 case FSCTL_SRV_COPYCHUNK_WRITE:
6589 copyfile_enabled = config->copyfile_enabled;
6596 * Check if this a OS X copyfile style copychunk request with
6597 * a requested chunk count of 0 that was translated to a
6598 * offload_write_send VFS call overloading the parameters src_off
6599 * = dest_off = num = 0.
6601 if (copyfile_enabled && num == 0 && src_off == 0 && dest_off == 0) {
6602 status = vfs_offload_token_db_fetch_fsp(
6603 fruit_offload_ctx, token, &src_fsp);
6604 if (tevent_req_nterror(req, status)) {
6605 return tevent_req_post(req, ev);
6607 state->src_fsp = src_fsp;
6609 status = vfs_stat_fsp(src_fsp);
6610 if (tevent_req_nterror(req, status)) {
6611 return tevent_req_post(req, ev);
6614 to_copy = src_fsp->fsp_name->st.st_ex_size;
6615 state->is_copyfile = true;
6618 subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle,
6627 if (tevent_req_nomem(subreq, req)) {
6628 return tevent_req_post(req, ev);
6631 tevent_req_set_callback(subreq, fruit_offload_write_done, req);
6635 static void fruit_offload_write_done(struct tevent_req *subreq)
6637 struct tevent_req *req = tevent_req_callback_data(
6638 subreq, struct tevent_req);
6639 struct fruit_offload_write_state *state = tevent_req_data(
6640 req, struct fruit_offload_write_state);
6642 unsigned int num_streams = 0;
6643 struct stream_struct *streams = NULL;
6645 struct smb_filename *src_fname_tmp = NULL;
6646 struct smb_filename *dst_fname_tmp = NULL;
6648 status = SMB_VFS_NEXT_OFFLOAD_WRITE_RECV(state->handle,
6651 TALLOC_FREE(subreq);
6652 if (tevent_req_nterror(req, status)) {
6656 if (!state->is_copyfile) {
6657 tevent_req_done(req);
6662 * Now copy all remaining streams. We know the share supports
6663 * streams, because we're in vfs_fruit. We don't do this async
6664 * because streams are few and small.
6666 status = vfs_streaminfo(state->handle->conn, state->src_fsp,
6667 state->src_fsp->fsp_name,
6668 req, &num_streams, &streams);
6669 if (tevent_req_nterror(req, status)) {
6673 if (num_streams == 1) {
6674 /* There is always one stream, ::$DATA. */
6675 tevent_req_done(req);
6679 for (i = 0; i < num_streams; i++) {
6680 DEBUG(10, ("%s: stream: '%s'/%zu\n",
6681 __func__, streams[i].name, (size_t)streams[i].size));
6683 src_fname_tmp = synthetic_smb_fname(
6685 state->src_fsp->fsp_name->base_name,
6688 state->src_fsp->fsp_name->flags);
6689 if (tevent_req_nomem(src_fname_tmp, req)) {
6693 if (is_ntfs_default_stream_smb_fname(src_fname_tmp)) {
6694 TALLOC_FREE(src_fname_tmp);
6698 dst_fname_tmp = synthetic_smb_fname(
6700 state->dst_fsp->fsp_name->base_name,
6703 state->dst_fsp->fsp_name->flags);
6704 if (tevent_req_nomem(dst_fname_tmp, req)) {
6705 TALLOC_FREE(src_fname_tmp);
6709 status = copy_file(req,
6710 state->handle->conn,
6713 OPENX_FILE_CREATE_IF_NOT_EXIST,
6715 if (!NT_STATUS_IS_OK(status)) {
6716 DEBUG(1, ("%s: copy %s to %s failed: %s\n", __func__,
6717 smb_fname_str_dbg(src_fname_tmp),
6718 smb_fname_str_dbg(dst_fname_tmp),
6719 nt_errstr(status)));
6720 TALLOC_FREE(src_fname_tmp);
6721 TALLOC_FREE(dst_fname_tmp);
6722 tevent_req_nterror(req, status);
6726 TALLOC_FREE(src_fname_tmp);
6727 TALLOC_FREE(dst_fname_tmp);
6730 TALLOC_FREE(streams);
6731 TALLOC_FREE(src_fname_tmp);
6732 TALLOC_FREE(dst_fname_tmp);
6733 tevent_req_done(req);
6736 static NTSTATUS fruit_offload_write_recv(struct vfs_handle_struct *handle,
6737 struct tevent_req *req,
6740 struct fruit_offload_write_state *state = tevent_req_data(
6741 req, struct fruit_offload_write_state);
6744 if (tevent_req_is_nterror(req, &status)) {
6745 DEBUG(1, ("server side copy chunk failed: %s\n",
6746 nt_errstr(status)));
6748 tevent_req_received(req);
6752 *copied = state->copied;
6753 tevent_req_received(req);
6755 return NT_STATUS_OK;
6758 static char *fruit_get_bandsize_line(char **lines, int numlines)
6761 static bool re_initialized = false;
6765 if (!re_initialized) {
6766 ret = regcomp(&re, "^[[:blank:]]*<key>band-size</key>$", 0);
6770 re_initialized = true;
6773 for (i = 0; i < numlines; i++) {
6774 regmatch_t matches[1];
6776 ret = regexec(&re, lines[i], 1, matches, 0);
6779 * Check if the match was on the last line, sa we want
6780 * the subsequent line.
6782 if (i + 1 == numlines) {
6785 return lines[i + 1];
6787 if (ret != REG_NOMATCH) {
6795 static bool fruit_get_bandsize_from_line(char *line, size_t *_band_size)
6798 static bool re_initialized = false;
6799 regmatch_t matches[2];
6804 if (!re_initialized) {
6807 "<integer>\\([[:digit:]]*\\)</integer>$",
6812 re_initialized = true;
6815 ret = regexec(&re, line, 2, matches, 0);
6817 DBG_ERR("regex failed [%s]\n", line);
6821 line[matches[1].rm_eo] = '\0';
6823 ok = conv_str_u64(&line[matches[1].rm_so], &band_size);
6827 *_band_size = (size_t)band_size;
6832 * This reads and parses an Info.plist from a TM sparsebundle looking for the
6833 * "band-size" key and value.
6835 static bool fruit_get_bandsize(vfs_handle_struct *handle,
6839 #define INFO_PLIST_MAX_SIZE 64*1024
6841 struct smb_filename *smb_fname = NULL;
6842 files_struct *fsp = NULL;
6843 uint8_t *file_data = NULL;
6844 char **lines = NULL;
6845 char *band_size_line = NULL;
6846 size_t plist_file_size;
6853 plist = talloc_asprintf(talloc_tos(),
6855 handle->conn->connectpath,
6857 if (plist == NULL) {
6862 smb_fname = synthetic_smb_fname(talloc_tos(), plist, NULL, NULL, 0);
6863 if (smb_fname == NULL) {
6868 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
6870 DBG_INFO("Ignoring Sparsebundle without Info.plist [%s]\n", dir);
6875 plist_file_size = smb_fname->st.st_ex_size;
6877 if (plist_file_size > INFO_PLIST_MAX_SIZE) {
6878 DBG_INFO("%s is too large, ignoring\n", plist);
6883 status = SMB_VFS_NEXT_CREATE_FILE(
6886 0, /* root_dir_fid */
6887 smb_fname, /* fname */
6888 FILE_GENERIC_READ, /* access_mask */
6889 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
6890 FILE_OPEN, /* create_disposition */
6891 0, /* create_options */
6892 0, /* file_attributes */
6893 INTERNAL_OPEN_ONLY, /* oplock_request */
6895 0, /* allocation_size */
6896 0, /* private_flags */
6901 NULL, NULL); /* create context */
6902 if (!NT_STATUS_IS_OK(status)) {
6903 DBG_INFO("Opening [%s] failed [%s]\n",
6904 smb_fname_str_dbg(smb_fname), nt_errstr(status));
6909 file_data = talloc_array(talloc_tos(), uint8_t, plist_file_size);
6910 if (file_data == NULL) {
6915 nread = SMB_VFS_NEXT_PREAD(handle, fsp, file_data, plist_file_size, 0);
6916 if (nread != plist_file_size) {
6917 DBG_ERR("Short read on [%s]: %zu/%zd\n",
6918 fsp_str_dbg(fsp), nread, plist_file_size);
6924 status = close_file(NULL, fsp, NORMAL_CLOSE);
6926 if (!NT_STATUS_IS_OK(status)) {
6927 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
6932 lines = file_lines_parse((char *)file_data,
6936 if (lines == NULL) {
6941 band_size_line = fruit_get_bandsize_line(lines, numlines);
6942 if (band_size_line == NULL) {
6943 DBG_ERR("Didn't find band-size key in [%s]\n",
6944 smb_fname_str_dbg(smb_fname));
6949 ok = fruit_get_bandsize_from_line(band_size_line, band_size);
6951 DBG_ERR("fruit_get_bandsize_from_line failed\n");
6955 DBG_DEBUG("Parsed band-size [%zu] for [%s]\n", *band_size, plist);
6959 status = close_file(NULL, fsp, NORMAL_CLOSE);
6960 if (!NT_STATUS_IS_OK(status)) {
6961 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
6966 TALLOC_FREE(smb_fname);
6967 TALLOC_FREE(file_data);
6972 struct fruit_disk_free_state {
6976 static bool fruit_get_num_bands(vfs_handle_struct *handle,
6981 struct smb_filename *bands_dir = NULL;
6983 struct dirent *e = NULL;
6987 path = talloc_asprintf(talloc_tos(),
6989 handle->conn->connectpath,
6995 bands_dir = synthetic_smb_fname(talloc_tos(),
7001 if (bands_dir == NULL) {
7005 d = SMB_VFS_NEXT_OPENDIR(handle, bands_dir, NULL, 0);
7007 TALLOC_FREE(bands_dir);
7013 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
7015 e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
7017 if (ISDOT(e->d_name) || ISDOTDOT(e->d_name)) {
7023 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
7025 TALLOC_FREE(bands_dir);
7029 DBG_DEBUG("%zu bands in [%s]\n", nbands, smb_fname_str_dbg(bands_dir));
7031 TALLOC_FREE(bands_dir);
7037 static bool fruit_tmsize_do_dirent(vfs_handle_struct *handle,
7038 struct fruit_disk_free_state *state,
7043 size_t sparsebundle_strlen = strlen("sparsebundle");
7044 size_t bandsize = 0;
7048 p = strstr(e->d_name, "sparsebundle");
7053 if (p[sparsebundle_strlen] != '\0') {
7057 DBG_DEBUG("Processing sparsebundle [%s]\n", e->d_name);
7059 ok = fruit_get_bandsize(handle, e->d_name, &bandsize);
7062 * Beware of race conditions: this may be an uninitialized
7063 * Info.plist that a client is just creating. We don't want let
7064 * this to trigger complete failure.
7066 DBG_ERR("Processing sparsebundle [%s] failed\n", e->d_name);
7070 ok = fruit_get_num_bands(handle, e->d_name, &nbands);
7073 * Beware of race conditions: this may be a backup sparsebundle
7074 * in an early stage lacking a bands subdirectory. We don't want
7075 * let this to trigger complete failure.
7077 DBG_ERR("Processing sparsebundle [%s] failed\n", e->d_name);
7081 if (bandsize > SIZE_MAX/nbands) {
7082 DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
7086 tm_size = bandsize * nbands;
7088 if (state->total_size + tm_size < state->total_size) {
7089 DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
7094 state->total_size += tm_size;
7096 DBG_DEBUG("[%s] tm_size [%jd] total_size [%jd]\n",
7097 e->d_name, (intmax_t)tm_size, (intmax_t)state->total_size);
7103 * Calculate used size of a TimeMachine volume
7105 * This assumes that the volume is used only for TimeMachine.
7107 * - readdir(basedir of share), then
7108 * - for every element that matches regex "^\(.*\)\.sparsebundle$" :
7109 * - parse "\1.sparsebundle/Info.plist" and read the band-size XML key
7110 * - count band files in "\1.sparsebundle/bands/"
7111 * - calculate used size of all bands: band_count * band_size
7113 static uint64_t fruit_disk_free(vfs_handle_struct *handle,
7114 const struct smb_filename *smb_fname,
7119 struct fruit_config_data *config = NULL;
7120 struct fruit_disk_free_state state = {0};
7122 struct dirent *e = NULL;
7128 SMB_VFS_HANDLE_GET_DATA(handle, config,
7129 struct fruit_config_data,
7132 if (!config->time_machine ||
7133 config->time_machine_max_size == 0)
7135 return SMB_VFS_NEXT_DISK_FREE(handle,
7142 d = SMB_VFS_NEXT_OPENDIR(handle, smb_fname, NULL, 0);
7147 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
7149 e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
7151 ok = fruit_tmsize_do_dirent(handle, &state, e);
7153 SMB_VFS_NEXT_CLOSEDIR(handle, d);
7158 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
7163 dsize = config->time_machine_max_size / 512;
7164 dfree = dsize - (state.total_size / 512);
7165 if (dfree > dsize) {
7175 static struct vfs_fn_pointers vfs_fruit_fns = {
7176 .connect_fn = fruit_connect,
7177 .disk_free_fn = fruit_disk_free,
7179 /* File operations */
7180 .chmod_fn = fruit_chmod,
7181 .chown_fn = fruit_chown,
7182 .unlink_fn = fruit_unlink,
7183 .rename_fn = fruit_rename,
7184 .rmdir_fn = fruit_rmdir,
7185 .open_fn = fruit_open,
7186 .close_fn = fruit_close,
7187 .pread_fn = fruit_pread,
7188 .pwrite_fn = fruit_pwrite,
7189 .pread_send_fn = fruit_pread_send,
7190 .pread_recv_fn = fruit_pread_recv,
7191 .pwrite_send_fn = fruit_pwrite_send,
7192 .pwrite_recv_fn = fruit_pwrite_recv,
7193 .stat_fn = fruit_stat,
7194 .lstat_fn = fruit_lstat,
7195 .fstat_fn = fruit_fstat,
7196 .streaminfo_fn = fruit_streaminfo,
7197 .ntimes_fn = fruit_ntimes,
7198 .ftruncate_fn = fruit_ftruncate,
7199 .fallocate_fn = fruit_fallocate,
7200 .create_file_fn = fruit_create_file,
7201 .readdir_attr_fn = fruit_readdir_attr,
7202 .offload_read_send_fn = fruit_offload_read_send,
7203 .offload_read_recv_fn = fruit_offload_read_recv,
7204 .offload_write_send_fn = fruit_offload_write_send,
7205 .offload_write_recv_fn = fruit_offload_write_recv,
7207 /* NT ACL operations */
7208 .fget_nt_acl_fn = fruit_fget_nt_acl,
7209 .fset_nt_acl_fn = fruit_fset_nt_acl,
7213 NTSTATUS vfs_fruit_init(TALLOC_CTX *ctx)
7215 NTSTATUS ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "fruit",
7217 if (!NT_STATUS_IS_OK(ret)) {
7221 vfs_fruit_debug_level = debug_add_class("fruit");
7222 if (vfs_fruit_debug_level == -1) {
7223 vfs_fruit_debug_level = DBGC_VFS;
7224 DEBUG(0, ("%s: Couldn't register custom debugging class!\n",
7227 DEBUG(10, ("%s: Debug class number of '%s': %d\n",
7228 "vfs_fruit_init","fruit",vfs_fruit_debug_level));