2 * OS X and Netatalk interoperability VFS module for Samba-3.x
4 * Copyright (C) Ralph Boehme, 2013, 2014
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
21 #include "MacExtensions.h"
22 #include "smbd/smbd.h"
23 #include "system/filesys.h"
24 #include "lib/util/time.h"
25 #include "system/shmem.h"
26 #include "locking/proto.h"
27 #include "smbd/globals.h"
29 #include "libcli/security/security.h"
30 #include "../libcli/smb/smb2_create_ctx.h"
31 #include "lib/util/sys_rw.h"
32 #include "lib/util/tevent_ntstatus.h"
33 #include "lib/util/tevent_unix.h"
34 #include "offload_token.h"
35 #include "string_replace.h"
37 #include <gnutls/gnutls.h>
38 #include <gnutls/crypto.h>
41 * Enhanced OS X and Netatalk compatibility
42 * ========================================
44 * This modules takes advantage of vfs_streams_xattr and
45 * vfs_catia. VFS modules vfs_fruit and vfs_streams_xattr must be
46 * loaded in the correct order:
48 * vfs modules = catia fruit streams_xattr
50 * The module intercepts the OS X special streams "AFP_AfpInfo" and
51 * "AFP_Resource" and handles them in a special way. All other named
52 * streams are deferred to vfs_streams_xattr.
54 * The OS X client maps all NTFS illegal characters to the Unicode
55 * private range. This module optionally stores the charcters using
56 * their native ASCII encoding using vfs_catia. If you're not enabling
57 * this feature, you can skip catia from vfs modules.
59 * Finally, open modes are optionally checked against Netatalk AFP
62 * The "AFP_AfpInfo" named stream is a binary blob containing OS X
63 * extended metadata for files and directories. This module optionally
64 * reads and stores this metadata in a way compatible with Netatalk 3
65 * which stores the metadata in an EA "org.netatalk.metadata". Cf
66 * source3/include/MacExtensions.h for a description of the binary
69 * The "AFP_Resource" named stream may be arbitrarily large, thus it
70 * can't be stored in an xattr on most filesystem. ZFS on Solaris is
71 * the only available filesystem where xattrs can be of any size and
72 * the OS supports using the file APIs for xattrs.
74 * The AFP_Resource stream is stored in an AppleDouble file prepending
75 * "._" to the filename. On Solaris with ZFS the stream is optionally
76 * stored in an EA "org.netatalk.resource".
82 * The OS X SMB client sends xattrs as ADS too. For xattr interop with
83 * other protocols you may want to adjust the xattr names the VFS
84 * module vfs_streams_xattr uses for storing ADS's. This defaults to
85 * user.DosStream.ADS_NAME:$DATA and can be changed by specifying
86 * these module parameters:
88 * streams_xattr:prefix = user.
89 * streams_xattr:store_stream_type = false
95 * - log diagnostic if any needed VFS module is not loaded
96 * (eg with lp_vfs_objects())
100 static int vfs_fruit_debug_level = DBGC_VFS;
102 static struct global_fruit_config {
103 bool nego_aapl; /* client negotiated AAPL */
105 } global_fruit_config;
108 #define DBGC_CLASS vfs_fruit_debug_level
110 #define FRUIT_PARAM_TYPE_NAME "fruit"
111 #define ADOUBLE_NAME_PREFIX "._"
113 #define NETATALK_META_XATTR "org.netatalk.Metadata"
114 #define NETATALK_RSRC_XATTR "org.netatalk.ResourceFork"
116 #if defined(HAVE_ATTROPEN)
117 #define AFPINFO_EA_NETATALK NETATALK_META_XATTR
118 #define AFPRESOURCE_EA_NETATALK NETATALK_RSRC_XATTR
120 #define AFPINFO_EA_NETATALK "user." NETATALK_META_XATTR
121 #define AFPRESOURCE_EA_NETATALK "user." NETATALK_RSRC_XATTR
124 enum apple_fork {APPLE_FORK_DATA, APPLE_FORK_RSRC};
126 enum fruit_rsrc {FRUIT_RSRC_STREAM, FRUIT_RSRC_ADFILE, FRUIT_RSRC_XATTR};
127 enum fruit_meta {FRUIT_META_STREAM, FRUIT_META_NETATALK};
128 enum fruit_locking {FRUIT_LOCKING_NETATALK, FRUIT_LOCKING_NONE};
129 enum fruit_encoding {FRUIT_ENC_NATIVE, FRUIT_ENC_PRIVATE};
131 struct fruit_config_data {
132 enum fruit_rsrc rsrc;
133 enum fruit_meta meta;
134 enum fruit_locking locking;
135 enum fruit_encoding encoding;
136 bool use_aapl; /* config from smb.conf */
138 bool readdir_attr_enabled;
139 bool unix_info_enabled;
140 bool copyfile_enabled;
141 bool veto_appledouble;
143 bool aapl_zero_file_id;
146 off_t time_machine_max_size;
147 bool wipe_intentionally_left_blank_rfork;
148 bool delete_empty_adfiles;
151 * Additional options, all enabled by default,
152 * possibly useful for analyzing performance. The associated
153 * operations with each of them may be expensive, so having
154 * the chance to disable them individually gives a chance
155 * tweaking the setup for the particular usecase.
157 bool readdir_attr_rsize;
158 bool readdir_attr_finder_info;
159 bool readdir_attr_max_access;
162 static const struct enum_list fruit_rsrc[] = {
163 {FRUIT_RSRC_STREAM, "stream"}, /* pass on to vfs_streams_xattr */
164 {FRUIT_RSRC_ADFILE, "file"}, /* ._ AppleDouble file */
165 {FRUIT_RSRC_XATTR, "xattr"}, /* Netatalk compatible xattr (ZFS only) */
169 static const struct enum_list fruit_meta[] = {
170 {FRUIT_META_STREAM, "stream"}, /* pass on to vfs_streams_xattr */
171 {FRUIT_META_NETATALK, "netatalk"}, /* Netatalk compatible xattr */
175 static const struct enum_list fruit_locking[] = {
176 {FRUIT_LOCKING_NETATALK, "netatalk"}, /* synchronize locks with Netatalk */
177 {FRUIT_LOCKING_NONE, "none"},
181 static const struct enum_list fruit_encoding[] = {
182 {FRUIT_ENC_NATIVE, "native"}, /* map unicode private chars to ASCII */
183 {FRUIT_ENC_PRIVATE, "private"}, /* keep unicode private chars */
187 static const char *fruit_catia_maps =
188 "0x01:0xf001,0x02:0xf002,0x03:0xf003,0x04:0xf004,"
189 "0x05:0xf005,0x06:0xf006,0x07:0xf007,0x08:0xf008,"
190 "0x09:0xf009,0x0a:0xf00a,0x0b:0xf00b,0x0c:0xf00c,"
191 "0x0d:0xf00d,0x0e:0xf00e,0x0f:0xf00f,0x10:0xf010,"
192 "0x11:0xf011,0x12:0xf012,0x13:0xf013,0x14:0xf014,"
193 "0x15:0xf015,0x16:0xf016,0x17:0xf017,0x18:0xf018,"
194 "0x19:0xf019,0x1a:0xf01a,0x1b:0xf01b,0x1c:0xf01c,"
195 "0x1d:0xf01d,0x1e:0xf01e,0x1f:0xf01f,"
196 "0x22:0xf020,0x2a:0xf021,0x3a:0xf022,0x3c:0xf023,"
197 "0x3e:0xf024,0x3f:0xf025,0x5c:0xf026,0x7c:0xf027,"
200 /*****************************************************************************
201 * Defines, functions and data structures that deal with AppleDouble
202 *****************************************************************************/
205 * There are two AppleDouble blobs we deal with:
207 * - ADOUBLE_META - AppleDouble blob used by Netatalk for storing
208 * metadata in an xattr
210 * - ADOUBLE_RSRC - AppleDouble blob used by OS X and Netatalk in
213 typedef enum {ADOUBLE_META, ADOUBLE_RSRC} adouble_type_t;
216 #define AD_VERSION2 0x00020000
217 #define AD_VERSION AD_VERSION2
220 * AppleDouble entry IDs.
222 #define ADEID_DFORK 1
223 #define ADEID_RFORK 2
225 #define ADEID_COMMENT 4
226 #define ADEID_ICONBW 5
227 #define ADEID_ICONCOL 6
228 #define ADEID_FILEI 7
229 #define ADEID_FILEDATESI 8
230 #define ADEID_FINDERI 9
231 #define ADEID_MACFILEI 10
232 #define ADEID_PRODOSFILEI 11
233 #define ADEID_MSDOSFILEI 12
234 #define ADEID_SHORTNAME 13
235 #define ADEID_AFPFILEI 14
238 /* Private Netatalk entries */
239 #define ADEID_PRIVDEV 16
240 #define ADEID_PRIVINO 17
241 #define ADEID_PRIVSYN 18
242 #define ADEID_PRIVID 19
243 #define ADEID_MAX (ADEID_PRIVID + 1)
246 * These are the real ids for the private entries,
247 * as stored in the adouble file
249 #define AD_DEV 0x80444556
250 #define AD_INO 0x80494E4F
251 #define AD_SYN 0x8053594E
252 #define AD_ID 0x8053567E
254 /* Number of actually used entries */
255 #define ADEID_NUM_XATTR 8
256 #define ADEID_NUM_DOT_UND 2
257 #define ADEID_NUM_RSRC_XATTR 1
259 /* AppleDouble magic */
260 #define AD_APPLESINGLE_MAGIC 0x00051600
261 #define AD_APPLEDOUBLE_MAGIC 0x00051607
262 #define AD_MAGIC AD_APPLEDOUBLE_MAGIC
264 /* Sizes of relevant entry bits */
265 #define ADEDLEN_MAGIC 4
266 #define ADEDLEN_VERSION 4
267 #define ADEDLEN_FILLER 16
268 #define AD_FILLER_TAG "Netatalk " /* should be 16 bytes */
269 #define AD_FILLER_TAG_OSX "Mac OS X " /* should be 16 bytes */
270 #define ADEDLEN_NENTRIES 2
271 #define AD_HEADER_LEN (ADEDLEN_MAGIC + ADEDLEN_VERSION + \
272 ADEDLEN_FILLER + ADEDLEN_NENTRIES) /* 26 */
273 #define AD_ENTRY_LEN_EID 4
274 #define AD_ENTRY_LEN_OFF 4
275 #define AD_ENTRY_LEN_LEN 4
276 #define AD_ENTRY_LEN (AD_ENTRY_LEN_EID + AD_ENTRY_LEN_OFF + AD_ENTRY_LEN_LEN)
279 #define ADEDLEN_NAME 255
280 #define ADEDLEN_COMMENT 200
281 #define ADEDLEN_FILEI 16
282 #define ADEDLEN_FINDERI 32
283 #define ADEDLEN_FILEDATESI 16
284 #define ADEDLEN_SHORTNAME 12 /* length up to 8.3 */
285 #define ADEDLEN_AFPFILEI 4
286 #define ADEDLEN_MACFILEI 4
287 #define ADEDLEN_PRODOSFILEI 8
288 #define ADEDLEN_MSDOSFILEI 2
289 #define ADEDLEN_DID 4
290 #define ADEDLEN_PRIVDEV 8
291 #define ADEDLEN_PRIVINO 8
292 #define ADEDLEN_PRIVSYN 8
293 #define ADEDLEN_PRIVID 4
296 #define ADEDOFF_MAGIC 0
297 #define ADEDOFF_VERSION (ADEDOFF_MAGIC + ADEDLEN_MAGIC)
298 #define ADEDOFF_FILLER (ADEDOFF_VERSION + ADEDLEN_VERSION)
299 #define ADEDOFF_NENTRIES (ADEDOFF_FILLER + ADEDLEN_FILLER)
301 #define ADEDOFF_FINDERI_XATTR (AD_HEADER_LEN + \
302 (ADEID_NUM_XATTR * AD_ENTRY_LEN))
303 #define ADEDOFF_COMMENT_XATTR (ADEDOFF_FINDERI_XATTR + ADEDLEN_FINDERI)
304 #define ADEDOFF_FILEDATESI_XATTR (ADEDOFF_COMMENT_XATTR + ADEDLEN_COMMENT)
305 #define ADEDOFF_AFPFILEI_XATTR (ADEDOFF_FILEDATESI_XATTR + \
307 #define ADEDOFF_PRIVDEV_XATTR (ADEDOFF_AFPFILEI_XATTR + ADEDLEN_AFPFILEI)
308 #define ADEDOFF_PRIVINO_XATTR (ADEDOFF_PRIVDEV_XATTR + ADEDLEN_PRIVDEV)
309 #define ADEDOFF_PRIVSYN_XATTR (ADEDOFF_PRIVINO_XATTR + ADEDLEN_PRIVINO)
310 #define ADEDOFF_PRIVID_XATTR (ADEDOFF_PRIVSYN_XATTR + ADEDLEN_PRIVSYN)
312 #define ADEDOFF_FINDERI_DOT_UND (AD_HEADER_LEN + \
313 (ADEID_NUM_DOT_UND * AD_ENTRY_LEN))
314 #define ADEDOFF_RFORK_DOT_UND (ADEDOFF_FINDERI_DOT_UND + ADEDLEN_FINDERI)
316 #define AD_DATASZ_XATTR (AD_HEADER_LEN + \
317 (ADEID_NUM_XATTR * AD_ENTRY_LEN) + \
318 ADEDLEN_FINDERI + ADEDLEN_COMMENT + \
319 ADEDLEN_FILEDATESI + ADEDLEN_AFPFILEI + \
320 ADEDLEN_PRIVDEV + ADEDLEN_PRIVINO + \
321 ADEDLEN_PRIVSYN + ADEDLEN_PRIVID)
323 #if AD_DATASZ_XATTR != 402
324 #error bad size for AD_DATASZ_XATTR
327 #define AD_DATASZ_DOT_UND (AD_HEADER_LEN + \
328 (ADEID_NUM_DOT_UND * AD_ENTRY_LEN) + \
330 #if AD_DATASZ_DOT_UND != 82
331 #error bad size for AD_DATASZ_DOT_UND
335 * Sharemode locks fcntl() offsets
337 #if _FILE_OFFSET_BITS == 64 || defined(HAVE_LARGEFILE)
338 #define AD_FILELOCK_BASE (UINT64_C(0x7FFFFFFFFFFFFFFF) - 9)
340 #define AD_FILELOCK_BASE (UINT32_C(0x7FFFFFFF) - 9)
342 #define BYTELOCK_MAX (AD_FILELOCK_BASE - 1)
344 #define AD_FILELOCK_OPEN_WR (AD_FILELOCK_BASE + 0)
345 #define AD_FILELOCK_OPEN_RD (AD_FILELOCK_BASE + 1)
346 #define AD_FILELOCK_RSRC_OPEN_WR (AD_FILELOCK_BASE + 2)
347 #define AD_FILELOCK_RSRC_OPEN_RD (AD_FILELOCK_BASE + 3)
348 #define AD_FILELOCK_DENY_WR (AD_FILELOCK_BASE + 4)
349 #define AD_FILELOCK_DENY_RD (AD_FILELOCK_BASE + 5)
350 #define AD_FILELOCK_RSRC_DENY_WR (AD_FILELOCK_BASE + 6)
351 #define AD_FILELOCK_RSRC_DENY_RD (AD_FILELOCK_BASE + 7)
352 #define AD_FILELOCK_OPEN_NONE (AD_FILELOCK_BASE + 8)
353 #define AD_FILELOCK_RSRC_OPEN_NONE (AD_FILELOCK_BASE + 9)
355 /* Time stuff we overload the bits a little */
356 #define AD_DATE_CREATE 0
357 #define AD_DATE_MODIFY 4
358 #define AD_DATE_BACKUP 8
359 #define AD_DATE_ACCESS 12
360 #define AD_DATE_MASK (AD_DATE_CREATE | AD_DATE_MODIFY | \
361 AD_DATE_BACKUP | AD_DATE_ACCESS)
362 #define AD_DATE_UNIX (1 << 10)
363 #define AD_DATE_START 0x80000000
364 #define AD_DATE_DELTA 946684800
365 #define AD_DATE_FROM_UNIX(x) (htonl((x) - AD_DATE_DELTA))
366 #define AD_DATE_TO_UNIX(x) (ntohl(x) + AD_DATE_DELTA)
368 #define AD_XATTR_HDR_MAGIC 0x41545452 /* 'ATTR' */
369 #define AD_XATTR_MAX_ENTRIES 1024 /* Some arbitrarily enforced limit */
370 #define AD_XATTR_HDR_SIZE 36
371 #define AD_XATTR_MAX_HDR_SIZE 65536
373 /* Accessor macros */
374 #define ad_getentrylen(ad,eid) ((ad)->ad_eid[(eid)].ade_len)
375 #define ad_getentryoff(ad,eid) ((ad)->ad_eid[(eid)].ade_off)
376 #define ad_setentrylen(ad,eid,len) ((ad)->ad_eid[(eid)].ade_len = (len))
377 #define ad_setentryoff(ad,eid,off) ((ad)->ad_eid[(eid)].ade_off = (off))
380 * Both struct ad_xattr_header and struct ad_xattr_entry describe the in memory
381 * representation as well as the on-disk format.
383 * The ad_xattr_header follows the FinderInfo data in the FinderInfo entry if
384 * the length of the FinderInfo entry is larger then 32 bytes. It is then
385 * preceeded with 2 bytes padding.
387 * Cf: https://opensource.apple.com/source/xnu/xnu-4570.1.46/bsd/vfs/vfs_xattr.c
390 struct ad_xattr_header {
391 uint32_t adx_magic; /* ATTR_HDR_MAGIC */
392 uint32_t adx_debug_tag; /* for debugging == file id of owning file */
393 uint32_t adx_total_size; /* file offset of end of attribute header + entries + data */
394 uint32_t adx_data_start; /* file offset to attribute data area */
395 uint32_t adx_data_length; /* length of attribute data area */
396 uint32_t adx_reserved[3];
398 uint16_t adx_num_attrs;
401 /* On-disk entries are aligned on 4 byte boundaries */
402 struct ad_xattr_entry {
403 uint32_t adx_offset; /* file offset to data */
404 uint32_t adx_length; /* size of attribute data */
406 uint8_t adx_namelen; /* included the NULL terminator */
407 char *adx_name; /* NULL-terminated UTF-8 name */
416 vfs_handle_struct *ad_handle;
419 adouble_type_t ad_type;
422 uint8_t ad_filler[ADEDLEN_FILLER];
423 struct ad_entry ad_eid[ADEID_MAX];
425 struct ad_xattr_header adx_header;
426 struct ad_xattr_entry *adx_entries;
429 struct ad_entry_order {
430 uint32_t id, offset, len;
433 /* Netatalk AppleDouble metadata xattr */
435 struct ad_entry_order entry_order_meta_xattr[ADEID_NUM_XATTR + 1] = {
436 {ADEID_FINDERI, ADEDOFF_FINDERI_XATTR, ADEDLEN_FINDERI},
437 {ADEID_COMMENT, ADEDOFF_COMMENT_XATTR, 0},
438 {ADEID_FILEDATESI, ADEDOFF_FILEDATESI_XATTR, ADEDLEN_FILEDATESI},
439 {ADEID_AFPFILEI, ADEDOFF_AFPFILEI_XATTR, ADEDLEN_AFPFILEI},
440 {ADEID_PRIVDEV, ADEDOFF_PRIVDEV_XATTR, 0},
441 {ADEID_PRIVINO, ADEDOFF_PRIVINO_XATTR, 0},
442 {ADEID_PRIVSYN, ADEDOFF_PRIVSYN_XATTR, 0},
443 {ADEID_PRIVID, ADEDOFF_PRIVID_XATTR, 0},
447 /* AppleDouble resource fork file (the ones prefixed by "._") */
449 struct ad_entry_order entry_order_dot_und[ADEID_NUM_DOT_UND + 1] = {
450 {ADEID_FINDERI, ADEDOFF_FINDERI_DOT_UND, ADEDLEN_FINDERI},
451 {ADEID_RFORK, ADEDOFF_RFORK_DOT_UND, 0},
456 * Fake AppleDouble entry oder for resource fork xattr. The xattr
457 * isn't an AppleDouble file, it simply contains the resource data,
458 * but in order to be able to use some API calls like ad_getentryoff()
459 * we build a fake/helper struct adouble with this entry order struct.
462 struct ad_entry_order entry_order_rsrc_xattr[ADEID_NUM_RSRC_XATTR + 1] = {
467 /* Conversion from enumerated id to on-disk AppleDouble id */
468 #define AD_EID_DISK(a) (set_eid[a])
469 static const uint32_t set_eid[] = {
470 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
471 AD_DEV, AD_INO, AD_SYN, AD_ID
474 static char empty_resourcefork[] = {
475 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
476 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
477 0x54, 0x68, 0x69, 0x73, 0x20, 0x72, 0x65, 0x73,
478 0x6F, 0x75, 0x72, 0x63, 0x65, 0x20, 0x66, 0x6F,
479 0x72, 0x6B, 0x20, 0x69, 0x6E, 0x74, 0x65, 0x6E,
480 0x74, 0x69, 0x6F, 0x6E, 0x61, 0x6C, 0x6C, 0x79,
481 0x20, 0x6C, 0x65, 0x66, 0x74, 0x20, 0x62, 0x6C,
482 0x61, 0x6E, 0x6B, 0x20, 0x20, 0x20, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
507 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
508 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
509 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
510 0x00, 0x1C, 0x00, 0x1E, 0xFF, 0xFF
514 /* tcon config handle */
515 struct fruit_config_data *config;
517 /* Denote stream type, meta or rsrc */
520 /* Whether the create created the stream */
524 * AFP_AfpInfo stream created, but not written yet, thus still a fake
525 * pipe fd. This is set to true in fruit_open_meta if there was no
526 * exisiting stream but the caller requested O_CREAT. It is later set to
527 * false when we get a write on the stream that then does open and
536 * Forward declarations
538 static struct adouble *ad_init(TALLOC_CTX *ctx, vfs_handle_struct *handle,
539 adouble_type_t type);
540 static struct adouble *ad_get(TALLOC_CTX *ctx,
541 vfs_handle_struct *handle,
542 const struct smb_filename *smb_fname,
543 adouble_type_t type);
544 static int ad_set(struct adouble *ad, const struct smb_filename *smb_fname);
545 static int ad_fset(struct adouble *ad, files_struct *fsp);
546 static int adouble_path(TALLOC_CTX *ctx,
547 const struct smb_filename *smb_fname__in,
548 struct smb_filename **ppsmb_fname_out);
549 static AfpInfo *afpinfo_new(TALLOC_CTX *ctx);
550 static ssize_t afpinfo_pack(const AfpInfo *ai, char *buf);
551 static AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data);
555 * Return a pointer to an AppleDouble entry
557 * Returns NULL if the entry is not present
559 static char *ad_get_entry(const struct adouble *ad, int eid)
561 off_t off = ad_getentryoff(ad, eid);
562 size_t len = ad_getentrylen(ad, eid);
564 if (off == 0 || len == 0) {
568 return ad->ad_data + off;
574 static int ad_getdate(const struct adouble *ad,
575 unsigned int dateoff,
578 bool xlate = (dateoff & AD_DATE_UNIX);
581 dateoff &= AD_DATE_MASK;
582 p = ad_get_entry(ad, ADEID_FILEDATESI);
587 if (dateoff > AD_DATE_ACCESS) {
591 memcpy(date, p + dateoff, sizeof(uint32_t));
594 *date = AD_DATE_TO_UNIX(*date);
602 static int ad_setdate(struct adouble *ad, unsigned int dateoff, uint32_t date)
604 bool xlate = (dateoff & AD_DATE_UNIX);
607 p = ad_get_entry(ad, ADEID_FILEDATESI);
612 dateoff &= AD_DATE_MASK;
614 date = AD_DATE_FROM_UNIX(date);
617 if (dateoff > AD_DATE_ACCESS) {
621 memcpy(p + dateoff, &date, sizeof(date));
628 * Map on-disk AppleDouble id to enumerated id
630 static uint32_t get_eid(uint32_t eid)
638 return ADEID_PRIVDEV;
640 return ADEID_PRIVINO;
642 return ADEID_PRIVSYN;
653 * Pack AppleDouble structure into data buffer
655 static bool ad_pack(struct adouble *ad)
662 bufsize = talloc_get_size(ad->ad_data);
663 if (bufsize < AD_DATASZ_DOT_UND) {
664 DBG_ERR("bad buffer size [0x%" PRIx32 "]\n", bufsize);
668 if (offset + ADEDLEN_MAGIC < offset ||
669 offset + ADEDLEN_MAGIC >= bufsize) {
672 RSIVAL(ad->ad_data, offset, ad->ad_magic);
673 offset += ADEDLEN_MAGIC;
675 if (offset + ADEDLEN_VERSION < offset ||
676 offset + ADEDLEN_VERSION >= bufsize) {
679 RSIVAL(ad->ad_data, offset, ad->ad_version);
680 offset += ADEDLEN_VERSION;
682 if (offset + ADEDLEN_FILLER < offset ||
683 offset + ADEDLEN_FILLER >= bufsize) {
686 if (ad->ad_type == ADOUBLE_RSRC) {
687 memcpy(ad->ad_data + offset, AD_FILLER_TAG, ADEDLEN_FILLER);
689 offset += ADEDLEN_FILLER;
691 if (offset + ADEDLEN_NENTRIES < offset ||
692 offset + ADEDLEN_NENTRIES >= bufsize) {
695 offset += ADEDLEN_NENTRIES;
697 for (eid = 0, nent = 0; eid < ADEID_MAX; eid++) {
698 if (ad->ad_eid[eid].ade_off == 0) {
700 * ade_off is also used as indicator whether a
701 * specific entry is used or not
706 if (offset + AD_ENTRY_LEN_EID < offset ||
707 offset + AD_ENTRY_LEN_EID >= bufsize) {
710 RSIVAL(ad->ad_data, offset, AD_EID_DISK(eid));
711 offset += AD_ENTRY_LEN_EID;
713 if (offset + AD_ENTRY_LEN_OFF < offset ||
714 offset + AD_ENTRY_LEN_OFF >= bufsize) {
717 RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_off);
718 offset += AD_ENTRY_LEN_OFF;
720 if (offset + AD_ENTRY_LEN_LEN < offset ||
721 offset + AD_ENTRY_LEN_LEN >= bufsize) {
724 RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_len);
725 offset += AD_ENTRY_LEN_LEN;
730 if (ADEDOFF_NENTRIES + 2 >= bufsize) {
733 RSSVAL(ad->ad_data, ADEDOFF_NENTRIES, nent);
738 static bool ad_unpack_xattrs(struct adouble *ad)
740 struct ad_xattr_header *h = &ad->adx_header;
741 const char *p = ad->ad_data;
745 if (ad_getentrylen(ad, ADEID_FINDERI) <= ADEDLEN_FINDERI) {
749 /* 2 bytes padding */
750 hoff = ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI + 2;
752 h->adx_magic = RIVAL(p, hoff + 0);
753 h->adx_debug_tag = RIVAL(p, hoff + 4); /* Not used -> not checked */
754 h->adx_total_size = RIVAL(p, hoff + 8);
755 h->adx_data_start = RIVAL(p, hoff + 12);
756 h->adx_data_length = RIVAL(p, hoff + 16);
757 h->adx_flags = RSVAL(p, hoff + 32); /* Not used -> not checked */
758 h->adx_num_attrs = RSVAL(p, hoff + 34);
760 if (h->adx_magic != AD_XATTR_HDR_MAGIC) {
761 DBG_ERR("Bad magic: 0x%" PRIx32 "\n", h->adx_magic);
765 if (h->adx_total_size > ad_getentryoff(ad, ADEID_RFORK)) {
766 DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size);
769 if (h->adx_total_size > AD_XATTR_MAX_HDR_SIZE) {
770 DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size);
774 if (h->adx_data_start < (hoff + AD_XATTR_HDR_SIZE)) {
775 DBG_ERR("Bad start: 0x%" PRIx32 "\n", h->adx_data_start);
779 if ((h->adx_data_start + h->adx_data_length) < h->adx_data_start) {
780 DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length);
783 if ((h->adx_data_start + h->adx_data_length) >
784 ad->adx_header.adx_total_size)
786 DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length);
790 if (h->adx_num_attrs > AD_XATTR_MAX_ENTRIES) {
791 DBG_ERR("Bad num xattrs: %" PRIu16 "\n", h->adx_num_attrs);
795 if (h->adx_num_attrs == 0) {
799 ad->adx_entries = talloc_zero_array(
800 ad, struct ad_xattr_entry, h->adx_num_attrs);
801 if (ad->adx_entries == NULL) {
805 hoff += AD_XATTR_HDR_SIZE;
807 for (i = 0; i < h->adx_num_attrs; i++) {
808 struct ad_xattr_entry *e = &ad->adx_entries[i];
810 hoff = (hoff + 3) & ~3;
812 e->adx_offset = RIVAL(p, hoff + 0);
813 e->adx_length = RIVAL(p, hoff + 4);
814 e->adx_flags = RSVAL(p, hoff + 8);
815 e->adx_namelen = *(p + hoff + 10);
817 if (e->adx_offset >= ad->adx_header.adx_total_size) {
818 DBG_ERR("Bad adx_offset: %" PRIx32 "\n",
823 if ((e->adx_offset + e->adx_length) < e->adx_offset) {
824 DBG_ERR("Bad adx_length: %" PRIx32 "\n",
829 if ((e->adx_offset + e->adx_length) >
830 ad->adx_header.adx_total_size)
832 DBG_ERR("Bad adx_length: %" PRIx32 "\n",
837 if (e->adx_namelen == 0) {
838 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
842 if ((hoff + 11 + e->adx_namelen) < hoff + 11) {
843 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
847 if ((hoff + 11 + e->adx_namelen) >
848 ad->adx_header.adx_data_start)
850 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
855 e->adx_name = talloc_strndup(ad->adx_entries,
858 if (e->adx_name == NULL) {
862 DBG_DEBUG("xattr [%s] offset [0x%x] size [0x%x]\n",
863 e->adx_name, e->adx_offset, e->adx_length);
864 dump_data(10, (uint8_t *)(ad->ad_data + e->adx_offset),
867 hoff += 11 + e->adx_namelen;
874 * Unpack an AppleDouble blob into a struct adoble
876 static bool ad_unpack(struct adouble *ad, const size_t nentries,
879 size_t bufsize = talloc_get_size(ad->ad_data);
881 uint32_t eid, len, off;
885 * The size of the buffer ad->ad_data is checked when read, so
886 * we wouldn't have to check our own offsets, a few extra
887 * checks won't hurt though. We have to check the offsets we
888 * read from the buffer anyway.
891 if (bufsize < (AD_HEADER_LEN + (AD_ENTRY_LEN * nentries))) {
892 DEBUG(1, ("bad size\n"));
896 ad->ad_magic = RIVAL(ad->ad_data, 0);
897 ad->ad_version = RIVAL(ad->ad_data, ADEDOFF_VERSION);
898 if ((ad->ad_magic != AD_MAGIC) || (ad->ad_version != AD_VERSION)) {
899 DEBUG(1, ("wrong magic or version\n"));
903 memcpy(ad->ad_filler, ad->ad_data + ADEDOFF_FILLER, ADEDLEN_FILLER);
905 adentries = RSVAL(ad->ad_data, ADEDOFF_NENTRIES);
906 if (adentries != nentries) {
907 DEBUG(1, ("invalid number of entries: %zu\n",
912 /* now, read in the entry bits */
913 for (i = 0; i < adentries; i++) {
914 eid = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN));
916 off = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 4);
917 len = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 8);
919 if (!eid || eid >= ADEID_MAX) {
920 DEBUG(1, ("bogus eid %d\n", eid));
925 * All entries other than the resource fork are
926 * expected to be read into the ad_data buffer, so
927 * ensure the specified offset is within that bound
929 if ((off > bufsize) && (eid != ADEID_RFORK)) {
930 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
936 * All entries besides FinderInfo and resource fork
937 * must fit into the buffer. FinderInfo is special as
938 * it may be larger then the default 32 bytes (if it
939 * contains marshalled xattrs), but we will fixup that
940 * in ad_convert(). And the resource fork is never
941 * accessed directly by the ad_data buf (also see
942 * comment above) anyway.
944 if ((eid != ADEID_RFORK) &&
945 (eid != ADEID_FINDERI) &&
946 ((off + len) > bufsize)) {
947 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
953 * That would be obviously broken
955 if (off > filesize) {
956 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
962 * Check for any entry that has its end beyond the
965 if (off + len < off) {
966 DEBUG(1, ("offset wrap in eid %d: off: %" PRIu32
967 ", len: %" PRIu32 "\n",
972 if (off + len > filesize) {
974 * If this is the resource fork entry, we fix
975 * up the length, for any other entry we bail
978 if (eid != ADEID_RFORK) {
979 DEBUG(1, ("bogus eid %d: off: %" PRIu32
980 ", len: %" PRIu32 "\n",
986 * Fixup the resource fork entry by limiting
987 * the size to entryoffset - filesize.
989 len = filesize - off;
990 DEBUG(1, ("Limiting ADEID_RFORK: off: %" PRIu32
991 ", len: %" PRIu32 "\n", off, len));
994 ad->ad_eid[eid].ade_off = off;
995 ad->ad_eid[eid].ade_len = len;
998 ok = ad_unpack_xattrs(ad);
1006 static bool ad_convert_move_reso(struct adouble *ad,
1007 const struct smb_filename *smb_fname)
1009 char *map = MAP_FAILED;
1015 if (ad_getentrylen(ad, ADEID_RFORK) == 0) {
1019 maplen = ad_getentryoff(ad, ADEID_RFORK) +
1020 ad_getentrylen(ad, ADEID_RFORK);
1022 /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
1023 map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
1025 if (map == MAP_FAILED) {
1026 DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
1031 memmove(map + ADEDOFF_RFORK_DOT_UND,
1032 map + ad_getentryoff(ad, ADEID_RFORK),
1033 ad_getentrylen(ad, ADEID_RFORK));
1035 rc = munmap(map, maplen);
1037 DBG_ERR("munmap failed: %s\n", strerror(errno));
1041 ad_setentryoff(ad, ADEID_RFORK, ADEDOFF_RFORK_DOT_UND);
1045 DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name);
1049 len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
1050 if (len != AD_DATASZ_DOT_UND) {
1051 DBG_ERR("%s: bad size: %zd\n", smb_fname->base_name, len);
1058 static bool ad_convert_xattr(struct adouble *ad,
1059 const struct smb_filename *smb_fname,
1060 bool *converted_xattr)
1062 static struct char_mappings **string_replace_cmaps = NULL;
1063 char *map = MAP_FAILED;
1067 int saved_errno = 0;
1072 *converted_xattr = false;
1074 if (ad_getentrylen(ad, ADEID_FINDERI) == ADEDLEN_FINDERI) {
1078 if (string_replace_cmaps == NULL) {
1079 const char **mappings = NULL;
1081 mappings = str_list_make_v3_const(
1082 talloc_tos(), fruit_catia_maps, NULL);
1083 if (mappings == NULL) {
1086 string_replace_cmaps = string_replace_init_map(mappings);
1087 TALLOC_FREE(mappings);
1090 maplen = ad_getentryoff(ad, ADEID_RFORK) +
1091 ad_getentrylen(ad, ADEID_RFORK);
1093 /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
1094 map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
1096 if (map == MAP_FAILED) {
1097 DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
1101 for (i = 0; i < ad->adx_header.adx_num_attrs; i++) {
1102 struct ad_xattr_entry *e = &ad->adx_entries[i];
1103 char *mapped_name = NULL;
1105 struct smb_filename *stream_name = NULL;
1106 files_struct *fsp = NULL;
1109 status = string_replace_allocate(ad->ad_handle->conn,
1111 string_replace_cmaps,
1114 vfs_translate_to_windows);
1115 if (!NT_STATUS_IS_OK(status) &&
1116 !NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))
1118 DBG_ERR("string_replace_allocate failed\n");
1124 mapped_name = talloc_asprintf(talloc_tos(), ":%s", tmp);
1126 if (mapped_name == NULL) {
1131 stream_name = synthetic_smb_fname(talloc_tos(),
1132 smb_fname->base_name,
1136 TALLOC_FREE(mapped_name);
1137 if (stream_name == NULL) {
1138 DBG_ERR("synthetic_smb_fname failed\n");
1143 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1145 status = SMB_VFS_CREATE_FILE(
1146 ad->ad_handle->conn, /* conn */
1148 0, /* root_dir_fid */
1149 stream_name, /* fname */
1150 FILE_GENERIC_WRITE, /* access_mask */
1151 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
1152 FILE_OPEN_IF, /* create_disposition */
1153 0, /* create_options */
1154 0, /* file_attributes */
1155 INTERNAL_OPEN_ONLY, /* oplock_request */
1157 0, /* allocation_size */
1158 0, /* private_flags */
1163 NULL, NULL); /* create context */
1164 TALLOC_FREE(stream_name);
1165 if (!NT_STATUS_IS_OK(status)) {
1166 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1171 nwritten = SMB_VFS_PWRITE(fsp,
1172 map + e->adx_offset,
1175 if (nwritten == -1) {
1176 DBG_ERR("SMB_VFS_PWRITE failed\n");
1177 saved_errno = errno;
1178 close_file(NULL, fsp, ERROR_CLOSE);
1179 errno = saved_errno;
1184 status = close_file(NULL, fsp, NORMAL_CLOSE);
1185 if (!NT_STATUS_IS_OK(status)) {
1192 ad_setentrylen(ad, ADEID_FINDERI, ADEDLEN_FINDERI);
1196 DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name);
1200 len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
1201 if (len != AD_DATASZ_DOT_UND) {
1202 DBG_ERR("%s: bad size: %zd\n", smb_fname->base_name, len);
1207 ok = ad_convert_move_reso(ad, smb_fname);
1212 *converted_xattr = true;
1216 rc = munmap(map, maplen);
1218 DBG_ERR("munmap failed: %s\n", strerror(errno));
1225 static bool ad_convert_finderinfo(struct adouble *ad,
1226 const struct smb_filename *smb_fname)
1231 struct smb_filename *stream_name = NULL;
1232 files_struct *fsp = NULL;
1236 int saved_errno = 0;
1239 cmp = memcmp(ad->ad_filler, AD_FILLER_TAG_OSX, ADEDLEN_FILLER);
1244 p_ad = ad_get_entry(ad, ADEID_FINDERI);
1249 ai = afpinfo_new(talloc_tos());
1254 memcpy(ai->afpi_FinderInfo, p_ad, ADEDLEN_FINDERI);
1256 aiblob = data_blob_talloc(talloc_tos(), NULL, AFP_INFO_SIZE);
1257 if (aiblob.data == NULL) {
1262 size = afpinfo_pack(ai, (char *)aiblob.data);
1264 if (size != AFP_INFO_SIZE) {
1268 stream_name = synthetic_smb_fname(talloc_tos(),
1269 smb_fname->base_name,
1273 if (stream_name == NULL) {
1274 data_blob_free(&aiblob);
1275 DBG_ERR("synthetic_smb_fname failed\n");
1279 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1281 status = SMB_VFS_CREATE_FILE(
1282 ad->ad_handle->conn, /* conn */
1284 0, /* root_dir_fid */
1285 stream_name, /* fname */
1286 FILE_GENERIC_WRITE, /* access_mask */
1287 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
1288 FILE_OPEN_IF, /* create_disposition */
1289 0, /* create_options */
1290 0, /* file_attributes */
1291 INTERNAL_OPEN_ONLY, /* oplock_request */
1293 0, /* allocation_size */
1294 0, /* private_flags */
1299 NULL, NULL); /* create context */
1300 TALLOC_FREE(stream_name);
1301 if (!NT_STATUS_IS_OK(status)) {
1302 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1306 nwritten = SMB_VFS_PWRITE(fsp,
1310 if (nwritten == -1) {
1311 DBG_ERR("SMB_VFS_PWRITE failed\n");
1312 saved_errno = errno;
1313 close_file(NULL, fsp, ERROR_CLOSE);
1314 errno = saved_errno;
1318 status = close_file(NULL, fsp, NORMAL_CLOSE);
1319 if (!NT_STATUS_IS_OK(status)) {
1327 static bool ad_convert_truncate(struct adouble *ad,
1328 const struct smb_filename *smb_fname)
1333 * FIXME: direct ftruncate(), but we don't have a fsp for the
1336 rc = ftruncate(ad->ad_fd, ADEDOFF_RFORK_DOT_UND +
1337 ad_getentrylen(ad, ADEID_RFORK));
1345 static bool ad_convert_blank_rfork(struct adouble *ad,
1348 struct fruit_config_data *config = NULL;
1349 uint8_t *map = MAP_FAILED;
1358 SMB_VFS_HANDLE_GET_DATA(ad->ad_handle, config,
1359 struct fruit_config_data, return false);
1361 if (!config->wipe_intentionally_left_blank_rfork) {
1365 if (ad_getentrylen(ad, ADEID_RFORK) != sizeof(empty_resourcefork)) {
1369 maplen = ad_getentryoff(ad, ADEID_RFORK) +
1370 ad_getentrylen(ad, ADEID_RFORK);
1372 /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
1373 map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
1375 if (map == MAP_FAILED) {
1376 DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
1380 cmp = memcmp(map + ADEDOFF_RFORK_DOT_UND,
1382 sizeof(empty_resourcefork));
1383 rc = munmap(map, maplen);
1385 DBG_ERR("munmap failed: %s\n", strerror(errno));
1393 ad_setentrylen(ad, ADEID_RFORK, 0);
1400 len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
1401 if (len != AD_DATASZ_DOT_UND) {
1409 static bool ad_convert_delete_adfile(struct adouble *ad,
1410 const struct smb_filename *smb_fname)
1412 struct fruit_config_data *config = NULL;
1413 struct smb_filename *ad_name = NULL;
1416 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
1420 SMB_VFS_HANDLE_GET_DATA(ad->ad_handle, config,
1421 struct fruit_config_data, return false);
1423 if (!config->delete_empty_adfiles) {
1427 rc = adouble_path(talloc_tos(), smb_fname, &ad_name);
1432 rc = SMB_VFS_NEXT_UNLINK(ad->ad_handle, ad_name);
1434 DBG_ERR("Unlinking [%s] failed: %s\n",
1435 smb_fname_str_dbg(ad_name), strerror(errno));
1436 TALLOC_FREE(ad_name);
1440 DBG_WARNING("Unlinked [%s] after conversion\n", smb_fname_str_dbg(ad_name));
1441 TALLOC_FREE(ad_name);
1447 * Convert from Apple's ._ file to Netatalk
1449 * Apple's AppleDouble may contain a FinderInfo entry longer then 32
1450 * bytes containing packed xattrs.
1452 * @return -1 in case an error occurred, 0 if no conversion was done, 1
1455 static int ad_convert(struct vfs_handle_struct *handle,
1456 const struct smb_filename *smb_fname)
1458 struct adouble *ad = NULL;
1460 bool converted_xattr = false;
1464 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
1469 ok = ad_convert_xattr(ad, smb_fname, &converted_xattr);
1475 ok = ad_convert_blank_rfork(ad, &blank);
1481 if (converted_xattr || blank) {
1482 ok = ad_convert_truncate(ad, smb_fname);
1489 ok = ad_convert_finderinfo(ad, smb_fname);
1491 DBG_ERR("Failed to convert [%s]\n",
1492 smb_fname_str_dbg(smb_fname));
1497 ok = ad_convert_delete_adfile(ad, smb_fname);
1510 * Read and parse Netatalk AppleDouble metadata xattr
1512 static ssize_t ad_read_meta(struct adouble *ad,
1513 const struct smb_filename *smb_fname)
1519 DEBUG(10, ("reading meta xattr for %s\n", smb_fname->base_name));
1521 ealen = SMB_VFS_GETXATTR(ad->ad_handle->conn, smb_fname,
1522 AFPINFO_EA_NETATALK, ad->ad_data,
1528 if (errno == ENOATTR) {
1534 DEBUG(2, ("error reading meta xattr: %s\n",
1540 if (ealen != AD_DATASZ_XATTR) {
1541 DEBUG(2, ("bad size %zd\n", ealen));
1547 /* Now parse entries */
1548 ok = ad_unpack(ad, ADEID_NUM_XATTR, AD_DATASZ_XATTR);
1550 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1556 if (!ad_getentryoff(ad, ADEID_FINDERI)
1557 || !ad_getentryoff(ad, ADEID_COMMENT)
1558 || !ad_getentryoff(ad, ADEID_FILEDATESI)
1559 || !ad_getentryoff(ad, ADEID_AFPFILEI)
1560 || !ad_getentryoff(ad, ADEID_PRIVDEV)
1561 || !ad_getentryoff(ad, ADEID_PRIVINO)
1562 || !ad_getentryoff(ad, ADEID_PRIVSYN)
1563 || !ad_getentryoff(ad, ADEID_PRIVID)) {
1564 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1571 DEBUG(10, ("reading meta xattr for %s, rc: %d\n",
1572 smb_fname->base_name, rc));
1576 if (errno == EINVAL) {
1578 removexattr(smb_fname->base_name, AFPINFO_EA_NETATALK);
1586 static int ad_open_rsrc_xattr(const struct smb_filename *smb_fname,
1590 #ifdef HAVE_ATTROPEN
1591 /* FIXME: direct Solaris xattr syscall */
1592 return attropen(smb_fname->base_name,
1593 AFPRESOURCE_EA_NETATALK, flags, mode);
1600 static int ad_open_rsrc_adouble(const struct smb_filename *smb_fname,
1606 struct smb_filename *adp_smb_fname = NULL;
1608 ret = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
1613 fd = open(adp_smb_fname->base_name, flags, mode);
1614 TALLOC_FREE(adp_smb_fname);
1619 static int ad_open_rsrc(vfs_handle_struct *handle,
1620 const struct smb_filename *smb_fname,
1624 struct fruit_config_data *config = NULL;
1627 SMB_VFS_HANDLE_GET_DATA(handle, config,
1628 struct fruit_config_data, return -1);
1630 if (config->rsrc == FRUIT_RSRC_XATTR) {
1631 fd = ad_open_rsrc_xattr(smb_fname, flags, mode);
1633 fd = ad_open_rsrc_adouble(smb_fname, flags, mode);
1640 * Here's the deal: for ADOUBLE_META we can do without an fd as we can issue
1641 * path based xattr calls. For ADOUBLE_RSRC however we need a full-fledged fd
1642 * for file IO on the ._ file.
1644 static int ad_open(vfs_handle_struct *handle,
1647 const struct smb_filename *smb_fname,
1653 DBG_DEBUG("Path [%s] type [%s]\n", smb_fname->base_name,
1654 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
1656 if (ad->ad_type == ADOUBLE_META) {
1660 if ((fsp != NULL) && (fsp->fh != NULL) && (fsp->fh->fd != -1)) {
1661 ad->ad_fd = fsp->fh->fd;
1662 ad->ad_opened = false;
1666 fd = ad_open_rsrc(handle, smb_fname, flags, mode);
1670 ad->ad_opened = true;
1673 DBG_DEBUG("Path [%s] type [%s] fd [%d]\n",
1674 smb_fname->base_name,
1675 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc", fd);
1680 static ssize_t ad_read_rsrc_xattr(struct adouble *ad)
1685 /* FIXME: direct sys_fstat(), don't have an fsp */
1686 ret = sys_fstat(ad->ad_fd, &st,
1687 lp_fake_directory_create_times(
1688 SNUM(ad->ad_handle->conn)));
1693 ad_setentrylen(ad, ADEID_RFORK, st.st_ex_size);
1694 return st.st_ex_size;
1697 static ssize_t ad_read_rsrc_adouble(struct adouble *ad,
1698 const struct smb_filename *smb_fname)
1700 SMB_STRUCT_STAT sbuf;
1707 ret = sys_fstat(ad->ad_fd, &sbuf, lp_fake_directory_create_times(
1708 SNUM(ad->ad_handle->conn)));
1714 * AppleDouble file header content and size, two cases:
1716 * - without xattrs it is exactly AD_DATASZ_DOT_UND (82) bytes large
1717 * - with embedded xattrs it can be larger, up to AD_XATTR_MAX_HDR_SIZE
1719 * Read as much as we can up to AD_XATTR_MAX_HDR_SIZE.
1721 size = sbuf.st_ex_size;
1722 if (size > talloc_array_length(ad->ad_data)) {
1723 if (size > AD_XATTR_MAX_HDR_SIZE) {
1724 size = AD_XATTR_MAX_HDR_SIZE;
1726 p_ad = talloc_realloc(ad, ad->ad_data, char, size);
1733 len = sys_pread(ad->ad_fd, ad->ad_data,
1734 talloc_array_length(ad->ad_data), 0);
1735 if (len != talloc_array_length(ad->ad_data)) {
1736 DBG_NOTICE("%s %s: bad size: %zd\n",
1737 smb_fname->base_name, strerror(errno), len);
1741 /* Now parse entries */
1742 ok = ad_unpack(ad, ADEID_NUM_DOT_UND, sbuf.st_ex_size);
1744 DBG_ERR("invalid AppleDouble resource %s\n",
1745 smb_fname->base_name);
1750 if ((ad_getentryoff(ad, ADEID_FINDERI) != ADEDOFF_FINDERI_DOT_UND)
1751 || (ad_getentrylen(ad, ADEID_FINDERI) < ADEDLEN_FINDERI)
1752 || (ad_getentryoff(ad, ADEID_RFORK) < ADEDOFF_RFORK_DOT_UND)) {
1753 DBG_ERR("invalid AppleDouble resource %s\n",
1754 smb_fname->base_name);
1763 * Read and parse resource fork, either ._ AppleDouble file or xattr
1765 static ssize_t ad_read_rsrc(struct adouble *ad,
1766 const struct smb_filename *smb_fname)
1768 struct fruit_config_data *config = NULL;
1771 SMB_VFS_HANDLE_GET_DATA(ad->ad_handle, config,
1772 struct fruit_config_data, return -1);
1774 if (config->rsrc == FRUIT_RSRC_XATTR) {
1775 len = ad_read_rsrc_xattr(ad);
1777 len = ad_read_rsrc_adouble(ad, smb_fname);
1784 * Read and unpack an AppleDouble metadata xattr or resource
1786 static ssize_t ad_read(struct adouble *ad, const struct smb_filename *smb_fname)
1788 switch (ad->ad_type) {
1790 return ad_read_meta(ad, smb_fname);
1792 return ad_read_rsrc(ad, smb_fname);
1798 static int adouble_destructor(struct adouble *ad)
1800 if ((ad->ad_fd != -1) && ad->ad_opened) {
1808 * Allocate a struct adouble without initialiing it
1810 * The struct is either hang of the fsp extension context or if fsp is
1813 * @param[in] ctx talloc context
1814 * @param[in] handle vfs handle
1815 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1817 * @return adouble handle
1819 static struct adouble *ad_alloc(TALLOC_CTX *ctx, vfs_handle_struct *handle,
1820 adouble_type_t type)
1825 struct fruit_config_data *config;
1827 SMB_VFS_HANDLE_GET_DATA(handle, config,
1828 struct fruit_config_data, return NULL);
1832 adsize = AD_DATASZ_XATTR;
1835 if (config->rsrc == FRUIT_RSRC_ADFILE) {
1836 adsize = AD_DATASZ_DOT_UND;
1843 ad = talloc_zero(ctx, struct adouble);
1850 ad->ad_data = talloc_zero_array(ad, char, adsize);
1851 if (ad->ad_data == NULL) {
1857 ad->ad_handle = handle;
1859 ad->ad_magic = AD_MAGIC;
1860 ad->ad_version = AD_VERSION;
1863 talloc_set_destructor(ad, adouble_destructor);
1873 * Allocate and initialize a new struct adouble
1875 * @param[in] ctx talloc context
1876 * @param[in] handle vfs handle
1877 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1879 * @return adouble handle, initialized
1881 static struct adouble *ad_init(TALLOC_CTX *ctx, vfs_handle_struct *handle,
1882 adouble_type_t type)
1885 const struct ad_entry_order *eid;
1886 struct adouble *ad = NULL;
1887 struct fruit_config_data *config;
1888 time_t t = time(NULL);
1890 SMB_VFS_HANDLE_GET_DATA(handle, config,
1891 struct fruit_config_data, return NULL);
1895 eid = entry_order_meta_xattr;
1898 if (config->rsrc == FRUIT_RSRC_ADFILE) {
1899 eid = entry_order_dot_und;
1901 eid = entry_order_rsrc_xattr;
1908 ad = ad_alloc(ctx, handle, type);
1914 ad->ad_eid[eid->id].ade_off = eid->offset;
1915 ad->ad_eid[eid->id].ade_len = eid->len;
1919 /* put something sane in the date fields */
1920 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, t);
1921 ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, t);
1922 ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, t);
1923 ad_setdate(ad, AD_DATE_BACKUP, htonl(AD_DATE_START));
1931 static struct adouble *ad_get_internal(TALLOC_CTX *ctx,
1932 vfs_handle_struct *handle,
1934 const struct smb_filename *smb_fname,
1935 adouble_type_t type)
1939 struct adouble *ad = NULL;
1943 smb_fname = fsp->base_fsp->fsp_name;
1946 DEBUG(10, ("ad_get(%s) called for %s\n",
1947 type == ADOUBLE_META ? "meta" : "rsrc",
1948 smb_fname->base_name));
1950 ad = ad_alloc(ctx, handle, type);
1956 /* Try rw first so we can use the fd in ad_convert() */
1959 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
1960 if (rc == -1 && ((errno == EROFS) || (errno == EACCES))) {
1962 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
1965 DBG_DEBUG("ad_open [%s] error [%s]\n",
1966 smb_fname->base_name, strerror(errno));
1971 len = ad_read(ad, smb_fname);
1973 DEBUG(10, ("error reading AppleDouble for %s\n",
1974 smb_fname->base_name));
1980 DEBUG(10, ("ad_get(%s) for %s returning %d\n",
1981 type == ADOUBLE_META ? "meta" : "rsrc",
1982 smb_fname->base_name, rc));
1991 * Return AppleDouble data for a file
1993 * @param[in] ctx talloc context
1994 * @param[in] handle vfs handle
1995 * @param[in] smb_fname pathname to file or directory
1996 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1998 * @return talloced struct adouble or NULL on error
2000 static struct adouble *ad_get(TALLOC_CTX *ctx,
2001 vfs_handle_struct *handle,
2002 const struct smb_filename *smb_fname,
2003 adouble_type_t type)
2005 return ad_get_internal(ctx, handle, NULL, smb_fname, type);
2009 * Return AppleDouble data for a file
2011 * @param[in] ctx talloc context
2012 * @param[in] handle vfs handle
2013 * @param[in] fsp fsp to use for IO
2014 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2016 * @return talloced struct adouble or NULL on error
2018 static struct adouble *ad_fget(TALLOC_CTX *ctx, vfs_handle_struct *handle,
2019 files_struct *fsp, adouble_type_t type)
2021 return ad_get_internal(ctx, handle, fsp, NULL, type);
2025 * Set AppleDouble metadata on a file or directory
2027 * @param[in] ad adouble handle
2029 * @param[in] smb_fname pathname to file or directory
2031 * @return status code, 0 means success
2033 static int ad_set(struct adouble *ad, const struct smb_filename *smb_fname)
2038 DBG_DEBUG("Path [%s]\n", smb_fname->base_name);
2040 if (ad->ad_type != ADOUBLE_META) {
2041 DBG_ERR("ad_set on [%s] used with ADOUBLE_RSRC\n",
2042 smb_fname->base_name);
2051 ret = SMB_VFS_SETXATTR(ad->ad_handle->conn,
2053 AFPINFO_EA_NETATALK,
2055 AD_DATASZ_XATTR, 0);
2057 DBG_DEBUG("Path [%s] ret [%d]\n", smb_fname->base_name, ret);
2063 * Set AppleDouble metadata on a file or directory
2065 * @param[in] ad adouble handle
2066 * @param[in] fsp file handle
2068 * @return status code, 0 means success
2070 static int ad_fset(struct adouble *ad, files_struct *fsp)
2076 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
2079 || (fsp->fh == NULL)
2080 || (fsp->fh->fd == -1))
2082 smb_panic("bad fsp");
2090 switch (ad->ad_type) {
2092 rc = SMB_VFS_NEXT_SETXATTR(ad->ad_handle,
2094 AFPINFO_EA_NETATALK,
2096 AD_DATASZ_XATTR, 0);
2100 len = SMB_VFS_NEXT_PWRITE(ad->ad_handle,
2105 if (len != AD_DATASZ_DOT_UND) {
2106 DBG_ERR("short write on %s: %zd", fsp_str_dbg(fsp), len);
2116 DBG_DEBUG("Path [%s] rc [%d]\n", fsp_str_dbg(fsp), rc);
2121 /*****************************************************************************
2123 *****************************************************************************/
2125 static bool is_afpinfo_stream(const struct smb_filename *smb_fname)
2127 if (strncasecmp_m(smb_fname->stream_name,
2128 AFPINFO_STREAM_NAME,
2129 strlen(AFPINFO_STREAM_NAME)) == 0) {
2135 static bool is_afpresource_stream(const struct smb_filename *smb_fname)
2137 if (strncasecmp_m(smb_fname->stream_name,
2138 AFPRESOURCE_STREAM_NAME,
2139 strlen(AFPRESOURCE_STREAM_NAME)) == 0) {
2146 * Test whether stream is an Apple stream.
2148 static bool is_apple_stream(const struct smb_filename *smb_fname)
2150 if (is_afpinfo_stream(smb_fname)) {
2153 if (is_afpresource_stream(smb_fname)) {
2160 * Initialize config struct from our smb.conf config parameters
2162 static int init_fruit_config(vfs_handle_struct *handle)
2164 struct fruit_config_data *config;
2166 const char *tm_size_str = NULL;
2168 config = talloc_zero(handle->conn, struct fruit_config_data);
2170 DEBUG(1, ("talloc_zero() failed\n"));
2176 * Versions up to Samba 4.5.x had a spelling bug in the
2177 * fruit:resource option calling lp_parm_enum with
2178 * "res*s*ource" (ie two s).
2180 * In Samba 4.6 we accept both the wrong and the correct
2181 * spelling, in Samba 4.7 the bad spelling will be removed.
2183 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2184 "ressource", fruit_rsrc, FRUIT_RSRC_ADFILE);
2185 if (enumval == -1) {
2186 DEBUG(1, ("value for %s: resource type unknown\n",
2187 FRUIT_PARAM_TYPE_NAME));
2190 config->rsrc = (enum fruit_rsrc)enumval;
2192 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2193 "resource", fruit_rsrc, enumval);
2194 if (enumval == -1) {
2195 DEBUG(1, ("value for %s: resource type unknown\n",
2196 FRUIT_PARAM_TYPE_NAME));
2199 config->rsrc = (enum fruit_rsrc)enumval;
2201 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2202 "metadata", fruit_meta, FRUIT_META_NETATALK);
2203 if (enumval == -1) {
2204 DEBUG(1, ("value for %s: metadata type unknown\n",
2205 FRUIT_PARAM_TYPE_NAME));
2208 config->meta = (enum fruit_meta)enumval;
2210 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2211 "locking", fruit_locking, FRUIT_LOCKING_NONE);
2212 if (enumval == -1) {
2213 DEBUG(1, ("value for %s: locking type unknown\n",
2214 FRUIT_PARAM_TYPE_NAME));
2217 config->locking = (enum fruit_locking)enumval;
2219 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2220 "encoding", fruit_encoding, FRUIT_ENC_PRIVATE);
2221 if (enumval == -1) {
2222 DEBUG(1, ("value for %s: encoding type unknown\n",
2223 FRUIT_PARAM_TYPE_NAME));
2226 config->encoding = (enum fruit_encoding)enumval;
2228 if (config->rsrc == FRUIT_RSRC_ADFILE) {
2229 config->veto_appledouble = lp_parm_bool(SNUM(handle->conn),
2230 FRUIT_PARAM_TYPE_NAME,
2235 config->use_aapl = lp_parm_bool(
2236 -1, FRUIT_PARAM_TYPE_NAME, "aapl", true);
2238 config->time_machine = lp_parm_bool(
2239 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "time machine", false);
2241 config->unix_info_enabled = lp_parm_bool(
2242 -1, FRUIT_PARAM_TYPE_NAME, "nfs_aces", true);
2244 config->use_copyfile = lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME,
2247 config->posix_rename = lp_parm_bool(
2248 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "posix_rename", true);
2250 config->aapl_zero_file_id =
2251 lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME, "zero_file_id", true);
2253 config->readdir_attr_rsize = lp_parm_bool(
2254 SNUM(handle->conn), "readdir_attr", "aapl_rsize", true);
2256 config->readdir_attr_finder_info = lp_parm_bool(
2257 SNUM(handle->conn), "readdir_attr", "aapl_finder_info", true);
2259 config->readdir_attr_max_access = lp_parm_bool(
2260 SNUM(handle->conn), "readdir_attr", "aapl_max_access", true);
2262 config->model = lp_parm_const_string(
2263 -1, FRUIT_PARAM_TYPE_NAME, "model", "MacSamba");
2265 tm_size_str = lp_parm_const_string(
2266 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2267 "time machine max size", NULL);
2268 if (tm_size_str != NULL) {
2269 config->time_machine_max_size = conv_str_size(tm_size_str);
2272 config->wipe_intentionally_left_blank_rfork = lp_parm_bool(
2273 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2274 "wipe_intentionally_left_blank_rfork", false);
2276 config->delete_empty_adfiles = lp_parm_bool(
2277 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2278 "delete_empty_adfiles", false);
2280 SMB_VFS_HANDLE_SET_DATA(handle, config,
2281 NULL, struct fruit_config_data,
2288 * Prepend "._" to a basename
2289 * Return a new struct smb_filename with stream_name == NULL.
2291 static int adouble_path(TALLOC_CTX *ctx,
2292 const struct smb_filename *smb_fname_in,
2293 struct smb_filename **pp_smb_fname_out)
2297 struct smb_filename *smb_fname = cp_smb_filename(ctx,
2300 if (smb_fname == NULL) {
2304 /* We need streamname to be NULL */
2305 TALLOC_FREE(smb_fname->stream_name);
2307 /* And we're replacing base_name. */
2308 TALLOC_FREE(smb_fname->base_name);
2310 if (!parent_dirname(smb_fname, smb_fname_in->base_name,
2312 TALLOC_FREE(smb_fname);
2316 smb_fname->base_name = talloc_asprintf(smb_fname,
2317 "%s/._%s", parent, base);
2318 if (smb_fname->base_name == NULL) {
2319 TALLOC_FREE(smb_fname);
2323 *pp_smb_fname_out = smb_fname;
2329 * Allocate and initialize an AfpInfo struct
2331 static AfpInfo *afpinfo_new(TALLOC_CTX *ctx)
2333 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2337 ai->afpi_Signature = AFP_Signature;
2338 ai->afpi_Version = AFP_Version;
2339 ai->afpi_BackupTime = AD_DATE_START;
2344 * Pack an AfpInfo struct into a buffer
2346 * Buffer size must be at least AFP_INFO_SIZE
2347 * Returns size of packed buffer
2349 static ssize_t afpinfo_pack(const AfpInfo *ai, char *buf)
2351 memset(buf, 0, AFP_INFO_SIZE);
2353 RSIVAL(buf, 0, ai->afpi_Signature);
2354 RSIVAL(buf, 4, ai->afpi_Version);
2355 RSIVAL(buf, 12, ai->afpi_BackupTime);
2356 memcpy(buf + 16, ai->afpi_FinderInfo, sizeof(ai->afpi_FinderInfo));
2358 return AFP_INFO_SIZE;
2362 * Unpack a buffer into a AfpInfo structure
2364 * Buffer size must be at least AFP_INFO_SIZE
2365 * Returns allocated AfpInfo struct
2367 static AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data)
2369 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2374 ai->afpi_Signature = RIVAL(data, 0);
2375 ai->afpi_Version = RIVAL(data, 4);
2376 ai->afpi_BackupTime = RIVAL(data, 12);
2377 memcpy(ai->afpi_FinderInfo, (const char *)data + 16,
2378 sizeof(ai->afpi_FinderInfo));
2380 if (ai->afpi_Signature != AFP_Signature
2381 || ai->afpi_Version != AFP_Version) {
2382 DEBUG(1, ("Bad AfpInfo signature or version\n"));
2390 * Fake an inode number from the md5 hash of the (xattr) name
2392 static SMB_INO_T fruit_inode(const SMB_STRUCT_STAT *sbuf, const char *sname)
2394 gnutls_hash_hd_t hash_hnd = NULL;
2395 unsigned char hash[16];
2396 SMB_INO_T result = 0;
2400 DBG_DEBUG("fruit_inode called for %ju/%ju [%s]\n",
2401 (uintmax_t)sbuf->st_ex_dev,
2402 (uintmax_t)sbuf->st_ex_ino, sname);
2404 upper_sname = talloc_strdup_upper(talloc_tos(), sname);
2405 SMB_ASSERT(upper_sname != NULL);
2407 rc = gnutls_hash_init(&hash_hnd, GNUTLS_DIG_MD5);
2412 rc = gnutls_hash(hash_hnd, &(sbuf->st_ex_dev), sizeof(sbuf->st_ex_dev));
2414 gnutls_hash_deinit(hash_hnd, NULL);
2417 rc = gnutls_hash(hash_hnd,
2419 sizeof(sbuf->st_ex_ino));
2421 gnutls_hash_deinit(hash_hnd, NULL);
2424 rc = gnutls_hash(hash_hnd,
2426 talloc_get_size(upper_sname) - 1);
2428 gnutls_hash_deinit(hash_hnd, NULL);
2432 gnutls_hash_deinit(hash_hnd, hash);
2434 /* Hopefully all the variation is in the lower 4 (or 8) bytes! */
2435 memcpy(&result, hash, sizeof(result));
2438 DBG_DEBUG("fruit_inode \"%s\": ino=%ju\n",
2439 sname, (uintmax_t)result);
2442 TALLOC_FREE(upper_sname);
2447 static bool add_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
2448 struct stream_struct **streams,
2449 const char *name, off_t size,
2452 struct stream_struct *tmp;
2454 tmp = talloc_realloc(mem_ctx, *streams, struct stream_struct,
2460 tmp[*num_streams].name = talloc_asprintf(tmp, "%s:$DATA", name);
2461 if (tmp[*num_streams].name == NULL) {
2465 tmp[*num_streams].size = size;
2466 tmp[*num_streams].alloc_size = alloc_size;
2473 static bool filter_empty_rsrc_stream(unsigned int *num_streams,
2474 struct stream_struct **streams)
2476 struct stream_struct *tmp = *streams;
2479 if (*num_streams == 0) {
2483 for (i = 0; i < *num_streams; i++) {
2484 if (strequal_m(tmp[i].name, AFPRESOURCE_STREAM)) {
2489 if (i == *num_streams) {
2493 if (tmp[i].size > 0) {
2497 TALLOC_FREE(tmp[i].name);
2498 if (*num_streams - 1 > i) {
2499 memmove(&tmp[i], &tmp[i+1],
2500 (*num_streams - i - 1) * sizeof(struct stream_struct));
2507 static bool del_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
2508 struct stream_struct **streams,
2511 struct stream_struct *tmp = *streams;
2514 if (*num_streams == 0) {
2518 for (i = 0; i < *num_streams; i++) {
2519 if (strequal_m(tmp[i].name, name)) {
2524 if (i == *num_streams) {
2528 TALLOC_FREE(tmp[i].name);
2529 if (*num_streams - 1 > i) {
2530 memmove(&tmp[i], &tmp[i+1],
2531 (*num_streams - i - 1) * sizeof(struct stream_struct));
2538 static bool ad_empty_finderinfo(const struct adouble *ad)
2541 char emptybuf[ADEDLEN_FINDERI] = {0};
2544 fi = ad_get_entry(ad, ADEID_FINDERI);
2546 DBG_ERR("Missing FinderInfo in struct adouble [%p]\n", ad);
2550 cmp = memcmp(emptybuf, fi, ADEDLEN_FINDERI);
2554 static bool ai_empty_finderinfo(const AfpInfo *ai)
2557 char emptybuf[ADEDLEN_FINDERI] = {0};
2559 cmp = memcmp(emptybuf, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
2564 * Update btime with btime from Netatalk
2566 static void update_btime(vfs_handle_struct *handle,
2567 struct smb_filename *smb_fname)
2570 struct timespec creation_time = {0};
2572 struct fruit_config_data *config = NULL;
2574 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
2577 switch (config->meta) {
2578 case FRUIT_META_STREAM:
2580 case FRUIT_META_NETATALK:
2584 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
2588 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
2592 if (ad_getdate(ad, AD_DATE_UNIX | AD_DATE_CREATE, &t) != 0) {
2598 creation_time.tv_sec = convert_uint32_t_to_time_t(t);
2599 update_stat_ex_create_time(&smb_fname->st, creation_time);
2605 * Map an access mask to a Netatalk single byte byte range lock
2607 static off_t access_to_netatalk_brl(enum apple_fork fork_type,
2608 uint32_t access_mask)
2612 switch (access_mask) {
2613 case FILE_READ_DATA:
2614 offset = AD_FILELOCK_OPEN_RD;
2617 case FILE_WRITE_DATA:
2618 case FILE_APPEND_DATA:
2619 offset = AD_FILELOCK_OPEN_WR;
2623 offset = AD_FILELOCK_OPEN_NONE;
2627 if (fork_type == APPLE_FORK_RSRC) {
2628 if (offset == AD_FILELOCK_OPEN_NONE) {
2629 offset = AD_FILELOCK_RSRC_OPEN_NONE;
2639 * Map a deny mode to a Netatalk brl
2641 static off_t denymode_to_netatalk_brl(enum apple_fork fork_type,
2646 switch (deny_mode) {
2648 offset = AD_FILELOCK_DENY_RD;
2652 offset = AD_FILELOCK_DENY_WR;
2656 smb_panic("denymode_to_netatalk_brl: bad deny mode\n");
2659 if (fork_type == APPLE_FORK_RSRC) {
2667 * Call fcntl() with an exclusive F_GETLK request in order to
2668 * determine if there's an exisiting shared lock
2670 * @return true if the requested lock was found or any error occurred
2671 * false if the lock was not found
2673 static bool test_netatalk_lock(files_struct *fsp, off_t in_offset)
2676 off_t offset = in_offset;
2681 result = SMB_VFS_GETLOCK(fsp, &offset, &len, &type, &pid);
2682 if (result == false) {
2686 if (type != F_UNLCK) {
2693 static NTSTATUS fruit_check_access(vfs_handle_struct *handle,
2695 uint32_t access_mask,
2696 uint32_t share_mode)
2698 NTSTATUS status = NT_STATUS_OK;
2700 bool share_for_read = (share_mode & FILE_SHARE_READ);
2701 bool share_for_write = (share_mode & FILE_SHARE_WRITE);
2702 bool netatalk_already_open_for_reading = false;
2703 bool netatalk_already_open_for_writing = false;
2704 bool netatalk_already_open_with_deny_read = false;
2705 bool netatalk_already_open_with_deny_write = false;
2707 /* FIXME: hardcoded data fork, add resource fork */
2708 enum apple_fork fork_type = APPLE_FORK_DATA;
2710 DBG_DEBUG("fruit_check_access: %s, am: %s/%s, sm: 0x%x\n",
2712 access_mask & FILE_READ_DATA ? "READ" :"-",
2713 access_mask & FILE_WRITE_DATA ? "WRITE" : "-",
2716 if (fsp->fh->fd == -1) {
2717 return NT_STATUS_OK;
2720 /* Read NetATalk opens and deny modes on the file. */
2721 netatalk_already_open_for_reading = test_netatalk_lock(fsp,
2722 access_to_netatalk_brl(fork_type,
2725 netatalk_already_open_with_deny_read = test_netatalk_lock(fsp,
2726 denymode_to_netatalk_brl(fork_type,
2729 netatalk_already_open_for_writing = test_netatalk_lock(fsp,
2730 access_to_netatalk_brl(fork_type,
2733 netatalk_already_open_with_deny_write = test_netatalk_lock(fsp,
2734 denymode_to_netatalk_brl(fork_type,
2737 /* If there are any conflicts - sharing violation. */
2738 if ((access_mask & FILE_READ_DATA) &&
2739 netatalk_already_open_with_deny_read) {
2740 return NT_STATUS_SHARING_VIOLATION;
2743 if (!share_for_read &&
2744 netatalk_already_open_for_reading) {
2745 return NT_STATUS_SHARING_VIOLATION;
2748 if ((access_mask & FILE_WRITE_DATA) &&
2749 netatalk_already_open_with_deny_write) {
2750 return NT_STATUS_SHARING_VIOLATION;
2753 if (!share_for_write &&
2754 netatalk_already_open_for_writing) {
2755 return NT_STATUS_SHARING_VIOLATION;
2758 if (!(access_mask & FILE_READ_DATA)) {
2760 * Nothing we can do here, we need read access
2763 return NT_STATUS_OK;
2766 /* Set NetAtalk locks matching our access */
2767 if (access_mask & FILE_READ_DATA) {
2768 struct byte_range_lock *br_lck = NULL;
2770 off = access_to_netatalk_brl(fork_type, FILE_READ_DATA);
2772 handle->conn->sconn->msg_ctx, fsp,
2773 fsp->op->global->open_persistent_id, 1, off,
2774 READ_LOCK, POSIX_LOCK, false,
2777 TALLOC_FREE(br_lck);
2779 if (!NT_STATUS_IS_OK(status)) {
2784 if (!share_for_read) {
2785 struct byte_range_lock *br_lck = NULL;
2787 off = denymode_to_netatalk_brl(fork_type, DENY_READ);
2789 handle->conn->sconn->msg_ctx, fsp,
2790 fsp->op->global->open_persistent_id, 1, off,
2791 READ_LOCK, POSIX_LOCK, false,
2794 TALLOC_FREE(br_lck);
2796 if (!NT_STATUS_IS_OK(status)) {
2801 if (access_mask & FILE_WRITE_DATA) {
2802 struct byte_range_lock *br_lck = NULL;
2804 off = access_to_netatalk_brl(fork_type, FILE_WRITE_DATA);
2806 handle->conn->sconn->msg_ctx, fsp,
2807 fsp->op->global->open_persistent_id, 1, off,
2808 READ_LOCK, POSIX_LOCK, false,
2811 TALLOC_FREE(br_lck);
2813 if (!NT_STATUS_IS_OK(status)) {
2818 if (!share_for_write) {
2819 struct byte_range_lock *br_lck = NULL;
2821 off = denymode_to_netatalk_brl(fork_type, DENY_WRITE);
2823 handle->conn->sconn->msg_ctx, fsp,
2824 fsp->op->global->open_persistent_id, 1, off,
2825 READ_LOCK, POSIX_LOCK, false,
2828 TALLOC_FREE(br_lck);
2830 if (!NT_STATUS_IS_OK(status)) {
2835 return NT_STATUS_OK;
2838 static NTSTATUS check_aapl(vfs_handle_struct *handle,
2839 struct smb_request *req,
2840 const struct smb2_create_blobs *in_context_blobs,
2841 struct smb2_create_blobs *out_context_blobs)
2843 struct fruit_config_data *config;
2845 struct smb2_create_blob *aapl = NULL;
2849 DATA_BLOB blob = data_blob_talloc(req, NULL, 0);
2850 uint64_t req_bitmap, client_caps;
2851 uint64_t server_caps = SMB2_CRTCTX_AAPL_UNIX_BASED;
2855 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
2856 return NT_STATUS_UNSUCCESSFUL);
2858 if (!config->use_aapl
2859 || in_context_blobs == NULL
2860 || out_context_blobs == NULL) {
2861 return NT_STATUS_OK;
2864 aapl = smb2_create_blob_find(in_context_blobs,
2865 SMB2_CREATE_TAG_AAPL);
2867 return NT_STATUS_OK;
2870 if (aapl->data.length != 24) {
2871 DEBUG(1, ("unexpected AAPL ctxt length: %ju\n",
2872 (uintmax_t)aapl->data.length));
2873 return NT_STATUS_INVALID_PARAMETER;
2876 cmd = IVAL(aapl->data.data, 0);
2877 if (cmd != SMB2_CRTCTX_AAPL_SERVER_QUERY) {
2878 DEBUG(1, ("unsupported AAPL cmd: %d\n", cmd));
2879 return NT_STATUS_INVALID_PARAMETER;
2882 req_bitmap = BVAL(aapl->data.data, 8);
2883 client_caps = BVAL(aapl->data.data, 16);
2885 SIVAL(p, 0, SMB2_CRTCTX_AAPL_SERVER_QUERY);
2887 SBVAL(p, 8, req_bitmap);
2888 ok = data_blob_append(req, &blob, p, 16);
2890 return NT_STATUS_UNSUCCESSFUL;
2893 if (req_bitmap & SMB2_CRTCTX_AAPL_SERVER_CAPS) {
2894 if ((client_caps & SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR) &&
2895 (handle->conn->tcon->compat->fs_capabilities & FILE_NAMED_STREAMS)) {
2896 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR;
2897 config->readdir_attr_enabled = true;
2900 if (config->use_copyfile) {
2901 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_OSX_COPYFILE;
2902 config->copyfile_enabled = true;
2906 * The client doesn't set the flag, so we can't check
2907 * for it and just set it unconditionally
2909 if (config->unix_info_enabled) {
2910 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_NFS_ACE;
2913 SBVAL(p, 0, server_caps);
2914 ok = data_blob_append(req, &blob, p, 8);
2916 return NT_STATUS_UNSUCCESSFUL;
2920 if (req_bitmap & SMB2_CRTCTX_AAPL_VOLUME_CAPS) {
2921 int val = lp_case_sensitive(SNUM(handle->conn->tcon->compat));
2929 caps |= SMB2_CRTCTX_AAPL_CASE_SENSITIVE;
2936 if (config->time_machine) {
2937 caps |= SMB2_CRTCTX_AAPL_FULL_SYNC;
2942 ok = data_blob_append(req, &blob, p, 8);
2944 return NT_STATUS_UNSUCCESSFUL;
2948 if (req_bitmap & SMB2_CRTCTX_AAPL_MODEL_INFO) {
2949 ok = convert_string_talloc(req,
2950 CH_UNIX, CH_UTF16LE,
2951 config->model, strlen(config->model),
2954 return NT_STATUS_UNSUCCESSFUL;
2958 SIVAL(p + 4, 0, modellen);
2959 ok = data_blob_append(req, &blob, p, 8);
2962 return NT_STATUS_UNSUCCESSFUL;
2965 ok = data_blob_append(req, &blob, model, modellen);
2968 return NT_STATUS_UNSUCCESSFUL;
2972 status = smb2_create_blob_add(out_context_blobs,
2974 SMB2_CREATE_TAG_AAPL,
2976 if (NT_STATUS_IS_OK(status)) {
2977 global_fruit_config.nego_aapl = true;
2978 if (config->aapl_zero_file_id) {
2979 aapl_force_zero_file_id(handle->conn->sconn);
2986 static bool readdir_attr_meta_finderi_stream(
2987 struct vfs_handle_struct *handle,
2988 const struct smb_filename *smb_fname,
2991 struct smb_filename *stream_name = NULL;
2992 files_struct *fsp = NULL;
2997 uint8_t buf[AFP_INFO_SIZE];
2999 stream_name = synthetic_smb_fname(talloc_tos(),
3000 smb_fname->base_name,
3001 AFPINFO_STREAM_NAME,
3002 NULL, smb_fname->flags);
3003 if (stream_name == NULL) {
3007 ret = SMB_VFS_STAT(handle->conn, stream_name);
3012 status = SMB_VFS_CREATE_FILE(
3013 handle->conn, /* conn */
3015 0, /* root_dir_fid */
3016 stream_name, /* fname */
3017 FILE_READ_DATA, /* access_mask */
3018 (FILE_SHARE_READ | FILE_SHARE_WRITE | /* share_access */
3020 FILE_OPEN, /* create_disposition*/
3021 0, /* create_options */
3022 0, /* file_attributes */
3023 INTERNAL_OPEN_ONLY, /* oplock_request */
3025 0, /* allocation_size */
3026 0, /* private_flags */
3031 NULL, NULL); /* create context */
3033 TALLOC_FREE(stream_name);
3035 if (!NT_STATUS_IS_OK(status)) {
3039 nread = SMB_VFS_PREAD(fsp, &buf[0], AFP_INFO_SIZE, 0);
3040 if (nread != AFP_INFO_SIZE) {
3041 DBG_ERR("short read [%s] [%zd/%d]\n",
3042 smb_fname_str_dbg(stream_name), nread, AFP_INFO_SIZE);
3047 memcpy(&ai->afpi_FinderInfo[0], &buf[AFP_OFF_FinderInfo],
3054 close_file(NULL, fsp, NORMAL_CLOSE);
3060 static bool readdir_attr_meta_finderi_netatalk(
3061 struct vfs_handle_struct *handle,
3062 const struct smb_filename *smb_fname,
3065 struct adouble *ad = NULL;
3068 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
3073 p = ad_get_entry(ad, ADEID_FINDERI);
3075 DBG_ERR("No ADEID_FINDERI for [%s]\n", smb_fname->base_name);
3080 memcpy(&ai->afpi_FinderInfo[0], p, AFP_FinderSize);
3085 static bool readdir_attr_meta_finderi(struct vfs_handle_struct *handle,
3086 const struct smb_filename *smb_fname,
3087 struct readdir_attr_data *attr_data)
3089 struct fruit_config_data *config = NULL;
3090 uint32_t date_added;
3094 SMB_VFS_HANDLE_GET_DATA(handle, config,
3095 struct fruit_config_data,
3098 switch (config->meta) {
3099 case FRUIT_META_NETATALK:
3100 ok = readdir_attr_meta_finderi_netatalk(
3101 handle, smb_fname, &ai);
3104 case FRUIT_META_STREAM:
3105 ok = readdir_attr_meta_finderi_stream(
3106 handle, smb_fname, &ai);
3110 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3115 /* Don't bother with errors, it's likely ENOENT */
3119 if (S_ISREG(smb_fname->st.st_ex_mode)) {
3121 memcpy(&attr_data->attr_data.aapl.finder_info[0],
3122 &ai.afpi_FinderInfo[0], 4);
3124 /* finder_creator */
3125 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 4,
3126 &ai.afpi_FinderInfo[4], 4);
3130 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 8,
3131 &ai.afpi_FinderInfo[8], 2);
3133 /* finder_ext_flags */
3134 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 10,
3135 &ai.afpi_FinderInfo[24], 2);
3138 date_added = convert_time_t_to_uint32_t(
3139 smb_fname->st.st_ex_btime.tv_sec - AD_DATE_DELTA);
3141 RSIVAL(&attr_data->attr_data.aapl.finder_info[0], 12, date_added);
3146 static uint64_t readdir_attr_rfork_size_adouble(
3147 struct vfs_handle_struct *handle,
3148 const struct smb_filename *smb_fname)
3150 struct adouble *ad = NULL;
3151 uint64_t rfork_size;
3153 ad = ad_get(talloc_tos(), handle, smb_fname,
3159 rfork_size = ad_getentrylen(ad, ADEID_RFORK);
3165 static uint64_t readdir_attr_rfork_size_stream(
3166 struct vfs_handle_struct *handle,
3167 const struct smb_filename *smb_fname)
3169 struct smb_filename *stream_name = NULL;
3171 uint64_t rfork_size;
3173 stream_name = synthetic_smb_fname(talloc_tos(),
3174 smb_fname->base_name,
3175 AFPRESOURCE_STREAM_NAME,
3177 if (stream_name == NULL) {
3181 ret = SMB_VFS_STAT(handle->conn, stream_name);
3183 TALLOC_FREE(stream_name);
3187 rfork_size = stream_name->st.st_ex_size;
3188 TALLOC_FREE(stream_name);
3193 static uint64_t readdir_attr_rfork_size(struct vfs_handle_struct *handle,
3194 const struct smb_filename *smb_fname)
3196 struct fruit_config_data *config = NULL;
3197 uint64_t rfork_size;
3199 SMB_VFS_HANDLE_GET_DATA(handle, config,
3200 struct fruit_config_data,
3203 switch (config->rsrc) {
3204 case FRUIT_RSRC_ADFILE:
3205 case FRUIT_RSRC_XATTR:
3206 rfork_size = readdir_attr_rfork_size_adouble(handle,
3210 case FRUIT_META_STREAM:
3211 rfork_size = readdir_attr_rfork_size_stream(handle,
3216 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3224 static NTSTATUS readdir_attr_macmeta(struct vfs_handle_struct *handle,
3225 const struct smb_filename *smb_fname,
3226 struct readdir_attr_data *attr_data)
3228 NTSTATUS status = NT_STATUS_OK;
3229 struct fruit_config_data *config = NULL;
3232 SMB_VFS_HANDLE_GET_DATA(handle, config,
3233 struct fruit_config_data,
3234 return NT_STATUS_UNSUCCESSFUL);
3237 /* Ensure we return a default value in the creation_date field */
3238 RSIVAL(&attr_data->attr_data.aapl.finder_info, 12, AD_DATE_START);
3241 * Resource fork length
3244 if (config->readdir_attr_rsize) {
3245 uint64_t rfork_size;
3247 rfork_size = readdir_attr_rfork_size(handle, smb_fname);
3248 attr_data->attr_data.aapl.rfork_size = rfork_size;
3255 if (config->readdir_attr_finder_info) {
3256 ok = readdir_attr_meta_finderi(handle, smb_fname, attr_data);
3258 status = NT_STATUS_INTERNAL_ERROR;
3265 static NTSTATUS remove_virtual_nfs_aces(struct security_descriptor *psd)
3270 if (psd->dacl == NULL) {
3271 return NT_STATUS_OK;
3274 for (i = 0; i < psd->dacl->num_aces; i++) {
3275 /* MS NFS style mode/uid/gid */
3276 int cmp = dom_sid_compare_domain(
3277 &global_sid_Unix_NFS,
3278 &psd->dacl->aces[i].trustee);
3280 /* Normal ACE entry. */
3285 * security_descriptor_dacl_del()
3286 * *must* return NT_STATUS_OK as we know
3287 * we have something to remove.
3290 status = security_descriptor_dacl_del(psd,
3291 &psd->dacl->aces[i].trustee);
3292 if (!NT_STATUS_IS_OK(status)) {
3293 DBG_WARNING("failed to remove MS NFS style ACE: %s\n",
3299 * security_descriptor_dacl_del() may delete more
3300 * then one entry subsequent to this one if the
3301 * SID matches, but we only need to ensure that
3302 * we stay looking at the same element in the array.
3306 return NT_STATUS_OK;
3309 /* Search MS NFS style ACE with UNIX mode */
3310 static NTSTATUS check_ms_nfs(vfs_handle_struct *handle,
3312 struct security_descriptor *psd,
3317 struct fruit_config_data *config = NULL;
3321 SMB_VFS_HANDLE_GET_DATA(handle, config,
3322 struct fruit_config_data,
3323 return NT_STATUS_UNSUCCESSFUL);
3325 if (!global_fruit_config.nego_aapl) {
3326 return NT_STATUS_OK;
3328 if (psd->dacl == NULL || !config->unix_info_enabled) {
3329 return NT_STATUS_OK;
3332 for (i = 0; i < psd->dacl->num_aces; i++) {
3333 if (dom_sid_compare_domain(
3334 &global_sid_Unix_NFS_Mode,
3335 &psd->dacl->aces[i].trustee) == 0) {
3336 *pmode = (mode_t)psd->dacl->aces[i].trustee.sub_auths[2];
3337 *pmode &= (S_IRWXU | S_IRWXG | S_IRWXO);
3340 DEBUG(10, ("MS NFS chmod request %s, %04o\n",
3341 fsp_str_dbg(fsp), (unsigned)(*pmode)));
3347 * Remove any incoming virtual ACE entries generated by
3348 * fruit_fget_nt_acl().
3351 return remove_virtual_nfs_aces(psd);
3354 /****************************************************************************
3356 ****************************************************************************/
3358 static int fruit_connect(vfs_handle_struct *handle,
3359 const char *service,
3363 char *list = NULL, *newlist = NULL;
3364 struct fruit_config_data *config;
3366 DEBUG(10, ("fruit_connect\n"));
3368 rc = SMB_VFS_NEXT_CONNECT(handle, service, user);
3373 rc = init_fruit_config(handle);
3378 SMB_VFS_HANDLE_GET_DATA(handle, config,
3379 struct fruit_config_data, return -1);
3381 if (config->veto_appledouble) {
3382 list = lp_veto_files(talloc_tos(), SNUM(handle->conn));
3385 if (strstr(list, "/" ADOUBLE_NAME_PREFIX "*/") == NULL) {
3386 newlist = talloc_asprintf(
3388 "%s/" ADOUBLE_NAME_PREFIX "*/",
3390 lp_do_parameter(SNUM(handle->conn),
3395 lp_do_parameter(SNUM(handle->conn),
3397 "/" ADOUBLE_NAME_PREFIX "*/");
3403 if (config->encoding == FRUIT_ENC_NATIVE) {
3404 lp_do_parameter(SNUM(handle->conn),
3409 if (config->time_machine) {
3410 DBG_NOTICE("Enabling durable handles for Time Machine "
3411 "support on [%s]\n", service);
3412 lp_do_parameter(SNUM(handle->conn), "durable handles", "yes");
3413 lp_do_parameter(SNUM(handle->conn), "kernel oplocks", "no");
3414 lp_do_parameter(SNUM(handle->conn), "kernel share modes", "no");
3415 if (!lp_strict_sync(SNUM(handle->conn))) {
3416 DBG_WARNING("Time Machine without strict sync is not "
3419 lp_do_parameter(SNUM(handle->conn), "posix locking", "no");
3425 static int fruit_fake_fd(void)
3432 * Return a valid fd, but ensure any attempt to use it returns
3433 * an error (EPIPE). Once we get a write on the handle, we open
3436 ret = pipe(pipe_fds);
3446 static int fruit_open_meta_stream(vfs_handle_struct *handle,
3447 struct smb_filename *smb_fname,
3452 struct fruit_config_data *config = NULL;
3453 struct fio *fio = NULL;
3454 int open_flags = flags & ~O_CREAT;
3457 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3459 SMB_VFS_HANDLE_GET_DATA(handle, config,
3460 struct fruit_config_data, return -1);
3462 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3463 fio->type = ADOUBLE_META;
3464 fio->config = config;
3466 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, open_flags, mode);
3471 if (!(flags & O_CREAT)) {
3472 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
3476 fd = fruit_fake_fd();
3478 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
3482 fio->fake_fd = true;
3489 static int fruit_open_meta_netatalk(vfs_handle_struct *handle,
3490 struct smb_filename *smb_fname,
3495 struct fruit_config_data *config = NULL;
3496 struct fio *fio = NULL;
3497 struct adouble *ad = NULL;
3498 bool meta_exists = false;
3501 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3503 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
3510 if (!meta_exists && !(flags & O_CREAT)) {
3515 fd = fruit_fake_fd();
3520 SMB_VFS_HANDLE_GET_DATA(handle, config,
3521 struct fruit_config_data, return -1);
3523 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3524 fio->type = ADOUBLE_META;
3525 fio->config = config;
3526 fio->fake_fd = true;
3533 static int fruit_open_meta(vfs_handle_struct *handle,
3534 struct smb_filename *smb_fname,
3535 files_struct *fsp, int flags, mode_t mode)
3538 struct fruit_config_data *config = NULL;
3540 DBG_DEBUG("path [%s]\n", smb_fname_str_dbg(smb_fname));
3542 SMB_VFS_HANDLE_GET_DATA(handle, config,
3543 struct fruit_config_data, return -1);
3545 switch (config->meta) {
3546 case FRUIT_META_STREAM:
3547 fd = fruit_open_meta_stream(handle, smb_fname,
3551 case FRUIT_META_NETATALK:
3552 fd = fruit_open_meta_netatalk(handle, smb_fname,
3557 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3561 DBG_DEBUG("path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3566 static int fruit_open_rsrc_adouble(vfs_handle_struct *handle,
3567 struct smb_filename *smb_fname,
3573 struct adouble *ad = NULL;
3574 struct smb_filename *smb_fname_base = NULL;
3575 struct fruit_config_data *config = NULL;
3578 SMB_VFS_HANDLE_GET_DATA(handle, config,
3579 struct fruit_config_data, return -1);
3581 if ((!(flags & O_CREAT)) &&
3582 S_ISDIR(fsp->base_fsp->fsp_name->st.st_ex_mode))
3584 /* sorry, but directories don't habe a resource fork */
3589 rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_base);
3594 /* We always need read/write access for the metadata header too */
3595 flags &= ~(O_RDONLY | O_WRONLY);
3598 hostfd = SMB_VFS_NEXT_OPEN(handle, smb_fname_base, fsp,
3605 if (flags & (O_CREAT | O_TRUNC)) {
3606 ad = ad_init(fsp, handle, ADOUBLE_RSRC);
3612 fsp->fh->fd = hostfd;
3614 rc = ad_fset(ad, fsp);
3625 TALLOC_FREE(smb_fname_base);
3627 DEBUG(10, ("fruit_open resource fork: rc=%d, fd=%d\n", rc, hostfd));
3629 int saved_errno = errno;
3632 * BUGBUGBUG -- we would need to call
3633 * fd_close_posix here, but we don't have a
3636 fsp->fh->fd = hostfd;
3640 errno = saved_errno;
3645 static int fruit_open_rsrc_xattr(vfs_handle_struct *handle,
3646 struct smb_filename *smb_fname,
3651 #ifdef HAVE_ATTROPEN
3654 fd = attropen(smb_fname->base_name,
3655 AFPRESOURCE_EA_NETATALK,
3670 static int fruit_open_rsrc(vfs_handle_struct *handle,
3671 struct smb_filename *smb_fname,
3672 files_struct *fsp, int flags, mode_t mode)
3675 struct fruit_config_data *config = NULL;
3676 struct fio *fio = NULL;
3678 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3680 SMB_VFS_HANDLE_GET_DATA(handle, config,
3681 struct fruit_config_data, return -1);
3683 switch (config->rsrc) {
3684 case FRUIT_RSRC_STREAM:
3685 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3688 case FRUIT_RSRC_ADFILE:
3689 fd = fruit_open_rsrc_adouble(handle, smb_fname,
3693 case FRUIT_RSRC_XATTR:
3694 fd = fruit_open_rsrc_xattr(handle, smb_fname,
3699 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3703 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3709 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3710 fio->type = ADOUBLE_RSRC;
3711 fio->config = config;
3716 static int fruit_open(vfs_handle_struct *handle,
3717 struct smb_filename *smb_fname,
3718 files_struct *fsp, int flags, mode_t mode)
3722 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3724 if (!is_ntfs_stream_smb_fname(smb_fname)) {
3725 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3728 if (is_afpinfo_stream(smb_fname)) {
3729 fd = fruit_open_meta(handle, smb_fname, fsp, flags, mode);
3730 } else if (is_afpresource_stream(smb_fname)) {
3731 fd = fruit_open_rsrc(handle, smb_fname, fsp, flags, mode);
3733 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3736 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3741 static int fruit_close_meta(vfs_handle_struct *handle,
3745 struct fruit_config_data *config = NULL;
3747 SMB_VFS_HANDLE_GET_DATA(handle, config,
3748 struct fruit_config_data, return -1);
3750 switch (config->meta) {
3751 case FRUIT_META_STREAM:
3752 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3755 case FRUIT_META_NETATALK:
3756 ret = close(fsp->fh->fd);
3761 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3769 static int fruit_close_rsrc(vfs_handle_struct *handle,
3773 struct fruit_config_data *config = NULL;
3775 SMB_VFS_HANDLE_GET_DATA(handle, config,
3776 struct fruit_config_data, return -1);
3778 switch (config->rsrc) {
3779 case FRUIT_RSRC_STREAM:
3780 case FRUIT_RSRC_ADFILE:
3781 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3784 case FRUIT_RSRC_XATTR:
3785 ret = close(fsp->fh->fd);
3790 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3797 static int fruit_close(vfs_handle_struct *handle,
3805 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(fsp->fsp_name), fd);
3807 if (!is_ntfs_stream_smb_fname(fsp->fsp_name)) {
3808 return SMB_VFS_NEXT_CLOSE(handle, fsp);
3811 if (is_afpinfo_stream(fsp->fsp_name)) {
3812 ret = fruit_close_meta(handle, fsp);
3813 } else if (is_afpresource_stream(fsp->fsp_name)) {
3814 ret = fruit_close_rsrc(handle, fsp);
3816 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3822 static int fruit_rename(struct vfs_handle_struct *handle,
3823 const struct smb_filename *smb_fname_src,
3824 const struct smb_filename *smb_fname_dst)
3827 struct fruit_config_data *config = NULL;
3828 struct smb_filename *src_adp_smb_fname = NULL;
3829 struct smb_filename *dst_adp_smb_fname = NULL;
3831 SMB_VFS_HANDLE_GET_DATA(handle, config,
3832 struct fruit_config_data, return -1);
3834 if (!VALID_STAT(smb_fname_src->st)) {
3835 DBG_ERR("Need valid stat for [%s]\n",
3836 smb_fname_str_dbg(smb_fname_src));
3840 rc = SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
3845 if ((config->rsrc != FRUIT_RSRC_ADFILE) ||
3846 (!S_ISREG(smb_fname_src->st.st_ex_mode)))
3851 rc = adouble_path(talloc_tos(), smb_fname_src, &src_adp_smb_fname);
3856 rc = adouble_path(talloc_tos(), smb_fname_dst, &dst_adp_smb_fname);
3861 DBG_DEBUG("%s -> %s\n",
3862 smb_fname_str_dbg(src_adp_smb_fname),
3863 smb_fname_str_dbg(dst_adp_smb_fname));
3865 rc = SMB_VFS_NEXT_RENAME(handle, src_adp_smb_fname, dst_adp_smb_fname);
3866 if (errno == ENOENT) {
3871 TALLOC_FREE(src_adp_smb_fname);
3872 TALLOC_FREE(dst_adp_smb_fname);
3876 static int fruit_unlink_meta_stream(vfs_handle_struct *handle,
3877 const struct smb_filename *smb_fname)
3879 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3882 static int fruit_unlink_meta_netatalk(vfs_handle_struct *handle,
3883 const struct smb_filename *smb_fname)
3885 return SMB_VFS_REMOVEXATTR(handle->conn,
3887 AFPINFO_EA_NETATALK);
3890 static int fruit_unlink_meta(vfs_handle_struct *handle,
3891 const struct smb_filename *smb_fname)
3893 struct fruit_config_data *config = NULL;
3896 SMB_VFS_HANDLE_GET_DATA(handle, config,
3897 struct fruit_config_data, return -1);
3899 switch (config->meta) {
3900 case FRUIT_META_STREAM:
3901 rc = fruit_unlink_meta_stream(handle, smb_fname);
3904 case FRUIT_META_NETATALK:
3905 rc = fruit_unlink_meta_netatalk(handle, smb_fname);
3909 DBG_ERR("Unsupported meta config [%d]\n", config->meta);
3916 static int fruit_unlink_rsrc_stream(vfs_handle_struct *handle,
3917 const struct smb_filename *smb_fname,
3922 if (!force_unlink) {
3923 struct smb_filename *smb_fname_cp = NULL;
3926 smb_fname_cp = cp_smb_filename(talloc_tos(), smb_fname);
3927 if (smb_fname_cp == NULL) {
3932 * 0 byte resource fork streams are not listed by
3933 * vfs_streaminfo, as a result stream cleanup/deletion of file
3934 * deletion doesn't remove the resourcefork stream.
3937 ret = SMB_VFS_NEXT_STAT(handle, smb_fname_cp);
3939 TALLOC_FREE(smb_fname_cp);
3940 DBG_ERR("stat [%s] failed [%s]\n",
3941 smb_fname_str_dbg(smb_fname_cp), strerror(errno));
3945 size = smb_fname_cp->st.st_ex_size;
3946 TALLOC_FREE(smb_fname_cp);
3949 /* OS X ignores resource fork stream delete requests */
3954 ret = SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3955 if ((ret != 0) && (errno == ENOENT) && force_unlink) {
3962 static int fruit_unlink_rsrc_adouble(vfs_handle_struct *handle,
3963 const struct smb_filename *smb_fname,
3967 struct adouble *ad = NULL;
3968 struct smb_filename *adp_smb_fname = NULL;
3970 if (!force_unlink) {
3971 ad = ad_get(talloc_tos(), handle, smb_fname,
3980 * 0 byte resource fork streams are not listed by
3981 * vfs_streaminfo, as a result stream cleanup/deletion of file
3982 * deletion doesn't remove the resourcefork stream.
3985 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
3986 /* OS X ignores resource fork stream delete requests */
3994 rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
3999 rc = SMB_VFS_NEXT_UNLINK(handle, adp_smb_fname);
4000 TALLOC_FREE(adp_smb_fname);
4001 if ((rc != 0) && (errno == ENOENT) && force_unlink) {
4008 static int fruit_unlink_rsrc_xattr(vfs_handle_struct *handle,
4009 const struct smb_filename *smb_fname,
4013 * OS X ignores resource fork stream delete requests, so nothing to do
4014 * here. Removing the file will remove the xattr anyway, so we don't
4015 * have to take care of removing 0 byte resource forks that could be
4021 static int fruit_unlink_rsrc(vfs_handle_struct *handle,
4022 const struct smb_filename *smb_fname,
4025 struct fruit_config_data *config = NULL;
4028 SMB_VFS_HANDLE_GET_DATA(handle, config,
4029 struct fruit_config_data, return -1);
4031 switch (config->rsrc) {
4032 case FRUIT_RSRC_STREAM:
4033 rc = fruit_unlink_rsrc_stream(handle, smb_fname, force_unlink);
4036 case FRUIT_RSRC_ADFILE:
4037 rc = fruit_unlink_rsrc_adouble(handle, smb_fname, force_unlink);
4040 case FRUIT_RSRC_XATTR:
4041 rc = fruit_unlink_rsrc_xattr(handle, smb_fname, force_unlink);
4045 DBG_ERR("Unsupported rsrc config [%d]\n", config->rsrc);
4052 static int fruit_unlink(vfs_handle_struct *handle,
4053 const struct smb_filename *smb_fname)
4056 struct fruit_config_data *config = NULL;
4057 struct smb_filename *rsrc_smb_fname = NULL;
4059 SMB_VFS_HANDLE_GET_DATA(handle, config,
4060 struct fruit_config_data, return -1);
4062 if (is_afpinfo_stream(smb_fname)) {
4063 return fruit_unlink_meta(handle, smb_fname);
4064 } else if (is_afpresource_stream(smb_fname)) {
4065 return fruit_unlink_rsrc(handle, smb_fname, false);
4066 } if (is_ntfs_stream_smb_fname(smb_fname)) {
4067 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4071 * A request to delete the base file. Because 0 byte resource
4072 * fork streams are not listed by fruit_streaminfo,
4073 * delete_all_streams() can't remove 0 byte resource fork
4074 * streams, so we have to cleanup this here.
4076 rsrc_smb_fname = synthetic_smb_fname(talloc_tos(),
4077 smb_fname->base_name,
4078 AFPRESOURCE_STREAM_NAME,
4081 if (rsrc_smb_fname == NULL) {
4085 rc = fruit_unlink_rsrc(handle, rsrc_smb_fname, true);
4086 if ((rc != 0) && (errno != ENOENT)) {
4087 DBG_ERR("Forced unlink of [%s] failed [%s]\n",
4088 smb_fname_str_dbg(rsrc_smb_fname), strerror(errno));
4089 TALLOC_FREE(rsrc_smb_fname);
4092 TALLOC_FREE(rsrc_smb_fname);
4094 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4097 static int fruit_chmod(vfs_handle_struct *handle,
4098 const struct smb_filename *smb_fname,
4102 struct fruit_config_data *config = NULL;
4103 struct smb_filename *smb_fname_adp = NULL;
4105 rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname, mode);
4110 SMB_VFS_HANDLE_GET_DATA(handle, config,
4111 struct fruit_config_data, return -1);
4113 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4117 if (!VALID_STAT(smb_fname->st)) {
4121 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
4125 rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_adp);
4130 DEBUG(10, ("fruit_chmod: %s\n", smb_fname_adp->base_name));
4132 rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname_adp, mode);
4133 if (errno == ENOENT) {
4137 TALLOC_FREE(smb_fname_adp);
4141 static int fruit_chown(vfs_handle_struct *handle,
4142 const struct smb_filename *smb_fname,
4147 struct fruit_config_data *config = NULL;
4148 struct smb_filename *adp_smb_fname = NULL;
4150 rc = SMB_VFS_NEXT_CHOWN(handle, smb_fname, uid, gid);
4155 SMB_VFS_HANDLE_GET_DATA(handle, config,
4156 struct fruit_config_data, return -1);
4158 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4162 if (!VALID_STAT(smb_fname->st)) {
4166 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
4170 rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
4175 DEBUG(10, ("fruit_chown: %s\n", adp_smb_fname->base_name));
4177 rc = SMB_VFS_NEXT_CHOWN(handle, adp_smb_fname, uid, gid);
4178 if (errno == ENOENT) {
4183 TALLOC_FREE(adp_smb_fname);
4187 static int fruit_rmdir(struct vfs_handle_struct *handle,
4188 const struct smb_filename *smb_fname)
4192 struct fruit_config_data *config;
4194 SMB_VFS_HANDLE_GET_DATA(handle, config,
4195 struct fruit_config_data, return -1);
4197 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4202 * Due to there is no way to change bDeleteVetoFiles variable
4203 * from this module, need to clean up ourselves
4206 dh = SMB_VFS_OPENDIR(handle->conn, smb_fname, NULL, 0);
4211 while ((de = SMB_VFS_READDIR(handle->conn, dh, NULL)) != NULL) {
4213 struct adouble *ad = NULL;
4215 struct smb_filename *ad_smb_fname = NULL;
4218 match = strncmp(de->d_name,
4219 ADOUBLE_NAME_PREFIX,
4220 strlen(ADOUBLE_NAME_PREFIX));
4225 p = talloc_asprintf(talloc_tos(), "%s/%s",
4226 smb_fname->base_name, de->d_name);
4228 DBG_ERR("talloc_asprintf failed\n");
4232 ad_smb_fname = synthetic_smb_fname(talloc_tos(), p,
4236 if (ad_smb_fname == NULL) {
4237 DBG_ERR("synthetic_smb_fname failed\n");
4242 * Check whether it's a valid AppleDouble file, if
4243 * yes, delete it, ignore it otherwise.
4245 ad = ad_get(talloc_tos(), handle, ad_smb_fname, ADOUBLE_RSRC);
4247 TALLOC_FREE(ad_smb_fname);
4253 ret = SMB_VFS_NEXT_UNLINK(handle, ad_smb_fname);
4255 DBG_ERR("Deleting [%s] failed\n",
4256 smb_fname_str_dbg(ad_smb_fname));
4258 TALLOC_FREE(ad_smb_fname);
4263 SMB_VFS_CLOSEDIR(handle->conn, dh);
4265 return SMB_VFS_NEXT_RMDIR(handle, smb_fname);
4268 static ssize_t fruit_pread_meta_stream(vfs_handle_struct *handle,
4269 files_struct *fsp, void *data,
4270 size_t n, off_t offset)
4275 nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4276 if (nread == -1 || nread == n) {
4280 DBG_ERR("Removing [%s] after short read [%zd]\n",
4281 fsp_str_dbg(fsp), nread);
4283 ret = SMB_VFS_NEXT_UNLINK(handle, fsp->fsp_name);
4285 DBG_ERR("Removing [%s] failed\n", fsp_str_dbg(fsp));
4293 static ssize_t fruit_pread_meta_adouble(vfs_handle_struct *handle,
4294 files_struct *fsp, void *data,
4295 size_t n, off_t offset)
4298 struct adouble *ad = NULL;
4299 char afpinfo_buf[AFP_INFO_SIZE];
4303 ai = afpinfo_new(talloc_tos());
4308 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
4314 p = ad_get_entry(ad, ADEID_FINDERI);
4316 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
4321 memcpy(&ai->afpi_FinderInfo[0], p, ADEDLEN_FINDERI);
4323 nread = afpinfo_pack(ai, afpinfo_buf);
4324 if (nread != AFP_INFO_SIZE) {
4329 memcpy(data, afpinfo_buf, n);
4337 static ssize_t fruit_pread_meta(vfs_handle_struct *handle,
4338 files_struct *fsp, void *data,
4339 size_t n, off_t offset)
4341 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4346 * OS X has a off-by-1 error in the offset calculation, so we're
4347 * bug compatible here. It won't hurt, as any relevant real
4348 * world read requests from the AFP_AfpInfo stream will be
4349 * offset=0 n=60. offset is ignored anyway, see below.
4351 if ((offset < 0) || (offset >= AFP_INFO_SIZE + 1)) {
4356 DBG_ERR("Failed to fetch fsp extension");
4360 /* Yes, macOS always reads from offset 0 */
4362 to_return = MIN(n, AFP_INFO_SIZE);
4364 switch (fio->config->meta) {
4365 case FRUIT_META_STREAM:
4366 nread = fruit_pread_meta_stream(handle, fsp, data,
4370 case FRUIT_META_NETATALK:
4371 nread = fruit_pread_meta_adouble(handle, fsp, data,
4376 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
4380 if (nread == -1 && fio->created) {
4382 char afpinfo_buf[AFP_INFO_SIZE];
4384 ai = afpinfo_new(talloc_tos());
4389 nread = afpinfo_pack(ai, afpinfo_buf);
4391 if (nread != AFP_INFO_SIZE) {
4395 memcpy(data, afpinfo_buf, to_return);
4402 static ssize_t fruit_pread_rsrc_stream(vfs_handle_struct *handle,
4403 files_struct *fsp, void *data,
4404 size_t n, off_t offset)
4406 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4409 static ssize_t fruit_pread_rsrc_xattr(vfs_handle_struct *handle,
4410 files_struct *fsp, void *data,
4411 size_t n, off_t offset)
4413 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4416 static ssize_t fruit_pread_rsrc_adouble(vfs_handle_struct *handle,
4417 files_struct *fsp, void *data,
4418 size_t n, off_t offset)
4420 struct adouble *ad = NULL;
4423 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
4428 nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n,
4429 offset + ad_getentryoff(ad, ADEID_RFORK));
4435 static ssize_t fruit_pread_rsrc(vfs_handle_struct *handle,
4436 files_struct *fsp, void *data,
4437 size_t n, off_t offset)
4439 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4447 switch (fio->config->rsrc) {
4448 case FRUIT_RSRC_STREAM:
4449 nread = fruit_pread_rsrc_stream(handle, fsp, data, n, offset);
4452 case FRUIT_RSRC_ADFILE:
4453 nread = fruit_pread_rsrc_adouble(handle, fsp, data, n, offset);
4456 case FRUIT_RSRC_XATTR:
4457 nread = fruit_pread_rsrc_xattr(handle, fsp, data, n, offset);
4461 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
4468 static ssize_t fruit_pread(vfs_handle_struct *handle,
4469 files_struct *fsp, void *data,
4470 size_t n, off_t offset)
4472 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4475 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4476 fsp_str_dbg(fsp), (intmax_t)offset, n);
4479 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4482 if (fio->type == ADOUBLE_META) {
4483 nread = fruit_pread_meta(handle, fsp, data, n, offset);
4485 nread = fruit_pread_rsrc(handle, fsp, data, n, offset);
4488 DBG_DEBUG("Path [%s] nread [%zd]\n", fsp_str_dbg(fsp), nread);
4492 static bool fruit_must_handle_aio_stream(struct fio *fio)
4498 if (fio->type == ADOUBLE_META) {
4502 if ((fio->type == ADOUBLE_RSRC) &&
4503 (fio->config->rsrc == FRUIT_RSRC_ADFILE))
4511 struct fruit_pread_state {
4513 struct vfs_aio_state vfs_aio_state;
4516 static void fruit_pread_done(struct tevent_req *subreq);
4518 static struct tevent_req *fruit_pread_send(
4519 struct vfs_handle_struct *handle,
4520 TALLOC_CTX *mem_ctx,
4521 struct tevent_context *ev,
4522 struct files_struct *fsp,
4524 size_t n, off_t offset)
4526 struct tevent_req *req = NULL;
4527 struct tevent_req *subreq = NULL;
4528 struct fruit_pread_state *state = NULL;
4529 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4531 req = tevent_req_create(mem_ctx, &state,
4532 struct fruit_pread_state);
4537 if (fruit_must_handle_aio_stream(fio)) {
4538 state->nread = SMB_VFS_PREAD(fsp, data, n, offset);
4539 if (state->nread != n) {
4540 if (state->nread != -1) {
4543 tevent_req_error(req, errno);
4544 return tevent_req_post(req, ev);
4546 tevent_req_done(req);
4547 return tevent_req_post(req, ev);
4550 subreq = SMB_VFS_NEXT_PREAD_SEND(state, ev, handle, fsp,
4552 if (tevent_req_nomem(req, subreq)) {
4553 return tevent_req_post(req, ev);
4555 tevent_req_set_callback(subreq, fruit_pread_done, req);
4559 static void fruit_pread_done(struct tevent_req *subreq)
4561 struct tevent_req *req = tevent_req_callback_data(
4562 subreq, struct tevent_req);
4563 struct fruit_pread_state *state = tevent_req_data(
4564 req, struct fruit_pread_state);
4566 state->nread = SMB_VFS_PREAD_RECV(subreq, &state->vfs_aio_state);
4567 TALLOC_FREE(subreq);
4569 if (tevent_req_error(req, state->vfs_aio_state.error)) {
4572 tevent_req_done(req);
4575 static ssize_t fruit_pread_recv(struct tevent_req *req,
4576 struct vfs_aio_state *vfs_aio_state)
4578 struct fruit_pread_state *state = tevent_req_data(
4579 req, struct fruit_pread_state);
4581 if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
4585 *vfs_aio_state = state->vfs_aio_state;
4586 return state->nread;
4589 static ssize_t fruit_pwrite_meta_stream(vfs_handle_struct *handle,
4590 files_struct *fsp, const void *data,
4591 size_t n, off_t offset)
4593 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4599 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4600 fsp_str_dbg(fsp), (intmax_t)offset, n);
4609 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
4611 DBG_ERR("Close [%s] failed: %s\n",
4612 fsp_str_dbg(fsp), strerror(errno));
4617 fd = SMB_VFS_NEXT_OPEN(handle,
4623 DBG_ERR("On-demand create [%s] in write failed: %s\n",
4624 fsp_str_dbg(fsp), strerror(errno));
4628 fio->fake_fd = false;
4631 ai = afpinfo_unpack(talloc_tos(), data);
4636 if (ai_empty_finderinfo(ai)) {
4638 * Writing an all 0 blob to the metadata stream results in the
4639 * stream being removed on a macOS server. This ensures we
4640 * behave the same and it verified by the "delete AFP_AfpInfo by
4641 * writing all 0" test.
4643 ret = SMB_VFS_NEXT_FTRUNCATE(handle, fsp, 0);
4645 DBG_ERR("SMB_VFS_NEXT_FTRUNCATE on [%s] failed\n",
4650 ok = set_delete_on_close(
4653 handle->conn->session_info->security_token,
4654 handle->conn->session_info->unix_token);
4656 DBG_ERR("set_delete_on_close on [%s] failed\n",
4663 nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4664 if (nwritten != n) {
4671 static ssize_t fruit_pwrite_meta_netatalk(vfs_handle_struct *handle,
4672 files_struct *fsp, const void *data,
4673 size_t n, off_t offset)
4675 struct adouble *ad = NULL;
4681 ai = afpinfo_unpack(talloc_tos(), data);
4686 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
4688 ad = ad_init(talloc_tos(), handle, ADOUBLE_META);
4693 p = ad_get_entry(ad, ADEID_FINDERI);
4695 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
4700 memcpy(p, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
4702 ret = ad_fset(ad, fsp);
4704 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
4711 if (!ai_empty_finderinfo(ai)) {
4716 * Writing an all 0 blob to the metadata stream results in the stream
4717 * being removed on a macOS server. This ensures we behave the same and
4718 * it verified by the "delete AFP_AfpInfo by writing all 0" test.
4721 ok = set_delete_on_close(
4724 handle->conn->session_info->security_token,
4725 handle->conn->session_info->unix_token);
4727 DBG_ERR("set_delete_on_close on [%s] failed\n",
4735 static ssize_t fruit_pwrite_meta(vfs_handle_struct *handle,
4736 files_struct *fsp, const void *data,
4737 size_t n, off_t offset)
4739 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4741 uint8_t buf[AFP_INFO_SIZE];
4747 DBG_ERR("Failed to fetch fsp extension");
4756 if (offset != 0 && n < 60) {
4761 cmp = memcmp(data, "AFP", 3);
4767 if (n <= AFP_OFF_FinderInfo) {
4769 * Nothing to do here really, just return
4777 if (to_copy > AFP_INFO_SIZE) {
4778 to_copy = AFP_INFO_SIZE;
4780 memcpy(buf, data, to_copy);
4783 if (to_write != AFP_INFO_SIZE) {
4784 to_write = AFP_INFO_SIZE;
4787 switch (fio->config->meta) {
4788 case FRUIT_META_STREAM:
4789 nwritten = fruit_pwrite_meta_stream(handle,
4796 case FRUIT_META_NETATALK:
4797 nwritten = fruit_pwrite_meta_netatalk(handle,
4805 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
4809 if (nwritten != to_write) {
4814 * Return the requested amount, verified against macOS SMB server
4819 static ssize_t fruit_pwrite_rsrc_stream(vfs_handle_struct *handle,
4820 files_struct *fsp, const void *data,
4821 size_t n, off_t offset)
4823 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4826 static ssize_t fruit_pwrite_rsrc_xattr(vfs_handle_struct *handle,
4827 files_struct *fsp, const void *data,
4828 size_t n, off_t offset)
4830 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4833 static ssize_t fruit_pwrite_rsrc_adouble(vfs_handle_struct *handle,
4834 files_struct *fsp, const void *data,
4835 size_t n, off_t offset)
4837 struct adouble *ad = NULL;
4841 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
4843 DBG_ERR("ad_get [%s] failed\n", fsp_str_dbg(fsp));
4847 nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n,
4848 offset + ad_getentryoff(ad, ADEID_RFORK));
4849 if (nwritten != n) {
4850 DBG_ERR("Short write on [%s] [%zd/%zd]\n",
4851 fsp_str_dbg(fsp), nwritten, n);
4856 if ((n + offset) > ad_getentrylen(ad, ADEID_RFORK)) {
4857 ad_setentrylen(ad, ADEID_RFORK, n + offset);
4858 ret = ad_fset(ad, fsp);
4860 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
4870 static ssize_t fruit_pwrite_rsrc(vfs_handle_struct *handle,
4871 files_struct *fsp, const void *data,
4872 size_t n, off_t offset)
4874 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4878 DBG_ERR("Failed to fetch fsp extension");
4882 switch (fio->config->rsrc) {
4883 case FRUIT_RSRC_STREAM:
4884 nwritten = fruit_pwrite_rsrc_stream(handle, fsp, data, n, offset);
4887 case FRUIT_RSRC_ADFILE:
4888 nwritten = fruit_pwrite_rsrc_adouble(handle, fsp, data, n, offset);
4891 case FRUIT_RSRC_XATTR:
4892 nwritten = fruit_pwrite_rsrc_xattr(handle, fsp, data, n, offset);
4896 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
4903 static ssize_t fruit_pwrite(vfs_handle_struct *handle,
4904 files_struct *fsp, const void *data,
4905 size_t n, off_t offset)
4907 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4910 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4911 fsp_str_dbg(fsp), (intmax_t)offset, n);
4914 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4917 if (fio->type == ADOUBLE_META) {
4918 nwritten = fruit_pwrite_meta(handle, fsp, data, n, offset);
4920 nwritten = fruit_pwrite_rsrc(handle, fsp, data, n, offset);
4923 DBG_DEBUG("Path [%s] nwritten=%zd\n", fsp_str_dbg(fsp), nwritten);
4927 struct fruit_pwrite_state {
4929 struct vfs_aio_state vfs_aio_state;
4932 static void fruit_pwrite_done(struct tevent_req *subreq);
4934 static struct tevent_req *fruit_pwrite_send(
4935 struct vfs_handle_struct *handle,
4936 TALLOC_CTX *mem_ctx,
4937 struct tevent_context *ev,
4938 struct files_struct *fsp,
4940 size_t n, off_t offset)
4942 struct tevent_req *req = NULL;
4943 struct tevent_req *subreq = NULL;
4944 struct fruit_pwrite_state *state = NULL;
4945 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4947 req = tevent_req_create(mem_ctx, &state,
4948 struct fruit_pwrite_state);
4953 if (fruit_must_handle_aio_stream(fio)) {
4954 state->nwritten = SMB_VFS_PWRITE(fsp, data, n, offset);
4955 if (state->nwritten != n) {
4956 if (state->nwritten != -1) {
4959 tevent_req_error(req, errno);
4960 return tevent_req_post(req, ev);
4962 tevent_req_done(req);
4963 return tevent_req_post(req, ev);
4966 subreq = SMB_VFS_NEXT_PWRITE_SEND(state, ev, handle, fsp,
4968 if (tevent_req_nomem(req, subreq)) {
4969 return tevent_req_post(req, ev);
4971 tevent_req_set_callback(subreq, fruit_pwrite_done, req);
4975 static void fruit_pwrite_done(struct tevent_req *subreq)
4977 struct tevent_req *req = tevent_req_callback_data(
4978 subreq, struct tevent_req);
4979 struct fruit_pwrite_state *state = tevent_req_data(
4980 req, struct fruit_pwrite_state);
4982 state->nwritten = SMB_VFS_PWRITE_RECV(subreq, &state->vfs_aio_state);
4983 TALLOC_FREE(subreq);
4985 if (tevent_req_error(req, state->vfs_aio_state.error)) {
4988 tevent_req_done(req);
4991 static ssize_t fruit_pwrite_recv(struct tevent_req *req,
4992 struct vfs_aio_state *vfs_aio_state)
4994 struct fruit_pwrite_state *state = tevent_req_data(
4995 req, struct fruit_pwrite_state);
4997 if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
5001 *vfs_aio_state = state->vfs_aio_state;
5002 return state->nwritten;
5006 * Helper to stat/lstat the base file of an smb_fname.
5008 static int fruit_stat_base(vfs_handle_struct *handle,
5009 struct smb_filename *smb_fname,
5012 char *tmp_stream_name;
5015 tmp_stream_name = smb_fname->stream_name;
5016 smb_fname->stream_name = NULL;
5018 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
5020 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5022 smb_fname->stream_name = tmp_stream_name;
5024 DBG_DEBUG("fruit_stat_base [%s] dev [%ju] ino [%ju]\n",
5025 smb_fname->base_name,
5026 (uintmax_t)smb_fname->st.st_ex_dev,
5027 (uintmax_t)smb_fname->st.st_ex_ino);
5031 static int fruit_stat_meta_stream(vfs_handle_struct *handle,
5032 struct smb_filename *smb_fname,
5038 ret = fruit_stat_base(handle, smb_fname, false);
5043 ino = fruit_inode(&smb_fname->st, smb_fname->stream_name);
5046 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
5048 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5051 smb_fname->st.st_ex_ino = ino;
5056 static int fruit_stat_meta_netatalk(vfs_handle_struct *handle,
5057 struct smb_filename *smb_fname,
5060 struct adouble *ad = NULL;
5062 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5064 DBG_INFO("fruit_stat_meta %s: %s\n",
5065 smb_fname_str_dbg(smb_fname), strerror(errno));
5071 /* Populate the stat struct with info from the base file. */
5072 if (fruit_stat_base(handle, smb_fname, follow_links) == -1) {
5075 smb_fname->st.st_ex_size = AFP_INFO_SIZE;
5076 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5077 smb_fname->stream_name);
5081 static int fruit_stat_meta(vfs_handle_struct *handle,
5082 struct smb_filename *smb_fname,
5085 struct fruit_config_data *config = NULL;
5088 SMB_VFS_HANDLE_GET_DATA(handle, config,
5089 struct fruit_config_data, return -1);
5091 switch (config->meta) {
5092 case FRUIT_META_STREAM:
5093 ret = fruit_stat_meta_stream(handle, smb_fname, follow_links);
5096 case FRUIT_META_NETATALK:
5097 ret = fruit_stat_meta_netatalk(handle, smb_fname, follow_links);
5101 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
5108 static int fruit_stat_rsrc_netatalk(vfs_handle_struct *handle,
5109 struct smb_filename *smb_fname,
5112 struct adouble *ad = NULL;
5115 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
5121 /* Populate the stat struct with info from the base file. */
5122 ret = fruit_stat_base(handle, smb_fname, follow_links);
5128 smb_fname->st.st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
5129 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5130 smb_fname->stream_name);
5135 static int fruit_stat_rsrc_stream(vfs_handle_struct *handle,
5136 struct smb_filename *smb_fname,
5142 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
5144 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5150 static int fruit_stat_rsrc_xattr(vfs_handle_struct *handle,
5151 struct smb_filename *smb_fname,
5154 #ifdef HAVE_ATTROPEN
5158 /* Populate the stat struct with info from the base file. */
5159 ret = fruit_stat_base(handle, smb_fname, follow_links);
5164 fd = attropen(smb_fname->base_name,
5165 AFPRESOURCE_EA_NETATALK,
5171 ret = sys_fstat(fd, &smb_fname->st, false);
5174 DBG_ERR("fstat [%s:%s] failed\n", smb_fname->base_name,
5175 AFPRESOURCE_EA_NETATALK);
5181 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5182 smb_fname->stream_name);
5192 static int fruit_stat_rsrc(vfs_handle_struct *handle,
5193 struct smb_filename *smb_fname,
5196 struct fruit_config_data *config = NULL;
5199 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
5201 SMB_VFS_HANDLE_GET_DATA(handle, config,
5202 struct fruit_config_data, return -1);
5204 switch (config->rsrc) {
5205 case FRUIT_RSRC_STREAM:
5206 ret = fruit_stat_rsrc_stream(handle, smb_fname, follow_links);
5209 case FRUIT_RSRC_XATTR:
5210 ret = fruit_stat_rsrc_xattr(handle, smb_fname, follow_links);
5213 case FRUIT_RSRC_ADFILE:
5214 ret = fruit_stat_rsrc_netatalk(handle, smb_fname, follow_links);
5218 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
5225 static int fruit_stat(vfs_handle_struct *handle,
5226 struct smb_filename *smb_fname)
5230 DEBUG(10, ("fruit_stat called for %s\n",
5231 smb_fname_str_dbg(smb_fname)));
5233 if (!is_ntfs_stream_smb_fname(smb_fname)
5234 || is_ntfs_default_stream_smb_fname(smb_fname)) {
5235 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
5237 update_btime(handle, smb_fname);
5243 * Note if lp_posix_paths() is true, we can never
5244 * get here as is_ntfs_stream_smb_fname() is
5245 * always false. So we never need worry about
5246 * not following links here.
5249 if (is_afpinfo_stream(smb_fname)) {
5250 rc = fruit_stat_meta(handle, smb_fname, true);
5251 } else if (is_afpresource_stream(smb_fname)) {
5252 rc = fruit_stat_rsrc(handle, smb_fname, true);
5254 return SMB_VFS_NEXT_STAT(handle, smb_fname);
5258 update_btime(handle, smb_fname);
5259 smb_fname->st.st_ex_mode &= ~S_IFMT;
5260 smb_fname->st.st_ex_mode |= S_IFREG;
5261 smb_fname->st.st_ex_blocks =
5262 smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
5267 static int fruit_lstat(vfs_handle_struct *handle,
5268 struct smb_filename *smb_fname)
5272 DEBUG(10, ("fruit_lstat called for %s\n",
5273 smb_fname_str_dbg(smb_fname)));
5275 if (!is_ntfs_stream_smb_fname(smb_fname)
5276 || is_ntfs_default_stream_smb_fname(smb_fname)) {
5277 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5279 update_btime(handle, smb_fname);
5284 if (is_afpinfo_stream(smb_fname)) {
5285 rc = fruit_stat_meta(handle, smb_fname, false);
5286 } else if (is_afpresource_stream(smb_fname)) {
5287 rc = fruit_stat_rsrc(handle, smb_fname, false);
5289 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5293 update_btime(handle, smb_fname);
5294 smb_fname->st.st_ex_mode &= ~S_IFMT;
5295 smb_fname->st.st_ex_mode |= S_IFREG;
5296 smb_fname->st.st_ex_blocks =
5297 smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
5302 static int fruit_fstat_meta_stream(vfs_handle_struct *handle,
5304 SMB_STRUCT_STAT *sbuf)
5306 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5307 struct smb_filename smb_fname;
5316 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5321 *sbuf = fsp->base_fsp->fsp_name->st;
5322 sbuf->st_ex_size = AFP_INFO_SIZE;
5323 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5327 smb_fname = (struct smb_filename) {
5328 .base_name = fsp->fsp_name->base_name,
5331 ret = fruit_stat_base(handle, &smb_fname, false);
5335 *sbuf = smb_fname.st;
5337 ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5339 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5344 sbuf->st_ex_ino = ino;
5348 static int fruit_fstat_meta_netatalk(vfs_handle_struct *handle,
5350 SMB_STRUCT_STAT *sbuf)
5354 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5359 *sbuf = fsp->base_fsp->fsp_name->st;
5360 sbuf->st_ex_size = AFP_INFO_SIZE;
5361 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5366 static int fruit_fstat_meta(vfs_handle_struct *handle,
5368 SMB_STRUCT_STAT *sbuf,
5373 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
5375 switch (fio->config->meta) {
5376 case FRUIT_META_STREAM:
5377 ret = fruit_fstat_meta_stream(handle, fsp, sbuf);
5380 case FRUIT_META_NETATALK:
5381 ret = fruit_fstat_meta_netatalk(handle, fsp, sbuf);
5385 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
5389 DBG_DEBUG("Path [%s] ret [%d]\n", fsp_str_dbg(fsp), ret);
5393 static int fruit_fstat_rsrc_xattr(vfs_handle_struct *handle,
5395 SMB_STRUCT_STAT *sbuf)
5397 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5400 static int fruit_fstat_rsrc_stream(vfs_handle_struct *handle,
5402 SMB_STRUCT_STAT *sbuf)
5404 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5407 static int fruit_fstat_rsrc_adouble(vfs_handle_struct *handle,
5409 SMB_STRUCT_STAT *sbuf)
5411 struct adouble *ad = NULL;
5414 /* Populate the stat struct with info from the base file. */
5415 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5420 ad = ad_get(talloc_tos(), handle,
5421 fsp->base_fsp->fsp_name,
5424 DBG_ERR("ad_get [%s] failed [%s]\n",
5425 fsp_str_dbg(fsp), strerror(errno));
5429 *sbuf = fsp->base_fsp->fsp_name->st;
5430 sbuf->st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
5431 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5437 static int fruit_fstat_rsrc(vfs_handle_struct *handle, files_struct *fsp,
5438 SMB_STRUCT_STAT *sbuf, struct fio *fio)
5442 switch (fio->config->rsrc) {
5443 case FRUIT_RSRC_STREAM:
5444 ret = fruit_fstat_rsrc_stream(handle, fsp, sbuf);
5447 case FRUIT_RSRC_ADFILE:
5448 ret = fruit_fstat_rsrc_adouble(handle, fsp, sbuf);
5451 case FRUIT_RSRC_XATTR:
5452 ret = fruit_fstat_rsrc_xattr(handle, fsp, sbuf);
5456 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
5463 static int fruit_fstat(vfs_handle_struct *handle, files_struct *fsp,
5464 SMB_STRUCT_STAT *sbuf)
5466 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5470 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5473 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
5475 if (fio->type == ADOUBLE_META) {
5476 rc = fruit_fstat_meta(handle, fsp, sbuf, fio);
5478 rc = fruit_fstat_rsrc(handle, fsp, sbuf, fio);
5482 sbuf->st_ex_mode &= ~S_IFMT;
5483 sbuf->st_ex_mode |= S_IFREG;
5484 sbuf->st_ex_blocks = sbuf->st_ex_size / STAT_ST_BLOCKSIZE + 1;
5487 DBG_DEBUG("Path [%s] rc [%d] size [%"PRIdMAX"]\n",
5488 fsp_str_dbg(fsp), rc, (intmax_t)sbuf->st_ex_size);
5492 static NTSTATUS delete_invalid_meta_stream(
5493 vfs_handle_struct *handle,
5494 const struct smb_filename *smb_fname,
5495 TALLOC_CTX *mem_ctx,
5496 unsigned int *pnum_streams,
5497 struct stream_struct **pstreams,
5500 struct smb_filename *sname = NULL;
5504 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams, AFPINFO_STREAM);
5506 return NT_STATUS_INTERNAL_ERROR;
5510 return NT_STATUS_OK;
5513 sname = synthetic_smb_fname(talloc_tos(),
5514 smb_fname->base_name,
5515 AFPINFO_STREAM_NAME,
5517 if (sname == NULL) {
5518 return NT_STATUS_NO_MEMORY;
5521 ret = SMB_VFS_NEXT_UNLINK(handle, sname);
5524 DBG_ERR("Removing [%s] failed\n", smb_fname_str_dbg(sname));
5525 return map_nt_error_from_unix(errno);
5528 return NT_STATUS_OK;
5531 static NTSTATUS fruit_streaminfo_meta_stream(
5532 vfs_handle_struct *handle,
5533 struct files_struct *fsp,
5534 const struct smb_filename *smb_fname,
5535 TALLOC_CTX *mem_ctx,
5536 unsigned int *pnum_streams,
5537 struct stream_struct **pstreams)
5539 struct stream_struct *stream = *pstreams;
5540 unsigned int num_streams = *pnum_streams;
5543 for (i = 0; i < num_streams; i++) {
5544 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
5549 if (i == num_streams) {
5550 return NT_STATUS_OK;
5553 if (stream[i].size != AFP_INFO_SIZE) {
5554 DBG_ERR("Removing invalid AFPINFO_STREAM size [%jd] from [%s]\n",
5555 (intmax_t)stream[i].size, smb_fname_str_dbg(smb_fname));
5557 return delete_invalid_meta_stream(handle,
5566 return NT_STATUS_OK;
5569 static NTSTATUS fruit_streaminfo_meta_netatalk(
5570 vfs_handle_struct *handle,
5571 struct files_struct *fsp,
5572 const struct smb_filename *smb_fname,
5573 TALLOC_CTX *mem_ctx,
5574 unsigned int *pnum_streams,
5575 struct stream_struct **pstreams)
5577 struct stream_struct *stream = *pstreams;
5578 unsigned int num_streams = *pnum_streams;
5579 struct adouble *ad = NULL;
5584 /* Remove the Netatalk xattr from the list */
5585 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5586 ":" NETATALK_META_XATTR ":$DATA");
5588 return NT_STATUS_NO_MEMORY;
5592 * Check if there's a AFPINFO_STREAM from the VFS streams
5593 * backend and if yes, remove it from the list
5595 for (i = 0; i < num_streams; i++) {
5596 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
5601 if (i < num_streams) {
5602 DBG_WARNING("Unexpected AFPINFO_STREAM on [%s]\n",
5603 smb_fname_str_dbg(smb_fname));
5605 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5608 return NT_STATUS_INTERNAL_ERROR;
5612 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5614 return NT_STATUS_OK;
5617 is_fi_empty = ad_empty_finderinfo(ad);
5621 return NT_STATUS_OK;
5624 ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
5625 AFPINFO_STREAM_NAME, AFP_INFO_SIZE,
5626 smb_roundup(handle->conn, AFP_INFO_SIZE));
5628 return NT_STATUS_NO_MEMORY;
5631 return NT_STATUS_OK;
5634 static NTSTATUS fruit_streaminfo_meta(vfs_handle_struct *handle,
5635 struct files_struct *fsp,
5636 const struct smb_filename *smb_fname,
5637 TALLOC_CTX *mem_ctx,
5638 unsigned int *pnum_streams,
5639 struct stream_struct **pstreams)
5641 struct fruit_config_data *config = NULL;
5644 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5645 return NT_STATUS_INTERNAL_ERROR);
5647 switch (config->meta) {
5648 case FRUIT_META_NETATALK:
5649 status = fruit_streaminfo_meta_netatalk(handle, fsp, smb_fname,
5650 mem_ctx, pnum_streams,
5654 case FRUIT_META_STREAM:
5655 status = fruit_streaminfo_meta_stream(handle, fsp, smb_fname,
5656 mem_ctx, pnum_streams,
5661 return NT_STATUS_INTERNAL_ERROR;
5667 static NTSTATUS fruit_streaminfo_rsrc_stream(
5668 vfs_handle_struct *handle,
5669 struct files_struct *fsp,
5670 const struct smb_filename *smb_fname,
5671 TALLOC_CTX *mem_ctx,
5672 unsigned int *pnum_streams,
5673 struct stream_struct **pstreams)
5677 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
5679 DBG_ERR("Filtering resource stream failed\n");
5680 return NT_STATUS_INTERNAL_ERROR;
5682 return NT_STATUS_OK;
5685 static NTSTATUS fruit_streaminfo_rsrc_xattr(
5686 vfs_handle_struct *handle,
5687 struct files_struct *fsp,
5688 const struct smb_filename *smb_fname,
5689 TALLOC_CTX *mem_ctx,
5690 unsigned int *pnum_streams,
5691 struct stream_struct **pstreams)
5695 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
5697 DBG_ERR("Filtering resource stream failed\n");
5698 return NT_STATUS_INTERNAL_ERROR;
5700 return NT_STATUS_OK;
5703 static NTSTATUS fruit_streaminfo_rsrc_adouble(
5704 vfs_handle_struct *handle,
5705 struct files_struct *fsp,
5706 const struct smb_filename *smb_fname,
5707 TALLOC_CTX *mem_ctx,
5708 unsigned int *pnum_streams,
5709 struct stream_struct **pstreams)
5711 struct stream_struct *stream = *pstreams;
5712 unsigned int num_streams = *pnum_streams;
5713 struct adouble *ad = NULL;
5719 * Check if there's a AFPRESOURCE_STREAM from the VFS streams backend
5720 * and if yes, remove it from the list
5722 for (i = 0; i < num_streams; i++) {
5723 if (strequal_m(stream[i].name, AFPRESOURCE_STREAM)) {
5728 if (i < num_streams) {
5729 DBG_WARNING("Unexpected AFPRESOURCE_STREAM on [%s]\n",
5730 smb_fname_str_dbg(smb_fname));
5732 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5733 AFPRESOURCE_STREAM);
5735 return NT_STATUS_INTERNAL_ERROR;
5739 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
5741 return NT_STATUS_OK;
5744 rlen = ad_getentrylen(ad, ADEID_RFORK);
5748 return NT_STATUS_OK;
5751 ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
5752 AFPRESOURCE_STREAM_NAME, rlen,
5753 smb_roundup(handle->conn, rlen));
5755 return NT_STATUS_NO_MEMORY;
5758 return NT_STATUS_OK;
5761 static NTSTATUS fruit_streaminfo_rsrc(vfs_handle_struct *handle,
5762 struct files_struct *fsp,
5763 const struct smb_filename *smb_fname,
5764 TALLOC_CTX *mem_ctx,
5765 unsigned int *pnum_streams,
5766 struct stream_struct **pstreams)
5768 struct fruit_config_data *config = NULL;
5771 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5772 return NT_STATUS_INTERNAL_ERROR);
5774 switch (config->rsrc) {
5775 case FRUIT_RSRC_STREAM:
5776 status = fruit_streaminfo_rsrc_stream(handle, fsp, smb_fname,
5777 mem_ctx, pnum_streams,
5781 case FRUIT_RSRC_XATTR:
5782 status = fruit_streaminfo_rsrc_xattr(handle, fsp, smb_fname,
5783 mem_ctx, pnum_streams,
5787 case FRUIT_RSRC_ADFILE:
5788 status = fruit_streaminfo_rsrc_adouble(handle, fsp, smb_fname,
5789 mem_ctx, pnum_streams,
5794 return NT_STATUS_INTERNAL_ERROR;
5800 static void fruit_filter_empty_streams(unsigned int *pnum_streams,
5801 struct stream_struct **pstreams)
5803 unsigned num_streams = *pnum_streams;
5804 struct stream_struct *streams = *pstreams;
5807 if (!global_fruit_config.nego_aapl) {
5811 while (i < num_streams) {
5812 struct smb_filename smb_fname = (struct smb_filename) {
5813 .stream_name = streams[i].name,
5816 if (is_ntfs_default_stream_smb_fname(&smb_fname)
5817 || streams[i].size > 0)
5823 streams[i] = streams[num_streams - 1];
5827 *pnum_streams = num_streams;
5830 static NTSTATUS fruit_streaminfo(vfs_handle_struct *handle,
5831 struct files_struct *fsp,
5832 const struct smb_filename *smb_fname,
5833 TALLOC_CTX *mem_ctx,
5834 unsigned int *pnum_streams,
5835 struct stream_struct **pstreams)
5837 struct fruit_config_data *config = NULL;
5840 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5841 return NT_STATUS_UNSUCCESSFUL);
5843 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
5845 status = SMB_VFS_NEXT_STREAMINFO(handle, fsp, smb_fname, mem_ctx,
5846 pnum_streams, pstreams);
5847 if (!NT_STATUS_IS_OK(status)) {
5851 fruit_filter_empty_streams(pnum_streams, pstreams);
5853 status = fruit_streaminfo_meta(handle, fsp, smb_fname,
5854 mem_ctx, pnum_streams, pstreams);
5855 if (!NT_STATUS_IS_OK(status)) {
5859 status = fruit_streaminfo_rsrc(handle, fsp, smb_fname,
5860 mem_ctx, pnum_streams, pstreams);
5861 if (!NT_STATUS_IS_OK(status)) {
5865 return NT_STATUS_OK;
5868 static int fruit_ntimes(vfs_handle_struct *handle,
5869 const struct smb_filename *smb_fname,
5870 struct smb_file_time *ft)
5873 struct adouble *ad = NULL;
5874 struct fruit_config_data *config = NULL;
5876 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5879 if ((config->meta != FRUIT_META_NETATALK) ||
5880 null_timespec(ft->create_time))
5882 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
5885 DEBUG(10,("set btime for %s to %s\n", smb_fname_str_dbg(smb_fname),
5886 time_to_asc(convert_timespec_to_time_t(ft->create_time))));
5888 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5893 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX,
5894 convert_time_t_to_uint32_t(ft->create_time.tv_sec));
5896 rc = ad_set(ad, smb_fname);
5902 DEBUG(1, ("fruit_ntimes: %s\n", smb_fname_str_dbg(smb_fname)));
5905 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
5908 static int fruit_fallocate(struct vfs_handle_struct *handle,
5909 struct files_struct *fsp,
5914 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5917 return SMB_VFS_NEXT_FALLOCATE(handle, fsp, mode, offset, len);
5920 /* Let the pwrite code path handle it. */
5925 static int fruit_ftruncate_rsrc_xattr(struct vfs_handle_struct *handle,
5926 struct files_struct *fsp,
5929 #ifdef HAVE_ATTROPEN
5930 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
5935 static int fruit_ftruncate_rsrc_adouble(struct vfs_handle_struct *handle,
5936 struct files_struct *fsp,
5940 struct adouble *ad = NULL;
5943 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
5945 DBG_DEBUG("ad_get [%s] failed [%s]\n",
5946 fsp_str_dbg(fsp), strerror(errno));
5950 ad_off = ad_getentryoff(ad, ADEID_RFORK);
5952 rc = ftruncate(fsp->fh->fd, offset + ad_off);
5958 ad_setentrylen(ad, ADEID_RFORK, offset);
5960 rc = ad_fset(ad, fsp);
5962 DBG_ERR("ad_fset [%s] failed [%s]\n",
5963 fsp_str_dbg(fsp), strerror(errno));
5972 static int fruit_ftruncate_rsrc_stream(struct vfs_handle_struct *handle,
5973 struct files_struct *fsp,
5976 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
5979 static int fruit_ftruncate_rsrc(struct vfs_handle_struct *handle,
5980 struct files_struct *fsp,
5983 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5987 DBG_ERR("Failed to fetch fsp extension");
5991 switch (fio->config->rsrc) {
5992 case FRUIT_RSRC_XATTR:
5993 ret = fruit_ftruncate_rsrc_xattr(handle, fsp, offset);
5996 case FRUIT_RSRC_ADFILE:
5997 ret = fruit_ftruncate_rsrc_adouble(handle, fsp, offset);
6000 case FRUIT_RSRC_STREAM:
6001 ret = fruit_ftruncate_rsrc_stream(handle, fsp, offset);
6005 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
6013 static int fruit_ftruncate_meta(struct vfs_handle_struct *handle,
6014 struct files_struct *fsp,
6018 DBG_WARNING("ftruncate %s to %jd",
6019 fsp_str_dbg(fsp), (intmax_t)offset);
6020 /* OS X returns NT_STATUS_ALLOTTED_SPACE_EXCEEDED */
6025 /* OS X returns success but does nothing */
6026 DBG_INFO("ignoring ftruncate %s to %jd\n",
6027 fsp_str_dbg(fsp), (intmax_t)offset);
6031 static int fruit_ftruncate(struct vfs_handle_struct *handle,
6032 struct files_struct *fsp,
6035 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
6038 DBG_DEBUG("Path [%s] offset [%"PRIdMAX"]\n", fsp_str_dbg(fsp),
6042 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
6045 if (fio->type == ADOUBLE_META) {
6046 ret = fruit_ftruncate_meta(handle, fsp, offset);
6048 ret = fruit_ftruncate_rsrc(handle, fsp, offset);
6051 DBG_DEBUG("Path [%s] result [%d]\n", fsp_str_dbg(fsp), ret);
6055 static NTSTATUS fruit_create_file(vfs_handle_struct *handle,
6056 struct smb_request *req,
6057 uint16_t root_dir_fid,
6058 struct smb_filename *smb_fname,
6059 uint32_t access_mask,
6060 uint32_t share_access,
6061 uint32_t create_disposition,
6062 uint32_t create_options,
6063 uint32_t file_attributes,
6064 uint32_t oplock_request,
6065 struct smb2_lease *lease,
6066 uint64_t allocation_size,
6067 uint32_t private_flags,
6068 struct security_descriptor *sd,
6069 struct ea_list *ea_list,
6070 files_struct **result,
6072 const struct smb2_create_blobs *in_context_blobs,
6073 struct smb2_create_blobs *out_context_blobs)
6076 struct fruit_config_data *config = NULL;
6077 files_struct *fsp = NULL;
6078 struct fio *fio = NULL;
6079 bool internal_open = (oplock_request & INTERNAL_OPEN_ONLY);
6082 status = check_aapl(handle, req, in_context_blobs, out_context_blobs);
6083 if (!NT_STATUS_IS_OK(status)) {
6087 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
6088 return NT_STATUS_UNSUCCESSFUL);
6090 if (is_apple_stream(smb_fname) && !internal_open) {
6091 ret = ad_convert(handle, smb_fname);
6093 DBG_ERR("ad_convert() failed\n");
6094 return NT_STATUS_UNSUCCESSFUL;
6098 status = SMB_VFS_NEXT_CREATE_FILE(
6099 handle, req, root_dir_fid, smb_fname,
6100 access_mask, share_access,
6101 create_disposition, create_options,
6102 file_attributes, oplock_request,
6104 allocation_size, private_flags,
6105 sd, ea_list, result,
6106 pinfo, in_context_blobs, out_context_blobs);
6107 if (!NT_STATUS_IS_OK(status)) {
6113 if (global_fruit_config.nego_aapl) {
6114 if (config->posix_rename && fsp->is_directory) {
6116 * Enable POSIX directory rename behaviour
6118 fsp->posix_flags |= FSP_POSIX_FLAGS_RENAME;
6123 * If this is a plain open for existing files, opening an 0
6124 * byte size resource fork MUST fail with
6125 * NT_STATUS_OBJECT_NAME_NOT_FOUND.
6127 * Cf the vfs_fruit torture tests in test_rfork_create().
6129 if (global_fruit_config.nego_aapl &&
6130 create_disposition == FILE_OPEN &&
6131 smb_fname->st.st_ex_size == 0 &&
6132 is_ntfs_stream_smb_fname(smb_fname) &&
6133 !(is_ntfs_default_stream_smb_fname(smb_fname)))
6135 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
6139 fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
6140 if (fio != NULL && pinfo != NULL && *pinfo == FILE_WAS_CREATED) {
6141 fio->created = true;
6144 if (is_ntfs_stream_smb_fname(smb_fname)
6145 || fsp->is_directory) {
6149 if (config->locking == FRUIT_LOCKING_NETATALK) {
6150 status = fruit_check_access(
6154 if (!NT_STATUS_IS_OK(status)) {
6162 DEBUG(10, ("fruit_create_file: %s\n", nt_errstr(status)));
6165 close_file(req, fsp, ERROR_CLOSE);
6166 *result = fsp = NULL;
6172 static NTSTATUS fruit_readdir_attr(struct vfs_handle_struct *handle,
6173 const struct smb_filename *fname,
6174 TALLOC_CTX *mem_ctx,
6175 struct readdir_attr_data **pattr_data)
6177 struct fruit_config_data *config = NULL;
6178 struct readdir_attr_data *attr_data;
6182 SMB_VFS_HANDLE_GET_DATA(handle, config,
6183 struct fruit_config_data,
6184 return NT_STATUS_UNSUCCESSFUL);
6186 if (!global_fruit_config.nego_aapl) {
6187 return SMB_VFS_NEXT_READDIR_ATTR(handle, fname, mem_ctx, pattr_data);
6190 DEBUG(10, ("fruit_readdir_attr %s\n", fname->base_name));
6192 ret = ad_convert(handle, fname);
6194 DBG_ERR("ad_convert() failed\n");
6195 return NT_STATUS_UNSUCCESSFUL;
6198 *pattr_data = talloc_zero(mem_ctx, struct readdir_attr_data);
6199 if (*pattr_data == NULL) {
6200 return NT_STATUS_UNSUCCESSFUL;
6202 attr_data = *pattr_data;
6203 attr_data->type = RDATTR_AAPL;
6206 * Mac metadata: compressed FinderInfo, resource fork length
6209 status = readdir_attr_macmeta(handle, fname, attr_data);
6210 if (!NT_STATUS_IS_OK(status)) {
6212 * Error handling is tricky: if we return failure from
6213 * this function, the corresponding directory entry
6214 * will to be passed to the client, so we really just
6215 * want to error out on fatal errors.
6217 if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
6225 if (config->unix_info_enabled) {
6226 attr_data->attr_data.aapl.unix_mode = fname->st.st_ex_mode;
6232 if (!config->readdir_attr_max_access) {
6233 attr_data->attr_data.aapl.max_access = FILE_GENERIC_ALL;
6235 status = smbd_calculate_access_mask(
6239 SEC_FLAG_MAXIMUM_ALLOWED,
6240 &attr_data->attr_data.aapl.max_access);
6241 if (!NT_STATUS_IS_OK(status)) {
6246 return NT_STATUS_OK;
6249 DEBUG(1, ("fruit_readdir_attr %s, error: %s\n",
6250 fname->base_name, nt_errstr(status)));
6251 TALLOC_FREE(*pattr_data);
6255 static NTSTATUS fruit_fget_nt_acl(vfs_handle_struct *handle,
6257 uint32_t security_info,
6258 TALLOC_CTX *mem_ctx,
6259 struct security_descriptor **ppdesc)
6262 struct security_ace ace;
6264 struct fruit_config_data *config;
6266 SMB_VFS_HANDLE_GET_DATA(handle, config,
6267 struct fruit_config_data,
6268 return NT_STATUS_UNSUCCESSFUL);
6270 status = SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
6272 if (!NT_STATUS_IS_OK(status)) {
6277 * Add MS NFS style ACEs with uid, gid and mode
6279 if (!global_fruit_config.nego_aapl) {
6280 return NT_STATUS_OK;
6282 if (!config->unix_info_enabled) {
6283 return NT_STATUS_OK;
6286 /* First remove any existing ACE's with NFS style mode/uid/gid SIDs. */
6287 status = remove_virtual_nfs_aces(*ppdesc);
6288 if (!NT_STATUS_IS_OK(status)) {
6289 DBG_WARNING("failed to remove MS NFS style ACEs\n");
6293 /* MS NFS style mode */
6294 sid_compose(&sid, &global_sid_Unix_NFS_Mode, fsp->fsp_name->st.st_ex_mode);
6295 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6296 status = security_descriptor_dacl_add(*ppdesc, &ace);
6297 if (!NT_STATUS_IS_OK(status)) {
6298 DEBUG(1,("failed to add MS NFS style ACE\n"));
6302 /* MS NFS style uid */
6303 sid_compose(&sid, &global_sid_Unix_NFS_Users, fsp->fsp_name->st.st_ex_uid);
6304 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6305 status = security_descriptor_dacl_add(*ppdesc, &ace);
6306 if (!NT_STATUS_IS_OK(status)) {
6307 DEBUG(1,("failed to add MS NFS style ACE\n"));
6311 /* MS NFS style gid */
6312 sid_compose(&sid, &global_sid_Unix_NFS_Groups, fsp->fsp_name->st.st_ex_gid);
6313 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6314 status = security_descriptor_dacl_add(*ppdesc, &ace);
6315 if (!NT_STATUS_IS_OK(status)) {
6316 DEBUG(1,("failed to add MS NFS style ACE\n"));
6320 return NT_STATUS_OK;
6323 static NTSTATUS fruit_fset_nt_acl(vfs_handle_struct *handle,
6325 uint32_t security_info_sent,
6326 const struct security_descriptor *orig_psd)
6330 mode_t ms_nfs_mode = 0;
6332 struct security_descriptor *psd = NULL;
6333 uint32_t orig_num_aces = 0;
6335 if (orig_psd->dacl != NULL) {
6336 orig_num_aces = orig_psd->dacl->num_aces;
6339 psd = security_descriptor_copy(talloc_tos(), orig_psd);
6341 return NT_STATUS_NO_MEMORY;
6344 DBG_DEBUG("fruit_fset_nt_acl: %s\n", fsp_str_dbg(fsp));
6346 status = check_ms_nfs(handle, fsp, psd, &ms_nfs_mode, &do_chmod);
6347 if (!NT_STATUS_IS_OK(status)) {
6348 DEBUG(1, ("fruit_fset_nt_acl: check_ms_nfs failed%s\n", fsp_str_dbg(fsp)));
6354 * If only ms_nfs ACE entries were sent, ensure we set the DACL
6355 * sent/present flags correctly now we've removed them.
6358 if (orig_num_aces != 0) {
6360 * Are there any ACE's left ?
6362 if (psd->dacl->num_aces == 0) {
6363 /* No - clear the DACL sent/present flags. */
6364 security_info_sent &= ~SECINFO_DACL;
6365 psd->type &= ~SEC_DESC_DACL_PRESENT;
6369 status = SMB_VFS_NEXT_FSET_NT_ACL(handle, fsp, security_info_sent, psd);
6370 if (!NT_STATUS_IS_OK(status)) {
6371 DEBUG(1, ("fruit_fset_nt_acl: SMB_VFS_NEXT_FSET_NT_ACL failed%s\n", fsp_str_dbg(fsp)));
6377 if (fsp->fh->fd != -1) {
6378 result = SMB_VFS_FCHMOD(fsp, ms_nfs_mode);
6380 result = SMB_VFS_CHMOD(fsp->conn,
6386 DEBUG(1, ("chmod: %s, result: %d, %04o error %s\n", fsp_str_dbg(fsp),
6387 result, (unsigned)ms_nfs_mode,
6389 status = map_nt_error_from_unix(errno);
6396 return NT_STATUS_OK;
6399 static struct vfs_offload_ctx *fruit_offload_ctx;
6401 struct fruit_offload_read_state {
6402 struct vfs_handle_struct *handle;
6403 struct tevent_context *ev;
6409 static void fruit_offload_read_done(struct tevent_req *subreq);
6411 static struct tevent_req *fruit_offload_read_send(
6412 TALLOC_CTX *mem_ctx,
6413 struct tevent_context *ev,
6414 struct vfs_handle_struct *handle,
6421 struct tevent_req *req = NULL;
6422 struct tevent_req *subreq = NULL;
6423 struct fruit_offload_read_state *state = NULL;
6425 req = tevent_req_create(mem_ctx, &state,
6426 struct fruit_offload_read_state);
6430 *state = (struct fruit_offload_read_state) {
6437 subreq = SMB_VFS_NEXT_OFFLOAD_READ_SEND(mem_ctx, ev, handle, fsp,
6438 fsctl, ttl, offset, to_copy);
6439 if (tevent_req_nomem(subreq, req)) {
6440 return tevent_req_post(req, ev);
6442 tevent_req_set_callback(subreq, fruit_offload_read_done, req);
6446 static void fruit_offload_read_done(struct tevent_req *subreq)
6448 struct tevent_req *req = tevent_req_callback_data(
6449 subreq, struct tevent_req);
6450 struct fruit_offload_read_state *state = tevent_req_data(
6451 req, struct fruit_offload_read_state);
6454 status = SMB_VFS_NEXT_OFFLOAD_READ_RECV(subreq,
6458 TALLOC_FREE(subreq);
6459 if (tevent_req_nterror(req, status)) {
6463 if (state->fsctl != FSCTL_SRV_REQUEST_RESUME_KEY) {
6464 tevent_req_done(req);
6468 status = vfs_offload_token_ctx_init(state->fsp->conn->sconn->client,
6469 &fruit_offload_ctx);
6470 if (tevent_req_nterror(req, status)) {
6474 status = vfs_offload_token_db_store_fsp(fruit_offload_ctx,
6477 if (tevent_req_nterror(req, status)) {
6481 tevent_req_done(req);
6485 static NTSTATUS fruit_offload_read_recv(struct tevent_req *req,
6486 struct vfs_handle_struct *handle,
6487 TALLOC_CTX *mem_ctx,
6490 struct fruit_offload_read_state *state = tevent_req_data(
6491 req, struct fruit_offload_read_state);
6494 if (tevent_req_is_nterror(req, &status)) {
6495 tevent_req_received(req);
6499 token->length = state->token.length;
6500 token->data = talloc_move(mem_ctx, &state->token.data);
6502 tevent_req_received(req);
6503 return NT_STATUS_OK;
6506 struct fruit_offload_write_state {
6507 struct vfs_handle_struct *handle;
6509 struct files_struct *src_fsp;
6510 struct files_struct *dst_fsp;
6514 static void fruit_offload_write_done(struct tevent_req *subreq);
6515 static struct tevent_req *fruit_offload_write_send(struct vfs_handle_struct *handle,
6516 TALLOC_CTX *mem_ctx,
6517 struct tevent_context *ev,
6520 off_t transfer_offset,
6521 struct files_struct *dest_fsp,
6525 struct tevent_req *req, *subreq;
6526 struct fruit_offload_write_state *state;
6528 struct fruit_config_data *config;
6529 off_t src_off = transfer_offset;
6530 files_struct *src_fsp = NULL;
6531 off_t to_copy = num;
6532 bool copyfile_enabled = false;
6534 DEBUG(10,("soff: %ju, doff: %ju, len: %ju\n",
6535 (uintmax_t)src_off, (uintmax_t)dest_off, (uintmax_t)num));
6537 SMB_VFS_HANDLE_GET_DATA(handle, config,
6538 struct fruit_config_data,
6541 req = tevent_req_create(mem_ctx, &state,
6542 struct fruit_offload_write_state);
6546 state->handle = handle;
6547 state->dst_fsp = dest_fsp;
6550 case FSCTL_SRV_COPYCHUNK:
6551 case FSCTL_SRV_COPYCHUNK_WRITE:
6552 copyfile_enabled = config->copyfile_enabled;
6559 * Check if this a OS X copyfile style copychunk request with
6560 * a requested chunk count of 0 that was translated to a
6561 * offload_write_send VFS call overloading the parameters src_off
6562 * = dest_off = num = 0.
6564 if (copyfile_enabled && num == 0 && src_off == 0 && dest_off == 0) {
6565 status = vfs_offload_token_db_fetch_fsp(
6566 fruit_offload_ctx, token, &src_fsp);
6567 if (tevent_req_nterror(req, status)) {
6568 return tevent_req_post(req, ev);
6570 state->src_fsp = src_fsp;
6572 status = vfs_stat_fsp(src_fsp);
6573 if (tevent_req_nterror(req, status)) {
6574 return tevent_req_post(req, ev);
6577 to_copy = src_fsp->fsp_name->st.st_ex_size;
6578 state->is_copyfile = true;
6581 subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle,
6590 if (tevent_req_nomem(subreq, req)) {
6591 return tevent_req_post(req, ev);
6594 tevent_req_set_callback(subreq, fruit_offload_write_done, req);
6598 static void fruit_offload_write_done(struct tevent_req *subreq)
6600 struct tevent_req *req = tevent_req_callback_data(
6601 subreq, struct tevent_req);
6602 struct fruit_offload_write_state *state = tevent_req_data(
6603 req, struct fruit_offload_write_state);
6605 unsigned int num_streams = 0;
6606 struct stream_struct *streams = NULL;
6608 struct smb_filename *src_fname_tmp = NULL;
6609 struct smb_filename *dst_fname_tmp = NULL;
6611 status = SMB_VFS_NEXT_OFFLOAD_WRITE_RECV(state->handle,
6614 TALLOC_FREE(subreq);
6615 if (tevent_req_nterror(req, status)) {
6619 if (!state->is_copyfile) {
6620 tevent_req_done(req);
6625 * Now copy all remaining streams. We know the share supports
6626 * streams, because we're in vfs_fruit. We don't do this async
6627 * because streams are few and small.
6629 status = vfs_streaminfo(state->handle->conn, state->src_fsp,
6630 state->src_fsp->fsp_name,
6631 req, &num_streams, &streams);
6632 if (tevent_req_nterror(req, status)) {
6636 if (num_streams == 1) {
6637 /* There is always one stream, ::$DATA. */
6638 tevent_req_done(req);
6642 for (i = 0; i < num_streams; i++) {
6643 DEBUG(10, ("%s: stream: '%s'/%zu\n",
6644 __func__, streams[i].name, (size_t)streams[i].size));
6646 src_fname_tmp = synthetic_smb_fname(
6648 state->src_fsp->fsp_name->base_name,
6651 state->src_fsp->fsp_name->flags);
6652 if (tevent_req_nomem(src_fname_tmp, req)) {
6656 if (is_ntfs_default_stream_smb_fname(src_fname_tmp)) {
6657 TALLOC_FREE(src_fname_tmp);
6661 dst_fname_tmp = synthetic_smb_fname(
6663 state->dst_fsp->fsp_name->base_name,
6666 state->dst_fsp->fsp_name->flags);
6667 if (tevent_req_nomem(dst_fname_tmp, req)) {
6668 TALLOC_FREE(src_fname_tmp);
6672 status = copy_file(req,
6673 state->handle->conn,
6676 OPENX_FILE_CREATE_IF_NOT_EXIST,
6678 if (!NT_STATUS_IS_OK(status)) {
6679 DEBUG(1, ("%s: copy %s to %s failed: %s\n", __func__,
6680 smb_fname_str_dbg(src_fname_tmp),
6681 smb_fname_str_dbg(dst_fname_tmp),
6682 nt_errstr(status)));
6683 TALLOC_FREE(src_fname_tmp);
6684 TALLOC_FREE(dst_fname_tmp);
6685 tevent_req_nterror(req, status);
6689 TALLOC_FREE(src_fname_tmp);
6690 TALLOC_FREE(dst_fname_tmp);
6693 TALLOC_FREE(streams);
6694 TALLOC_FREE(src_fname_tmp);
6695 TALLOC_FREE(dst_fname_tmp);
6696 tevent_req_done(req);
6699 static NTSTATUS fruit_offload_write_recv(struct vfs_handle_struct *handle,
6700 struct tevent_req *req,
6703 struct fruit_offload_write_state *state = tevent_req_data(
6704 req, struct fruit_offload_write_state);
6707 if (tevent_req_is_nterror(req, &status)) {
6708 DEBUG(1, ("server side copy chunk failed: %s\n",
6709 nt_errstr(status)));
6711 tevent_req_received(req);
6715 *copied = state->copied;
6716 tevent_req_received(req);
6718 return NT_STATUS_OK;
6721 static char *fruit_get_bandsize_line(char **lines, int numlines)
6724 static bool re_initialized = false;
6728 if (!re_initialized) {
6729 ret = regcomp(&re, "^[[:blank:]]*<key>band-size</key>$", 0);
6733 re_initialized = true;
6736 for (i = 0; i < numlines; i++) {
6737 regmatch_t matches[1];
6739 ret = regexec(&re, lines[i], 1, matches, 0);
6742 * Check if the match was on the last line, sa we want
6743 * the subsequent line.
6745 if (i + 1 == numlines) {
6748 return lines[i + 1];
6750 if (ret != REG_NOMATCH) {
6758 static bool fruit_get_bandsize_from_line(char *line, size_t *_band_size)
6761 static bool re_initialized = false;
6762 regmatch_t matches[2];
6767 if (!re_initialized) {
6770 "<integer>\\([[:digit:]]*\\)</integer>$",
6775 re_initialized = true;
6778 ret = regexec(&re, line, 2, matches, 0);
6780 DBG_ERR("regex failed [%s]\n", line);
6784 line[matches[1].rm_eo] = '\0';
6786 ok = conv_str_u64(&line[matches[1].rm_so], &band_size);
6790 *_band_size = (size_t)band_size;
6795 * This reads and parses an Info.plist from a TM sparsebundle looking for the
6796 * "band-size" key and value.
6798 static bool fruit_get_bandsize(vfs_handle_struct *handle,
6802 #define INFO_PLIST_MAX_SIZE 64*1024
6804 struct smb_filename *smb_fname = NULL;
6805 files_struct *fsp = NULL;
6806 uint8_t *file_data = NULL;
6807 char **lines = NULL;
6808 char *band_size_line = NULL;
6809 size_t plist_file_size;
6816 plist = talloc_asprintf(talloc_tos(),
6818 handle->conn->connectpath,
6820 if (plist == NULL) {
6825 smb_fname = synthetic_smb_fname(talloc_tos(), plist, NULL, NULL, 0);
6826 if (smb_fname == NULL) {
6831 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
6833 DBG_INFO("Ignoring Sparsebundle without Info.plist [%s]\n", dir);
6838 plist_file_size = smb_fname->st.st_ex_size;
6840 if (plist_file_size > INFO_PLIST_MAX_SIZE) {
6841 DBG_INFO("%s is too large, ignoring\n", plist);
6846 status = SMB_VFS_NEXT_CREATE_FILE(
6849 0, /* root_dir_fid */
6850 smb_fname, /* fname */
6851 FILE_GENERIC_READ, /* access_mask */
6852 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
6853 FILE_OPEN, /* create_disposition */
6854 0, /* create_options */
6855 0, /* file_attributes */
6856 INTERNAL_OPEN_ONLY, /* oplock_request */
6858 0, /* allocation_size */
6859 0, /* private_flags */
6864 NULL, NULL); /* create context */
6865 if (!NT_STATUS_IS_OK(status)) {
6866 DBG_INFO("Opening [%s] failed [%s]\n",
6867 smb_fname_str_dbg(smb_fname), nt_errstr(status));
6872 file_data = talloc_array(talloc_tos(), uint8_t, plist_file_size);
6873 if (file_data == NULL) {
6878 nread = SMB_VFS_NEXT_PREAD(handle, fsp, file_data, plist_file_size, 0);
6879 if (nread != plist_file_size) {
6880 DBG_ERR("Short read on [%s]: %zu/%zd\n",
6881 fsp_str_dbg(fsp), nread, plist_file_size);
6887 status = close_file(NULL, fsp, NORMAL_CLOSE);
6889 if (!NT_STATUS_IS_OK(status)) {
6890 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
6895 lines = file_lines_parse((char *)file_data,
6899 if (lines == NULL) {
6904 band_size_line = fruit_get_bandsize_line(lines, numlines);
6905 if (band_size_line == NULL) {
6906 DBG_ERR("Didn't find band-size key in [%s]\n",
6907 smb_fname_str_dbg(smb_fname));
6912 ok = fruit_get_bandsize_from_line(band_size_line, band_size);
6914 DBG_ERR("fruit_get_bandsize_from_line failed\n");
6918 DBG_DEBUG("Parsed band-size [%zu] for [%s]\n", *band_size, plist);
6922 status = close_file(NULL, fsp, NORMAL_CLOSE);
6923 if (!NT_STATUS_IS_OK(status)) {
6924 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
6929 TALLOC_FREE(smb_fname);
6930 TALLOC_FREE(file_data);
6935 struct fruit_disk_free_state {
6939 static bool fruit_get_num_bands(vfs_handle_struct *handle,
6944 struct smb_filename *bands_dir = NULL;
6946 struct dirent *e = NULL;
6950 path = talloc_asprintf(talloc_tos(),
6952 handle->conn->connectpath,
6958 bands_dir = synthetic_smb_fname(talloc_tos(),
6964 if (bands_dir == NULL) {
6968 d = SMB_VFS_NEXT_OPENDIR(handle, bands_dir, NULL, 0);
6970 TALLOC_FREE(bands_dir);
6976 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
6978 e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
6980 if (ISDOT(e->d_name) || ISDOTDOT(e->d_name)) {
6986 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
6988 TALLOC_FREE(bands_dir);
6992 DBG_DEBUG("%zu bands in [%s]\n", nbands, smb_fname_str_dbg(bands_dir));
6994 TALLOC_FREE(bands_dir);
7000 static bool fruit_tmsize_do_dirent(vfs_handle_struct *handle,
7001 struct fruit_disk_free_state *state,
7006 size_t sparsebundle_strlen = strlen("sparsebundle");
7007 size_t bandsize = 0;
7011 p = strstr(e->d_name, "sparsebundle");
7016 if (p[sparsebundle_strlen] != '\0') {
7020 DBG_DEBUG("Processing sparsebundle [%s]\n", e->d_name);
7022 ok = fruit_get_bandsize(handle, e->d_name, &bandsize);
7025 * Beware of race conditions: this may be an uninitialized
7026 * Info.plist that a client is just creating. We don't want let
7027 * this to trigger complete failure.
7029 DBG_ERR("Processing sparsebundle [%s] failed\n", e->d_name);
7033 ok = fruit_get_num_bands(handle, e->d_name, &nbands);
7036 * Beware of race conditions: this may be a backup sparsebundle
7037 * in an early stage lacking a bands subdirectory. We don't want
7038 * let this to trigger complete failure.
7040 DBG_ERR("Processing sparsebundle [%s] failed\n", e->d_name);
7044 if (bandsize > SIZE_MAX/nbands) {
7045 DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
7049 tm_size = bandsize * nbands;
7051 if (state->total_size + tm_size < state->total_size) {
7052 DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
7057 state->total_size += tm_size;
7059 DBG_DEBUG("[%s] tm_size [%jd] total_size [%jd]\n",
7060 e->d_name, (intmax_t)tm_size, (intmax_t)state->total_size);
7066 * Calculate used size of a TimeMachine volume
7068 * This assumes that the volume is used only for TimeMachine.
7070 * - readdir(basedir of share), then
7071 * - for every element that matches regex "^\(.*\)\.sparsebundle$" :
7072 * - parse "\1.sparsebundle/Info.plist" and read the band-size XML key
7073 * - count band files in "\1.sparsebundle/bands/"
7074 * - calculate used size of all bands: band_count * band_size
7076 static uint64_t fruit_disk_free(vfs_handle_struct *handle,
7077 const struct smb_filename *smb_fname,
7082 struct fruit_config_data *config = NULL;
7083 struct fruit_disk_free_state state = {0};
7085 struct dirent *e = NULL;
7091 SMB_VFS_HANDLE_GET_DATA(handle, config,
7092 struct fruit_config_data,
7095 if (!config->time_machine ||
7096 config->time_machine_max_size == 0)
7098 return SMB_VFS_NEXT_DISK_FREE(handle,
7105 d = SMB_VFS_NEXT_OPENDIR(handle, smb_fname, NULL, 0);
7110 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
7112 e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
7114 ok = fruit_tmsize_do_dirent(handle, &state, e);
7116 SMB_VFS_NEXT_CLOSEDIR(handle, d);
7121 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
7126 dsize = config->time_machine_max_size / 512;
7127 dfree = dsize - (state.total_size / 512);
7128 if (dfree > dsize) {
7138 static struct vfs_fn_pointers vfs_fruit_fns = {
7139 .connect_fn = fruit_connect,
7140 .disk_free_fn = fruit_disk_free,
7142 /* File operations */
7143 .chmod_fn = fruit_chmod,
7144 .chown_fn = fruit_chown,
7145 .unlink_fn = fruit_unlink,
7146 .rename_fn = fruit_rename,
7147 .rmdir_fn = fruit_rmdir,
7148 .open_fn = fruit_open,
7149 .close_fn = fruit_close,
7150 .pread_fn = fruit_pread,
7151 .pwrite_fn = fruit_pwrite,
7152 .pread_send_fn = fruit_pread_send,
7153 .pread_recv_fn = fruit_pread_recv,
7154 .pwrite_send_fn = fruit_pwrite_send,
7155 .pwrite_recv_fn = fruit_pwrite_recv,
7156 .stat_fn = fruit_stat,
7157 .lstat_fn = fruit_lstat,
7158 .fstat_fn = fruit_fstat,
7159 .streaminfo_fn = fruit_streaminfo,
7160 .ntimes_fn = fruit_ntimes,
7161 .ftruncate_fn = fruit_ftruncate,
7162 .fallocate_fn = fruit_fallocate,
7163 .create_file_fn = fruit_create_file,
7164 .readdir_attr_fn = fruit_readdir_attr,
7165 .offload_read_send_fn = fruit_offload_read_send,
7166 .offload_read_recv_fn = fruit_offload_read_recv,
7167 .offload_write_send_fn = fruit_offload_write_send,
7168 .offload_write_recv_fn = fruit_offload_write_recv,
7170 /* NT ACL operations */
7171 .fget_nt_acl_fn = fruit_fget_nt_acl,
7172 .fset_nt_acl_fn = fruit_fset_nt_acl,
7176 NTSTATUS vfs_fruit_init(TALLOC_CTX *ctx)
7178 NTSTATUS ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "fruit",
7180 if (!NT_STATUS_IS_OK(ret)) {
7184 vfs_fruit_debug_level = debug_add_class("fruit");
7185 if (vfs_fruit_debug_level == -1) {
7186 vfs_fruit_debug_level = DBGC_VFS;
7187 DEBUG(0, ("%s: Couldn't register custom debugging class!\n",
7190 DEBUG(10, ("%s: Debug class number of '%s': %d\n",
7191 "vfs_fruit_init","fruit",vfs_fruit_debug_level));