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 adouble *ad,
1456 const struct smb_filename *smb_fname)
1459 bool converted_xattr = false;
1462 ok = ad_convert_xattr(ad, smb_fname, &converted_xattr);
1467 ok = ad_convert_blank_rfork(ad, &blank);
1472 if (converted_xattr || blank) {
1473 ok = ad_convert_truncate(ad, smb_fname);
1479 ok = ad_convert_finderinfo(ad, smb_fname);
1481 DBG_ERR("Failed to convert [%s]\n",
1482 smb_fname_str_dbg(smb_fname));
1486 ok = ad_convert_delete_adfile(ad, smb_fname);
1495 * Read and parse Netatalk AppleDouble metadata xattr
1497 static ssize_t ad_read_meta(struct adouble *ad,
1498 const struct smb_filename *smb_fname)
1504 DEBUG(10, ("reading meta xattr for %s\n", smb_fname->base_name));
1506 ealen = SMB_VFS_GETXATTR(ad->ad_handle->conn, smb_fname,
1507 AFPINFO_EA_NETATALK, ad->ad_data,
1513 if (errno == ENOATTR) {
1519 DEBUG(2, ("error reading meta xattr: %s\n",
1525 if (ealen != AD_DATASZ_XATTR) {
1526 DEBUG(2, ("bad size %zd\n", ealen));
1532 /* Now parse entries */
1533 ok = ad_unpack(ad, ADEID_NUM_XATTR, AD_DATASZ_XATTR);
1535 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1541 if (!ad_getentryoff(ad, ADEID_FINDERI)
1542 || !ad_getentryoff(ad, ADEID_COMMENT)
1543 || !ad_getentryoff(ad, ADEID_FILEDATESI)
1544 || !ad_getentryoff(ad, ADEID_AFPFILEI)
1545 || !ad_getentryoff(ad, ADEID_PRIVDEV)
1546 || !ad_getentryoff(ad, ADEID_PRIVINO)
1547 || !ad_getentryoff(ad, ADEID_PRIVSYN)
1548 || !ad_getentryoff(ad, ADEID_PRIVID)) {
1549 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1556 DEBUG(10, ("reading meta xattr for %s, rc: %d\n",
1557 smb_fname->base_name, rc));
1561 if (errno == EINVAL) {
1563 removexattr(smb_fname->base_name, AFPINFO_EA_NETATALK);
1571 static int ad_open_rsrc_xattr(const struct smb_filename *smb_fname,
1575 #ifdef HAVE_ATTROPEN
1576 /* FIXME: direct Solaris xattr syscall */
1577 return attropen(smb_fname->base_name,
1578 AFPRESOURCE_EA_NETATALK, flags, mode);
1585 static int ad_open_rsrc_adouble(const struct smb_filename *smb_fname,
1591 struct smb_filename *adp_smb_fname = NULL;
1593 ret = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
1598 fd = open(adp_smb_fname->base_name, flags, mode);
1599 TALLOC_FREE(adp_smb_fname);
1604 static int ad_open_rsrc(vfs_handle_struct *handle,
1605 const struct smb_filename *smb_fname,
1609 struct fruit_config_data *config = NULL;
1612 SMB_VFS_HANDLE_GET_DATA(handle, config,
1613 struct fruit_config_data, return -1);
1615 if (config->rsrc == FRUIT_RSRC_XATTR) {
1616 fd = ad_open_rsrc_xattr(smb_fname, flags, mode);
1618 fd = ad_open_rsrc_adouble(smb_fname, flags, mode);
1625 * Here's the deal: for ADOUBLE_META we can do without an fd as we can issue
1626 * path based xattr calls. For ADOUBLE_RSRC however we need a full-fledged fd
1627 * for file IO on the ._ file.
1629 static int ad_open(vfs_handle_struct *handle,
1632 const struct smb_filename *smb_fname,
1638 DBG_DEBUG("Path [%s] type [%s]\n", smb_fname->base_name,
1639 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
1641 if (ad->ad_type == ADOUBLE_META) {
1645 if ((fsp != NULL) && (fsp->fh != NULL) && (fsp->fh->fd != -1)) {
1646 ad->ad_fd = fsp->fh->fd;
1647 ad->ad_opened = false;
1651 fd = ad_open_rsrc(handle, smb_fname, flags, mode);
1655 ad->ad_opened = true;
1658 DBG_DEBUG("Path [%s] type [%s] fd [%d]\n",
1659 smb_fname->base_name,
1660 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc", fd);
1665 static ssize_t ad_read_rsrc_xattr(struct adouble *ad)
1670 /* FIXME: direct sys_fstat(), don't have an fsp */
1671 ret = sys_fstat(ad->ad_fd, &st,
1672 lp_fake_directory_create_times(
1673 SNUM(ad->ad_handle->conn)));
1678 ad_setentrylen(ad, ADEID_RFORK, st.st_ex_size);
1679 return st.st_ex_size;
1682 static ssize_t ad_read_rsrc_adouble(struct adouble *ad,
1683 const struct smb_filename *smb_fname)
1685 SMB_STRUCT_STAT sbuf;
1692 ret = sys_fstat(ad->ad_fd, &sbuf, lp_fake_directory_create_times(
1693 SNUM(ad->ad_handle->conn)));
1699 * AppleDouble file header content and size, two cases:
1701 * - without xattrs it is exactly AD_DATASZ_DOT_UND (82) bytes large
1702 * - with embedded xattrs it can be larger, up to AD_XATTR_MAX_HDR_SIZE
1704 * Read as much as we can up to AD_XATTR_MAX_HDR_SIZE.
1706 size = sbuf.st_ex_size;
1707 if (size > talloc_array_length(ad->ad_data)) {
1708 if (size > AD_XATTR_MAX_HDR_SIZE) {
1709 size = AD_XATTR_MAX_HDR_SIZE;
1711 p_ad = talloc_realloc(ad, ad->ad_data, char, size);
1718 len = sys_pread(ad->ad_fd, ad->ad_data,
1719 talloc_array_length(ad->ad_data), 0);
1720 if (len != talloc_array_length(ad->ad_data)) {
1721 DBG_NOTICE("%s %s: bad size: %zd\n",
1722 smb_fname->base_name, strerror(errno), len);
1726 /* Now parse entries */
1727 ok = ad_unpack(ad, ADEID_NUM_DOT_UND, sbuf.st_ex_size);
1729 DBG_ERR("invalid AppleDouble resource %s\n",
1730 smb_fname->base_name);
1735 if ((ad_getentryoff(ad, ADEID_FINDERI) != ADEDOFF_FINDERI_DOT_UND)
1736 || (ad_getentrylen(ad, ADEID_FINDERI) < ADEDLEN_FINDERI)
1737 || (ad_getentryoff(ad, ADEID_RFORK) < ADEDOFF_RFORK_DOT_UND)) {
1738 DBG_ERR("invalid AppleDouble resource %s\n",
1739 smb_fname->base_name);
1745 * Try to fixup AppleDouble files created by OS X with xattrs
1746 * appended to the ADEID_FINDERI entry.
1749 ret = ad_convert(ad, smb_fname);
1751 DBG_WARNING("Failed to convert [%s]\n", smb_fname->base_name);
1759 * Read and parse resource fork, either ._ AppleDouble file or xattr
1761 static ssize_t ad_read_rsrc(struct adouble *ad,
1762 const struct smb_filename *smb_fname)
1764 struct fruit_config_data *config = NULL;
1767 SMB_VFS_HANDLE_GET_DATA(ad->ad_handle, config,
1768 struct fruit_config_data, return -1);
1770 if (config->rsrc == FRUIT_RSRC_XATTR) {
1771 len = ad_read_rsrc_xattr(ad);
1773 len = ad_read_rsrc_adouble(ad, smb_fname);
1780 * Read and unpack an AppleDouble metadata xattr or resource
1782 static ssize_t ad_read(struct adouble *ad, const struct smb_filename *smb_fname)
1784 switch (ad->ad_type) {
1786 return ad_read_meta(ad, smb_fname);
1788 return ad_read_rsrc(ad, smb_fname);
1794 static int adouble_destructor(struct adouble *ad)
1796 if ((ad->ad_fd != -1) && ad->ad_opened) {
1804 * Allocate a struct adouble without initialiing it
1806 * The struct is either hang of the fsp extension context or if fsp is
1809 * @param[in] ctx talloc context
1810 * @param[in] handle vfs handle
1811 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1813 * @return adouble handle
1815 static struct adouble *ad_alloc(TALLOC_CTX *ctx, vfs_handle_struct *handle,
1816 adouble_type_t type)
1821 struct fruit_config_data *config;
1823 SMB_VFS_HANDLE_GET_DATA(handle, config,
1824 struct fruit_config_data, return NULL);
1828 adsize = AD_DATASZ_XATTR;
1831 if (config->rsrc == FRUIT_RSRC_ADFILE) {
1832 adsize = AD_DATASZ_DOT_UND;
1839 ad = talloc_zero(ctx, struct adouble);
1846 ad->ad_data = talloc_zero_array(ad, char, adsize);
1847 if (ad->ad_data == NULL) {
1853 ad->ad_handle = handle;
1855 ad->ad_magic = AD_MAGIC;
1856 ad->ad_version = AD_VERSION;
1859 talloc_set_destructor(ad, adouble_destructor);
1869 * Allocate and initialize a new struct adouble
1871 * @param[in] ctx talloc context
1872 * @param[in] handle vfs handle
1873 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1875 * @return adouble handle, initialized
1877 static struct adouble *ad_init(TALLOC_CTX *ctx, vfs_handle_struct *handle,
1878 adouble_type_t type)
1881 const struct ad_entry_order *eid;
1882 struct adouble *ad = NULL;
1883 struct fruit_config_data *config;
1884 time_t t = time(NULL);
1886 SMB_VFS_HANDLE_GET_DATA(handle, config,
1887 struct fruit_config_data, return NULL);
1891 eid = entry_order_meta_xattr;
1894 if (config->rsrc == FRUIT_RSRC_ADFILE) {
1895 eid = entry_order_dot_und;
1897 eid = entry_order_rsrc_xattr;
1904 ad = ad_alloc(ctx, handle, type);
1910 ad->ad_eid[eid->id].ade_off = eid->offset;
1911 ad->ad_eid[eid->id].ade_len = eid->len;
1915 /* put something sane in the date fields */
1916 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, t);
1917 ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, t);
1918 ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, t);
1919 ad_setdate(ad, AD_DATE_BACKUP, htonl(AD_DATE_START));
1927 static struct adouble *ad_get_internal(TALLOC_CTX *ctx,
1928 vfs_handle_struct *handle,
1930 const struct smb_filename *smb_fname,
1931 adouble_type_t type)
1935 struct adouble *ad = NULL;
1939 smb_fname = fsp->base_fsp->fsp_name;
1942 DEBUG(10, ("ad_get(%s) called for %s\n",
1943 type == ADOUBLE_META ? "meta" : "rsrc",
1944 smb_fname->base_name));
1946 ad = ad_alloc(ctx, handle, type);
1952 /* Try rw first so we can use the fd in ad_convert() */
1955 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
1956 if (rc == -1 && ((errno == EROFS) || (errno == EACCES))) {
1958 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
1961 DBG_DEBUG("ad_open [%s] error [%s]\n",
1962 smb_fname->base_name, strerror(errno));
1967 len = ad_read(ad, smb_fname);
1969 DEBUG(10, ("error reading AppleDouble for %s\n",
1970 smb_fname->base_name));
1976 DEBUG(10, ("ad_get(%s) for %s returning %d\n",
1977 type == ADOUBLE_META ? "meta" : "rsrc",
1978 smb_fname->base_name, rc));
1987 * Return AppleDouble data for a file
1989 * @param[in] ctx talloc context
1990 * @param[in] handle vfs handle
1991 * @param[in] smb_fname pathname to file or directory
1992 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1994 * @return talloced struct adouble or NULL on error
1996 static struct adouble *ad_get(TALLOC_CTX *ctx,
1997 vfs_handle_struct *handle,
1998 const struct smb_filename *smb_fname,
1999 adouble_type_t type)
2001 return ad_get_internal(ctx, handle, NULL, smb_fname, type);
2005 * Return AppleDouble data for a file
2007 * @param[in] ctx talloc context
2008 * @param[in] handle vfs handle
2009 * @param[in] fsp fsp to use for IO
2010 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2012 * @return talloced struct adouble or NULL on error
2014 static struct adouble *ad_fget(TALLOC_CTX *ctx, vfs_handle_struct *handle,
2015 files_struct *fsp, adouble_type_t type)
2017 return ad_get_internal(ctx, handle, fsp, NULL, type);
2021 * Set AppleDouble metadata on a file or directory
2023 * @param[in] ad adouble handle
2025 * @param[in] smb_fname pathname to file or directory
2027 * @return status code, 0 means success
2029 static int ad_set(struct adouble *ad, const struct smb_filename *smb_fname)
2034 DBG_DEBUG("Path [%s]\n", smb_fname->base_name);
2036 if (ad->ad_type != ADOUBLE_META) {
2037 DBG_ERR("ad_set on [%s] used with ADOUBLE_RSRC\n",
2038 smb_fname->base_name);
2047 ret = SMB_VFS_SETXATTR(ad->ad_handle->conn,
2049 AFPINFO_EA_NETATALK,
2051 AD_DATASZ_XATTR, 0);
2053 DBG_DEBUG("Path [%s] ret [%d]\n", smb_fname->base_name, ret);
2059 * Set AppleDouble metadata on a file or directory
2061 * @param[in] ad adouble handle
2062 * @param[in] fsp file handle
2064 * @return status code, 0 means success
2066 static int ad_fset(struct adouble *ad, files_struct *fsp)
2072 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
2075 || (fsp->fh == NULL)
2076 || (fsp->fh->fd == -1))
2078 smb_panic("bad fsp");
2086 switch (ad->ad_type) {
2088 rc = SMB_VFS_NEXT_SETXATTR(ad->ad_handle,
2090 AFPINFO_EA_NETATALK,
2092 AD_DATASZ_XATTR, 0);
2096 len = SMB_VFS_NEXT_PWRITE(ad->ad_handle,
2101 if (len != AD_DATASZ_DOT_UND) {
2102 DBG_ERR("short write on %s: %zd", fsp_str_dbg(fsp), len);
2112 DBG_DEBUG("Path [%s] rc [%d]\n", fsp_str_dbg(fsp), rc);
2117 /*****************************************************************************
2119 *****************************************************************************/
2121 static bool is_afpinfo_stream(const struct smb_filename *smb_fname)
2123 if (strncasecmp_m(smb_fname->stream_name,
2124 AFPINFO_STREAM_NAME,
2125 strlen(AFPINFO_STREAM_NAME)) == 0) {
2131 static bool is_afpresource_stream(const struct smb_filename *smb_fname)
2133 if (strncasecmp_m(smb_fname->stream_name,
2134 AFPRESOURCE_STREAM_NAME,
2135 strlen(AFPRESOURCE_STREAM_NAME)) == 0) {
2142 * Test whether stream is an Apple stream, not used atm
2145 static bool is_apple_stream(const struct smb_filename *smb_fname)
2147 if (is_afpinfo_stream(smb_fname)) {
2150 if (is_afpresource_stream(smb_fname)) {
2158 * Initialize config struct from our smb.conf config parameters
2160 static int init_fruit_config(vfs_handle_struct *handle)
2162 struct fruit_config_data *config;
2164 const char *tm_size_str = NULL;
2166 config = talloc_zero(handle->conn, struct fruit_config_data);
2168 DEBUG(1, ("talloc_zero() failed\n"));
2174 * Versions up to Samba 4.5.x had a spelling bug in the
2175 * fruit:resource option calling lp_parm_enum with
2176 * "res*s*ource" (ie two s).
2178 * In Samba 4.6 we accept both the wrong and the correct
2179 * spelling, in Samba 4.7 the bad spelling will be removed.
2181 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2182 "ressource", fruit_rsrc, FRUIT_RSRC_ADFILE);
2183 if (enumval == -1) {
2184 DEBUG(1, ("value for %s: resource type unknown\n",
2185 FRUIT_PARAM_TYPE_NAME));
2188 config->rsrc = (enum fruit_rsrc)enumval;
2190 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2191 "resource", fruit_rsrc, enumval);
2192 if (enumval == -1) {
2193 DEBUG(1, ("value for %s: resource type unknown\n",
2194 FRUIT_PARAM_TYPE_NAME));
2197 config->rsrc = (enum fruit_rsrc)enumval;
2199 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2200 "metadata", fruit_meta, FRUIT_META_NETATALK);
2201 if (enumval == -1) {
2202 DEBUG(1, ("value for %s: metadata type unknown\n",
2203 FRUIT_PARAM_TYPE_NAME));
2206 config->meta = (enum fruit_meta)enumval;
2208 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2209 "locking", fruit_locking, FRUIT_LOCKING_NONE);
2210 if (enumval == -1) {
2211 DEBUG(1, ("value for %s: locking type unknown\n",
2212 FRUIT_PARAM_TYPE_NAME));
2215 config->locking = (enum fruit_locking)enumval;
2217 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2218 "encoding", fruit_encoding, FRUIT_ENC_PRIVATE);
2219 if (enumval == -1) {
2220 DEBUG(1, ("value for %s: encoding type unknown\n",
2221 FRUIT_PARAM_TYPE_NAME));
2224 config->encoding = (enum fruit_encoding)enumval;
2226 if (config->rsrc == FRUIT_RSRC_ADFILE) {
2227 config->veto_appledouble = lp_parm_bool(SNUM(handle->conn),
2228 FRUIT_PARAM_TYPE_NAME,
2233 config->use_aapl = lp_parm_bool(
2234 -1, FRUIT_PARAM_TYPE_NAME, "aapl", true);
2236 config->time_machine = lp_parm_bool(
2237 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "time machine", false);
2239 config->unix_info_enabled = lp_parm_bool(
2240 -1, FRUIT_PARAM_TYPE_NAME, "nfs_aces", true);
2242 config->use_copyfile = lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME,
2245 config->posix_rename = lp_parm_bool(
2246 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "posix_rename", true);
2248 config->aapl_zero_file_id =
2249 lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME, "zero_file_id", true);
2251 config->readdir_attr_rsize = lp_parm_bool(
2252 SNUM(handle->conn), "readdir_attr", "aapl_rsize", true);
2254 config->readdir_attr_finder_info = lp_parm_bool(
2255 SNUM(handle->conn), "readdir_attr", "aapl_finder_info", true);
2257 config->readdir_attr_max_access = lp_parm_bool(
2258 SNUM(handle->conn), "readdir_attr", "aapl_max_access", true);
2260 config->model = lp_parm_const_string(
2261 -1, FRUIT_PARAM_TYPE_NAME, "model", "MacSamba");
2263 tm_size_str = lp_parm_const_string(
2264 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2265 "time machine max size", NULL);
2266 if (tm_size_str != NULL) {
2267 config->time_machine_max_size = conv_str_size(tm_size_str);
2270 config->wipe_intentionally_left_blank_rfork = lp_parm_bool(
2271 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2272 "wipe_intentionally_left_blank_rfork", false);
2274 config->delete_empty_adfiles = lp_parm_bool(
2275 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2276 "delete_empty_adfiles", false);
2278 SMB_VFS_HANDLE_SET_DATA(handle, config,
2279 NULL, struct fruit_config_data,
2286 * Prepend "._" to a basename
2287 * Return a new struct smb_filename with stream_name == NULL.
2289 static int adouble_path(TALLOC_CTX *ctx,
2290 const struct smb_filename *smb_fname_in,
2291 struct smb_filename **pp_smb_fname_out)
2295 struct smb_filename *smb_fname = cp_smb_filename(ctx,
2298 if (smb_fname == NULL) {
2302 /* We need streamname to be NULL */
2303 TALLOC_FREE(smb_fname->stream_name);
2305 /* And we're replacing base_name. */
2306 TALLOC_FREE(smb_fname->base_name);
2308 if (!parent_dirname(smb_fname, smb_fname_in->base_name,
2310 TALLOC_FREE(smb_fname);
2314 smb_fname->base_name = talloc_asprintf(smb_fname,
2315 "%s/._%s", parent, base);
2316 if (smb_fname->base_name == NULL) {
2317 TALLOC_FREE(smb_fname);
2321 *pp_smb_fname_out = smb_fname;
2327 * Allocate and initialize an AfpInfo struct
2329 static AfpInfo *afpinfo_new(TALLOC_CTX *ctx)
2331 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2335 ai->afpi_Signature = AFP_Signature;
2336 ai->afpi_Version = AFP_Version;
2337 ai->afpi_BackupTime = AD_DATE_START;
2342 * Pack an AfpInfo struct into a buffer
2344 * Buffer size must be at least AFP_INFO_SIZE
2345 * Returns size of packed buffer
2347 static ssize_t afpinfo_pack(const AfpInfo *ai, char *buf)
2349 memset(buf, 0, AFP_INFO_SIZE);
2351 RSIVAL(buf, 0, ai->afpi_Signature);
2352 RSIVAL(buf, 4, ai->afpi_Version);
2353 RSIVAL(buf, 12, ai->afpi_BackupTime);
2354 memcpy(buf + 16, ai->afpi_FinderInfo, sizeof(ai->afpi_FinderInfo));
2356 return AFP_INFO_SIZE;
2360 * Unpack a buffer into a AfpInfo structure
2362 * Buffer size must be at least AFP_INFO_SIZE
2363 * Returns allocated AfpInfo struct
2365 static AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data)
2367 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2372 ai->afpi_Signature = RIVAL(data, 0);
2373 ai->afpi_Version = RIVAL(data, 4);
2374 ai->afpi_BackupTime = RIVAL(data, 12);
2375 memcpy(ai->afpi_FinderInfo, (const char *)data + 16,
2376 sizeof(ai->afpi_FinderInfo));
2378 if (ai->afpi_Signature != AFP_Signature
2379 || ai->afpi_Version != AFP_Version) {
2380 DEBUG(1, ("Bad AfpInfo signature or version\n"));
2388 * Fake an inode number from the md5 hash of the (xattr) name
2390 static SMB_INO_T fruit_inode(const SMB_STRUCT_STAT *sbuf, const char *sname)
2392 gnutls_hash_hd_t hash_hnd = NULL;
2393 unsigned char hash[16];
2394 SMB_INO_T result = 0;
2398 DBG_DEBUG("fruit_inode called for %ju/%ju [%s]\n",
2399 (uintmax_t)sbuf->st_ex_dev,
2400 (uintmax_t)sbuf->st_ex_ino, sname);
2402 upper_sname = talloc_strdup_upper(talloc_tos(), sname);
2403 SMB_ASSERT(upper_sname != NULL);
2405 rc = gnutls_hash_init(&hash_hnd, GNUTLS_DIG_MD5);
2410 rc = gnutls_hash(hash_hnd, &(sbuf->st_ex_dev), sizeof(sbuf->st_ex_dev));
2412 gnutls_hash_deinit(hash_hnd, NULL);
2415 rc = gnutls_hash(hash_hnd,
2417 sizeof(sbuf->st_ex_ino));
2419 gnutls_hash_deinit(hash_hnd, NULL);
2422 rc = gnutls_hash(hash_hnd,
2424 talloc_get_size(upper_sname) - 1);
2426 gnutls_hash_deinit(hash_hnd, NULL);
2430 gnutls_hash_deinit(hash_hnd, hash);
2432 /* Hopefully all the variation is in the lower 4 (or 8) bytes! */
2433 memcpy(&result, hash, sizeof(result));
2436 DBG_DEBUG("fruit_inode \"%s\": ino=%ju\n",
2437 sname, (uintmax_t)result);
2440 TALLOC_FREE(upper_sname);
2445 static bool add_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
2446 struct stream_struct **streams,
2447 const char *name, off_t size,
2450 struct stream_struct *tmp;
2452 tmp = talloc_realloc(mem_ctx, *streams, struct stream_struct,
2458 tmp[*num_streams].name = talloc_asprintf(tmp, "%s:$DATA", name);
2459 if (tmp[*num_streams].name == NULL) {
2463 tmp[*num_streams].size = size;
2464 tmp[*num_streams].alloc_size = alloc_size;
2471 static bool filter_empty_rsrc_stream(unsigned int *num_streams,
2472 struct stream_struct **streams)
2474 struct stream_struct *tmp = *streams;
2477 if (*num_streams == 0) {
2481 for (i = 0; i < *num_streams; i++) {
2482 if (strequal_m(tmp[i].name, AFPRESOURCE_STREAM)) {
2487 if (i == *num_streams) {
2491 if (tmp[i].size > 0) {
2495 TALLOC_FREE(tmp[i].name);
2496 if (*num_streams - 1 > i) {
2497 memmove(&tmp[i], &tmp[i+1],
2498 (*num_streams - i - 1) * sizeof(struct stream_struct));
2505 static bool del_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
2506 struct stream_struct **streams,
2509 struct stream_struct *tmp = *streams;
2512 if (*num_streams == 0) {
2516 for (i = 0; i < *num_streams; i++) {
2517 if (strequal_m(tmp[i].name, name)) {
2522 if (i == *num_streams) {
2526 TALLOC_FREE(tmp[i].name);
2527 if (*num_streams - 1 > i) {
2528 memmove(&tmp[i], &tmp[i+1],
2529 (*num_streams - i - 1) * sizeof(struct stream_struct));
2536 static bool ad_empty_finderinfo(const struct adouble *ad)
2539 char emptybuf[ADEDLEN_FINDERI] = {0};
2542 fi = ad_get_entry(ad, ADEID_FINDERI);
2544 DBG_ERR("Missing FinderInfo in struct adouble [%p]\n", ad);
2548 cmp = memcmp(emptybuf, fi, ADEDLEN_FINDERI);
2552 static bool ai_empty_finderinfo(const AfpInfo *ai)
2555 char emptybuf[ADEDLEN_FINDERI] = {0};
2557 cmp = memcmp(emptybuf, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
2562 * Update btime with btime from Netatalk
2564 static void update_btime(vfs_handle_struct *handle,
2565 struct smb_filename *smb_fname)
2568 struct timespec creation_time = {0};
2570 struct fruit_config_data *config = NULL;
2572 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
2575 switch (config->meta) {
2576 case FRUIT_META_STREAM:
2578 case FRUIT_META_NETATALK:
2582 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
2586 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
2590 if (ad_getdate(ad, AD_DATE_UNIX | AD_DATE_CREATE, &t) != 0) {
2596 creation_time.tv_sec = convert_uint32_t_to_time_t(t);
2597 update_stat_ex_create_time(&smb_fname->st, creation_time);
2603 * Map an access mask to a Netatalk single byte byte range lock
2605 static off_t access_to_netatalk_brl(enum apple_fork fork_type,
2606 uint32_t access_mask)
2610 switch (access_mask) {
2611 case FILE_READ_DATA:
2612 offset = AD_FILELOCK_OPEN_RD;
2615 case FILE_WRITE_DATA:
2616 case FILE_APPEND_DATA:
2617 offset = AD_FILELOCK_OPEN_WR;
2621 offset = AD_FILELOCK_OPEN_NONE;
2625 if (fork_type == APPLE_FORK_RSRC) {
2626 if (offset == AD_FILELOCK_OPEN_NONE) {
2627 offset = AD_FILELOCK_RSRC_OPEN_NONE;
2637 * Map a deny mode to a Netatalk brl
2639 static off_t denymode_to_netatalk_brl(enum apple_fork fork_type,
2644 switch (deny_mode) {
2646 offset = AD_FILELOCK_DENY_RD;
2650 offset = AD_FILELOCK_DENY_WR;
2654 smb_panic("denymode_to_netatalk_brl: bad deny mode\n");
2657 if (fork_type == APPLE_FORK_RSRC) {
2665 * Call fcntl() with an exclusive F_GETLK request in order to
2666 * determine if there's an exisiting shared lock
2668 * @return true if the requested lock was found or any error occurred
2669 * false if the lock was not found
2671 static bool test_netatalk_lock(files_struct *fsp, off_t in_offset)
2674 off_t offset = in_offset;
2679 result = SMB_VFS_GETLOCK(fsp, &offset, &len, &type, &pid);
2680 if (result == false) {
2684 if (type != F_UNLCK) {
2691 static NTSTATUS fruit_check_access(vfs_handle_struct *handle,
2693 uint32_t access_mask,
2694 uint32_t share_mode)
2696 NTSTATUS status = NT_STATUS_OK;
2698 bool share_for_read = (share_mode & FILE_SHARE_READ);
2699 bool share_for_write = (share_mode & FILE_SHARE_WRITE);
2700 bool netatalk_already_open_for_reading = false;
2701 bool netatalk_already_open_for_writing = false;
2702 bool netatalk_already_open_with_deny_read = false;
2703 bool netatalk_already_open_with_deny_write = false;
2705 /* FIXME: hardcoded data fork, add resource fork */
2706 enum apple_fork fork_type = APPLE_FORK_DATA;
2708 DBG_DEBUG("fruit_check_access: %s, am: %s/%s, sm: 0x%x\n",
2710 access_mask & FILE_READ_DATA ? "READ" :"-",
2711 access_mask & FILE_WRITE_DATA ? "WRITE" : "-",
2714 if (fsp->fh->fd == -1) {
2715 return NT_STATUS_OK;
2718 /* Read NetATalk opens and deny modes on the file. */
2719 netatalk_already_open_for_reading = test_netatalk_lock(fsp,
2720 access_to_netatalk_brl(fork_type,
2723 netatalk_already_open_with_deny_read = test_netatalk_lock(fsp,
2724 denymode_to_netatalk_brl(fork_type,
2727 netatalk_already_open_for_writing = test_netatalk_lock(fsp,
2728 access_to_netatalk_brl(fork_type,
2731 netatalk_already_open_with_deny_write = test_netatalk_lock(fsp,
2732 denymode_to_netatalk_brl(fork_type,
2735 /* If there are any conflicts - sharing violation. */
2736 if ((access_mask & FILE_READ_DATA) &&
2737 netatalk_already_open_with_deny_read) {
2738 return NT_STATUS_SHARING_VIOLATION;
2741 if (!share_for_read &&
2742 netatalk_already_open_for_reading) {
2743 return NT_STATUS_SHARING_VIOLATION;
2746 if ((access_mask & FILE_WRITE_DATA) &&
2747 netatalk_already_open_with_deny_write) {
2748 return NT_STATUS_SHARING_VIOLATION;
2751 if (!share_for_write &&
2752 netatalk_already_open_for_writing) {
2753 return NT_STATUS_SHARING_VIOLATION;
2756 if (!(access_mask & FILE_READ_DATA)) {
2758 * Nothing we can do here, we need read access
2761 return NT_STATUS_OK;
2764 /* Set NetAtalk locks matching our access */
2765 if (access_mask & FILE_READ_DATA) {
2766 struct byte_range_lock *br_lck = NULL;
2768 off = access_to_netatalk_brl(fork_type, FILE_READ_DATA);
2770 handle->conn->sconn->msg_ctx, fsp,
2771 fsp->op->global->open_persistent_id, 1, off,
2772 READ_LOCK, POSIX_LOCK, false,
2775 TALLOC_FREE(br_lck);
2777 if (!NT_STATUS_IS_OK(status)) {
2782 if (!share_for_read) {
2783 struct byte_range_lock *br_lck = NULL;
2785 off = denymode_to_netatalk_brl(fork_type, DENY_READ);
2787 handle->conn->sconn->msg_ctx, fsp,
2788 fsp->op->global->open_persistent_id, 1, off,
2789 READ_LOCK, POSIX_LOCK, false,
2792 TALLOC_FREE(br_lck);
2794 if (!NT_STATUS_IS_OK(status)) {
2799 if (access_mask & FILE_WRITE_DATA) {
2800 struct byte_range_lock *br_lck = NULL;
2802 off = access_to_netatalk_brl(fork_type, FILE_WRITE_DATA);
2804 handle->conn->sconn->msg_ctx, fsp,
2805 fsp->op->global->open_persistent_id, 1, off,
2806 READ_LOCK, POSIX_LOCK, false,
2809 TALLOC_FREE(br_lck);
2811 if (!NT_STATUS_IS_OK(status)) {
2816 if (!share_for_write) {
2817 struct byte_range_lock *br_lck = NULL;
2819 off = denymode_to_netatalk_brl(fork_type, DENY_WRITE);
2821 handle->conn->sconn->msg_ctx, fsp,
2822 fsp->op->global->open_persistent_id, 1, off,
2823 READ_LOCK, POSIX_LOCK, false,
2826 TALLOC_FREE(br_lck);
2828 if (!NT_STATUS_IS_OK(status)) {
2833 return NT_STATUS_OK;
2836 static NTSTATUS check_aapl(vfs_handle_struct *handle,
2837 struct smb_request *req,
2838 const struct smb2_create_blobs *in_context_blobs,
2839 struct smb2_create_blobs *out_context_blobs)
2841 struct fruit_config_data *config;
2843 struct smb2_create_blob *aapl = NULL;
2847 DATA_BLOB blob = data_blob_talloc(req, NULL, 0);
2848 uint64_t req_bitmap, client_caps;
2849 uint64_t server_caps = SMB2_CRTCTX_AAPL_UNIX_BASED;
2853 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
2854 return NT_STATUS_UNSUCCESSFUL);
2856 if (!config->use_aapl
2857 || in_context_blobs == NULL
2858 || out_context_blobs == NULL) {
2859 return NT_STATUS_OK;
2862 aapl = smb2_create_blob_find(in_context_blobs,
2863 SMB2_CREATE_TAG_AAPL);
2865 return NT_STATUS_OK;
2868 if (aapl->data.length != 24) {
2869 DEBUG(1, ("unexpected AAPL ctxt length: %ju\n",
2870 (uintmax_t)aapl->data.length));
2871 return NT_STATUS_INVALID_PARAMETER;
2874 cmd = IVAL(aapl->data.data, 0);
2875 if (cmd != SMB2_CRTCTX_AAPL_SERVER_QUERY) {
2876 DEBUG(1, ("unsupported AAPL cmd: %d\n", cmd));
2877 return NT_STATUS_INVALID_PARAMETER;
2880 req_bitmap = BVAL(aapl->data.data, 8);
2881 client_caps = BVAL(aapl->data.data, 16);
2883 SIVAL(p, 0, SMB2_CRTCTX_AAPL_SERVER_QUERY);
2885 SBVAL(p, 8, req_bitmap);
2886 ok = data_blob_append(req, &blob, p, 16);
2888 return NT_STATUS_UNSUCCESSFUL;
2891 if (req_bitmap & SMB2_CRTCTX_AAPL_SERVER_CAPS) {
2892 if ((client_caps & SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR) &&
2893 (handle->conn->tcon->compat->fs_capabilities & FILE_NAMED_STREAMS)) {
2894 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR;
2895 config->readdir_attr_enabled = true;
2898 if (config->use_copyfile) {
2899 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_OSX_COPYFILE;
2900 config->copyfile_enabled = true;
2904 * The client doesn't set the flag, so we can't check
2905 * for it and just set it unconditionally
2907 if (config->unix_info_enabled) {
2908 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_NFS_ACE;
2911 SBVAL(p, 0, server_caps);
2912 ok = data_blob_append(req, &blob, p, 8);
2914 return NT_STATUS_UNSUCCESSFUL;
2918 if (req_bitmap & SMB2_CRTCTX_AAPL_VOLUME_CAPS) {
2919 int val = lp_case_sensitive(SNUM(handle->conn->tcon->compat));
2927 caps |= SMB2_CRTCTX_AAPL_CASE_SENSITIVE;
2934 if (config->time_machine) {
2935 caps |= SMB2_CRTCTX_AAPL_FULL_SYNC;
2940 ok = data_blob_append(req, &blob, p, 8);
2942 return NT_STATUS_UNSUCCESSFUL;
2946 if (req_bitmap & SMB2_CRTCTX_AAPL_MODEL_INFO) {
2947 ok = convert_string_talloc(req,
2948 CH_UNIX, CH_UTF16LE,
2949 config->model, strlen(config->model),
2952 return NT_STATUS_UNSUCCESSFUL;
2956 SIVAL(p + 4, 0, modellen);
2957 ok = data_blob_append(req, &blob, p, 8);
2960 return NT_STATUS_UNSUCCESSFUL;
2963 ok = data_blob_append(req, &blob, model, modellen);
2966 return NT_STATUS_UNSUCCESSFUL;
2970 status = smb2_create_blob_add(out_context_blobs,
2972 SMB2_CREATE_TAG_AAPL,
2974 if (NT_STATUS_IS_OK(status)) {
2975 global_fruit_config.nego_aapl = true;
2976 if (config->aapl_zero_file_id) {
2977 aapl_force_zero_file_id(handle->conn->sconn);
2984 static bool readdir_attr_meta_finderi_stream(
2985 struct vfs_handle_struct *handle,
2986 const struct smb_filename *smb_fname,
2989 struct smb_filename *stream_name = NULL;
2990 files_struct *fsp = NULL;
2995 uint8_t buf[AFP_INFO_SIZE];
2997 stream_name = synthetic_smb_fname(talloc_tos(),
2998 smb_fname->base_name,
2999 AFPINFO_STREAM_NAME,
3000 NULL, smb_fname->flags);
3001 if (stream_name == NULL) {
3005 ret = SMB_VFS_STAT(handle->conn, stream_name);
3010 status = SMB_VFS_CREATE_FILE(
3011 handle->conn, /* conn */
3013 0, /* root_dir_fid */
3014 stream_name, /* fname */
3015 FILE_READ_DATA, /* access_mask */
3016 (FILE_SHARE_READ | FILE_SHARE_WRITE | /* share_access */
3018 FILE_OPEN, /* create_disposition*/
3019 0, /* create_options */
3020 0, /* file_attributes */
3021 INTERNAL_OPEN_ONLY, /* oplock_request */
3023 0, /* allocation_size */
3024 0, /* private_flags */
3029 NULL, NULL); /* create context */
3031 TALLOC_FREE(stream_name);
3033 if (!NT_STATUS_IS_OK(status)) {
3037 nread = SMB_VFS_PREAD(fsp, &buf[0], AFP_INFO_SIZE, 0);
3038 if (nread != AFP_INFO_SIZE) {
3039 DBG_ERR("short read [%s] [%zd/%d]\n",
3040 smb_fname_str_dbg(stream_name), nread, AFP_INFO_SIZE);
3045 memcpy(&ai->afpi_FinderInfo[0], &buf[AFP_OFF_FinderInfo],
3052 close_file(NULL, fsp, NORMAL_CLOSE);
3058 static bool readdir_attr_meta_finderi_netatalk(
3059 struct vfs_handle_struct *handle,
3060 const struct smb_filename *smb_fname,
3063 struct adouble *ad = NULL;
3066 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
3071 p = ad_get_entry(ad, ADEID_FINDERI);
3073 DBG_ERR("No ADEID_FINDERI for [%s]\n", smb_fname->base_name);
3078 memcpy(&ai->afpi_FinderInfo[0], p, AFP_FinderSize);
3083 static bool readdir_attr_meta_finderi(struct vfs_handle_struct *handle,
3084 const struct smb_filename *smb_fname,
3085 struct readdir_attr_data *attr_data)
3087 struct fruit_config_data *config = NULL;
3088 uint32_t date_added;
3092 SMB_VFS_HANDLE_GET_DATA(handle, config,
3093 struct fruit_config_data,
3096 switch (config->meta) {
3097 case FRUIT_META_NETATALK:
3098 ok = readdir_attr_meta_finderi_netatalk(
3099 handle, smb_fname, &ai);
3102 case FRUIT_META_STREAM:
3103 ok = readdir_attr_meta_finderi_stream(
3104 handle, smb_fname, &ai);
3108 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3113 /* Don't bother with errors, it's likely ENOENT */
3117 if (S_ISREG(smb_fname->st.st_ex_mode)) {
3119 memcpy(&attr_data->attr_data.aapl.finder_info[0],
3120 &ai.afpi_FinderInfo[0], 4);
3122 /* finder_creator */
3123 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 4,
3124 &ai.afpi_FinderInfo[4], 4);
3128 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 8,
3129 &ai.afpi_FinderInfo[8], 2);
3131 /* finder_ext_flags */
3132 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 10,
3133 &ai.afpi_FinderInfo[24], 2);
3136 date_added = convert_time_t_to_uint32_t(
3137 smb_fname->st.st_ex_btime.tv_sec - AD_DATE_DELTA);
3139 RSIVAL(&attr_data->attr_data.aapl.finder_info[0], 12, date_added);
3144 static uint64_t readdir_attr_rfork_size_adouble(
3145 struct vfs_handle_struct *handle,
3146 const struct smb_filename *smb_fname)
3148 struct adouble *ad = NULL;
3149 uint64_t rfork_size;
3151 ad = ad_get(talloc_tos(), handle, smb_fname,
3157 rfork_size = ad_getentrylen(ad, ADEID_RFORK);
3163 static uint64_t readdir_attr_rfork_size_stream(
3164 struct vfs_handle_struct *handle,
3165 const struct smb_filename *smb_fname)
3167 struct smb_filename *stream_name = NULL;
3169 uint64_t rfork_size;
3171 stream_name = synthetic_smb_fname(talloc_tos(),
3172 smb_fname->base_name,
3173 AFPRESOURCE_STREAM_NAME,
3175 if (stream_name == NULL) {
3179 ret = SMB_VFS_STAT(handle->conn, stream_name);
3181 TALLOC_FREE(stream_name);
3185 rfork_size = stream_name->st.st_ex_size;
3186 TALLOC_FREE(stream_name);
3191 static uint64_t readdir_attr_rfork_size(struct vfs_handle_struct *handle,
3192 const struct smb_filename *smb_fname)
3194 struct fruit_config_data *config = NULL;
3195 uint64_t rfork_size;
3197 SMB_VFS_HANDLE_GET_DATA(handle, config,
3198 struct fruit_config_data,
3201 switch (config->rsrc) {
3202 case FRUIT_RSRC_ADFILE:
3203 case FRUIT_RSRC_XATTR:
3204 rfork_size = readdir_attr_rfork_size_adouble(handle,
3208 case FRUIT_META_STREAM:
3209 rfork_size = readdir_attr_rfork_size_stream(handle,
3214 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3222 static NTSTATUS readdir_attr_macmeta(struct vfs_handle_struct *handle,
3223 const struct smb_filename *smb_fname,
3224 struct readdir_attr_data *attr_data)
3226 NTSTATUS status = NT_STATUS_OK;
3227 struct fruit_config_data *config = NULL;
3230 SMB_VFS_HANDLE_GET_DATA(handle, config,
3231 struct fruit_config_data,
3232 return NT_STATUS_UNSUCCESSFUL);
3235 /* Ensure we return a default value in the creation_date field */
3236 RSIVAL(&attr_data->attr_data.aapl.finder_info, 12, AD_DATE_START);
3239 * Resource fork length
3242 if (config->readdir_attr_rsize) {
3243 uint64_t rfork_size;
3245 rfork_size = readdir_attr_rfork_size(handle, smb_fname);
3246 attr_data->attr_data.aapl.rfork_size = rfork_size;
3253 if (config->readdir_attr_finder_info) {
3254 ok = readdir_attr_meta_finderi(handle, smb_fname, attr_data);
3256 status = NT_STATUS_INTERNAL_ERROR;
3263 static NTSTATUS remove_virtual_nfs_aces(struct security_descriptor *psd)
3268 if (psd->dacl == NULL) {
3269 return NT_STATUS_OK;
3272 for (i = 0; i < psd->dacl->num_aces; i++) {
3273 /* MS NFS style mode/uid/gid */
3274 int cmp = dom_sid_compare_domain(
3275 &global_sid_Unix_NFS,
3276 &psd->dacl->aces[i].trustee);
3278 /* Normal ACE entry. */
3283 * security_descriptor_dacl_del()
3284 * *must* return NT_STATUS_OK as we know
3285 * we have something to remove.
3288 status = security_descriptor_dacl_del(psd,
3289 &psd->dacl->aces[i].trustee);
3290 if (!NT_STATUS_IS_OK(status)) {
3291 DBG_WARNING("failed to remove MS NFS style ACE: %s\n",
3297 * security_descriptor_dacl_del() may delete more
3298 * then one entry subsequent to this one if the
3299 * SID matches, but we only need to ensure that
3300 * we stay looking at the same element in the array.
3304 return NT_STATUS_OK;
3307 /* Search MS NFS style ACE with UNIX mode */
3308 static NTSTATUS check_ms_nfs(vfs_handle_struct *handle,
3310 struct security_descriptor *psd,
3315 struct fruit_config_data *config = NULL;
3319 SMB_VFS_HANDLE_GET_DATA(handle, config,
3320 struct fruit_config_data,
3321 return NT_STATUS_UNSUCCESSFUL);
3323 if (!global_fruit_config.nego_aapl) {
3324 return NT_STATUS_OK;
3326 if (psd->dacl == NULL || !config->unix_info_enabled) {
3327 return NT_STATUS_OK;
3330 for (i = 0; i < psd->dacl->num_aces; i++) {
3331 if (dom_sid_compare_domain(
3332 &global_sid_Unix_NFS_Mode,
3333 &psd->dacl->aces[i].trustee) == 0) {
3334 *pmode = (mode_t)psd->dacl->aces[i].trustee.sub_auths[2];
3335 *pmode &= (S_IRWXU | S_IRWXG | S_IRWXO);
3338 DEBUG(10, ("MS NFS chmod request %s, %04o\n",
3339 fsp_str_dbg(fsp), (unsigned)(*pmode)));
3345 * Remove any incoming virtual ACE entries generated by
3346 * fruit_fget_nt_acl().
3349 return remove_virtual_nfs_aces(psd);
3352 /****************************************************************************
3354 ****************************************************************************/
3356 static int fruit_connect(vfs_handle_struct *handle,
3357 const char *service,
3361 char *list = NULL, *newlist = NULL;
3362 struct fruit_config_data *config;
3364 DEBUG(10, ("fruit_connect\n"));
3366 rc = SMB_VFS_NEXT_CONNECT(handle, service, user);
3371 rc = init_fruit_config(handle);
3376 SMB_VFS_HANDLE_GET_DATA(handle, config,
3377 struct fruit_config_data, return -1);
3379 if (config->veto_appledouble) {
3380 list = lp_veto_files(talloc_tos(), SNUM(handle->conn));
3383 if (strstr(list, "/" ADOUBLE_NAME_PREFIX "*/") == NULL) {
3384 newlist = talloc_asprintf(
3386 "%s/" ADOUBLE_NAME_PREFIX "*/",
3388 lp_do_parameter(SNUM(handle->conn),
3393 lp_do_parameter(SNUM(handle->conn),
3395 "/" ADOUBLE_NAME_PREFIX "*/");
3401 if (config->encoding == FRUIT_ENC_NATIVE) {
3402 lp_do_parameter(SNUM(handle->conn),
3407 if (config->time_machine) {
3408 DBG_NOTICE("Enabling durable handles for Time Machine "
3409 "support on [%s]\n", service);
3410 lp_do_parameter(SNUM(handle->conn), "durable handles", "yes");
3411 lp_do_parameter(SNUM(handle->conn), "kernel oplocks", "no");
3412 lp_do_parameter(SNUM(handle->conn), "kernel share modes", "no");
3413 if (!lp_strict_sync(SNUM(handle->conn))) {
3414 DBG_WARNING("Time Machine without strict sync is not "
3417 lp_do_parameter(SNUM(handle->conn), "posix locking", "no");
3423 static int fruit_fake_fd(void)
3430 * Return a valid fd, but ensure any attempt to use it returns
3431 * an error (EPIPE). Once we get a write on the handle, we open
3434 ret = pipe(pipe_fds);
3444 static int fruit_open_meta_stream(vfs_handle_struct *handle,
3445 struct smb_filename *smb_fname,
3450 struct fruit_config_data *config = NULL;
3451 struct fio *fio = NULL;
3452 int open_flags = flags & ~O_CREAT;
3455 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3457 SMB_VFS_HANDLE_GET_DATA(handle, config,
3458 struct fruit_config_data, return -1);
3460 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3461 fio->type = ADOUBLE_META;
3462 fio->config = config;
3464 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, open_flags, mode);
3469 if (!(flags & O_CREAT)) {
3470 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
3474 fd = fruit_fake_fd();
3476 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
3480 fio->fake_fd = true;
3487 static int fruit_open_meta_netatalk(vfs_handle_struct *handle,
3488 struct smb_filename *smb_fname,
3493 struct fruit_config_data *config = NULL;
3494 struct fio *fio = NULL;
3495 struct adouble *ad = NULL;
3496 bool meta_exists = false;
3499 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3501 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
3508 if (!meta_exists && !(flags & O_CREAT)) {
3513 fd = fruit_fake_fd();
3518 SMB_VFS_HANDLE_GET_DATA(handle, config,
3519 struct fruit_config_data, return -1);
3521 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3522 fio->type = ADOUBLE_META;
3523 fio->config = config;
3524 fio->fake_fd = true;
3531 static int fruit_open_meta(vfs_handle_struct *handle,
3532 struct smb_filename *smb_fname,
3533 files_struct *fsp, int flags, mode_t mode)
3536 struct fruit_config_data *config = NULL;
3538 DBG_DEBUG("path [%s]\n", smb_fname_str_dbg(smb_fname));
3540 SMB_VFS_HANDLE_GET_DATA(handle, config,
3541 struct fruit_config_data, return -1);
3543 switch (config->meta) {
3544 case FRUIT_META_STREAM:
3545 fd = fruit_open_meta_stream(handle, smb_fname,
3549 case FRUIT_META_NETATALK:
3550 fd = fruit_open_meta_netatalk(handle, smb_fname,
3555 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3559 DBG_DEBUG("path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3564 static int fruit_open_rsrc_adouble(vfs_handle_struct *handle,
3565 struct smb_filename *smb_fname,
3571 struct adouble *ad = NULL;
3572 struct smb_filename *smb_fname_base = NULL;
3573 struct fruit_config_data *config = NULL;
3576 SMB_VFS_HANDLE_GET_DATA(handle, config,
3577 struct fruit_config_data, return -1);
3579 if ((!(flags & O_CREAT)) &&
3580 S_ISDIR(fsp->base_fsp->fsp_name->st.st_ex_mode))
3582 /* sorry, but directories don't habe a resource fork */
3587 rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_base);
3592 /* We always need read/write access for the metadata header too */
3593 flags &= ~(O_RDONLY | O_WRONLY);
3596 hostfd = SMB_VFS_NEXT_OPEN(handle, smb_fname_base, fsp,
3603 if (flags & (O_CREAT | O_TRUNC)) {
3604 ad = ad_init(fsp, handle, ADOUBLE_RSRC);
3610 fsp->fh->fd = hostfd;
3612 rc = ad_fset(ad, fsp);
3623 TALLOC_FREE(smb_fname_base);
3625 DEBUG(10, ("fruit_open resource fork: rc=%d, fd=%d\n", rc, hostfd));
3627 int saved_errno = errno;
3630 * BUGBUGBUG -- we would need to call
3631 * fd_close_posix here, but we don't have a
3634 fsp->fh->fd = hostfd;
3638 errno = saved_errno;
3643 static int fruit_open_rsrc_xattr(vfs_handle_struct *handle,
3644 struct smb_filename *smb_fname,
3649 #ifdef HAVE_ATTROPEN
3652 fd = attropen(smb_fname->base_name,
3653 AFPRESOURCE_EA_NETATALK,
3668 static int fruit_open_rsrc(vfs_handle_struct *handle,
3669 struct smb_filename *smb_fname,
3670 files_struct *fsp, int flags, mode_t mode)
3673 struct fruit_config_data *config = NULL;
3674 struct fio *fio = NULL;
3676 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3678 SMB_VFS_HANDLE_GET_DATA(handle, config,
3679 struct fruit_config_data, return -1);
3681 switch (config->rsrc) {
3682 case FRUIT_RSRC_STREAM:
3683 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3686 case FRUIT_RSRC_ADFILE:
3687 fd = fruit_open_rsrc_adouble(handle, smb_fname,
3691 case FRUIT_RSRC_XATTR:
3692 fd = fruit_open_rsrc_xattr(handle, smb_fname,
3697 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3701 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3707 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3708 fio->type = ADOUBLE_RSRC;
3709 fio->config = config;
3714 static int fruit_open(vfs_handle_struct *handle,
3715 struct smb_filename *smb_fname,
3716 files_struct *fsp, int flags, mode_t mode)
3720 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3722 if (!is_ntfs_stream_smb_fname(smb_fname)) {
3723 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3726 if (is_afpinfo_stream(smb_fname)) {
3727 fd = fruit_open_meta(handle, smb_fname, fsp, flags, mode);
3728 } else if (is_afpresource_stream(smb_fname)) {
3729 fd = fruit_open_rsrc(handle, smb_fname, fsp, flags, mode);
3731 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3734 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3739 static int fruit_close_meta(vfs_handle_struct *handle,
3743 struct fruit_config_data *config = NULL;
3745 SMB_VFS_HANDLE_GET_DATA(handle, config,
3746 struct fruit_config_data, return -1);
3748 switch (config->meta) {
3749 case FRUIT_META_STREAM:
3750 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3753 case FRUIT_META_NETATALK:
3754 ret = close(fsp->fh->fd);
3759 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3767 static int fruit_close_rsrc(vfs_handle_struct *handle,
3771 struct fruit_config_data *config = NULL;
3773 SMB_VFS_HANDLE_GET_DATA(handle, config,
3774 struct fruit_config_data, return -1);
3776 switch (config->rsrc) {
3777 case FRUIT_RSRC_STREAM:
3778 case FRUIT_RSRC_ADFILE:
3779 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3782 case FRUIT_RSRC_XATTR:
3783 ret = close(fsp->fh->fd);
3788 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3795 static int fruit_close(vfs_handle_struct *handle,
3803 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(fsp->fsp_name), fd);
3805 if (!is_ntfs_stream_smb_fname(fsp->fsp_name)) {
3806 return SMB_VFS_NEXT_CLOSE(handle, fsp);
3809 if (is_afpinfo_stream(fsp->fsp_name)) {
3810 ret = fruit_close_meta(handle, fsp);
3811 } else if (is_afpresource_stream(fsp->fsp_name)) {
3812 ret = fruit_close_rsrc(handle, fsp);
3814 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3820 static int fruit_rename(struct vfs_handle_struct *handle,
3821 const struct smb_filename *smb_fname_src,
3822 const struct smb_filename *smb_fname_dst)
3825 struct fruit_config_data *config = NULL;
3826 struct smb_filename *src_adp_smb_fname = NULL;
3827 struct smb_filename *dst_adp_smb_fname = NULL;
3829 SMB_VFS_HANDLE_GET_DATA(handle, config,
3830 struct fruit_config_data, return -1);
3832 if (!VALID_STAT(smb_fname_src->st)) {
3833 DBG_ERR("Need valid stat for [%s]\n",
3834 smb_fname_str_dbg(smb_fname_src));
3838 rc = SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
3843 if ((config->rsrc != FRUIT_RSRC_ADFILE) ||
3844 (!S_ISREG(smb_fname_src->st.st_ex_mode)))
3849 rc = adouble_path(talloc_tos(), smb_fname_src, &src_adp_smb_fname);
3854 rc = adouble_path(talloc_tos(), smb_fname_dst, &dst_adp_smb_fname);
3859 DBG_DEBUG("%s -> %s\n",
3860 smb_fname_str_dbg(src_adp_smb_fname),
3861 smb_fname_str_dbg(dst_adp_smb_fname));
3863 rc = SMB_VFS_NEXT_RENAME(handle, src_adp_smb_fname, dst_adp_smb_fname);
3864 if (errno == ENOENT) {
3869 TALLOC_FREE(src_adp_smb_fname);
3870 TALLOC_FREE(dst_adp_smb_fname);
3874 static int fruit_unlink_meta_stream(vfs_handle_struct *handle,
3875 const struct smb_filename *smb_fname)
3877 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3880 static int fruit_unlink_meta_netatalk(vfs_handle_struct *handle,
3881 const struct smb_filename *smb_fname)
3883 return SMB_VFS_REMOVEXATTR(handle->conn,
3885 AFPINFO_EA_NETATALK);
3888 static int fruit_unlink_meta(vfs_handle_struct *handle,
3889 const struct smb_filename *smb_fname)
3891 struct fruit_config_data *config = NULL;
3894 SMB_VFS_HANDLE_GET_DATA(handle, config,
3895 struct fruit_config_data, return -1);
3897 switch (config->meta) {
3898 case FRUIT_META_STREAM:
3899 rc = fruit_unlink_meta_stream(handle, smb_fname);
3902 case FRUIT_META_NETATALK:
3903 rc = fruit_unlink_meta_netatalk(handle, smb_fname);
3907 DBG_ERR("Unsupported meta config [%d]\n", config->meta);
3914 static int fruit_unlink_rsrc_stream(vfs_handle_struct *handle,
3915 const struct smb_filename *smb_fname,
3920 if (!force_unlink) {
3921 struct smb_filename *smb_fname_cp = NULL;
3924 smb_fname_cp = cp_smb_filename(talloc_tos(), smb_fname);
3925 if (smb_fname_cp == NULL) {
3930 * 0 byte resource fork streams are not listed by
3931 * vfs_streaminfo, as a result stream cleanup/deletion of file
3932 * deletion doesn't remove the resourcefork stream.
3935 ret = SMB_VFS_NEXT_STAT(handle, smb_fname_cp);
3937 TALLOC_FREE(smb_fname_cp);
3938 DBG_ERR("stat [%s] failed [%s]\n",
3939 smb_fname_str_dbg(smb_fname_cp), strerror(errno));
3943 size = smb_fname_cp->st.st_ex_size;
3944 TALLOC_FREE(smb_fname_cp);
3947 /* OS X ignores resource fork stream delete requests */
3952 ret = SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3953 if ((ret != 0) && (errno == ENOENT) && force_unlink) {
3960 static int fruit_unlink_rsrc_adouble(vfs_handle_struct *handle,
3961 const struct smb_filename *smb_fname,
3965 struct adouble *ad = NULL;
3966 struct smb_filename *adp_smb_fname = NULL;
3968 if (!force_unlink) {
3969 ad = ad_get(talloc_tos(), handle, smb_fname,
3978 * 0 byte resource fork streams are not listed by
3979 * vfs_streaminfo, as a result stream cleanup/deletion of file
3980 * deletion doesn't remove the resourcefork stream.
3983 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
3984 /* OS X ignores resource fork stream delete requests */
3992 rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
3997 rc = SMB_VFS_NEXT_UNLINK(handle, adp_smb_fname);
3998 TALLOC_FREE(adp_smb_fname);
3999 if ((rc != 0) && (errno == ENOENT) && force_unlink) {
4006 static int fruit_unlink_rsrc_xattr(vfs_handle_struct *handle,
4007 const struct smb_filename *smb_fname,
4011 * OS X ignores resource fork stream delete requests, so nothing to do
4012 * here. Removing the file will remove the xattr anyway, so we don't
4013 * have to take care of removing 0 byte resource forks that could be
4019 static int fruit_unlink_rsrc(vfs_handle_struct *handle,
4020 const struct smb_filename *smb_fname,
4023 struct fruit_config_data *config = NULL;
4026 SMB_VFS_HANDLE_GET_DATA(handle, config,
4027 struct fruit_config_data, return -1);
4029 switch (config->rsrc) {
4030 case FRUIT_RSRC_STREAM:
4031 rc = fruit_unlink_rsrc_stream(handle, smb_fname, force_unlink);
4034 case FRUIT_RSRC_ADFILE:
4035 rc = fruit_unlink_rsrc_adouble(handle, smb_fname, force_unlink);
4038 case FRUIT_RSRC_XATTR:
4039 rc = fruit_unlink_rsrc_xattr(handle, smb_fname, force_unlink);
4043 DBG_ERR("Unsupported rsrc config [%d]\n", config->rsrc);
4050 static int fruit_unlink(vfs_handle_struct *handle,
4051 const struct smb_filename *smb_fname)
4054 struct fruit_config_data *config = NULL;
4055 struct smb_filename *rsrc_smb_fname = NULL;
4057 SMB_VFS_HANDLE_GET_DATA(handle, config,
4058 struct fruit_config_data, return -1);
4060 if (is_afpinfo_stream(smb_fname)) {
4061 return fruit_unlink_meta(handle, smb_fname);
4062 } else if (is_afpresource_stream(smb_fname)) {
4063 return fruit_unlink_rsrc(handle, smb_fname, false);
4064 } if (is_ntfs_stream_smb_fname(smb_fname)) {
4065 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4069 * A request to delete the base file. Because 0 byte resource
4070 * fork streams are not listed by fruit_streaminfo,
4071 * delete_all_streams() can't remove 0 byte resource fork
4072 * streams, so we have to cleanup this here.
4074 rsrc_smb_fname = synthetic_smb_fname(talloc_tos(),
4075 smb_fname->base_name,
4076 AFPRESOURCE_STREAM_NAME,
4079 if (rsrc_smb_fname == NULL) {
4083 rc = fruit_unlink_rsrc(handle, rsrc_smb_fname, true);
4084 if ((rc != 0) && (errno != ENOENT)) {
4085 DBG_ERR("Forced unlink of [%s] failed [%s]\n",
4086 smb_fname_str_dbg(rsrc_smb_fname), strerror(errno));
4087 TALLOC_FREE(rsrc_smb_fname);
4090 TALLOC_FREE(rsrc_smb_fname);
4092 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4095 static int fruit_chmod(vfs_handle_struct *handle,
4096 const struct smb_filename *smb_fname,
4100 struct fruit_config_data *config = NULL;
4101 struct smb_filename *smb_fname_adp = NULL;
4103 rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname, mode);
4108 SMB_VFS_HANDLE_GET_DATA(handle, config,
4109 struct fruit_config_data, return -1);
4111 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4115 if (!VALID_STAT(smb_fname->st)) {
4119 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
4123 rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_adp);
4128 DEBUG(10, ("fruit_chmod: %s\n", smb_fname_adp->base_name));
4130 rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname_adp, mode);
4131 if (errno == ENOENT) {
4135 TALLOC_FREE(smb_fname_adp);
4139 static int fruit_chown(vfs_handle_struct *handle,
4140 const struct smb_filename *smb_fname,
4145 struct fruit_config_data *config = NULL;
4146 struct smb_filename *adp_smb_fname = NULL;
4148 rc = SMB_VFS_NEXT_CHOWN(handle, smb_fname, uid, gid);
4153 SMB_VFS_HANDLE_GET_DATA(handle, config,
4154 struct fruit_config_data, return -1);
4156 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4160 if (!VALID_STAT(smb_fname->st)) {
4164 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
4168 rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
4173 DEBUG(10, ("fruit_chown: %s\n", adp_smb_fname->base_name));
4175 rc = SMB_VFS_NEXT_CHOWN(handle, adp_smb_fname, uid, gid);
4176 if (errno == ENOENT) {
4181 TALLOC_FREE(adp_smb_fname);
4185 static int fruit_rmdir(struct vfs_handle_struct *handle,
4186 const struct smb_filename *smb_fname)
4190 struct fruit_config_data *config;
4192 SMB_VFS_HANDLE_GET_DATA(handle, config,
4193 struct fruit_config_data, return -1);
4195 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4200 * Due to there is no way to change bDeleteVetoFiles variable
4201 * from this module, need to clean up ourselves
4204 dh = SMB_VFS_OPENDIR(handle->conn, smb_fname, NULL, 0);
4209 while ((de = SMB_VFS_READDIR(handle->conn, dh, NULL)) != NULL) {
4211 struct adouble *ad = NULL;
4213 struct smb_filename *ad_smb_fname = NULL;
4216 match = strncmp(de->d_name,
4217 ADOUBLE_NAME_PREFIX,
4218 strlen(ADOUBLE_NAME_PREFIX));
4223 p = talloc_asprintf(talloc_tos(), "%s/%s",
4224 smb_fname->base_name, de->d_name);
4226 DBG_ERR("talloc_asprintf failed\n");
4230 ad_smb_fname = synthetic_smb_fname(talloc_tos(), p,
4234 if (ad_smb_fname == NULL) {
4235 DBG_ERR("synthetic_smb_fname failed\n");
4240 * Check whether it's a valid AppleDouble file, if
4241 * yes, delete it, ignore it otherwise.
4243 ad = ad_get(talloc_tos(), handle, ad_smb_fname, ADOUBLE_RSRC);
4245 TALLOC_FREE(ad_smb_fname);
4251 ret = SMB_VFS_NEXT_UNLINK(handle, ad_smb_fname);
4253 DBG_ERR("Deleting [%s] failed\n",
4254 smb_fname_str_dbg(ad_smb_fname));
4256 TALLOC_FREE(ad_smb_fname);
4261 SMB_VFS_CLOSEDIR(handle->conn, dh);
4263 return SMB_VFS_NEXT_RMDIR(handle, smb_fname);
4266 static ssize_t fruit_pread_meta_stream(vfs_handle_struct *handle,
4267 files_struct *fsp, void *data,
4268 size_t n, off_t offset)
4273 nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4274 if (nread == -1 || nread == n) {
4278 DBG_ERR("Removing [%s] after short read [%zd]\n",
4279 fsp_str_dbg(fsp), nread);
4281 ret = SMB_VFS_NEXT_UNLINK(handle, fsp->fsp_name);
4283 DBG_ERR("Removing [%s] failed\n", fsp_str_dbg(fsp));
4291 static ssize_t fruit_pread_meta_adouble(vfs_handle_struct *handle,
4292 files_struct *fsp, void *data,
4293 size_t n, off_t offset)
4296 struct adouble *ad = NULL;
4297 char afpinfo_buf[AFP_INFO_SIZE];
4301 ai = afpinfo_new(talloc_tos());
4306 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
4312 p = ad_get_entry(ad, ADEID_FINDERI);
4314 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
4319 memcpy(&ai->afpi_FinderInfo[0], p, ADEDLEN_FINDERI);
4321 nread = afpinfo_pack(ai, afpinfo_buf);
4322 if (nread != AFP_INFO_SIZE) {
4327 memcpy(data, afpinfo_buf, n);
4335 static ssize_t fruit_pread_meta(vfs_handle_struct *handle,
4336 files_struct *fsp, void *data,
4337 size_t n, off_t offset)
4339 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4344 * OS X has a off-by-1 error in the offset calculation, so we're
4345 * bug compatible here. It won't hurt, as any relevant real
4346 * world read requests from the AFP_AfpInfo stream will be
4347 * offset=0 n=60. offset is ignored anyway, see below.
4349 if ((offset < 0) || (offset >= AFP_INFO_SIZE + 1)) {
4354 DBG_ERR("Failed to fetch fsp extension");
4358 /* Yes, macOS always reads from offset 0 */
4360 to_return = MIN(n, AFP_INFO_SIZE);
4362 switch (fio->config->meta) {
4363 case FRUIT_META_STREAM:
4364 nread = fruit_pread_meta_stream(handle, fsp, data,
4368 case FRUIT_META_NETATALK:
4369 nread = fruit_pread_meta_adouble(handle, fsp, data,
4374 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
4378 if (nread == -1 && fio->created) {
4380 char afpinfo_buf[AFP_INFO_SIZE];
4382 ai = afpinfo_new(talloc_tos());
4387 nread = afpinfo_pack(ai, afpinfo_buf);
4389 if (nread != AFP_INFO_SIZE) {
4393 memcpy(data, afpinfo_buf, to_return);
4400 static ssize_t fruit_pread_rsrc_stream(vfs_handle_struct *handle,
4401 files_struct *fsp, void *data,
4402 size_t n, off_t offset)
4404 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4407 static ssize_t fruit_pread_rsrc_xattr(vfs_handle_struct *handle,
4408 files_struct *fsp, void *data,
4409 size_t n, off_t offset)
4411 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4414 static ssize_t fruit_pread_rsrc_adouble(vfs_handle_struct *handle,
4415 files_struct *fsp, void *data,
4416 size_t n, off_t offset)
4418 struct adouble *ad = NULL;
4421 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
4426 nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n,
4427 offset + ad_getentryoff(ad, ADEID_RFORK));
4433 static ssize_t fruit_pread_rsrc(vfs_handle_struct *handle,
4434 files_struct *fsp, void *data,
4435 size_t n, off_t offset)
4437 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4445 switch (fio->config->rsrc) {
4446 case FRUIT_RSRC_STREAM:
4447 nread = fruit_pread_rsrc_stream(handle, fsp, data, n, offset);
4450 case FRUIT_RSRC_ADFILE:
4451 nread = fruit_pread_rsrc_adouble(handle, fsp, data, n, offset);
4454 case FRUIT_RSRC_XATTR:
4455 nread = fruit_pread_rsrc_xattr(handle, fsp, data, n, offset);
4459 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
4466 static ssize_t fruit_pread(vfs_handle_struct *handle,
4467 files_struct *fsp, void *data,
4468 size_t n, off_t offset)
4470 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4473 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4474 fsp_str_dbg(fsp), (intmax_t)offset, n);
4477 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4480 if (fio->type == ADOUBLE_META) {
4481 nread = fruit_pread_meta(handle, fsp, data, n, offset);
4483 nread = fruit_pread_rsrc(handle, fsp, data, n, offset);
4486 DBG_DEBUG("Path [%s] nread [%zd]\n", fsp_str_dbg(fsp), nread);
4490 static bool fruit_must_handle_aio_stream(struct fio *fio)
4496 if (fio->type == ADOUBLE_META) {
4500 if ((fio->type == ADOUBLE_RSRC) &&
4501 (fio->config->rsrc == FRUIT_RSRC_ADFILE))
4509 struct fruit_pread_state {
4511 struct vfs_aio_state vfs_aio_state;
4514 static void fruit_pread_done(struct tevent_req *subreq);
4516 static struct tevent_req *fruit_pread_send(
4517 struct vfs_handle_struct *handle,
4518 TALLOC_CTX *mem_ctx,
4519 struct tevent_context *ev,
4520 struct files_struct *fsp,
4522 size_t n, off_t offset)
4524 struct tevent_req *req = NULL;
4525 struct tevent_req *subreq = NULL;
4526 struct fruit_pread_state *state = NULL;
4527 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4529 req = tevent_req_create(mem_ctx, &state,
4530 struct fruit_pread_state);
4535 if (fruit_must_handle_aio_stream(fio)) {
4536 state->nread = SMB_VFS_PREAD(fsp, data, n, offset);
4537 if (state->nread != n) {
4538 if (state->nread != -1) {
4541 tevent_req_error(req, errno);
4542 return tevent_req_post(req, ev);
4544 tevent_req_done(req);
4545 return tevent_req_post(req, ev);
4548 subreq = SMB_VFS_NEXT_PREAD_SEND(state, ev, handle, fsp,
4550 if (tevent_req_nomem(req, subreq)) {
4551 return tevent_req_post(req, ev);
4553 tevent_req_set_callback(subreq, fruit_pread_done, req);
4557 static void fruit_pread_done(struct tevent_req *subreq)
4559 struct tevent_req *req = tevent_req_callback_data(
4560 subreq, struct tevent_req);
4561 struct fruit_pread_state *state = tevent_req_data(
4562 req, struct fruit_pread_state);
4564 state->nread = SMB_VFS_PREAD_RECV(subreq, &state->vfs_aio_state);
4565 TALLOC_FREE(subreq);
4567 if (tevent_req_error(req, state->vfs_aio_state.error)) {
4570 tevent_req_done(req);
4573 static ssize_t fruit_pread_recv(struct tevent_req *req,
4574 struct vfs_aio_state *vfs_aio_state)
4576 struct fruit_pread_state *state = tevent_req_data(
4577 req, struct fruit_pread_state);
4579 if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
4583 *vfs_aio_state = state->vfs_aio_state;
4584 return state->nread;
4587 static ssize_t fruit_pwrite_meta_stream(vfs_handle_struct *handle,
4588 files_struct *fsp, const void *data,
4589 size_t n, off_t offset)
4591 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4597 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4598 fsp_str_dbg(fsp), (intmax_t)offset, n);
4607 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
4609 DBG_ERR("Close [%s] failed: %s\n",
4610 fsp_str_dbg(fsp), strerror(errno));
4615 fd = SMB_VFS_NEXT_OPEN(handle,
4621 DBG_ERR("On-demand create [%s] in write failed: %s\n",
4622 fsp_str_dbg(fsp), strerror(errno));
4626 fio->fake_fd = false;
4629 ai = afpinfo_unpack(talloc_tos(), data);
4634 if (ai_empty_finderinfo(ai)) {
4636 * Writing an all 0 blob to the metadata stream results in the
4637 * stream being removed on a macOS server. This ensures we
4638 * behave the same and it verified by the "delete AFP_AfpInfo by
4639 * writing all 0" test.
4641 ret = SMB_VFS_NEXT_FTRUNCATE(handle, fsp, 0);
4643 DBG_ERR("SMB_VFS_NEXT_FTRUNCATE on [%s] failed\n",
4648 ok = set_delete_on_close(
4651 handle->conn->session_info->security_token,
4652 handle->conn->session_info->unix_token);
4654 DBG_ERR("set_delete_on_close on [%s] failed\n",
4661 nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4662 if (nwritten != n) {
4669 static ssize_t fruit_pwrite_meta_netatalk(vfs_handle_struct *handle,
4670 files_struct *fsp, const void *data,
4671 size_t n, off_t offset)
4673 struct adouble *ad = NULL;
4679 ai = afpinfo_unpack(talloc_tos(), data);
4684 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
4686 ad = ad_init(talloc_tos(), handle, ADOUBLE_META);
4691 p = ad_get_entry(ad, ADEID_FINDERI);
4693 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
4698 memcpy(p, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
4700 ret = ad_fset(ad, fsp);
4702 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
4709 if (!ai_empty_finderinfo(ai)) {
4714 * Writing an all 0 blob to the metadata stream results in the stream
4715 * being removed on a macOS server. This ensures we behave the same and
4716 * it verified by the "delete AFP_AfpInfo by writing all 0" test.
4719 ok = set_delete_on_close(
4722 handle->conn->session_info->security_token,
4723 handle->conn->session_info->unix_token);
4725 DBG_ERR("set_delete_on_close on [%s] failed\n",
4733 static ssize_t fruit_pwrite_meta(vfs_handle_struct *handle,
4734 files_struct *fsp, const void *data,
4735 size_t n, off_t offset)
4737 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4739 uint8_t buf[AFP_INFO_SIZE];
4745 DBG_ERR("Failed to fetch fsp extension");
4754 if (offset != 0 && n < 60) {
4759 cmp = memcmp(data, "AFP", 3);
4765 if (n <= AFP_OFF_FinderInfo) {
4767 * Nothing to do here really, just return
4775 if (to_copy > AFP_INFO_SIZE) {
4776 to_copy = AFP_INFO_SIZE;
4778 memcpy(buf, data, to_copy);
4781 if (to_write != AFP_INFO_SIZE) {
4782 to_write = AFP_INFO_SIZE;
4785 switch (fio->config->meta) {
4786 case FRUIT_META_STREAM:
4787 nwritten = fruit_pwrite_meta_stream(handle,
4794 case FRUIT_META_NETATALK:
4795 nwritten = fruit_pwrite_meta_netatalk(handle,
4803 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
4807 if (nwritten != to_write) {
4812 * Return the requested amount, verified against macOS SMB server
4817 static ssize_t fruit_pwrite_rsrc_stream(vfs_handle_struct *handle,
4818 files_struct *fsp, const void *data,
4819 size_t n, off_t offset)
4821 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4824 static ssize_t fruit_pwrite_rsrc_xattr(vfs_handle_struct *handle,
4825 files_struct *fsp, const void *data,
4826 size_t n, off_t offset)
4828 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4831 static ssize_t fruit_pwrite_rsrc_adouble(vfs_handle_struct *handle,
4832 files_struct *fsp, const void *data,
4833 size_t n, off_t offset)
4835 struct adouble *ad = NULL;
4839 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
4841 DBG_ERR("ad_get [%s] failed\n", fsp_str_dbg(fsp));
4845 nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n,
4846 offset + ad_getentryoff(ad, ADEID_RFORK));
4847 if (nwritten != n) {
4848 DBG_ERR("Short write on [%s] [%zd/%zd]\n",
4849 fsp_str_dbg(fsp), nwritten, n);
4854 if ((n + offset) > ad_getentrylen(ad, ADEID_RFORK)) {
4855 ad_setentrylen(ad, ADEID_RFORK, n + offset);
4856 ret = ad_fset(ad, fsp);
4858 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
4868 static ssize_t fruit_pwrite_rsrc(vfs_handle_struct *handle,
4869 files_struct *fsp, const void *data,
4870 size_t n, off_t offset)
4872 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4876 DBG_ERR("Failed to fetch fsp extension");
4880 switch (fio->config->rsrc) {
4881 case FRUIT_RSRC_STREAM:
4882 nwritten = fruit_pwrite_rsrc_stream(handle, fsp, data, n, offset);
4885 case FRUIT_RSRC_ADFILE:
4886 nwritten = fruit_pwrite_rsrc_adouble(handle, fsp, data, n, offset);
4889 case FRUIT_RSRC_XATTR:
4890 nwritten = fruit_pwrite_rsrc_xattr(handle, fsp, data, n, offset);
4894 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
4901 static ssize_t fruit_pwrite(vfs_handle_struct *handle,
4902 files_struct *fsp, const void *data,
4903 size_t n, off_t offset)
4905 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4908 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4909 fsp_str_dbg(fsp), (intmax_t)offset, n);
4912 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4915 if (fio->type == ADOUBLE_META) {
4916 nwritten = fruit_pwrite_meta(handle, fsp, data, n, offset);
4918 nwritten = fruit_pwrite_rsrc(handle, fsp, data, n, offset);
4921 DBG_DEBUG("Path [%s] nwritten=%zd\n", fsp_str_dbg(fsp), nwritten);
4925 struct fruit_pwrite_state {
4927 struct vfs_aio_state vfs_aio_state;
4930 static void fruit_pwrite_done(struct tevent_req *subreq);
4932 static struct tevent_req *fruit_pwrite_send(
4933 struct vfs_handle_struct *handle,
4934 TALLOC_CTX *mem_ctx,
4935 struct tevent_context *ev,
4936 struct files_struct *fsp,
4938 size_t n, off_t offset)
4940 struct tevent_req *req = NULL;
4941 struct tevent_req *subreq = NULL;
4942 struct fruit_pwrite_state *state = NULL;
4943 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4945 req = tevent_req_create(mem_ctx, &state,
4946 struct fruit_pwrite_state);
4951 if (fruit_must_handle_aio_stream(fio)) {
4952 state->nwritten = SMB_VFS_PWRITE(fsp, data, n, offset);
4953 if (state->nwritten != n) {
4954 if (state->nwritten != -1) {
4957 tevent_req_error(req, errno);
4958 return tevent_req_post(req, ev);
4960 tevent_req_done(req);
4961 return tevent_req_post(req, ev);
4964 subreq = SMB_VFS_NEXT_PWRITE_SEND(state, ev, handle, fsp,
4966 if (tevent_req_nomem(req, subreq)) {
4967 return tevent_req_post(req, ev);
4969 tevent_req_set_callback(subreq, fruit_pwrite_done, req);
4973 static void fruit_pwrite_done(struct tevent_req *subreq)
4975 struct tevent_req *req = tevent_req_callback_data(
4976 subreq, struct tevent_req);
4977 struct fruit_pwrite_state *state = tevent_req_data(
4978 req, struct fruit_pwrite_state);
4980 state->nwritten = SMB_VFS_PWRITE_RECV(subreq, &state->vfs_aio_state);
4981 TALLOC_FREE(subreq);
4983 if (tevent_req_error(req, state->vfs_aio_state.error)) {
4986 tevent_req_done(req);
4989 static ssize_t fruit_pwrite_recv(struct tevent_req *req,
4990 struct vfs_aio_state *vfs_aio_state)
4992 struct fruit_pwrite_state *state = tevent_req_data(
4993 req, struct fruit_pwrite_state);
4995 if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
4999 *vfs_aio_state = state->vfs_aio_state;
5000 return state->nwritten;
5004 * Helper to stat/lstat the base file of an smb_fname.
5006 static int fruit_stat_base(vfs_handle_struct *handle,
5007 struct smb_filename *smb_fname,
5010 char *tmp_stream_name;
5013 tmp_stream_name = smb_fname->stream_name;
5014 smb_fname->stream_name = NULL;
5016 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
5018 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5020 smb_fname->stream_name = tmp_stream_name;
5022 DBG_DEBUG("fruit_stat_base [%s] dev [%ju] ino [%ju]\n",
5023 smb_fname->base_name,
5024 (uintmax_t)smb_fname->st.st_ex_dev,
5025 (uintmax_t)smb_fname->st.st_ex_ino);
5029 static int fruit_stat_meta_stream(vfs_handle_struct *handle,
5030 struct smb_filename *smb_fname,
5036 ret = fruit_stat_base(handle, smb_fname, false);
5041 ino = fruit_inode(&smb_fname->st, smb_fname->stream_name);
5044 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
5046 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5049 smb_fname->st.st_ex_ino = ino;
5054 static int fruit_stat_meta_netatalk(vfs_handle_struct *handle,
5055 struct smb_filename *smb_fname,
5058 struct adouble *ad = NULL;
5060 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5062 DBG_INFO("fruit_stat_meta %s: %s\n",
5063 smb_fname_str_dbg(smb_fname), strerror(errno));
5069 /* Populate the stat struct with info from the base file. */
5070 if (fruit_stat_base(handle, smb_fname, follow_links) == -1) {
5073 smb_fname->st.st_ex_size = AFP_INFO_SIZE;
5074 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5075 smb_fname->stream_name);
5079 static int fruit_stat_meta(vfs_handle_struct *handle,
5080 struct smb_filename *smb_fname,
5083 struct fruit_config_data *config = NULL;
5086 SMB_VFS_HANDLE_GET_DATA(handle, config,
5087 struct fruit_config_data, return -1);
5089 switch (config->meta) {
5090 case FRUIT_META_STREAM:
5091 ret = fruit_stat_meta_stream(handle, smb_fname, follow_links);
5094 case FRUIT_META_NETATALK:
5095 ret = fruit_stat_meta_netatalk(handle, smb_fname, follow_links);
5099 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
5106 static int fruit_stat_rsrc_netatalk(vfs_handle_struct *handle,
5107 struct smb_filename *smb_fname,
5110 struct adouble *ad = NULL;
5113 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
5119 /* Populate the stat struct with info from the base file. */
5120 ret = fruit_stat_base(handle, smb_fname, follow_links);
5126 smb_fname->st.st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
5127 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5128 smb_fname->stream_name);
5133 static int fruit_stat_rsrc_stream(vfs_handle_struct *handle,
5134 struct smb_filename *smb_fname,
5140 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
5142 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5148 static int fruit_stat_rsrc_xattr(vfs_handle_struct *handle,
5149 struct smb_filename *smb_fname,
5152 #ifdef HAVE_ATTROPEN
5156 /* Populate the stat struct with info from the base file. */
5157 ret = fruit_stat_base(handle, smb_fname, follow_links);
5162 fd = attropen(smb_fname->base_name,
5163 AFPRESOURCE_EA_NETATALK,
5169 ret = sys_fstat(fd, &smb_fname->st, false);
5172 DBG_ERR("fstat [%s:%s] failed\n", smb_fname->base_name,
5173 AFPRESOURCE_EA_NETATALK);
5179 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5180 smb_fname->stream_name);
5190 static int fruit_stat_rsrc(vfs_handle_struct *handle,
5191 struct smb_filename *smb_fname,
5194 struct fruit_config_data *config = NULL;
5197 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
5199 SMB_VFS_HANDLE_GET_DATA(handle, config,
5200 struct fruit_config_data, return -1);
5202 switch (config->rsrc) {
5203 case FRUIT_RSRC_STREAM:
5204 ret = fruit_stat_rsrc_stream(handle, smb_fname, follow_links);
5207 case FRUIT_RSRC_XATTR:
5208 ret = fruit_stat_rsrc_xattr(handle, smb_fname, follow_links);
5211 case FRUIT_RSRC_ADFILE:
5212 ret = fruit_stat_rsrc_netatalk(handle, smb_fname, follow_links);
5216 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
5223 static int fruit_stat(vfs_handle_struct *handle,
5224 struct smb_filename *smb_fname)
5228 DEBUG(10, ("fruit_stat called for %s\n",
5229 smb_fname_str_dbg(smb_fname)));
5231 if (!is_ntfs_stream_smb_fname(smb_fname)
5232 || is_ntfs_default_stream_smb_fname(smb_fname)) {
5233 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
5235 update_btime(handle, smb_fname);
5241 * Note if lp_posix_paths() is true, we can never
5242 * get here as is_ntfs_stream_smb_fname() is
5243 * always false. So we never need worry about
5244 * not following links here.
5247 if (is_afpinfo_stream(smb_fname)) {
5248 rc = fruit_stat_meta(handle, smb_fname, true);
5249 } else if (is_afpresource_stream(smb_fname)) {
5250 rc = fruit_stat_rsrc(handle, smb_fname, true);
5252 return SMB_VFS_NEXT_STAT(handle, smb_fname);
5256 update_btime(handle, smb_fname);
5257 smb_fname->st.st_ex_mode &= ~S_IFMT;
5258 smb_fname->st.st_ex_mode |= S_IFREG;
5259 smb_fname->st.st_ex_blocks =
5260 smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
5265 static int fruit_lstat(vfs_handle_struct *handle,
5266 struct smb_filename *smb_fname)
5270 DEBUG(10, ("fruit_lstat called for %s\n",
5271 smb_fname_str_dbg(smb_fname)));
5273 if (!is_ntfs_stream_smb_fname(smb_fname)
5274 || is_ntfs_default_stream_smb_fname(smb_fname)) {
5275 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5277 update_btime(handle, smb_fname);
5282 if (is_afpinfo_stream(smb_fname)) {
5283 rc = fruit_stat_meta(handle, smb_fname, false);
5284 } else if (is_afpresource_stream(smb_fname)) {
5285 rc = fruit_stat_rsrc(handle, smb_fname, false);
5287 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5291 update_btime(handle, smb_fname);
5292 smb_fname->st.st_ex_mode &= ~S_IFMT;
5293 smb_fname->st.st_ex_mode |= S_IFREG;
5294 smb_fname->st.st_ex_blocks =
5295 smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
5300 static int fruit_fstat_meta_stream(vfs_handle_struct *handle,
5302 SMB_STRUCT_STAT *sbuf)
5304 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5305 struct smb_filename smb_fname;
5314 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5319 *sbuf = fsp->base_fsp->fsp_name->st;
5320 sbuf->st_ex_size = AFP_INFO_SIZE;
5321 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5325 smb_fname = (struct smb_filename) {
5326 .base_name = fsp->fsp_name->base_name,
5329 ret = fruit_stat_base(handle, &smb_fname, false);
5333 *sbuf = smb_fname.st;
5335 ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5337 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5342 sbuf->st_ex_ino = ino;
5346 static int fruit_fstat_meta_netatalk(vfs_handle_struct *handle,
5348 SMB_STRUCT_STAT *sbuf)
5352 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5357 *sbuf = fsp->base_fsp->fsp_name->st;
5358 sbuf->st_ex_size = AFP_INFO_SIZE;
5359 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5364 static int fruit_fstat_meta(vfs_handle_struct *handle,
5366 SMB_STRUCT_STAT *sbuf,
5371 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
5373 switch (fio->config->meta) {
5374 case FRUIT_META_STREAM:
5375 ret = fruit_fstat_meta_stream(handle, fsp, sbuf);
5378 case FRUIT_META_NETATALK:
5379 ret = fruit_fstat_meta_netatalk(handle, fsp, sbuf);
5383 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
5387 DBG_DEBUG("Path [%s] ret [%d]\n", fsp_str_dbg(fsp), ret);
5391 static int fruit_fstat_rsrc_xattr(vfs_handle_struct *handle,
5393 SMB_STRUCT_STAT *sbuf)
5395 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5398 static int fruit_fstat_rsrc_stream(vfs_handle_struct *handle,
5400 SMB_STRUCT_STAT *sbuf)
5402 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5405 static int fruit_fstat_rsrc_adouble(vfs_handle_struct *handle,
5407 SMB_STRUCT_STAT *sbuf)
5409 struct adouble *ad = NULL;
5412 /* Populate the stat struct with info from the base file. */
5413 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5418 ad = ad_get(talloc_tos(), handle,
5419 fsp->base_fsp->fsp_name,
5422 DBG_ERR("ad_get [%s] failed [%s]\n",
5423 fsp_str_dbg(fsp), strerror(errno));
5427 *sbuf = fsp->base_fsp->fsp_name->st;
5428 sbuf->st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
5429 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5435 static int fruit_fstat_rsrc(vfs_handle_struct *handle, files_struct *fsp,
5436 SMB_STRUCT_STAT *sbuf, struct fio *fio)
5440 switch (fio->config->rsrc) {
5441 case FRUIT_RSRC_STREAM:
5442 ret = fruit_fstat_rsrc_stream(handle, fsp, sbuf);
5445 case FRUIT_RSRC_ADFILE:
5446 ret = fruit_fstat_rsrc_adouble(handle, fsp, sbuf);
5449 case FRUIT_RSRC_XATTR:
5450 ret = fruit_fstat_rsrc_xattr(handle, fsp, sbuf);
5454 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
5461 static int fruit_fstat(vfs_handle_struct *handle, files_struct *fsp,
5462 SMB_STRUCT_STAT *sbuf)
5464 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5468 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5471 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
5473 if (fio->type == ADOUBLE_META) {
5474 rc = fruit_fstat_meta(handle, fsp, sbuf, fio);
5476 rc = fruit_fstat_rsrc(handle, fsp, sbuf, fio);
5480 sbuf->st_ex_mode &= ~S_IFMT;
5481 sbuf->st_ex_mode |= S_IFREG;
5482 sbuf->st_ex_blocks = sbuf->st_ex_size / STAT_ST_BLOCKSIZE + 1;
5485 DBG_DEBUG("Path [%s] rc [%d] size [%"PRIdMAX"]\n",
5486 fsp_str_dbg(fsp), rc, (intmax_t)sbuf->st_ex_size);
5490 static NTSTATUS delete_invalid_meta_stream(
5491 vfs_handle_struct *handle,
5492 const struct smb_filename *smb_fname,
5493 TALLOC_CTX *mem_ctx,
5494 unsigned int *pnum_streams,
5495 struct stream_struct **pstreams,
5498 struct smb_filename *sname = NULL;
5502 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams, AFPINFO_STREAM);
5504 return NT_STATUS_INTERNAL_ERROR;
5508 return NT_STATUS_OK;
5511 sname = synthetic_smb_fname(talloc_tos(),
5512 smb_fname->base_name,
5513 AFPINFO_STREAM_NAME,
5515 if (sname == NULL) {
5516 return NT_STATUS_NO_MEMORY;
5519 ret = SMB_VFS_NEXT_UNLINK(handle, sname);
5522 DBG_ERR("Removing [%s] failed\n", smb_fname_str_dbg(sname));
5523 return map_nt_error_from_unix(errno);
5526 return NT_STATUS_OK;
5529 static NTSTATUS fruit_streaminfo_meta_stream(
5530 vfs_handle_struct *handle,
5531 struct files_struct *fsp,
5532 const struct smb_filename *smb_fname,
5533 TALLOC_CTX *mem_ctx,
5534 unsigned int *pnum_streams,
5535 struct stream_struct **pstreams)
5537 struct stream_struct *stream = *pstreams;
5538 unsigned int num_streams = *pnum_streams;
5541 for (i = 0; i < num_streams; i++) {
5542 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
5547 if (i == num_streams) {
5548 return NT_STATUS_OK;
5551 if (stream[i].size != AFP_INFO_SIZE) {
5552 DBG_ERR("Removing invalid AFPINFO_STREAM size [%jd] from [%s]\n",
5553 (intmax_t)stream[i].size, smb_fname_str_dbg(smb_fname));
5555 return delete_invalid_meta_stream(handle,
5564 return NT_STATUS_OK;
5567 static NTSTATUS fruit_streaminfo_meta_netatalk(
5568 vfs_handle_struct *handle,
5569 struct files_struct *fsp,
5570 const struct smb_filename *smb_fname,
5571 TALLOC_CTX *mem_ctx,
5572 unsigned int *pnum_streams,
5573 struct stream_struct **pstreams)
5575 struct stream_struct *stream = *pstreams;
5576 unsigned int num_streams = *pnum_streams;
5577 struct adouble *ad = NULL;
5582 /* Remove the Netatalk xattr from the list */
5583 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5584 ":" NETATALK_META_XATTR ":$DATA");
5586 return NT_STATUS_NO_MEMORY;
5590 * Check if there's a AFPINFO_STREAM from the VFS streams
5591 * backend and if yes, remove it from the list
5593 for (i = 0; i < num_streams; i++) {
5594 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
5599 if (i < num_streams) {
5600 DBG_WARNING("Unexpected AFPINFO_STREAM on [%s]\n",
5601 smb_fname_str_dbg(smb_fname));
5603 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5606 return NT_STATUS_INTERNAL_ERROR;
5610 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5612 return NT_STATUS_OK;
5615 is_fi_empty = ad_empty_finderinfo(ad);
5619 return NT_STATUS_OK;
5622 ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
5623 AFPINFO_STREAM_NAME, AFP_INFO_SIZE,
5624 smb_roundup(handle->conn, AFP_INFO_SIZE));
5626 return NT_STATUS_NO_MEMORY;
5629 return NT_STATUS_OK;
5632 static NTSTATUS fruit_streaminfo_meta(vfs_handle_struct *handle,
5633 struct files_struct *fsp,
5634 const struct smb_filename *smb_fname,
5635 TALLOC_CTX *mem_ctx,
5636 unsigned int *pnum_streams,
5637 struct stream_struct **pstreams)
5639 struct fruit_config_data *config = NULL;
5642 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5643 return NT_STATUS_INTERNAL_ERROR);
5645 switch (config->meta) {
5646 case FRUIT_META_NETATALK:
5647 status = fruit_streaminfo_meta_netatalk(handle, fsp, smb_fname,
5648 mem_ctx, pnum_streams,
5652 case FRUIT_META_STREAM:
5653 status = fruit_streaminfo_meta_stream(handle, fsp, smb_fname,
5654 mem_ctx, pnum_streams,
5659 return NT_STATUS_INTERNAL_ERROR;
5665 static NTSTATUS fruit_streaminfo_rsrc_stream(
5666 vfs_handle_struct *handle,
5667 struct files_struct *fsp,
5668 const struct smb_filename *smb_fname,
5669 TALLOC_CTX *mem_ctx,
5670 unsigned int *pnum_streams,
5671 struct stream_struct **pstreams)
5675 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
5677 DBG_ERR("Filtering resource stream failed\n");
5678 return NT_STATUS_INTERNAL_ERROR;
5680 return NT_STATUS_OK;
5683 static NTSTATUS fruit_streaminfo_rsrc_xattr(
5684 vfs_handle_struct *handle,
5685 struct files_struct *fsp,
5686 const struct smb_filename *smb_fname,
5687 TALLOC_CTX *mem_ctx,
5688 unsigned int *pnum_streams,
5689 struct stream_struct **pstreams)
5693 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
5695 DBG_ERR("Filtering resource stream failed\n");
5696 return NT_STATUS_INTERNAL_ERROR;
5698 return NT_STATUS_OK;
5701 static NTSTATUS fruit_streaminfo_rsrc_adouble(
5702 vfs_handle_struct *handle,
5703 struct files_struct *fsp,
5704 const struct smb_filename *smb_fname,
5705 TALLOC_CTX *mem_ctx,
5706 unsigned int *pnum_streams,
5707 struct stream_struct **pstreams)
5709 struct stream_struct *stream = *pstreams;
5710 unsigned int num_streams = *pnum_streams;
5711 struct adouble *ad = NULL;
5717 * Check if there's a AFPRESOURCE_STREAM from the VFS streams backend
5718 * and if yes, remove it from the list
5720 for (i = 0; i < num_streams; i++) {
5721 if (strequal_m(stream[i].name, AFPRESOURCE_STREAM)) {
5726 if (i < num_streams) {
5727 DBG_WARNING("Unexpected AFPRESOURCE_STREAM on [%s]\n",
5728 smb_fname_str_dbg(smb_fname));
5730 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5731 AFPRESOURCE_STREAM);
5733 return NT_STATUS_INTERNAL_ERROR;
5737 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
5739 return NT_STATUS_OK;
5742 rlen = ad_getentrylen(ad, ADEID_RFORK);
5746 return NT_STATUS_OK;
5749 ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
5750 AFPRESOURCE_STREAM_NAME, rlen,
5751 smb_roundup(handle->conn, rlen));
5753 return NT_STATUS_NO_MEMORY;
5756 return NT_STATUS_OK;
5759 static NTSTATUS fruit_streaminfo_rsrc(vfs_handle_struct *handle,
5760 struct files_struct *fsp,
5761 const struct smb_filename *smb_fname,
5762 TALLOC_CTX *mem_ctx,
5763 unsigned int *pnum_streams,
5764 struct stream_struct **pstreams)
5766 struct fruit_config_data *config = NULL;
5769 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5770 return NT_STATUS_INTERNAL_ERROR);
5772 switch (config->rsrc) {
5773 case FRUIT_RSRC_STREAM:
5774 status = fruit_streaminfo_rsrc_stream(handle, fsp, smb_fname,
5775 mem_ctx, pnum_streams,
5779 case FRUIT_RSRC_XATTR:
5780 status = fruit_streaminfo_rsrc_xattr(handle, fsp, smb_fname,
5781 mem_ctx, pnum_streams,
5785 case FRUIT_RSRC_ADFILE:
5786 status = fruit_streaminfo_rsrc_adouble(handle, fsp, smb_fname,
5787 mem_ctx, pnum_streams,
5792 return NT_STATUS_INTERNAL_ERROR;
5798 static void fruit_filter_empty_streams(unsigned int *pnum_streams,
5799 struct stream_struct **pstreams)
5801 unsigned num_streams = *pnum_streams;
5802 struct stream_struct *streams = *pstreams;
5805 if (!global_fruit_config.nego_aapl) {
5809 while (i < num_streams) {
5810 struct smb_filename smb_fname = (struct smb_filename) {
5811 .stream_name = streams[i].name,
5814 if (is_ntfs_default_stream_smb_fname(&smb_fname)
5815 || streams[i].size > 0)
5821 streams[i] = streams[num_streams - 1];
5825 *pnum_streams = num_streams;
5828 static NTSTATUS fruit_streaminfo(vfs_handle_struct *handle,
5829 struct files_struct *fsp,
5830 const struct smb_filename *smb_fname,
5831 TALLOC_CTX *mem_ctx,
5832 unsigned int *pnum_streams,
5833 struct stream_struct **pstreams)
5835 struct fruit_config_data *config = NULL;
5838 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5839 return NT_STATUS_UNSUCCESSFUL);
5841 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
5843 status = SMB_VFS_NEXT_STREAMINFO(handle, fsp, smb_fname, mem_ctx,
5844 pnum_streams, pstreams);
5845 if (!NT_STATUS_IS_OK(status)) {
5849 fruit_filter_empty_streams(pnum_streams, pstreams);
5851 status = fruit_streaminfo_meta(handle, fsp, smb_fname,
5852 mem_ctx, pnum_streams, pstreams);
5853 if (!NT_STATUS_IS_OK(status)) {
5857 status = fruit_streaminfo_rsrc(handle, fsp, smb_fname,
5858 mem_ctx, pnum_streams, pstreams);
5859 if (!NT_STATUS_IS_OK(status)) {
5863 return NT_STATUS_OK;
5866 static int fruit_ntimes(vfs_handle_struct *handle,
5867 const struct smb_filename *smb_fname,
5868 struct smb_file_time *ft)
5871 struct adouble *ad = NULL;
5872 struct fruit_config_data *config = NULL;
5874 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5877 if ((config->meta != FRUIT_META_NETATALK) ||
5878 null_timespec(ft->create_time))
5880 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
5883 DEBUG(10,("set btime for %s to %s\n", smb_fname_str_dbg(smb_fname),
5884 time_to_asc(convert_timespec_to_time_t(ft->create_time))));
5886 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5891 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX,
5892 convert_time_t_to_uint32_t(ft->create_time.tv_sec));
5894 rc = ad_set(ad, smb_fname);
5900 DEBUG(1, ("fruit_ntimes: %s\n", smb_fname_str_dbg(smb_fname)));
5903 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
5906 static int fruit_fallocate(struct vfs_handle_struct *handle,
5907 struct files_struct *fsp,
5912 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5915 return SMB_VFS_NEXT_FALLOCATE(handle, fsp, mode, offset, len);
5918 /* Let the pwrite code path handle it. */
5923 static int fruit_ftruncate_rsrc_xattr(struct vfs_handle_struct *handle,
5924 struct files_struct *fsp,
5927 #ifdef HAVE_ATTROPEN
5928 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
5933 static int fruit_ftruncate_rsrc_adouble(struct vfs_handle_struct *handle,
5934 struct files_struct *fsp,
5938 struct adouble *ad = NULL;
5941 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
5943 DBG_DEBUG("ad_get [%s] failed [%s]\n",
5944 fsp_str_dbg(fsp), strerror(errno));
5948 ad_off = ad_getentryoff(ad, ADEID_RFORK);
5950 rc = ftruncate(fsp->fh->fd, offset + ad_off);
5956 ad_setentrylen(ad, ADEID_RFORK, offset);
5958 rc = ad_fset(ad, fsp);
5960 DBG_ERR("ad_fset [%s] failed [%s]\n",
5961 fsp_str_dbg(fsp), strerror(errno));
5970 static int fruit_ftruncate_rsrc_stream(struct vfs_handle_struct *handle,
5971 struct files_struct *fsp,
5974 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
5977 static int fruit_ftruncate_rsrc(struct vfs_handle_struct *handle,
5978 struct files_struct *fsp,
5981 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5985 DBG_ERR("Failed to fetch fsp extension");
5989 switch (fio->config->rsrc) {
5990 case FRUIT_RSRC_XATTR:
5991 ret = fruit_ftruncate_rsrc_xattr(handle, fsp, offset);
5994 case FRUIT_RSRC_ADFILE:
5995 ret = fruit_ftruncate_rsrc_adouble(handle, fsp, offset);
5998 case FRUIT_RSRC_STREAM:
5999 ret = fruit_ftruncate_rsrc_stream(handle, fsp, offset);
6003 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
6011 static int fruit_ftruncate_meta(struct vfs_handle_struct *handle,
6012 struct files_struct *fsp,
6016 DBG_WARNING("ftruncate %s to %jd",
6017 fsp_str_dbg(fsp), (intmax_t)offset);
6018 /* OS X returns NT_STATUS_ALLOTTED_SPACE_EXCEEDED */
6023 /* OS X returns success but does nothing */
6024 DBG_INFO("ignoring ftruncate %s to %jd\n",
6025 fsp_str_dbg(fsp), (intmax_t)offset);
6029 static int fruit_ftruncate(struct vfs_handle_struct *handle,
6030 struct files_struct *fsp,
6033 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
6036 DBG_DEBUG("Path [%s] offset [%"PRIdMAX"]\n", fsp_str_dbg(fsp),
6040 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
6043 if (fio->type == ADOUBLE_META) {
6044 ret = fruit_ftruncate_meta(handle, fsp, offset);
6046 ret = fruit_ftruncate_rsrc(handle, fsp, offset);
6049 DBG_DEBUG("Path [%s] result [%d]\n", fsp_str_dbg(fsp), ret);
6053 static NTSTATUS fruit_create_file(vfs_handle_struct *handle,
6054 struct smb_request *req,
6055 uint16_t root_dir_fid,
6056 struct smb_filename *smb_fname,
6057 uint32_t access_mask,
6058 uint32_t share_access,
6059 uint32_t create_disposition,
6060 uint32_t create_options,
6061 uint32_t file_attributes,
6062 uint32_t oplock_request,
6063 struct smb2_lease *lease,
6064 uint64_t allocation_size,
6065 uint32_t private_flags,
6066 struct security_descriptor *sd,
6067 struct ea_list *ea_list,
6068 files_struct **result,
6070 const struct smb2_create_blobs *in_context_blobs,
6071 struct smb2_create_blobs *out_context_blobs)
6074 struct fruit_config_data *config = NULL;
6075 files_struct *fsp = NULL;
6076 struct fio *fio = NULL;
6078 status = check_aapl(handle, req, in_context_blobs, out_context_blobs);
6079 if (!NT_STATUS_IS_OK(status)) {
6083 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
6084 return NT_STATUS_UNSUCCESSFUL);
6086 status = SMB_VFS_NEXT_CREATE_FILE(
6087 handle, req, root_dir_fid, smb_fname,
6088 access_mask, share_access,
6089 create_disposition, create_options,
6090 file_attributes, oplock_request,
6092 allocation_size, private_flags,
6093 sd, ea_list, result,
6094 pinfo, in_context_blobs, out_context_blobs);
6095 if (!NT_STATUS_IS_OK(status)) {
6101 if (global_fruit_config.nego_aapl) {
6102 if (config->posix_rename && fsp->is_directory) {
6104 * Enable POSIX directory rename behaviour
6106 fsp->posix_flags |= FSP_POSIX_FLAGS_RENAME;
6111 * If this is a plain open for existing files, opening an 0
6112 * byte size resource fork MUST fail with
6113 * NT_STATUS_OBJECT_NAME_NOT_FOUND.
6115 * Cf the vfs_fruit torture tests in test_rfork_create().
6117 if (global_fruit_config.nego_aapl &&
6118 create_disposition == FILE_OPEN &&
6119 smb_fname->st.st_ex_size == 0 &&
6120 is_ntfs_stream_smb_fname(smb_fname) &&
6121 !(is_ntfs_default_stream_smb_fname(smb_fname)))
6123 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
6127 fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
6128 if (fio != NULL && pinfo != NULL && *pinfo == FILE_WAS_CREATED) {
6129 fio->created = true;
6132 if (is_ntfs_stream_smb_fname(smb_fname)
6133 || fsp->is_directory) {
6137 if (config->locking == FRUIT_LOCKING_NETATALK) {
6138 status = fruit_check_access(
6142 if (!NT_STATUS_IS_OK(status)) {
6150 DEBUG(10, ("fruit_create_file: %s\n", nt_errstr(status)));
6153 close_file(req, fsp, ERROR_CLOSE);
6154 *result = fsp = NULL;
6160 static NTSTATUS fruit_readdir_attr(struct vfs_handle_struct *handle,
6161 const struct smb_filename *fname,
6162 TALLOC_CTX *mem_ctx,
6163 struct readdir_attr_data **pattr_data)
6165 struct fruit_config_data *config = NULL;
6166 struct readdir_attr_data *attr_data;
6169 SMB_VFS_HANDLE_GET_DATA(handle, config,
6170 struct fruit_config_data,
6171 return NT_STATUS_UNSUCCESSFUL);
6173 if (!global_fruit_config.nego_aapl) {
6174 return SMB_VFS_NEXT_READDIR_ATTR(handle, fname, mem_ctx, pattr_data);
6177 DEBUG(10, ("fruit_readdir_attr %s\n", fname->base_name));
6179 *pattr_data = talloc_zero(mem_ctx, struct readdir_attr_data);
6180 if (*pattr_data == NULL) {
6181 return NT_STATUS_UNSUCCESSFUL;
6183 attr_data = *pattr_data;
6184 attr_data->type = RDATTR_AAPL;
6187 * Mac metadata: compressed FinderInfo, resource fork length
6190 status = readdir_attr_macmeta(handle, fname, attr_data);
6191 if (!NT_STATUS_IS_OK(status)) {
6193 * Error handling is tricky: if we return failure from
6194 * this function, the corresponding directory entry
6195 * will to be passed to the client, so we really just
6196 * want to error out on fatal errors.
6198 if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
6206 if (config->unix_info_enabled) {
6207 attr_data->attr_data.aapl.unix_mode = fname->st.st_ex_mode;
6213 if (!config->readdir_attr_max_access) {
6214 attr_data->attr_data.aapl.max_access = FILE_GENERIC_ALL;
6216 status = smbd_calculate_access_mask(
6220 SEC_FLAG_MAXIMUM_ALLOWED,
6221 &attr_data->attr_data.aapl.max_access);
6222 if (!NT_STATUS_IS_OK(status)) {
6227 return NT_STATUS_OK;
6230 DEBUG(1, ("fruit_readdir_attr %s, error: %s\n",
6231 fname->base_name, nt_errstr(status)));
6232 TALLOC_FREE(*pattr_data);
6236 static NTSTATUS fruit_fget_nt_acl(vfs_handle_struct *handle,
6238 uint32_t security_info,
6239 TALLOC_CTX *mem_ctx,
6240 struct security_descriptor **ppdesc)
6243 struct security_ace ace;
6245 struct fruit_config_data *config;
6247 SMB_VFS_HANDLE_GET_DATA(handle, config,
6248 struct fruit_config_data,
6249 return NT_STATUS_UNSUCCESSFUL);
6251 status = SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
6253 if (!NT_STATUS_IS_OK(status)) {
6258 * Add MS NFS style ACEs with uid, gid and mode
6260 if (!global_fruit_config.nego_aapl) {
6261 return NT_STATUS_OK;
6263 if (!config->unix_info_enabled) {
6264 return NT_STATUS_OK;
6267 /* First remove any existing ACE's with NFS style mode/uid/gid SIDs. */
6268 status = remove_virtual_nfs_aces(*ppdesc);
6269 if (!NT_STATUS_IS_OK(status)) {
6270 DBG_WARNING("failed to remove MS NFS style ACEs\n");
6274 /* MS NFS style mode */
6275 sid_compose(&sid, &global_sid_Unix_NFS_Mode, fsp->fsp_name->st.st_ex_mode);
6276 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6277 status = security_descriptor_dacl_add(*ppdesc, &ace);
6278 if (!NT_STATUS_IS_OK(status)) {
6279 DEBUG(1,("failed to add MS NFS style ACE\n"));
6283 /* MS NFS style uid */
6284 sid_compose(&sid, &global_sid_Unix_NFS_Users, fsp->fsp_name->st.st_ex_uid);
6285 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6286 status = security_descriptor_dacl_add(*ppdesc, &ace);
6287 if (!NT_STATUS_IS_OK(status)) {
6288 DEBUG(1,("failed to add MS NFS style ACE\n"));
6292 /* MS NFS style gid */
6293 sid_compose(&sid, &global_sid_Unix_NFS_Groups, fsp->fsp_name->st.st_ex_gid);
6294 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6295 status = security_descriptor_dacl_add(*ppdesc, &ace);
6296 if (!NT_STATUS_IS_OK(status)) {
6297 DEBUG(1,("failed to add MS NFS style ACE\n"));
6301 return NT_STATUS_OK;
6304 static NTSTATUS fruit_fset_nt_acl(vfs_handle_struct *handle,
6306 uint32_t security_info_sent,
6307 const struct security_descriptor *orig_psd)
6311 mode_t ms_nfs_mode = 0;
6313 struct security_descriptor *psd = NULL;
6314 uint32_t orig_num_aces = 0;
6316 if (orig_psd->dacl != NULL) {
6317 orig_num_aces = orig_psd->dacl->num_aces;
6320 psd = security_descriptor_copy(talloc_tos(), orig_psd);
6322 return NT_STATUS_NO_MEMORY;
6325 DBG_DEBUG("fruit_fset_nt_acl: %s\n", fsp_str_dbg(fsp));
6327 status = check_ms_nfs(handle, fsp, psd, &ms_nfs_mode, &do_chmod);
6328 if (!NT_STATUS_IS_OK(status)) {
6329 DEBUG(1, ("fruit_fset_nt_acl: check_ms_nfs failed%s\n", fsp_str_dbg(fsp)));
6335 * If only ms_nfs ACE entries were sent, ensure we set the DACL
6336 * sent/present flags correctly now we've removed them.
6339 if (orig_num_aces != 0) {
6341 * Are there any ACE's left ?
6343 if (psd->dacl->num_aces == 0) {
6344 /* No - clear the DACL sent/present flags. */
6345 security_info_sent &= ~SECINFO_DACL;
6346 psd->type &= ~SEC_DESC_DACL_PRESENT;
6350 status = SMB_VFS_NEXT_FSET_NT_ACL(handle, fsp, security_info_sent, psd);
6351 if (!NT_STATUS_IS_OK(status)) {
6352 DEBUG(1, ("fruit_fset_nt_acl: SMB_VFS_NEXT_FSET_NT_ACL failed%s\n", fsp_str_dbg(fsp)));
6358 if (fsp->fh->fd != -1) {
6359 result = SMB_VFS_FCHMOD(fsp, ms_nfs_mode);
6361 result = SMB_VFS_CHMOD(fsp->conn,
6367 DEBUG(1, ("chmod: %s, result: %d, %04o error %s\n", fsp_str_dbg(fsp),
6368 result, (unsigned)ms_nfs_mode,
6370 status = map_nt_error_from_unix(errno);
6377 return NT_STATUS_OK;
6380 static struct vfs_offload_ctx *fruit_offload_ctx;
6382 struct fruit_offload_read_state {
6383 struct vfs_handle_struct *handle;
6384 struct tevent_context *ev;
6390 static void fruit_offload_read_done(struct tevent_req *subreq);
6392 static struct tevent_req *fruit_offload_read_send(
6393 TALLOC_CTX *mem_ctx,
6394 struct tevent_context *ev,
6395 struct vfs_handle_struct *handle,
6402 struct tevent_req *req = NULL;
6403 struct tevent_req *subreq = NULL;
6404 struct fruit_offload_read_state *state = NULL;
6406 req = tevent_req_create(mem_ctx, &state,
6407 struct fruit_offload_read_state);
6411 *state = (struct fruit_offload_read_state) {
6418 subreq = SMB_VFS_NEXT_OFFLOAD_READ_SEND(mem_ctx, ev, handle, fsp,
6419 fsctl, ttl, offset, to_copy);
6420 if (tevent_req_nomem(subreq, req)) {
6421 return tevent_req_post(req, ev);
6423 tevent_req_set_callback(subreq, fruit_offload_read_done, req);
6427 static void fruit_offload_read_done(struct tevent_req *subreq)
6429 struct tevent_req *req = tevent_req_callback_data(
6430 subreq, struct tevent_req);
6431 struct fruit_offload_read_state *state = tevent_req_data(
6432 req, struct fruit_offload_read_state);
6435 status = SMB_VFS_NEXT_OFFLOAD_READ_RECV(subreq,
6439 TALLOC_FREE(subreq);
6440 if (tevent_req_nterror(req, status)) {
6444 if (state->fsctl != FSCTL_SRV_REQUEST_RESUME_KEY) {
6445 tevent_req_done(req);
6449 status = vfs_offload_token_ctx_init(state->fsp->conn->sconn->client,
6450 &fruit_offload_ctx);
6451 if (tevent_req_nterror(req, status)) {
6455 status = vfs_offload_token_db_store_fsp(fruit_offload_ctx,
6458 if (tevent_req_nterror(req, status)) {
6462 tevent_req_done(req);
6466 static NTSTATUS fruit_offload_read_recv(struct tevent_req *req,
6467 struct vfs_handle_struct *handle,
6468 TALLOC_CTX *mem_ctx,
6471 struct fruit_offload_read_state *state = tevent_req_data(
6472 req, struct fruit_offload_read_state);
6475 if (tevent_req_is_nterror(req, &status)) {
6476 tevent_req_received(req);
6480 token->length = state->token.length;
6481 token->data = talloc_move(mem_ctx, &state->token.data);
6483 tevent_req_received(req);
6484 return NT_STATUS_OK;
6487 struct fruit_offload_write_state {
6488 struct vfs_handle_struct *handle;
6490 struct files_struct *src_fsp;
6491 struct files_struct *dst_fsp;
6495 static void fruit_offload_write_done(struct tevent_req *subreq);
6496 static struct tevent_req *fruit_offload_write_send(struct vfs_handle_struct *handle,
6497 TALLOC_CTX *mem_ctx,
6498 struct tevent_context *ev,
6501 off_t transfer_offset,
6502 struct files_struct *dest_fsp,
6506 struct tevent_req *req, *subreq;
6507 struct fruit_offload_write_state *state;
6509 struct fruit_config_data *config;
6510 off_t src_off = transfer_offset;
6511 files_struct *src_fsp = NULL;
6512 off_t to_copy = num;
6513 bool copyfile_enabled = false;
6515 DEBUG(10,("soff: %ju, doff: %ju, len: %ju\n",
6516 (uintmax_t)src_off, (uintmax_t)dest_off, (uintmax_t)num));
6518 SMB_VFS_HANDLE_GET_DATA(handle, config,
6519 struct fruit_config_data,
6522 req = tevent_req_create(mem_ctx, &state,
6523 struct fruit_offload_write_state);
6527 state->handle = handle;
6528 state->dst_fsp = dest_fsp;
6531 case FSCTL_SRV_COPYCHUNK:
6532 case FSCTL_SRV_COPYCHUNK_WRITE:
6533 copyfile_enabled = config->copyfile_enabled;
6540 * Check if this a OS X copyfile style copychunk request with
6541 * a requested chunk count of 0 that was translated to a
6542 * offload_write_send VFS call overloading the parameters src_off
6543 * = dest_off = num = 0.
6545 if (copyfile_enabled && num == 0 && src_off == 0 && dest_off == 0) {
6546 status = vfs_offload_token_db_fetch_fsp(
6547 fruit_offload_ctx, token, &src_fsp);
6548 if (tevent_req_nterror(req, status)) {
6549 return tevent_req_post(req, ev);
6551 state->src_fsp = src_fsp;
6553 status = vfs_stat_fsp(src_fsp);
6554 if (tevent_req_nterror(req, status)) {
6555 return tevent_req_post(req, ev);
6558 to_copy = src_fsp->fsp_name->st.st_ex_size;
6559 state->is_copyfile = true;
6562 subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle,
6571 if (tevent_req_nomem(subreq, req)) {
6572 return tevent_req_post(req, ev);
6575 tevent_req_set_callback(subreq, fruit_offload_write_done, req);
6579 static void fruit_offload_write_done(struct tevent_req *subreq)
6581 struct tevent_req *req = tevent_req_callback_data(
6582 subreq, struct tevent_req);
6583 struct fruit_offload_write_state *state = tevent_req_data(
6584 req, struct fruit_offload_write_state);
6586 unsigned int num_streams = 0;
6587 struct stream_struct *streams = NULL;
6589 struct smb_filename *src_fname_tmp = NULL;
6590 struct smb_filename *dst_fname_tmp = NULL;
6592 status = SMB_VFS_NEXT_OFFLOAD_WRITE_RECV(state->handle,
6595 TALLOC_FREE(subreq);
6596 if (tevent_req_nterror(req, status)) {
6600 if (!state->is_copyfile) {
6601 tevent_req_done(req);
6606 * Now copy all remaining streams. We know the share supports
6607 * streams, because we're in vfs_fruit. We don't do this async
6608 * because streams are few and small.
6610 status = vfs_streaminfo(state->handle->conn, state->src_fsp,
6611 state->src_fsp->fsp_name,
6612 req, &num_streams, &streams);
6613 if (tevent_req_nterror(req, status)) {
6617 if (num_streams == 1) {
6618 /* There is always one stream, ::$DATA. */
6619 tevent_req_done(req);
6623 for (i = 0; i < num_streams; i++) {
6624 DEBUG(10, ("%s: stream: '%s'/%zu\n",
6625 __func__, streams[i].name, (size_t)streams[i].size));
6627 src_fname_tmp = synthetic_smb_fname(
6629 state->src_fsp->fsp_name->base_name,
6632 state->src_fsp->fsp_name->flags);
6633 if (tevent_req_nomem(src_fname_tmp, req)) {
6637 if (is_ntfs_default_stream_smb_fname(src_fname_tmp)) {
6638 TALLOC_FREE(src_fname_tmp);
6642 dst_fname_tmp = synthetic_smb_fname(
6644 state->dst_fsp->fsp_name->base_name,
6647 state->dst_fsp->fsp_name->flags);
6648 if (tevent_req_nomem(dst_fname_tmp, req)) {
6649 TALLOC_FREE(src_fname_tmp);
6653 status = copy_file(req,
6654 state->handle->conn,
6657 OPENX_FILE_CREATE_IF_NOT_EXIST,
6659 if (!NT_STATUS_IS_OK(status)) {
6660 DEBUG(1, ("%s: copy %s to %s failed: %s\n", __func__,
6661 smb_fname_str_dbg(src_fname_tmp),
6662 smb_fname_str_dbg(dst_fname_tmp),
6663 nt_errstr(status)));
6664 TALLOC_FREE(src_fname_tmp);
6665 TALLOC_FREE(dst_fname_tmp);
6666 tevent_req_nterror(req, status);
6670 TALLOC_FREE(src_fname_tmp);
6671 TALLOC_FREE(dst_fname_tmp);
6674 TALLOC_FREE(streams);
6675 TALLOC_FREE(src_fname_tmp);
6676 TALLOC_FREE(dst_fname_tmp);
6677 tevent_req_done(req);
6680 static NTSTATUS fruit_offload_write_recv(struct vfs_handle_struct *handle,
6681 struct tevent_req *req,
6684 struct fruit_offload_write_state *state = tevent_req_data(
6685 req, struct fruit_offload_write_state);
6688 if (tevent_req_is_nterror(req, &status)) {
6689 DEBUG(1, ("server side copy chunk failed: %s\n",
6690 nt_errstr(status)));
6692 tevent_req_received(req);
6696 *copied = state->copied;
6697 tevent_req_received(req);
6699 return NT_STATUS_OK;
6702 static char *fruit_get_bandsize_line(char **lines, int numlines)
6705 static bool re_initialized = false;
6709 if (!re_initialized) {
6710 ret = regcomp(&re, "^[[:blank:]]*<key>band-size</key>$", 0);
6714 re_initialized = true;
6717 for (i = 0; i < numlines; i++) {
6718 regmatch_t matches[1];
6720 ret = regexec(&re, lines[i], 1, matches, 0);
6723 * Check if the match was on the last line, sa we want
6724 * the subsequent line.
6726 if (i + 1 == numlines) {
6729 return lines[i + 1];
6731 if (ret != REG_NOMATCH) {
6739 static bool fruit_get_bandsize_from_line(char *line, size_t *_band_size)
6742 static bool re_initialized = false;
6743 regmatch_t matches[2];
6748 if (!re_initialized) {
6751 "<integer>\\([[:digit:]]*\\)</integer>$",
6756 re_initialized = true;
6759 ret = regexec(&re, line, 2, matches, 0);
6761 DBG_ERR("regex failed [%s]\n", line);
6765 line[matches[1].rm_eo] = '\0';
6767 ok = conv_str_u64(&line[matches[1].rm_so], &band_size);
6771 *_band_size = (size_t)band_size;
6776 * This reads and parses an Info.plist from a TM sparsebundle looking for the
6777 * "band-size" key and value.
6779 static bool fruit_get_bandsize(vfs_handle_struct *handle,
6783 #define INFO_PLIST_MAX_SIZE 64*1024
6785 struct smb_filename *smb_fname = NULL;
6786 files_struct *fsp = NULL;
6787 uint8_t *file_data = NULL;
6788 char **lines = NULL;
6789 char *band_size_line = NULL;
6790 size_t plist_file_size;
6797 plist = talloc_asprintf(talloc_tos(),
6799 handle->conn->connectpath,
6801 if (plist == NULL) {
6806 smb_fname = synthetic_smb_fname(talloc_tos(), plist, NULL, NULL, 0);
6807 if (smb_fname == NULL) {
6812 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
6814 DBG_INFO("Ignoring Sparsebundle without Info.plist [%s]\n", dir);
6819 plist_file_size = smb_fname->st.st_ex_size;
6821 if (plist_file_size > INFO_PLIST_MAX_SIZE) {
6822 DBG_INFO("%s is too large, ignoring\n", plist);
6827 status = SMB_VFS_NEXT_CREATE_FILE(
6830 0, /* root_dir_fid */
6831 smb_fname, /* fname */
6832 FILE_GENERIC_READ, /* access_mask */
6833 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
6834 FILE_OPEN, /* create_disposition */
6835 0, /* create_options */
6836 0, /* file_attributes */
6837 INTERNAL_OPEN_ONLY, /* oplock_request */
6839 0, /* allocation_size */
6840 0, /* private_flags */
6845 NULL, NULL); /* create context */
6846 if (!NT_STATUS_IS_OK(status)) {
6847 DBG_INFO("Opening [%s] failed [%s]\n",
6848 smb_fname_str_dbg(smb_fname), nt_errstr(status));
6853 file_data = talloc_array(talloc_tos(), uint8_t, plist_file_size);
6854 if (file_data == NULL) {
6859 nread = SMB_VFS_NEXT_PREAD(handle, fsp, file_data, plist_file_size, 0);
6860 if (nread != plist_file_size) {
6861 DBG_ERR("Short read on [%s]: %zu/%zd\n",
6862 fsp_str_dbg(fsp), nread, plist_file_size);
6868 status = close_file(NULL, fsp, NORMAL_CLOSE);
6870 if (!NT_STATUS_IS_OK(status)) {
6871 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
6876 lines = file_lines_parse((char *)file_data,
6880 if (lines == NULL) {
6885 band_size_line = fruit_get_bandsize_line(lines, numlines);
6886 if (band_size_line == NULL) {
6887 DBG_ERR("Didn't find band-size key in [%s]\n",
6888 smb_fname_str_dbg(smb_fname));
6893 ok = fruit_get_bandsize_from_line(band_size_line, band_size);
6895 DBG_ERR("fruit_get_bandsize_from_line failed\n");
6899 DBG_DEBUG("Parsed band-size [%zu] for [%s]\n", *band_size, plist);
6903 status = close_file(NULL, fsp, NORMAL_CLOSE);
6904 if (!NT_STATUS_IS_OK(status)) {
6905 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
6910 TALLOC_FREE(smb_fname);
6911 TALLOC_FREE(file_data);
6916 struct fruit_disk_free_state {
6920 static bool fruit_get_num_bands(vfs_handle_struct *handle,
6925 struct smb_filename *bands_dir = NULL;
6927 struct dirent *e = NULL;
6931 path = talloc_asprintf(talloc_tos(),
6933 handle->conn->connectpath,
6939 bands_dir = synthetic_smb_fname(talloc_tos(),
6945 if (bands_dir == NULL) {
6949 d = SMB_VFS_NEXT_OPENDIR(handle, bands_dir, NULL, 0);
6951 TALLOC_FREE(bands_dir);
6957 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
6959 e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
6961 if (ISDOT(e->d_name) || ISDOTDOT(e->d_name)) {
6967 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
6969 TALLOC_FREE(bands_dir);
6973 DBG_DEBUG("%zu bands in [%s]\n", nbands, smb_fname_str_dbg(bands_dir));
6975 TALLOC_FREE(bands_dir);
6981 static bool fruit_tmsize_do_dirent(vfs_handle_struct *handle,
6982 struct fruit_disk_free_state *state,
6987 size_t sparsebundle_strlen = strlen("sparsebundle");
6988 size_t bandsize = 0;
6992 p = strstr(e->d_name, "sparsebundle");
6997 if (p[sparsebundle_strlen] != '\0') {
7001 DBG_DEBUG("Processing sparsebundle [%s]\n", e->d_name);
7003 ok = fruit_get_bandsize(handle, e->d_name, &bandsize);
7006 * Beware of race conditions: this may be an uninitialized
7007 * Info.plist that a client is just creating. We don't want let
7008 * this to trigger complete failure.
7010 DBG_ERR("Processing sparsebundle [%s] failed\n", e->d_name);
7014 ok = fruit_get_num_bands(handle, e->d_name, &nbands);
7017 * Beware of race conditions: this may be a backup sparsebundle
7018 * in an early stage lacking a bands subdirectory. We don't want
7019 * let this to trigger complete failure.
7021 DBG_ERR("Processing sparsebundle [%s] failed\n", e->d_name);
7025 if (bandsize > SIZE_MAX/nbands) {
7026 DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
7030 tm_size = bandsize * nbands;
7032 if (state->total_size + tm_size < state->total_size) {
7033 DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
7038 state->total_size += tm_size;
7040 DBG_DEBUG("[%s] tm_size [%jd] total_size [%jd]\n",
7041 e->d_name, (intmax_t)tm_size, (intmax_t)state->total_size);
7047 * Calculate used size of a TimeMachine volume
7049 * This assumes that the volume is used only for TimeMachine.
7051 * - readdir(basedir of share), then
7052 * - for every element that matches regex "^\(.*\)\.sparsebundle$" :
7053 * - parse "\1.sparsebundle/Info.plist" and read the band-size XML key
7054 * - count band files in "\1.sparsebundle/bands/"
7055 * - calculate used size of all bands: band_count * band_size
7057 static uint64_t fruit_disk_free(vfs_handle_struct *handle,
7058 const struct smb_filename *smb_fname,
7063 struct fruit_config_data *config = NULL;
7064 struct fruit_disk_free_state state = {0};
7066 struct dirent *e = NULL;
7072 SMB_VFS_HANDLE_GET_DATA(handle, config,
7073 struct fruit_config_data,
7076 if (!config->time_machine ||
7077 config->time_machine_max_size == 0)
7079 return SMB_VFS_NEXT_DISK_FREE(handle,
7086 d = SMB_VFS_NEXT_OPENDIR(handle, smb_fname, NULL, 0);
7091 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
7093 e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
7095 ok = fruit_tmsize_do_dirent(handle, &state, e);
7097 SMB_VFS_NEXT_CLOSEDIR(handle, d);
7102 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
7107 dsize = config->time_machine_max_size / 512;
7108 dfree = dsize - (state.total_size / 512);
7109 if (dfree > dsize) {
7119 static struct vfs_fn_pointers vfs_fruit_fns = {
7120 .connect_fn = fruit_connect,
7121 .disk_free_fn = fruit_disk_free,
7123 /* File operations */
7124 .chmod_fn = fruit_chmod,
7125 .chown_fn = fruit_chown,
7126 .unlink_fn = fruit_unlink,
7127 .rename_fn = fruit_rename,
7128 .rmdir_fn = fruit_rmdir,
7129 .open_fn = fruit_open,
7130 .close_fn = fruit_close,
7131 .pread_fn = fruit_pread,
7132 .pwrite_fn = fruit_pwrite,
7133 .pread_send_fn = fruit_pread_send,
7134 .pread_recv_fn = fruit_pread_recv,
7135 .pwrite_send_fn = fruit_pwrite_send,
7136 .pwrite_recv_fn = fruit_pwrite_recv,
7137 .stat_fn = fruit_stat,
7138 .lstat_fn = fruit_lstat,
7139 .fstat_fn = fruit_fstat,
7140 .streaminfo_fn = fruit_streaminfo,
7141 .ntimes_fn = fruit_ntimes,
7142 .ftruncate_fn = fruit_ftruncate,
7143 .fallocate_fn = fruit_fallocate,
7144 .create_file_fn = fruit_create_file,
7145 .readdir_attr_fn = fruit_readdir_attr,
7146 .offload_read_send_fn = fruit_offload_read_send,
7147 .offload_read_recv_fn = fruit_offload_read_recv,
7148 .offload_write_send_fn = fruit_offload_write_send,
7149 .offload_write_recv_fn = fruit_offload_write_recv,
7151 /* NT ACL operations */
7152 .fget_nt_acl_fn = fruit_fget_nt_acl,
7153 .fset_nt_acl_fn = fruit_fset_nt_acl,
7157 NTSTATUS vfs_fruit_init(TALLOC_CTX *ctx)
7159 NTSTATUS ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "fruit",
7161 if (!NT_STATUS_IS_OK(ret)) {
7165 vfs_fruit_debug_level = debug_add_class("fruit");
7166 if (vfs_fruit_debug_level == -1) {
7167 vfs_fruit_debug_level = DBGC_VFS;
7168 DEBUG(0, ("%s: Couldn't register custom debugging class!\n",
7171 DEBUG(10, ("%s: Debug class number of '%s': %d\n",
7172 "vfs_fruit_init","fruit",vfs_fruit_debug_level));