2 * OS X and Netatalk interoperability VFS module for Samba-3.x
4 * Copyright (C) Ralph Boehme, 2013, 2014
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
21 #include "MacExtensions.h"
22 #include "smbd/smbd.h"
23 #include "system/filesys.h"
24 #include "lib/util/time.h"
25 #include "system/shmem.h"
26 #include "locking/proto.h"
27 #include "smbd/globals.h"
29 #include "libcli/security/security.h"
30 #include "../libcli/smb/smb2_create_ctx.h"
31 #include "lib/util/sys_rw.h"
32 #include "lib/util/tevent_ntstatus.h"
33 #include "lib/util/tevent_unix.h"
34 #include "offload_token.h"
35 #include "string_replace.h"
37 #include <gnutls/gnutls.h>
38 #include <gnutls/crypto.h>
41 * Enhanced OS X and Netatalk compatibility
42 * ========================================
44 * This modules takes advantage of vfs_streams_xattr and
45 * vfs_catia. VFS modules vfs_fruit and vfs_streams_xattr must be
46 * loaded in the correct order:
48 * vfs modules = catia fruit streams_xattr
50 * The module intercepts the OS X special streams "AFP_AfpInfo" and
51 * "AFP_Resource" and handles them in a special way. All other named
52 * streams are deferred to vfs_streams_xattr.
54 * The OS X client maps all NTFS illegal characters to the Unicode
55 * private range. This module optionally stores the charcters using
56 * their native ASCII encoding using vfs_catia. If you're not enabling
57 * this feature, you can skip catia from vfs modules.
59 * Finally, open modes are optionally checked against Netatalk AFP
62 * The "AFP_AfpInfo" named stream is a binary blob containing OS X
63 * extended metadata for files and directories. This module optionally
64 * reads and stores this metadata in a way compatible with Netatalk 3
65 * which stores the metadata in an EA "org.netatalk.metadata". Cf
66 * source3/include/MacExtensions.h for a description of the binary
69 * The "AFP_Resource" named stream may be arbitrarily large, thus it
70 * can't be stored in an xattr on most filesystem. ZFS on Solaris is
71 * the only available filesystem where xattrs can be of any size and
72 * the OS supports using the file APIs for xattrs.
74 * The AFP_Resource stream is stored in an AppleDouble file prepending
75 * "._" to the filename. On Solaris with ZFS the stream is optionally
76 * stored in an EA "org.netatalk.resource".
82 * The OS X SMB client sends xattrs as ADS too. For xattr interop with
83 * other protocols you may want to adjust the xattr names the VFS
84 * module vfs_streams_xattr uses for storing ADS's. This defaults to
85 * user.DosStream.ADS_NAME:$DATA and can be changed by specifying
86 * these module parameters:
88 * streams_xattr:prefix = user.
89 * streams_xattr:store_stream_type = false
95 * - log diagnostic if any needed VFS module is not loaded
96 * (eg with lp_vfs_objects())
100 static int vfs_fruit_debug_level = DBGC_VFS;
102 static struct global_fruit_config {
103 bool nego_aapl; /* client negotiated AAPL */
105 } global_fruit_config;
108 #define DBGC_CLASS vfs_fruit_debug_level
110 #define FRUIT_PARAM_TYPE_NAME "fruit"
111 #define ADOUBLE_NAME_PREFIX "._"
113 #define NETATALK_META_XATTR "org.netatalk.Metadata"
114 #define NETATALK_RSRC_XATTR "org.netatalk.ResourceFork"
116 #if defined(HAVE_ATTROPEN)
117 #define AFPINFO_EA_NETATALK NETATALK_META_XATTR
118 #define AFPRESOURCE_EA_NETATALK NETATALK_RSRC_XATTR
120 #define AFPINFO_EA_NETATALK "user." NETATALK_META_XATTR
121 #define AFPRESOURCE_EA_NETATALK "user." NETATALK_RSRC_XATTR
124 enum apple_fork {APPLE_FORK_DATA, APPLE_FORK_RSRC};
126 enum fruit_rsrc {FRUIT_RSRC_STREAM, FRUIT_RSRC_ADFILE, FRUIT_RSRC_XATTR};
127 enum fruit_meta {FRUIT_META_STREAM, FRUIT_META_NETATALK};
128 enum fruit_locking {FRUIT_LOCKING_NETATALK, FRUIT_LOCKING_NONE};
129 enum fruit_encoding {FRUIT_ENC_NATIVE, FRUIT_ENC_PRIVATE};
131 struct fruit_config_data {
132 enum fruit_rsrc rsrc;
133 enum fruit_meta meta;
134 enum fruit_locking locking;
135 enum fruit_encoding encoding;
136 bool use_aapl; /* config from smb.conf */
138 bool readdir_attr_enabled;
139 bool unix_info_enabled;
140 bool copyfile_enabled;
141 bool veto_appledouble;
143 bool aapl_zero_file_id;
146 off_t time_machine_max_size;
147 bool wipe_intentionally_left_blank_rfork;
148 bool delete_empty_adfiles;
151 * Additional options, all enabled by default,
152 * possibly useful for analyzing performance. The associated
153 * operations with each of them may be expensive, so having
154 * the chance to disable them individually gives a chance
155 * tweaking the setup for the particular usecase.
157 bool readdir_attr_rsize;
158 bool readdir_attr_finder_info;
159 bool readdir_attr_max_access;
162 static const struct enum_list fruit_rsrc[] = {
163 {FRUIT_RSRC_STREAM, "stream"}, /* pass on to vfs_streams_xattr */
164 {FRUIT_RSRC_ADFILE, "file"}, /* ._ AppleDouble file */
165 {FRUIT_RSRC_XATTR, "xattr"}, /* Netatalk compatible xattr (ZFS only) */
169 static const struct enum_list fruit_meta[] = {
170 {FRUIT_META_STREAM, "stream"}, /* pass on to vfs_streams_xattr */
171 {FRUIT_META_NETATALK, "netatalk"}, /* Netatalk compatible xattr */
175 static const struct enum_list fruit_locking[] = {
176 {FRUIT_LOCKING_NETATALK, "netatalk"}, /* synchronize locks with Netatalk */
177 {FRUIT_LOCKING_NONE, "none"},
181 static const struct enum_list fruit_encoding[] = {
182 {FRUIT_ENC_NATIVE, "native"}, /* map unicode private chars to ASCII */
183 {FRUIT_ENC_PRIVATE, "private"}, /* keep unicode private chars */
187 static const char *fruit_catia_maps =
188 "0x01:0xf001,0x02:0xf002,0x03:0xf003,0x04:0xf004,"
189 "0x05:0xf005,0x06:0xf006,0x07:0xf007,0x08:0xf008,"
190 "0x09:0xf009,0x0a:0xf00a,0x0b:0xf00b,0x0c:0xf00c,"
191 "0x0d:0xf00d,0x0e:0xf00e,0x0f:0xf00f,0x10:0xf010,"
192 "0x11:0xf011,0x12:0xf012,0x13:0xf013,0x14:0xf014,"
193 "0x15:0xf015,0x16:0xf016,0x17:0xf017,0x18:0xf018,"
194 "0x19:0xf019,0x1a:0xf01a,0x1b:0xf01b,0x1c:0xf01c,"
195 "0x1d:0xf01d,0x1e:0xf01e,0x1f:0xf01f,"
196 "0x22:0xf020,0x2a:0xf021,0x3a:0xf022,0x3c:0xf023,"
197 "0x3e:0xf024,0x3f:0xf025,0x5c:0xf026,0x7c:0xf027,"
200 /*****************************************************************************
201 * Defines, functions and data structures that deal with AppleDouble
202 *****************************************************************************/
205 * There are two AppleDouble blobs we deal with:
207 * - ADOUBLE_META - AppleDouble blob used by Netatalk for storing
208 * metadata in an xattr
210 * - ADOUBLE_RSRC - AppleDouble blob used by OS X and Netatalk in
213 typedef enum {ADOUBLE_META, ADOUBLE_RSRC} adouble_type_t;
216 #define AD_VERSION2 0x00020000
217 #define AD_VERSION AD_VERSION2
220 * AppleDouble entry IDs.
222 #define ADEID_DFORK 1
223 #define ADEID_RFORK 2
225 #define ADEID_COMMENT 4
226 #define ADEID_ICONBW 5
227 #define ADEID_ICONCOL 6
228 #define ADEID_FILEI 7
229 #define ADEID_FILEDATESI 8
230 #define ADEID_FINDERI 9
231 #define ADEID_MACFILEI 10
232 #define ADEID_PRODOSFILEI 11
233 #define ADEID_MSDOSFILEI 12
234 #define ADEID_SHORTNAME 13
235 #define ADEID_AFPFILEI 14
238 /* Private Netatalk entries */
239 #define ADEID_PRIVDEV 16
240 #define ADEID_PRIVINO 17
241 #define ADEID_PRIVSYN 18
242 #define ADEID_PRIVID 19
243 #define ADEID_MAX (ADEID_PRIVID + 1)
246 * These are the real ids for the private entries,
247 * as stored in the adouble file
249 #define AD_DEV 0x80444556
250 #define AD_INO 0x80494E4F
251 #define AD_SYN 0x8053594E
252 #define AD_ID 0x8053567E
254 /* Number of actually used entries */
255 #define ADEID_NUM_XATTR 8
256 #define ADEID_NUM_DOT_UND 2
257 #define ADEID_NUM_RSRC_XATTR 1
259 /* AppleDouble magic */
260 #define AD_APPLESINGLE_MAGIC 0x00051600
261 #define AD_APPLEDOUBLE_MAGIC 0x00051607
262 #define AD_MAGIC AD_APPLEDOUBLE_MAGIC
264 /* Sizes of relevant entry bits */
265 #define ADEDLEN_MAGIC 4
266 #define ADEDLEN_VERSION 4
267 #define ADEDLEN_FILLER 16
268 #define AD_FILLER_TAG "Netatalk " /* should be 16 bytes */
269 #define AD_FILLER_TAG_OSX "Mac OS X " /* should be 16 bytes */
270 #define ADEDLEN_NENTRIES 2
271 #define AD_HEADER_LEN (ADEDLEN_MAGIC + ADEDLEN_VERSION + \
272 ADEDLEN_FILLER + ADEDLEN_NENTRIES) /* 26 */
273 #define AD_ENTRY_LEN_EID 4
274 #define AD_ENTRY_LEN_OFF 4
275 #define AD_ENTRY_LEN_LEN 4
276 #define AD_ENTRY_LEN (AD_ENTRY_LEN_EID + AD_ENTRY_LEN_OFF + AD_ENTRY_LEN_LEN)
279 #define ADEDLEN_NAME 255
280 #define ADEDLEN_COMMENT 200
281 #define ADEDLEN_FILEI 16
282 #define ADEDLEN_FINDERI 32
283 #define ADEDLEN_FILEDATESI 16
284 #define ADEDLEN_SHORTNAME 12 /* length up to 8.3 */
285 #define ADEDLEN_AFPFILEI 4
286 #define ADEDLEN_MACFILEI 4
287 #define ADEDLEN_PRODOSFILEI 8
288 #define ADEDLEN_MSDOSFILEI 2
289 #define ADEDLEN_DID 4
290 #define ADEDLEN_PRIVDEV 8
291 #define ADEDLEN_PRIVINO 8
292 #define ADEDLEN_PRIVSYN 8
293 #define ADEDLEN_PRIVID 4
296 #define ADEDOFF_MAGIC 0
297 #define ADEDOFF_VERSION (ADEDOFF_MAGIC + ADEDLEN_MAGIC)
298 #define ADEDOFF_FILLER (ADEDOFF_VERSION + ADEDLEN_VERSION)
299 #define ADEDOFF_NENTRIES (ADEDOFF_FILLER + ADEDLEN_FILLER)
301 #define ADEDOFF_FINDERI_XATTR (AD_HEADER_LEN + \
302 (ADEID_NUM_XATTR * AD_ENTRY_LEN))
303 #define ADEDOFF_COMMENT_XATTR (ADEDOFF_FINDERI_XATTR + ADEDLEN_FINDERI)
304 #define ADEDOFF_FILEDATESI_XATTR (ADEDOFF_COMMENT_XATTR + ADEDLEN_COMMENT)
305 #define ADEDOFF_AFPFILEI_XATTR (ADEDOFF_FILEDATESI_XATTR + \
307 #define ADEDOFF_PRIVDEV_XATTR (ADEDOFF_AFPFILEI_XATTR + ADEDLEN_AFPFILEI)
308 #define ADEDOFF_PRIVINO_XATTR (ADEDOFF_PRIVDEV_XATTR + ADEDLEN_PRIVDEV)
309 #define ADEDOFF_PRIVSYN_XATTR (ADEDOFF_PRIVINO_XATTR + ADEDLEN_PRIVINO)
310 #define ADEDOFF_PRIVID_XATTR (ADEDOFF_PRIVSYN_XATTR + ADEDLEN_PRIVSYN)
312 #define ADEDOFF_FINDERI_DOT_UND (AD_HEADER_LEN + \
313 (ADEID_NUM_DOT_UND * AD_ENTRY_LEN))
314 #define ADEDOFF_RFORK_DOT_UND (ADEDOFF_FINDERI_DOT_UND + ADEDLEN_FINDERI)
316 #define AD_DATASZ_XATTR (AD_HEADER_LEN + \
317 (ADEID_NUM_XATTR * AD_ENTRY_LEN) + \
318 ADEDLEN_FINDERI + ADEDLEN_COMMENT + \
319 ADEDLEN_FILEDATESI + ADEDLEN_AFPFILEI + \
320 ADEDLEN_PRIVDEV + ADEDLEN_PRIVINO + \
321 ADEDLEN_PRIVSYN + ADEDLEN_PRIVID)
323 #if AD_DATASZ_XATTR != 402
324 #error bad size for AD_DATASZ_XATTR
327 #define AD_DATASZ_DOT_UND (AD_HEADER_LEN + \
328 (ADEID_NUM_DOT_UND * AD_ENTRY_LEN) + \
330 #if AD_DATASZ_DOT_UND != 82
331 #error bad size for AD_DATASZ_DOT_UND
335 * Sharemode locks fcntl() offsets
337 #if _FILE_OFFSET_BITS == 64 || defined(HAVE_LARGEFILE)
338 #define AD_FILELOCK_BASE (UINT64_C(0x7FFFFFFFFFFFFFFF) - 9)
340 #define AD_FILELOCK_BASE (UINT32_C(0x7FFFFFFF) - 9)
342 #define BYTELOCK_MAX (AD_FILELOCK_BASE - 1)
344 #define AD_FILELOCK_OPEN_WR (AD_FILELOCK_BASE + 0)
345 #define AD_FILELOCK_OPEN_RD (AD_FILELOCK_BASE + 1)
346 #define AD_FILELOCK_RSRC_OPEN_WR (AD_FILELOCK_BASE + 2)
347 #define AD_FILELOCK_RSRC_OPEN_RD (AD_FILELOCK_BASE + 3)
348 #define AD_FILELOCK_DENY_WR (AD_FILELOCK_BASE + 4)
349 #define AD_FILELOCK_DENY_RD (AD_FILELOCK_BASE + 5)
350 #define AD_FILELOCK_RSRC_DENY_WR (AD_FILELOCK_BASE + 6)
351 #define AD_FILELOCK_RSRC_DENY_RD (AD_FILELOCK_BASE + 7)
352 #define AD_FILELOCK_OPEN_NONE (AD_FILELOCK_BASE + 8)
353 #define AD_FILELOCK_RSRC_OPEN_NONE (AD_FILELOCK_BASE + 9)
355 /* Time stuff we overload the bits a little */
356 #define AD_DATE_CREATE 0
357 #define AD_DATE_MODIFY 4
358 #define AD_DATE_BACKUP 8
359 #define AD_DATE_ACCESS 12
360 #define AD_DATE_MASK (AD_DATE_CREATE | AD_DATE_MODIFY | \
361 AD_DATE_BACKUP | AD_DATE_ACCESS)
362 #define AD_DATE_UNIX (1 << 10)
363 #define AD_DATE_START 0x80000000
364 #define AD_DATE_DELTA 946684800
365 #define AD_DATE_FROM_UNIX(x) (htonl((x) - AD_DATE_DELTA))
366 #define AD_DATE_TO_UNIX(x) (ntohl(x) + AD_DATE_DELTA)
368 #define AD_XATTR_HDR_MAGIC 0x41545452 /* 'ATTR' */
369 #define AD_XATTR_MAX_ENTRIES 1024 /* Some arbitrarily enforced limit */
370 #define AD_XATTR_HDR_SIZE 36
371 #define AD_XATTR_MAX_HDR_SIZE 65536
373 /* Accessor macros */
374 #define ad_getentrylen(ad,eid) ((ad)->ad_eid[(eid)].ade_len)
375 #define ad_getentryoff(ad,eid) ((ad)->ad_eid[(eid)].ade_off)
376 #define ad_setentrylen(ad,eid,len) ((ad)->ad_eid[(eid)].ade_len = (len))
377 #define ad_setentryoff(ad,eid,off) ((ad)->ad_eid[(eid)].ade_off = (off))
380 * Both struct ad_xattr_header and struct ad_xattr_entry describe the in memory
381 * representation as well as the on-disk format.
383 * The ad_xattr_header follows the FinderInfo data in the FinderInfo entry if
384 * the length of the FinderInfo entry is larger then 32 bytes. It is then
385 * preceeded with 2 bytes padding.
387 * Cf: https://opensource.apple.com/source/xnu/xnu-4570.1.46/bsd/vfs/vfs_xattr.c
390 struct ad_xattr_header {
391 uint32_t adx_magic; /* ATTR_HDR_MAGIC */
392 uint32_t adx_debug_tag; /* for debugging == file id of owning file */
393 uint32_t adx_total_size; /* file offset of end of attribute header + entries + data */
394 uint32_t adx_data_start; /* file offset to attribute data area */
395 uint32_t adx_data_length; /* length of attribute data area */
396 uint32_t adx_reserved[3];
398 uint16_t adx_num_attrs;
401 /* On-disk entries are aligned on 4 byte boundaries */
402 struct ad_xattr_entry {
403 uint32_t adx_offset; /* file offset to data */
404 uint32_t adx_length; /* size of attribute data */
406 uint8_t adx_namelen; /* included the NULL terminator */
407 char *adx_name; /* NULL-terminated UTF-8 name */
416 vfs_handle_struct *ad_handle;
419 adouble_type_t ad_type;
422 uint8_t ad_filler[ADEDLEN_FILLER];
423 struct ad_entry ad_eid[ADEID_MAX];
425 struct ad_xattr_header adx_header;
426 struct ad_xattr_entry *adx_entries;
429 struct ad_entry_order {
430 uint32_t id, offset, len;
433 /* Netatalk AppleDouble metadata xattr */
435 struct ad_entry_order entry_order_meta_xattr[ADEID_NUM_XATTR + 1] = {
436 {ADEID_FINDERI, ADEDOFF_FINDERI_XATTR, ADEDLEN_FINDERI},
437 {ADEID_COMMENT, ADEDOFF_COMMENT_XATTR, 0},
438 {ADEID_FILEDATESI, ADEDOFF_FILEDATESI_XATTR, ADEDLEN_FILEDATESI},
439 {ADEID_AFPFILEI, ADEDOFF_AFPFILEI_XATTR, ADEDLEN_AFPFILEI},
440 {ADEID_PRIVDEV, ADEDOFF_PRIVDEV_XATTR, 0},
441 {ADEID_PRIVINO, ADEDOFF_PRIVINO_XATTR, 0},
442 {ADEID_PRIVSYN, ADEDOFF_PRIVSYN_XATTR, 0},
443 {ADEID_PRIVID, ADEDOFF_PRIVID_XATTR, 0},
447 /* AppleDouble resource fork file (the ones prefixed by "._") */
449 struct ad_entry_order entry_order_dot_und[ADEID_NUM_DOT_UND + 1] = {
450 {ADEID_FINDERI, ADEDOFF_FINDERI_DOT_UND, ADEDLEN_FINDERI},
451 {ADEID_RFORK, ADEDOFF_RFORK_DOT_UND, 0},
456 * Fake AppleDouble entry oder for resource fork xattr. The xattr
457 * isn't an AppleDouble file, it simply contains the resource data,
458 * but in order to be able to use some API calls like ad_getentryoff()
459 * we build a fake/helper struct adouble with this entry order struct.
462 struct ad_entry_order entry_order_rsrc_xattr[ADEID_NUM_RSRC_XATTR + 1] = {
467 /* Conversion from enumerated id to on-disk AppleDouble id */
468 #define AD_EID_DISK(a) (set_eid[a])
469 static const uint32_t set_eid[] = {
470 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
471 AD_DEV, AD_INO, AD_SYN, AD_ID
474 static char empty_resourcefork[] = {
475 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
476 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
477 0x54, 0x68, 0x69, 0x73, 0x20, 0x72, 0x65, 0x73,
478 0x6F, 0x75, 0x72, 0x63, 0x65, 0x20, 0x66, 0x6F,
479 0x72, 0x6B, 0x20, 0x69, 0x6E, 0x74, 0x65, 0x6E,
480 0x74, 0x69, 0x6F, 0x6E, 0x61, 0x6C, 0x6C, 0x79,
481 0x20, 0x6C, 0x65, 0x66, 0x74, 0x20, 0x62, 0x6C,
482 0x61, 0x6E, 0x6B, 0x20, 0x20, 0x20, 0x00, 0x00,
483 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
484 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
485 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
486 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
487 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
488 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
489 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
490 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
491 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
492 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
493 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
494 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
495 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
496 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
497 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
498 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
499 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
500 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
501 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
502 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
503 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
504 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
505 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
506 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
507 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
508 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
509 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
510 0x00, 0x1C, 0x00, 0x1E, 0xFF, 0xFF
514 /* tcon config handle */
515 struct fruit_config_data *config;
517 /* Denote stream type, meta or rsrc */
520 /* Whether the create created the stream */
524 * AFP_AfpInfo stream created, but not written yet, thus still a fake
525 * pipe fd. This is set to true in fruit_open_meta if there was no
526 * exisiting stream but the caller requested O_CREAT. It is later set to
527 * false when we get a write on the stream that then does open and
536 * Forward declarations
538 static struct adouble *ad_init(TALLOC_CTX *ctx, vfs_handle_struct *handle,
539 adouble_type_t type);
540 static struct adouble *ad_get(TALLOC_CTX *ctx,
541 vfs_handle_struct *handle,
542 const struct smb_filename *smb_fname,
543 adouble_type_t type);
544 static int ad_set(struct adouble *ad, const struct smb_filename *smb_fname);
545 static int ad_fset(struct vfs_handle_struct *handle,
548 static int adouble_path(TALLOC_CTX *ctx,
549 const struct smb_filename *smb_fname__in,
550 struct smb_filename **ppsmb_fname_out);
551 static AfpInfo *afpinfo_new(TALLOC_CTX *ctx);
552 static ssize_t afpinfo_pack(const AfpInfo *ai, char *buf);
553 static AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data);
557 * Return a pointer to an AppleDouble entry
559 * Returns NULL if the entry is not present
561 static char *ad_get_entry(const struct adouble *ad, int eid)
563 off_t off = ad_getentryoff(ad, eid);
564 size_t len = ad_getentrylen(ad, eid);
566 if (off == 0 || len == 0) {
570 return ad->ad_data + off;
576 static int ad_getdate(const struct adouble *ad,
577 unsigned int dateoff,
580 bool xlate = (dateoff & AD_DATE_UNIX);
583 dateoff &= AD_DATE_MASK;
584 p = ad_get_entry(ad, ADEID_FILEDATESI);
589 if (dateoff > AD_DATE_ACCESS) {
593 memcpy(date, p + dateoff, sizeof(uint32_t));
596 *date = AD_DATE_TO_UNIX(*date);
604 static int ad_setdate(struct adouble *ad, unsigned int dateoff, uint32_t date)
606 bool xlate = (dateoff & AD_DATE_UNIX);
609 p = ad_get_entry(ad, ADEID_FILEDATESI);
614 dateoff &= AD_DATE_MASK;
616 date = AD_DATE_FROM_UNIX(date);
619 if (dateoff > AD_DATE_ACCESS) {
623 memcpy(p + dateoff, &date, sizeof(date));
630 * Map on-disk AppleDouble id to enumerated id
632 static uint32_t get_eid(uint32_t eid)
640 return ADEID_PRIVDEV;
642 return ADEID_PRIVINO;
644 return ADEID_PRIVSYN;
655 * Pack AppleDouble structure into data buffer
657 static bool ad_pack(struct adouble *ad)
664 bufsize = talloc_get_size(ad->ad_data);
665 if (bufsize < AD_DATASZ_DOT_UND) {
666 DBG_ERR("bad buffer size [0x%" PRIx32 "]\n", bufsize);
670 if (offset + ADEDLEN_MAGIC < offset ||
671 offset + ADEDLEN_MAGIC >= bufsize) {
674 RSIVAL(ad->ad_data, offset, ad->ad_magic);
675 offset += ADEDLEN_MAGIC;
677 if (offset + ADEDLEN_VERSION < offset ||
678 offset + ADEDLEN_VERSION >= bufsize) {
681 RSIVAL(ad->ad_data, offset, ad->ad_version);
682 offset += ADEDLEN_VERSION;
684 if (offset + ADEDLEN_FILLER < offset ||
685 offset + ADEDLEN_FILLER >= bufsize) {
688 if (ad->ad_type == ADOUBLE_RSRC) {
689 memcpy(ad->ad_data + offset, AD_FILLER_TAG, ADEDLEN_FILLER);
691 offset += ADEDLEN_FILLER;
693 if (offset + ADEDLEN_NENTRIES < offset ||
694 offset + ADEDLEN_NENTRIES >= bufsize) {
697 offset += ADEDLEN_NENTRIES;
699 for (eid = 0, nent = 0; eid < ADEID_MAX; eid++) {
700 if (ad->ad_eid[eid].ade_off == 0) {
702 * ade_off is also used as indicator whether a
703 * specific entry is used or not
708 if (offset + AD_ENTRY_LEN_EID < offset ||
709 offset + AD_ENTRY_LEN_EID >= bufsize) {
712 RSIVAL(ad->ad_data, offset, AD_EID_DISK(eid));
713 offset += AD_ENTRY_LEN_EID;
715 if (offset + AD_ENTRY_LEN_OFF < offset ||
716 offset + AD_ENTRY_LEN_OFF >= bufsize) {
719 RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_off);
720 offset += AD_ENTRY_LEN_OFF;
722 if (offset + AD_ENTRY_LEN_LEN < offset ||
723 offset + AD_ENTRY_LEN_LEN >= bufsize) {
726 RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_len);
727 offset += AD_ENTRY_LEN_LEN;
732 if (ADEDOFF_NENTRIES + 2 >= bufsize) {
735 RSSVAL(ad->ad_data, ADEDOFF_NENTRIES, nent);
740 static bool ad_unpack_xattrs(struct adouble *ad)
742 struct ad_xattr_header *h = &ad->adx_header;
743 const char *p = ad->ad_data;
747 if (ad_getentrylen(ad, ADEID_FINDERI) <= ADEDLEN_FINDERI) {
751 /* 2 bytes padding */
752 hoff = ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI + 2;
754 h->adx_magic = RIVAL(p, hoff + 0);
755 h->adx_debug_tag = RIVAL(p, hoff + 4); /* Not used -> not checked */
756 h->adx_total_size = RIVAL(p, hoff + 8);
757 h->adx_data_start = RIVAL(p, hoff + 12);
758 h->adx_data_length = RIVAL(p, hoff + 16);
759 h->adx_flags = RSVAL(p, hoff + 32); /* Not used -> not checked */
760 h->adx_num_attrs = RSVAL(p, hoff + 34);
762 if (h->adx_magic != AD_XATTR_HDR_MAGIC) {
763 DBG_ERR("Bad magic: 0x%" PRIx32 "\n", h->adx_magic);
767 if (h->adx_total_size > ad_getentryoff(ad, ADEID_RFORK)) {
768 DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size);
771 if (h->adx_total_size > AD_XATTR_MAX_HDR_SIZE) {
772 DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size);
776 if (h->adx_data_start < (hoff + AD_XATTR_HDR_SIZE)) {
777 DBG_ERR("Bad start: 0x%" PRIx32 "\n", h->adx_data_start);
781 if ((h->adx_data_start + h->adx_data_length) < h->adx_data_start) {
782 DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length);
785 if ((h->adx_data_start + h->adx_data_length) >
786 ad->adx_header.adx_total_size)
788 DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length);
792 if (h->adx_num_attrs > AD_XATTR_MAX_ENTRIES) {
793 DBG_ERR("Bad num xattrs: %" PRIu16 "\n", h->adx_num_attrs);
797 if (h->adx_num_attrs == 0) {
801 ad->adx_entries = talloc_zero_array(
802 ad, struct ad_xattr_entry, h->adx_num_attrs);
803 if (ad->adx_entries == NULL) {
807 hoff += AD_XATTR_HDR_SIZE;
809 for (i = 0; i < h->adx_num_attrs; i++) {
810 struct ad_xattr_entry *e = &ad->adx_entries[i];
812 hoff = (hoff + 3) & ~3;
814 e->adx_offset = RIVAL(p, hoff + 0);
815 e->adx_length = RIVAL(p, hoff + 4);
816 e->adx_flags = RSVAL(p, hoff + 8);
817 e->adx_namelen = *(p + hoff + 10);
819 if (e->adx_offset >= ad->adx_header.adx_total_size) {
820 DBG_ERR("Bad adx_offset: %" PRIx32 "\n",
825 if ((e->adx_offset + e->adx_length) < e->adx_offset) {
826 DBG_ERR("Bad adx_length: %" PRIx32 "\n",
831 if ((e->adx_offset + e->adx_length) >
832 ad->adx_header.adx_total_size)
834 DBG_ERR("Bad adx_length: %" PRIx32 "\n",
839 if (e->adx_namelen == 0) {
840 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
844 if ((hoff + 11 + e->adx_namelen) < hoff + 11) {
845 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
849 if ((hoff + 11 + e->adx_namelen) >
850 ad->adx_header.adx_data_start)
852 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
857 e->adx_name = talloc_strndup(ad->adx_entries,
860 if (e->adx_name == NULL) {
864 DBG_DEBUG("xattr [%s] offset [0x%x] size [0x%x]\n",
865 e->adx_name, e->adx_offset, e->adx_length);
866 dump_data(10, (uint8_t *)(ad->ad_data + e->adx_offset),
869 hoff += 11 + e->adx_namelen;
876 * Unpack an AppleDouble blob into a struct adoble
878 static bool ad_unpack(struct adouble *ad, const size_t nentries,
881 size_t bufsize = talloc_get_size(ad->ad_data);
883 uint32_t eid, len, off;
887 * The size of the buffer ad->ad_data is checked when read, so
888 * we wouldn't have to check our own offsets, a few extra
889 * checks won't hurt though. We have to check the offsets we
890 * read from the buffer anyway.
893 if (bufsize < (AD_HEADER_LEN + (AD_ENTRY_LEN * nentries))) {
894 DEBUG(1, ("bad size\n"));
898 ad->ad_magic = RIVAL(ad->ad_data, 0);
899 ad->ad_version = RIVAL(ad->ad_data, ADEDOFF_VERSION);
900 if ((ad->ad_magic != AD_MAGIC) || (ad->ad_version != AD_VERSION)) {
901 DEBUG(1, ("wrong magic or version\n"));
905 memcpy(ad->ad_filler, ad->ad_data + ADEDOFF_FILLER, ADEDLEN_FILLER);
907 adentries = RSVAL(ad->ad_data, ADEDOFF_NENTRIES);
908 if (adentries != nentries) {
909 DEBUG(1, ("invalid number of entries: %zu\n",
914 /* now, read in the entry bits */
915 for (i = 0; i < adentries; i++) {
916 eid = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN));
918 off = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 4);
919 len = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 8);
921 if (!eid || eid >= ADEID_MAX) {
922 DEBUG(1, ("bogus eid %d\n", eid));
927 * All entries other than the resource fork are
928 * expected to be read into the ad_data buffer, so
929 * ensure the specified offset is within that bound
931 if ((off > bufsize) && (eid != ADEID_RFORK)) {
932 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
938 * All entries besides FinderInfo and resource fork
939 * must fit into the buffer. FinderInfo is special as
940 * it may be larger then the default 32 bytes (if it
941 * contains marshalled xattrs), but we will fixup that
942 * in ad_convert(). And the resource fork is never
943 * accessed directly by the ad_data buf (also see
944 * comment above) anyway.
946 if ((eid != ADEID_RFORK) &&
947 (eid != ADEID_FINDERI) &&
948 ((off + len) > bufsize)) {
949 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
955 * That would be obviously broken
957 if (off > filesize) {
958 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
964 * Check for any entry that has its end beyond the
967 if (off + len < off) {
968 DEBUG(1, ("offset wrap in eid %d: off: %" PRIu32
969 ", len: %" PRIu32 "\n",
974 if (off + len > filesize) {
976 * If this is the resource fork entry, we fix
977 * up the length, for any other entry we bail
980 if (eid != ADEID_RFORK) {
981 DEBUG(1, ("bogus eid %d: off: %" PRIu32
982 ", len: %" PRIu32 "\n",
988 * Fixup the resource fork entry by limiting
989 * the size to entryoffset - filesize.
991 len = filesize - off;
992 DEBUG(1, ("Limiting ADEID_RFORK: off: %" PRIu32
993 ", len: %" PRIu32 "\n", off, len));
996 ad->ad_eid[eid].ade_off = off;
997 ad->ad_eid[eid].ade_len = len;
1000 ok = ad_unpack_xattrs(ad);
1008 static bool ad_convert_move_reso(struct adouble *ad,
1009 const struct smb_filename *smb_fname)
1011 char *map = MAP_FAILED;
1017 if (ad_getentrylen(ad, ADEID_RFORK) == 0) {
1021 maplen = ad_getentryoff(ad, ADEID_RFORK) +
1022 ad_getentrylen(ad, ADEID_RFORK);
1024 /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
1025 map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
1027 if (map == MAP_FAILED) {
1028 DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
1033 memmove(map + ADEDOFF_RFORK_DOT_UND,
1034 map + ad_getentryoff(ad, ADEID_RFORK),
1035 ad_getentrylen(ad, ADEID_RFORK));
1037 rc = munmap(map, maplen);
1039 DBG_ERR("munmap failed: %s\n", strerror(errno));
1043 ad_setentryoff(ad, ADEID_RFORK, ADEDOFF_RFORK_DOT_UND);
1047 DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name);
1051 len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
1052 if (len != AD_DATASZ_DOT_UND) {
1053 DBG_ERR("%s: bad size: %zd\n", smb_fname->base_name, len);
1060 static bool ad_convert_xattr(struct adouble *ad,
1061 const struct smb_filename *smb_fname,
1062 bool *converted_xattr)
1064 static struct char_mappings **string_replace_cmaps = NULL;
1065 char *map = MAP_FAILED;
1069 int saved_errno = 0;
1074 *converted_xattr = false;
1076 if (ad_getentrylen(ad, ADEID_FINDERI) == ADEDLEN_FINDERI) {
1080 if (string_replace_cmaps == NULL) {
1081 const char **mappings = NULL;
1083 mappings = str_list_make_v3_const(
1084 talloc_tos(), fruit_catia_maps, NULL);
1085 if (mappings == NULL) {
1088 string_replace_cmaps = string_replace_init_map(mappings);
1089 TALLOC_FREE(mappings);
1092 maplen = ad_getentryoff(ad, ADEID_RFORK) +
1093 ad_getentrylen(ad, ADEID_RFORK);
1095 /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
1096 map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
1098 if (map == MAP_FAILED) {
1099 DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
1103 for (i = 0; i < ad->adx_header.adx_num_attrs; i++) {
1104 struct ad_xattr_entry *e = &ad->adx_entries[i];
1105 char *mapped_name = NULL;
1107 struct smb_filename *stream_name = NULL;
1108 files_struct *fsp = NULL;
1111 status = string_replace_allocate(ad->ad_handle->conn,
1113 string_replace_cmaps,
1116 vfs_translate_to_windows);
1117 if (!NT_STATUS_IS_OK(status) &&
1118 !NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))
1120 DBG_ERR("string_replace_allocate failed\n");
1126 mapped_name = talloc_asprintf(talloc_tos(), ":%s", tmp);
1128 if (mapped_name == NULL) {
1133 stream_name = synthetic_smb_fname(talloc_tos(),
1134 smb_fname->base_name,
1138 TALLOC_FREE(mapped_name);
1139 if (stream_name == NULL) {
1140 DBG_ERR("synthetic_smb_fname failed\n");
1145 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1147 status = SMB_VFS_CREATE_FILE(
1148 ad->ad_handle->conn, /* conn */
1150 0, /* root_dir_fid */
1151 stream_name, /* fname */
1152 FILE_GENERIC_WRITE, /* access_mask */
1153 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
1154 FILE_OPEN_IF, /* create_disposition */
1155 0, /* create_options */
1156 0, /* file_attributes */
1157 INTERNAL_OPEN_ONLY, /* oplock_request */
1159 0, /* allocation_size */
1160 0, /* private_flags */
1165 NULL, NULL); /* create context */
1166 TALLOC_FREE(stream_name);
1167 if (!NT_STATUS_IS_OK(status)) {
1168 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1173 nwritten = SMB_VFS_PWRITE(fsp,
1174 map + e->adx_offset,
1177 if (nwritten == -1) {
1178 DBG_ERR("SMB_VFS_PWRITE failed\n");
1179 saved_errno = errno;
1180 close_file(NULL, fsp, ERROR_CLOSE);
1181 errno = saved_errno;
1186 status = close_file(NULL, fsp, NORMAL_CLOSE);
1187 if (!NT_STATUS_IS_OK(status)) {
1194 ad_setentrylen(ad, ADEID_FINDERI, ADEDLEN_FINDERI);
1198 DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name);
1202 len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
1203 if (len != AD_DATASZ_DOT_UND) {
1204 DBG_ERR("%s: bad size: %zd\n", smb_fname->base_name, len);
1209 ok = ad_convert_move_reso(ad, smb_fname);
1214 *converted_xattr = true;
1218 rc = munmap(map, maplen);
1220 DBG_ERR("munmap failed: %s\n", strerror(errno));
1227 static bool ad_convert_finderinfo(struct adouble *ad,
1228 const struct smb_filename *smb_fname)
1233 struct smb_filename *stream_name = NULL;
1234 files_struct *fsp = NULL;
1238 int saved_errno = 0;
1241 cmp = memcmp(ad->ad_filler, AD_FILLER_TAG_OSX, ADEDLEN_FILLER);
1246 p_ad = ad_get_entry(ad, ADEID_FINDERI);
1251 ai = afpinfo_new(talloc_tos());
1256 memcpy(ai->afpi_FinderInfo, p_ad, ADEDLEN_FINDERI);
1258 aiblob = data_blob_talloc(talloc_tos(), NULL, AFP_INFO_SIZE);
1259 if (aiblob.data == NULL) {
1264 size = afpinfo_pack(ai, (char *)aiblob.data);
1266 if (size != AFP_INFO_SIZE) {
1270 stream_name = synthetic_smb_fname(talloc_tos(),
1271 smb_fname->base_name,
1275 if (stream_name == NULL) {
1276 data_blob_free(&aiblob);
1277 DBG_ERR("synthetic_smb_fname failed\n");
1281 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1283 status = SMB_VFS_CREATE_FILE(
1284 ad->ad_handle->conn, /* conn */
1286 0, /* root_dir_fid */
1287 stream_name, /* fname */
1288 FILE_GENERIC_WRITE, /* access_mask */
1289 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
1290 FILE_OPEN_IF, /* create_disposition */
1291 0, /* create_options */
1292 0, /* file_attributes */
1293 INTERNAL_OPEN_ONLY, /* oplock_request */
1295 0, /* allocation_size */
1296 0, /* private_flags */
1301 NULL, NULL); /* create context */
1302 TALLOC_FREE(stream_name);
1303 if (!NT_STATUS_IS_OK(status)) {
1304 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1308 nwritten = SMB_VFS_PWRITE(fsp,
1312 if (nwritten == -1) {
1313 DBG_ERR("SMB_VFS_PWRITE failed\n");
1314 saved_errno = errno;
1315 close_file(NULL, fsp, ERROR_CLOSE);
1316 errno = saved_errno;
1320 status = close_file(NULL, fsp, NORMAL_CLOSE);
1321 if (!NT_STATUS_IS_OK(status)) {
1329 static bool ad_convert_truncate(struct adouble *ad,
1330 const struct smb_filename *smb_fname)
1335 * FIXME: direct ftruncate(), but we don't have a fsp for the
1338 rc = ftruncate(ad->ad_fd, ADEDOFF_RFORK_DOT_UND +
1339 ad_getentrylen(ad, ADEID_RFORK));
1347 static bool ad_convert_blank_rfork(struct adouble *ad,
1350 struct fruit_config_data *config = NULL;
1351 uint8_t *map = MAP_FAILED;
1360 SMB_VFS_HANDLE_GET_DATA(ad->ad_handle, config,
1361 struct fruit_config_data, return false);
1363 if (!config->wipe_intentionally_left_blank_rfork) {
1367 if (ad_getentrylen(ad, ADEID_RFORK) != sizeof(empty_resourcefork)) {
1371 maplen = ad_getentryoff(ad, ADEID_RFORK) +
1372 ad_getentrylen(ad, ADEID_RFORK);
1374 /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
1375 map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
1377 if (map == MAP_FAILED) {
1378 DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
1382 cmp = memcmp(map + ADEDOFF_RFORK_DOT_UND,
1384 sizeof(empty_resourcefork));
1385 rc = munmap(map, maplen);
1387 DBG_ERR("munmap failed: %s\n", strerror(errno));
1395 ad_setentrylen(ad, ADEID_RFORK, 0);
1402 len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
1403 if (len != AD_DATASZ_DOT_UND) {
1411 static bool ad_convert_delete_adfile(struct adouble *ad,
1412 const struct smb_filename *smb_fname)
1414 struct fruit_config_data *config = NULL;
1415 struct smb_filename *ad_name = NULL;
1418 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
1422 SMB_VFS_HANDLE_GET_DATA(ad->ad_handle, config,
1423 struct fruit_config_data, return false);
1425 if (!config->delete_empty_adfiles) {
1429 rc = adouble_path(talloc_tos(), smb_fname, &ad_name);
1434 rc = SMB_VFS_NEXT_UNLINK(ad->ad_handle, ad_name);
1436 DBG_ERR("Unlinking [%s] failed: %s\n",
1437 smb_fname_str_dbg(ad_name), strerror(errno));
1438 TALLOC_FREE(ad_name);
1442 DBG_WARNING("Unlinked [%s] after conversion\n", smb_fname_str_dbg(ad_name));
1443 TALLOC_FREE(ad_name);
1449 * Convert from Apple's ._ file to Netatalk
1451 * Apple's AppleDouble may contain a FinderInfo entry longer then 32
1452 * bytes containing packed xattrs.
1454 * @return -1 in case an error occurred, 0 if no conversion was done, 1
1457 static int ad_convert(struct vfs_handle_struct *handle,
1458 const struct smb_filename *smb_fname)
1460 struct adouble *ad = NULL;
1462 bool converted_xattr = false;
1466 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
1471 ok = ad_convert_xattr(ad, smb_fname, &converted_xattr);
1477 ok = ad_convert_blank_rfork(ad, &blank);
1483 if (converted_xattr || blank) {
1484 ok = ad_convert_truncate(ad, smb_fname);
1491 ok = ad_convert_finderinfo(ad, smb_fname);
1493 DBG_ERR("Failed to convert [%s]\n",
1494 smb_fname_str_dbg(smb_fname));
1499 ok = ad_convert_delete_adfile(ad, smb_fname);
1512 * Read and parse Netatalk AppleDouble metadata xattr
1514 static ssize_t ad_read_meta(struct adouble *ad,
1515 const struct smb_filename *smb_fname)
1521 DEBUG(10, ("reading meta xattr for %s\n", smb_fname->base_name));
1523 ealen = SMB_VFS_GETXATTR(ad->ad_handle->conn, smb_fname,
1524 AFPINFO_EA_NETATALK, ad->ad_data,
1530 if (errno == ENOATTR) {
1536 DEBUG(2, ("error reading meta xattr: %s\n",
1542 if (ealen != AD_DATASZ_XATTR) {
1543 DEBUG(2, ("bad size %zd\n", ealen));
1549 /* Now parse entries */
1550 ok = ad_unpack(ad, ADEID_NUM_XATTR, AD_DATASZ_XATTR);
1552 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1558 if (!ad_getentryoff(ad, ADEID_FINDERI)
1559 || !ad_getentryoff(ad, ADEID_COMMENT)
1560 || !ad_getentryoff(ad, ADEID_FILEDATESI)
1561 || !ad_getentryoff(ad, ADEID_AFPFILEI)
1562 || !ad_getentryoff(ad, ADEID_PRIVDEV)
1563 || !ad_getentryoff(ad, ADEID_PRIVINO)
1564 || !ad_getentryoff(ad, ADEID_PRIVSYN)
1565 || !ad_getentryoff(ad, ADEID_PRIVID)) {
1566 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1573 DEBUG(10, ("reading meta xattr for %s, rc: %d\n",
1574 smb_fname->base_name, rc));
1578 if (errno == EINVAL) {
1580 removexattr(smb_fname->base_name, AFPINFO_EA_NETATALK);
1588 static int ad_open_rsrc_xattr(const struct smb_filename *smb_fname,
1592 #ifdef HAVE_ATTROPEN
1593 /* FIXME: direct Solaris xattr syscall */
1594 return attropen(smb_fname->base_name,
1595 AFPRESOURCE_EA_NETATALK, flags, mode);
1602 static int ad_open_rsrc_adouble(const struct smb_filename *smb_fname,
1608 struct smb_filename *adp_smb_fname = NULL;
1610 ret = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
1615 fd = open(adp_smb_fname->base_name, flags, mode);
1616 TALLOC_FREE(adp_smb_fname);
1621 static int ad_open_rsrc(vfs_handle_struct *handle,
1622 const struct smb_filename *smb_fname,
1626 struct fruit_config_data *config = NULL;
1629 SMB_VFS_HANDLE_GET_DATA(handle, config,
1630 struct fruit_config_data, return -1);
1632 if (config->rsrc == FRUIT_RSRC_XATTR) {
1633 fd = ad_open_rsrc_xattr(smb_fname, flags, mode);
1635 fd = ad_open_rsrc_adouble(smb_fname, flags, mode);
1642 * Here's the deal: for ADOUBLE_META we can do without an fd as we can issue
1643 * path based xattr calls. For ADOUBLE_RSRC however we need a full-fledged fd
1644 * for file IO on the ._ file.
1646 static int ad_open(vfs_handle_struct *handle,
1649 const struct smb_filename *smb_fname,
1655 DBG_DEBUG("Path [%s] type [%s]\n", smb_fname->base_name,
1656 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
1658 if (ad->ad_type == ADOUBLE_META) {
1662 if ((fsp != NULL) && (fsp->fh != NULL) && (fsp->fh->fd != -1)) {
1663 ad->ad_fd = fsp->fh->fd;
1664 ad->ad_opened = false;
1668 fd = ad_open_rsrc(handle, smb_fname, flags, mode);
1672 ad->ad_opened = true;
1675 DBG_DEBUG("Path [%s] type [%s] fd [%d]\n",
1676 smb_fname->base_name,
1677 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc", fd);
1682 static ssize_t ad_read_rsrc_xattr(struct adouble *ad)
1687 /* FIXME: direct sys_fstat(), don't have an fsp */
1688 ret = sys_fstat(ad->ad_fd, &st,
1689 lp_fake_directory_create_times(
1690 SNUM(ad->ad_handle->conn)));
1695 ad_setentrylen(ad, ADEID_RFORK, st.st_ex_size);
1696 return st.st_ex_size;
1699 static ssize_t ad_read_rsrc_adouble(struct adouble *ad,
1700 const struct smb_filename *smb_fname)
1702 SMB_STRUCT_STAT sbuf;
1709 ret = sys_fstat(ad->ad_fd, &sbuf, lp_fake_directory_create_times(
1710 SNUM(ad->ad_handle->conn)));
1716 * AppleDouble file header content and size, two cases:
1718 * - without xattrs it is exactly AD_DATASZ_DOT_UND (82) bytes large
1719 * - with embedded xattrs it can be larger, up to AD_XATTR_MAX_HDR_SIZE
1721 * Read as much as we can up to AD_XATTR_MAX_HDR_SIZE.
1723 size = sbuf.st_ex_size;
1724 if (size > talloc_array_length(ad->ad_data)) {
1725 if (size > AD_XATTR_MAX_HDR_SIZE) {
1726 size = AD_XATTR_MAX_HDR_SIZE;
1728 p_ad = talloc_realloc(ad, ad->ad_data, char, size);
1735 len = sys_pread(ad->ad_fd, ad->ad_data,
1736 talloc_array_length(ad->ad_data), 0);
1737 if (len != talloc_array_length(ad->ad_data)) {
1738 DBG_NOTICE("%s %s: bad size: %zd\n",
1739 smb_fname->base_name, strerror(errno), len);
1743 /* Now parse entries */
1744 ok = ad_unpack(ad, ADEID_NUM_DOT_UND, sbuf.st_ex_size);
1746 DBG_ERR("invalid AppleDouble resource %s\n",
1747 smb_fname->base_name);
1752 if ((ad_getentryoff(ad, ADEID_FINDERI) != ADEDOFF_FINDERI_DOT_UND)
1753 || (ad_getentrylen(ad, ADEID_FINDERI) < ADEDLEN_FINDERI)
1754 || (ad_getentryoff(ad, ADEID_RFORK) < ADEDOFF_RFORK_DOT_UND)) {
1755 DBG_ERR("invalid AppleDouble resource %s\n",
1756 smb_fname->base_name);
1765 * Read and parse resource fork, either ._ AppleDouble file or xattr
1767 static ssize_t ad_read_rsrc(struct adouble *ad,
1768 const struct smb_filename *smb_fname)
1770 struct fruit_config_data *config = NULL;
1773 SMB_VFS_HANDLE_GET_DATA(ad->ad_handle, config,
1774 struct fruit_config_data, return -1);
1776 if (config->rsrc == FRUIT_RSRC_XATTR) {
1777 len = ad_read_rsrc_xattr(ad);
1779 len = ad_read_rsrc_adouble(ad, smb_fname);
1786 * Read and unpack an AppleDouble metadata xattr or resource
1788 static ssize_t ad_read(struct adouble *ad, const struct smb_filename *smb_fname)
1790 switch (ad->ad_type) {
1792 return ad_read_meta(ad, smb_fname);
1794 return ad_read_rsrc(ad, smb_fname);
1800 static int adouble_destructor(struct adouble *ad)
1802 if ((ad->ad_fd != -1) && ad->ad_opened) {
1810 * Allocate a struct adouble without initialiing it
1812 * The struct is either hang of the fsp extension context or if fsp is
1815 * @param[in] ctx talloc context
1816 * @param[in] handle vfs handle
1817 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1819 * @return adouble handle
1821 static struct adouble *ad_alloc(TALLOC_CTX *ctx, vfs_handle_struct *handle,
1822 adouble_type_t type)
1827 struct fruit_config_data *config;
1829 SMB_VFS_HANDLE_GET_DATA(handle, config,
1830 struct fruit_config_data, return NULL);
1834 adsize = AD_DATASZ_XATTR;
1837 if (config->rsrc == FRUIT_RSRC_ADFILE) {
1838 adsize = AD_DATASZ_DOT_UND;
1845 ad = talloc_zero(ctx, struct adouble);
1852 ad->ad_data = talloc_zero_array(ad, char, adsize);
1853 if (ad->ad_data == NULL) {
1859 ad->ad_handle = handle;
1861 ad->ad_magic = AD_MAGIC;
1862 ad->ad_version = AD_VERSION;
1865 talloc_set_destructor(ad, adouble_destructor);
1875 * Allocate and initialize a new struct adouble
1877 * @param[in] ctx talloc context
1878 * @param[in] handle vfs handle
1879 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1881 * @return adouble handle, initialized
1883 static struct adouble *ad_init(TALLOC_CTX *ctx, vfs_handle_struct *handle,
1884 adouble_type_t type)
1887 const struct ad_entry_order *eid;
1888 struct adouble *ad = NULL;
1889 struct fruit_config_data *config;
1890 time_t t = time(NULL);
1892 SMB_VFS_HANDLE_GET_DATA(handle, config,
1893 struct fruit_config_data, return NULL);
1897 eid = entry_order_meta_xattr;
1900 if (config->rsrc == FRUIT_RSRC_ADFILE) {
1901 eid = entry_order_dot_und;
1903 eid = entry_order_rsrc_xattr;
1910 ad = ad_alloc(ctx, handle, type);
1916 ad->ad_eid[eid->id].ade_off = eid->offset;
1917 ad->ad_eid[eid->id].ade_len = eid->len;
1921 /* put something sane in the date fields */
1922 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, t);
1923 ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, t);
1924 ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, t);
1925 ad_setdate(ad, AD_DATE_BACKUP, htonl(AD_DATE_START));
1933 static struct adouble *ad_get_internal(TALLOC_CTX *ctx,
1934 vfs_handle_struct *handle,
1936 const struct smb_filename *smb_fname,
1937 adouble_type_t type)
1941 struct adouble *ad = NULL;
1945 smb_fname = fsp->base_fsp->fsp_name;
1948 DEBUG(10, ("ad_get(%s) called for %s\n",
1949 type == ADOUBLE_META ? "meta" : "rsrc",
1950 smb_fname->base_name));
1952 ad = ad_alloc(ctx, handle, type);
1958 /* Try rw first so we can use the fd in ad_convert() */
1961 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
1962 if (rc == -1 && ((errno == EROFS) || (errno == EACCES))) {
1964 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
1967 DBG_DEBUG("ad_open [%s] error [%s]\n",
1968 smb_fname->base_name, strerror(errno));
1973 len = ad_read(ad, smb_fname);
1975 DEBUG(10, ("error reading AppleDouble for %s\n",
1976 smb_fname->base_name));
1982 DEBUG(10, ("ad_get(%s) for %s returning %d\n",
1983 type == ADOUBLE_META ? "meta" : "rsrc",
1984 smb_fname->base_name, rc));
1993 * Return AppleDouble data for a file
1995 * @param[in] ctx talloc context
1996 * @param[in] handle vfs handle
1997 * @param[in] smb_fname pathname to file or directory
1998 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2000 * @return talloced struct adouble or NULL on error
2002 static struct adouble *ad_get(TALLOC_CTX *ctx,
2003 vfs_handle_struct *handle,
2004 const struct smb_filename *smb_fname,
2005 adouble_type_t type)
2007 return ad_get_internal(ctx, handle, NULL, smb_fname, type);
2011 * Return AppleDouble data for a file
2013 * @param[in] ctx talloc context
2014 * @param[in] handle vfs handle
2015 * @param[in] fsp fsp to use for IO
2016 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2018 * @return talloced struct adouble or NULL on error
2020 static struct adouble *ad_fget(TALLOC_CTX *ctx, vfs_handle_struct *handle,
2021 files_struct *fsp, adouble_type_t type)
2023 return ad_get_internal(ctx, handle, fsp, NULL, type);
2027 * Set AppleDouble metadata on a file or directory
2029 * @param[in] ad adouble handle
2031 * @param[in] smb_fname pathname to file or directory
2033 * @return status code, 0 means success
2035 static int ad_set(struct adouble *ad, const struct smb_filename *smb_fname)
2040 DBG_DEBUG("Path [%s]\n", smb_fname->base_name);
2042 if (ad->ad_type != ADOUBLE_META) {
2043 DBG_ERR("ad_set on [%s] used with ADOUBLE_RSRC\n",
2044 smb_fname->base_name);
2053 ret = SMB_VFS_SETXATTR(ad->ad_handle->conn,
2055 AFPINFO_EA_NETATALK,
2057 AD_DATASZ_XATTR, 0);
2059 DBG_DEBUG("Path [%s] ret [%d]\n", smb_fname->base_name, ret);
2065 * Set AppleDouble metadata on a file or directory
2067 * @param[in] ad adouble handle
2068 * @param[in] fsp file handle
2070 * @return status code, 0 means success
2072 static int ad_fset(struct vfs_handle_struct *handle,
2080 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
2083 || (fsp->fh == NULL)
2084 || (fsp->fh->fd == -1))
2086 smb_panic("bad fsp");
2094 switch (ad->ad_type) {
2096 rc = SMB_VFS_NEXT_SETXATTR(handle,
2098 AFPINFO_EA_NETATALK,
2100 AD_DATASZ_XATTR, 0);
2104 len = SMB_VFS_NEXT_PWRITE(handle,
2109 if (len != AD_DATASZ_DOT_UND) {
2110 DBG_ERR("short write on %s: %zd", fsp_str_dbg(fsp), len);
2120 DBG_DEBUG("Path [%s] rc [%d]\n", fsp_str_dbg(fsp), rc);
2125 /*****************************************************************************
2127 *****************************************************************************/
2129 static bool is_afpinfo_stream(const struct smb_filename *smb_fname)
2131 if (strncasecmp_m(smb_fname->stream_name,
2132 AFPINFO_STREAM_NAME,
2133 strlen(AFPINFO_STREAM_NAME)) == 0) {
2139 static bool is_afpresource_stream(const struct smb_filename *smb_fname)
2141 if (strncasecmp_m(smb_fname->stream_name,
2142 AFPRESOURCE_STREAM_NAME,
2143 strlen(AFPRESOURCE_STREAM_NAME)) == 0) {
2150 * Test whether stream is an Apple stream.
2152 static bool is_apple_stream(const struct smb_filename *smb_fname)
2154 if (is_afpinfo_stream(smb_fname)) {
2157 if (is_afpresource_stream(smb_fname)) {
2164 * Initialize config struct from our smb.conf config parameters
2166 static int init_fruit_config(vfs_handle_struct *handle)
2168 struct fruit_config_data *config;
2170 const char *tm_size_str = NULL;
2172 config = talloc_zero(handle->conn, struct fruit_config_data);
2174 DEBUG(1, ("talloc_zero() failed\n"));
2180 * Versions up to Samba 4.5.x had a spelling bug in the
2181 * fruit:resource option calling lp_parm_enum with
2182 * "res*s*ource" (ie two s).
2184 * In Samba 4.6 we accept both the wrong and the correct
2185 * spelling, in Samba 4.7 the bad spelling will be removed.
2187 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2188 "ressource", fruit_rsrc, FRUIT_RSRC_ADFILE);
2189 if (enumval == -1) {
2190 DEBUG(1, ("value for %s: resource type unknown\n",
2191 FRUIT_PARAM_TYPE_NAME));
2194 config->rsrc = (enum fruit_rsrc)enumval;
2196 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2197 "resource", fruit_rsrc, enumval);
2198 if (enumval == -1) {
2199 DEBUG(1, ("value for %s: resource type unknown\n",
2200 FRUIT_PARAM_TYPE_NAME));
2203 config->rsrc = (enum fruit_rsrc)enumval;
2205 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2206 "metadata", fruit_meta, FRUIT_META_NETATALK);
2207 if (enumval == -1) {
2208 DEBUG(1, ("value for %s: metadata type unknown\n",
2209 FRUIT_PARAM_TYPE_NAME));
2212 config->meta = (enum fruit_meta)enumval;
2214 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2215 "locking", fruit_locking, FRUIT_LOCKING_NONE);
2216 if (enumval == -1) {
2217 DEBUG(1, ("value for %s: locking type unknown\n",
2218 FRUIT_PARAM_TYPE_NAME));
2221 config->locking = (enum fruit_locking)enumval;
2223 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2224 "encoding", fruit_encoding, FRUIT_ENC_PRIVATE);
2225 if (enumval == -1) {
2226 DEBUG(1, ("value for %s: encoding type unknown\n",
2227 FRUIT_PARAM_TYPE_NAME));
2230 config->encoding = (enum fruit_encoding)enumval;
2232 if (config->rsrc == FRUIT_RSRC_ADFILE) {
2233 config->veto_appledouble = lp_parm_bool(SNUM(handle->conn),
2234 FRUIT_PARAM_TYPE_NAME,
2239 config->use_aapl = lp_parm_bool(
2240 -1, FRUIT_PARAM_TYPE_NAME, "aapl", true);
2242 config->time_machine = lp_parm_bool(
2243 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "time machine", false);
2245 config->unix_info_enabled = lp_parm_bool(
2246 -1, FRUIT_PARAM_TYPE_NAME, "nfs_aces", true);
2248 config->use_copyfile = lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME,
2251 config->posix_rename = lp_parm_bool(
2252 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "posix_rename", true);
2254 config->aapl_zero_file_id =
2255 lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME, "zero_file_id", true);
2257 config->readdir_attr_rsize = lp_parm_bool(
2258 SNUM(handle->conn), "readdir_attr", "aapl_rsize", true);
2260 config->readdir_attr_finder_info = lp_parm_bool(
2261 SNUM(handle->conn), "readdir_attr", "aapl_finder_info", true);
2263 config->readdir_attr_max_access = lp_parm_bool(
2264 SNUM(handle->conn), "readdir_attr", "aapl_max_access", true);
2266 config->model = lp_parm_const_string(
2267 -1, FRUIT_PARAM_TYPE_NAME, "model", "MacSamba");
2269 tm_size_str = lp_parm_const_string(
2270 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2271 "time machine max size", NULL);
2272 if (tm_size_str != NULL) {
2273 config->time_machine_max_size = conv_str_size(tm_size_str);
2276 config->wipe_intentionally_left_blank_rfork = lp_parm_bool(
2277 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2278 "wipe_intentionally_left_blank_rfork", false);
2280 config->delete_empty_adfiles = lp_parm_bool(
2281 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2282 "delete_empty_adfiles", false);
2284 SMB_VFS_HANDLE_SET_DATA(handle, config,
2285 NULL, struct fruit_config_data,
2292 * Prepend "._" to a basename
2293 * Return a new struct smb_filename with stream_name == NULL.
2295 static int adouble_path(TALLOC_CTX *ctx,
2296 const struct smb_filename *smb_fname_in,
2297 struct smb_filename **pp_smb_fname_out)
2301 struct smb_filename *smb_fname = cp_smb_filename(ctx,
2304 if (smb_fname == NULL) {
2308 /* We need streamname to be NULL */
2309 TALLOC_FREE(smb_fname->stream_name);
2311 /* And we're replacing base_name. */
2312 TALLOC_FREE(smb_fname->base_name);
2314 if (!parent_dirname(smb_fname, smb_fname_in->base_name,
2316 TALLOC_FREE(smb_fname);
2320 smb_fname->base_name = talloc_asprintf(smb_fname,
2321 "%s/._%s", parent, base);
2322 if (smb_fname->base_name == NULL) {
2323 TALLOC_FREE(smb_fname);
2327 *pp_smb_fname_out = smb_fname;
2333 * Allocate and initialize an AfpInfo struct
2335 static AfpInfo *afpinfo_new(TALLOC_CTX *ctx)
2337 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2341 ai->afpi_Signature = AFP_Signature;
2342 ai->afpi_Version = AFP_Version;
2343 ai->afpi_BackupTime = AD_DATE_START;
2348 * Pack an AfpInfo struct into a buffer
2350 * Buffer size must be at least AFP_INFO_SIZE
2351 * Returns size of packed buffer
2353 static ssize_t afpinfo_pack(const AfpInfo *ai, char *buf)
2355 memset(buf, 0, AFP_INFO_SIZE);
2357 RSIVAL(buf, 0, ai->afpi_Signature);
2358 RSIVAL(buf, 4, ai->afpi_Version);
2359 RSIVAL(buf, 12, ai->afpi_BackupTime);
2360 memcpy(buf + 16, ai->afpi_FinderInfo, sizeof(ai->afpi_FinderInfo));
2362 return AFP_INFO_SIZE;
2366 * Unpack a buffer into a AfpInfo structure
2368 * Buffer size must be at least AFP_INFO_SIZE
2369 * Returns allocated AfpInfo struct
2371 static AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data)
2373 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2378 ai->afpi_Signature = RIVAL(data, 0);
2379 ai->afpi_Version = RIVAL(data, 4);
2380 ai->afpi_BackupTime = RIVAL(data, 12);
2381 memcpy(ai->afpi_FinderInfo, (const char *)data + 16,
2382 sizeof(ai->afpi_FinderInfo));
2384 if (ai->afpi_Signature != AFP_Signature
2385 || ai->afpi_Version != AFP_Version) {
2386 DEBUG(1, ("Bad AfpInfo signature or version\n"));
2394 * Fake an inode number from the md5 hash of the (xattr) name
2396 static SMB_INO_T fruit_inode(const SMB_STRUCT_STAT *sbuf, const char *sname)
2398 gnutls_hash_hd_t hash_hnd = NULL;
2399 unsigned char hash[16];
2400 SMB_INO_T result = 0;
2404 DBG_DEBUG("fruit_inode called for %ju/%ju [%s]\n",
2405 (uintmax_t)sbuf->st_ex_dev,
2406 (uintmax_t)sbuf->st_ex_ino, sname);
2408 upper_sname = talloc_strdup_upper(talloc_tos(), sname);
2409 SMB_ASSERT(upper_sname != NULL);
2411 rc = gnutls_hash_init(&hash_hnd, GNUTLS_DIG_MD5);
2416 rc = gnutls_hash(hash_hnd, &(sbuf->st_ex_dev), sizeof(sbuf->st_ex_dev));
2418 gnutls_hash_deinit(hash_hnd, NULL);
2421 rc = gnutls_hash(hash_hnd,
2423 sizeof(sbuf->st_ex_ino));
2425 gnutls_hash_deinit(hash_hnd, NULL);
2428 rc = gnutls_hash(hash_hnd,
2430 talloc_get_size(upper_sname) - 1);
2432 gnutls_hash_deinit(hash_hnd, NULL);
2436 gnutls_hash_deinit(hash_hnd, hash);
2438 /* Hopefully all the variation is in the lower 4 (or 8) bytes! */
2439 memcpy(&result, hash, sizeof(result));
2442 DBG_DEBUG("fruit_inode \"%s\": ino=%ju\n",
2443 sname, (uintmax_t)result);
2446 TALLOC_FREE(upper_sname);
2451 static bool add_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
2452 struct stream_struct **streams,
2453 const char *name, off_t size,
2456 struct stream_struct *tmp;
2458 tmp = talloc_realloc(mem_ctx, *streams, struct stream_struct,
2464 tmp[*num_streams].name = talloc_asprintf(tmp, "%s:$DATA", name);
2465 if (tmp[*num_streams].name == NULL) {
2469 tmp[*num_streams].size = size;
2470 tmp[*num_streams].alloc_size = alloc_size;
2477 static bool filter_empty_rsrc_stream(unsigned int *num_streams,
2478 struct stream_struct **streams)
2480 struct stream_struct *tmp = *streams;
2483 if (*num_streams == 0) {
2487 for (i = 0; i < *num_streams; i++) {
2488 if (strequal_m(tmp[i].name, AFPRESOURCE_STREAM)) {
2493 if (i == *num_streams) {
2497 if (tmp[i].size > 0) {
2501 TALLOC_FREE(tmp[i].name);
2502 if (*num_streams - 1 > i) {
2503 memmove(&tmp[i], &tmp[i+1],
2504 (*num_streams - i - 1) * sizeof(struct stream_struct));
2511 static bool del_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
2512 struct stream_struct **streams,
2515 struct stream_struct *tmp = *streams;
2518 if (*num_streams == 0) {
2522 for (i = 0; i < *num_streams; i++) {
2523 if (strequal_m(tmp[i].name, name)) {
2528 if (i == *num_streams) {
2532 TALLOC_FREE(tmp[i].name);
2533 if (*num_streams - 1 > i) {
2534 memmove(&tmp[i], &tmp[i+1],
2535 (*num_streams - i - 1) * sizeof(struct stream_struct));
2542 static bool ad_empty_finderinfo(const struct adouble *ad)
2545 char emptybuf[ADEDLEN_FINDERI] = {0};
2548 fi = ad_get_entry(ad, ADEID_FINDERI);
2550 DBG_ERR("Missing FinderInfo in struct adouble [%p]\n", ad);
2554 cmp = memcmp(emptybuf, fi, ADEDLEN_FINDERI);
2558 static bool ai_empty_finderinfo(const AfpInfo *ai)
2561 char emptybuf[ADEDLEN_FINDERI] = {0};
2563 cmp = memcmp(emptybuf, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
2568 * Update btime with btime from Netatalk
2570 static void update_btime(vfs_handle_struct *handle,
2571 struct smb_filename *smb_fname)
2574 struct timespec creation_time = {0};
2576 struct fruit_config_data *config = NULL;
2578 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
2581 switch (config->meta) {
2582 case FRUIT_META_STREAM:
2584 case FRUIT_META_NETATALK:
2588 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
2592 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
2596 if (ad_getdate(ad, AD_DATE_UNIX | AD_DATE_CREATE, &t) != 0) {
2602 creation_time.tv_sec = convert_uint32_t_to_time_t(t);
2603 update_stat_ex_create_time(&smb_fname->st, creation_time);
2609 * Map an access mask to a Netatalk single byte byte range lock
2611 static off_t access_to_netatalk_brl(enum apple_fork fork_type,
2612 uint32_t access_mask)
2616 switch (access_mask) {
2617 case FILE_READ_DATA:
2618 offset = AD_FILELOCK_OPEN_RD;
2621 case FILE_WRITE_DATA:
2622 case FILE_APPEND_DATA:
2623 offset = AD_FILELOCK_OPEN_WR;
2627 offset = AD_FILELOCK_OPEN_NONE;
2631 if (fork_type == APPLE_FORK_RSRC) {
2632 if (offset == AD_FILELOCK_OPEN_NONE) {
2633 offset = AD_FILELOCK_RSRC_OPEN_NONE;
2643 * Map a deny mode to a Netatalk brl
2645 static off_t denymode_to_netatalk_brl(enum apple_fork fork_type,
2650 switch (deny_mode) {
2652 offset = AD_FILELOCK_DENY_RD;
2656 offset = AD_FILELOCK_DENY_WR;
2660 smb_panic("denymode_to_netatalk_brl: bad deny mode\n");
2663 if (fork_type == APPLE_FORK_RSRC) {
2671 * Call fcntl() with an exclusive F_GETLK request in order to
2672 * determine if there's an exisiting shared lock
2674 * @return true if the requested lock was found or any error occurred
2675 * false if the lock was not found
2677 static bool test_netatalk_lock(files_struct *fsp, off_t in_offset)
2680 off_t offset = in_offset;
2685 result = SMB_VFS_GETLOCK(fsp, &offset, &len, &type, &pid);
2686 if (result == false) {
2690 if (type != F_UNLCK) {
2697 static NTSTATUS fruit_check_access(vfs_handle_struct *handle,
2699 uint32_t access_mask,
2700 uint32_t share_mode)
2702 NTSTATUS status = NT_STATUS_OK;
2704 bool share_for_read = (share_mode & FILE_SHARE_READ);
2705 bool share_for_write = (share_mode & FILE_SHARE_WRITE);
2706 bool netatalk_already_open_for_reading = false;
2707 bool netatalk_already_open_for_writing = false;
2708 bool netatalk_already_open_with_deny_read = false;
2709 bool netatalk_already_open_with_deny_write = false;
2711 /* FIXME: hardcoded data fork, add resource fork */
2712 enum apple_fork fork_type = APPLE_FORK_DATA;
2714 DBG_DEBUG("fruit_check_access: %s, am: %s/%s, sm: 0x%x\n",
2716 access_mask & FILE_READ_DATA ? "READ" :"-",
2717 access_mask & FILE_WRITE_DATA ? "WRITE" : "-",
2720 if (fsp->fh->fd == -1) {
2721 return NT_STATUS_OK;
2724 /* Read NetATalk opens and deny modes on the file. */
2725 netatalk_already_open_for_reading = test_netatalk_lock(fsp,
2726 access_to_netatalk_brl(fork_type,
2729 netatalk_already_open_with_deny_read = test_netatalk_lock(fsp,
2730 denymode_to_netatalk_brl(fork_type,
2733 netatalk_already_open_for_writing = test_netatalk_lock(fsp,
2734 access_to_netatalk_brl(fork_type,
2737 netatalk_already_open_with_deny_write = test_netatalk_lock(fsp,
2738 denymode_to_netatalk_brl(fork_type,
2741 /* If there are any conflicts - sharing violation. */
2742 if ((access_mask & FILE_READ_DATA) &&
2743 netatalk_already_open_with_deny_read) {
2744 return NT_STATUS_SHARING_VIOLATION;
2747 if (!share_for_read &&
2748 netatalk_already_open_for_reading) {
2749 return NT_STATUS_SHARING_VIOLATION;
2752 if ((access_mask & FILE_WRITE_DATA) &&
2753 netatalk_already_open_with_deny_write) {
2754 return NT_STATUS_SHARING_VIOLATION;
2757 if (!share_for_write &&
2758 netatalk_already_open_for_writing) {
2759 return NT_STATUS_SHARING_VIOLATION;
2762 if (!(access_mask & FILE_READ_DATA)) {
2764 * Nothing we can do here, we need read access
2767 return NT_STATUS_OK;
2770 /* Set NetAtalk locks matching our access */
2771 if (access_mask & FILE_READ_DATA) {
2772 struct byte_range_lock *br_lck = NULL;
2774 off = access_to_netatalk_brl(fork_type, FILE_READ_DATA);
2776 handle->conn->sconn->msg_ctx, fsp,
2777 fsp->op->global->open_persistent_id, 1, off,
2778 READ_LOCK, POSIX_LOCK, false,
2781 TALLOC_FREE(br_lck);
2783 if (!NT_STATUS_IS_OK(status)) {
2788 if (!share_for_read) {
2789 struct byte_range_lock *br_lck = NULL;
2791 off = denymode_to_netatalk_brl(fork_type, DENY_READ);
2793 handle->conn->sconn->msg_ctx, fsp,
2794 fsp->op->global->open_persistent_id, 1, off,
2795 READ_LOCK, POSIX_LOCK, false,
2798 TALLOC_FREE(br_lck);
2800 if (!NT_STATUS_IS_OK(status)) {
2805 if (access_mask & FILE_WRITE_DATA) {
2806 struct byte_range_lock *br_lck = NULL;
2808 off = access_to_netatalk_brl(fork_type, FILE_WRITE_DATA);
2810 handle->conn->sconn->msg_ctx, fsp,
2811 fsp->op->global->open_persistent_id, 1, off,
2812 READ_LOCK, POSIX_LOCK, false,
2815 TALLOC_FREE(br_lck);
2817 if (!NT_STATUS_IS_OK(status)) {
2822 if (!share_for_write) {
2823 struct byte_range_lock *br_lck = NULL;
2825 off = denymode_to_netatalk_brl(fork_type, DENY_WRITE);
2827 handle->conn->sconn->msg_ctx, fsp,
2828 fsp->op->global->open_persistent_id, 1, off,
2829 READ_LOCK, POSIX_LOCK, false,
2832 TALLOC_FREE(br_lck);
2834 if (!NT_STATUS_IS_OK(status)) {
2839 return NT_STATUS_OK;
2842 static NTSTATUS check_aapl(vfs_handle_struct *handle,
2843 struct smb_request *req,
2844 const struct smb2_create_blobs *in_context_blobs,
2845 struct smb2_create_blobs *out_context_blobs)
2847 struct fruit_config_data *config;
2849 struct smb2_create_blob *aapl = NULL;
2853 DATA_BLOB blob = data_blob_talloc(req, NULL, 0);
2854 uint64_t req_bitmap, client_caps;
2855 uint64_t server_caps = SMB2_CRTCTX_AAPL_UNIX_BASED;
2859 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
2860 return NT_STATUS_UNSUCCESSFUL);
2862 if (!config->use_aapl
2863 || in_context_blobs == NULL
2864 || out_context_blobs == NULL) {
2865 return NT_STATUS_OK;
2868 aapl = smb2_create_blob_find(in_context_blobs,
2869 SMB2_CREATE_TAG_AAPL);
2871 return NT_STATUS_OK;
2874 if (aapl->data.length != 24) {
2875 DEBUG(1, ("unexpected AAPL ctxt length: %ju\n",
2876 (uintmax_t)aapl->data.length));
2877 return NT_STATUS_INVALID_PARAMETER;
2880 cmd = IVAL(aapl->data.data, 0);
2881 if (cmd != SMB2_CRTCTX_AAPL_SERVER_QUERY) {
2882 DEBUG(1, ("unsupported AAPL cmd: %d\n", cmd));
2883 return NT_STATUS_INVALID_PARAMETER;
2886 req_bitmap = BVAL(aapl->data.data, 8);
2887 client_caps = BVAL(aapl->data.data, 16);
2889 SIVAL(p, 0, SMB2_CRTCTX_AAPL_SERVER_QUERY);
2891 SBVAL(p, 8, req_bitmap);
2892 ok = data_blob_append(req, &blob, p, 16);
2894 return NT_STATUS_UNSUCCESSFUL;
2897 if (req_bitmap & SMB2_CRTCTX_AAPL_SERVER_CAPS) {
2898 if ((client_caps & SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR) &&
2899 (handle->conn->tcon->compat->fs_capabilities & FILE_NAMED_STREAMS)) {
2900 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR;
2901 config->readdir_attr_enabled = true;
2904 if (config->use_copyfile) {
2905 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_OSX_COPYFILE;
2906 config->copyfile_enabled = true;
2910 * The client doesn't set the flag, so we can't check
2911 * for it and just set it unconditionally
2913 if (config->unix_info_enabled) {
2914 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_NFS_ACE;
2917 SBVAL(p, 0, server_caps);
2918 ok = data_blob_append(req, &blob, p, 8);
2920 return NT_STATUS_UNSUCCESSFUL;
2924 if (req_bitmap & SMB2_CRTCTX_AAPL_VOLUME_CAPS) {
2925 int val = lp_case_sensitive(SNUM(handle->conn->tcon->compat));
2933 caps |= SMB2_CRTCTX_AAPL_CASE_SENSITIVE;
2940 if (config->time_machine) {
2941 caps |= SMB2_CRTCTX_AAPL_FULL_SYNC;
2946 ok = data_blob_append(req, &blob, p, 8);
2948 return NT_STATUS_UNSUCCESSFUL;
2952 if (req_bitmap & SMB2_CRTCTX_AAPL_MODEL_INFO) {
2953 ok = convert_string_talloc(req,
2954 CH_UNIX, CH_UTF16LE,
2955 config->model, strlen(config->model),
2958 return NT_STATUS_UNSUCCESSFUL;
2962 SIVAL(p + 4, 0, modellen);
2963 ok = data_blob_append(req, &blob, p, 8);
2966 return NT_STATUS_UNSUCCESSFUL;
2969 ok = data_blob_append(req, &blob, model, modellen);
2972 return NT_STATUS_UNSUCCESSFUL;
2976 status = smb2_create_blob_add(out_context_blobs,
2978 SMB2_CREATE_TAG_AAPL,
2980 if (NT_STATUS_IS_OK(status)) {
2981 global_fruit_config.nego_aapl = true;
2982 if (config->aapl_zero_file_id) {
2983 aapl_force_zero_file_id(handle->conn->sconn);
2990 static bool readdir_attr_meta_finderi_stream(
2991 struct vfs_handle_struct *handle,
2992 const struct smb_filename *smb_fname,
2995 struct smb_filename *stream_name = NULL;
2996 files_struct *fsp = NULL;
3001 uint8_t buf[AFP_INFO_SIZE];
3003 stream_name = synthetic_smb_fname(talloc_tos(),
3004 smb_fname->base_name,
3005 AFPINFO_STREAM_NAME,
3006 NULL, smb_fname->flags);
3007 if (stream_name == NULL) {
3011 ret = SMB_VFS_STAT(handle->conn, stream_name);
3016 status = SMB_VFS_CREATE_FILE(
3017 handle->conn, /* conn */
3019 0, /* root_dir_fid */
3020 stream_name, /* fname */
3021 FILE_READ_DATA, /* access_mask */
3022 (FILE_SHARE_READ | FILE_SHARE_WRITE | /* share_access */
3024 FILE_OPEN, /* create_disposition*/
3025 0, /* create_options */
3026 0, /* file_attributes */
3027 INTERNAL_OPEN_ONLY, /* oplock_request */
3029 0, /* allocation_size */
3030 0, /* private_flags */
3035 NULL, NULL); /* create context */
3037 TALLOC_FREE(stream_name);
3039 if (!NT_STATUS_IS_OK(status)) {
3043 nread = SMB_VFS_PREAD(fsp, &buf[0], AFP_INFO_SIZE, 0);
3044 if (nread != AFP_INFO_SIZE) {
3045 DBG_ERR("short read [%s] [%zd/%d]\n",
3046 smb_fname_str_dbg(stream_name), nread, AFP_INFO_SIZE);
3051 memcpy(&ai->afpi_FinderInfo[0], &buf[AFP_OFF_FinderInfo],
3058 close_file(NULL, fsp, NORMAL_CLOSE);
3064 static bool readdir_attr_meta_finderi_netatalk(
3065 struct vfs_handle_struct *handle,
3066 const struct smb_filename *smb_fname,
3069 struct adouble *ad = NULL;
3072 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
3077 p = ad_get_entry(ad, ADEID_FINDERI);
3079 DBG_ERR("No ADEID_FINDERI for [%s]\n", smb_fname->base_name);
3084 memcpy(&ai->afpi_FinderInfo[0], p, AFP_FinderSize);
3089 static bool readdir_attr_meta_finderi(struct vfs_handle_struct *handle,
3090 const struct smb_filename *smb_fname,
3091 struct readdir_attr_data *attr_data)
3093 struct fruit_config_data *config = NULL;
3094 uint32_t date_added;
3098 SMB_VFS_HANDLE_GET_DATA(handle, config,
3099 struct fruit_config_data,
3102 switch (config->meta) {
3103 case FRUIT_META_NETATALK:
3104 ok = readdir_attr_meta_finderi_netatalk(
3105 handle, smb_fname, &ai);
3108 case FRUIT_META_STREAM:
3109 ok = readdir_attr_meta_finderi_stream(
3110 handle, smb_fname, &ai);
3114 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3119 /* Don't bother with errors, it's likely ENOENT */
3123 if (S_ISREG(smb_fname->st.st_ex_mode)) {
3125 memcpy(&attr_data->attr_data.aapl.finder_info[0],
3126 &ai.afpi_FinderInfo[0], 4);
3128 /* finder_creator */
3129 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 4,
3130 &ai.afpi_FinderInfo[4], 4);
3134 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 8,
3135 &ai.afpi_FinderInfo[8], 2);
3137 /* finder_ext_flags */
3138 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 10,
3139 &ai.afpi_FinderInfo[24], 2);
3142 date_added = convert_time_t_to_uint32_t(
3143 smb_fname->st.st_ex_btime.tv_sec - AD_DATE_DELTA);
3145 RSIVAL(&attr_data->attr_data.aapl.finder_info[0], 12, date_added);
3150 static uint64_t readdir_attr_rfork_size_adouble(
3151 struct vfs_handle_struct *handle,
3152 const struct smb_filename *smb_fname)
3154 struct adouble *ad = NULL;
3155 uint64_t rfork_size;
3157 ad = ad_get(talloc_tos(), handle, smb_fname,
3163 rfork_size = ad_getentrylen(ad, ADEID_RFORK);
3169 static uint64_t readdir_attr_rfork_size_stream(
3170 struct vfs_handle_struct *handle,
3171 const struct smb_filename *smb_fname)
3173 struct smb_filename *stream_name = NULL;
3175 uint64_t rfork_size;
3177 stream_name = synthetic_smb_fname(talloc_tos(),
3178 smb_fname->base_name,
3179 AFPRESOURCE_STREAM_NAME,
3181 if (stream_name == NULL) {
3185 ret = SMB_VFS_STAT(handle->conn, stream_name);
3187 TALLOC_FREE(stream_name);
3191 rfork_size = stream_name->st.st_ex_size;
3192 TALLOC_FREE(stream_name);
3197 static uint64_t readdir_attr_rfork_size(struct vfs_handle_struct *handle,
3198 const struct smb_filename *smb_fname)
3200 struct fruit_config_data *config = NULL;
3201 uint64_t rfork_size;
3203 SMB_VFS_HANDLE_GET_DATA(handle, config,
3204 struct fruit_config_data,
3207 switch (config->rsrc) {
3208 case FRUIT_RSRC_ADFILE:
3209 case FRUIT_RSRC_XATTR:
3210 rfork_size = readdir_attr_rfork_size_adouble(handle,
3214 case FRUIT_META_STREAM:
3215 rfork_size = readdir_attr_rfork_size_stream(handle,
3220 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3228 static NTSTATUS readdir_attr_macmeta(struct vfs_handle_struct *handle,
3229 const struct smb_filename *smb_fname,
3230 struct readdir_attr_data *attr_data)
3232 NTSTATUS status = NT_STATUS_OK;
3233 struct fruit_config_data *config = NULL;
3236 SMB_VFS_HANDLE_GET_DATA(handle, config,
3237 struct fruit_config_data,
3238 return NT_STATUS_UNSUCCESSFUL);
3241 /* Ensure we return a default value in the creation_date field */
3242 RSIVAL(&attr_data->attr_data.aapl.finder_info, 12, AD_DATE_START);
3245 * Resource fork length
3248 if (config->readdir_attr_rsize) {
3249 uint64_t rfork_size;
3251 rfork_size = readdir_attr_rfork_size(handle, smb_fname);
3252 attr_data->attr_data.aapl.rfork_size = rfork_size;
3259 if (config->readdir_attr_finder_info) {
3260 ok = readdir_attr_meta_finderi(handle, smb_fname, attr_data);
3262 status = NT_STATUS_INTERNAL_ERROR;
3269 static NTSTATUS remove_virtual_nfs_aces(struct security_descriptor *psd)
3274 if (psd->dacl == NULL) {
3275 return NT_STATUS_OK;
3278 for (i = 0; i < psd->dacl->num_aces; i++) {
3279 /* MS NFS style mode/uid/gid */
3280 int cmp = dom_sid_compare_domain(
3281 &global_sid_Unix_NFS,
3282 &psd->dacl->aces[i].trustee);
3284 /* Normal ACE entry. */
3289 * security_descriptor_dacl_del()
3290 * *must* return NT_STATUS_OK as we know
3291 * we have something to remove.
3294 status = security_descriptor_dacl_del(psd,
3295 &psd->dacl->aces[i].trustee);
3296 if (!NT_STATUS_IS_OK(status)) {
3297 DBG_WARNING("failed to remove MS NFS style ACE: %s\n",
3303 * security_descriptor_dacl_del() may delete more
3304 * then one entry subsequent to this one if the
3305 * SID matches, but we only need to ensure that
3306 * we stay looking at the same element in the array.
3310 return NT_STATUS_OK;
3313 /* Search MS NFS style ACE with UNIX mode */
3314 static NTSTATUS check_ms_nfs(vfs_handle_struct *handle,
3316 struct security_descriptor *psd,
3321 struct fruit_config_data *config = NULL;
3325 SMB_VFS_HANDLE_GET_DATA(handle, config,
3326 struct fruit_config_data,
3327 return NT_STATUS_UNSUCCESSFUL);
3329 if (!global_fruit_config.nego_aapl) {
3330 return NT_STATUS_OK;
3332 if (psd->dacl == NULL || !config->unix_info_enabled) {
3333 return NT_STATUS_OK;
3336 for (i = 0; i < psd->dacl->num_aces; i++) {
3337 if (dom_sid_compare_domain(
3338 &global_sid_Unix_NFS_Mode,
3339 &psd->dacl->aces[i].trustee) == 0) {
3340 *pmode = (mode_t)psd->dacl->aces[i].trustee.sub_auths[2];
3341 *pmode &= (S_IRWXU | S_IRWXG | S_IRWXO);
3344 DEBUG(10, ("MS NFS chmod request %s, %04o\n",
3345 fsp_str_dbg(fsp), (unsigned)(*pmode)));
3351 * Remove any incoming virtual ACE entries generated by
3352 * fruit_fget_nt_acl().
3355 return remove_virtual_nfs_aces(psd);
3358 /****************************************************************************
3360 ****************************************************************************/
3362 static int fruit_connect(vfs_handle_struct *handle,
3363 const char *service,
3367 char *list = NULL, *newlist = NULL;
3368 struct fruit_config_data *config;
3370 DEBUG(10, ("fruit_connect\n"));
3372 rc = SMB_VFS_NEXT_CONNECT(handle, service, user);
3377 rc = init_fruit_config(handle);
3382 SMB_VFS_HANDLE_GET_DATA(handle, config,
3383 struct fruit_config_data, return -1);
3385 if (config->veto_appledouble) {
3386 list = lp_veto_files(talloc_tos(), SNUM(handle->conn));
3389 if (strstr(list, "/" ADOUBLE_NAME_PREFIX "*/") == NULL) {
3390 newlist = talloc_asprintf(
3392 "%s/" ADOUBLE_NAME_PREFIX "*/",
3394 lp_do_parameter(SNUM(handle->conn),
3399 lp_do_parameter(SNUM(handle->conn),
3401 "/" ADOUBLE_NAME_PREFIX "*/");
3407 if (config->encoding == FRUIT_ENC_NATIVE) {
3408 lp_do_parameter(SNUM(handle->conn),
3413 if (config->time_machine) {
3414 DBG_NOTICE("Enabling durable handles for Time Machine "
3415 "support on [%s]\n", service);
3416 lp_do_parameter(SNUM(handle->conn), "durable handles", "yes");
3417 lp_do_parameter(SNUM(handle->conn), "kernel oplocks", "no");
3418 lp_do_parameter(SNUM(handle->conn), "kernel share modes", "no");
3419 if (!lp_strict_sync(SNUM(handle->conn))) {
3420 DBG_WARNING("Time Machine without strict sync is not "
3423 lp_do_parameter(SNUM(handle->conn), "posix locking", "no");
3429 static int fruit_fake_fd(void)
3436 * Return a valid fd, but ensure any attempt to use it returns
3437 * an error (EPIPE). Once we get a write on the handle, we open
3440 ret = pipe(pipe_fds);
3450 static int fruit_open_meta_stream(vfs_handle_struct *handle,
3451 struct smb_filename *smb_fname,
3456 struct fruit_config_data *config = NULL;
3457 struct fio *fio = NULL;
3458 int open_flags = flags & ~O_CREAT;
3461 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3463 SMB_VFS_HANDLE_GET_DATA(handle, config,
3464 struct fruit_config_data, return -1);
3466 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3467 fio->type = ADOUBLE_META;
3468 fio->config = config;
3470 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, open_flags, mode);
3475 if (!(flags & O_CREAT)) {
3476 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
3480 fd = fruit_fake_fd();
3482 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
3486 fio->fake_fd = true;
3493 static int fruit_open_meta_netatalk(vfs_handle_struct *handle,
3494 struct smb_filename *smb_fname,
3499 struct fruit_config_data *config = NULL;
3500 struct fio *fio = NULL;
3501 struct adouble *ad = NULL;
3502 bool meta_exists = false;
3505 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3507 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
3514 if (!meta_exists && !(flags & O_CREAT)) {
3519 fd = fruit_fake_fd();
3524 SMB_VFS_HANDLE_GET_DATA(handle, config,
3525 struct fruit_config_data, return -1);
3527 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3528 fio->type = ADOUBLE_META;
3529 fio->config = config;
3530 fio->fake_fd = true;
3537 static int fruit_open_meta(vfs_handle_struct *handle,
3538 struct smb_filename *smb_fname,
3539 files_struct *fsp, int flags, mode_t mode)
3542 struct fruit_config_data *config = NULL;
3544 DBG_DEBUG("path [%s]\n", smb_fname_str_dbg(smb_fname));
3546 SMB_VFS_HANDLE_GET_DATA(handle, config,
3547 struct fruit_config_data, return -1);
3549 switch (config->meta) {
3550 case FRUIT_META_STREAM:
3551 fd = fruit_open_meta_stream(handle, smb_fname,
3555 case FRUIT_META_NETATALK:
3556 fd = fruit_open_meta_netatalk(handle, smb_fname,
3561 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3565 DBG_DEBUG("path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3570 static int fruit_open_rsrc_adouble(vfs_handle_struct *handle,
3571 struct smb_filename *smb_fname,
3577 struct adouble *ad = NULL;
3578 struct smb_filename *smb_fname_base = NULL;
3579 struct fruit_config_data *config = NULL;
3582 SMB_VFS_HANDLE_GET_DATA(handle, config,
3583 struct fruit_config_data, return -1);
3585 if ((!(flags & O_CREAT)) &&
3586 S_ISDIR(fsp->base_fsp->fsp_name->st.st_ex_mode))
3588 /* sorry, but directories don't habe a resource fork */
3593 rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_base);
3598 /* We always need read/write access for the metadata header too */
3599 flags &= ~(O_RDONLY | O_WRONLY);
3602 hostfd = SMB_VFS_NEXT_OPEN(handle, smb_fname_base, fsp,
3609 if (flags & (O_CREAT | O_TRUNC)) {
3610 ad = ad_init(fsp, handle, ADOUBLE_RSRC);
3616 fsp->fh->fd = hostfd;
3618 rc = ad_fset(handle, ad, fsp);
3629 TALLOC_FREE(smb_fname_base);
3631 DEBUG(10, ("fruit_open resource fork: rc=%d, fd=%d\n", rc, hostfd));
3633 int saved_errno = errno;
3636 * BUGBUGBUG -- we would need to call
3637 * fd_close_posix here, but we don't have a
3640 fsp->fh->fd = hostfd;
3644 errno = saved_errno;
3649 static int fruit_open_rsrc_xattr(vfs_handle_struct *handle,
3650 struct smb_filename *smb_fname,
3655 #ifdef HAVE_ATTROPEN
3658 fd = attropen(smb_fname->base_name,
3659 AFPRESOURCE_EA_NETATALK,
3674 static int fruit_open_rsrc(vfs_handle_struct *handle,
3675 struct smb_filename *smb_fname,
3676 files_struct *fsp, int flags, mode_t mode)
3679 struct fruit_config_data *config = NULL;
3680 struct fio *fio = NULL;
3682 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3684 SMB_VFS_HANDLE_GET_DATA(handle, config,
3685 struct fruit_config_data, return -1);
3687 switch (config->rsrc) {
3688 case FRUIT_RSRC_STREAM:
3689 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3692 case FRUIT_RSRC_ADFILE:
3693 fd = fruit_open_rsrc_adouble(handle, smb_fname,
3697 case FRUIT_RSRC_XATTR:
3698 fd = fruit_open_rsrc_xattr(handle, smb_fname,
3703 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3707 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3713 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3714 fio->type = ADOUBLE_RSRC;
3715 fio->config = config;
3720 static int fruit_open(vfs_handle_struct *handle,
3721 struct smb_filename *smb_fname,
3722 files_struct *fsp, int flags, mode_t mode)
3726 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3728 if (!is_ntfs_stream_smb_fname(smb_fname)) {
3729 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3732 if (is_afpinfo_stream(smb_fname)) {
3733 fd = fruit_open_meta(handle, smb_fname, fsp, flags, mode);
3734 } else if (is_afpresource_stream(smb_fname)) {
3735 fd = fruit_open_rsrc(handle, smb_fname, fsp, flags, mode);
3737 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3740 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3745 static int fruit_close_meta(vfs_handle_struct *handle,
3749 struct fruit_config_data *config = NULL;
3751 SMB_VFS_HANDLE_GET_DATA(handle, config,
3752 struct fruit_config_data, return -1);
3754 switch (config->meta) {
3755 case FRUIT_META_STREAM:
3756 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3759 case FRUIT_META_NETATALK:
3760 ret = close(fsp->fh->fd);
3765 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3773 static int fruit_close_rsrc(vfs_handle_struct *handle,
3777 struct fruit_config_data *config = NULL;
3779 SMB_VFS_HANDLE_GET_DATA(handle, config,
3780 struct fruit_config_data, return -1);
3782 switch (config->rsrc) {
3783 case FRUIT_RSRC_STREAM:
3784 case FRUIT_RSRC_ADFILE:
3785 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3788 case FRUIT_RSRC_XATTR:
3789 ret = close(fsp->fh->fd);
3794 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3801 static int fruit_close(vfs_handle_struct *handle,
3809 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(fsp->fsp_name), fd);
3811 if (!is_ntfs_stream_smb_fname(fsp->fsp_name)) {
3812 return SMB_VFS_NEXT_CLOSE(handle, fsp);
3815 if (is_afpinfo_stream(fsp->fsp_name)) {
3816 ret = fruit_close_meta(handle, fsp);
3817 } else if (is_afpresource_stream(fsp->fsp_name)) {
3818 ret = fruit_close_rsrc(handle, fsp);
3820 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3826 static int fruit_rename(struct vfs_handle_struct *handle,
3827 const struct smb_filename *smb_fname_src,
3828 const struct smb_filename *smb_fname_dst)
3831 struct fruit_config_data *config = NULL;
3832 struct smb_filename *src_adp_smb_fname = NULL;
3833 struct smb_filename *dst_adp_smb_fname = NULL;
3835 SMB_VFS_HANDLE_GET_DATA(handle, config,
3836 struct fruit_config_data, return -1);
3838 if (!VALID_STAT(smb_fname_src->st)) {
3839 DBG_ERR("Need valid stat for [%s]\n",
3840 smb_fname_str_dbg(smb_fname_src));
3844 rc = SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
3849 if ((config->rsrc != FRUIT_RSRC_ADFILE) ||
3850 (!S_ISREG(smb_fname_src->st.st_ex_mode)))
3855 rc = adouble_path(talloc_tos(), smb_fname_src, &src_adp_smb_fname);
3860 rc = adouble_path(talloc_tos(), smb_fname_dst, &dst_adp_smb_fname);
3865 DBG_DEBUG("%s -> %s\n",
3866 smb_fname_str_dbg(src_adp_smb_fname),
3867 smb_fname_str_dbg(dst_adp_smb_fname));
3869 rc = SMB_VFS_NEXT_RENAME(handle, src_adp_smb_fname, dst_adp_smb_fname);
3870 if (errno == ENOENT) {
3875 TALLOC_FREE(src_adp_smb_fname);
3876 TALLOC_FREE(dst_adp_smb_fname);
3880 static int fruit_unlink_meta_stream(vfs_handle_struct *handle,
3881 const struct smb_filename *smb_fname)
3883 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3886 static int fruit_unlink_meta_netatalk(vfs_handle_struct *handle,
3887 const struct smb_filename *smb_fname)
3889 return SMB_VFS_REMOVEXATTR(handle->conn,
3891 AFPINFO_EA_NETATALK);
3894 static int fruit_unlink_meta(vfs_handle_struct *handle,
3895 const struct smb_filename *smb_fname)
3897 struct fruit_config_data *config = NULL;
3900 SMB_VFS_HANDLE_GET_DATA(handle, config,
3901 struct fruit_config_data, return -1);
3903 switch (config->meta) {
3904 case FRUIT_META_STREAM:
3905 rc = fruit_unlink_meta_stream(handle, smb_fname);
3908 case FRUIT_META_NETATALK:
3909 rc = fruit_unlink_meta_netatalk(handle, smb_fname);
3913 DBG_ERR("Unsupported meta config [%d]\n", config->meta);
3920 static int fruit_unlink_rsrc_stream(vfs_handle_struct *handle,
3921 const struct smb_filename *smb_fname,
3926 if (!force_unlink) {
3927 struct smb_filename *smb_fname_cp = NULL;
3930 smb_fname_cp = cp_smb_filename(talloc_tos(), smb_fname);
3931 if (smb_fname_cp == NULL) {
3936 * 0 byte resource fork streams are not listed by
3937 * vfs_streaminfo, as a result stream cleanup/deletion of file
3938 * deletion doesn't remove the resourcefork stream.
3941 ret = SMB_VFS_NEXT_STAT(handle, smb_fname_cp);
3943 TALLOC_FREE(smb_fname_cp);
3944 DBG_ERR("stat [%s] failed [%s]\n",
3945 smb_fname_str_dbg(smb_fname_cp), strerror(errno));
3949 size = smb_fname_cp->st.st_ex_size;
3950 TALLOC_FREE(smb_fname_cp);
3953 /* OS X ignores resource fork stream delete requests */
3958 ret = SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3959 if ((ret != 0) && (errno == ENOENT) && force_unlink) {
3966 static int fruit_unlink_rsrc_adouble(vfs_handle_struct *handle,
3967 const struct smb_filename *smb_fname,
3971 struct adouble *ad = NULL;
3972 struct smb_filename *adp_smb_fname = NULL;
3974 if (!force_unlink) {
3975 ad = ad_get(talloc_tos(), handle, smb_fname,
3984 * 0 byte resource fork streams are not listed by
3985 * vfs_streaminfo, as a result stream cleanup/deletion of file
3986 * deletion doesn't remove the resourcefork stream.
3989 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
3990 /* OS X ignores resource fork stream delete requests */
3998 rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
4003 rc = SMB_VFS_NEXT_UNLINK(handle, adp_smb_fname);
4004 TALLOC_FREE(adp_smb_fname);
4005 if ((rc != 0) && (errno == ENOENT) && force_unlink) {
4012 static int fruit_unlink_rsrc_xattr(vfs_handle_struct *handle,
4013 const struct smb_filename *smb_fname,
4017 * OS X ignores resource fork stream delete requests, so nothing to do
4018 * here. Removing the file will remove the xattr anyway, so we don't
4019 * have to take care of removing 0 byte resource forks that could be
4025 static int fruit_unlink_rsrc(vfs_handle_struct *handle,
4026 const struct smb_filename *smb_fname,
4029 struct fruit_config_data *config = NULL;
4032 SMB_VFS_HANDLE_GET_DATA(handle, config,
4033 struct fruit_config_data, return -1);
4035 switch (config->rsrc) {
4036 case FRUIT_RSRC_STREAM:
4037 rc = fruit_unlink_rsrc_stream(handle, smb_fname, force_unlink);
4040 case FRUIT_RSRC_ADFILE:
4041 rc = fruit_unlink_rsrc_adouble(handle, smb_fname, force_unlink);
4044 case FRUIT_RSRC_XATTR:
4045 rc = fruit_unlink_rsrc_xattr(handle, smb_fname, force_unlink);
4049 DBG_ERR("Unsupported rsrc config [%d]\n", config->rsrc);
4056 static int fruit_unlink(vfs_handle_struct *handle,
4057 const struct smb_filename *smb_fname)
4060 struct fruit_config_data *config = NULL;
4061 struct smb_filename *rsrc_smb_fname = NULL;
4063 SMB_VFS_HANDLE_GET_DATA(handle, config,
4064 struct fruit_config_data, return -1);
4066 if (is_afpinfo_stream(smb_fname)) {
4067 return fruit_unlink_meta(handle, smb_fname);
4068 } else if (is_afpresource_stream(smb_fname)) {
4069 return fruit_unlink_rsrc(handle, smb_fname, false);
4070 } if (is_ntfs_stream_smb_fname(smb_fname)) {
4071 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4075 * A request to delete the base file. Because 0 byte resource
4076 * fork streams are not listed by fruit_streaminfo,
4077 * delete_all_streams() can't remove 0 byte resource fork
4078 * streams, so we have to cleanup this here.
4080 rsrc_smb_fname = synthetic_smb_fname(talloc_tos(),
4081 smb_fname->base_name,
4082 AFPRESOURCE_STREAM_NAME,
4085 if (rsrc_smb_fname == NULL) {
4089 rc = fruit_unlink_rsrc(handle, rsrc_smb_fname, true);
4090 if ((rc != 0) && (errno != ENOENT)) {
4091 DBG_ERR("Forced unlink of [%s] failed [%s]\n",
4092 smb_fname_str_dbg(rsrc_smb_fname), strerror(errno));
4093 TALLOC_FREE(rsrc_smb_fname);
4096 TALLOC_FREE(rsrc_smb_fname);
4098 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4101 static int fruit_chmod(vfs_handle_struct *handle,
4102 const struct smb_filename *smb_fname,
4106 struct fruit_config_data *config = NULL;
4107 struct smb_filename *smb_fname_adp = NULL;
4109 rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname, mode);
4114 SMB_VFS_HANDLE_GET_DATA(handle, config,
4115 struct fruit_config_data, return -1);
4117 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4121 if (!VALID_STAT(smb_fname->st)) {
4125 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
4129 rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_adp);
4134 DEBUG(10, ("fruit_chmod: %s\n", smb_fname_adp->base_name));
4136 rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname_adp, mode);
4137 if (errno == ENOENT) {
4141 TALLOC_FREE(smb_fname_adp);
4145 static int fruit_chown(vfs_handle_struct *handle,
4146 const struct smb_filename *smb_fname,
4151 struct fruit_config_data *config = NULL;
4152 struct smb_filename *adp_smb_fname = NULL;
4154 rc = SMB_VFS_NEXT_CHOWN(handle, smb_fname, uid, gid);
4159 SMB_VFS_HANDLE_GET_DATA(handle, config,
4160 struct fruit_config_data, return -1);
4162 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4166 if (!VALID_STAT(smb_fname->st)) {
4170 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
4174 rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
4179 DEBUG(10, ("fruit_chown: %s\n", adp_smb_fname->base_name));
4181 rc = SMB_VFS_NEXT_CHOWN(handle, adp_smb_fname, uid, gid);
4182 if (errno == ENOENT) {
4187 TALLOC_FREE(adp_smb_fname);
4191 static int fruit_rmdir(struct vfs_handle_struct *handle,
4192 const struct smb_filename *smb_fname)
4196 struct fruit_config_data *config;
4198 SMB_VFS_HANDLE_GET_DATA(handle, config,
4199 struct fruit_config_data, return -1);
4201 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4206 * Due to there is no way to change bDeleteVetoFiles variable
4207 * from this module, need to clean up ourselves
4210 dh = SMB_VFS_OPENDIR(handle->conn, smb_fname, NULL, 0);
4215 while ((de = SMB_VFS_READDIR(handle->conn, dh, NULL)) != NULL) {
4217 struct adouble *ad = NULL;
4219 struct smb_filename *ad_smb_fname = NULL;
4222 match = strncmp(de->d_name,
4223 ADOUBLE_NAME_PREFIX,
4224 strlen(ADOUBLE_NAME_PREFIX));
4229 p = talloc_asprintf(talloc_tos(), "%s/%s",
4230 smb_fname->base_name, de->d_name);
4232 DBG_ERR("talloc_asprintf failed\n");
4236 ad_smb_fname = synthetic_smb_fname(talloc_tos(), p,
4240 if (ad_smb_fname == NULL) {
4241 DBG_ERR("synthetic_smb_fname failed\n");
4246 * Check whether it's a valid AppleDouble file, if
4247 * yes, delete it, ignore it otherwise.
4249 ad = ad_get(talloc_tos(), handle, ad_smb_fname, ADOUBLE_RSRC);
4251 TALLOC_FREE(ad_smb_fname);
4257 ret = SMB_VFS_NEXT_UNLINK(handle, ad_smb_fname);
4259 DBG_ERR("Deleting [%s] failed\n",
4260 smb_fname_str_dbg(ad_smb_fname));
4262 TALLOC_FREE(ad_smb_fname);
4267 SMB_VFS_CLOSEDIR(handle->conn, dh);
4269 return SMB_VFS_NEXT_RMDIR(handle, smb_fname);
4272 static ssize_t fruit_pread_meta_stream(vfs_handle_struct *handle,
4273 files_struct *fsp, void *data,
4274 size_t n, off_t offset)
4279 nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4280 if (nread == -1 || nread == n) {
4284 DBG_ERR("Removing [%s] after short read [%zd]\n",
4285 fsp_str_dbg(fsp), nread);
4287 ret = SMB_VFS_NEXT_UNLINK(handle, fsp->fsp_name);
4289 DBG_ERR("Removing [%s] failed\n", fsp_str_dbg(fsp));
4297 static ssize_t fruit_pread_meta_adouble(vfs_handle_struct *handle,
4298 files_struct *fsp, void *data,
4299 size_t n, off_t offset)
4302 struct adouble *ad = NULL;
4303 char afpinfo_buf[AFP_INFO_SIZE];
4307 ai = afpinfo_new(talloc_tos());
4312 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
4318 p = ad_get_entry(ad, ADEID_FINDERI);
4320 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
4325 memcpy(&ai->afpi_FinderInfo[0], p, ADEDLEN_FINDERI);
4327 nread = afpinfo_pack(ai, afpinfo_buf);
4328 if (nread != AFP_INFO_SIZE) {
4333 memcpy(data, afpinfo_buf, n);
4341 static ssize_t fruit_pread_meta(vfs_handle_struct *handle,
4342 files_struct *fsp, void *data,
4343 size_t n, off_t offset)
4345 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4350 * OS X has a off-by-1 error in the offset calculation, so we're
4351 * bug compatible here. It won't hurt, as any relevant real
4352 * world read requests from the AFP_AfpInfo stream will be
4353 * offset=0 n=60. offset is ignored anyway, see below.
4355 if ((offset < 0) || (offset >= AFP_INFO_SIZE + 1)) {
4360 DBG_ERR("Failed to fetch fsp extension");
4364 /* Yes, macOS always reads from offset 0 */
4366 to_return = MIN(n, AFP_INFO_SIZE);
4368 switch (fio->config->meta) {
4369 case FRUIT_META_STREAM:
4370 nread = fruit_pread_meta_stream(handle, fsp, data,
4374 case FRUIT_META_NETATALK:
4375 nread = fruit_pread_meta_adouble(handle, fsp, data,
4380 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
4384 if (nread == -1 && fio->created) {
4386 char afpinfo_buf[AFP_INFO_SIZE];
4388 ai = afpinfo_new(talloc_tos());
4393 nread = afpinfo_pack(ai, afpinfo_buf);
4395 if (nread != AFP_INFO_SIZE) {
4399 memcpy(data, afpinfo_buf, to_return);
4406 static ssize_t fruit_pread_rsrc_stream(vfs_handle_struct *handle,
4407 files_struct *fsp, void *data,
4408 size_t n, off_t offset)
4410 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4413 static ssize_t fruit_pread_rsrc_xattr(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_adouble(vfs_handle_struct *handle,
4421 files_struct *fsp, void *data,
4422 size_t n, off_t offset)
4424 struct adouble *ad = NULL;
4427 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
4432 nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n,
4433 offset + ad_getentryoff(ad, ADEID_RFORK));
4439 static ssize_t fruit_pread_rsrc(vfs_handle_struct *handle,
4440 files_struct *fsp, void *data,
4441 size_t n, off_t offset)
4443 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4451 switch (fio->config->rsrc) {
4452 case FRUIT_RSRC_STREAM:
4453 nread = fruit_pread_rsrc_stream(handle, fsp, data, n, offset);
4456 case FRUIT_RSRC_ADFILE:
4457 nread = fruit_pread_rsrc_adouble(handle, fsp, data, n, offset);
4460 case FRUIT_RSRC_XATTR:
4461 nread = fruit_pread_rsrc_xattr(handle, fsp, data, n, offset);
4465 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
4472 static ssize_t fruit_pread(vfs_handle_struct *handle,
4473 files_struct *fsp, void *data,
4474 size_t n, off_t offset)
4476 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4479 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4480 fsp_str_dbg(fsp), (intmax_t)offset, n);
4483 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4486 if (fio->type == ADOUBLE_META) {
4487 nread = fruit_pread_meta(handle, fsp, data, n, offset);
4489 nread = fruit_pread_rsrc(handle, fsp, data, n, offset);
4492 DBG_DEBUG("Path [%s] nread [%zd]\n", fsp_str_dbg(fsp), nread);
4496 static bool fruit_must_handle_aio_stream(struct fio *fio)
4502 if (fio->type == ADOUBLE_META) {
4506 if ((fio->type == ADOUBLE_RSRC) &&
4507 (fio->config->rsrc == FRUIT_RSRC_ADFILE))
4515 struct fruit_pread_state {
4517 struct vfs_aio_state vfs_aio_state;
4520 static void fruit_pread_done(struct tevent_req *subreq);
4522 static struct tevent_req *fruit_pread_send(
4523 struct vfs_handle_struct *handle,
4524 TALLOC_CTX *mem_ctx,
4525 struct tevent_context *ev,
4526 struct files_struct *fsp,
4528 size_t n, off_t offset)
4530 struct tevent_req *req = NULL;
4531 struct tevent_req *subreq = NULL;
4532 struct fruit_pread_state *state = NULL;
4533 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4535 req = tevent_req_create(mem_ctx, &state,
4536 struct fruit_pread_state);
4541 if (fruit_must_handle_aio_stream(fio)) {
4542 state->nread = SMB_VFS_PREAD(fsp, data, n, offset);
4543 if (state->nread != n) {
4544 if (state->nread != -1) {
4547 tevent_req_error(req, errno);
4548 return tevent_req_post(req, ev);
4550 tevent_req_done(req);
4551 return tevent_req_post(req, ev);
4554 subreq = SMB_VFS_NEXT_PREAD_SEND(state, ev, handle, fsp,
4556 if (tevent_req_nomem(req, subreq)) {
4557 return tevent_req_post(req, ev);
4559 tevent_req_set_callback(subreq, fruit_pread_done, req);
4563 static void fruit_pread_done(struct tevent_req *subreq)
4565 struct tevent_req *req = tevent_req_callback_data(
4566 subreq, struct tevent_req);
4567 struct fruit_pread_state *state = tevent_req_data(
4568 req, struct fruit_pread_state);
4570 state->nread = SMB_VFS_PREAD_RECV(subreq, &state->vfs_aio_state);
4571 TALLOC_FREE(subreq);
4573 if (tevent_req_error(req, state->vfs_aio_state.error)) {
4576 tevent_req_done(req);
4579 static ssize_t fruit_pread_recv(struct tevent_req *req,
4580 struct vfs_aio_state *vfs_aio_state)
4582 struct fruit_pread_state *state = tevent_req_data(
4583 req, struct fruit_pread_state);
4585 if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
4589 *vfs_aio_state = state->vfs_aio_state;
4590 return state->nread;
4593 static ssize_t fruit_pwrite_meta_stream(vfs_handle_struct *handle,
4594 files_struct *fsp, const void *data,
4595 size_t n, off_t offset)
4597 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4603 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4604 fsp_str_dbg(fsp), (intmax_t)offset, n);
4613 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
4615 DBG_ERR("Close [%s] failed: %s\n",
4616 fsp_str_dbg(fsp), strerror(errno));
4621 fd = SMB_VFS_NEXT_OPEN(handle,
4627 DBG_ERR("On-demand create [%s] in write failed: %s\n",
4628 fsp_str_dbg(fsp), strerror(errno));
4632 fio->fake_fd = false;
4635 ai = afpinfo_unpack(talloc_tos(), data);
4640 if (ai_empty_finderinfo(ai)) {
4642 * Writing an all 0 blob to the metadata stream results in the
4643 * stream being removed on a macOS server. This ensures we
4644 * behave the same and it verified by the "delete AFP_AfpInfo by
4645 * writing all 0" test.
4647 ret = SMB_VFS_NEXT_FTRUNCATE(handle, fsp, 0);
4649 DBG_ERR("SMB_VFS_NEXT_FTRUNCATE on [%s] failed\n",
4654 ok = set_delete_on_close(
4657 handle->conn->session_info->security_token,
4658 handle->conn->session_info->unix_token);
4660 DBG_ERR("set_delete_on_close on [%s] failed\n",
4667 nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4668 if (nwritten != n) {
4675 static ssize_t fruit_pwrite_meta_netatalk(vfs_handle_struct *handle,
4676 files_struct *fsp, const void *data,
4677 size_t n, off_t offset)
4679 struct adouble *ad = NULL;
4685 ai = afpinfo_unpack(talloc_tos(), data);
4690 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
4692 ad = ad_init(talloc_tos(), handle, ADOUBLE_META);
4697 p = ad_get_entry(ad, ADEID_FINDERI);
4699 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
4704 memcpy(p, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
4706 ret = ad_fset(handle, ad, fsp);
4708 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
4715 if (!ai_empty_finderinfo(ai)) {
4720 * Writing an all 0 blob to the metadata stream results in the stream
4721 * being removed on a macOS server. This ensures we behave the same and
4722 * it verified by the "delete AFP_AfpInfo by writing all 0" test.
4725 ok = set_delete_on_close(
4728 handle->conn->session_info->security_token,
4729 handle->conn->session_info->unix_token);
4731 DBG_ERR("set_delete_on_close on [%s] failed\n",
4739 static ssize_t fruit_pwrite_meta(vfs_handle_struct *handle,
4740 files_struct *fsp, const void *data,
4741 size_t n, off_t offset)
4743 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4745 uint8_t buf[AFP_INFO_SIZE];
4751 DBG_ERR("Failed to fetch fsp extension");
4760 if (offset != 0 && n < 60) {
4765 cmp = memcmp(data, "AFP", 3);
4771 if (n <= AFP_OFF_FinderInfo) {
4773 * Nothing to do here really, just return
4781 if (to_copy > AFP_INFO_SIZE) {
4782 to_copy = AFP_INFO_SIZE;
4784 memcpy(buf, data, to_copy);
4787 if (to_write != AFP_INFO_SIZE) {
4788 to_write = AFP_INFO_SIZE;
4791 switch (fio->config->meta) {
4792 case FRUIT_META_STREAM:
4793 nwritten = fruit_pwrite_meta_stream(handle,
4800 case FRUIT_META_NETATALK:
4801 nwritten = fruit_pwrite_meta_netatalk(handle,
4809 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
4813 if (nwritten != to_write) {
4818 * Return the requested amount, verified against macOS SMB server
4823 static ssize_t fruit_pwrite_rsrc_stream(vfs_handle_struct *handle,
4824 files_struct *fsp, const void *data,
4825 size_t n, off_t offset)
4827 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4830 static ssize_t fruit_pwrite_rsrc_xattr(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_adouble(vfs_handle_struct *handle,
4838 files_struct *fsp, const void *data,
4839 size_t n, off_t offset)
4841 struct adouble *ad = NULL;
4845 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
4847 DBG_ERR("ad_get [%s] failed\n", fsp_str_dbg(fsp));
4851 nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n,
4852 offset + ad_getentryoff(ad, ADEID_RFORK));
4853 if (nwritten != n) {
4854 DBG_ERR("Short write on [%s] [%zd/%zd]\n",
4855 fsp_str_dbg(fsp), nwritten, n);
4860 if ((n + offset) > ad_getentrylen(ad, ADEID_RFORK)) {
4861 ad_setentrylen(ad, ADEID_RFORK, n + offset);
4862 ret = ad_fset(handle, ad, fsp);
4864 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
4874 static ssize_t fruit_pwrite_rsrc(vfs_handle_struct *handle,
4875 files_struct *fsp, const void *data,
4876 size_t n, off_t offset)
4878 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4882 DBG_ERR("Failed to fetch fsp extension");
4886 switch (fio->config->rsrc) {
4887 case FRUIT_RSRC_STREAM:
4888 nwritten = fruit_pwrite_rsrc_stream(handle, fsp, data, n, offset);
4891 case FRUIT_RSRC_ADFILE:
4892 nwritten = fruit_pwrite_rsrc_adouble(handle, fsp, data, n, offset);
4895 case FRUIT_RSRC_XATTR:
4896 nwritten = fruit_pwrite_rsrc_xattr(handle, fsp, data, n, offset);
4900 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
4907 static ssize_t fruit_pwrite(vfs_handle_struct *handle,
4908 files_struct *fsp, const void *data,
4909 size_t n, off_t offset)
4911 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4914 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4915 fsp_str_dbg(fsp), (intmax_t)offset, n);
4918 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4921 if (fio->type == ADOUBLE_META) {
4922 nwritten = fruit_pwrite_meta(handle, fsp, data, n, offset);
4924 nwritten = fruit_pwrite_rsrc(handle, fsp, data, n, offset);
4927 DBG_DEBUG("Path [%s] nwritten=%zd\n", fsp_str_dbg(fsp), nwritten);
4931 struct fruit_pwrite_state {
4933 struct vfs_aio_state vfs_aio_state;
4936 static void fruit_pwrite_done(struct tevent_req *subreq);
4938 static struct tevent_req *fruit_pwrite_send(
4939 struct vfs_handle_struct *handle,
4940 TALLOC_CTX *mem_ctx,
4941 struct tevent_context *ev,
4942 struct files_struct *fsp,
4944 size_t n, off_t offset)
4946 struct tevent_req *req = NULL;
4947 struct tevent_req *subreq = NULL;
4948 struct fruit_pwrite_state *state = NULL;
4949 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4951 req = tevent_req_create(mem_ctx, &state,
4952 struct fruit_pwrite_state);
4957 if (fruit_must_handle_aio_stream(fio)) {
4958 state->nwritten = SMB_VFS_PWRITE(fsp, data, n, offset);
4959 if (state->nwritten != n) {
4960 if (state->nwritten != -1) {
4963 tevent_req_error(req, errno);
4964 return tevent_req_post(req, ev);
4966 tevent_req_done(req);
4967 return tevent_req_post(req, ev);
4970 subreq = SMB_VFS_NEXT_PWRITE_SEND(state, ev, handle, fsp,
4972 if (tevent_req_nomem(req, subreq)) {
4973 return tevent_req_post(req, ev);
4975 tevent_req_set_callback(subreq, fruit_pwrite_done, req);
4979 static void fruit_pwrite_done(struct tevent_req *subreq)
4981 struct tevent_req *req = tevent_req_callback_data(
4982 subreq, struct tevent_req);
4983 struct fruit_pwrite_state *state = tevent_req_data(
4984 req, struct fruit_pwrite_state);
4986 state->nwritten = SMB_VFS_PWRITE_RECV(subreq, &state->vfs_aio_state);
4987 TALLOC_FREE(subreq);
4989 if (tevent_req_error(req, state->vfs_aio_state.error)) {
4992 tevent_req_done(req);
4995 static ssize_t fruit_pwrite_recv(struct tevent_req *req,
4996 struct vfs_aio_state *vfs_aio_state)
4998 struct fruit_pwrite_state *state = tevent_req_data(
4999 req, struct fruit_pwrite_state);
5001 if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
5005 *vfs_aio_state = state->vfs_aio_state;
5006 return state->nwritten;
5010 * Helper to stat/lstat the base file of an smb_fname.
5012 static int fruit_stat_base(vfs_handle_struct *handle,
5013 struct smb_filename *smb_fname,
5016 char *tmp_stream_name;
5019 tmp_stream_name = smb_fname->stream_name;
5020 smb_fname->stream_name = NULL;
5022 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
5024 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5026 smb_fname->stream_name = tmp_stream_name;
5028 DBG_DEBUG("fruit_stat_base [%s] dev [%ju] ino [%ju]\n",
5029 smb_fname->base_name,
5030 (uintmax_t)smb_fname->st.st_ex_dev,
5031 (uintmax_t)smb_fname->st.st_ex_ino);
5035 static int fruit_stat_meta_stream(vfs_handle_struct *handle,
5036 struct smb_filename *smb_fname,
5042 ret = fruit_stat_base(handle, smb_fname, false);
5047 ino = fruit_inode(&smb_fname->st, smb_fname->stream_name);
5050 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
5052 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5055 smb_fname->st.st_ex_ino = ino;
5060 static int fruit_stat_meta_netatalk(vfs_handle_struct *handle,
5061 struct smb_filename *smb_fname,
5064 struct adouble *ad = NULL;
5066 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5068 DBG_INFO("fruit_stat_meta %s: %s\n",
5069 smb_fname_str_dbg(smb_fname), strerror(errno));
5075 /* Populate the stat struct with info from the base file. */
5076 if (fruit_stat_base(handle, smb_fname, follow_links) == -1) {
5079 smb_fname->st.st_ex_size = AFP_INFO_SIZE;
5080 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5081 smb_fname->stream_name);
5085 static int fruit_stat_meta(vfs_handle_struct *handle,
5086 struct smb_filename *smb_fname,
5089 struct fruit_config_data *config = NULL;
5092 SMB_VFS_HANDLE_GET_DATA(handle, config,
5093 struct fruit_config_data, return -1);
5095 switch (config->meta) {
5096 case FRUIT_META_STREAM:
5097 ret = fruit_stat_meta_stream(handle, smb_fname, follow_links);
5100 case FRUIT_META_NETATALK:
5101 ret = fruit_stat_meta_netatalk(handle, smb_fname, follow_links);
5105 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
5112 static int fruit_stat_rsrc_netatalk(vfs_handle_struct *handle,
5113 struct smb_filename *smb_fname,
5116 struct adouble *ad = NULL;
5119 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
5125 /* Populate the stat struct with info from the base file. */
5126 ret = fruit_stat_base(handle, smb_fname, follow_links);
5132 smb_fname->st.st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
5133 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5134 smb_fname->stream_name);
5139 static int fruit_stat_rsrc_stream(vfs_handle_struct *handle,
5140 struct smb_filename *smb_fname,
5146 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
5148 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5154 static int fruit_stat_rsrc_xattr(vfs_handle_struct *handle,
5155 struct smb_filename *smb_fname,
5158 #ifdef HAVE_ATTROPEN
5162 /* Populate the stat struct with info from the base file. */
5163 ret = fruit_stat_base(handle, smb_fname, follow_links);
5168 fd = attropen(smb_fname->base_name,
5169 AFPRESOURCE_EA_NETATALK,
5175 ret = sys_fstat(fd, &smb_fname->st, false);
5178 DBG_ERR("fstat [%s:%s] failed\n", smb_fname->base_name,
5179 AFPRESOURCE_EA_NETATALK);
5185 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5186 smb_fname->stream_name);
5196 static int fruit_stat_rsrc(vfs_handle_struct *handle,
5197 struct smb_filename *smb_fname,
5200 struct fruit_config_data *config = NULL;
5203 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
5205 SMB_VFS_HANDLE_GET_DATA(handle, config,
5206 struct fruit_config_data, return -1);
5208 switch (config->rsrc) {
5209 case FRUIT_RSRC_STREAM:
5210 ret = fruit_stat_rsrc_stream(handle, smb_fname, follow_links);
5213 case FRUIT_RSRC_XATTR:
5214 ret = fruit_stat_rsrc_xattr(handle, smb_fname, follow_links);
5217 case FRUIT_RSRC_ADFILE:
5218 ret = fruit_stat_rsrc_netatalk(handle, smb_fname, follow_links);
5222 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
5229 static int fruit_stat(vfs_handle_struct *handle,
5230 struct smb_filename *smb_fname)
5234 DEBUG(10, ("fruit_stat called for %s\n",
5235 smb_fname_str_dbg(smb_fname)));
5237 if (!is_ntfs_stream_smb_fname(smb_fname)
5238 || is_ntfs_default_stream_smb_fname(smb_fname)) {
5239 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
5241 update_btime(handle, smb_fname);
5247 * Note if lp_posix_paths() is true, we can never
5248 * get here as is_ntfs_stream_smb_fname() is
5249 * always false. So we never need worry about
5250 * not following links here.
5253 if (is_afpinfo_stream(smb_fname)) {
5254 rc = fruit_stat_meta(handle, smb_fname, true);
5255 } else if (is_afpresource_stream(smb_fname)) {
5256 rc = fruit_stat_rsrc(handle, smb_fname, true);
5258 return SMB_VFS_NEXT_STAT(handle, smb_fname);
5262 update_btime(handle, smb_fname);
5263 smb_fname->st.st_ex_mode &= ~S_IFMT;
5264 smb_fname->st.st_ex_mode |= S_IFREG;
5265 smb_fname->st.st_ex_blocks =
5266 smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
5271 static int fruit_lstat(vfs_handle_struct *handle,
5272 struct smb_filename *smb_fname)
5276 DEBUG(10, ("fruit_lstat called for %s\n",
5277 smb_fname_str_dbg(smb_fname)));
5279 if (!is_ntfs_stream_smb_fname(smb_fname)
5280 || is_ntfs_default_stream_smb_fname(smb_fname)) {
5281 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5283 update_btime(handle, smb_fname);
5288 if (is_afpinfo_stream(smb_fname)) {
5289 rc = fruit_stat_meta(handle, smb_fname, false);
5290 } else if (is_afpresource_stream(smb_fname)) {
5291 rc = fruit_stat_rsrc(handle, smb_fname, false);
5293 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5297 update_btime(handle, smb_fname);
5298 smb_fname->st.st_ex_mode &= ~S_IFMT;
5299 smb_fname->st.st_ex_mode |= S_IFREG;
5300 smb_fname->st.st_ex_blocks =
5301 smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
5306 static int fruit_fstat_meta_stream(vfs_handle_struct *handle,
5308 SMB_STRUCT_STAT *sbuf)
5310 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5311 struct smb_filename smb_fname;
5320 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5325 *sbuf = fsp->base_fsp->fsp_name->st;
5326 sbuf->st_ex_size = AFP_INFO_SIZE;
5327 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5331 smb_fname = (struct smb_filename) {
5332 .base_name = fsp->fsp_name->base_name,
5335 ret = fruit_stat_base(handle, &smb_fname, false);
5339 *sbuf = smb_fname.st;
5341 ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5343 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5348 sbuf->st_ex_ino = ino;
5352 static int fruit_fstat_meta_netatalk(vfs_handle_struct *handle,
5354 SMB_STRUCT_STAT *sbuf)
5358 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5363 *sbuf = fsp->base_fsp->fsp_name->st;
5364 sbuf->st_ex_size = AFP_INFO_SIZE;
5365 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5370 static int fruit_fstat_meta(vfs_handle_struct *handle,
5372 SMB_STRUCT_STAT *sbuf,
5377 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
5379 switch (fio->config->meta) {
5380 case FRUIT_META_STREAM:
5381 ret = fruit_fstat_meta_stream(handle, fsp, sbuf);
5384 case FRUIT_META_NETATALK:
5385 ret = fruit_fstat_meta_netatalk(handle, fsp, sbuf);
5389 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
5393 DBG_DEBUG("Path [%s] ret [%d]\n", fsp_str_dbg(fsp), ret);
5397 static int fruit_fstat_rsrc_xattr(vfs_handle_struct *handle,
5399 SMB_STRUCT_STAT *sbuf)
5401 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5404 static int fruit_fstat_rsrc_stream(vfs_handle_struct *handle,
5406 SMB_STRUCT_STAT *sbuf)
5408 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5411 static int fruit_fstat_rsrc_adouble(vfs_handle_struct *handle,
5413 SMB_STRUCT_STAT *sbuf)
5415 struct adouble *ad = NULL;
5418 /* Populate the stat struct with info from the base file. */
5419 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5424 ad = ad_get(talloc_tos(), handle,
5425 fsp->base_fsp->fsp_name,
5428 DBG_ERR("ad_get [%s] failed [%s]\n",
5429 fsp_str_dbg(fsp), strerror(errno));
5433 *sbuf = fsp->base_fsp->fsp_name->st;
5434 sbuf->st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
5435 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5441 static int fruit_fstat_rsrc(vfs_handle_struct *handle, files_struct *fsp,
5442 SMB_STRUCT_STAT *sbuf, struct fio *fio)
5446 switch (fio->config->rsrc) {
5447 case FRUIT_RSRC_STREAM:
5448 ret = fruit_fstat_rsrc_stream(handle, fsp, sbuf);
5451 case FRUIT_RSRC_ADFILE:
5452 ret = fruit_fstat_rsrc_adouble(handle, fsp, sbuf);
5455 case FRUIT_RSRC_XATTR:
5456 ret = fruit_fstat_rsrc_xattr(handle, fsp, sbuf);
5460 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
5467 static int fruit_fstat(vfs_handle_struct *handle, files_struct *fsp,
5468 SMB_STRUCT_STAT *sbuf)
5470 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5474 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5477 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
5479 if (fio->type == ADOUBLE_META) {
5480 rc = fruit_fstat_meta(handle, fsp, sbuf, fio);
5482 rc = fruit_fstat_rsrc(handle, fsp, sbuf, fio);
5486 sbuf->st_ex_mode &= ~S_IFMT;
5487 sbuf->st_ex_mode |= S_IFREG;
5488 sbuf->st_ex_blocks = sbuf->st_ex_size / STAT_ST_BLOCKSIZE + 1;
5491 DBG_DEBUG("Path [%s] rc [%d] size [%"PRIdMAX"]\n",
5492 fsp_str_dbg(fsp), rc, (intmax_t)sbuf->st_ex_size);
5496 static NTSTATUS delete_invalid_meta_stream(
5497 vfs_handle_struct *handle,
5498 const struct smb_filename *smb_fname,
5499 TALLOC_CTX *mem_ctx,
5500 unsigned int *pnum_streams,
5501 struct stream_struct **pstreams,
5504 struct smb_filename *sname = NULL;
5508 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams, AFPINFO_STREAM);
5510 return NT_STATUS_INTERNAL_ERROR;
5514 return NT_STATUS_OK;
5517 sname = synthetic_smb_fname(talloc_tos(),
5518 smb_fname->base_name,
5519 AFPINFO_STREAM_NAME,
5521 if (sname == NULL) {
5522 return NT_STATUS_NO_MEMORY;
5525 ret = SMB_VFS_NEXT_UNLINK(handle, sname);
5528 DBG_ERR("Removing [%s] failed\n", smb_fname_str_dbg(sname));
5529 return map_nt_error_from_unix(errno);
5532 return NT_STATUS_OK;
5535 static NTSTATUS fruit_streaminfo_meta_stream(
5536 vfs_handle_struct *handle,
5537 struct files_struct *fsp,
5538 const struct smb_filename *smb_fname,
5539 TALLOC_CTX *mem_ctx,
5540 unsigned int *pnum_streams,
5541 struct stream_struct **pstreams)
5543 struct stream_struct *stream = *pstreams;
5544 unsigned int num_streams = *pnum_streams;
5547 for (i = 0; i < num_streams; i++) {
5548 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
5553 if (i == num_streams) {
5554 return NT_STATUS_OK;
5557 if (stream[i].size != AFP_INFO_SIZE) {
5558 DBG_ERR("Removing invalid AFPINFO_STREAM size [%jd] from [%s]\n",
5559 (intmax_t)stream[i].size, smb_fname_str_dbg(smb_fname));
5561 return delete_invalid_meta_stream(handle,
5570 return NT_STATUS_OK;
5573 static NTSTATUS fruit_streaminfo_meta_netatalk(
5574 vfs_handle_struct *handle,
5575 struct files_struct *fsp,
5576 const struct smb_filename *smb_fname,
5577 TALLOC_CTX *mem_ctx,
5578 unsigned int *pnum_streams,
5579 struct stream_struct **pstreams)
5581 struct stream_struct *stream = *pstreams;
5582 unsigned int num_streams = *pnum_streams;
5583 struct adouble *ad = NULL;
5588 /* Remove the Netatalk xattr from the list */
5589 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5590 ":" NETATALK_META_XATTR ":$DATA");
5592 return NT_STATUS_NO_MEMORY;
5596 * Check if there's a AFPINFO_STREAM from the VFS streams
5597 * backend and if yes, remove it from the list
5599 for (i = 0; i < num_streams; i++) {
5600 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
5605 if (i < num_streams) {
5606 DBG_WARNING("Unexpected AFPINFO_STREAM on [%s]\n",
5607 smb_fname_str_dbg(smb_fname));
5609 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5612 return NT_STATUS_INTERNAL_ERROR;
5616 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5618 return NT_STATUS_OK;
5621 is_fi_empty = ad_empty_finderinfo(ad);
5625 return NT_STATUS_OK;
5628 ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
5629 AFPINFO_STREAM_NAME, AFP_INFO_SIZE,
5630 smb_roundup(handle->conn, AFP_INFO_SIZE));
5632 return NT_STATUS_NO_MEMORY;
5635 return NT_STATUS_OK;
5638 static NTSTATUS fruit_streaminfo_meta(vfs_handle_struct *handle,
5639 struct files_struct *fsp,
5640 const struct smb_filename *smb_fname,
5641 TALLOC_CTX *mem_ctx,
5642 unsigned int *pnum_streams,
5643 struct stream_struct **pstreams)
5645 struct fruit_config_data *config = NULL;
5648 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5649 return NT_STATUS_INTERNAL_ERROR);
5651 switch (config->meta) {
5652 case FRUIT_META_NETATALK:
5653 status = fruit_streaminfo_meta_netatalk(handle, fsp, smb_fname,
5654 mem_ctx, pnum_streams,
5658 case FRUIT_META_STREAM:
5659 status = fruit_streaminfo_meta_stream(handle, fsp, smb_fname,
5660 mem_ctx, pnum_streams,
5665 return NT_STATUS_INTERNAL_ERROR;
5671 static NTSTATUS fruit_streaminfo_rsrc_stream(
5672 vfs_handle_struct *handle,
5673 struct files_struct *fsp,
5674 const struct smb_filename *smb_fname,
5675 TALLOC_CTX *mem_ctx,
5676 unsigned int *pnum_streams,
5677 struct stream_struct **pstreams)
5681 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
5683 DBG_ERR("Filtering resource stream failed\n");
5684 return NT_STATUS_INTERNAL_ERROR;
5686 return NT_STATUS_OK;
5689 static NTSTATUS fruit_streaminfo_rsrc_xattr(
5690 vfs_handle_struct *handle,
5691 struct files_struct *fsp,
5692 const struct smb_filename *smb_fname,
5693 TALLOC_CTX *mem_ctx,
5694 unsigned int *pnum_streams,
5695 struct stream_struct **pstreams)
5699 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
5701 DBG_ERR("Filtering resource stream failed\n");
5702 return NT_STATUS_INTERNAL_ERROR;
5704 return NT_STATUS_OK;
5707 static NTSTATUS fruit_streaminfo_rsrc_adouble(
5708 vfs_handle_struct *handle,
5709 struct files_struct *fsp,
5710 const struct smb_filename *smb_fname,
5711 TALLOC_CTX *mem_ctx,
5712 unsigned int *pnum_streams,
5713 struct stream_struct **pstreams)
5715 struct stream_struct *stream = *pstreams;
5716 unsigned int num_streams = *pnum_streams;
5717 struct adouble *ad = NULL;
5723 * Check if there's a AFPRESOURCE_STREAM from the VFS streams backend
5724 * and if yes, remove it from the list
5726 for (i = 0; i < num_streams; i++) {
5727 if (strequal_m(stream[i].name, AFPRESOURCE_STREAM)) {
5732 if (i < num_streams) {
5733 DBG_WARNING("Unexpected AFPRESOURCE_STREAM on [%s]\n",
5734 smb_fname_str_dbg(smb_fname));
5736 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5737 AFPRESOURCE_STREAM);
5739 return NT_STATUS_INTERNAL_ERROR;
5743 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
5745 return NT_STATUS_OK;
5748 rlen = ad_getentrylen(ad, ADEID_RFORK);
5752 return NT_STATUS_OK;
5755 ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
5756 AFPRESOURCE_STREAM_NAME, rlen,
5757 smb_roundup(handle->conn, rlen));
5759 return NT_STATUS_NO_MEMORY;
5762 return NT_STATUS_OK;
5765 static NTSTATUS fruit_streaminfo_rsrc(vfs_handle_struct *handle,
5766 struct files_struct *fsp,
5767 const struct smb_filename *smb_fname,
5768 TALLOC_CTX *mem_ctx,
5769 unsigned int *pnum_streams,
5770 struct stream_struct **pstreams)
5772 struct fruit_config_data *config = NULL;
5775 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5776 return NT_STATUS_INTERNAL_ERROR);
5778 switch (config->rsrc) {
5779 case FRUIT_RSRC_STREAM:
5780 status = fruit_streaminfo_rsrc_stream(handle, fsp, smb_fname,
5781 mem_ctx, pnum_streams,
5785 case FRUIT_RSRC_XATTR:
5786 status = fruit_streaminfo_rsrc_xattr(handle, fsp, smb_fname,
5787 mem_ctx, pnum_streams,
5791 case FRUIT_RSRC_ADFILE:
5792 status = fruit_streaminfo_rsrc_adouble(handle, fsp, smb_fname,
5793 mem_ctx, pnum_streams,
5798 return NT_STATUS_INTERNAL_ERROR;
5804 static void fruit_filter_empty_streams(unsigned int *pnum_streams,
5805 struct stream_struct **pstreams)
5807 unsigned num_streams = *pnum_streams;
5808 struct stream_struct *streams = *pstreams;
5811 if (!global_fruit_config.nego_aapl) {
5815 while (i < num_streams) {
5816 struct smb_filename smb_fname = (struct smb_filename) {
5817 .stream_name = streams[i].name,
5820 if (is_ntfs_default_stream_smb_fname(&smb_fname)
5821 || streams[i].size > 0)
5827 streams[i] = streams[num_streams - 1];
5831 *pnum_streams = num_streams;
5834 static NTSTATUS fruit_streaminfo(vfs_handle_struct *handle,
5835 struct files_struct *fsp,
5836 const struct smb_filename *smb_fname,
5837 TALLOC_CTX *mem_ctx,
5838 unsigned int *pnum_streams,
5839 struct stream_struct **pstreams)
5841 struct fruit_config_data *config = NULL;
5844 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5845 return NT_STATUS_UNSUCCESSFUL);
5847 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
5849 status = SMB_VFS_NEXT_STREAMINFO(handle, fsp, smb_fname, mem_ctx,
5850 pnum_streams, pstreams);
5851 if (!NT_STATUS_IS_OK(status)) {
5855 fruit_filter_empty_streams(pnum_streams, pstreams);
5857 status = fruit_streaminfo_meta(handle, fsp, smb_fname,
5858 mem_ctx, pnum_streams, pstreams);
5859 if (!NT_STATUS_IS_OK(status)) {
5863 status = fruit_streaminfo_rsrc(handle, fsp, smb_fname,
5864 mem_ctx, pnum_streams, pstreams);
5865 if (!NT_STATUS_IS_OK(status)) {
5869 return NT_STATUS_OK;
5872 static int fruit_ntimes(vfs_handle_struct *handle,
5873 const struct smb_filename *smb_fname,
5874 struct smb_file_time *ft)
5877 struct adouble *ad = NULL;
5878 struct fruit_config_data *config = NULL;
5880 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5883 if ((config->meta != FRUIT_META_NETATALK) ||
5884 null_timespec(ft->create_time))
5886 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
5889 DEBUG(10,("set btime for %s to %s\n", smb_fname_str_dbg(smb_fname),
5890 time_to_asc(convert_timespec_to_time_t(ft->create_time))));
5892 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5897 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX,
5898 convert_time_t_to_uint32_t(ft->create_time.tv_sec));
5900 rc = ad_set(ad, smb_fname);
5906 DEBUG(1, ("fruit_ntimes: %s\n", smb_fname_str_dbg(smb_fname)));
5909 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
5912 static int fruit_fallocate(struct vfs_handle_struct *handle,
5913 struct files_struct *fsp,
5918 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5921 return SMB_VFS_NEXT_FALLOCATE(handle, fsp, mode, offset, len);
5924 /* Let the pwrite code path handle it. */
5929 static int fruit_ftruncate_rsrc_xattr(struct vfs_handle_struct *handle,
5930 struct files_struct *fsp,
5933 #ifdef HAVE_ATTROPEN
5934 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
5939 static int fruit_ftruncate_rsrc_adouble(struct vfs_handle_struct *handle,
5940 struct files_struct *fsp,
5944 struct adouble *ad = NULL;
5947 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
5949 DBG_DEBUG("ad_get [%s] failed [%s]\n",
5950 fsp_str_dbg(fsp), strerror(errno));
5954 ad_off = ad_getentryoff(ad, ADEID_RFORK);
5956 rc = ftruncate(fsp->fh->fd, offset + ad_off);
5962 ad_setentrylen(ad, ADEID_RFORK, offset);
5964 rc = ad_fset(handle, ad, fsp);
5966 DBG_ERR("ad_fset [%s] failed [%s]\n",
5967 fsp_str_dbg(fsp), strerror(errno));
5976 static int fruit_ftruncate_rsrc_stream(struct vfs_handle_struct *handle,
5977 struct files_struct *fsp,
5980 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
5983 static int fruit_ftruncate_rsrc(struct vfs_handle_struct *handle,
5984 struct files_struct *fsp,
5987 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5991 DBG_ERR("Failed to fetch fsp extension");
5995 switch (fio->config->rsrc) {
5996 case FRUIT_RSRC_XATTR:
5997 ret = fruit_ftruncate_rsrc_xattr(handle, fsp, offset);
6000 case FRUIT_RSRC_ADFILE:
6001 ret = fruit_ftruncate_rsrc_adouble(handle, fsp, offset);
6004 case FRUIT_RSRC_STREAM:
6005 ret = fruit_ftruncate_rsrc_stream(handle, fsp, offset);
6009 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
6017 static int fruit_ftruncate_meta(struct vfs_handle_struct *handle,
6018 struct files_struct *fsp,
6022 DBG_WARNING("ftruncate %s to %jd",
6023 fsp_str_dbg(fsp), (intmax_t)offset);
6024 /* OS X returns NT_STATUS_ALLOTTED_SPACE_EXCEEDED */
6029 /* OS X returns success but does nothing */
6030 DBG_INFO("ignoring ftruncate %s to %jd\n",
6031 fsp_str_dbg(fsp), (intmax_t)offset);
6035 static int fruit_ftruncate(struct vfs_handle_struct *handle,
6036 struct files_struct *fsp,
6039 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
6042 DBG_DEBUG("Path [%s] offset [%"PRIdMAX"]\n", fsp_str_dbg(fsp),
6046 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
6049 if (fio->type == ADOUBLE_META) {
6050 ret = fruit_ftruncate_meta(handle, fsp, offset);
6052 ret = fruit_ftruncate_rsrc(handle, fsp, offset);
6055 DBG_DEBUG("Path [%s] result [%d]\n", fsp_str_dbg(fsp), ret);
6059 static NTSTATUS fruit_create_file(vfs_handle_struct *handle,
6060 struct smb_request *req,
6061 uint16_t root_dir_fid,
6062 struct smb_filename *smb_fname,
6063 uint32_t access_mask,
6064 uint32_t share_access,
6065 uint32_t create_disposition,
6066 uint32_t create_options,
6067 uint32_t file_attributes,
6068 uint32_t oplock_request,
6069 struct smb2_lease *lease,
6070 uint64_t allocation_size,
6071 uint32_t private_flags,
6072 struct security_descriptor *sd,
6073 struct ea_list *ea_list,
6074 files_struct **result,
6076 const struct smb2_create_blobs *in_context_blobs,
6077 struct smb2_create_blobs *out_context_blobs)
6080 struct fruit_config_data *config = NULL;
6081 files_struct *fsp = NULL;
6082 struct fio *fio = NULL;
6083 bool internal_open = (oplock_request & INTERNAL_OPEN_ONLY);
6086 status = check_aapl(handle, req, in_context_blobs, out_context_blobs);
6087 if (!NT_STATUS_IS_OK(status)) {
6091 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
6092 return NT_STATUS_UNSUCCESSFUL);
6094 if (is_apple_stream(smb_fname) && !internal_open) {
6095 ret = ad_convert(handle, smb_fname);
6097 DBG_ERR("ad_convert() failed\n");
6098 return NT_STATUS_UNSUCCESSFUL;
6102 status = SMB_VFS_NEXT_CREATE_FILE(
6103 handle, req, root_dir_fid, smb_fname,
6104 access_mask, share_access,
6105 create_disposition, create_options,
6106 file_attributes, oplock_request,
6108 allocation_size, private_flags,
6109 sd, ea_list, result,
6110 pinfo, in_context_blobs, out_context_blobs);
6111 if (!NT_STATUS_IS_OK(status)) {
6117 if (global_fruit_config.nego_aapl) {
6118 if (config->posix_rename && fsp->is_directory) {
6120 * Enable POSIX directory rename behaviour
6122 fsp->posix_flags |= FSP_POSIX_FLAGS_RENAME;
6127 * If this is a plain open for existing files, opening an 0
6128 * byte size resource fork MUST fail with
6129 * NT_STATUS_OBJECT_NAME_NOT_FOUND.
6131 * Cf the vfs_fruit torture tests in test_rfork_create().
6133 if (global_fruit_config.nego_aapl &&
6134 create_disposition == FILE_OPEN &&
6135 smb_fname->st.st_ex_size == 0 &&
6136 is_ntfs_stream_smb_fname(smb_fname) &&
6137 !(is_ntfs_default_stream_smb_fname(smb_fname)))
6139 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
6143 fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
6144 if (fio != NULL && pinfo != NULL && *pinfo == FILE_WAS_CREATED) {
6145 fio->created = true;
6148 if (is_ntfs_stream_smb_fname(smb_fname)
6149 || fsp->is_directory) {
6153 if (config->locking == FRUIT_LOCKING_NETATALK) {
6154 status = fruit_check_access(
6158 if (!NT_STATUS_IS_OK(status)) {
6166 DEBUG(10, ("fruit_create_file: %s\n", nt_errstr(status)));
6169 close_file(req, fsp, ERROR_CLOSE);
6170 *result = fsp = NULL;
6176 static NTSTATUS fruit_readdir_attr(struct vfs_handle_struct *handle,
6177 const struct smb_filename *fname,
6178 TALLOC_CTX *mem_ctx,
6179 struct readdir_attr_data **pattr_data)
6181 struct fruit_config_data *config = NULL;
6182 struct readdir_attr_data *attr_data;
6186 SMB_VFS_HANDLE_GET_DATA(handle, config,
6187 struct fruit_config_data,
6188 return NT_STATUS_UNSUCCESSFUL);
6190 if (!global_fruit_config.nego_aapl) {
6191 return SMB_VFS_NEXT_READDIR_ATTR(handle, fname, mem_ctx, pattr_data);
6194 DEBUG(10, ("fruit_readdir_attr %s\n", fname->base_name));
6196 ret = ad_convert(handle, fname);
6198 DBG_ERR("ad_convert() failed\n");
6199 return NT_STATUS_UNSUCCESSFUL;
6202 *pattr_data = talloc_zero(mem_ctx, struct readdir_attr_data);
6203 if (*pattr_data == NULL) {
6204 return NT_STATUS_UNSUCCESSFUL;
6206 attr_data = *pattr_data;
6207 attr_data->type = RDATTR_AAPL;
6210 * Mac metadata: compressed FinderInfo, resource fork length
6213 status = readdir_attr_macmeta(handle, fname, attr_data);
6214 if (!NT_STATUS_IS_OK(status)) {
6216 * Error handling is tricky: if we return failure from
6217 * this function, the corresponding directory entry
6218 * will to be passed to the client, so we really just
6219 * want to error out on fatal errors.
6221 if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
6229 if (config->unix_info_enabled) {
6230 attr_data->attr_data.aapl.unix_mode = fname->st.st_ex_mode;
6236 if (!config->readdir_attr_max_access) {
6237 attr_data->attr_data.aapl.max_access = FILE_GENERIC_ALL;
6239 status = smbd_calculate_access_mask(
6243 SEC_FLAG_MAXIMUM_ALLOWED,
6244 &attr_data->attr_data.aapl.max_access);
6245 if (!NT_STATUS_IS_OK(status)) {
6250 return NT_STATUS_OK;
6253 DEBUG(1, ("fruit_readdir_attr %s, error: %s\n",
6254 fname->base_name, nt_errstr(status)));
6255 TALLOC_FREE(*pattr_data);
6259 static NTSTATUS fruit_fget_nt_acl(vfs_handle_struct *handle,
6261 uint32_t security_info,
6262 TALLOC_CTX *mem_ctx,
6263 struct security_descriptor **ppdesc)
6266 struct security_ace ace;
6268 struct fruit_config_data *config;
6270 SMB_VFS_HANDLE_GET_DATA(handle, config,
6271 struct fruit_config_data,
6272 return NT_STATUS_UNSUCCESSFUL);
6274 status = SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
6276 if (!NT_STATUS_IS_OK(status)) {
6281 * Add MS NFS style ACEs with uid, gid and mode
6283 if (!global_fruit_config.nego_aapl) {
6284 return NT_STATUS_OK;
6286 if (!config->unix_info_enabled) {
6287 return NT_STATUS_OK;
6290 /* First remove any existing ACE's with NFS style mode/uid/gid SIDs. */
6291 status = remove_virtual_nfs_aces(*ppdesc);
6292 if (!NT_STATUS_IS_OK(status)) {
6293 DBG_WARNING("failed to remove MS NFS style ACEs\n");
6297 /* MS NFS style mode */
6298 sid_compose(&sid, &global_sid_Unix_NFS_Mode, fsp->fsp_name->st.st_ex_mode);
6299 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6300 status = security_descriptor_dacl_add(*ppdesc, &ace);
6301 if (!NT_STATUS_IS_OK(status)) {
6302 DEBUG(1,("failed to add MS NFS style ACE\n"));
6306 /* MS NFS style uid */
6307 sid_compose(&sid, &global_sid_Unix_NFS_Users, fsp->fsp_name->st.st_ex_uid);
6308 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6309 status = security_descriptor_dacl_add(*ppdesc, &ace);
6310 if (!NT_STATUS_IS_OK(status)) {
6311 DEBUG(1,("failed to add MS NFS style ACE\n"));
6315 /* MS NFS style gid */
6316 sid_compose(&sid, &global_sid_Unix_NFS_Groups, fsp->fsp_name->st.st_ex_gid);
6317 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6318 status = security_descriptor_dacl_add(*ppdesc, &ace);
6319 if (!NT_STATUS_IS_OK(status)) {
6320 DEBUG(1,("failed to add MS NFS style ACE\n"));
6324 return NT_STATUS_OK;
6327 static NTSTATUS fruit_fset_nt_acl(vfs_handle_struct *handle,
6329 uint32_t security_info_sent,
6330 const struct security_descriptor *orig_psd)
6334 mode_t ms_nfs_mode = 0;
6336 struct security_descriptor *psd = NULL;
6337 uint32_t orig_num_aces = 0;
6339 if (orig_psd->dacl != NULL) {
6340 orig_num_aces = orig_psd->dacl->num_aces;
6343 psd = security_descriptor_copy(talloc_tos(), orig_psd);
6345 return NT_STATUS_NO_MEMORY;
6348 DBG_DEBUG("fruit_fset_nt_acl: %s\n", fsp_str_dbg(fsp));
6350 status = check_ms_nfs(handle, fsp, psd, &ms_nfs_mode, &do_chmod);
6351 if (!NT_STATUS_IS_OK(status)) {
6352 DEBUG(1, ("fruit_fset_nt_acl: check_ms_nfs failed%s\n", fsp_str_dbg(fsp)));
6358 * If only ms_nfs ACE entries were sent, ensure we set the DACL
6359 * sent/present flags correctly now we've removed them.
6362 if (orig_num_aces != 0) {
6364 * Are there any ACE's left ?
6366 if (psd->dacl->num_aces == 0) {
6367 /* No - clear the DACL sent/present flags. */
6368 security_info_sent &= ~SECINFO_DACL;
6369 psd->type &= ~SEC_DESC_DACL_PRESENT;
6373 status = SMB_VFS_NEXT_FSET_NT_ACL(handle, fsp, security_info_sent, psd);
6374 if (!NT_STATUS_IS_OK(status)) {
6375 DEBUG(1, ("fruit_fset_nt_acl: SMB_VFS_NEXT_FSET_NT_ACL failed%s\n", fsp_str_dbg(fsp)));
6381 if (fsp->fh->fd != -1) {
6382 result = SMB_VFS_FCHMOD(fsp, ms_nfs_mode);
6384 result = SMB_VFS_CHMOD(fsp->conn,
6390 DEBUG(1, ("chmod: %s, result: %d, %04o error %s\n", fsp_str_dbg(fsp),
6391 result, (unsigned)ms_nfs_mode,
6393 status = map_nt_error_from_unix(errno);
6400 return NT_STATUS_OK;
6403 static struct vfs_offload_ctx *fruit_offload_ctx;
6405 struct fruit_offload_read_state {
6406 struct vfs_handle_struct *handle;
6407 struct tevent_context *ev;
6413 static void fruit_offload_read_done(struct tevent_req *subreq);
6415 static struct tevent_req *fruit_offload_read_send(
6416 TALLOC_CTX *mem_ctx,
6417 struct tevent_context *ev,
6418 struct vfs_handle_struct *handle,
6425 struct tevent_req *req = NULL;
6426 struct tevent_req *subreq = NULL;
6427 struct fruit_offload_read_state *state = NULL;
6429 req = tevent_req_create(mem_ctx, &state,
6430 struct fruit_offload_read_state);
6434 *state = (struct fruit_offload_read_state) {
6441 subreq = SMB_VFS_NEXT_OFFLOAD_READ_SEND(mem_ctx, ev, handle, fsp,
6442 fsctl, ttl, offset, to_copy);
6443 if (tevent_req_nomem(subreq, req)) {
6444 return tevent_req_post(req, ev);
6446 tevent_req_set_callback(subreq, fruit_offload_read_done, req);
6450 static void fruit_offload_read_done(struct tevent_req *subreq)
6452 struct tevent_req *req = tevent_req_callback_data(
6453 subreq, struct tevent_req);
6454 struct fruit_offload_read_state *state = tevent_req_data(
6455 req, struct fruit_offload_read_state);
6458 status = SMB_VFS_NEXT_OFFLOAD_READ_RECV(subreq,
6462 TALLOC_FREE(subreq);
6463 if (tevent_req_nterror(req, status)) {
6467 if (state->fsctl != FSCTL_SRV_REQUEST_RESUME_KEY) {
6468 tevent_req_done(req);
6472 status = vfs_offload_token_ctx_init(state->fsp->conn->sconn->client,
6473 &fruit_offload_ctx);
6474 if (tevent_req_nterror(req, status)) {
6478 status = vfs_offload_token_db_store_fsp(fruit_offload_ctx,
6481 if (tevent_req_nterror(req, status)) {
6485 tevent_req_done(req);
6489 static NTSTATUS fruit_offload_read_recv(struct tevent_req *req,
6490 struct vfs_handle_struct *handle,
6491 TALLOC_CTX *mem_ctx,
6494 struct fruit_offload_read_state *state = tevent_req_data(
6495 req, struct fruit_offload_read_state);
6498 if (tevent_req_is_nterror(req, &status)) {
6499 tevent_req_received(req);
6503 token->length = state->token.length;
6504 token->data = talloc_move(mem_ctx, &state->token.data);
6506 tevent_req_received(req);
6507 return NT_STATUS_OK;
6510 struct fruit_offload_write_state {
6511 struct vfs_handle_struct *handle;
6513 struct files_struct *src_fsp;
6514 struct files_struct *dst_fsp;
6518 static void fruit_offload_write_done(struct tevent_req *subreq);
6519 static struct tevent_req *fruit_offload_write_send(struct vfs_handle_struct *handle,
6520 TALLOC_CTX *mem_ctx,
6521 struct tevent_context *ev,
6524 off_t transfer_offset,
6525 struct files_struct *dest_fsp,
6529 struct tevent_req *req, *subreq;
6530 struct fruit_offload_write_state *state;
6532 struct fruit_config_data *config;
6533 off_t src_off = transfer_offset;
6534 files_struct *src_fsp = NULL;
6535 off_t to_copy = num;
6536 bool copyfile_enabled = false;
6538 DEBUG(10,("soff: %ju, doff: %ju, len: %ju\n",
6539 (uintmax_t)src_off, (uintmax_t)dest_off, (uintmax_t)num));
6541 SMB_VFS_HANDLE_GET_DATA(handle, config,
6542 struct fruit_config_data,
6545 req = tevent_req_create(mem_ctx, &state,
6546 struct fruit_offload_write_state);
6550 state->handle = handle;
6551 state->dst_fsp = dest_fsp;
6554 case FSCTL_SRV_COPYCHUNK:
6555 case FSCTL_SRV_COPYCHUNK_WRITE:
6556 copyfile_enabled = config->copyfile_enabled;
6563 * Check if this a OS X copyfile style copychunk request with
6564 * a requested chunk count of 0 that was translated to a
6565 * offload_write_send VFS call overloading the parameters src_off
6566 * = dest_off = num = 0.
6568 if (copyfile_enabled && num == 0 && src_off == 0 && dest_off == 0) {
6569 status = vfs_offload_token_db_fetch_fsp(
6570 fruit_offload_ctx, token, &src_fsp);
6571 if (tevent_req_nterror(req, status)) {
6572 return tevent_req_post(req, ev);
6574 state->src_fsp = src_fsp;
6576 status = vfs_stat_fsp(src_fsp);
6577 if (tevent_req_nterror(req, status)) {
6578 return tevent_req_post(req, ev);
6581 to_copy = src_fsp->fsp_name->st.st_ex_size;
6582 state->is_copyfile = true;
6585 subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle,
6594 if (tevent_req_nomem(subreq, req)) {
6595 return tevent_req_post(req, ev);
6598 tevent_req_set_callback(subreq, fruit_offload_write_done, req);
6602 static void fruit_offload_write_done(struct tevent_req *subreq)
6604 struct tevent_req *req = tevent_req_callback_data(
6605 subreq, struct tevent_req);
6606 struct fruit_offload_write_state *state = tevent_req_data(
6607 req, struct fruit_offload_write_state);
6609 unsigned int num_streams = 0;
6610 struct stream_struct *streams = NULL;
6612 struct smb_filename *src_fname_tmp = NULL;
6613 struct smb_filename *dst_fname_tmp = NULL;
6615 status = SMB_VFS_NEXT_OFFLOAD_WRITE_RECV(state->handle,
6618 TALLOC_FREE(subreq);
6619 if (tevent_req_nterror(req, status)) {
6623 if (!state->is_copyfile) {
6624 tevent_req_done(req);
6629 * Now copy all remaining streams. We know the share supports
6630 * streams, because we're in vfs_fruit. We don't do this async
6631 * because streams are few and small.
6633 status = vfs_streaminfo(state->handle->conn, state->src_fsp,
6634 state->src_fsp->fsp_name,
6635 req, &num_streams, &streams);
6636 if (tevent_req_nterror(req, status)) {
6640 if (num_streams == 1) {
6641 /* There is always one stream, ::$DATA. */
6642 tevent_req_done(req);
6646 for (i = 0; i < num_streams; i++) {
6647 DEBUG(10, ("%s: stream: '%s'/%zu\n",
6648 __func__, streams[i].name, (size_t)streams[i].size));
6650 src_fname_tmp = synthetic_smb_fname(
6652 state->src_fsp->fsp_name->base_name,
6655 state->src_fsp->fsp_name->flags);
6656 if (tevent_req_nomem(src_fname_tmp, req)) {
6660 if (is_ntfs_default_stream_smb_fname(src_fname_tmp)) {
6661 TALLOC_FREE(src_fname_tmp);
6665 dst_fname_tmp = synthetic_smb_fname(
6667 state->dst_fsp->fsp_name->base_name,
6670 state->dst_fsp->fsp_name->flags);
6671 if (tevent_req_nomem(dst_fname_tmp, req)) {
6672 TALLOC_FREE(src_fname_tmp);
6676 status = copy_file(req,
6677 state->handle->conn,
6680 OPENX_FILE_CREATE_IF_NOT_EXIST,
6682 if (!NT_STATUS_IS_OK(status)) {
6683 DEBUG(1, ("%s: copy %s to %s failed: %s\n", __func__,
6684 smb_fname_str_dbg(src_fname_tmp),
6685 smb_fname_str_dbg(dst_fname_tmp),
6686 nt_errstr(status)));
6687 TALLOC_FREE(src_fname_tmp);
6688 TALLOC_FREE(dst_fname_tmp);
6689 tevent_req_nterror(req, status);
6693 TALLOC_FREE(src_fname_tmp);
6694 TALLOC_FREE(dst_fname_tmp);
6697 TALLOC_FREE(streams);
6698 TALLOC_FREE(src_fname_tmp);
6699 TALLOC_FREE(dst_fname_tmp);
6700 tevent_req_done(req);
6703 static NTSTATUS fruit_offload_write_recv(struct vfs_handle_struct *handle,
6704 struct tevent_req *req,
6707 struct fruit_offload_write_state *state = tevent_req_data(
6708 req, struct fruit_offload_write_state);
6711 if (tevent_req_is_nterror(req, &status)) {
6712 DEBUG(1, ("server side copy chunk failed: %s\n",
6713 nt_errstr(status)));
6715 tevent_req_received(req);
6719 *copied = state->copied;
6720 tevent_req_received(req);
6722 return NT_STATUS_OK;
6725 static char *fruit_get_bandsize_line(char **lines, int numlines)
6728 static bool re_initialized = false;
6732 if (!re_initialized) {
6733 ret = regcomp(&re, "^[[:blank:]]*<key>band-size</key>$", 0);
6737 re_initialized = true;
6740 for (i = 0; i < numlines; i++) {
6741 regmatch_t matches[1];
6743 ret = regexec(&re, lines[i], 1, matches, 0);
6746 * Check if the match was on the last line, sa we want
6747 * the subsequent line.
6749 if (i + 1 == numlines) {
6752 return lines[i + 1];
6754 if (ret != REG_NOMATCH) {
6762 static bool fruit_get_bandsize_from_line(char *line, size_t *_band_size)
6765 static bool re_initialized = false;
6766 regmatch_t matches[2];
6771 if (!re_initialized) {
6774 "<integer>\\([[:digit:]]*\\)</integer>$",
6779 re_initialized = true;
6782 ret = regexec(&re, line, 2, matches, 0);
6784 DBG_ERR("regex failed [%s]\n", line);
6788 line[matches[1].rm_eo] = '\0';
6790 ok = conv_str_u64(&line[matches[1].rm_so], &band_size);
6794 *_band_size = (size_t)band_size;
6799 * This reads and parses an Info.plist from a TM sparsebundle looking for the
6800 * "band-size" key and value.
6802 static bool fruit_get_bandsize(vfs_handle_struct *handle,
6806 #define INFO_PLIST_MAX_SIZE 64*1024
6808 struct smb_filename *smb_fname = NULL;
6809 files_struct *fsp = NULL;
6810 uint8_t *file_data = NULL;
6811 char **lines = NULL;
6812 char *band_size_line = NULL;
6813 size_t plist_file_size;
6820 plist = talloc_asprintf(talloc_tos(),
6822 handle->conn->connectpath,
6824 if (plist == NULL) {
6829 smb_fname = synthetic_smb_fname(talloc_tos(), plist, NULL, NULL, 0);
6830 if (smb_fname == NULL) {
6835 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
6837 DBG_INFO("Ignoring Sparsebundle without Info.plist [%s]\n", dir);
6842 plist_file_size = smb_fname->st.st_ex_size;
6844 if (plist_file_size > INFO_PLIST_MAX_SIZE) {
6845 DBG_INFO("%s is too large, ignoring\n", plist);
6850 status = SMB_VFS_NEXT_CREATE_FILE(
6853 0, /* root_dir_fid */
6854 smb_fname, /* fname */
6855 FILE_GENERIC_READ, /* access_mask */
6856 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
6857 FILE_OPEN, /* create_disposition */
6858 0, /* create_options */
6859 0, /* file_attributes */
6860 INTERNAL_OPEN_ONLY, /* oplock_request */
6862 0, /* allocation_size */
6863 0, /* private_flags */
6868 NULL, NULL); /* create context */
6869 if (!NT_STATUS_IS_OK(status)) {
6870 DBG_INFO("Opening [%s] failed [%s]\n",
6871 smb_fname_str_dbg(smb_fname), nt_errstr(status));
6876 file_data = talloc_array(talloc_tos(), uint8_t, plist_file_size);
6877 if (file_data == NULL) {
6882 nread = SMB_VFS_NEXT_PREAD(handle, fsp, file_data, plist_file_size, 0);
6883 if (nread != plist_file_size) {
6884 DBG_ERR("Short read on [%s]: %zu/%zd\n",
6885 fsp_str_dbg(fsp), nread, plist_file_size);
6891 status = close_file(NULL, fsp, NORMAL_CLOSE);
6893 if (!NT_STATUS_IS_OK(status)) {
6894 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
6899 lines = file_lines_parse((char *)file_data,
6903 if (lines == NULL) {
6908 band_size_line = fruit_get_bandsize_line(lines, numlines);
6909 if (band_size_line == NULL) {
6910 DBG_ERR("Didn't find band-size key in [%s]\n",
6911 smb_fname_str_dbg(smb_fname));
6916 ok = fruit_get_bandsize_from_line(band_size_line, band_size);
6918 DBG_ERR("fruit_get_bandsize_from_line failed\n");
6922 DBG_DEBUG("Parsed band-size [%zu] for [%s]\n", *band_size, plist);
6926 status = close_file(NULL, fsp, NORMAL_CLOSE);
6927 if (!NT_STATUS_IS_OK(status)) {
6928 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
6933 TALLOC_FREE(smb_fname);
6934 TALLOC_FREE(file_data);
6939 struct fruit_disk_free_state {
6943 static bool fruit_get_num_bands(vfs_handle_struct *handle,
6948 struct smb_filename *bands_dir = NULL;
6950 struct dirent *e = NULL;
6954 path = talloc_asprintf(talloc_tos(),
6956 handle->conn->connectpath,
6962 bands_dir = synthetic_smb_fname(talloc_tos(),
6968 if (bands_dir == NULL) {
6972 d = SMB_VFS_NEXT_OPENDIR(handle, bands_dir, NULL, 0);
6974 TALLOC_FREE(bands_dir);
6980 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
6982 e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
6984 if (ISDOT(e->d_name) || ISDOTDOT(e->d_name)) {
6990 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
6992 TALLOC_FREE(bands_dir);
6996 DBG_DEBUG("%zu bands in [%s]\n", nbands, smb_fname_str_dbg(bands_dir));
6998 TALLOC_FREE(bands_dir);
7004 static bool fruit_tmsize_do_dirent(vfs_handle_struct *handle,
7005 struct fruit_disk_free_state *state,
7010 size_t sparsebundle_strlen = strlen("sparsebundle");
7011 size_t bandsize = 0;
7015 p = strstr(e->d_name, "sparsebundle");
7020 if (p[sparsebundle_strlen] != '\0') {
7024 DBG_DEBUG("Processing sparsebundle [%s]\n", e->d_name);
7026 ok = fruit_get_bandsize(handle, e->d_name, &bandsize);
7029 * Beware of race conditions: this may be an uninitialized
7030 * Info.plist that a client is just creating. We don't want let
7031 * this to trigger complete failure.
7033 DBG_ERR("Processing sparsebundle [%s] failed\n", e->d_name);
7037 ok = fruit_get_num_bands(handle, e->d_name, &nbands);
7040 * Beware of race conditions: this may be a backup sparsebundle
7041 * in an early stage lacking a bands subdirectory. We don't want
7042 * let this to trigger complete failure.
7044 DBG_ERR("Processing sparsebundle [%s] failed\n", e->d_name);
7048 if (bandsize > SIZE_MAX/nbands) {
7049 DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
7053 tm_size = bandsize * nbands;
7055 if (state->total_size + tm_size < state->total_size) {
7056 DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
7061 state->total_size += tm_size;
7063 DBG_DEBUG("[%s] tm_size [%jd] total_size [%jd]\n",
7064 e->d_name, (intmax_t)tm_size, (intmax_t)state->total_size);
7070 * Calculate used size of a TimeMachine volume
7072 * This assumes that the volume is used only for TimeMachine.
7074 * - readdir(basedir of share), then
7075 * - for every element that matches regex "^\(.*\)\.sparsebundle$" :
7076 * - parse "\1.sparsebundle/Info.plist" and read the band-size XML key
7077 * - count band files in "\1.sparsebundle/bands/"
7078 * - calculate used size of all bands: band_count * band_size
7080 static uint64_t fruit_disk_free(vfs_handle_struct *handle,
7081 const struct smb_filename *smb_fname,
7086 struct fruit_config_data *config = NULL;
7087 struct fruit_disk_free_state state = {0};
7089 struct dirent *e = NULL;
7095 SMB_VFS_HANDLE_GET_DATA(handle, config,
7096 struct fruit_config_data,
7099 if (!config->time_machine ||
7100 config->time_machine_max_size == 0)
7102 return SMB_VFS_NEXT_DISK_FREE(handle,
7109 d = SMB_VFS_NEXT_OPENDIR(handle, smb_fname, NULL, 0);
7114 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
7116 e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
7118 ok = fruit_tmsize_do_dirent(handle, &state, e);
7120 SMB_VFS_NEXT_CLOSEDIR(handle, d);
7125 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
7130 dsize = config->time_machine_max_size / 512;
7131 dfree = dsize - (state.total_size / 512);
7132 if (dfree > dsize) {
7142 static struct vfs_fn_pointers vfs_fruit_fns = {
7143 .connect_fn = fruit_connect,
7144 .disk_free_fn = fruit_disk_free,
7146 /* File operations */
7147 .chmod_fn = fruit_chmod,
7148 .chown_fn = fruit_chown,
7149 .unlink_fn = fruit_unlink,
7150 .rename_fn = fruit_rename,
7151 .rmdir_fn = fruit_rmdir,
7152 .open_fn = fruit_open,
7153 .close_fn = fruit_close,
7154 .pread_fn = fruit_pread,
7155 .pwrite_fn = fruit_pwrite,
7156 .pread_send_fn = fruit_pread_send,
7157 .pread_recv_fn = fruit_pread_recv,
7158 .pwrite_send_fn = fruit_pwrite_send,
7159 .pwrite_recv_fn = fruit_pwrite_recv,
7160 .stat_fn = fruit_stat,
7161 .lstat_fn = fruit_lstat,
7162 .fstat_fn = fruit_fstat,
7163 .streaminfo_fn = fruit_streaminfo,
7164 .ntimes_fn = fruit_ntimes,
7165 .ftruncate_fn = fruit_ftruncate,
7166 .fallocate_fn = fruit_fallocate,
7167 .create_file_fn = fruit_create_file,
7168 .readdir_attr_fn = fruit_readdir_attr,
7169 .offload_read_send_fn = fruit_offload_read_send,
7170 .offload_read_recv_fn = fruit_offload_read_recv,
7171 .offload_write_send_fn = fruit_offload_write_send,
7172 .offload_write_recv_fn = fruit_offload_write_recv,
7174 /* NT ACL operations */
7175 .fget_nt_acl_fn = fruit_fget_nt_acl,
7176 .fset_nt_acl_fn = fruit_fset_nt_acl,
7180 NTSTATUS vfs_fruit_init(TALLOC_CTX *ctx)
7182 NTSTATUS ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "fruit",
7184 if (!NT_STATUS_IS_OK(ret)) {
7188 vfs_fruit_debug_level = debug_add_class("fruit");
7189 if (vfs_fruit_debug_level == -1) {
7190 vfs_fruit_debug_level = DBGC_VFS;
7191 DEBUG(0, ("%s: Couldn't register custom debugging class!\n",
7194 DEBUG(10, ("%s: Debug class number of '%s': %d\n",
7195 "vfs_fruit_init","fruit",vfs_fruit_debug_level));