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(vfs_handle_struct *handle,
546 const struct smb_filename *smb_fname);
547 static int ad_fset(struct vfs_handle_struct *handle,
550 static int adouble_path(TALLOC_CTX *ctx,
551 const struct smb_filename *smb_fname__in,
552 struct smb_filename **ppsmb_fname_out);
553 static AfpInfo *afpinfo_new(TALLOC_CTX *ctx);
554 static ssize_t afpinfo_pack(const AfpInfo *ai, char *buf);
555 static AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data);
559 * Return a pointer to an AppleDouble entry
561 * Returns NULL if the entry is not present
563 static char *ad_get_entry(const struct adouble *ad, int eid)
565 off_t off = ad_getentryoff(ad, eid);
566 size_t len = ad_getentrylen(ad, eid);
568 if (off == 0 || len == 0) {
572 return ad->ad_data + off;
578 static int ad_getdate(const struct adouble *ad,
579 unsigned int dateoff,
582 bool xlate = (dateoff & AD_DATE_UNIX);
585 dateoff &= AD_DATE_MASK;
586 p = ad_get_entry(ad, ADEID_FILEDATESI);
591 if (dateoff > AD_DATE_ACCESS) {
595 memcpy(date, p + dateoff, sizeof(uint32_t));
598 *date = AD_DATE_TO_UNIX(*date);
606 static int ad_setdate(struct adouble *ad, unsigned int dateoff, uint32_t date)
608 bool xlate = (dateoff & AD_DATE_UNIX);
611 p = ad_get_entry(ad, ADEID_FILEDATESI);
616 dateoff &= AD_DATE_MASK;
618 date = AD_DATE_FROM_UNIX(date);
621 if (dateoff > AD_DATE_ACCESS) {
625 memcpy(p + dateoff, &date, sizeof(date));
632 * Map on-disk AppleDouble id to enumerated id
634 static uint32_t get_eid(uint32_t eid)
642 return ADEID_PRIVDEV;
644 return ADEID_PRIVINO;
646 return ADEID_PRIVSYN;
657 * Pack AppleDouble structure into data buffer
659 static bool ad_pack(struct adouble *ad)
666 bufsize = talloc_get_size(ad->ad_data);
667 if (bufsize < AD_DATASZ_DOT_UND) {
668 DBG_ERR("bad buffer size [0x%" PRIx32 "]\n", bufsize);
672 if (offset + ADEDLEN_MAGIC < offset ||
673 offset + ADEDLEN_MAGIC >= bufsize) {
676 RSIVAL(ad->ad_data, offset, ad->ad_magic);
677 offset += ADEDLEN_MAGIC;
679 if (offset + ADEDLEN_VERSION < offset ||
680 offset + ADEDLEN_VERSION >= bufsize) {
683 RSIVAL(ad->ad_data, offset, ad->ad_version);
684 offset += ADEDLEN_VERSION;
686 if (offset + ADEDLEN_FILLER < offset ||
687 offset + ADEDLEN_FILLER >= bufsize) {
690 if (ad->ad_type == ADOUBLE_RSRC) {
691 memcpy(ad->ad_data + offset, AD_FILLER_TAG, ADEDLEN_FILLER);
693 offset += ADEDLEN_FILLER;
695 if (offset + ADEDLEN_NENTRIES < offset ||
696 offset + ADEDLEN_NENTRIES >= bufsize) {
699 offset += ADEDLEN_NENTRIES;
701 for (eid = 0, nent = 0; eid < ADEID_MAX; eid++) {
702 if (ad->ad_eid[eid].ade_off == 0) {
704 * ade_off is also used as indicator whether a
705 * specific entry is used or not
710 if (offset + AD_ENTRY_LEN_EID < offset ||
711 offset + AD_ENTRY_LEN_EID >= bufsize) {
714 RSIVAL(ad->ad_data, offset, AD_EID_DISK(eid));
715 offset += AD_ENTRY_LEN_EID;
717 if (offset + AD_ENTRY_LEN_OFF < offset ||
718 offset + AD_ENTRY_LEN_OFF >= bufsize) {
721 RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_off);
722 offset += AD_ENTRY_LEN_OFF;
724 if (offset + AD_ENTRY_LEN_LEN < offset ||
725 offset + AD_ENTRY_LEN_LEN >= bufsize) {
728 RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_len);
729 offset += AD_ENTRY_LEN_LEN;
734 if (ADEDOFF_NENTRIES + 2 >= bufsize) {
737 RSSVAL(ad->ad_data, ADEDOFF_NENTRIES, nent);
742 static bool ad_unpack_xattrs(struct adouble *ad)
744 struct ad_xattr_header *h = &ad->adx_header;
745 const char *p = ad->ad_data;
749 if (ad_getentrylen(ad, ADEID_FINDERI) <= ADEDLEN_FINDERI) {
753 /* 2 bytes padding */
754 hoff = ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI + 2;
756 h->adx_magic = RIVAL(p, hoff + 0);
757 h->adx_debug_tag = RIVAL(p, hoff + 4); /* Not used -> not checked */
758 h->adx_total_size = RIVAL(p, hoff + 8);
759 h->adx_data_start = RIVAL(p, hoff + 12);
760 h->adx_data_length = RIVAL(p, hoff + 16);
761 h->adx_flags = RSVAL(p, hoff + 32); /* Not used -> not checked */
762 h->adx_num_attrs = RSVAL(p, hoff + 34);
764 if (h->adx_magic != AD_XATTR_HDR_MAGIC) {
765 DBG_ERR("Bad magic: 0x%" PRIx32 "\n", h->adx_magic);
769 if (h->adx_total_size > ad_getentryoff(ad, ADEID_RFORK)) {
770 DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size);
773 if (h->adx_total_size > AD_XATTR_MAX_HDR_SIZE) {
774 DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size);
778 if (h->adx_data_start < (hoff + AD_XATTR_HDR_SIZE)) {
779 DBG_ERR("Bad start: 0x%" PRIx32 "\n", h->adx_data_start);
783 if ((h->adx_data_start + h->adx_data_length) < h->adx_data_start) {
784 DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length);
787 if ((h->adx_data_start + h->adx_data_length) >
788 ad->adx_header.adx_total_size)
790 DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length);
794 if (h->adx_num_attrs > AD_XATTR_MAX_ENTRIES) {
795 DBG_ERR("Bad num xattrs: %" PRIu16 "\n", h->adx_num_attrs);
799 if (h->adx_num_attrs == 0) {
803 ad->adx_entries = talloc_zero_array(
804 ad, struct ad_xattr_entry, h->adx_num_attrs);
805 if (ad->adx_entries == NULL) {
809 hoff += AD_XATTR_HDR_SIZE;
811 for (i = 0; i < h->adx_num_attrs; i++) {
812 struct ad_xattr_entry *e = &ad->adx_entries[i];
814 hoff = (hoff + 3) & ~3;
816 e->adx_offset = RIVAL(p, hoff + 0);
817 e->adx_length = RIVAL(p, hoff + 4);
818 e->adx_flags = RSVAL(p, hoff + 8);
819 e->adx_namelen = *(p + hoff + 10);
821 if (e->adx_offset >= ad->adx_header.adx_total_size) {
822 DBG_ERR("Bad adx_offset: %" PRIx32 "\n",
827 if ((e->adx_offset + e->adx_length) < e->adx_offset) {
828 DBG_ERR("Bad adx_length: %" PRIx32 "\n",
833 if ((e->adx_offset + e->adx_length) >
834 ad->adx_header.adx_total_size)
836 DBG_ERR("Bad adx_length: %" PRIx32 "\n",
841 if (e->adx_namelen == 0) {
842 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
846 if ((hoff + 11 + e->adx_namelen) < hoff + 11) {
847 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
851 if ((hoff + 11 + e->adx_namelen) >
852 ad->adx_header.adx_data_start)
854 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
859 e->adx_name = talloc_strndup(ad->adx_entries,
862 if (e->adx_name == NULL) {
866 DBG_DEBUG("xattr [%s] offset [0x%x] size [0x%x]\n",
867 e->adx_name, e->adx_offset, e->adx_length);
868 dump_data(10, (uint8_t *)(ad->ad_data + e->adx_offset),
871 hoff += 11 + e->adx_namelen;
878 * Unpack an AppleDouble blob into a struct adoble
880 static bool ad_unpack(struct adouble *ad, const size_t nentries,
883 size_t bufsize = talloc_get_size(ad->ad_data);
885 uint32_t eid, len, off;
889 * The size of the buffer ad->ad_data is checked when read, so
890 * we wouldn't have to check our own offsets, a few extra
891 * checks won't hurt though. We have to check the offsets we
892 * read from the buffer anyway.
895 if (bufsize < (AD_HEADER_LEN + (AD_ENTRY_LEN * nentries))) {
896 DEBUG(1, ("bad size\n"));
900 ad->ad_magic = RIVAL(ad->ad_data, 0);
901 ad->ad_version = RIVAL(ad->ad_data, ADEDOFF_VERSION);
902 if ((ad->ad_magic != AD_MAGIC) || (ad->ad_version != AD_VERSION)) {
903 DEBUG(1, ("wrong magic or version\n"));
907 memcpy(ad->ad_filler, ad->ad_data + ADEDOFF_FILLER, ADEDLEN_FILLER);
909 adentries = RSVAL(ad->ad_data, ADEDOFF_NENTRIES);
910 if (adentries != nentries) {
911 DEBUG(1, ("invalid number of entries: %zu\n",
916 /* now, read in the entry bits */
917 for (i = 0; i < adentries; i++) {
918 eid = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN));
920 off = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 4);
921 len = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 8);
923 if (!eid || eid >= ADEID_MAX) {
924 DEBUG(1, ("bogus eid %d\n", eid));
929 * All entries other than the resource fork are
930 * expected to be read into the ad_data buffer, so
931 * ensure the specified offset is within that bound
933 if ((off > bufsize) && (eid != ADEID_RFORK)) {
934 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
940 * All entries besides FinderInfo and resource fork
941 * must fit into the buffer. FinderInfo is special as
942 * it may be larger then the default 32 bytes (if it
943 * contains marshalled xattrs), but we will fixup that
944 * in ad_convert(). And the resource fork is never
945 * accessed directly by the ad_data buf (also see
946 * comment above) anyway.
948 if ((eid != ADEID_RFORK) &&
949 (eid != ADEID_FINDERI) &&
950 ((off + len) > bufsize)) {
951 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
957 * That would be obviously broken
959 if (off > filesize) {
960 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
966 * Check for any entry that has its end beyond the
969 if (off + len < off) {
970 DEBUG(1, ("offset wrap in eid %d: off: %" PRIu32
971 ", len: %" PRIu32 "\n",
976 if (off + len > filesize) {
978 * If this is the resource fork entry, we fix
979 * up the length, for any other entry we bail
982 if (eid != ADEID_RFORK) {
983 DEBUG(1, ("bogus eid %d: off: %" PRIu32
984 ", len: %" PRIu32 "\n",
990 * Fixup the resource fork entry by limiting
991 * the size to entryoffset - filesize.
993 len = filesize - off;
994 DEBUG(1, ("Limiting ADEID_RFORK: off: %" PRIu32
995 ", len: %" PRIu32 "\n", off, len));
998 ad->ad_eid[eid].ade_off = off;
999 ad->ad_eid[eid].ade_len = len;
1002 ok = ad_unpack_xattrs(ad);
1010 static bool ad_convert_move_reso(struct adouble *ad,
1011 const struct smb_filename *smb_fname)
1013 char *map = MAP_FAILED;
1019 if (ad_getentrylen(ad, ADEID_RFORK) == 0) {
1023 maplen = ad_getentryoff(ad, ADEID_RFORK) +
1024 ad_getentrylen(ad, ADEID_RFORK);
1026 /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
1027 map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
1029 if (map == MAP_FAILED) {
1030 DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
1035 memmove(map + ADEDOFF_RFORK_DOT_UND,
1036 map + ad_getentryoff(ad, ADEID_RFORK),
1037 ad_getentrylen(ad, ADEID_RFORK));
1039 rc = munmap(map, maplen);
1041 DBG_ERR("munmap failed: %s\n", strerror(errno));
1045 ad_setentryoff(ad, ADEID_RFORK, ADEDOFF_RFORK_DOT_UND);
1049 DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name);
1053 len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
1054 if (len != AD_DATASZ_DOT_UND) {
1055 DBG_ERR("%s: bad size: %zd\n", smb_fname->base_name, len);
1062 static bool ad_convert_xattr(struct adouble *ad,
1063 const struct smb_filename *smb_fname,
1064 bool *converted_xattr)
1066 static struct char_mappings **string_replace_cmaps = NULL;
1067 char *map = MAP_FAILED;
1071 int saved_errno = 0;
1076 *converted_xattr = false;
1078 if (ad_getentrylen(ad, ADEID_FINDERI) == ADEDLEN_FINDERI) {
1082 if (string_replace_cmaps == NULL) {
1083 const char **mappings = NULL;
1085 mappings = str_list_make_v3_const(
1086 talloc_tos(), fruit_catia_maps, NULL);
1087 if (mappings == NULL) {
1090 string_replace_cmaps = string_replace_init_map(mappings);
1091 TALLOC_FREE(mappings);
1094 maplen = ad_getentryoff(ad, ADEID_RFORK) +
1095 ad_getentrylen(ad, ADEID_RFORK);
1097 /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
1098 map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
1100 if (map == MAP_FAILED) {
1101 DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
1105 for (i = 0; i < ad->adx_header.adx_num_attrs; i++) {
1106 struct ad_xattr_entry *e = &ad->adx_entries[i];
1107 char *mapped_name = NULL;
1109 struct smb_filename *stream_name = NULL;
1110 files_struct *fsp = NULL;
1113 status = string_replace_allocate(ad->ad_handle->conn,
1115 string_replace_cmaps,
1118 vfs_translate_to_windows);
1119 if (!NT_STATUS_IS_OK(status) &&
1120 !NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))
1122 DBG_ERR("string_replace_allocate failed\n");
1128 mapped_name = talloc_asprintf(talloc_tos(), ":%s", tmp);
1130 if (mapped_name == NULL) {
1135 stream_name = synthetic_smb_fname(talloc_tos(),
1136 smb_fname->base_name,
1140 TALLOC_FREE(mapped_name);
1141 if (stream_name == NULL) {
1142 DBG_ERR("synthetic_smb_fname failed\n");
1147 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1149 status = SMB_VFS_CREATE_FILE(
1150 ad->ad_handle->conn, /* conn */
1152 0, /* root_dir_fid */
1153 stream_name, /* fname */
1154 FILE_GENERIC_WRITE, /* access_mask */
1155 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
1156 FILE_OPEN_IF, /* create_disposition */
1157 0, /* create_options */
1158 0, /* file_attributes */
1159 INTERNAL_OPEN_ONLY, /* oplock_request */
1161 0, /* allocation_size */
1162 0, /* private_flags */
1167 NULL, NULL); /* create context */
1168 TALLOC_FREE(stream_name);
1169 if (!NT_STATUS_IS_OK(status)) {
1170 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1175 nwritten = SMB_VFS_PWRITE(fsp,
1176 map + e->adx_offset,
1179 if (nwritten == -1) {
1180 DBG_ERR("SMB_VFS_PWRITE failed\n");
1181 saved_errno = errno;
1182 close_file(NULL, fsp, ERROR_CLOSE);
1183 errno = saved_errno;
1188 status = close_file(NULL, fsp, NORMAL_CLOSE);
1189 if (!NT_STATUS_IS_OK(status)) {
1196 ad_setentrylen(ad, ADEID_FINDERI, ADEDLEN_FINDERI);
1200 DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name);
1204 len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
1205 if (len != AD_DATASZ_DOT_UND) {
1206 DBG_ERR("%s: bad size: %zd\n", smb_fname->base_name, len);
1211 ok = ad_convert_move_reso(ad, smb_fname);
1216 *converted_xattr = true;
1220 rc = munmap(map, maplen);
1222 DBG_ERR("munmap failed: %s\n", strerror(errno));
1229 static bool ad_convert_finderinfo(struct adouble *ad,
1230 const struct smb_filename *smb_fname)
1235 struct smb_filename *stream_name = NULL;
1236 files_struct *fsp = NULL;
1240 int saved_errno = 0;
1243 cmp = memcmp(ad->ad_filler, AD_FILLER_TAG_OSX, ADEDLEN_FILLER);
1248 p_ad = ad_get_entry(ad, ADEID_FINDERI);
1253 ai = afpinfo_new(talloc_tos());
1258 memcpy(ai->afpi_FinderInfo, p_ad, ADEDLEN_FINDERI);
1260 aiblob = data_blob_talloc(talloc_tos(), NULL, AFP_INFO_SIZE);
1261 if (aiblob.data == NULL) {
1266 size = afpinfo_pack(ai, (char *)aiblob.data);
1268 if (size != AFP_INFO_SIZE) {
1272 stream_name = synthetic_smb_fname(talloc_tos(),
1273 smb_fname->base_name,
1277 if (stream_name == NULL) {
1278 data_blob_free(&aiblob);
1279 DBG_ERR("synthetic_smb_fname failed\n");
1283 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1285 status = SMB_VFS_CREATE_FILE(
1286 ad->ad_handle->conn, /* conn */
1288 0, /* root_dir_fid */
1289 stream_name, /* fname */
1290 FILE_GENERIC_WRITE, /* access_mask */
1291 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
1292 FILE_OPEN_IF, /* create_disposition */
1293 0, /* create_options */
1294 0, /* file_attributes */
1295 INTERNAL_OPEN_ONLY, /* oplock_request */
1297 0, /* allocation_size */
1298 0, /* private_flags */
1303 NULL, NULL); /* create context */
1304 TALLOC_FREE(stream_name);
1305 if (!NT_STATUS_IS_OK(status)) {
1306 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1310 nwritten = SMB_VFS_PWRITE(fsp,
1314 if (nwritten == -1) {
1315 DBG_ERR("SMB_VFS_PWRITE failed\n");
1316 saved_errno = errno;
1317 close_file(NULL, fsp, ERROR_CLOSE);
1318 errno = saved_errno;
1322 status = close_file(NULL, fsp, NORMAL_CLOSE);
1323 if (!NT_STATUS_IS_OK(status)) {
1331 static bool ad_convert_truncate(struct adouble *ad,
1332 const struct smb_filename *smb_fname)
1337 * FIXME: direct ftruncate(), but we don't have a fsp for the
1340 rc = ftruncate(ad->ad_fd, ADEDOFF_RFORK_DOT_UND +
1341 ad_getentrylen(ad, ADEID_RFORK));
1349 static bool ad_convert_blank_rfork(struct adouble *ad,
1352 struct fruit_config_data *config = NULL;
1353 uint8_t *map = MAP_FAILED;
1362 SMB_VFS_HANDLE_GET_DATA(ad->ad_handle, config,
1363 struct fruit_config_data, return false);
1365 if (!config->wipe_intentionally_left_blank_rfork) {
1369 if (ad_getentrylen(ad, ADEID_RFORK) != sizeof(empty_resourcefork)) {
1373 maplen = ad_getentryoff(ad, ADEID_RFORK) +
1374 ad_getentrylen(ad, ADEID_RFORK);
1376 /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
1377 map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
1379 if (map == MAP_FAILED) {
1380 DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
1384 cmp = memcmp(map + ADEDOFF_RFORK_DOT_UND,
1386 sizeof(empty_resourcefork));
1387 rc = munmap(map, maplen);
1389 DBG_ERR("munmap failed: %s\n", strerror(errno));
1397 ad_setentrylen(ad, ADEID_RFORK, 0);
1404 len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
1405 if (len != AD_DATASZ_DOT_UND) {
1413 static bool ad_convert_delete_adfile(struct adouble *ad,
1414 const struct smb_filename *smb_fname)
1416 struct fruit_config_data *config = NULL;
1417 struct smb_filename *ad_name = NULL;
1420 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
1424 SMB_VFS_HANDLE_GET_DATA(ad->ad_handle, config,
1425 struct fruit_config_data, return false);
1427 if (!config->delete_empty_adfiles) {
1431 rc = adouble_path(talloc_tos(), smb_fname, &ad_name);
1436 rc = SMB_VFS_NEXT_UNLINK(ad->ad_handle, ad_name);
1438 DBG_ERR("Unlinking [%s] failed: %s\n",
1439 smb_fname_str_dbg(ad_name), strerror(errno));
1440 TALLOC_FREE(ad_name);
1444 DBG_WARNING("Unlinked [%s] after conversion\n", smb_fname_str_dbg(ad_name));
1445 TALLOC_FREE(ad_name);
1451 * Convert from Apple's ._ file to Netatalk
1453 * Apple's AppleDouble may contain a FinderInfo entry longer then 32
1454 * bytes containing packed xattrs.
1456 * @return -1 in case an error occurred, 0 if no conversion was done, 1
1459 static int ad_convert(struct vfs_handle_struct *handle,
1460 const struct smb_filename *smb_fname)
1462 struct adouble *ad = NULL;
1464 bool converted_xattr = false;
1468 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
1473 ok = ad_convert_xattr(ad, smb_fname, &converted_xattr);
1479 ok = ad_convert_blank_rfork(ad, &blank);
1485 if (converted_xattr || blank) {
1486 ok = ad_convert_truncate(ad, smb_fname);
1493 ok = ad_convert_finderinfo(ad, smb_fname);
1495 DBG_ERR("Failed to convert [%s]\n",
1496 smb_fname_str_dbg(smb_fname));
1501 ok = ad_convert_delete_adfile(ad, smb_fname);
1514 * Read and parse Netatalk AppleDouble metadata xattr
1516 static ssize_t ad_read_meta(vfs_handle_struct *handle,
1518 const struct smb_filename *smb_fname)
1524 DEBUG(10, ("reading meta xattr for %s\n", smb_fname->base_name));
1526 ealen = SMB_VFS_GETXATTR(handle->conn, smb_fname,
1527 AFPINFO_EA_NETATALK, ad->ad_data,
1533 if (errno == ENOATTR) {
1539 DEBUG(2, ("error reading meta xattr: %s\n",
1545 if (ealen != AD_DATASZ_XATTR) {
1546 DEBUG(2, ("bad size %zd\n", ealen));
1552 /* Now parse entries */
1553 ok = ad_unpack(ad, ADEID_NUM_XATTR, AD_DATASZ_XATTR);
1555 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1561 if (!ad_getentryoff(ad, ADEID_FINDERI)
1562 || !ad_getentryoff(ad, ADEID_COMMENT)
1563 || !ad_getentryoff(ad, ADEID_FILEDATESI)
1564 || !ad_getentryoff(ad, ADEID_AFPFILEI)
1565 || !ad_getentryoff(ad, ADEID_PRIVDEV)
1566 || !ad_getentryoff(ad, ADEID_PRIVINO)
1567 || !ad_getentryoff(ad, ADEID_PRIVSYN)
1568 || !ad_getentryoff(ad, ADEID_PRIVID)) {
1569 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1576 DEBUG(10, ("reading meta xattr for %s, rc: %d\n",
1577 smb_fname->base_name, rc));
1581 if (errno == EINVAL) {
1583 removexattr(smb_fname->base_name, AFPINFO_EA_NETATALK);
1591 static int ad_open_rsrc_xattr(const struct smb_filename *smb_fname,
1595 #ifdef HAVE_ATTROPEN
1596 /* FIXME: direct Solaris xattr syscall */
1597 return attropen(smb_fname->base_name,
1598 AFPRESOURCE_EA_NETATALK, flags, mode);
1605 static int ad_open_rsrc_adouble(const struct smb_filename *smb_fname,
1611 struct smb_filename *adp_smb_fname = NULL;
1613 ret = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
1618 fd = open(adp_smb_fname->base_name, flags, mode);
1619 TALLOC_FREE(adp_smb_fname);
1624 static int ad_open_rsrc(vfs_handle_struct *handle,
1625 const struct smb_filename *smb_fname,
1629 struct fruit_config_data *config = NULL;
1632 SMB_VFS_HANDLE_GET_DATA(handle, config,
1633 struct fruit_config_data, return -1);
1635 if (config->rsrc == FRUIT_RSRC_XATTR) {
1636 fd = ad_open_rsrc_xattr(smb_fname, flags, mode);
1638 fd = ad_open_rsrc_adouble(smb_fname, flags, mode);
1645 * Here's the deal: for ADOUBLE_META we can do without an fd as we can issue
1646 * path based xattr calls. For ADOUBLE_RSRC however we need a full-fledged fd
1647 * for file IO on the ._ file.
1649 static int ad_open(vfs_handle_struct *handle,
1652 const struct smb_filename *smb_fname,
1658 DBG_DEBUG("Path [%s] type [%s]\n", smb_fname->base_name,
1659 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
1661 if (ad->ad_type == ADOUBLE_META) {
1665 if ((fsp != NULL) && (fsp->fh != NULL) && (fsp->fh->fd != -1)) {
1666 ad->ad_fd = fsp->fh->fd;
1667 ad->ad_opened = false;
1671 fd = ad_open_rsrc(handle, smb_fname, flags, mode);
1675 ad->ad_opened = true;
1678 DBG_DEBUG("Path [%s] type [%s] fd [%d]\n",
1679 smb_fname->base_name,
1680 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc", fd);
1685 static ssize_t ad_read_rsrc_xattr(struct adouble *ad)
1690 /* FIXME: direct sys_fstat(), don't have an fsp */
1691 ret = sys_fstat(ad->ad_fd, &st,
1692 lp_fake_directory_create_times(
1693 SNUM(ad->ad_handle->conn)));
1698 ad_setentrylen(ad, ADEID_RFORK, st.st_ex_size);
1699 return st.st_ex_size;
1702 static ssize_t ad_read_rsrc_adouble(struct adouble *ad,
1703 const struct smb_filename *smb_fname)
1705 SMB_STRUCT_STAT sbuf;
1712 ret = sys_fstat(ad->ad_fd, &sbuf, lp_fake_directory_create_times(
1713 SNUM(ad->ad_handle->conn)));
1719 * AppleDouble file header content and size, two cases:
1721 * - without xattrs it is exactly AD_DATASZ_DOT_UND (82) bytes large
1722 * - with embedded xattrs it can be larger, up to AD_XATTR_MAX_HDR_SIZE
1724 * Read as much as we can up to AD_XATTR_MAX_HDR_SIZE.
1726 size = sbuf.st_ex_size;
1727 if (size > talloc_array_length(ad->ad_data)) {
1728 if (size > AD_XATTR_MAX_HDR_SIZE) {
1729 size = AD_XATTR_MAX_HDR_SIZE;
1731 p_ad = talloc_realloc(ad, ad->ad_data, char, size);
1738 len = sys_pread(ad->ad_fd, ad->ad_data,
1739 talloc_array_length(ad->ad_data), 0);
1740 if (len != talloc_array_length(ad->ad_data)) {
1741 DBG_NOTICE("%s %s: bad size: %zd\n",
1742 smb_fname->base_name, strerror(errno), len);
1746 /* Now parse entries */
1747 ok = ad_unpack(ad, ADEID_NUM_DOT_UND, sbuf.st_ex_size);
1749 DBG_ERR("invalid AppleDouble resource %s\n",
1750 smb_fname->base_name);
1755 if ((ad_getentryoff(ad, ADEID_FINDERI) != ADEDOFF_FINDERI_DOT_UND)
1756 || (ad_getentrylen(ad, ADEID_FINDERI) < ADEDLEN_FINDERI)
1757 || (ad_getentryoff(ad, ADEID_RFORK) < ADEDOFF_RFORK_DOT_UND)) {
1758 DBG_ERR("invalid AppleDouble resource %s\n",
1759 smb_fname->base_name);
1768 * Read and parse resource fork, either ._ AppleDouble file or xattr
1770 static ssize_t ad_read_rsrc(struct adouble *ad,
1771 const struct smb_filename *smb_fname)
1773 struct fruit_config_data *config = NULL;
1776 SMB_VFS_HANDLE_GET_DATA(ad->ad_handle, config,
1777 struct fruit_config_data, return -1);
1779 if (config->rsrc == FRUIT_RSRC_XATTR) {
1780 len = ad_read_rsrc_xattr(ad);
1782 len = ad_read_rsrc_adouble(ad, smb_fname);
1789 * Read and unpack an AppleDouble metadata xattr or resource
1791 static ssize_t ad_read(vfs_handle_struct *handle,
1793 const struct smb_filename *smb_fname)
1795 switch (ad->ad_type) {
1797 return ad_read_meta(handle, ad, smb_fname);
1799 return ad_read_rsrc(ad, smb_fname);
1805 static int adouble_destructor(struct adouble *ad)
1807 if ((ad->ad_fd != -1) && ad->ad_opened) {
1815 * Allocate a struct adouble without initialiing it
1817 * The struct is either hang of the fsp extension context or if fsp is
1820 * @param[in] ctx talloc context
1821 * @param[in] handle vfs handle
1822 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1824 * @return adouble handle
1826 static struct adouble *ad_alloc(TALLOC_CTX *ctx, vfs_handle_struct *handle,
1827 adouble_type_t type)
1832 struct fruit_config_data *config;
1834 SMB_VFS_HANDLE_GET_DATA(handle, config,
1835 struct fruit_config_data, return NULL);
1839 adsize = AD_DATASZ_XATTR;
1842 if (config->rsrc == FRUIT_RSRC_ADFILE) {
1843 adsize = AD_DATASZ_DOT_UND;
1850 ad = talloc_zero(ctx, struct adouble);
1857 ad->ad_data = talloc_zero_array(ad, char, adsize);
1858 if (ad->ad_data == NULL) {
1864 ad->ad_handle = handle;
1866 ad->ad_magic = AD_MAGIC;
1867 ad->ad_version = AD_VERSION;
1870 talloc_set_destructor(ad, adouble_destructor);
1880 * Allocate and initialize a new struct adouble
1882 * @param[in] ctx talloc context
1883 * @param[in] handle vfs handle
1884 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1886 * @return adouble handle, initialized
1888 static struct adouble *ad_init(TALLOC_CTX *ctx, vfs_handle_struct *handle,
1889 adouble_type_t type)
1892 const struct ad_entry_order *eid;
1893 struct adouble *ad = NULL;
1894 struct fruit_config_data *config;
1895 time_t t = time(NULL);
1897 SMB_VFS_HANDLE_GET_DATA(handle, config,
1898 struct fruit_config_data, return NULL);
1902 eid = entry_order_meta_xattr;
1905 if (config->rsrc == FRUIT_RSRC_ADFILE) {
1906 eid = entry_order_dot_und;
1908 eid = entry_order_rsrc_xattr;
1915 ad = ad_alloc(ctx, handle, type);
1921 ad->ad_eid[eid->id].ade_off = eid->offset;
1922 ad->ad_eid[eid->id].ade_len = eid->len;
1926 /* put something sane in the date fields */
1927 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, t);
1928 ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, t);
1929 ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, t);
1930 ad_setdate(ad, AD_DATE_BACKUP, htonl(AD_DATE_START));
1938 static struct adouble *ad_get_internal(TALLOC_CTX *ctx,
1939 vfs_handle_struct *handle,
1941 const struct smb_filename *smb_fname,
1942 adouble_type_t type)
1946 struct adouble *ad = NULL;
1950 smb_fname = fsp->base_fsp->fsp_name;
1953 DEBUG(10, ("ad_get(%s) called for %s\n",
1954 type == ADOUBLE_META ? "meta" : "rsrc",
1955 smb_fname->base_name));
1957 ad = ad_alloc(ctx, handle, type);
1963 /* Try rw first so we can use the fd in ad_convert() */
1966 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
1967 if (rc == -1 && ((errno == EROFS) || (errno == EACCES))) {
1969 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
1972 DBG_DEBUG("ad_open [%s] error [%s]\n",
1973 smb_fname->base_name, strerror(errno));
1978 len = ad_read(handle, ad, smb_fname);
1980 DEBUG(10, ("error reading AppleDouble for %s\n",
1981 smb_fname->base_name));
1987 DEBUG(10, ("ad_get(%s) for %s returning %d\n",
1988 type == ADOUBLE_META ? "meta" : "rsrc",
1989 smb_fname->base_name, rc));
1998 * Return AppleDouble data for a file
2000 * @param[in] ctx talloc context
2001 * @param[in] handle vfs handle
2002 * @param[in] smb_fname pathname to file or directory
2003 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2005 * @return talloced struct adouble or NULL on error
2007 static struct adouble *ad_get(TALLOC_CTX *ctx,
2008 vfs_handle_struct *handle,
2009 const struct smb_filename *smb_fname,
2010 adouble_type_t type)
2012 return ad_get_internal(ctx, handle, NULL, smb_fname, type);
2016 * Return AppleDouble data for a file
2018 * @param[in] ctx talloc context
2019 * @param[in] handle vfs handle
2020 * @param[in] fsp fsp to use for IO
2021 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2023 * @return talloced struct adouble or NULL on error
2025 static struct adouble *ad_fget(TALLOC_CTX *ctx, vfs_handle_struct *handle,
2026 files_struct *fsp, adouble_type_t type)
2028 return ad_get_internal(ctx, handle, fsp, NULL, type);
2032 * Set AppleDouble metadata on a file or directory
2034 * @param[in] ad adouble handle
2036 * @param[in] smb_fname pathname to file or directory
2038 * @return status code, 0 means success
2040 static int ad_set(vfs_handle_struct *handle,
2042 const struct smb_filename *smb_fname)
2047 DBG_DEBUG("Path [%s]\n", smb_fname->base_name);
2049 if (ad->ad_type != ADOUBLE_META) {
2050 DBG_ERR("ad_set on [%s] used with ADOUBLE_RSRC\n",
2051 smb_fname->base_name);
2060 ret = SMB_VFS_SETXATTR(handle->conn,
2062 AFPINFO_EA_NETATALK,
2064 AD_DATASZ_XATTR, 0);
2066 DBG_DEBUG("Path [%s] ret [%d]\n", smb_fname->base_name, ret);
2072 * Set AppleDouble metadata on a file or directory
2074 * @param[in] ad adouble handle
2075 * @param[in] fsp file handle
2077 * @return status code, 0 means success
2079 static int ad_fset(struct vfs_handle_struct *handle,
2087 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
2090 || (fsp->fh == NULL)
2091 || (fsp->fh->fd == -1))
2093 smb_panic("bad fsp");
2101 switch (ad->ad_type) {
2103 rc = SMB_VFS_NEXT_SETXATTR(handle,
2105 AFPINFO_EA_NETATALK,
2107 AD_DATASZ_XATTR, 0);
2111 len = SMB_VFS_NEXT_PWRITE(handle,
2116 if (len != AD_DATASZ_DOT_UND) {
2117 DBG_ERR("short write on %s: %zd", fsp_str_dbg(fsp), len);
2127 DBG_DEBUG("Path [%s] rc [%d]\n", fsp_str_dbg(fsp), rc);
2132 /*****************************************************************************
2134 *****************************************************************************/
2136 static bool is_afpinfo_stream(const struct smb_filename *smb_fname)
2138 if (strncasecmp_m(smb_fname->stream_name,
2139 AFPINFO_STREAM_NAME,
2140 strlen(AFPINFO_STREAM_NAME)) == 0) {
2146 static bool is_afpresource_stream(const struct smb_filename *smb_fname)
2148 if (strncasecmp_m(smb_fname->stream_name,
2149 AFPRESOURCE_STREAM_NAME,
2150 strlen(AFPRESOURCE_STREAM_NAME)) == 0) {
2157 * Test whether stream is an Apple stream.
2159 static bool is_apple_stream(const struct smb_filename *smb_fname)
2161 if (is_afpinfo_stream(smb_fname)) {
2164 if (is_afpresource_stream(smb_fname)) {
2171 * Initialize config struct from our smb.conf config parameters
2173 static int init_fruit_config(vfs_handle_struct *handle)
2175 struct fruit_config_data *config;
2177 const char *tm_size_str = NULL;
2179 config = talloc_zero(handle->conn, struct fruit_config_data);
2181 DEBUG(1, ("talloc_zero() failed\n"));
2187 * Versions up to Samba 4.5.x had a spelling bug in the
2188 * fruit:resource option calling lp_parm_enum with
2189 * "res*s*ource" (ie two s).
2191 * In Samba 4.6 we accept both the wrong and the correct
2192 * spelling, in Samba 4.7 the bad spelling will be removed.
2194 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2195 "ressource", fruit_rsrc, FRUIT_RSRC_ADFILE);
2196 if (enumval == -1) {
2197 DEBUG(1, ("value for %s: resource type unknown\n",
2198 FRUIT_PARAM_TYPE_NAME));
2201 config->rsrc = (enum fruit_rsrc)enumval;
2203 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2204 "resource", fruit_rsrc, enumval);
2205 if (enumval == -1) {
2206 DEBUG(1, ("value for %s: resource type unknown\n",
2207 FRUIT_PARAM_TYPE_NAME));
2210 config->rsrc = (enum fruit_rsrc)enumval;
2212 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2213 "metadata", fruit_meta, FRUIT_META_NETATALK);
2214 if (enumval == -1) {
2215 DEBUG(1, ("value for %s: metadata type unknown\n",
2216 FRUIT_PARAM_TYPE_NAME));
2219 config->meta = (enum fruit_meta)enumval;
2221 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2222 "locking", fruit_locking, FRUIT_LOCKING_NONE);
2223 if (enumval == -1) {
2224 DEBUG(1, ("value for %s: locking type unknown\n",
2225 FRUIT_PARAM_TYPE_NAME));
2228 config->locking = (enum fruit_locking)enumval;
2230 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2231 "encoding", fruit_encoding, FRUIT_ENC_PRIVATE);
2232 if (enumval == -1) {
2233 DEBUG(1, ("value for %s: encoding type unknown\n",
2234 FRUIT_PARAM_TYPE_NAME));
2237 config->encoding = (enum fruit_encoding)enumval;
2239 if (config->rsrc == FRUIT_RSRC_ADFILE) {
2240 config->veto_appledouble = lp_parm_bool(SNUM(handle->conn),
2241 FRUIT_PARAM_TYPE_NAME,
2246 config->use_aapl = lp_parm_bool(
2247 -1, FRUIT_PARAM_TYPE_NAME, "aapl", true);
2249 config->time_machine = lp_parm_bool(
2250 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "time machine", false);
2252 config->unix_info_enabled = lp_parm_bool(
2253 -1, FRUIT_PARAM_TYPE_NAME, "nfs_aces", true);
2255 config->use_copyfile = lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME,
2258 config->posix_rename = lp_parm_bool(
2259 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "posix_rename", true);
2261 config->aapl_zero_file_id =
2262 lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME, "zero_file_id", true);
2264 config->readdir_attr_rsize = lp_parm_bool(
2265 SNUM(handle->conn), "readdir_attr", "aapl_rsize", true);
2267 config->readdir_attr_finder_info = lp_parm_bool(
2268 SNUM(handle->conn), "readdir_attr", "aapl_finder_info", true);
2270 config->readdir_attr_max_access = lp_parm_bool(
2271 SNUM(handle->conn), "readdir_attr", "aapl_max_access", true);
2273 config->model = lp_parm_const_string(
2274 -1, FRUIT_PARAM_TYPE_NAME, "model", "MacSamba");
2276 tm_size_str = lp_parm_const_string(
2277 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2278 "time machine max size", NULL);
2279 if (tm_size_str != NULL) {
2280 config->time_machine_max_size = conv_str_size(tm_size_str);
2283 config->wipe_intentionally_left_blank_rfork = lp_parm_bool(
2284 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2285 "wipe_intentionally_left_blank_rfork", false);
2287 config->delete_empty_adfiles = lp_parm_bool(
2288 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2289 "delete_empty_adfiles", false);
2291 SMB_VFS_HANDLE_SET_DATA(handle, config,
2292 NULL, struct fruit_config_data,
2299 * Prepend "._" to a basename
2300 * Return a new struct smb_filename with stream_name == NULL.
2302 static int adouble_path(TALLOC_CTX *ctx,
2303 const struct smb_filename *smb_fname_in,
2304 struct smb_filename **pp_smb_fname_out)
2308 struct smb_filename *smb_fname = cp_smb_filename(ctx,
2311 if (smb_fname == NULL) {
2315 /* We need streamname to be NULL */
2316 TALLOC_FREE(smb_fname->stream_name);
2318 /* And we're replacing base_name. */
2319 TALLOC_FREE(smb_fname->base_name);
2321 if (!parent_dirname(smb_fname, smb_fname_in->base_name,
2323 TALLOC_FREE(smb_fname);
2327 smb_fname->base_name = talloc_asprintf(smb_fname,
2328 "%s/._%s", parent, base);
2329 if (smb_fname->base_name == NULL) {
2330 TALLOC_FREE(smb_fname);
2334 *pp_smb_fname_out = smb_fname;
2340 * Allocate and initialize an AfpInfo struct
2342 static AfpInfo *afpinfo_new(TALLOC_CTX *ctx)
2344 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2348 ai->afpi_Signature = AFP_Signature;
2349 ai->afpi_Version = AFP_Version;
2350 ai->afpi_BackupTime = AD_DATE_START;
2355 * Pack an AfpInfo struct into a buffer
2357 * Buffer size must be at least AFP_INFO_SIZE
2358 * Returns size of packed buffer
2360 static ssize_t afpinfo_pack(const AfpInfo *ai, char *buf)
2362 memset(buf, 0, AFP_INFO_SIZE);
2364 RSIVAL(buf, 0, ai->afpi_Signature);
2365 RSIVAL(buf, 4, ai->afpi_Version);
2366 RSIVAL(buf, 12, ai->afpi_BackupTime);
2367 memcpy(buf + 16, ai->afpi_FinderInfo, sizeof(ai->afpi_FinderInfo));
2369 return AFP_INFO_SIZE;
2373 * Unpack a buffer into a AfpInfo structure
2375 * Buffer size must be at least AFP_INFO_SIZE
2376 * Returns allocated AfpInfo struct
2378 static AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data)
2380 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2385 ai->afpi_Signature = RIVAL(data, 0);
2386 ai->afpi_Version = RIVAL(data, 4);
2387 ai->afpi_BackupTime = RIVAL(data, 12);
2388 memcpy(ai->afpi_FinderInfo, (const char *)data + 16,
2389 sizeof(ai->afpi_FinderInfo));
2391 if (ai->afpi_Signature != AFP_Signature
2392 || ai->afpi_Version != AFP_Version) {
2393 DEBUG(1, ("Bad AfpInfo signature or version\n"));
2401 * Fake an inode number from the md5 hash of the (xattr) name
2403 static SMB_INO_T fruit_inode(const SMB_STRUCT_STAT *sbuf, const char *sname)
2405 gnutls_hash_hd_t hash_hnd = NULL;
2406 unsigned char hash[16];
2407 SMB_INO_T result = 0;
2411 DBG_DEBUG("fruit_inode called for %ju/%ju [%s]\n",
2412 (uintmax_t)sbuf->st_ex_dev,
2413 (uintmax_t)sbuf->st_ex_ino, sname);
2415 upper_sname = talloc_strdup_upper(talloc_tos(), sname);
2416 SMB_ASSERT(upper_sname != NULL);
2418 rc = gnutls_hash_init(&hash_hnd, GNUTLS_DIG_MD5);
2423 rc = gnutls_hash(hash_hnd, &(sbuf->st_ex_dev), sizeof(sbuf->st_ex_dev));
2425 gnutls_hash_deinit(hash_hnd, NULL);
2428 rc = gnutls_hash(hash_hnd,
2430 sizeof(sbuf->st_ex_ino));
2432 gnutls_hash_deinit(hash_hnd, NULL);
2435 rc = gnutls_hash(hash_hnd,
2437 talloc_get_size(upper_sname) - 1);
2439 gnutls_hash_deinit(hash_hnd, NULL);
2443 gnutls_hash_deinit(hash_hnd, hash);
2445 /* Hopefully all the variation is in the lower 4 (or 8) bytes! */
2446 memcpy(&result, hash, sizeof(result));
2449 DBG_DEBUG("fruit_inode \"%s\": ino=%ju\n",
2450 sname, (uintmax_t)result);
2453 TALLOC_FREE(upper_sname);
2458 static bool add_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
2459 struct stream_struct **streams,
2460 const char *name, off_t size,
2463 struct stream_struct *tmp;
2465 tmp = talloc_realloc(mem_ctx, *streams, struct stream_struct,
2471 tmp[*num_streams].name = talloc_asprintf(tmp, "%s:$DATA", name);
2472 if (tmp[*num_streams].name == NULL) {
2476 tmp[*num_streams].size = size;
2477 tmp[*num_streams].alloc_size = alloc_size;
2484 static bool filter_empty_rsrc_stream(unsigned int *num_streams,
2485 struct stream_struct **streams)
2487 struct stream_struct *tmp = *streams;
2490 if (*num_streams == 0) {
2494 for (i = 0; i < *num_streams; i++) {
2495 if (strequal_m(tmp[i].name, AFPRESOURCE_STREAM)) {
2500 if (i == *num_streams) {
2504 if (tmp[i].size > 0) {
2508 TALLOC_FREE(tmp[i].name);
2509 if (*num_streams - 1 > i) {
2510 memmove(&tmp[i], &tmp[i+1],
2511 (*num_streams - i - 1) * sizeof(struct stream_struct));
2518 static bool del_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
2519 struct stream_struct **streams,
2522 struct stream_struct *tmp = *streams;
2525 if (*num_streams == 0) {
2529 for (i = 0; i < *num_streams; i++) {
2530 if (strequal_m(tmp[i].name, name)) {
2535 if (i == *num_streams) {
2539 TALLOC_FREE(tmp[i].name);
2540 if (*num_streams - 1 > i) {
2541 memmove(&tmp[i], &tmp[i+1],
2542 (*num_streams - i - 1) * sizeof(struct stream_struct));
2549 static bool ad_empty_finderinfo(const struct adouble *ad)
2552 char emptybuf[ADEDLEN_FINDERI] = {0};
2555 fi = ad_get_entry(ad, ADEID_FINDERI);
2557 DBG_ERR("Missing FinderInfo in struct adouble [%p]\n", ad);
2561 cmp = memcmp(emptybuf, fi, ADEDLEN_FINDERI);
2565 static bool ai_empty_finderinfo(const AfpInfo *ai)
2568 char emptybuf[ADEDLEN_FINDERI] = {0};
2570 cmp = memcmp(emptybuf, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
2575 * Update btime with btime from Netatalk
2577 static void update_btime(vfs_handle_struct *handle,
2578 struct smb_filename *smb_fname)
2581 struct timespec creation_time = {0};
2583 struct fruit_config_data *config = NULL;
2585 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
2588 switch (config->meta) {
2589 case FRUIT_META_STREAM:
2591 case FRUIT_META_NETATALK:
2595 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
2599 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
2603 if (ad_getdate(ad, AD_DATE_UNIX | AD_DATE_CREATE, &t) != 0) {
2609 creation_time.tv_sec = convert_uint32_t_to_time_t(t);
2610 update_stat_ex_create_time(&smb_fname->st, creation_time);
2616 * Map an access mask to a Netatalk single byte byte range lock
2618 static off_t access_to_netatalk_brl(enum apple_fork fork_type,
2619 uint32_t access_mask)
2623 switch (access_mask) {
2624 case FILE_READ_DATA:
2625 offset = AD_FILELOCK_OPEN_RD;
2628 case FILE_WRITE_DATA:
2629 case FILE_APPEND_DATA:
2630 offset = AD_FILELOCK_OPEN_WR;
2634 offset = AD_FILELOCK_OPEN_NONE;
2638 if (fork_type == APPLE_FORK_RSRC) {
2639 if (offset == AD_FILELOCK_OPEN_NONE) {
2640 offset = AD_FILELOCK_RSRC_OPEN_NONE;
2650 * Map a deny mode to a Netatalk brl
2652 static off_t denymode_to_netatalk_brl(enum apple_fork fork_type,
2657 switch (deny_mode) {
2659 offset = AD_FILELOCK_DENY_RD;
2663 offset = AD_FILELOCK_DENY_WR;
2667 smb_panic("denymode_to_netatalk_brl: bad deny mode\n");
2670 if (fork_type == APPLE_FORK_RSRC) {
2678 * Call fcntl() with an exclusive F_GETLK request in order to
2679 * determine if there's an exisiting shared lock
2681 * @return true if the requested lock was found or any error occurred
2682 * false if the lock was not found
2684 static bool test_netatalk_lock(files_struct *fsp, off_t in_offset)
2687 off_t offset = in_offset;
2692 result = SMB_VFS_GETLOCK(fsp, &offset, &len, &type, &pid);
2693 if (result == false) {
2697 if (type != F_UNLCK) {
2704 static NTSTATUS fruit_check_access(vfs_handle_struct *handle,
2706 uint32_t access_mask,
2707 uint32_t share_mode)
2709 NTSTATUS status = NT_STATUS_OK;
2711 bool share_for_read = (share_mode & FILE_SHARE_READ);
2712 bool share_for_write = (share_mode & FILE_SHARE_WRITE);
2713 bool netatalk_already_open_for_reading = false;
2714 bool netatalk_already_open_for_writing = false;
2715 bool netatalk_already_open_with_deny_read = false;
2716 bool netatalk_already_open_with_deny_write = false;
2718 /* FIXME: hardcoded data fork, add resource fork */
2719 enum apple_fork fork_type = APPLE_FORK_DATA;
2721 DBG_DEBUG("fruit_check_access: %s, am: %s/%s, sm: 0x%x\n",
2723 access_mask & FILE_READ_DATA ? "READ" :"-",
2724 access_mask & FILE_WRITE_DATA ? "WRITE" : "-",
2727 if (fsp->fh->fd == -1) {
2728 return NT_STATUS_OK;
2731 /* Read NetATalk opens and deny modes on the file. */
2732 netatalk_already_open_for_reading = test_netatalk_lock(fsp,
2733 access_to_netatalk_brl(fork_type,
2736 netatalk_already_open_with_deny_read = test_netatalk_lock(fsp,
2737 denymode_to_netatalk_brl(fork_type,
2740 netatalk_already_open_for_writing = test_netatalk_lock(fsp,
2741 access_to_netatalk_brl(fork_type,
2744 netatalk_already_open_with_deny_write = test_netatalk_lock(fsp,
2745 denymode_to_netatalk_brl(fork_type,
2748 /* If there are any conflicts - sharing violation. */
2749 if ((access_mask & FILE_READ_DATA) &&
2750 netatalk_already_open_with_deny_read) {
2751 return NT_STATUS_SHARING_VIOLATION;
2754 if (!share_for_read &&
2755 netatalk_already_open_for_reading) {
2756 return NT_STATUS_SHARING_VIOLATION;
2759 if ((access_mask & FILE_WRITE_DATA) &&
2760 netatalk_already_open_with_deny_write) {
2761 return NT_STATUS_SHARING_VIOLATION;
2764 if (!share_for_write &&
2765 netatalk_already_open_for_writing) {
2766 return NT_STATUS_SHARING_VIOLATION;
2769 if (!(access_mask & FILE_READ_DATA)) {
2771 * Nothing we can do here, we need read access
2774 return NT_STATUS_OK;
2777 /* Set NetAtalk locks matching our access */
2778 if (access_mask & FILE_READ_DATA) {
2779 struct byte_range_lock *br_lck = NULL;
2781 off = access_to_netatalk_brl(fork_type, FILE_READ_DATA);
2783 handle->conn->sconn->msg_ctx, fsp,
2784 fsp->op->global->open_persistent_id, 1, off,
2785 READ_LOCK, POSIX_LOCK, false,
2788 TALLOC_FREE(br_lck);
2790 if (!NT_STATUS_IS_OK(status)) {
2795 if (!share_for_read) {
2796 struct byte_range_lock *br_lck = NULL;
2798 off = denymode_to_netatalk_brl(fork_type, DENY_READ);
2800 handle->conn->sconn->msg_ctx, fsp,
2801 fsp->op->global->open_persistent_id, 1, off,
2802 READ_LOCK, POSIX_LOCK, false,
2805 TALLOC_FREE(br_lck);
2807 if (!NT_STATUS_IS_OK(status)) {
2812 if (access_mask & FILE_WRITE_DATA) {
2813 struct byte_range_lock *br_lck = NULL;
2815 off = access_to_netatalk_brl(fork_type, FILE_WRITE_DATA);
2817 handle->conn->sconn->msg_ctx, fsp,
2818 fsp->op->global->open_persistent_id, 1, off,
2819 READ_LOCK, POSIX_LOCK, false,
2822 TALLOC_FREE(br_lck);
2824 if (!NT_STATUS_IS_OK(status)) {
2829 if (!share_for_write) {
2830 struct byte_range_lock *br_lck = NULL;
2832 off = denymode_to_netatalk_brl(fork_type, DENY_WRITE);
2834 handle->conn->sconn->msg_ctx, fsp,
2835 fsp->op->global->open_persistent_id, 1, off,
2836 READ_LOCK, POSIX_LOCK, false,
2839 TALLOC_FREE(br_lck);
2841 if (!NT_STATUS_IS_OK(status)) {
2846 return NT_STATUS_OK;
2849 static NTSTATUS check_aapl(vfs_handle_struct *handle,
2850 struct smb_request *req,
2851 const struct smb2_create_blobs *in_context_blobs,
2852 struct smb2_create_blobs *out_context_blobs)
2854 struct fruit_config_data *config;
2856 struct smb2_create_blob *aapl = NULL;
2860 DATA_BLOB blob = data_blob_talloc(req, NULL, 0);
2861 uint64_t req_bitmap, client_caps;
2862 uint64_t server_caps = SMB2_CRTCTX_AAPL_UNIX_BASED;
2866 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
2867 return NT_STATUS_UNSUCCESSFUL);
2869 if (!config->use_aapl
2870 || in_context_blobs == NULL
2871 || out_context_blobs == NULL) {
2872 return NT_STATUS_OK;
2875 aapl = smb2_create_blob_find(in_context_blobs,
2876 SMB2_CREATE_TAG_AAPL);
2878 return NT_STATUS_OK;
2881 if (aapl->data.length != 24) {
2882 DEBUG(1, ("unexpected AAPL ctxt length: %ju\n",
2883 (uintmax_t)aapl->data.length));
2884 return NT_STATUS_INVALID_PARAMETER;
2887 cmd = IVAL(aapl->data.data, 0);
2888 if (cmd != SMB2_CRTCTX_AAPL_SERVER_QUERY) {
2889 DEBUG(1, ("unsupported AAPL cmd: %d\n", cmd));
2890 return NT_STATUS_INVALID_PARAMETER;
2893 req_bitmap = BVAL(aapl->data.data, 8);
2894 client_caps = BVAL(aapl->data.data, 16);
2896 SIVAL(p, 0, SMB2_CRTCTX_AAPL_SERVER_QUERY);
2898 SBVAL(p, 8, req_bitmap);
2899 ok = data_blob_append(req, &blob, p, 16);
2901 return NT_STATUS_UNSUCCESSFUL;
2904 if (req_bitmap & SMB2_CRTCTX_AAPL_SERVER_CAPS) {
2905 if ((client_caps & SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR) &&
2906 (handle->conn->tcon->compat->fs_capabilities & FILE_NAMED_STREAMS)) {
2907 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR;
2908 config->readdir_attr_enabled = true;
2911 if (config->use_copyfile) {
2912 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_OSX_COPYFILE;
2913 config->copyfile_enabled = true;
2917 * The client doesn't set the flag, so we can't check
2918 * for it and just set it unconditionally
2920 if (config->unix_info_enabled) {
2921 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_NFS_ACE;
2924 SBVAL(p, 0, server_caps);
2925 ok = data_blob_append(req, &blob, p, 8);
2927 return NT_STATUS_UNSUCCESSFUL;
2931 if (req_bitmap & SMB2_CRTCTX_AAPL_VOLUME_CAPS) {
2932 int val = lp_case_sensitive(SNUM(handle->conn->tcon->compat));
2940 caps |= SMB2_CRTCTX_AAPL_CASE_SENSITIVE;
2947 if (config->time_machine) {
2948 caps |= SMB2_CRTCTX_AAPL_FULL_SYNC;
2953 ok = data_blob_append(req, &blob, p, 8);
2955 return NT_STATUS_UNSUCCESSFUL;
2959 if (req_bitmap & SMB2_CRTCTX_AAPL_MODEL_INFO) {
2960 ok = convert_string_talloc(req,
2961 CH_UNIX, CH_UTF16LE,
2962 config->model, strlen(config->model),
2965 return NT_STATUS_UNSUCCESSFUL;
2969 SIVAL(p + 4, 0, modellen);
2970 ok = data_blob_append(req, &blob, p, 8);
2973 return NT_STATUS_UNSUCCESSFUL;
2976 ok = data_blob_append(req, &blob, model, modellen);
2979 return NT_STATUS_UNSUCCESSFUL;
2983 status = smb2_create_blob_add(out_context_blobs,
2985 SMB2_CREATE_TAG_AAPL,
2987 if (NT_STATUS_IS_OK(status)) {
2988 global_fruit_config.nego_aapl = true;
2989 if (config->aapl_zero_file_id) {
2990 aapl_force_zero_file_id(handle->conn->sconn);
2997 static bool readdir_attr_meta_finderi_stream(
2998 struct vfs_handle_struct *handle,
2999 const struct smb_filename *smb_fname,
3002 struct smb_filename *stream_name = NULL;
3003 files_struct *fsp = NULL;
3008 uint8_t buf[AFP_INFO_SIZE];
3010 stream_name = synthetic_smb_fname(talloc_tos(),
3011 smb_fname->base_name,
3012 AFPINFO_STREAM_NAME,
3013 NULL, smb_fname->flags);
3014 if (stream_name == NULL) {
3018 ret = SMB_VFS_STAT(handle->conn, stream_name);
3023 status = SMB_VFS_CREATE_FILE(
3024 handle->conn, /* conn */
3026 0, /* root_dir_fid */
3027 stream_name, /* fname */
3028 FILE_READ_DATA, /* access_mask */
3029 (FILE_SHARE_READ | FILE_SHARE_WRITE | /* share_access */
3031 FILE_OPEN, /* create_disposition*/
3032 0, /* create_options */
3033 0, /* file_attributes */
3034 INTERNAL_OPEN_ONLY, /* oplock_request */
3036 0, /* allocation_size */
3037 0, /* private_flags */
3042 NULL, NULL); /* create context */
3044 TALLOC_FREE(stream_name);
3046 if (!NT_STATUS_IS_OK(status)) {
3050 nread = SMB_VFS_PREAD(fsp, &buf[0], AFP_INFO_SIZE, 0);
3051 if (nread != AFP_INFO_SIZE) {
3052 DBG_ERR("short read [%s] [%zd/%d]\n",
3053 smb_fname_str_dbg(stream_name), nread, AFP_INFO_SIZE);
3058 memcpy(&ai->afpi_FinderInfo[0], &buf[AFP_OFF_FinderInfo],
3065 close_file(NULL, fsp, NORMAL_CLOSE);
3071 static bool readdir_attr_meta_finderi_netatalk(
3072 struct vfs_handle_struct *handle,
3073 const struct smb_filename *smb_fname,
3076 struct adouble *ad = NULL;
3079 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
3084 p = ad_get_entry(ad, ADEID_FINDERI);
3086 DBG_ERR("No ADEID_FINDERI for [%s]\n", smb_fname->base_name);
3091 memcpy(&ai->afpi_FinderInfo[0], p, AFP_FinderSize);
3096 static bool readdir_attr_meta_finderi(struct vfs_handle_struct *handle,
3097 const struct smb_filename *smb_fname,
3098 struct readdir_attr_data *attr_data)
3100 struct fruit_config_data *config = NULL;
3101 uint32_t date_added;
3105 SMB_VFS_HANDLE_GET_DATA(handle, config,
3106 struct fruit_config_data,
3109 switch (config->meta) {
3110 case FRUIT_META_NETATALK:
3111 ok = readdir_attr_meta_finderi_netatalk(
3112 handle, smb_fname, &ai);
3115 case FRUIT_META_STREAM:
3116 ok = readdir_attr_meta_finderi_stream(
3117 handle, smb_fname, &ai);
3121 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3126 /* Don't bother with errors, it's likely ENOENT */
3130 if (S_ISREG(smb_fname->st.st_ex_mode)) {
3132 memcpy(&attr_data->attr_data.aapl.finder_info[0],
3133 &ai.afpi_FinderInfo[0], 4);
3135 /* finder_creator */
3136 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 4,
3137 &ai.afpi_FinderInfo[4], 4);
3141 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 8,
3142 &ai.afpi_FinderInfo[8], 2);
3144 /* finder_ext_flags */
3145 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 10,
3146 &ai.afpi_FinderInfo[24], 2);
3149 date_added = convert_time_t_to_uint32_t(
3150 smb_fname->st.st_ex_btime.tv_sec - AD_DATE_DELTA);
3152 RSIVAL(&attr_data->attr_data.aapl.finder_info[0], 12, date_added);
3157 static uint64_t readdir_attr_rfork_size_adouble(
3158 struct vfs_handle_struct *handle,
3159 const struct smb_filename *smb_fname)
3161 struct adouble *ad = NULL;
3162 uint64_t rfork_size;
3164 ad = ad_get(talloc_tos(), handle, smb_fname,
3170 rfork_size = ad_getentrylen(ad, ADEID_RFORK);
3176 static uint64_t readdir_attr_rfork_size_stream(
3177 struct vfs_handle_struct *handle,
3178 const struct smb_filename *smb_fname)
3180 struct smb_filename *stream_name = NULL;
3182 uint64_t rfork_size;
3184 stream_name = synthetic_smb_fname(talloc_tos(),
3185 smb_fname->base_name,
3186 AFPRESOURCE_STREAM_NAME,
3188 if (stream_name == NULL) {
3192 ret = SMB_VFS_STAT(handle->conn, stream_name);
3194 TALLOC_FREE(stream_name);
3198 rfork_size = stream_name->st.st_ex_size;
3199 TALLOC_FREE(stream_name);
3204 static uint64_t readdir_attr_rfork_size(struct vfs_handle_struct *handle,
3205 const struct smb_filename *smb_fname)
3207 struct fruit_config_data *config = NULL;
3208 uint64_t rfork_size;
3210 SMB_VFS_HANDLE_GET_DATA(handle, config,
3211 struct fruit_config_data,
3214 switch (config->rsrc) {
3215 case FRUIT_RSRC_ADFILE:
3216 case FRUIT_RSRC_XATTR:
3217 rfork_size = readdir_attr_rfork_size_adouble(handle,
3221 case FRUIT_META_STREAM:
3222 rfork_size = readdir_attr_rfork_size_stream(handle,
3227 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3235 static NTSTATUS readdir_attr_macmeta(struct vfs_handle_struct *handle,
3236 const struct smb_filename *smb_fname,
3237 struct readdir_attr_data *attr_data)
3239 NTSTATUS status = NT_STATUS_OK;
3240 struct fruit_config_data *config = NULL;
3243 SMB_VFS_HANDLE_GET_DATA(handle, config,
3244 struct fruit_config_data,
3245 return NT_STATUS_UNSUCCESSFUL);
3248 /* Ensure we return a default value in the creation_date field */
3249 RSIVAL(&attr_data->attr_data.aapl.finder_info, 12, AD_DATE_START);
3252 * Resource fork length
3255 if (config->readdir_attr_rsize) {
3256 uint64_t rfork_size;
3258 rfork_size = readdir_attr_rfork_size(handle, smb_fname);
3259 attr_data->attr_data.aapl.rfork_size = rfork_size;
3266 if (config->readdir_attr_finder_info) {
3267 ok = readdir_attr_meta_finderi(handle, smb_fname, attr_data);
3269 status = NT_STATUS_INTERNAL_ERROR;
3276 static NTSTATUS remove_virtual_nfs_aces(struct security_descriptor *psd)
3281 if (psd->dacl == NULL) {
3282 return NT_STATUS_OK;
3285 for (i = 0; i < psd->dacl->num_aces; i++) {
3286 /* MS NFS style mode/uid/gid */
3287 int cmp = dom_sid_compare_domain(
3288 &global_sid_Unix_NFS,
3289 &psd->dacl->aces[i].trustee);
3291 /* Normal ACE entry. */
3296 * security_descriptor_dacl_del()
3297 * *must* return NT_STATUS_OK as we know
3298 * we have something to remove.
3301 status = security_descriptor_dacl_del(psd,
3302 &psd->dacl->aces[i].trustee);
3303 if (!NT_STATUS_IS_OK(status)) {
3304 DBG_WARNING("failed to remove MS NFS style ACE: %s\n",
3310 * security_descriptor_dacl_del() may delete more
3311 * then one entry subsequent to this one if the
3312 * SID matches, but we only need to ensure that
3313 * we stay looking at the same element in the array.
3317 return NT_STATUS_OK;
3320 /* Search MS NFS style ACE with UNIX mode */
3321 static NTSTATUS check_ms_nfs(vfs_handle_struct *handle,
3323 struct security_descriptor *psd,
3328 struct fruit_config_data *config = NULL;
3332 SMB_VFS_HANDLE_GET_DATA(handle, config,
3333 struct fruit_config_data,
3334 return NT_STATUS_UNSUCCESSFUL);
3336 if (!global_fruit_config.nego_aapl) {
3337 return NT_STATUS_OK;
3339 if (psd->dacl == NULL || !config->unix_info_enabled) {
3340 return NT_STATUS_OK;
3343 for (i = 0; i < psd->dacl->num_aces; i++) {
3344 if (dom_sid_compare_domain(
3345 &global_sid_Unix_NFS_Mode,
3346 &psd->dacl->aces[i].trustee) == 0) {
3347 *pmode = (mode_t)psd->dacl->aces[i].trustee.sub_auths[2];
3348 *pmode &= (S_IRWXU | S_IRWXG | S_IRWXO);
3351 DEBUG(10, ("MS NFS chmod request %s, %04o\n",
3352 fsp_str_dbg(fsp), (unsigned)(*pmode)));
3358 * Remove any incoming virtual ACE entries generated by
3359 * fruit_fget_nt_acl().
3362 return remove_virtual_nfs_aces(psd);
3365 /****************************************************************************
3367 ****************************************************************************/
3369 static int fruit_connect(vfs_handle_struct *handle,
3370 const char *service,
3374 char *list = NULL, *newlist = NULL;
3375 struct fruit_config_data *config;
3377 DEBUG(10, ("fruit_connect\n"));
3379 rc = SMB_VFS_NEXT_CONNECT(handle, service, user);
3384 rc = init_fruit_config(handle);
3389 SMB_VFS_HANDLE_GET_DATA(handle, config,
3390 struct fruit_config_data, return -1);
3392 if (config->veto_appledouble) {
3393 list = lp_veto_files(talloc_tos(), SNUM(handle->conn));
3396 if (strstr(list, "/" ADOUBLE_NAME_PREFIX "*/") == NULL) {
3397 newlist = talloc_asprintf(
3399 "%s/" ADOUBLE_NAME_PREFIX "*/",
3401 lp_do_parameter(SNUM(handle->conn),
3406 lp_do_parameter(SNUM(handle->conn),
3408 "/" ADOUBLE_NAME_PREFIX "*/");
3414 if (config->encoding == FRUIT_ENC_NATIVE) {
3415 lp_do_parameter(SNUM(handle->conn),
3420 if (config->time_machine) {
3421 DBG_NOTICE("Enabling durable handles for Time Machine "
3422 "support on [%s]\n", service);
3423 lp_do_parameter(SNUM(handle->conn), "durable handles", "yes");
3424 lp_do_parameter(SNUM(handle->conn), "kernel oplocks", "no");
3425 lp_do_parameter(SNUM(handle->conn), "kernel share modes", "no");
3426 if (!lp_strict_sync(SNUM(handle->conn))) {
3427 DBG_WARNING("Time Machine without strict sync is not "
3430 lp_do_parameter(SNUM(handle->conn), "posix locking", "no");
3436 static int fruit_fake_fd(void)
3443 * Return a valid fd, but ensure any attempt to use it returns
3444 * an error (EPIPE). Once we get a write on the handle, we open
3447 ret = pipe(pipe_fds);
3457 static int fruit_open_meta_stream(vfs_handle_struct *handle,
3458 struct smb_filename *smb_fname,
3463 struct fruit_config_data *config = NULL;
3464 struct fio *fio = NULL;
3465 int open_flags = flags & ~O_CREAT;
3468 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3470 SMB_VFS_HANDLE_GET_DATA(handle, config,
3471 struct fruit_config_data, return -1);
3473 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3474 fio->type = ADOUBLE_META;
3475 fio->config = config;
3477 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, open_flags, mode);
3482 if (!(flags & O_CREAT)) {
3483 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
3487 fd = fruit_fake_fd();
3489 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
3493 fio->fake_fd = true;
3500 static int fruit_open_meta_netatalk(vfs_handle_struct *handle,
3501 struct smb_filename *smb_fname,
3506 struct fruit_config_data *config = NULL;
3507 struct fio *fio = NULL;
3508 struct adouble *ad = NULL;
3509 bool meta_exists = false;
3512 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3514 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
3521 if (!meta_exists && !(flags & O_CREAT)) {
3526 fd = fruit_fake_fd();
3531 SMB_VFS_HANDLE_GET_DATA(handle, config,
3532 struct fruit_config_data, return -1);
3534 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3535 fio->type = ADOUBLE_META;
3536 fio->config = config;
3537 fio->fake_fd = true;
3544 static int fruit_open_meta(vfs_handle_struct *handle,
3545 struct smb_filename *smb_fname,
3546 files_struct *fsp, int flags, mode_t mode)
3549 struct fruit_config_data *config = NULL;
3551 DBG_DEBUG("path [%s]\n", smb_fname_str_dbg(smb_fname));
3553 SMB_VFS_HANDLE_GET_DATA(handle, config,
3554 struct fruit_config_data, return -1);
3556 switch (config->meta) {
3557 case FRUIT_META_STREAM:
3558 fd = fruit_open_meta_stream(handle, smb_fname,
3562 case FRUIT_META_NETATALK:
3563 fd = fruit_open_meta_netatalk(handle, smb_fname,
3568 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3572 DBG_DEBUG("path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3577 static int fruit_open_rsrc_adouble(vfs_handle_struct *handle,
3578 struct smb_filename *smb_fname,
3584 struct adouble *ad = NULL;
3585 struct smb_filename *smb_fname_base = NULL;
3586 struct fruit_config_data *config = NULL;
3589 SMB_VFS_HANDLE_GET_DATA(handle, config,
3590 struct fruit_config_data, return -1);
3592 if ((!(flags & O_CREAT)) &&
3593 S_ISDIR(fsp->base_fsp->fsp_name->st.st_ex_mode))
3595 /* sorry, but directories don't habe a resource fork */
3600 rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_base);
3605 /* We always need read/write access for the metadata header too */
3606 flags &= ~(O_RDONLY | O_WRONLY);
3609 hostfd = SMB_VFS_NEXT_OPEN(handle, smb_fname_base, fsp,
3616 if (flags & (O_CREAT | O_TRUNC)) {
3617 ad = ad_init(fsp, handle, ADOUBLE_RSRC);
3623 fsp->fh->fd = hostfd;
3625 rc = ad_fset(handle, ad, fsp);
3636 TALLOC_FREE(smb_fname_base);
3638 DEBUG(10, ("fruit_open resource fork: rc=%d, fd=%d\n", rc, hostfd));
3640 int saved_errno = errno;
3643 * BUGBUGBUG -- we would need to call
3644 * fd_close_posix here, but we don't have a
3647 fsp->fh->fd = hostfd;
3651 errno = saved_errno;
3656 static int fruit_open_rsrc_xattr(vfs_handle_struct *handle,
3657 struct smb_filename *smb_fname,
3662 #ifdef HAVE_ATTROPEN
3665 fd = attropen(smb_fname->base_name,
3666 AFPRESOURCE_EA_NETATALK,
3681 static int fruit_open_rsrc(vfs_handle_struct *handle,
3682 struct smb_filename *smb_fname,
3683 files_struct *fsp, int flags, mode_t mode)
3686 struct fruit_config_data *config = NULL;
3687 struct fio *fio = NULL;
3689 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3691 SMB_VFS_HANDLE_GET_DATA(handle, config,
3692 struct fruit_config_data, return -1);
3694 switch (config->rsrc) {
3695 case FRUIT_RSRC_STREAM:
3696 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3699 case FRUIT_RSRC_ADFILE:
3700 fd = fruit_open_rsrc_adouble(handle, smb_fname,
3704 case FRUIT_RSRC_XATTR:
3705 fd = fruit_open_rsrc_xattr(handle, smb_fname,
3710 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3714 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3720 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3721 fio->type = ADOUBLE_RSRC;
3722 fio->config = config;
3727 static int fruit_open(vfs_handle_struct *handle,
3728 struct smb_filename *smb_fname,
3729 files_struct *fsp, int flags, mode_t mode)
3733 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3735 if (!is_ntfs_stream_smb_fname(smb_fname)) {
3736 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3739 if (is_afpinfo_stream(smb_fname)) {
3740 fd = fruit_open_meta(handle, smb_fname, fsp, flags, mode);
3741 } else if (is_afpresource_stream(smb_fname)) {
3742 fd = fruit_open_rsrc(handle, smb_fname, fsp, flags, mode);
3744 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3747 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3752 static int fruit_close_meta(vfs_handle_struct *handle,
3756 struct fruit_config_data *config = NULL;
3758 SMB_VFS_HANDLE_GET_DATA(handle, config,
3759 struct fruit_config_data, return -1);
3761 switch (config->meta) {
3762 case FRUIT_META_STREAM:
3763 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3766 case FRUIT_META_NETATALK:
3767 ret = close(fsp->fh->fd);
3772 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3780 static int fruit_close_rsrc(vfs_handle_struct *handle,
3784 struct fruit_config_data *config = NULL;
3786 SMB_VFS_HANDLE_GET_DATA(handle, config,
3787 struct fruit_config_data, return -1);
3789 switch (config->rsrc) {
3790 case FRUIT_RSRC_STREAM:
3791 case FRUIT_RSRC_ADFILE:
3792 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3795 case FRUIT_RSRC_XATTR:
3796 ret = close(fsp->fh->fd);
3801 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3808 static int fruit_close(vfs_handle_struct *handle,
3816 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(fsp->fsp_name), fd);
3818 if (!is_ntfs_stream_smb_fname(fsp->fsp_name)) {
3819 return SMB_VFS_NEXT_CLOSE(handle, fsp);
3822 if (is_afpinfo_stream(fsp->fsp_name)) {
3823 ret = fruit_close_meta(handle, fsp);
3824 } else if (is_afpresource_stream(fsp->fsp_name)) {
3825 ret = fruit_close_rsrc(handle, fsp);
3827 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3833 static int fruit_rename(struct vfs_handle_struct *handle,
3834 const struct smb_filename *smb_fname_src,
3835 const struct smb_filename *smb_fname_dst)
3838 struct fruit_config_data *config = NULL;
3839 struct smb_filename *src_adp_smb_fname = NULL;
3840 struct smb_filename *dst_adp_smb_fname = NULL;
3842 SMB_VFS_HANDLE_GET_DATA(handle, config,
3843 struct fruit_config_data, return -1);
3845 if (!VALID_STAT(smb_fname_src->st)) {
3846 DBG_ERR("Need valid stat for [%s]\n",
3847 smb_fname_str_dbg(smb_fname_src));
3851 rc = SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
3856 if ((config->rsrc != FRUIT_RSRC_ADFILE) ||
3857 (!S_ISREG(smb_fname_src->st.st_ex_mode)))
3862 rc = adouble_path(talloc_tos(), smb_fname_src, &src_adp_smb_fname);
3867 rc = adouble_path(talloc_tos(), smb_fname_dst, &dst_adp_smb_fname);
3872 DBG_DEBUG("%s -> %s\n",
3873 smb_fname_str_dbg(src_adp_smb_fname),
3874 smb_fname_str_dbg(dst_adp_smb_fname));
3876 rc = SMB_VFS_NEXT_RENAME(handle, src_adp_smb_fname, dst_adp_smb_fname);
3877 if (errno == ENOENT) {
3882 TALLOC_FREE(src_adp_smb_fname);
3883 TALLOC_FREE(dst_adp_smb_fname);
3887 static int fruit_unlink_meta_stream(vfs_handle_struct *handle,
3888 const struct smb_filename *smb_fname)
3890 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3893 static int fruit_unlink_meta_netatalk(vfs_handle_struct *handle,
3894 const struct smb_filename *smb_fname)
3896 return SMB_VFS_REMOVEXATTR(handle->conn,
3898 AFPINFO_EA_NETATALK);
3901 static int fruit_unlink_meta(vfs_handle_struct *handle,
3902 const struct smb_filename *smb_fname)
3904 struct fruit_config_data *config = NULL;
3907 SMB_VFS_HANDLE_GET_DATA(handle, config,
3908 struct fruit_config_data, return -1);
3910 switch (config->meta) {
3911 case FRUIT_META_STREAM:
3912 rc = fruit_unlink_meta_stream(handle, smb_fname);
3915 case FRUIT_META_NETATALK:
3916 rc = fruit_unlink_meta_netatalk(handle, smb_fname);
3920 DBG_ERR("Unsupported meta config [%d]\n", config->meta);
3927 static int fruit_unlink_rsrc_stream(vfs_handle_struct *handle,
3928 const struct smb_filename *smb_fname,
3933 if (!force_unlink) {
3934 struct smb_filename *smb_fname_cp = NULL;
3937 smb_fname_cp = cp_smb_filename(talloc_tos(), smb_fname);
3938 if (smb_fname_cp == NULL) {
3943 * 0 byte resource fork streams are not listed by
3944 * vfs_streaminfo, as a result stream cleanup/deletion of file
3945 * deletion doesn't remove the resourcefork stream.
3948 ret = SMB_VFS_NEXT_STAT(handle, smb_fname_cp);
3950 TALLOC_FREE(smb_fname_cp);
3951 DBG_ERR("stat [%s] failed [%s]\n",
3952 smb_fname_str_dbg(smb_fname_cp), strerror(errno));
3956 size = smb_fname_cp->st.st_ex_size;
3957 TALLOC_FREE(smb_fname_cp);
3960 /* OS X ignores resource fork stream delete requests */
3965 ret = SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3966 if ((ret != 0) && (errno == ENOENT) && force_unlink) {
3973 static int fruit_unlink_rsrc_adouble(vfs_handle_struct *handle,
3974 const struct smb_filename *smb_fname,
3978 struct adouble *ad = NULL;
3979 struct smb_filename *adp_smb_fname = NULL;
3981 if (!force_unlink) {
3982 ad = ad_get(talloc_tos(), handle, smb_fname,
3991 * 0 byte resource fork streams are not listed by
3992 * vfs_streaminfo, as a result stream cleanup/deletion of file
3993 * deletion doesn't remove the resourcefork stream.
3996 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
3997 /* OS X ignores resource fork stream delete requests */
4005 rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
4010 rc = SMB_VFS_NEXT_UNLINK(handle, adp_smb_fname);
4011 TALLOC_FREE(adp_smb_fname);
4012 if ((rc != 0) && (errno == ENOENT) && force_unlink) {
4019 static int fruit_unlink_rsrc_xattr(vfs_handle_struct *handle,
4020 const struct smb_filename *smb_fname,
4024 * OS X ignores resource fork stream delete requests, so nothing to do
4025 * here. Removing the file will remove the xattr anyway, so we don't
4026 * have to take care of removing 0 byte resource forks that could be
4032 static int fruit_unlink_rsrc(vfs_handle_struct *handle,
4033 const struct smb_filename *smb_fname,
4036 struct fruit_config_data *config = NULL;
4039 SMB_VFS_HANDLE_GET_DATA(handle, config,
4040 struct fruit_config_data, return -1);
4042 switch (config->rsrc) {
4043 case FRUIT_RSRC_STREAM:
4044 rc = fruit_unlink_rsrc_stream(handle, smb_fname, force_unlink);
4047 case FRUIT_RSRC_ADFILE:
4048 rc = fruit_unlink_rsrc_adouble(handle, smb_fname, force_unlink);
4051 case FRUIT_RSRC_XATTR:
4052 rc = fruit_unlink_rsrc_xattr(handle, smb_fname, force_unlink);
4056 DBG_ERR("Unsupported rsrc config [%d]\n", config->rsrc);
4063 static int fruit_unlink(vfs_handle_struct *handle,
4064 const struct smb_filename *smb_fname)
4067 struct fruit_config_data *config = NULL;
4068 struct smb_filename *rsrc_smb_fname = NULL;
4070 SMB_VFS_HANDLE_GET_DATA(handle, config,
4071 struct fruit_config_data, return -1);
4073 if (is_afpinfo_stream(smb_fname)) {
4074 return fruit_unlink_meta(handle, smb_fname);
4075 } else if (is_afpresource_stream(smb_fname)) {
4076 return fruit_unlink_rsrc(handle, smb_fname, false);
4077 } if (is_ntfs_stream_smb_fname(smb_fname)) {
4078 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4082 * A request to delete the base file. Because 0 byte resource
4083 * fork streams are not listed by fruit_streaminfo,
4084 * delete_all_streams() can't remove 0 byte resource fork
4085 * streams, so we have to cleanup this here.
4087 rsrc_smb_fname = synthetic_smb_fname(talloc_tos(),
4088 smb_fname->base_name,
4089 AFPRESOURCE_STREAM_NAME,
4092 if (rsrc_smb_fname == NULL) {
4096 rc = fruit_unlink_rsrc(handle, rsrc_smb_fname, true);
4097 if ((rc != 0) && (errno != ENOENT)) {
4098 DBG_ERR("Forced unlink of [%s] failed [%s]\n",
4099 smb_fname_str_dbg(rsrc_smb_fname), strerror(errno));
4100 TALLOC_FREE(rsrc_smb_fname);
4103 TALLOC_FREE(rsrc_smb_fname);
4105 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4108 static int fruit_chmod(vfs_handle_struct *handle,
4109 const struct smb_filename *smb_fname,
4113 struct fruit_config_data *config = NULL;
4114 struct smb_filename *smb_fname_adp = NULL;
4116 rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname, mode);
4121 SMB_VFS_HANDLE_GET_DATA(handle, config,
4122 struct fruit_config_data, return -1);
4124 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4128 if (!VALID_STAT(smb_fname->st)) {
4132 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
4136 rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_adp);
4141 DEBUG(10, ("fruit_chmod: %s\n", smb_fname_adp->base_name));
4143 rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname_adp, mode);
4144 if (errno == ENOENT) {
4148 TALLOC_FREE(smb_fname_adp);
4152 static int fruit_chown(vfs_handle_struct *handle,
4153 const struct smb_filename *smb_fname,
4158 struct fruit_config_data *config = NULL;
4159 struct smb_filename *adp_smb_fname = NULL;
4161 rc = SMB_VFS_NEXT_CHOWN(handle, smb_fname, uid, gid);
4166 SMB_VFS_HANDLE_GET_DATA(handle, config,
4167 struct fruit_config_data, return -1);
4169 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4173 if (!VALID_STAT(smb_fname->st)) {
4177 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
4181 rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
4186 DEBUG(10, ("fruit_chown: %s\n", adp_smb_fname->base_name));
4188 rc = SMB_VFS_NEXT_CHOWN(handle, adp_smb_fname, uid, gid);
4189 if (errno == ENOENT) {
4194 TALLOC_FREE(adp_smb_fname);
4198 static int fruit_rmdir(struct vfs_handle_struct *handle,
4199 const struct smb_filename *smb_fname)
4203 struct fruit_config_data *config;
4205 SMB_VFS_HANDLE_GET_DATA(handle, config,
4206 struct fruit_config_data, return -1);
4208 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4213 * Due to there is no way to change bDeleteVetoFiles variable
4214 * from this module, need to clean up ourselves
4217 dh = SMB_VFS_OPENDIR(handle->conn, smb_fname, NULL, 0);
4222 while ((de = SMB_VFS_READDIR(handle->conn, dh, NULL)) != NULL) {
4224 struct adouble *ad = NULL;
4226 struct smb_filename *ad_smb_fname = NULL;
4229 match = strncmp(de->d_name,
4230 ADOUBLE_NAME_PREFIX,
4231 strlen(ADOUBLE_NAME_PREFIX));
4236 p = talloc_asprintf(talloc_tos(), "%s/%s",
4237 smb_fname->base_name, de->d_name);
4239 DBG_ERR("talloc_asprintf failed\n");
4243 ad_smb_fname = synthetic_smb_fname(talloc_tos(), p,
4247 if (ad_smb_fname == NULL) {
4248 DBG_ERR("synthetic_smb_fname failed\n");
4253 * Check whether it's a valid AppleDouble file, if
4254 * yes, delete it, ignore it otherwise.
4256 ad = ad_get(talloc_tos(), handle, ad_smb_fname, ADOUBLE_RSRC);
4258 TALLOC_FREE(ad_smb_fname);
4264 ret = SMB_VFS_NEXT_UNLINK(handle, ad_smb_fname);
4266 DBG_ERR("Deleting [%s] failed\n",
4267 smb_fname_str_dbg(ad_smb_fname));
4269 TALLOC_FREE(ad_smb_fname);
4274 SMB_VFS_CLOSEDIR(handle->conn, dh);
4276 return SMB_VFS_NEXT_RMDIR(handle, smb_fname);
4279 static ssize_t fruit_pread_meta_stream(vfs_handle_struct *handle,
4280 files_struct *fsp, void *data,
4281 size_t n, off_t offset)
4286 nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4287 if (nread == -1 || nread == n) {
4291 DBG_ERR("Removing [%s] after short read [%zd]\n",
4292 fsp_str_dbg(fsp), nread);
4294 ret = SMB_VFS_NEXT_UNLINK(handle, fsp->fsp_name);
4296 DBG_ERR("Removing [%s] failed\n", fsp_str_dbg(fsp));
4304 static ssize_t fruit_pread_meta_adouble(vfs_handle_struct *handle,
4305 files_struct *fsp, void *data,
4306 size_t n, off_t offset)
4309 struct adouble *ad = NULL;
4310 char afpinfo_buf[AFP_INFO_SIZE];
4314 ai = afpinfo_new(talloc_tos());
4319 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
4325 p = ad_get_entry(ad, ADEID_FINDERI);
4327 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
4332 memcpy(&ai->afpi_FinderInfo[0], p, ADEDLEN_FINDERI);
4334 nread = afpinfo_pack(ai, afpinfo_buf);
4335 if (nread != AFP_INFO_SIZE) {
4340 memcpy(data, afpinfo_buf, n);
4348 static ssize_t fruit_pread_meta(vfs_handle_struct *handle,
4349 files_struct *fsp, void *data,
4350 size_t n, off_t offset)
4352 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4357 * OS X has a off-by-1 error in the offset calculation, so we're
4358 * bug compatible here. It won't hurt, as any relevant real
4359 * world read requests from the AFP_AfpInfo stream will be
4360 * offset=0 n=60. offset is ignored anyway, see below.
4362 if ((offset < 0) || (offset >= AFP_INFO_SIZE + 1)) {
4367 DBG_ERR("Failed to fetch fsp extension");
4371 /* Yes, macOS always reads from offset 0 */
4373 to_return = MIN(n, AFP_INFO_SIZE);
4375 switch (fio->config->meta) {
4376 case FRUIT_META_STREAM:
4377 nread = fruit_pread_meta_stream(handle, fsp, data,
4381 case FRUIT_META_NETATALK:
4382 nread = fruit_pread_meta_adouble(handle, fsp, data,
4387 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
4391 if (nread == -1 && fio->created) {
4393 char afpinfo_buf[AFP_INFO_SIZE];
4395 ai = afpinfo_new(talloc_tos());
4400 nread = afpinfo_pack(ai, afpinfo_buf);
4402 if (nread != AFP_INFO_SIZE) {
4406 memcpy(data, afpinfo_buf, to_return);
4413 static ssize_t fruit_pread_rsrc_stream(vfs_handle_struct *handle,
4414 files_struct *fsp, void *data,
4415 size_t n, off_t offset)
4417 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4420 static ssize_t fruit_pread_rsrc_xattr(vfs_handle_struct *handle,
4421 files_struct *fsp, void *data,
4422 size_t n, off_t offset)
4424 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4427 static ssize_t fruit_pread_rsrc_adouble(vfs_handle_struct *handle,
4428 files_struct *fsp, void *data,
4429 size_t n, off_t offset)
4431 struct adouble *ad = NULL;
4434 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
4439 nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n,
4440 offset + ad_getentryoff(ad, ADEID_RFORK));
4446 static ssize_t fruit_pread_rsrc(vfs_handle_struct *handle,
4447 files_struct *fsp, void *data,
4448 size_t n, off_t offset)
4450 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4458 switch (fio->config->rsrc) {
4459 case FRUIT_RSRC_STREAM:
4460 nread = fruit_pread_rsrc_stream(handle, fsp, data, n, offset);
4463 case FRUIT_RSRC_ADFILE:
4464 nread = fruit_pread_rsrc_adouble(handle, fsp, data, n, offset);
4467 case FRUIT_RSRC_XATTR:
4468 nread = fruit_pread_rsrc_xattr(handle, fsp, data, n, offset);
4472 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
4479 static ssize_t fruit_pread(vfs_handle_struct *handle,
4480 files_struct *fsp, void *data,
4481 size_t n, off_t offset)
4483 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4486 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4487 fsp_str_dbg(fsp), (intmax_t)offset, n);
4490 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4493 if (fio->type == ADOUBLE_META) {
4494 nread = fruit_pread_meta(handle, fsp, data, n, offset);
4496 nread = fruit_pread_rsrc(handle, fsp, data, n, offset);
4499 DBG_DEBUG("Path [%s] nread [%zd]\n", fsp_str_dbg(fsp), nread);
4503 static bool fruit_must_handle_aio_stream(struct fio *fio)
4509 if (fio->type == ADOUBLE_META) {
4513 if ((fio->type == ADOUBLE_RSRC) &&
4514 (fio->config->rsrc == FRUIT_RSRC_ADFILE))
4522 struct fruit_pread_state {
4524 struct vfs_aio_state vfs_aio_state;
4527 static void fruit_pread_done(struct tevent_req *subreq);
4529 static struct tevent_req *fruit_pread_send(
4530 struct vfs_handle_struct *handle,
4531 TALLOC_CTX *mem_ctx,
4532 struct tevent_context *ev,
4533 struct files_struct *fsp,
4535 size_t n, off_t offset)
4537 struct tevent_req *req = NULL;
4538 struct tevent_req *subreq = NULL;
4539 struct fruit_pread_state *state = NULL;
4540 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4542 req = tevent_req_create(mem_ctx, &state,
4543 struct fruit_pread_state);
4548 if (fruit_must_handle_aio_stream(fio)) {
4549 state->nread = SMB_VFS_PREAD(fsp, data, n, offset);
4550 if (state->nread != n) {
4551 if (state->nread != -1) {
4554 tevent_req_error(req, errno);
4555 return tevent_req_post(req, ev);
4557 tevent_req_done(req);
4558 return tevent_req_post(req, ev);
4561 subreq = SMB_VFS_NEXT_PREAD_SEND(state, ev, handle, fsp,
4563 if (tevent_req_nomem(req, subreq)) {
4564 return tevent_req_post(req, ev);
4566 tevent_req_set_callback(subreq, fruit_pread_done, req);
4570 static void fruit_pread_done(struct tevent_req *subreq)
4572 struct tevent_req *req = tevent_req_callback_data(
4573 subreq, struct tevent_req);
4574 struct fruit_pread_state *state = tevent_req_data(
4575 req, struct fruit_pread_state);
4577 state->nread = SMB_VFS_PREAD_RECV(subreq, &state->vfs_aio_state);
4578 TALLOC_FREE(subreq);
4580 if (tevent_req_error(req, state->vfs_aio_state.error)) {
4583 tevent_req_done(req);
4586 static ssize_t fruit_pread_recv(struct tevent_req *req,
4587 struct vfs_aio_state *vfs_aio_state)
4589 struct fruit_pread_state *state = tevent_req_data(
4590 req, struct fruit_pread_state);
4592 if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
4596 *vfs_aio_state = state->vfs_aio_state;
4597 return state->nread;
4600 static ssize_t fruit_pwrite_meta_stream(vfs_handle_struct *handle,
4601 files_struct *fsp, const void *data,
4602 size_t n, off_t offset)
4604 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4610 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4611 fsp_str_dbg(fsp), (intmax_t)offset, n);
4620 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
4622 DBG_ERR("Close [%s] failed: %s\n",
4623 fsp_str_dbg(fsp), strerror(errno));
4628 fd = SMB_VFS_NEXT_OPEN(handle,
4634 DBG_ERR("On-demand create [%s] in write failed: %s\n",
4635 fsp_str_dbg(fsp), strerror(errno));
4639 fio->fake_fd = false;
4642 ai = afpinfo_unpack(talloc_tos(), data);
4647 if (ai_empty_finderinfo(ai)) {
4649 * Writing an all 0 blob to the metadata stream results in the
4650 * stream being removed on a macOS server. This ensures we
4651 * behave the same and it verified by the "delete AFP_AfpInfo by
4652 * writing all 0" test.
4654 ret = SMB_VFS_NEXT_FTRUNCATE(handle, fsp, 0);
4656 DBG_ERR("SMB_VFS_NEXT_FTRUNCATE on [%s] failed\n",
4661 ok = set_delete_on_close(
4664 handle->conn->session_info->security_token,
4665 handle->conn->session_info->unix_token);
4667 DBG_ERR("set_delete_on_close on [%s] failed\n",
4674 nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4675 if (nwritten != n) {
4682 static ssize_t fruit_pwrite_meta_netatalk(vfs_handle_struct *handle,
4683 files_struct *fsp, const void *data,
4684 size_t n, off_t offset)
4686 struct adouble *ad = NULL;
4692 ai = afpinfo_unpack(talloc_tos(), data);
4697 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
4699 ad = ad_init(talloc_tos(), handle, ADOUBLE_META);
4704 p = ad_get_entry(ad, ADEID_FINDERI);
4706 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
4711 memcpy(p, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
4713 ret = ad_fset(handle, ad, fsp);
4715 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
4722 if (!ai_empty_finderinfo(ai)) {
4727 * Writing an all 0 blob to the metadata stream results in the stream
4728 * being removed on a macOS server. This ensures we behave the same and
4729 * it verified by the "delete AFP_AfpInfo by writing all 0" test.
4732 ok = set_delete_on_close(
4735 handle->conn->session_info->security_token,
4736 handle->conn->session_info->unix_token);
4738 DBG_ERR("set_delete_on_close on [%s] failed\n",
4746 static ssize_t fruit_pwrite_meta(vfs_handle_struct *handle,
4747 files_struct *fsp, const void *data,
4748 size_t n, off_t offset)
4750 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4752 uint8_t buf[AFP_INFO_SIZE];
4758 DBG_ERR("Failed to fetch fsp extension");
4767 if (offset != 0 && n < 60) {
4772 cmp = memcmp(data, "AFP", 3);
4778 if (n <= AFP_OFF_FinderInfo) {
4780 * Nothing to do here really, just return
4788 if (to_copy > AFP_INFO_SIZE) {
4789 to_copy = AFP_INFO_SIZE;
4791 memcpy(buf, data, to_copy);
4794 if (to_write != AFP_INFO_SIZE) {
4795 to_write = AFP_INFO_SIZE;
4798 switch (fio->config->meta) {
4799 case FRUIT_META_STREAM:
4800 nwritten = fruit_pwrite_meta_stream(handle,
4807 case FRUIT_META_NETATALK:
4808 nwritten = fruit_pwrite_meta_netatalk(handle,
4816 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
4820 if (nwritten != to_write) {
4825 * Return the requested amount, verified against macOS SMB server
4830 static ssize_t fruit_pwrite_rsrc_stream(vfs_handle_struct *handle,
4831 files_struct *fsp, const void *data,
4832 size_t n, off_t offset)
4834 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4837 static ssize_t fruit_pwrite_rsrc_xattr(vfs_handle_struct *handle,
4838 files_struct *fsp, const void *data,
4839 size_t n, off_t offset)
4841 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4844 static ssize_t fruit_pwrite_rsrc_adouble(vfs_handle_struct *handle,
4845 files_struct *fsp, const void *data,
4846 size_t n, off_t offset)
4848 struct adouble *ad = NULL;
4852 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
4854 DBG_ERR("ad_get [%s] failed\n", fsp_str_dbg(fsp));
4858 nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n,
4859 offset + ad_getentryoff(ad, ADEID_RFORK));
4860 if (nwritten != n) {
4861 DBG_ERR("Short write on [%s] [%zd/%zd]\n",
4862 fsp_str_dbg(fsp), nwritten, n);
4867 if ((n + offset) > ad_getentrylen(ad, ADEID_RFORK)) {
4868 ad_setentrylen(ad, ADEID_RFORK, n + offset);
4869 ret = ad_fset(handle, ad, fsp);
4871 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
4881 static ssize_t fruit_pwrite_rsrc(vfs_handle_struct *handle,
4882 files_struct *fsp, const void *data,
4883 size_t n, off_t offset)
4885 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4889 DBG_ERR("Failed to fetch fsp extension");
4893 switch (fio->config->rsrc) {
4894 case FRUIT_RSRC_STREAM:
4895 nwritten = fruit_pwrite_rsrc_stream(handle, fsp, data, n, offset);
4898 case FRUIT_RSRC_ADFILE:
4899 nwritten = fruit_pwrite_rsrc_adouble(handle, fsp, data, n, offset);
4902 case FRUIT_RSRC_XATTR:
4903 nwritten = fruit_pwrite_rsrc_xattr(handle, fsp, data, n, offset);
4907 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
4914 static ssize_t fruit_pwrite(vfs_handle_struct *handle,
4915 files_struct *fsp, const void *data,
4916 size_t n, off_t offset)
4918 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4921 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4922 fsp_str_dbg(fsp), (intmax_t)offset, n);
4925 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4928 if (fio->type == ADOUBLE_META) {
4929 nwritten = fruit_pwrite_meta(handle, fsp, data, n, offset);
4931 nwritten = fruit_pwrite_rsrc(handle, fsp, data, n, offset);
4934 DBG_DEBUG("Path [%s] nwritten=%zd\n", fsp_str_dbg(fsp), nwritten);
4938 struct fruit_pwrite_state {
4940 struct vfs_aio_state vfs_aio_state;
4943 static void fruit_pwrite_done(struct tevent_req *subreq);
4945 static struct tevent_req *fruit_pwrite_send(
4946 struct vfs_handle_struct *handle,
4947 TALLOC_CTX *mem_ctx,
4948 struct tevent_context *ev,
4949 struct files_struct *fsp,
4951 size_t n, off_t offset)
4953 struct tevent_req *req = NULL;
4954 struct tevent_req *subreq = NULL;
4955 struct fruit_pwrite_state *state = NULL;
4956 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4958 req = tevent_req_create(mem_ctx, &state,
4959 struct fruit_pwrite_state);
4964 if (fruit_must_handle_aio_stream(fio)) {
4965 state->nwritten = SMB_VFS_PWRITE(fsp, data, n, offset);
4966 if (state->nwritten != n) {
4967 if (state->nwritten != -1) {
4970 tevent_req_error(req, errno);
4971 return tevent_req_post(req, ev);
4973 tevent_req_done(req);
4974 return tevent_req_post(req, ev);
4977 subreq = SMB_VFS_NEXT_PWRITE_SEND(state, ev, handle, fsp,
4979 if (tevent_req_nomem(req, subreq)) {
4980 return tevent_req_post(req, ev);
4982 tevent_req_set_callback(subreq, fruit_pwrite_done, req);
4986 static void fruit_pwrite_done(struct tevent_req *subreq)
4988 struct tevent_req *req = tevent_req_callback_data(
4989 subreq, struct tevent_req);
4990 struct fruit_pwrite_state *state = tevent_req_data(
4991 req, struct fruit_pwrite_state);
4993 state->nwritten = SMB_VFS_PWRITE_RECV(subreq, &state->vfs_aio_state);
4994 TALLOC_FREE(subreq);
4996 if (tevent_req_error(req, state->vfs_aio_state.error)) {
4999 tevent_req_done(req);
5002 static ssize_t fruit_pwrite_recv(struct tevent_req *req,
5003 struct vfs_aio_state *vfs_aio_state)
5005 struct fruit_pwrite_state *state = tevent_req_data(
5006 req, struct fruit_pwrite_state);
5008 if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
5012 *vfs_aio_state = state->vfs_aio_state;
5013 return state->nwritten;
5017 * Helper to stat/lstat the base file of an smb_fname.
5019 static int fruit_stat_base(vfs_handle_struct *handle,
5020 struct smb_filename *smb_fname,
5023 char *tmp_stream_name;
5026 tmp_stream_name = smb_fname->stream_name;
5027 smb_fname->stream_name = NULL;
5029 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
5031 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5033 smb_fname->stream_name = tmp_stream_name;
5035 DBG_DEBUG("fruit_stat_base [%s] dev [%ju] ino [%ju]\n",
5036 smb_fname->base_name,
5037 (uintmax_t)smb_fname->st.st_ex_dev,
5038 (uintmax_t)smb_fname->st.st_ex_ino);
5042 static int fruit_stat_meta_stream(vfs_handle_struct *handle,
5043 struct smb_filename *smb_fname,
5049 ret = fruit_stat_base(handle, smb_fname, false);
5054 ino = fruit_inode(&smb_fname->st, smb_fname->stream_name);
5057 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
5059 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5062 smb_fname->st.st_ex_ino = ino;
5067 static int fruit_stat_meta_netatalk(vfs_handle_struct *handle,
5068 struct smb_filename *smb_fname,
5071 struct adouble *ad = NULL;
5073 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5075 DBG_INFO("fruit_stat_meta %s: %s\n",
5076 smb_fname_str_dbg(smb_fname), strerror(errno));
5082 /* Populate the stat struct with info from the base file. */
5083 if (fruit_stat_base(handle, smb_fname, follow_links) == -1) {
5086 smb_fname->st.st_ex_size = AFP_INFO_SIZE;
5087 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5088 smb_fname->stream_name);
5092 static int fruit_stat_meta(vfs_handle_struct *handle,
5093 struct smb_filename *smb_fname,
5096 struct fruit_config_data *config = NULL;
5099 SMB_VFS_HANDLE_GET_DATA(handle, config,
5100 struct fruit_config_data, return -1);
5102 switch (config->meta) {
5103 case FRUIT_META_STREAM:
5104 ret = fruit_stat_meta_stream(handle, smb_fname, follow_links);
5107 case FRUIT_META_NETATALK:
5108 ret = fruit_stat_meta_netatalk(handle, smb_fname, follow_links);
5112 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
5119 static int fruit_stat_rsrc_netatalk(vfs_handle_struct *handle,
5120 struct smb_filename *smb_fname,
5123 struct adouble *ad = NULL;
5126 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
5132 /* Populate the stat struct with info from the base file. */
5133 ret = fruit_stat_base(handle, smb_fname, follow_links);
5139 smb_fname->st.st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
5140 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5141 smb_fname->stream_name);
5146 static int fruit_stat_rsrc_stream(vfs_handle_struct *handle,
5147 struct smb_filename *smb_fname,
5153 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
5155 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5161 static int fruit_stat_rsrc_xattr(vfs_handle_struct *handle,
5162 struct smb_filename *smb_fname,
5165 #ifdef HAVE_ATTROPEN
5169 /* Populate the stat struct with info from the base file. */
5170 ret = fruit_stat_base(handle, smb_fname, follow_links);
5175 fd = attropen(smb_fname->base_name,
5176 AFPRESOURCE_EA_NETATALK,
5182 ret = sys_fstat(fd, &smb_fname->st, false);
5185 DBG_ERR("fstat [%s:%s] failed\n", smb_fname->base_name,
5186 AFPRESOURCE_EA_NETATALK);
5192 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5193 smb_fname->stream_name);
5203 static int fruit_stat_rsrc(vfs_handle_struct *handle,
5204 struct smb_filename *smb_fname,
5207 struct fruit_config_data *config = NULL;
5210 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
5212 SMB_VFS_HANDLE_GET_DATA(handle, config,
5213 struct fruit_config_data, return -1);
5215 switch (config->rsrc) {
5216 case FRUIT_RSRC_STREAM:
5217 ret = fruit_stat_rsrc_stream(handle, smb_fname, follow_links);
5220 case FRUIT_RSRC_XATTR:
5221 ret = fruit_stat_rsrc_xattr(handle, smb_fname, follow_links);
5224 case FRUIT_RSRC_ADFILE:
5225 ret = fruit_stat_rsrc_netatalk(handle, smb_fname, follow_links);
5229 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
5236 static int fruit_stat(vfs_handle_struct *handle,
5237 struct smb_filename *smb_fname)
5241 DEBUG(10, ("fruit_stat called for %s\n",
5242 smb_fname_str_dbg(smb_fname)));
5244 if (!is_ntfs_stream_smb_fname(smb_fname)
5245 || is_ntfs_default_stream_smb_fname(smb_fname)) {
5246 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
5248 update_btime(handle, smb_fname);
5254 * Note if lp_posix_paths() is true, we can never
5255 * get here as is_ntfs_stream_smb_fname() is
5256 * always false. So we never need worry about
5257 * not following links here.
5260 if (is_afpinfo_stream(smb_fname)) {
5261 rc = fruit_stat_meta(handle, smb_fname, true);
5262 } else if (is_afpresource_stream(smb_fname)) {
5263 rc = fruit_stat_rsrc(handle, smb_fname, true);
5265 return SMB_VFS_NEXT_STAT(handle, smb_fname);
5269 update_btime(handle, smb_fname);
5270 smb_fname->st.st_ex_mode &= ~S_IFMT;
5271 smb_fname->st.st_ex_mode |= S_IFREG;
5272 smb_fname->st.st_ex_blocks =
5273 smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
5278 static int fruit_lstat(vfs_handle_struct *handle,
5279 struct smb_filename *smb_fname)
5283 DEBUG(10, ("fruit_lstat called for %s\n",
5284 smb_fname_str_dbg(smb_fname)));
5286 if (!is_ntfs_stream_smb_fname(smb_fname)
5287 || is_ntfs_default_stream_smb_fname(smb_fname)) {
5288 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5290 update_btime(handle, smb_fname);
5295 if (is_afpinfo_stream(smb_fname)) {
5296 rc = fruit_stat_meta(handle, smb_fname, false);
5297 } else if (is_afpresource_stream(smb_fname)) {
5298 rc = fruit_stat_rsrc(handle, smb_fname, false);
5300 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5304 update_btime(handle, smb_fname);
5305 smb_fname->st.st_ex_mode &= ~S_IFMT;
5306 smb_fname->st.st_ex_mode |= S_IFREG;
5307 smb_fname->st.st_ex_blocks =
5308 smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
5313 static int fruit_fstat_meta_stream(vfs_handle_struct *handle,
5315 SMB_STRUCT_STAT *sbuf)
5317 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5318 struct smb_filename smb_fname;
5327 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5332 *sbuf = fsp->base_fsp->fsp_name->st;
5333 sbuf->st_ex_size = AFP_INFO_SIZE;
5334 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5338 smb_fname = (struct smb_filename) {
5339 .base_name = fsp->fsp_name->base_name,
5342 ret = fruit_stat_base(handle, &smb_fname, false);
5346 *sbuf = smb_fname.st;
5348 ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5350 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5355 sbuf->st_ex_ino = ino;
5359 static int fruit_fstat_meta_netatalk(vfs_handle_struct *handle,
5361 SMB_STRUCT_STAT *sbuf)
5365 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5370 *sbuf = fsp->base_fsp->fsp_name->st;
5371 sbuf->st_ex_size = AFP_INFO_SIZE;
5372 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5377 static int fruit_fstat_meta(vfs_handle_struct *handle,
5379 SMB_STRUCT_STAT *sbuf,
5384 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
5386 switch (fio->config->meta) {
5387 case FRUIT_META_STREAM:
5388 ret = fruit_fstat_meta_stream(handle, fsp, sbuf);
5391 case FRUIT_META_NETATALK:
5392 ret = fruit_fstat_meta_netatalk(handle, fsp, sbuf);
5396 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
5400 DBG_DEBUG("Path [%s] ret [%d]\n", fsp_str_dbg(fsp), ret);
5404 static int fruit_fstat_rsrc_xattr(vfs_handle_struct *handle,
5406 SMB_STRUCT_STAT *sbuf)
5408 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5411 static int fruit_fstat_rsrc_stream(vfs_handle_struct *handle,
5413 SMB_STRUCT_STAT *sbuf)
5415 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5418 static int fruit_fstat_rsrc_adouble(vfs_handle_struct *handle,
5420 SMB_STRUCT_STAT *sbuf)
5422 struct adouble *ad = NULL;
5425 /* Populate the stat struct with info from the base file. */
5426 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5431 ad = ad_get(talloc_tos(), handle,
5432 fsp->base_fsp->fsp_name,
5435 DBG_ERR("ad_get [%s] failed [%s]\n",
5436 fsp_str_dbg(fsp), strerror(errno));
5440 *sbuf = fsp->base_fsp->fsp_name->st;
5441 sbuf->st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
5442 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5448 static int fruit_fstat_rsrc(vfs_handle_struct *handle, files_struct *fsp,
5449 SMB_STRUCT_STAT *sbuf, struct fio *fio)
5453 switch (fio->config->rsrc) {
5454 case FRUIT_RSRC_STREAM:
5455 ret = fruit_fstat_rsrc_stream(handle, fsp, sbuf);
5458 case FRUIT_RSRC_ADFILE:
5459 ret = fruit_fstat_rsrc_adouble(handle, fsp, sbuf);
5462 case FRUIT_RSRC_XATTR:
5463 ret = fruit_fstat_rsrc_xattr(handle, fsp, sbuf);
5467 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
5474 static int fruit_fstat(vfs_handle_struct *handle, files_struct *fsp,
5475 SMB_STRUCT_STAT *sbuf)
5477 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5481 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5484 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
5486 if (fio->type == ADOUBLE_META) {
5487 rc = fruit_fstat_meta(handle, fsp, sbuf, fio);
5489 rc = fruit_fstat_rsrc(handle, fsp, sbuf, fio);
5493 sbuf->st_ex_mode &= ~S_IFMT;
5494 sbuf->st_ex_mode |= S_IFREG;
5495 sbuf->st_ex_blocks = sbuf->st_ex_size / STAT_ST_BLOCKSIZE + 1;
5498 DBG_DEBUG("Path [%s] rc [%d] size [%"PRIdMAX"]\n",
5499 fsp_str_dbg(fsp), rc, (intmax_t)sbuf->st_ex_size);
5503 static NTSTATUS delete_invalid_meta_stream(
5504 vfs_handle_struct *handle,
5505 const struct smb_filename *smb_fname,
5506 TALLOC_CTX *mem_ctx,
5507 unsigned int *pnum_streams,
5508 struct stream_struct **pstreams,
5511 struct smb_filename *sname = NULL;
5515 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams, AFPINFO_STREAM);
5517 return NT_STATUS_INTERNAL_ERROR;
5521 return NT_STATUS_OK;
5524 sname = synthetic_smb_fname(talloc_tos(),
5525 smb_fname->base_name,
5526 AFPINFO_STREAM_NAME,
5528 if (sname == NULL) {
5529 return NT_STATUS_NO_MEMORY;
5532 ret = SMB_VFS_NEXT_UNLINK(handle, sname);
5535 DBG_ERR("Removing [%s] failed\n", smb_fname_str_dbg(sname));
5536 return map_nt_error_from_unix(errno);
5539 return NT_STATUS_OK;
5542 static NTSTATUS fruit_streaminfo_meta_stream(
5543 vfs_handle_struct *handle,
5544 struct files_struct *fsp,
5545 const struct smb_filename *smb_fname,
5546 TALLOC_CTX *mem_ctx,
5547 unsigned int *pnum_streams,
5548 struct stream_struct **pstreams)
5550 struct stream_struct *stream = *pstreams;
5551 unsigned int num_streams = *pnum_streams;
5554 for (i = 0; i < num_streams; i++) {
5555 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
5560 if (i == num_streams) {
5561 return NT_STATUS_OK;
5564 if (stream[i].size != AFP_INFO_SIZE) {
5565 DBG_ERR("Removing invalid AFPINFO_STREAM size [%jd] from [%s]\n",
5566 (intmax_t)stream[i].size, smb_fname_str_dbg(smb_fname));
5568 return delete_invalid_meta_stream(handle,
5577 return NT_STATUS_OK;
5580 static NTSTATUS fruit_streaminfo_meta_netatalk(
5581 vfs_handle_struct *handle,
5582 struct files_struct *fsp,
5583 const struct smb_filename *smb_fname,
5584 TALLOC_CTX *mem_ctx,
5585 unsigned int *pnum_streams,
5586 struct stream_struct **pstreams)
5588 struct stream_struct *stream = *pstreams;
5589 unsigned int num_streams = *pnum_streams;
5590 struct adouble *ad = NULL;
5595 /* Remove the Netatalk xattr from the list */
5596 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5597 ":" NETATALK_META_XATTR ":$DATA");
5599 return NT_STATUS_NO_MEMORY;
5603 * Check if there's a AFPINFO_STREAM from the VFS streams
5604 * backend and if yes, remove it from the list
5606 for (i = 0; i < num_streams; i++) {
5607 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
5612 if (i < num_streams) {
5613 DBG_WARNING("Unexpected AFPINFO_STREAM on [%s]\n",
5614 smb_fname_str_dbg(smb_fname));
5616 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5619 return NT_STATUS_INTERNAL_ERROR;
5623 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5625 return NT_STATUS_OK;
5628 is_fi_empty = ad_empty_finderinfo(ad);
5632 return NT_STATUS_OK;
5635 ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
5636 AFPINFO_STREAM_NAME, AFP_INFO_SIZE,
5637 smb_roundup(handle->conn, AFP_INFO_SIZE));
5639 return NT_STATUS_NO_MEMORY;
5642 return NT_STATUS_OK;
5645 static NTSTATUS fruit_streaminfo_meta(vfs_handle_struct *handle,
5646 struct files_struct *fsp,
5647 const struct smb_filename *smb_fname,
5648 TALLOC_CTX *mem_ctx,
5649 unsigned int *pnum_streams,
5650 struct stream_struct **pstreams)
5652 struct fruit_config_data *config = NULL;
5655 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5656 return NT_STATUS_INTERNAL_ERROR);
5658 switch (config->meta) {
5659 case FRUIT_META_NETATALK:
5660 status = fruit_streaminfo_meta_netatalk(handle, fsp, smb_fname,
5661 mem_ctx, pnum_streams,
5665 case FRUIT_META_STREAM:
5666 status = fruit_streaminfo_meta_stream(handle, fsp, smb_fname,
5667 mem_ctx, pnum_streams,
5672 return NT_STATUS_INTERNAL_ERROR;
5678 static NTSTATUS fruit_streaminfo_rsrc_stream(
5679 vfs_handle_struct *handle,
5680 struct files_struct *fsp,
5681 const struct smb_filename *smb_fname,
5682 TALLOC_CTX *mem_ctx,
5683 unsigned int *pnum_streams,
5684 struct stream_struct **pstreams)
5688 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
5690 DBG_ERR("Filtering resource stream failed\n");
5691 return NT_STATUS_INTERNAL_ERROR;
5693 return NT_STATUS_OK;
5696 static NTSTATUS fruit_streaminfo_rsrc_xattr(
5697 vfs_handle_struct *handle,
5698 struct files_struct *fsp,
5699 const struct smb_filename *smb_fname,
5700 TALLOC_CTX *mem_ctx,
5701 unsigned int *pnum_streams,
5702 struct stream_struct **pstreams)
5706 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
5708 DBG_ERR("Filtering resource stream failed\n");
5709 return NT_STATUS_INTERNAL_ERROR;
5711 return NT_STATUS_OK;
5714 static NTSTATUS fruit_streaminfo_rsrc_adouble(
5715 vfs_handle_struct *handle,
5716 struct files_struct *fsp,
5717 const struct smb_filename *smb_fname,
5718 TALLOC_CTX *mem_ctx,
5719 unsigned int *pnum_streams,
5720 struct stream_struct **pstreams)
5722 struct stream_struct *stream = *pstreams;
5723 unsigned int num_streams = *pnum_streams;
5724 struct adouble *ad = NULL;
5730 * Check if there's a AFPRESOURCE_STREAM from the VFS streams backend
5731 * and if yes, remove it from the list
5733 for (i = 0; i < num_streams; i++) {
5734 if (strequal_m(stream[i].name, AFPRESOURCE_STREAM)) {
5739 if (i < num_streams) {
5740 DBG_WARNING("Unexpected AFPRESOURCE_STREAM on [%s]\n",
5741 smb_fname_str_dbg(smb_fname));
5743 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5744 AFPRESOURCE_STREAM);
5746 return NT_STATUS_INTERNAL_ERROR;
5750 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
5752 return NT_STATUS_OK;
5755 rlen = ad_getentrylen(ad, ADEID_RFORK);
5759 return NT_STATUS_OK;
5762 ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
5763 AFPRESOURCE_STREAM_NAME, rlen,
5764 smb_roundup(handle->conn, rlen));
5766 return NT_STATUS_NO_MEMORY;
5769 return NT_STATUS_OK;
5772 static NTSTATUS fruit_streaminfo_rsrc(vfs_handle_struct *handle,
5773 struct files_struct *fsp,
5774 const struct smb_filename *smb_fname,
5775 TALLOC_CTX *mem_ctx,
5776 unsigned int *pnum_streams,
5777 struct stream_struct **pstreams)
5779 struct fruit_config_data *config = NULL;
5782 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5783 return NT_STATUS_INTERNAL_ERROR);
5785 switch (config->rsrc) {
5786 case FRUIT_RSRC_STREAM:
5787 status = fruit_streaminfo_rsrc_stream(handle, fsp, smb_fname,
5788 mem_ctx, pnum_streams,
5792 case FRUIT_RSRC_XATTR:
5793 status = fruit_streaminfo_rsrc_xattr(handle, fsp, smb_fname,
5794 mem_ctx, pnum_streams,
5798 case FRUIT_RSRC_ADFILE:
5799 status = fruit_streaminfo_rsrc_adouble(handle, fsp, smb_fname,
5800 mem_ctx, pnum_streams,
5805 return NT_STATUS_INTERNAL_ERROR;
5811 static void fruit_filter_empty_streams(unsigned int *pnum_streams,
5812 struct stream_struct **pstreams)
5814 unsigned num_streams = *pnum_streams;
5815 struct stream_struct *streams = *pstreams;
5818 if (!global_fruit_config.nego_aapl) {
5822 while (i < num_streams) {
5823 struct smb_filename smb_fname = (struct smb_filename) {
5824 .stream_name = streams[i].name,
5827 if (is_ntfs_default_stream_smb_fname(&smb_fname)
5828 || streams[i].size > 0)
5834 streams[i] = streams[num_streams - 1];
5838 *pnum_streams = num_streams;
5841 static NTSTATUS fruit_streaminfo(vfs_handle_struct *handle,
5842 struct files_struct *fsp,
5843 const struct smb_filename *smb_fname,
5844 TALLOC_CTX *mem_ctx,
5845 unsigned int *pnum_streams,
5846 struct stream_struct **pstreams)
5848 struct fruit_config_data *config = NULL;
5851 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5852 return NT_STATUS_UNSUCCESSFUL);
5854 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
5856 status = SMB_VFS_NEXT_STREAMINFO(handle, fsp, smb_fname, mem_ctx,
5857 pnum_streams, pstreams);
5858 if (!NT_STATUS_IS_OK(status)) {
5862 fruit_filter_empty_streams(pnum_streams, pstreams);
5864 status = fruit_streaminfo_meta(handle, fsp, smb_fname,
5865 mem_ctx, pnum_streams, pstreams);
5866 if (!NT_STATUS_IS_OK(status)) {
5870 status = fruit_streaminfo_rsrc(handle, fsp, smb_fname,
5871 mem_ctx, pnum_streams, pstreams);
5872 if (!NT_STATUS_IS_OK(status)) {
5876 return NT_STATUS_OK;
5879 static int fruit_ntimes(vfs_handle_struct *handle,
5880 const struct smb_filename *smb_fname,
5881 struct smb_file_time *ft)
5884 struct adouble *ad = NULL;
5885 struct fruit_config_data *config = NULL;
5887 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5890 if ((config->meta != FRUIT_META_NETATALK) ||
5891 null_timespec(ft->create_time))
5893 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
5896 DEBUG(10,("set btime for %s to %s\n", smb_fname_str_dbg(smb_fname),
5897 time_to_asc(convert_timespec_to_time_t(ft->create_time))));
5899 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5904 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX,
5905 convert_time_t_to_uint32_t(ft->create_time.tv_sec));
5907 rc = ad_set(handle, ad, smb_fname);
5913 DEBUG(1, ("fruit_ntimes: %s\n", smb_fname_str_dbg(smb_fname)));
5916 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
5919 static int fruit_fallocate(struct vfs_handle_struct *handle,
5920 struct files_struct *fsp,
5925 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5928 return SMB_VFS_NEXT_FALLOCATE(handle, fsp, mode, offset, len);
5931 /* Let the pwrite code path handle it. */
5936 static int fruit_ftruncate_rsrc_xattr(struct vfs_handle_struct *handle,
5937 struct files_struct *fsp,
5940 #ifdef HAVE_ATTROPEN
5941 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
5946 static int fruit_ftruncate_rsrc_adouble(struct vfs_handle_struct *handle,
5947 struct files_struct *fsp,
5951 struct adouble *ad = NULL;
5954 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
5956 DBG_DEBUG("ad_get [%s] failed [%s]\n",
5957 fsp_str_dbg(fsp), strerror(errno));
5961 ad_off = ad_getentryoff(ad, ADEID_RFORK);
5963 rc = ftruncate(fsp->fh->fd, offset + ad_off);
5969 ad_setentrylen(ad, ADEID_RFORK, offset);
5971 rc = ad_fset(handle, ad, fsp);
5973 DBG_ERR("ad_fset [%s] failed [%s]\n",
5974 fsp_str_dbg(fsp), strerror(errno));
5983 static int fruit_ftruncate_rsrc_stream(struct vfs_handle_struct *handle,
5984 struct files_struct *fsp,
5987 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
5990 static int fruit_ftruncate_rsrc(struct vfs_handle_struct *handle,
5991 struct files_struct *fsp,
5994 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5998 DBG_ERR("Failed to fetch fsp extension");
6002 switch (fio->config->rsrc) {
6003 case FRUIT_RSRC_XATTR:
6004 ret = fruit_ftruncate_rsrc_xattr(handle, fsp, offset);
6007 case FRUIT_RSRC_ADFILE:
6008 ret = fruit_ftruncate_rsrc_adouble(handle, fsp, offset);
6011 case FRUIT_RSRC_STREAM:
6012 ret = fruit_ftruncate_rsrc_stream(handle, fsp, offset);
6016 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
6024 static int fruit_ftruncate_meta(struct vfs_handle_struct *handle,
6025 struct files_struct *fsp,
6029 DBG_WARNING("ftruncate %s to %jd",
6030 fsp_str_dbg(fsp), (intmax_t)offset);
6031 /* OS X returns NT_STATUS_ALLOTTED_SPACE_EXCEEDED */
6036 /* OS X returns success but does nothing */
6037 DBG_INFO("ignoring ftruncate %s to %jd\n",
6038 fsp_str_dbg(fsp), (intmax_t)offset);
6042 static int fruit_ftruncate(struct vfs_handle_struct *handle,
6043 struct files_struct *fsp,
6046 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
6049 DBG_DEBUG("Path [%s] offset [%"PRIdMAX"]\n", fsp_str_dbg(fsp),
6053 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
6056 if (fio->type == ADOUBLE_META) {
6057 ret = fruit_ftruncate_meta(handle, fsp, offset);
6059 ret = fruit_ftruncate_rsrc(handle, fsp, offset);
6062 DBG_DEBUG("Path [%s] result [%d]\n", fsp_str_dbg(fsp), ret);
6066 static NTSTATUS fruit_create_file(vfs_handle_struct *handle,
6067 struct smb_request *req,
6068 uint16_t root_dir_fid,
6069 struct smb_filename *smb_fname,
6070 uint32_t access_mask,
6071 uint32_t share_access,
6072 uint32_t create_disposition,
6073 uint32_t create_options,
6074 uint32_t file_attributes,
6075 uint32_t oplock_request,
6076 struct smb2_lease *lease,
6077 uint64_t allocation_size,
6078 uint32_t private_flags,
6079 struct security_descriptor *sd,
6080 struct ea_list *ea_list,
6081 files_struct **result,
6083 const struct smb2_create_blobs *in_context_blobs,
6084 struct smb2_create_blobs *out_context_blobs)
6087 struct fruit_config_data *config = NULL;
6088 files_struct *fsp = NULL;
6089 struct fio *fio = NULL;
6090 bool internal_open = (oplock_request & INTERNAL_OPEN_ONLY);
6093 status = check_aapl(handle, req, in_context_blobs, out_context_blobs);
6094 if (!NT_STATUS_IS_OK(status)) {
6098 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
6099 return NT_STATUS_UNSUCCESSFUL);
6101 if (is_apple_stream(smb_fname) && !internal_open) {
6102 ret = ad_convert(handle, smb_fname);
6104 DBG_ERR("ad_convert() failed\n");
6105 return NT_STATUS_UNSUCCESSFUL;
6109 status = SMB_VFS_NEXT_CREATE_FILE(
6110 handle, req, root_dir_fid, smb_fname,
6111 access_mask, share_access,
6112 create_disposition, create_options,
6113 file_attributes, oplock_request,
6115 allocation_size, private_flags,
6116 sd, ea_list, result,
6117 pinfo, in_context_blobs, out_context_blobs);
6118 if (!NT_STATUS_IS_OK(status)) {
6124 if (global_fruit_config.nego_aapl) {
6125 if (config->posix_rename && fsp->is_directory) {
6127 * Enable POSIX directory rename behaviour
6129 fsp->posix_flags |= FSP_POSIX_FLAGS_RENAME;
6134 * If this is a plain open for existing files, opening an 0
6135 * byte size resource fork MUST fail with
6136 * NT_STATUS_OBJECT_NAME_NOT_FOUND.
6138 * Cf the vfs_fruit torture tests in test_rfork_create().
6140 if (global_fruit_config.nego_aapl &&
6141 create_disposition == FILE_OPEN &&
6142 smb_fname->st.st_ex_size == 0 &&
6143 is_ntfs_stream_smb_fname(smb_fname) &&
6144 !(is_ntfs_default_stream_smb_fname(smb_fname)))
6146 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
6150 fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
6151 if (fio != NULL && pinfo != NULL && *pinfo == FILE_WAS_CREATED) {
6152 fio->created = true;
6155 if (is_ntfs_stream_smb_fname(smb_fname)
6156 || fsp->is_directory) {
6160 if (config->locking == FRUIT_LOCKING_NETATALK) {
6161 status = fruit_check_access(
6165 if (!NT_STATUS_IS_OK(status)) {
6173 DEBUG(10, ("fruit_create_file: %s\n", nt_errstr(status)));
6176 close_file(req, fsp, ERROR_CLOSE);
6177 *result = fsp = NULL;
6183 static NTSTATUS fruit_readdir_attr(struct vfs_handle_struct *handle,
6184 const struct smb_filename *fname,
6185 TALLOC_CTX *mem_ctx,
6186 struct readdir_attr_data **pattr_data)
6188 struct fruit_config_data *config = NULL;
6189 struct readdir_attr_data *attr_data;
6193 SMB_VFS_HANDLE_GET_DATA(handle, config,
6194 struct fruit_config_data,
6195 return NT_STATUS_UNSUCCESSFUL);
6197 if (!global_fruit_config.nego_aapl) {
6198 return SMB_VFS_NEXT_READDIR_ATTR(handle, fname, mem_ctx, pattr_data);
6201 DEBUG(10, ("fruit_readdir_attr %s\n", fname->base_name));
6203 ret = ad_convert(handle, fname);
6205 DBG_ERR("ad_convert() failed\n");
6206 return NT_STATUS_UNSUCCESSFUL;
6209 *pattr_data = talloc_zero(mem_ctx, struct readdir_attr_data);
6210 if (*pattr_data == NULL) {
6211 return NT_STATUS_UNSUCCESSFUL;
6213 attr_data = *pattr_data;
6214 attr_data->type = RDATTR_AAPL;
6217 * Mac metadata: compressed FinderInfo, resource fork length
6220 status = readdir_attr_macmeta(handle, fname, attr_data);
6221 if (!NT_STATUS_IS_OK(status)) {
6223 * Error handling is tricky: if we return failure from
6224 * this function, the corresponding directory entry
6225 * will to be passed to the client, so we really just
6226 * want to error out on fatal errors.
6228 if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
6236 if (config->unix_info_enabled) {
6237 attr_data->attr_data.aapl.unix_mode = fname->st.st_ex_mode;
6243 if (!config->readdir_attr_max_access) {
6244 attr_data->attr_data.aapl.max_access = FILE_GENERIC_ALL;
6246 status = smbd_calculate_access_mask(
6250 SEC_FLAG_MAXIMUM_ALLOWED,
6251 &attr_data->attr_data.aapl.max_access);
6252 if (!NT_STATUS_IS_OK(status)) {
6257 return NT_STATUS_OK;
6260 DEBUG(1, ("fruit_readdir_attr %s, error: %s\n",
6261 fname->base_name, nt_errstr(status)));
6262 TALLOC_FREE(*pattr_data);
6266 static NTSTATUS fruit_fget_nt_acl(vfs_handle_struct *handle,
6268 uint32_t security_info,
6269 TALLOC_CTX *mem_ctx,
6270 struct security_descriptor **ppdesc)
6273 struct security_ace ace;
6275 struct fruit_config_data *config;
6277 SMB_VFS_HANDLE_GET_DATA(handle, config,
6278 struct fruit_config_data,
6279 return NT_STATUS_UNSUCCESSFUL);
6281 status = SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
6283 if (!NT_STATUS_IS_OK(status)) {
6288 * Add MS NFS style ACEs with uid, gid and mode
6290 if (!global_fruit_config.nego_aapl) {
6291 return NT_STATUS_OK;
6293 if (!config->unix_info_enabled) {
6294 return NT_STATUS_OK;
6297 /* First remove any existing ACE's with NFS style mode/uid/gid SIDs. */
6298 status = remove_virtual_nfs_aces(*ppdesc);
6299 if (!NT_STATUS_IS_OK(status)) {
6300 DBG_WARNING("failed to remove MS NFS style ACEs\n");
6304 /* MS NFS style mode */
6305 sid_compose(&sid, &global_sid_Unix_NFS_Mode, fsp->fsp_name->st.st_ex_mode);
6306 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6307 status = security_descriptor_dacl_add(*ppdesc, &ace);
6308 if (!NT_STATUS_IS_OK(status)) {
6309 DEBUG(1,("failed to add MS NFS style ACE\n"));
6313 /* MS NFS style uid */
6314 sid_compose(&sid, &global_sid_Unix_NFS_Users, fsp->fsp_name->st.st_ex_uid);
6315 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6316 status = security_descriptor_dacl_add(*ppdesc, &ace);
6317 if (!NT_STATUS_IS_OK(status)) {
6318 DEBUG(1,("failed to add MS NFS style ACE\n"));
6322 /* MS NFS style gid */
6323 sid_compose(&sid, &global_sid_Unix_NFS_Groups, fsp->fsp_name->st.st_ex_gid);
6324 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6325 status = security_descriptor_dacl_add(*ppdesc, &ace);
6326 if (!NT_STATUS_IS_OK(status)) {
6327 DEBUG(1,("failed to add MS NFS style ACE\n"));
6331 return NT_STATUS_OK;
6334 static NTSTATUS fruit_fset_nt_acl(vfs_handle_struct *handle,
6336 uint32_t security_info_sent,
6337 const struct security_descriptor *orig_psd)
6341 mode_t ms_nfs_mode = 0;
6343 struct security_descriptor *psd = NULL;
6344 uint32_t orig_num_aces = 0;
6346 if (orig_psd->dacl != NULL) {
6347 orig_num_aces = orig_psd->dacl->num_aces;
6350 psd = security_descriptor_copy(talloc_tos(), orig_psd);
6352 return NT_STATUS_NO_MEMORY;
6355 DBG_DEBUG("fruit_fset_nt_acl: %s\n", fsp_str_dbg(fsp));
6357 status = check_ms_nfs(handle, fsp, psd, &ms_nfs_mode, &do_chmod);
6358 if (!NT_STATUS_IS_OK(status)) {
6359 DEBUG(1, ("fruit_fset_nt_acl: check_ms_nfs failed%s\n", fsp_str_dbg(fsp)));
6365 * If only ms_nfs ACE entries were sent, ensure we set the DACL
6366 * sent/present flags correctly now we've removed them.
6369 if (orig_num_aces != 0) {
6371 * Are there any ACE's left ?
6373 if (psd->dacl->num_aces == 0) {
6374 /* No - clear the DACL sent/present flags. */
6375 security_info_sent &= ~SECINFO_DACL;
6376 psd->type &= ~SEC_DESC_DACL_PRESENT;
6380 status = SMB_VFS_NEXT_FSET_NT_ACL(handle, fsp, security_info_sent, psd);
6381 if (!NT_STATUS_IS_OK(status)) {
6382 DEBUG(1, ("fruit_fset_nt_acl: SMB_VFS_NEXT_FSET_NT_ACL failed%s\n", fsp_str_dbg(fsp)));
6388 if (fsp->fh->fd != -1) {
6389 result = SMB_VFS_FCHMOD(fsp, ms_nfs_mode);
6391 result = SMB_VFS_CHMOD(fsp->conn,
6397 DEBUG(1, ("chmod: %s, result: %d, %04o error %s\n", fsp_str_dbg(fsp),
6398 result, (unsigned)ms_nfs_mode,
6400 status = map_nt_error_from_unix(errno);
6407 return NT_STATUS_OK;
6410 static struct vfs_offload_ctx *fruit_offload_ctx;
6412 struct fruit_offload_read_state {
6413 struct vfs_handle_struct *handle;
6414 struct tevent_context *ev;
6420 static void fruit_offload_read_done(struct tevent_req *subreq);
6422 static struct tevent_req *fruit_offload_read_send(
6423 TALLOC_CTX *mem_ctx,
6424 struct tevent_context *ev,
6425 struct vfs_handle_struct *handle,
6432 struct tevent_req *req = NULL;
6433 struct tevent_req *subreq = NULL;
6434 struct fruit_offload_read_state *state = NULL;
6436 req = tevent_req_create(mem_ctx, &state,
6437 struct fruit_offload_read_state);
6441 *state = (struct fruit_offload_read_state) {
6448 subreq = SMB_VFS_NEXT_OFFLOAD_READ_SEND(mem_ctx, ev, handle, fsp,
6449 fsctl, ttl, offset, to_copy);
6450 if (tevent_req_nomem(subreq, req)) {
6451 return tevent_req_post(req, ev);
6453 tevent_req_set_callback(subreq, fruit_offload_read_done, req);
6457 static void fruit_offload_read_done(struct tevent_req *subreq)
6459 struct tevent_req *req = tevent_req_callback_data(
6460 subreq, struct tevent_req);
6461 struct fruit_offload_read_state *state = tevent_req_data(
6462 req, struct fruit_offload_read_state);
6465 status = SMB_VFS_NEXT_OFFLOAD_READ_RECV(subreq,
6469 TALLOC_FREE(subreq);
6470 if (tevent_req_nterror(req, status)) {
6474 if (state->fsctl != FSCTL_SRV_REQUEST_RESUME_KEY) {
6475 tevent_req_done(req);
6479 status = vfs_offload_token_ctx_init(state->fsp->conn->sconn->client,
6480 &fruit_offload_ctx);
6481 if (tevent_req_nterror(req, status)) {
6485 status = vfs_offload_token_db_store_fsp(fruit_offload_ctx,
6488 if (tevent_req_nterror(req, status)) {
6492 tevent_req_done(req);
6496 static NTSTATUS fruit_offload_read_recv(struct tevent_req *req,
6497 struct vfs_handle_struct *handle,
6498 TALLOC_CTX *mem_ctx,
6501 struct fruit_offload_read_state *state = tevent_req_data(
6502 req, struct fruit_offload_read_state);
6505 if (tevent_req_is_nterror(req, &status)) {
6506 tevent_req_received(req);
6510 token->length = state->token.length;
6511 token->data = talloc_move(mem_ctx, &state->token.data);
6513 tevent_req_received(req);
6514 return NT_STATUS_OK;
6517 struct fruit_offload_write_state {
6518 struct vfs_handle_struct *handle;
6520 struct files_struct *src_fsp;
6521 struct files_struct *dst_fsp;
6525 static void fruit_offload_write_done(struct tevent_req *subreq);
6526 static struct tevent_req *fruit_offload_write_send(struct vfs_handle_struct *handle,
6527 TALLOC_CTX *mem_ctx,
6528 struct tevent_context *ev,
6531 off_t transfer_offset,
6532 struct files_struct *dest_fsp,
6536 struct tevent_req *req, *subreq;
6537 struct fruit_offload_write_state *state;
6539 struct fruit_config_data *config;
6540 off_t src_off = transfer_offset;
6541 files_struct *src_fsp = NULL;
6542 off_t to_copy = num;
6543 bool copyfile_enabled = false;
6545 DEBUG(10,("soff: %ju, doff: %ju, len: %ju\n",
6546 (uintmax_t)src_off, (uintmax_t)dest_off, (uintmax_t)num));
6548 SMB_VFS_HANDLE_GET_DATA(handle, config,
6549 struct fruit_config_data,
6552 req = tevent_req_create(mem_ctx, &state,
6553 struct fruit_offload_write_state);
6557 state->handle = handle;
6558 state->dst_fsp = dest_fsp;
6561 case FSCTL_SRV_COPYCHUNK:
6562 case FSCTL_SRV_COPYCHUNK_WRITE:
6563 copyfile_enabled = config->copyfile_enabled;
6570 * Check if this a OS X copyfile style copychunk request with
6571 * a requested chunk count of 0 that was translated to a
6572 * offload_write_send VFS call overloading the parameters src_off
6573 * = dest_off = num = 0.
6575 if (copyfile_enabled && num == 0 && src_off == 0 && dest_off == 0) {
6576 status = vfs_offload_token_db_fetch_fsp(
6577 fruit_offload_ctx, token, &src_fsp);
6578 if (tevent_req_nterror(req, status)) {
6579 return tevent_req_post(req, ev);
6581 state->src_fsp = src_fsp;
6583 status = vfs_stat_fsp(src_fsp);
6584 if (tevent_req_nterror(req, status)) {
6585 return tevent_req_post(req, ev);
6588 to_copy = src_fsp->fsp_name->st.st_ex_size;
6589 state->is_copyfile = true;
6592 subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle,
6601 if (tevent_req_nomem(subreq, req)) {
6602 return tevent_req_post(req, ev);
6605 tevent_req_set_callback(subreq, fruit_offload_write_done, req);
6609 static void fruit_offload_write_done(struct tevent_req *subreq)
6611 struct tevent_req *req = tevent_req_callback_data(
6612 subreq, struct tevent_req);
6613 struct fruit_offload_write_state *state = tevent_req_data(
6614 req, struct fruit_offload_write_state);
6616 unsigned int num_streams = 0;
6617 struct stream_struct *streams = NULL;
6619 struct smb_filename *src_fname_tmp = NULL;
6620 struct smb_filename *dst_fname_tmp = NULL;
6622 status = SMB_VFS_NEXT_OFFLOAD_WRITE_RECV(state->handle,
6625 TALLOC_FREE(subreq);
6626 if (tevent_req_nterror(req, status)) {
6630 if (!state->is_copyfile) {
6631 tevent_req_done(req);
6636 * Now copy all remaining streams. We know the share supports
6637 * streams, because we're in vfs_fruit. We don't do this async
6638 * because streams are few and small.
6640 status = vfs_streaminfo(state->handle->conn, state->src_fsp,
6641 state->src_fsp->fsp_name,
6642 req, &num_streams, &streams);
6643 if (tevent_req_nterror(req, status)) {
6647 if (num_streams == 1) {
6648 /* There is always one stream, ::$DATA. */
6649 tevent_req_done(req);
6653 for (i = 0; i < num_streams; i++) {
6654 DEBUG(10, ("%s: stream: '%s'/%zu\n",
6655 __func__, streams[i].name, (size_t)streams[i].size));
6657 src_fname_tmp = synthetic_smb_fname(
6659 state->src_fsp->fsp_name->base_name,
6662 state->src_fsp->fsp_name->flags);
6663 if (tevent_req_nomem(src_fname_tmp, req)) {
6667 if (is_ntfs_default_stream_smb_fname(src_fname_tmp)) {
6668 TALLOC_FREE(src_fname_tmp);
6672 dst_fname_tmp = synthetic_smb_fname(
6674 state->dst_fsp->fsp_name->base_name,
6677 state->dst_fsp->fsp_name->flags);
6678 if (tevent_req_nomem(dst_fname_tmp, req)) {
6679 TALLOC_FREE(src_fname_tmp);
6683 status = copy_file(req,
6684 state->handle->conn,
6687 OPENX_FILE_CREATE_IF_NOT_EXIST,
6689 if (!NT_STATUS_IS_OK(status)) {
6690 DEBUG(1, ("%s: copy %s to %s failed: %s\n", __func__,
6691 smb_fname_str_dbg(src_fname_tmp),
6692 smb_fname_str_dbg(dst_fname_tmp),
6693 nt_errstr(status)));
6694 TALLOC_FREE(src_fname_tmp);
6695 TALLOC_FREE(dst_fname_tmp);
6696 tevent_req_nterror(req, status);
6700 TALLOC_FREE(src_fname_tmp);
6701 TALLOC_FREE(dst_fname_tmp);
6704 TALLOC_FREE(streams);
6705 TALLOC_FREE(src_fname_tmp);
6706 TALLOC_FREE(dst_fname_tmp);
6707 tevent_req_done(req);
6710 static NTSTATUS fruit_offload_write_recv(struct vfs_handle_struct *handle,
6711 struct tevent_req *req,
6714 struct fruit_offload_write_state *state = tevent_req_data(
6715 req, struct fruit_offload_write_state);
6718 if (tevent_req_is_nterror(req, &status)) {
6719 DEBUG(1, ("server side copy chunk failed: %s\n",
6720 nt_errstr(status)));
6722 tevent_req_received(req);
6726 *copied = state->copied;
6727 tevent_req_received(req);
6729 return NT_STATUS_OK;
6732 static char *fruit_get_bandsize_line(char **lines, int numlines)
6735 static bool re_initialized = false;
6739 if (!re_initialized) {
6740 ret = regcomp(&re, "^[[:blank:]]*<key>band-size</key>$", 0);
6744 re_initialized = true;
6747 for (i = 0; i < numlines; i++) {
6748 regmatch_t matches[1];
6750 ret = regexec(&re, lines[i], 1, matches, 0);
6753 * Check if the match was on the last line, sa we want
6754 * the subsequent line.
6756 if (i + 1 == numlines) {
6759 return lines[i + 1];
6761 if (ret != REG_NOMATCH) {
6769 static bool fruit_get_bandsize_from_line(char *line, size_t *_band_size)
6772 static bool re_initialized = false;
6773 regmatch_t matches[2];
6778 if (!re_initialized) {
6781 "<integer>\\([[:digit:]]*\\)</integer>$",
6786 re_initialized = true;
6789 ret = regexec(&re, line, 2, matches, 0);
6791 DBG_ERR("regex failed [%s]\n", line);
6795 line[matches[1].rm_eo] = '\0';
6797 ok = conv_str_u64(&line[matches[1].rm_so], &band_size);
6801 *_band_size = (size_t)band_size;
6806 * This reads and parses an Info.plist from a TM sparsebundle looking for the
6807 * "band-size" key and value.
6809 static bool fruit_get_bandsize(vfs_handle_struct *handle,
6813 #define INFO_PLIST_MAX_SIZE 64*1024
6815 struct smb_filename *smb_fname = NULL;
6816 files_struct *fsp = NULL;
6817 uint8_t *file_data = NULL;
6818 char **lines = NULL;
6819 char *band_size_line = NULL;
6820 size_t plist_file_size;
6827 plist = talloc_asprintf(talloc_tos(),
6829 handle->conn->connectpath,
6831 if (plist == NULL) {
6836 smb_fname = synthetic_smb_fname(talloc_tos(), plist, NULL, NULL, 0);
6837 if (smb_fname == NULL) {
6842 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
6844 DBG_INFO("Ignoring Sparsebundle without Info.plist [%s]\n", dir);
6849 plist_file_size = smb_fname->st.st_ex_size;
6851 if (plist_file_size > INFO_PLIST_MAX_SIZE) {
6852 DBG_INFO("%s is too large, ignoring\n", plist);
6857 status = SMB_VFS_NEXT_CREATE_FILE(
6860 0, /* root_dir_fid */
6861 smb_fname, /* fname */
6862 FILE_GENERIC_READ, /* access_mask */
6863 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
6864 FILE_OPEN, /* create_disposition */
6865 0, /* create_options */
6866 0, /* file_attributes */
6867 INTERNAL_OPEN_ONLY, /* oplock_request */
6869 0, /* allocation_size */
6870 0, /* private_flags */
6875 NULL, NULL); /* create context */
6876 if (!NT_STATUS_IS_OK(status)) {
6877 DBG_INFO("Opening [%s] failed [%s]\n",
6878 smb_fname_str_dbg(smb_fname), nt_errstr(status));
6883 file_data = talloc_array(talloc_tos(), uint8_t, plist_file_size);
6884 if (file_data == NULL) {
6889 nread = SMB_VFS_NEXT_PREAD(handle, fsp, file_data, plist_file_size, 0);
6890 if (nread != plist_file_size) {
6891 DBG_ERR("Short read on [%s]: %zu/%zd\n",
6892 fsp_str_dbg(fsp), nread, plist_file_size);
6898 status = close_file(NULL, fsp, NORMAL_CLOSE);
6900 if (!NT_STATUS_IS_OK(status)) {
6901 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
6906 lines = file_lines_parse((char *)file_data,
6910 if (lines == NULL) {
6915 band_size_line = fruit_get_bandsize_line(lines, numlines);
6916 if (band_size_line == NULL) {
6917 DBG_ERR("Didn't find band-size key in [%s]\n",
6918 smb_fname_str_dbg(smb_fname));
6923 ok = fruit_get_bandsize_from_line(band_size_line, band_size);
6925 DBG_ERR("fruit_get_bandsize_from_line failed\n");
6929 DBG_DEBUG("Parsed band-size [%zu] for [%s]\n", *band_size, plist);
6933 status = close_file(NULL, fsp, NORMAL_CLOSE);
6934 if (!NT_STATUS_IS_OK(status)) {
6935 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
6940 TALLOC_FREE(smb_fname);
6941 TALLOC_FREE(file_data);
6946 struct fruit_disk_free_state {
6950 static bool fruit_get_num_bands(vfs_handle_struct *handle,
6955 struct smb_filename *bands_dir = NULL;
6957 struct dirent *e = NULL;
6961 path = talloc_asprintf(talloc_tos(),
6963 handle->conn->connectpath,
6969 bands_dir = synthetic_smb_fname(talloc_tos(),
6975 if (bands_dir == NULL) {
6979 d = SMB_VFS_NEXT_OPENDIR(handle, bands_dir, NULL, 0);
6981 TALLOC_FREE(bands_dir);
6987 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
6989 e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
6991 if (ISDOT(e->d_name) || ISDOTDOT(e->d_name)) {
6997 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
6999 TALLOC_FREE(bands_dir);
7003 DBG_DEBUG("%zu bands in [%s]\n", nbands, smb_fname_str_dbg(bands_dir));
7005 TALLOC_FREE(bands_dir);
7011 static bool fruit_tmsize_do_dirent(vfs_handle_struct *handle,
7012 struct fruit_disk_free_state *state,
7017 size_t sparsebundle_strlen = strlen("sparsebundle");
7018 size_t bandsize = 0;
7022 p = strstr(e->d_name, "sparsebundle");
7027 if (p[sparsebundle_strlen] != '\0') {
7031 DBG_DEBUG("Processing sparsebundle [%s]\n", e->d_name);
7033 ok = fruit_get_bandsize(handle, e->d_name, &bandsize);
7036 * Beware of race conditions: this may be an uninitialized
7037 * Info.plist that a client is just creating. We don't want let
7038 * this to trigger complete failure.
7040 DBG_ERR("Processing sparsebundle [%s] failed\n", e->d_name);
7044 ok = fruit_get_num_bands(handle, e->d_name, &nbands);
7047 * Beware of race conditions: this may be a backup sparsebundle
7048 * in an early stage lacking a bands subdirectory. We don't want
7049 * let this to trigger complete failure.
7051 DBG_ERR("Processing sparsebundle [%s] failed\n", e->d_name);
7055 if (bandsize > SIZE_MAX/nbands) {
7056 DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
7060 tm_size = bandsize * nbands;
7062 if (state->total_size + tm_size < state->total_size) {
7063 DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
7068 state->total_size += tm_size;
7070 DBG_DEBUG("[%s] tm_size [%jd] total_size [%jd]\n",
7071 e->d_name, (intmax_t)tm_size, (intmax_t)state->total_size);
7077 * Calculate used size of a TimeMachine volume
7079 * This assumes that the volume is used only for TimeMachine.
7081 * - readdir(basedir of share), then
7082 * - for every element that matches regex "^\(.*\)\.sparsebundle$" :
7083 * - parse "\1.sparsebundle/Info.plist" and read the band-size XML key
7084 * - count band files in "\1.sparsebundle/bands/"
7085 * - calculate used size of all bands: band_count * band_size
7087 static uint64_t fruit_disk_free(vfs_handle_struct *handle,
7088 const struct smb_filename *smb_fname,
7093 struct fruit_config_data *config = NULL;
7094 struct fruit_disk_free_state state = {0};
7096 struct dirent *e = NULL;
7102 SMB_VFS_HANDLE_GET_DATA(handle, config,
7103 struct fruit_config_data,
7106 if (!config->time_machine ||
7107 config->time_machine_max_size == 0)
7109 return SMB_VFS_NEXT_DISK_FREE(handle,
7116 d = SMB_VFS_NEXT_OPENDIR(handle, smb_fname, NULL, 0);
7121 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
7123 e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
7125 ok = fruit_tmsize_do_dirent(handle, &state, e);
7127 SMB_VFS_NEXT_CLOSEDIR(handle, d);
7132 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
7137 dsize = config->time_machine_max_size / 512;
7138 dfree = dsize - (state.total_size / 512);
7139 if (dfree > dsize) {
7149 static struct vfs_fn_pointers vfs_fruit_fns = {
7150 .connect_fn = fruit_connect,
7151 .disk_free_fn = fruit_disk_free,
7153 /* File operations */
7154 .chmod_fn = fruit_chmod,
7155 .chown_fn = fruit_chown,
7156 .unlink_fn = fruit_unlink,
7157 .rename_fn = fruit_rename,
7158 .rmdir_fn = fruit_rmdir,
7159 .open_fn = fruit_open,
7160 .close_fn = fruit_close,
7161 .pread_fn = fruit_pread,
7162 .pwrite_fn = fruit_pwrite,
7163 .pread_send_fn = fruit_pread_send,
7164 .pread_recv_fn = fruit_pread_recv,
7165 .pwrite_send_fn = fruit_pwrite_send,
7166 .pwrite_recv_fn = fruit_pwrite_recv,
7167 .stat_fn = fruit_stat,
7168 .lstat_fn = fruit_lstat,
7169 .fstat_fn = fruit_fstat,
7170 .streaminfo_fn = fruit_streaminfo,
7171 .ntimes_fn = fruit_ntimes,
7172 .ftruncate_fn = fruit_ftruncate,
7173 .fallocate_fn = fruit_fallocate,
7174 .create_file_fn = fruit_create_file,
7175 .readdir_attr_fn = fruit_readdir_attr,
7176 .offload_read_send_fn = fruit_offload_read_send,
7177 .offload_read_recv_fn = fruit_offload_read_recv,
7178 .offload_write_send_fn = fruit_offload_write_send,
7179 .offload_write_recv_fn = fruit_offload_write_recv,
7181 /* NT ACL operations */
7182 .fget_nt_acl_fn = fruit_fget_nt_acl,
7183 .fset_nt_acl_fn = fruit_fset_nt_acl,
7187 NTSTATUS vfs_fruit_init(TALLOC_CTX *ctx)
7189 NTSTATUS ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "fruit",
7191 if (!NT_STATUS_IS_OK(ret)) {
7195 vfs_fruit_debug_level = debug_add_class("fruit");
7196 if (vfs_fruit_debug_level == -1) {
7197 vfs_fruit_debug_level = DBGC_VFS;
7198 DEBUG(0, ("%s: Couldn't register custom debugging class!\n",
7201 DEBUG(10, ("%s: Debug class number of '%s': %d\n",
7202 "vfs_fruit_init","fruit",vfs_fruit_debug_level));