2 * OS X and Netatalk interoperability VFS module for Samba-3.x
4 * Copyright (C) Ralph Boehme, 2013, 2014
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
21 #include "MacExtensions.h"
22 #include "smbd/smbd.h"
23 #include "system/filesys.h"
24 #include "lib/util/time.h"
25 #include "system/shmem.h"
26 #include "locking/proto.h"
27 #include "smbd/globals.h"
29 #include "libcli/security/security.h"
30 #include "../libcli/smb/smb2_create_ctx.h"
31 #include "lib/util/sys_rw.h"
32 #include "lib/util/tevent_ntstatus.h"
33 #include "lib/util/tevent_unix.h"
34 #include "offload_token.h"
35 #include "string_replace.h"
37 #include <gnutls/gnutls.h>
38 #include <gnutls/crypto.h>
41 * Enhanced OS X and Netatalk compatibility
42 * ========================================
44 * This modules takes advantage of vfs_streams_xattr and
45 * vfs_catia. VFS modules vfs_fruit and vfs_streams_xattr must be
46 * loaded in the correct order:
48 * vfs modules = catia fruit streams_xattr
50 * The module intercepts the OS X special streams "AFP_AfpInfo" and
51 * "AFP_Resource" and handles them in a special way. All other named
52 * streams are deferred to vfs_streams_xattr.
54 * The OS X client maps all NTFS illegal characters to the Unicode
55 * private range. This module optionally stores the charcters using
56 * their native ASCII encoding using vfs_catia. If you're not enabling
57 * this feature, you can skip catia from vfs modules.
59 * Finally, open modes are optionally checked against Netatalk AFP
62 * The "AFP_AfpInfo" named stream is a binary blob containing OS X
63 * extended metadata for files and directories. This module optionally
64 * reads and stores this metadata in a way compatible with Netatalk 3
65 * which stores the metadata in an EA "org.netatalk.metadata". Cf
66 * source3/include/MacExtensions.h for a description of the binary
69 * The "AFP_Resource" named stream may be arbitrarily large, thus it
70 * can't be stored in an xattr on most filesystem. ZFS on Solaris is
71 * the only available filesystem where xattrs can be of any size and
72 * the OS supports using the file APIs for xattrs.
74 * The AFP_Resource stream is stored in an AppleDouble file prepending
75 * "._" to the filename. On Solaris with ZFS the stream is optionally
76 * stored in an EA "org.netatalk.resource".
82 * The OS X SMB client sends xattrs as ADS too. For xattr interop with
83 * other protocols you may want to adjust the xattr names the VFS
84 * module vfs_streams_xattr uses for storing ADS's. This defaults to
85 * user.DosStream.ADS_NAME:$DATA and can be changed by specifying
86 * these module parameters:
88 * streams_xattr:prefix = user.
89 * streams_xattr:store_stream_type = false
95 * - log diagnostic if any needed VFS module is not loaded
96 * (eg with lp_vfs_objects())
100 static int vfs_fruit_debug_level = DBGC_VFS;
102 static struct global_fruit_config {
103 bool nego_aapl; /* client negotiated AAPL */
105 } global_fruit_config;
108 #define DBGC_CLASS vfs_fruit_debug_level
110 #define FRUIT_PARAM_TYPE_NAME "fruit"
111 #define ADOUBLE_NAME_PREFIX "._"
113 #define NETATALK_META_XATTR "org.netatalk.Metadata"
114 #define NETATALK_RSRC_XATTR "org.netatalk.ResourceFork"
116 #if defined(HAVE_ATTROPEN)
117 #define AFPINFO_EA_NETATALK NETATALK_META_XATTR
118 #define AFPRESOURCE_EA_NETATALK NETATALK_RSRC_XATTR
120 #define AFPINFO_EA_NETATALK "user." NETATALK_META_XATTR
121 #define AFPRESOURCE_EA_NETATALK "user." NETATALK_RSRC_XATTR
124 enum apple_fork {APPLE_FORK_DATA, APPLE_FORK_RSRC};
126 enum fruit_rsrc {FRUIT_RSRC_STREAM, FRUIT_RSRC_ADFILE, FRUIT_RSRC_XATTR};
127 enum fruit_meta {FRUIT_META_STREAM, FRUIT_META_NETATALK};
128 enum fruit_locking {FRUIT_LOCKING_NETATALK, FRUIT_LOCKING_NONE};
129 enum fruit_encoding {FRUIT_ENC_NATIVE, FRUIT_ENC_PRIVATE};
131 struct fruit_config_data {
132 enum fruit_rsrc rsrc;
133 enum fruit_meta meta;
134 enum fruit_locking locking;
135 enum fruit_encoding encoding;
136 bool use_aapl; /* config from smb.conf */
138 bool readdir_attr_enabled;
139 bool unix_info_enabled;
140 bool copyfile_enabled;
141 bool veto_appledouble;
143 bool aapl_zero_file_id;
146 off_t time_machine_max_size;
147 bool wipe_intentionally_left_blank_rfork;
148 bool delete_empty_adfiles;
151 * Additional options, all enabled by default,
152 * possibly useful for analyzing performance. The associated
153 * operations with each of them may be expensive, so having
154 * the chance to disable them individually gives a chance
155 * tweaking the setup for the particular usecase.
157 bool readdir_attr_rsize;
158 bool readdir_attr_finder_info;
159 bool readdir_attr_max_access;
162 static const struct enum_list fruit_rsrc[] = {
163 {FRUIT_RSRC_STREAM, "stream"}, /* pass on to vfs_streams_xattr */
164 {FRUIT_RSRC_ADFILE, "file"}, /* ._ AppleDouble file */
165 {FRUIT_RSRC_XATTR, "xattr"}, /* Netatalk compatible xattr (ZFS only) */
169 static const struct enum_list fruit_meta[] = {
170 {FRUIT_META_STREAM, "stream"}, /* pass on to vfs_streams_xattr */
171 {FRUIT_META_NETATALK, "netatalk"}, /* Netatalk compatible xattr */
175 static const struct enum_list fruit_locking[] = {
176 {FRUIT_LOCKING_NETATALK, "netatalk"}, /* synchronize locks with Netatalk */
177 {FRUIT_LOCKING_NONE, "none"},
181 static const struct enum_list fruit_encoding[] = {
182 {FRUIT_ENC_NATIVE, "native"}, /* map unicode private chars to ASCII */
183 {FRUIT_ENC_PRIVATE, "private"}, /* keep unicode private chars */
187 static const char *fruit_catia_maps =
188 "0x01:0xf001,0x02:0xf002,0x03:0xf003,0x04:0xf004,"
189 "0x05:0xf005,0x06:0xf006,0x07:0xf007,0x08:0xf008,"
190 "0x09:0xf009,0x0a:0xf00a,0x0b:0xf00b,0x0c:0xf00c,"
191 "0x0d:0xf00d,0x0e:0xf00e,0x0f:0xf00f,0x10:0xf010,"
192 "0x11:0xf011,0x12:0xf012,0x13:0xf013,0x14:0xf014,"
193 "0x15:0xf015,0x16:0xf016,0x17:0xf017,0x18:0xf018,"
194 "0x19:0xf019,0x1a:0xf01a,0x1b:0xf01b,0x1c:0xf01c,"
195 "0x1d:0xf01d,0x1e:0xf01e,0x1f:0xf01f,"
196 "0x22:0xf020,0x2a:0xf021,0x3a:0xf022,0x3c:0xf023,"
197 "0x3e:0xf024,0x3f:0xf025,0x5c:0xf026,0x7c:0xf027,"
200 /*****************************************************************************
201 * Defines, functions and data structures that deal with AppleDouble
202 *****************************************************************************/
205 * There are two AppleDouble blobs we deal with:
207 * - ADOUBLE_META - AppleDouble blob used by Netatalk for storing
208 * metadata in an xattr
210 * - ADOUBLE_RSRC - AppleDouble blob used by OS X and Netatalk in
213 typedef enum {ADOUBLE_META, ADOUBLE_RSRC} adouble_type_t;
216 #define AD_VERSION2 0x00020000
217 #define AD_VERSION AD_VERSION2
220 * AppleDouble entry IDs.
222 #define ADEID_DFORK 1
223 #define ADEID_RFORK 2
225 #define ADEID_COMMENT 4
226 #define ADEID_ICONBW 5
227 #define ADEID_ICONCOL 6
228 #define ADEID_FILEI 7
229 #define ADEID_FILEDATESI 8
230 #define ADEID_FINDERI 9
231 #define ADEID_MACFILEI 10
232 #define ADEID_PRODOSFILEI 11
233 #define ADEID_MSDOSFILEI 12
234 #define ADEID_SHORTNAME 13
235 #define ADEID_AFPFILEI 14
238 /* Private Netatalk entries */
239 #define ADEID_PRIVDEV 16
240 #define ADEID_PRIVINO 17
241 #define ADEID_PRIVSYN 18
242 #define ADEID_PRIVID 19
243 #define ADEID_MAX (ADEID_PRIVID + 1)
246 * These are the real ids for the private entries,
247 * as stored in the adouble file
249 #define AD_DEV 0x80444556
250 #define AD_INO 0x80494E4F
251 #define AD_SYN 0x8053594E
252 #define AD_ID 0x8053567E
254 /* Number of actually used entries */
255 #define ADEID_NUM_XATTR 8
256 #define ADEID_NUM_DOT_UND 2
257 #define ADEID_NUM_RSRC_XATTR 1
259 /* AppleDouble magic */
260 #define AD_APPLESINGLE_MAGIC 0x00051600
261 #define AD_APPLEDOUBLE_MAGIC 0x00051607
262 #define AD_MAGIC AD_APPLEDOUBLE_MAGIC
264 /* Sizes of relevant entry bits */
265 #define ADEDLEN_MAGIC 4
266 #define ADEDLEN_VERSION 4
267 #define ADEDLEN_FILLER 16
268 #define AD_FILLER_TAG "Netatalk " /* should be 16 bytes */
269 #define AD_FILLER_TAG_OSX "Mac OS X " /* should be 16 bytes */
270 #define ADEDLEN_NENTRIES 2
271 #define AD_HEADER_LEN (ADEDLEN_MAGIC + ADEDLEN_VERSION + \
272 ADEDLEN_FILLER + ADEDLEN_NENTRIES) /* 26 */
273 #define AD_ENTRY_LEN_EID 4
274 #define AD_ENTRY_LEN_OFF 4
275 #define AD_ENTRY_LEN_LEN 4
276 #define AD_ENTRY_LEN (AD_ENTRY_LEN_EID + AD_ENTRY_LEN_OFF + AD_ENTRY_LEN_LEN)
279 #define ADEDLEN_NAME 255
280 #define ADEDLEN_COMMENT 200
281 #define ADEDLEN_FILEI 16
282 #define ADEDLEN_FINDERI 32
283 #define ADEDLEN_FILEDATESI 16
284 #define ADEDLEN_SHORTNAME 12 /* length up to 8.3 */
285 #define ADEDLEN_AFPFILEI 4
286 #define ADEDLEN_MACFILEI 4
287 #define ADEDLEN_PRODOSFILEI 8
288 #define ADEDLEN_MSDOSFILEI 2
289 #define ADEDLEN_DID 4
290 #define ADEDLEN_PRIVDEV 8
291 #define ADEDLEN_PRIVINO 8
292 #define ADEDLEN_PRIVSYN 8
293 #define ADEDLEN_PRIVID 4
296 #define ADEDOFF_MAGIC 0
297 #define ADEDOFF_VERSION (ADEDOFF_MAGIC + ADEDLEN_MAGIC)
298 #define ADEDOFF_FILLER (ADEDOFF_VERSION + ADEDLEN_VERSION)
299 #define ADEDOFF_NENTRIES (ADEDOFF_FILLER + ADEDLEN_FILLER)
301 #define ADEDOFF_FINDERI_XATTR (AD_HEADER_LEN + \
302 (ADEID_NUM_XATTR * AD_ENTRY_LEN))
303 #define ADEDOFF_COMMENT_XATTR (ADEDOFF_FINDERI_XATTR + ADEDLEN_FINDERI)
304 #define ADEDOFF_FILEDATESI_XATTR (ADEDOFF_COMMENT_XATTR + ADEDLEN_COMMENT)
305 #define ADEDOFF_AFPFILEI_XATTR (ADEDOFF_FILEDATESI_XATTR + \
307 #define ADEDOFF_PRIVDEV_XATTR (ADEDOFF_AFPFILEI_XATTR + ADEDLEN_AFPFILEI)
308 #define ADEDOFF_PRIVINO_XATTR (ADEDOFF_PRIVDEV_XATTR + ADEDLEN_PRIVDEV)
309 #define ADEDOFF_PRIVSYN_XATTR (ADEDOFF_PRIVINO_XATTR + ADEDLEN_PRIVINO)
310 #define ADEDOFF_PRIVID_XATTR (ADEDOFF_PRIVSYN_XATTR + ADEDLEN_PRIVSYN)
312 #define ADEDOFF_FINDERI_DOT_UND (AD_HEADER_LEN + \
313 (ADEID_NUM_DOT_UND * AD_ENTRY_LEN))
314 #define ADEDOFF_RFORK_DOT_UND (ADEDOFF_FINDERI_DOT_UND + ADEDLEN_FINDERI)
316 #define AD_DATASZ_XATTR (AD_HEADER_LEN + \
317 (ADEID_NUM_XATTR * AD_ENTRY_LEN) + \
318 ADEDLEN_FINDERI + ADEDLEN_COMMENT + \
319 ADEDLEN_FILEDATESI + ADEDLEN_AFPFILEI + \
320 ADEDLEN_PRIVDEV + ADEDLEN_PRIVINO + \
321 ADEDLEN_PRIVSYN + ADEDLEN_PRIVID)
323 #if AD_DATASZ_XATTR != 402
324 #error bad size for AD_DATASZ_XATTR
327 #define AD_DATASZ_DOT_UND (AD_HEADER_LEN + \
328 (ADEID_NUM_DOT_UND * AD_ENTRY_LEN) + \
330 #if AD_DATASZ_DOT_UND != 82
331 #error bad size for AD_DATASZ_DOT_UND
335 * Sharemode locks fcntl() offsets
337 #if _FILE_OFFSET_BITS == 64 || defined(HAVE_LARGEFILE)
338 #define AD_FILELOCK_BASE (UINT64_C(0x7FFFFFFFFFFFFFFF) - 9)
340 #define AD_FILELOCK_BASE (UINT32_C(0x7FFFFFFF) - 9)
342 #define BYTELOCK_MAX (AD_FILELOCK_BASE - 1)
344 #define AD_FILELOCK_OPEN_WR (AD_FILELOCK_BASE + 0)
345 #define AD_FILELOCK_OPEN_RD (AD_FILELOCK_BASE + 1)
346 #define AD_FILELOCK_RSRC_OPEN_WR (AD_FILELOCK_BASE + 2)
347 #define AD_FILELOCK_RSRC_OPEN_RD (AD_FILELOCK_BASE + 3)
348 #define AD_FILELOCK_DENY_WR (AD_FILELOCK_BASE + 4)
349 #define AD_FILELOCK_DENY_RD (AD_FILELOCK_BASE + 5)
350 #define AD_FILELOCK_RSRC_DENY_WR (AD_FILELOCK_BASE + 6)
351 #define AD_FILELOCK_RSRC_DENY_RD (AD_FILELOCK_BASE + 7)
352 #define AD_FILELOCK_OPEN_NONE (AD_FILELOCK_BASE + 8)
353 #define AD_FILELOCK_RSRC_OPEN_NONE (AD_FILELOCK_BASE + 9)
355 /* Time stuff we overload the bits a little */
356 #define AD_DATE_CREATE 0
357 #define AD_DATE_MODIFY 4
358 #define AD_DATE_BACKUP 8
359 #define AD_DATE_ACCESS 12
360 #define AD_DATE_MASK (AD_DATE_CREATE | AD_DATE_MODIFY | \
361 AD_DATE_BACKUP | AD_DATE_ACCESS)
362 #define AD_DATE_UNIX (1 << 10)
363 #define AD_DATE_START 0x80000000
364 #define AD_DATE_DELTA 946684800
365 #define AD_DATE_FROM_UNIX(x) (htonl((x) - AD_DATE_DELTA))
366 #define AD_DATE_TO_UNIX(x) (ntohl(x) + AD_DATE_DELTA)
368 #define AD_XATTR_HDR_MAGIC 0x41545452 /* 'ATTR' */
369 #define AD_XATTR_MAX_ENTRIES 1024 /* Some arbitrarily enforced limit */
370 #define AD_XATTR_HDR_SIZE 36
371 #define AD_XATTR_MAX_HDR_SIZE 65536
373 /* Accessor macros */
374 #define ad_getentrylen(ad,eid) ((ad)->ad_eid[(eid)].ade_len)
375 #define ad_getentryoff(ad,eid) ((ad)->ad_eid[(eid)].ade_off)
376 #define ad_setentrylen(ad,eid,len) ((ad)->ad_eid[(eid)].ade_len = (len))
377 #define ad_setentryoff(ad,eid,off) ((ad)->ad_eid[(eid)].ade_off = (off))
380 * Both struct ad_xattr_header and struct ad_xattr_entry describe the in memory
381 * representation as well as the on-disk format.
383 * The ad_xattr_header follows the FinderInfo data in the FinderInfo entry if
384 * the length of the FinderInfo entry is larger then 32 bytes. It is then
385 * preceeded with 2 bytes padding.
387 * Cf: https://opensource.apple.com/source/xnu/xnu-4570.1.46/bsd/vfs/vfs_xattr.c
390 struct ad_xattr_header {
391 uint32_t adx_magic; /* ATTR_HDR_MAGIC */
392 uint32_t adx_debug_tag; /* for debugging == file id of owning file */
393 uint32_t adx_total_size; /* file offset of end of attribute header + entries + data */
394 uint32_t adx_data_start; /* file offset to attribute data area */
395 uint32_t adx_data_length; /* length of attribute data area */
396 uint32_t adx_reserved[3];
398 uint16_t adx_num_attrs;
401 /* On-disk entries are aligned on 4 byte boundaries */
402 struct ad_xattr_entry {
403 uint32_t adx_offset; /* file offset to data */
404 uint32_t adx_length; /* size of attribute data */
406 uint8_t adx_namelen; /* included the NULL terminator */
407 char *adx_name; /* NULL-terminated UTF-8 name */
416 vfs_handle_struct *ad_handle;
419 adouble_type_t ad_type;
422 uint8_t ad_filler[ADEDLEN_FILLER];
423 struct ad_entry ad_eid[ADEID_MAX];
425 struct ad_xattr_header adx_header;
426 struct ad_xattr_entry *adx_entries;
429 struct ad_entry_order {
430 uint32_t id, offset, len;
433 /* Netatalk AppleDouble metadata xattr */
435 struct ad_entry_order entry_order_meta_xattr[ADEID_NUM_XATTR + 1] = {
436 {ADEID_FINDERI, ADEDOFF_FINDERI_XATTR, ADEDLEN_FINDERI},
437 {ADEID_COMMENT, ADEDOFF_COMMENT_XATTR, 0},
438 {ADEID_FILEDATESI, ADEDOFF_FILEDATESI_XATTR, ADEDLEN_FILEDATESI},
439 {ADEID_AFPFILEI, ADEDOFF_AFPFILEI_XATTR, ADEDLEN_AFPFILEI},
440 {ADEID_PRIVDEV, ADEDOFF_PRIVDEV_XATTR, 0},
441 {ADEID_PRIVINO, ADEDOFF_PRIVINO_XATTR, 0},
442 {ADEID_PRIVSYN, ADEDOFF_PRIVSYN_XATTR, 0},
443 {ADEID_PRIVID, ADEDOFF_PRIVID_XATTR, 0},
447 /* AppleDouble resource fork file (the ones prefixed by "._") */
449 struct ad_entry_order entry_order_dot_und[ADEID_NUM_DOT_UND + 1] = {
450 {ADEID_FINDERI, ADEDOFF_FINDERI_DOT_UND, ADEDLEN_FINDERI},
451 {ADEID_RFORK, ADEDOFF_RFORK_DOT_UND, 0},
456 * Fake AppleDouble entry oder for resource fork xattr. The xattr
457 * isn't an AppleDouble file, it simply contains the resource data,
458 * but in order to be able to use some API calls like ad_getentryoff()
459 * we build a fake/helper struct adouble with this entry order struct.
462 struct ad_entry_order entry_order_rsrc_xattr[ADEID_NUM_RSRC_XATTR + 1] = {
467 /* Conversion from enumerated id to on-disk AppleDouble id */
468 #define AD_EID_DISK(a) (set_eid[a])
469 static const uint32_t set_eid[] = {
470 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
471 AD_DEV, AD_INO, AD_SYN, AD_ID
474 static char empty_resourcefork[] = {
475 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
476 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
477 0x54, 0x68, 0x69, 0x73, 0x20, 0x72, 0x65, 0x73,
478 0x6F, 0x75, 0x72, 0x63, 0x65, 0x20, 0x66, 0x6F,
479 0x72, 0x6B, 0x20, 0x69, 0x6E, 0x74, 0x65, 0x6E,
480 0x74, 0x69, 0x6F, 0x6E, 0x61, 0x6C, 0x6C, 0x79,
481 0x20, 0x6C, 0x65, 0x66, 0x74, 0x20, 0x62, 0x6C,
482 0x61, 0x6E, 0x6B, 0x20, 0x20, 0x20, 0x00, 0x00,
483 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
484 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
485 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
486 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
487 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
488 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
489 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
490 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
491 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
492 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
493 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
494 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
495 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
496 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
497 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
498 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
499 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
500 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
501 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
502 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
503 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
504 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
505 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
506 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
507 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
508 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
509 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
510 0x00, 0x1C, 0x00, 0x1E, 0xFF, 0xFF
514 /* tcon config handle */
515 struct fruit_config_data *config;
517 /* Denote stream type, meta or rsrc */
520 /* Whether the create created the stream */
524 * AFP_AfpInfo stream created, but not written yet, thus still a fake
525 * pipe fd. This is set to true in fruit_open_meta if there was no
526 * exisiting stream but the caller requested O_CREAT. It is later set to
527 * false when we get a write on the stream that then does open and
536 * Forward declarations
538 static struct adouble *ad_init(TALLOC_CTX *ctx, vfs_handle_struct *handle,
539 adouble_type_t type);
540 static struct adouble *ad_get(TALLOC_CTX *ctx,
541 vfs_handle_struct *handle,
542 const struct smb_filename *smb_fname,
543 adouble_type_t type);
544 static int ad_set(vfs_handle_struct *handle,
546 const struct smb_filename *smb_fname);
547 static int ad_fset(struct vfs_handle_struct *handle,
550 static int adouble_path(TALLOC_CTX *ctx,
551 const struct smb_filename *smb_fname__in,
552 struct smb_filename **ppsmb_fname_out);
553 static AfpInfo *afpinfo_new(TALLOC_CTX *ctx);
554 static ssize_t afpinfo_pack(const AfpInfo *ai, char *buf);
555 static AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data);
559 * Return a pointer to an AppleDouble entry
561 * Returns NULL if the entry is not present
563 static char *ad_get_entry(const struct adouble *ad, int eid)
565 off_t off = ad_getentryoff(ad, eid);
566 size_t len = ad_getentrylen(ad, eid);
568 if (off == 0 || len == 0) {
572 return ad->ad_data + off;
578 static int ad_getdate(const struct adouble *ad,
579 unsigned int dateoff,
582 bool xlate = (dateoff & AD_DATE_UNIX);
585 dateoff &= AD_DATE_MASK;
586 p = ad_get_entry(ad, ADEID_FILEDATESI);
591 if (dateoff > AD_DATE_ACCESS) {
595 memcpy(date, p + dateoff, sizeof(uint32_t));
598 *date = AD_DATE_TO_UNIX(*date);
606 static int ad_setdate(struct adouble *ad, unsigned int dateoff, uint32_t date)
608 bool xlate = (dateoff & AD_DATE_UNIX);
611 p = ad_get_entry(ad, ADEID_FILEDATESI);
616 dateoff &= AD_DATE_MASK;
618 date = AD_DATE_FROM_UNIX(date);
621 if (dateoff > AD_DATE_ACCESS) {
625 memcpy(p + dateoff, &date, sizeof(date));
632 * Map on-disk AppleDouble id to enumerated id
634 static uint32_t get_eid(uint32_t eid)
642 return ADEID_PRIVDEV;
644 return ADEID_PRIVINO;
646 return ADEID_PRIVSYN;
657 * Pack AppleDouble structure into data buffer
659 static bool ad_pack(struct adouble *ad)
666 bufsize = talloc_get_size(ad->ad_data);
667 if (bufsize < AD_DATASZ_DOT_UND) {
668 DBG_ERR("bad buffer size [0x%" PRIx32 "]\n", bufsize);
672 if (offset + ADEDLEN_MAGIC < offset ||
673 offset + ADEDLEN_MAGIC >= bufsize) {
676 RSIVAL(ad->ad_data, offset, ad->ad_magic);
677 offset += ADEDLEN_MAGIC;
679 if (offset + ADEDLEN_VERSION < offset ||
680 offset + ADEDLEN_VERSION >= bufsize) {
683 RSIVAL(ad->ad_data, offset, ad->ad_version);
684 offset += ADEDLEN_VERSION;
686 if (offset + ADEDLEN_FILLER < offset ||
687 offset + ADEDLEN_FILLER >= bufsize) {
690 if (ad->ad_type == ADOUBLE_RSRC) {
691 memcpy(ad->ad_data + offset, AD_FILLER_TAG, ADEDLEN_FILLER);
693 offset += ADEDLEN_FILLER;
695 if (offset + ADEDLEN_NENTRIES < offset ||
696 offset + ADEDLEN_NENTRIES >= bufsize) {
699 offset += ADEDLEN_NENTRIES;
701 for (eid = 0, nent = 0; eid < ADEID_MAX; eid++) {
702 if (ad->ad_eid[eid].ade_off == 0) {
704 * ade_off is also used as indicator whether a
705 * specific entry is used or not
710 if (offset + AD_ENTRY_LEN_EID < offset ||
711 offset + AD_ENTRY_LEN_EID >= bufsize) {
714 RSIVAL(ad->ad_data, offset, AD_EID_DISK(eid));
715 offset += AD_ENTRY_LEN_EID;
717 if (offset + AD_ENTRY_LEN_OFF < offset ||
718 offset + AD_ENTRY_LEN_OFF >= bufsize) {
721 RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_off);
722 offset += AD_ENTRY_LEN_OFF;
724 if (offset + AD_ENTRY_LEN_LEN < offset ||
725 offset + AD_ENTRY_LEN_LEN >= bufsize) {
728 RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_len);
729 offset += AD_ENTRY_LEN_LEN;
734 if (ADEDOFF_NENTRIES + 2 >= bufsize) {
737 RSSVAL(ad->ad_data, ADEDOFF_NENTRIES, nent);
742 static bool ad_unpack_xattrs(struct adouble *ad)
744 struct ad_xattr_header *h = &ad->adx_header;
745 const char *p = ad->ad_data;
749 if (ad_getentrylen(ad, ADEID_FINDERI) <= ADEDLEN_FINDERI) {
753 /* 2 bytes padding */
754 hoff = ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI + 2;
756 h->adx_magic = RIVAL(p, hoff + 0);
757 h->adx_debug_tag = RIVAL(p, hoff + 4); /* Not used -> not checked */
758 h->adx_total_size = RIVAL(p, hoff + 8);
759 h->adx_data_start = RIVAL(p, hoff + 12);
760 h->adx_data_length = RIVAL(p, hoff + 16);
761 h->adx_flags = RSVAL(p, hoff + 32); /* Not used -> not checked */
762 h->adx_num_attrs = RSVAL(p, hoff + 34);
764 if (h->adx_magic != AD_XATTR_HDR_MAGIC) {
765 DBG_ERR("Bad magic: 0x%" PRIx32 "\n", h->adx_magic);
769 if (h->adx_total_size > ad_getentryoff(ad, ADEID_RFORK)) {
770 DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size);
773 if (h->adx_total_size > AD_XATTR_MAX_HDR_SIZE) {
774 DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size);
778 if (h->adx_data_start < (hoff + AD_XATTR_HDR_SIZE)) {
779 DBG_ERR("Bad start: 0x%" PRIx32 "\n", h->adx_data_start);
783 if ((h->adx_data_start + h->adx_data_length) < h->adx_data_start) {
784 DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length);
787 if ((h->adx_data_start + h->adx_data_length) >
788 ad->adx_header.adx_total_size)
790 DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length);
794 if (h->adx_num_attrs > AD_XATTR_MAX_ENTRIES) {
795 DBG_ERR("Bad num xattrs: %" PRIu16 "\n", h->adx_num_attrs);
799 if (h->adx_num_attrs == 0) {
803 ad->adx_entries = talloc_zero_array(
804 ad, struct ad_xattr_entry, h->adx_num_attrs);
805 if (ad->adx_entries == NULL) {
809 hoff += AD_XATTR_HDR_SIZE;
811 for (i = 0; i < h->adx_num_attrs; i++) {
812 struct ad_xattr_entry *e = &ad->adx_entries[i];
814 hoff = (hoff + 3) & ~3;
816 e->adx_offset = RIVAL(p, hoff + 0);
817 e->adx_length = RIVAL(p, hoff + 4);
818 e->adx_flags = RSVAL(p, hoff + 8);
819 e->adx_namelen = *(p + hoff + 10);
821 if (e->adx_offset >= ad->adx_header.adx_total_size) {
822 DBG_ERR("Bad adx_offset: %" PRIx32 "\n",
827 if ((e->adx_offset + e->adx_length) < e->adx_offset) {
828 DBG_ERR("Bad adx_length: %" PRIx32 "\n",
833 if ((e->adx_offset + e->adx_length) >
834 ad->adx_header.adx_total_size)
836 DBG_ERR("Bad adx_length: %" PRIx32 "\n",
841 if (e->adx_namelen == 0) {
842 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
846 if ((hoff + 11 + e->adx_namelen) < hoff + 11) {
847 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
851 if ((hoff + 11 + e->adx_namelen) >
852 ad->adx_header.adx_data_start)
854 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
859 e->adx_name = talloc_strndup(ad->adx_entries,
862 if (e->adx_name == NULL) {
866 DBG_DEBUG("xattr [%s] offset [0x%x] size [0x%x]\n",
867 e->adx_name, e->adx_offset, e->adx_length);
868 dump_data(10, (uint8_t *)(ad->ad_data + e->adx_offset),
871 hoff += 11 + e->adx_namelen;
878 * Unpack an AppleDouble blob into a struct adoble
880 static bool ad_unpack(struct adouble *ad, const size_t nentries,
883 size_t bufsize = talloc_get_size(ad->ad_data);
885 uint32_t eid, len, off;
889 * The size of the buffer ad->ad_data is checked when read, so
890 * we wouldn't have to check our own offsets, a few extra
891 * checks won't hurt though. We have to check the offsets we
892 * read from the buffer anyway.
895 if (bufsize < (AD_HEADER_LEN + (AD_ENTRY_LEN * nentries))) {
896 DEBUG(1, ("bad size\n"));
900 ad->ad_magic = RIVAL(ad->ad_data, 0);
901 ad->ad_version = RIVAL(ad->ad_data, ADEDOFF_VERSION);
902 if ((ad->ad_magic != AD_MAGIC) || (ad->ad_version != AD_VERSION)) {
903 DEBUG(1, ("wrong magic or version\n"));
907 memcpy(ad->ad_filler, ad->ad_data + ADEDOFF_FILLER, ADEDLEN_FILLER);
909 adentries = RSVAL(ad->ad_data, ADEDOFF_NENTRIES);
910 if (adentries != nentries) {
911 DEBUG(1, ("invalid number of entries: %zu\n",
916 /* now, read in the entry bits */
917 for (i = 0; i < adentries; i++) {
918 eid = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN));
920 off = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 4);
921 len = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 8);
923 if (!eid || eid >= ADEID_MAX) {
924 DEBUG(1, ("bogus eid %d\n", eid));
929 * All entries other than the resource fork are
930 * expected to be read into the ad_data buffer, so
931 * ensure the specified offset is within that bound
933 if ((off > bufsize) && (eid != ADEID_RFORK)) {
934 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
940 * All entries besides FinderInfo and resource fork
941 * must fit into the buffer. FinderInfo is special as
942 * it may be larger then the default 32 bytes (if it
943 * contains marshalled xattrs), but we will fixup that
944 * in ad_convert(). And the resource fork is never
945 * accessed directly by the ad_data buf (also see
946 * comment above) anyway.
948 if ((eid != ADEID_RFORK) &&
949 (eid != ADEID_FINDERI) &&
950 ((off + len) > bufsize)) {
951 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
957 * That would be obviously broken
959 if (off > filesize) {
960 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
966 * Check for any entry that has its end beyond the
969 if (off + len < off) {
970 DEBUG(1, ("offset wrap in eid %d: off: %" PRIu32
971 ", len: %" PRIu32 "\n",
976 if (off + len > filesize) {
978 * If this is the resource fork entry, we fix
979 * up the length, for any other entry we bail
982 if (eid != ADEID_RFORK) {
983 DEBUG(1, ("bogus eid %d: off: %" PRIu32
984 ", len: %" PRIu32 "\n",
990 * Fixup the resource fork entry by limiting
991 * the size to entryoffset - filesize.
993 len = filesize - off;
994 DEBUG(1, ("Limiting ADEID_RFORK: off: %" PRIu32
995 ", len: %" PRIu32 "\n", off, len));
998 ad->ad_eid[eid].ade_off = off;
999 ad->ad_eid[eid].ade_len = len;
1002 ok = ad_unpack_xattrs(ad);
1010 static bool ad_convert_move_reso(struct adouble *ad,
1011 const struct smb_filename *smb_fname)
1013 char *map = MAP_FAILED;
1019 if (ad_getentrylen(ad, ADEID_RFORK) == 0) {
1023 maplen = ad_getentryoff(ad, ADEID_RFORK) +
1024 ad_getentrylen(ad, ADEID_RFORK);
1026 /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
1027 map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
1029 if (map == MAP_FAILED) {
1030 DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
1035 memmove(map + ADEDOFF_RFORK_DOT_UND,
1036 map + ad_getentryoff(ad, ADEID_RFORK),
1037 ad_getentrylen(ad, ADEID_RFORK));
1039 rc = munmap(map, maplen);
1041 DBG_ERR("munmap failed: %s\n", strerror(errno));
1045 ad_setentryoff(ad, ADEID_RFORK, ADEDOFF_RFORK_DOT_UND);
1049 DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name);
1053 len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
1054 if (len != AD_DATASZ_DOT_UND) {
1055 DBG_ERR("%s: bad size: %zd\n", smb_fname->base_name, len);
1062 static bool ad_convert_xattr(struct adouble *ad,
1063 const struct smb_filename *smb_fname,
1064 bool *converted_xattr)
1066 static struct char_mappings **string_replace_cmaps = NULL;
1067 char *map = MAP_FAILED;
1071 int saved_errno = 0;
1076 *converted_xattr = false;
1078 if (ad_getentrylen(ad, ADEID_FINDERI) == ADEDLEN_FINDERI) {
1082 if (string_replace_cmaps == NULL) {
1083 const char **mappings = NULL;
1085 mappings = str_list_make_v3_const(
1086 talloc_tos(), fruit_catia_maps, NULL);
1087 if (mappings == NULL) {
1090 string_replace_cmaps = string_replace_init_map(mappings);
1091 TALLOC_FREE(mappings);
1094 maplen = ad_getentryoff(ad, ADEID_RFORK) +
1095 ad_getentrylen(ad, ADEID_RFORK);
1097 /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
1098 map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
1100 if (map == MAP_FAILED) {
1101 DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
1105 for (i = 0; i < ad->adx_header.adx_num_attrs; i++) {
1106 struct ad_xattr_entry *e = &ad->adx_entries[i];
1107 char *mapped_name = NULL;
1109 struct smb_filename *stream_name = NULL;
1110 files_struct *fsp = NULL;
1113 status = string_replace_allocate(ad->ad_handle->conn,
1115 string_replace_cmaps,
1118 vfs_translate_to_windows);
1119 if (!NT_STATUS_IS_OK(status) &&
1120 !NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))
1122 DBG_ERR("string_replace_allocate failed\n");
1128 mapped_name = talloc_asprintf(talloc_tos(), ":%s", tmp);
1130 if (mapped_name == NULL) {
1135 stream_name = synthetic_smb_fname(talloc_tos(),
1136 smb_fname->base_name,
1140 TALLOC_FREE(mapped_name);
1141 if (stream_name == NULL) {
1142 DBG_ERR("synthetic_smb_fname failed\n");
1147 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1149 status = SMB_VFS_CREATE_FILE(
1150 ad->ad_handle->conn, /* conn */
1152 0, /* root_dir_fid */
1153 stream_name, /* fname */
1154 FILE_GENERIC_WRITE, /* access_mask */
1155 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
1156 FILE_OPEN_IF, /* create_disposition */
1157 0, /* create_options */
1158 0, /* file_attributes */
1159 INTERNAL_OPEN_ONLY, /* oplock_request */
1161 0, /* allocation_size */
1162 0, /* private_flags */
1167 NULL, NULL); /* create context */
1168 TALLOC_FREE(stream_name);
1169 if (!NT_STATUS_IS_OK(status)) {
1170 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1175 nwritten = SMB_VFS_PWRITE(fsp,
1176 map + e->adx_offset,
1179 if (nwritten == -1) {
1180 DBG_ERR("SMB_VFS_PWRITE failed\n");
1181 saved_errno = errno;
1182 close_file(NULL, fsp, ERROR_CLOSE);
1183 errno = saved_errno;
1188 status = close_file(NULL, fsp, NORMAL_CLOSE);
1189 if (!NT_STATUS_IS_OK(status)) {
1196 ad_setentrylen(ad, ADEID_FINDERI, ADEDLEN_FINDERI);
1200 DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name);
1204 len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
1205 if (len != AD_DATASZ_DOT_UND) {
1206 DBG_ERR("%s: bad size: %zd\n", smb_fname->base_name, len);
1211 ok = ad_convert_move_reso(ad, smb_fname);
1216 *converted_xattr = true;
1220 rc = munmap(map, maplen);
1222 DBG_ERR("munmap failed: %s\n", strerror(errno));
1229 static bool ad_convert_finderinfo(struct adouble *ad,
1230 const struct smb_filename *smb_fname)
1235 struct smb_filename *stream_name = NULL;
1236 files_struct *fsp = NULL;
1240 int saved_errno = 0;
1243 cmp = memcmp(ad->ad_filler, AD_FILLER_TAG_OSX, ADEDLEN_FILLER);
1248 p_ad = ad_get_entry(ad, ADEID_FINDERI);
1253 ai = afpinfo_new(talloc_tos());
1258 memcpy(ai->afpi_FinderInfo, p_ad, ADEDLEN_FINDERI);
1260 aiblob = data_blob_talloc(talloc_tos(), NULL, AFP_INFO_SIZE);
1261 if (aiblob.data == NULL) {
1266 size = afpinfo_pack(ai, (char *)aiblob.data);
1268 if (size != AFP_INFO_SIZE) {
1272 stream_name = synthetic_smb_fname(talloc_tos(),
1273 smb_fname->base_name,
1277 if (stream_name == NULL) {
1278 data_blob_free(&aiblob);
1279 DBG_ERR("synthetic_smb_fname failed\n");
1283 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1285 status = SMB_VFS_CREATE_FILE(
1286 ad->ad_handle->conn, /* conn */
1288 0, /* root_dir_fid */
1289 stream_name, /* fname */
1290 FILE_GENERIC_WRITE, /* access_mask */
1291 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
1292 FILE_OPEN_IF, /* create_disposition */
1293 0, /* create_options */
1294 0, /* file_attributes */
1295 INTERNAL_OPEN_ONLY, /* oplock_request */
1297 0, /* allocation_size */
1298 0, /* private_flags */
1303 NULL, NULL); /* create context */
1304 TALLOC_FREE(stream_name);
1305 if (!NT_STATUS_IS_OK(status)) {
1306 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1310 nwritten = SMB_VFS_PWRITE(fsp,
1314 if (nwritten == -1) {
1315 DBG_ERR("SMB_VFS_PWRITE failed\n");
1316 saved_errno = errno;
1317 close_file(NULL, fsp, ERROR_CLOSE);
1318 errno = saved_errno;
1322 status = close_file(NULL, fsp, NORMAL_CLOSE);
1323 if (!NT_STATUS_IS_OK(status)) {
1331 static bool ad_convert_truncate(struct adouble *ad,
1332 const struct smb_filename *smb_fname)
1337 * FIXME: direct ftruncate(), but we don't have a fsp for the
1340 rc = ftruncate(ad->ad_fd, ADEDOFF_RFORK_DOT_UND +
1341 ad_getentrylen(ad, ADEID_RFORK));
1349 static bool ad_convert_blank_rfork(struct adouble *ad,
1352 struct fruit_config_data *config = NULL;
1353 uint8_t *map = MAP_FAILED;
1362 SMB_VFS_HANDLE_GET_DATA(ad->ad_handle, config,
1363 struct fruit_config_data, return false);
1365 if (!config->wipe_intentionally_left_blank_rfork) {
1369 if (ad_getentrylen(ad, ADEID_RFORK) != sizeof(empty_resourcefork)) {
1373 maplen = ad_getentryoff(ad, ADEID_RFORK) +
1374 ad_getentrylen(ad, ADEID_RFORK);
1376 /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
1377 map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
1379 if (map == MAP_FAILED) {
1380 DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
1384 cmp = memcmp(map + ADEDOFF_RFORK_DOT_UND,
1386 sizeof(empty_resourcefork));
1387 rc = munmap(map, maplen);
1389 DBG_ERR("munmap failed: %s\n", strerror(errno));
1397 ad_setentrylen(ad, ADEID_RFORK, 0);
1404 len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
1405 if (len != AD_DATASZ_DOT_UND) {
1413 static bool ad_convert_delete_adfile(struct adouble *ad,
1414 const struct smb_filename *smb_fname)
1416 struct fruit_config_data *config = NULL;
1417 struct smb_filename *ad_name = NULL;
1420 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
1424 SMB_VFS_HANDLE_GET_DATA(ad->ad_handle, config,
1425 struct fruit_config_data, return false);
1427 if (!config->delete_empty_adfiles) {
1431 rc = adouble_path(talloc_tos(), smb_fname, &ad_name);
1436 rc = SMB_VFS_NEXT_UNLINK(ad->ad_handle, ad_name);
1438 DBG_ERR("Unlinking [%s] failed: %s\n",
1439 smb_fname_str_dbg(ad_name), strerror(errno));
1440 TALLOC_FREE(ad_name);
1444 DBG_WARNING("Unlinked [%s] after conversion\n", smb_fname_str_dbg(ad_name));
1445 TALLOC_FREE(ad_name);
1451 * Convert from Apple's ._ file to Netatalk
1453 * Apple's AppleDouble may contain a FinderInfo entry longer then 32
1454 * bytes containing packed xattrs.
1456 * @return -1 in case an error occurred, 0 if no conversion was done, 1
1459 static int ad_convert(struct vfs_handle_struct *handle,
1460 const struct smb_filename *smb_fname)
1462 struct adouble *ad = NULL;
1464 bool converted_xattr = false;
1468 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
1473 ok = ad_convert_xattr(ad, smb_fname, &converted_xattr);
1479 ok = ad_convert_blank_rfork(ad, &blank);
1485 if (converted_xattr || blank) {
1486 ok = ad_convert_truncate(ad, smb_fname);
1493 ok = ad_convert_finderinfo(ad, smb_fname);
1495 DBG_ERR("Failed to convert [%s]\n",
1496 smb_fname_str_dbg(smb_fname));
1501 ok = ad_convert_delete_adfile(ad, smb_fname);
1514 * Read and parse Netatalk AppleDouble metadata xattr
1516 static ssize_t ad_read_meta(struct adouble *ad,
1517 const struct smb_filename *smb_fname)
1523 DEBUG(10, ("reading meta xattr for %s\n", smb_fname->base_name));
1525 ealen = SMB_VFS_GETXATTR(ad->ad_handle->conn, smb_fname,
1526 AFPINFO_EA_NETATALK, ad->ad_data,
1532 if (errno == ENOATTR) {
1538 DEBUG(2, ("error reading meta xattr: %s\n",
1544 if (ealen != AD_DATASZ_XATTR) {
1545 DEBUG(2, ("bad size %zd\n", ealen));
1551 /* Now parse entries */
1552 ok = ad_unpack(ad, ADEID_NUM_XATTR, AD_DATASZ_XATTR);
1554 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1560 if (!ad_getentryoff(ad, ADEID_FINDERI)
1561 || !ad_getentryoff(ad, ADEID_COMMENT)
1562 || !ad_getentryoff(ad, ADEID_FILEDATESI)
1563 || !ad_getentryoff(ad, ADEID_AFPFILEI)
1564 || !ad_getentryoff(ad, ADEID_PRIVDEV)
1565 || !ad_getentryoff(ad, ADEID_PRIVINO)
1566 || !ad_getentryoff(ad, ADEID_PRIVSYN)
1567 || !ad_getentryoff(ad, ADEID_PRIVID)) {
1568 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1575 DEBUG(10, ("reading meta xattr for %s, rc: %d\n",
1576 smb_fname->base_name, rc));
1580 if (errno == EINVAL) {
1582 removexattr(smb_fname->base_name, AFPINFO_EA_NETATALK);
1590 static int ad_open_rsrc_xattr(const struct smb_filename *smb_fname,
1594 #ifdef HAVE_ATTROPEN
1595 /* FIXME: direct Solaris xattr syscall */
1596 return attropen(smb_fname->base_name,
1597 AFPRESOURCE_EA_NETATALK, flags, mode);
1604 static int ad_open_rsrc_adouble(const struct smb_filename *smb_fname,
1610 struct smb_filename *adp_smb_fname = NULL;
1612 ret = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
1617 fd = open(adp_smb_fname->base_name, flags, mode);
1618 TALLOC_FREE(adp_smb_fname);
1623 static int ad_open_rsrc(vfs_handle_struct *handle,
1624 const struct smb_filename *smb_fname,
1628 struct fruit_config_data *config = NULL;
1631 SMB_VFS_HANDLE_GET_DATA(handle, config,
1632 struct fruit_config_data, return -1);
1634 if (config->rsrc == FRUIT_RSRC_XATTR) {
1635 fd = ad_open_rsrc_xattr(smb_fname, flags, mode);
1637 fd = ad_open_rsrc_adouble(smb_fname, flags, mode);
1644 * Here's the deal: for ADOUBLE_META we can do without an fd as we can issue
1645 * path based xattr calls. For ADOUBLE_RSRC however we need a full-fledged fd
1646 * for file IO on the ._ file.
1648 static int ad_open(vfs_handle_struct *handle,
1651 const struct smb_filename *smb_fname,
1657 DBG_DEBUG("Path [%s] type [%s]\n", smb_fname->base_name,
1658 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
1660 if (ad->ad_type == ADOUBLE_META) {
1664 if ((fsp != NULL) && (fsp->fh != NULL) && (fsp->fh->fd != -1)) {
1665 ad->ad_fd = fsp->fh->fd;
1666 ad->ad_opened = false;
1670 fd = ad_open_rsrc(handle, smb_fname, flags, mode);
1674 ad->ad_opened = true;
1677 DBG_DEBUG("Path [%s] type [%s] fd [%d]\n",
1678 smb_fname->base_name,
1679 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc", fd);
1684 static ssize_t ad_read_rsrc_xattr(struct adouble *ad)
1689 /* FIXME: direct sys_fstat(), don't have an fsp */
1690 ret = sys_fstat(ad->ad_fd, &st,
1691 lp_fake_directory_create_times(
1692 SNUM(ad->ad_handle->conn)));
1697 ad_setentrylen(ad, ADEID_RFORK, st.st_ex_size);
1698 return st.st_ex_size;
1701 static ssize_t ad_read_rsrc_adouble(struct adouble *ad,
1702 const struct smb_filename *smb_fname)
1704 SMB_STRUCT_STAT sbuf;
1711 ret = sys_fstat(ad->ad_fd, &sbuf, lp_fake_directory_create_times(
1712 SNUM(ad->ad_handle->conn)));
1718 * AppleDouble file header content and size, two cases:
1720 * - without xattrs it is exactly AD_DATASZ_DOT_UND (82) bytes large
1721 * - with embedded xattrs it can be larger, up to AD_XATTR_MAX_HDR_SIZE
1723 * Read as much as we can up to AD_XATTR_MAX_HDR_SIZE.
1725 size = sbuf.st_ex_size;
1726 if (size > talloc_array_length(ad->ad_data)) {
1727 if (size > AD_XATTR_MAX_HDR_SIZE) {
1728 size = AD_XATTR_MAX_HDR_SIZE;
1730 p_ad = talloc_realloc(ad, ad->ad_data, char, size);
1737 len = sys_pread(ad->ad_fd, ad->ad_data,
1738 talloc_array_length(ad->ad_data), 0);
1739 if (len != talloc_array_length(ad->ad_data)) {
1740 DBG_NOTICE("%s %s: bad size: %zd\n",
1741 smb_fname->base_name, strerror(errno), len);
1745 /* Now parse entries */
1746 ok = ad_unpack(ad, ADEID_NUM_DOT_UND, sbuf.st_ex_size);
1748 DBG_ERR("invalid AppleDouble resource %s\n",
1749 smb_fname->base_name);
1754 if ((ad_getentryoff(ad, ADEID_FINDERI) != ADEDOFF_FINDERI_DOT_UND)
1755 || (ad_getentrylen(ad, ADEID_FINDERI) < ADEDLEN_FINDERI)
1756 || (ad_getentryoff(ad, ADEID_RFORK) < ADEDOFF_RFORK_DOT_UND)) {
1757 DBG_ERR("invalid AppleDouble resource %s\n",
1758 smb_fname->base_name);
1767 * Read and parse resource fork, either ._ AppleDouble file or xattr
1769 static ssize_t ad_read_rsrc(struct adouble *ad,
1770 const struct smb_filename *smb_fname)
1772 struct fruit_config_data *config = NULL;
1775 SMB_VFS_HANDLE_GET_DATA(ad->ad_handle, config,
1776 struct fruit_config_data, return -1);
1778 if (config->rsrc == FRUIT_RSRC_XATTR) {
1779 len = ad_read_rsrc_xattr(ad);
1781 len = ad_read_rsrc_adouble(ad, smb_fname);
1788 * Read and unpack an AppleDouble metadata xattr or resource
1790 static ssize_t ad_read(vfs_handle_struct *handle,
1792 const struct smb_filename *smb_fname)
1794 switch (ad->ad_type) {
1796 return ad_read_meta(ad, smb_fname);
1798 return ad_read_rsrc(ad, smb_fname);
1804 static int adouble_destructor(struct adouble *ad)
1806 if ((ad->ad_fd != -1) && ad->ad_opened) {
1814 * Allocate a struct adouble without initialiing it
1816 * The struct is either hang of the fsp extension context or if fsp is
1819 * @param[in] ctx talloc context
1820 * @param[in] handle vfs handle
1821 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1823 * @return adouble handle
1825 static struct adouble *ad_alloc(TALLOC_CTX *ctx, vfs_handle_struct *handle,
1826 adouble_type_t type)
1831 struct fruit_config_data *config;
1833 SMB_VFS_HANDLE_GET_DATA(handle, config,
1834 struct fruit_config_data, return NULL);
1838 adsize = AD_DATASZ_XATTR;
1841 if (config->rsrc == FRUIT_RSRC_ADFILE) {
1842 adsize = AD_DATASZ_DOT_UND;
1849 ad = talloc_zero(ctx, struct adouble);
1856 ad->ad_data = talloc_zero_array(ad, char, adsize);
1857 if (ad->ad_data == NULL) {
1863 ad->ad_handle = handle;
1865 ad->ad_magic = AD_MAGIC;
1866 ad->ad_version = AD_VERSION;
1869 talloc_set_destructor(ad, adouble_destructor);
1879 * Allocate and initialize a new struct adouble
1881 * @param[in] ctx talloc context
1882 * @param[in] handle vfs handle
1883 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1885 * @return adouble handle, initialized
1887 static struct adouble *ad_init(TALLOC_CTX *ctx, vfs_handle_struct *handle,
1888 adouble_type_t type)
1891 const struct ad_entry_order *eid;
1892 struct adouble *ad = NULL;
1893 struct fruit_config_data *config;
1894 time_t t = time(NULL);
1896 SMB_VFS_HANDLE_GET_DATA(handle, config,
1897 struct fruit_config_data, return NULL);
1901 eid = entry_order_meta_xattr;
1904 if (config->rsrc == FRUIT_RSRC_ADFILE) {
1905 eid = entry_order_dot_und;
1907 eid = entry_order_rsrc_xattr;
1914 ad = ad_alloc(ctx, handle, type);
1920 ad->ad_eid[eid->id].ade_off = eid->offset;
1921 ad->ad_eid[eid->id].ade_len = eid->len;
1925 /* put something sane in the date fields */
1926 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, t);
1927 ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, t);
1928 ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, t);
1929 ad_setdate(ad, AD_DATE_BACKUP, htonl(AD_DATE_START));
1937 static struct adouble *ad_get_internal(TALLOC_CTX *ctx,
1938 vfs_handle_struct *handle,
1940 const struct smb_filename *smb_fname,
1941 adouble_type_t type)
1945 struct adouble *ad = NULL;
1949 smb_fname = fsp->base_fsp->fsp_name;
1952 DEBUG(10, ("ad_get(%s) called for %s\n",
1953 type == ADOUBLE_META ? "meta" : "rsrc",
1954 smb_fname->base_name));
1956 ad = ad_alloc(ctx, handle, type);
1962 /* Try rw first so we can use the fd in ad_convert() */
1965 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
1966 if (rc == -1 && ((errno == EROFS) || (errno == EACCES))) {
1968 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
1971 DBG_DEBUG("ad_open [%s] error [%s]\n",
1972 smb_fname->base_name, strerror(errno));
1977 len = ad_read(handle, ad, smb_fname);
1979 DEBUG(10, ("error reading AppleDouble for %s\n",
1980 smb_fname->base_name));
1986 DEBUG(10, ("ad_get(%s) for %s returning %d\n",
1987 type == ADOUBLE_META ? "meta" : "rsrc",
1988 smb_fname->base_name, rc));
1997 * Return AppleDouble data for a file
1999 * @param[in] ctx talloc context
2000 * @param[in] handle vfs handle
2001 * @param[in] smb_fname pathname to file or directory
2002 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2004 * @return talloced struct adouble or NULL on error
2006 static struct adouble *ad_get(TALLOC_CTX *ctx,
2007 vfs_handle_struct *handle,
2008 const struct smb_filename *smb_fname,
2009 adouble_type_t type)
2011 return ad_get_internal(ctx, handle, NULL, smb_fname, type);
2015 * Return AppleDouble data for a file
2017 * @param[in] ctx talloc context
2018 * @param[in] handle vfs handle
2019 * @param[in] fsp fsp to use for IO
2020 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2022 * @return talloced struct adouble or NULL on error
2024 static struct adouble *ad_fget(TALLOC_CTX *ctx, vfs_handle_struct *handle,
2025 files_struct *fsp, adouble_type_t type)
2027 return ad_get_internal(ctx, handle, fsp, NULL, type);
2031 * Set AppleDouble metadata on a file or directory
2033 * @param[in] ad adouble handle
2035 * @param[in] smb_fname pathname to file or directory
2037 * @return status code, 0 means success
2039 static int ad_set(vfs_handle_struct *handle,
2041 const struct smb_filename *smb_fname)
2046 DBG_DEBUG("Path [%s]\n", smb_fname->base_name);
2048 if (ad->ad_type != ADOUBLE_META) {
2049 DBG_ERR("ad_set on [%s] used with ADOUBLE_RSRC\n",
2050 smb_fname->base_name);
2059 ret = SMB_VFS_SETXATTR(handle->conn,
2061 AFPINFO_EA_NETATALK,
2063 AD_DATASZ_XATTR, 0);
2065 DBG_DEBUG("Path [%s] ret [%d]\n", smb_fname->base_name, ret);
2071 * Set AppleDouble metadata on a file or directory
2073 * @param[in] ad adouble handle
2074 * @param[in] fsp file handle
2076 * @return status code, 0 means success
2078 static int ad_fset(struct vfs_handle_struct *handle,
2086 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
2089 || (fsp->fh == NULL)
2090 || (fsp->fh->fd == -1))
2092 smb_panic("bad fsp");
2100 switch (ad->ad_type) {
2102 rc = SMB_VFS_NEXT_SETXATTR(handle,
2104 AFPINFO_EA_NETATALK,
2106 AD_DATASZ_XATTR, 0);
2110 len = SMB_VFS_NEXT_PWRITE(handle,
2115 if (len != AD_DATASZ_DOT_UND) {
2116 DBG_ERR("short write on %s: %zd", fsp_str_dbg(fsp), len);
2126 DBG_DEBUG("Path [%s] rc [%d]\n", fsp_str_dbg(fsp), rc);
2131 /*****************************************************************************
2133 *****************************************************************************/
2135 static bool is_afpinfo_stream(const struct smb_filename *smb_fname)
2137 if (strncasecmp_m(smb_fname->stream_name,
2138 AFPINFO_STREAM_NAME,
2139 strlen(AFPINFO_STREAM_NAME)) == 0) {
2145 static bool is_afpresource_stream(const struct smb_filename *smb_fname)
2147 if (strncasecmp_m(smb_fname->stream_name,
2148 AFPRESOURCE_STREAM_NAME,
2149 strlen(AFPRESOURCE_STREAM_NAME)) == 0) {
2156 * Test whether stream is an Apple stream.
2158 static bool is_apple_stream(const struct smb_filename *smb_fname)
2160 if (is_afpinfo_stream(smb_fname)) {
2163 if (is_afpresource_stream(smb_fname)) {
2170 * Initialize config struct from our smb.conf config parameters
2172 static int init_fruit_config(vfs_handle_struct *handle)
2174 struct fruit_config_data *config;
2176 const char *tm_size_str = NULL;
2178 config = talloc_zero(handle->conn, struct fruit_config_data);
2180 DEBUG(1, ("talloc_zero() failed\n"));
2186 * Versions up to Samba 4.5.x had a spelling bug in the
2187 * fruit:resource option calling lp_parm_enum with
2188 * "res*s*ource" (ie two s).
2190 * In Samba 4.6 we accept both the wrong and the correct
2191 * spelling, in Samba 4.7 the bad spelling will be removed.
2193 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2194 "ressource", fruit_rsrc, FRUIT_RSRC_ADFILE);
2195 if (enumval == -1) {
2196 DEBUG(1, ("value for %s: resource type unknown\n",
2197 FRUIT_PARAM_TYPE_NAME));
2200 config->rsrc = (enum fruit_rsrc)enumval;
2202 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2203 "resource", fruit_rsrc, enumval);
2204 if (enumval == -1) {
2205 DEBUG(1, ("value for %s: resource type unknown\n",
2206 FRUIT_PARAM_TYPE_NAME));
2209 config->rsrc = (enum fruit_rsrc)enumval;
2211 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2212 "metadata", fruit_meta, FRUIT_META_NETATALK);
2213 if (enumval == -1) {
2214 DEBUG(1, ("value for %s: metadata type unknown\n",
2215 FRUIT_PARAM_TYPE_NAME));
2218 config->meta = (enum fruit_meta)enumval;
2220 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2221 "locking", fruit_locking, FRUIT_LOCKING_NONE);
2222 if (enumval == -1) {
2223 DEBUG(1, ("value for %s: locking type unknown\n",
2224 FRUIT_PARAM_TYPE_NAME));
2227 config->locking = (enum fruit_locking)enumval;
2229 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2230 "encoding", fruit_encoding, FRUIT_ENC_PRIVATE);
2231 if (enumval == -1) {
2232 DEBUG(1, ("value for %s: encoding type unknown\n",
2233 FRUIT_PARAM_TYPE_NAME));
2236 config->encoding = (enum fruit_encoding)enumval;
2238 if (config->rsrc == FRUIT_RSRC_ADFILE) {
2239 config->veto_appledouble = lp_parm_bool(SNUM(handle->conn),
2240 FRUIT_PARAM_TYPE_NAME,
2245 config->use_aapl = lp_parm_bool(
2246 -1, FRUIT_PARAM_TYPE_NAME, "aapl", true);
2248 config->time_machine = lp_parm_bool(
2249 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "time machine", false);
2251 config->unix_info_enabled = lp_parm_bool(
2252 -1, FRUIT_PARAM_TYPE_NAME, "nfs_aces", true);
2254 config->use_copyfile = lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME,
2257 config->posix_rename = lp_parm_bool(
2258 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "posix_rename", true);
2260 config->aapl_zero_file_id =
2261 lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME, "zero_file_id", true);
2263 config->readdir_attr_rsize = lp_parm_bool(
2264 SNUM(handle->conn), "readdir_attr", "aapl_rsize", true);
2266 config->readdir_attr_finder_info = lp_parm_bool(
2267 SNUM(handle->conn), "readdir_attr", "aapl_finder_info", true);
2269 config->readdir_attr_max_access = lp_parm_bool(
2270 SNUM(handle->conn), "readdir_attr", "aapl_max_access", true);
2272 config->model = lp_parm_const_string(
2273 -1, FRUIT_PARAM_TYPE_NAME, "model", "MacSamba");
2275 tm_size_str = lp_parm_const_string(
2276 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2277 "time machine max size", NULL);
2278 if (tm_size_str != NULL) {
2279 config->time_machine_max_size = conv_str_size(tm_size_str);
2282 config->wipe_intentionally_left_blank_rfork = lp_parm_bool(
2283 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2284 "wipe_intentionally_left_blank_rfork", false);
2286 config->delete_empty_adfiles = lp_parm_bool(
2287 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2288 "delete_empty_adfiles", false);
2290 SMB_VFS_HANDLE_SET_DATA(handle, config,
2291 NULL, struct fruit_config_data,
2298 * Prepend "._" to a basename
2299 * Return a new struct smb_filename with stream_name == NULL.
2301 static int adouble_path(TALLOC_CTX *ctx,
2302 const struct smb_filename *smb_fname_in,
2303 struct smb_filename **pp_smb_fname_out)
2307 struct smb_filename *smb_fname = cp_smb_filename(ctx,
2310 if (smb_fname == NULL) {
2314 /* We need streamname to be NULL */
2315 TALLOC_FREE(smb_fname->stream_name);
2317 /* And we're replacing base_name. */
2318 TALLOC_FREE(smb_fname->base_name);
2320 if (!parent_dirname(smb_fname, smb_fname_in->base_name,
2322 TALLOC_FREE(smb_fname);
2326 smb_fname->base_name = talloc_asprintf(smb_fname,
2327 "%s/._%s", parent, base);
2328 if (smb_fname->base_name == NULL) {
2329 TALLOC_FREE(smb_fname);
2333 *pp_smb_fname_out = smb_fname;
2339 * Allocate and initialize an AfpInfo struct
2341 static AfpInfo *afpinfo_new(TALLOC_CTX *ctx)
2343 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2347 ai->afpi_Signature = AFP_Signature;
2348 ai->afpi_Version = AFP_Version;
2349 ai->afpi_BackupTime = AD_DATE_START;
2354 * Pack an AfpInfo struct into a buffer
2356 * Buffer size must be at least AFP_INFO_SIZE
2357 * Returns size of packed buffer
2359 static ssize_t afpinfo_pack(const AfpInfo *ai, char *buf)
2361 memset(buf, 0, AFP_INFO_SIZE);
2363 RSIVAL(buf, 0, ai->afpi_Signature);
2364 RSIVAL(buf, 4, ai->afpi_Version);
2365 RSIVAL(buf, 12, ai->afpi_BackupTime);
2366 memcpy(buf + 16, ai->afpi_FinderInfo, sizeof(ai->afpi_FinderInfo));
2368 return AFP_INFO_SIZE;
2372 * Unpack a buffer into a AfpInfo structure
2374 * Buffer size must be at least AFP_INFO_SIZE
2375 * Returns allocated AfpInfo struct
2377 static AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data)
2379 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2384 ai->afpi_Signature = RIVAL(data, 0);
2385 ai->afpi_Version = RIVAL(data, 4);
2386 ai->afpi_BackupTime = RIVAL(data, 12);
2387 memcpy(ai->afpi_FinderInfo, (const char *)data + 16,
2388 sizeof(ai->afpi_FinderInfo));
2390 if (ai->afpi_Signature != AFP_Signature
2391 || ai->afpi_Version != AFP_Version) {
2392 DEBUG(1, ("Bad AfpInfo signature or version\n"));
2400 * Fake an inode number from the md5 hash of the (xattr) name
2402 static SMB_INO_T fruit_inode(const SMB_STRUCT_STAT *sbuf, const char *sname)
2404 gnutls_hash_hd_t hash_hnd = NULL;
2405 unsigned char hash[16];
2406 SMB_INO_T result = 0;
2410 DBG_DEBUG("fruit_inode called for %ju/%ju [%s]\n",
2411 (uintmax_t)sbuf->st_ex_dev,
2412 (uintmax_t)sbuf->st_ex_ino, sname);
2414 upper_sname = talloc_strdup_upper(talloc_tos(), sname);
2415 SMB_ASSERT(upper_sname != NULL);
2417 rc = gnutls_hash_init(&hash_hnd, GNUTLS_DIG_MD5);
2422 rc = gnutls_hash(hash_hnd, &(sbuf->st_ex_dev), sizeof(sbuf->st_ex_dev));
2424 gnutls_hash_deinit(hash_hnd, NULL);
2427 rc = gnutls_hash(hash_hnd,
2429 sizeof(sbuf->st_ex_ino));
2431 gnutls_hash_deinit(hash_hnd, NULL);
2434 rc = gnutls_hash(hash_hnd,
2436 talloc_get_size(upper_sname) - 1);
2438 gnutls_hash_deinit(hash_hnd, NULL);
2442 gnutls_hash_deinit(hash_hnd, hash);
2444 /* Hopefully all the variation is in the lower 4 (or 8) bytes! */
2445 memcpy(&result, hash, sizeof(result));
2448 DBG_DEBUG("fruit_inode \"%s\": ino=%ju\n",
2449 sname, (uintmax_t)result);
2452 TALLOC_FREE(upper_sname);
2457 static bool add_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
2458 struct stream_struct **streams,
2459 const char *name, off_t size,
2462 struct stream_struct *tmp;
2464 tmp = talloc_realloc(mem_ctx, *streams, struct stream_struct,
2470 tmp[*num_streams].name = talloc_asprintf(tmp, "%s:$DATA", name);
2471 if (tmp[*num_streams].name == NULL) {
2475 tmp[*num_streams].size = size;
2476 tmp[*num_streams].alloc_size = alloc_size;
2483 static bool filter_empty_rsrc_stream(unsigned int *num_streams,
2484 struct stream_struct **streams)
2486 struct stream_struct *tmp = *streams;
2489 if (*num_streams == 0) {
2493 for (i = 0; i < *num_streams; i++) {
2494 if (strequal_m(tmp[i].name, AFPRESOURCE_STREAM)) {
2499 if (i == *num_streams) {
2503 if (tmp[i].size > 0) {
2507 TALLOC_FREE(tmp[i].name);
2508 if (*num_streams - 1 > i) {
2509 memmove(&tmp[i], &tmp[i+1],
2510 (*num_streams - i - 1) * sizeof(struct stream_struct));
2517 static bool del_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
2518 struct stream_struct **streams,
2521 struct stream_struct *tmp = *streams;
2524 if (*num_streams == 0) {
2528 for (i = 0; i < *num_streams; i++) {
2529 if (strequal_m(tmp[i].name, name)) {
2534 if (i == *num_streams) {
2538 TALLOC_FREE(tmp[i].name);
2539 if (*num_streams - 1 > i) {
2540 memmove(&tmp[i], &tmp[i+1],
2541 (*num_streams - i - 1) * sizeof(struct stream_struct));
2548 static bool ad_empty_finderinfo(const struct adouble *ad)
2551 char emptybuf[ADEDLEN_FINDERI] = {0};
2554 fi = ad_get_entry(ad, ADEID_FINDERI);
2556 DBG_ERR("Missing FinderInfo in struct adouble [%p]\n", ad);
2560 cmp = memcmp(emptybuf, fi, ADEDLEN_FINDERI);
2564 static bool ai_empty_finderinfo(const AfpInfo *ai)
2567 char emptybuf[ADEDLEN_FINDERI] = {0};
2569 cmp = memcmp(emptybuf, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
2574 * Update btime with btime from Netatalk
2576 static void update_btime(vfs_handle_struct *handle,
2577 struct smb_filename *smb_fname)
2580 struct timespec creation_time = {0};
2582 struct fruit_config_data *config = NULL;
2584 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
2587 switch (config->meta) {
2588 case FRUIT_META_STREAM:
2590 case FRUIT_META_NETATALK:
2594 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
2598 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
2602 if (ad_getdate(ad, AD_DATE_UNIX | AD_DATE_CREATE, &t) != 0) {
2608 creation_time.tv_sec = convert_uint32_t_to_time_t(t);
2609 update_stat_ex_create_time(&smb_fname->st, creation_time);
2615 * Map an access mask to a Netatalk single byte byte range lock
2617 static off_t access_to_netatalk_brl(enum apple_fork fork_type,
2618 uint32_t access_mask)
2622 switch (access_mask) {
2623 case FILE_READ_DATA:
2624 offset = AD_FILELOCK_OPEN_RD;
2627 case FILE_WRITE_DATA:
2628 case FILE_APPEND_DATA:
2629 offset = AD_FILELOCK_OPEN_WR;
2633 offset = AD_FILELOCK_OPEN_NONE;
2637 if (fork_type == APPLE_FORK_RSRC) {
2638 if (offset == AD_FILELOCK_OPEN_NONE) {
2639 offset = AD_FILELOCK_RSRC_OPEN_NONE;
2649 * Map a deny mode to a Netatalk brl
2651 static off_t denymode_to_netatalk_brl(enum apple_fork fork_type,
2656 switch (deny_mode) {
2658 offset = AD_FILELOCK_DENY_RD;
2662 offset = AD_FILELOCK_DENY_WR;
2666 smb_panic("denymode_to_netatalk_brl: bad deny mode\n");
2669 if (fork_type == APPLE_FORK_RSRC) {
2677 * Call fcntl() with an exclusive F_GETLK request in order to
2678 * determine if there's an exisiting shared lock
2680 * @return true if the requested lock was found or any error occurred
2681 * false if the lock was not found
2683 static bool test_netatalk_lock(files_struct *fsp, off_t in_offset)
2686 off_t offset = in_offset;
2691 result = SMB_VFS_GETLOCK(fsp, &offset, &len, &type, &pid);
2692 if (result == false) {
2696 if (type != F_UNLCK) {
2703 static NTSTATUS fruit_check_access(vfs_handle_struct *handle,
2705 uint32_t access_mask,
2706 uint32_t share_mode)
2708 NTSTATUS status = NT_STATUS_OK;
2710 bool share_for_read = (share_mode & FILE_SHARE_READ);
2711 bool share_for_write = (share_mode & FILE_SHARE_WRITE);
2712 bool netatalk_already_open_for_reading = false;
2713 bool netatalk_already_open_for_writing = false;
2714 bool netatalk_already_open_with_deny_read = false;
2715 bool netatalk_already_open_with_deny_write = false;
2717 /* FIXME: hardcoded data fork, add resource fork */
2718 enum apple_fork fork_type = APPLE_FORK_DATA;
2720 DBG_DEBUG("fruit_check_access: %s, am: %s/%s, sm: 0x%x\n",
2722 access_mask & FILE_READ_DATA ? "READ" :"-",
2723 access_mask & FILE_WRITE_DATA ? "WRITE" : "-",
2726 if (fsp->fh->fd == -1) {
2727 return NT_STATUS_OK;
2730 /* Read NetATalk opens and deny modes on the file. */
2731 netatalk_already_open_for_reading = test_netatalk_lock(fsp,
2732 access_to_netatalk_brl(fork_type,
2735 netatalk_already_open_with_deny_read = test_netatalk_lock(fsp,
2736 denymode_to_netatalk_brl(fork_type,
2739 netatalk_already_open_for_writing = test_netatalk_lock(fsp,
2740 access_to_netatalk_brl(fork_type,
2743 netatalk_already_open_with_deny_write = test_netatalk_lock(fsp,
2744 denymode_to_netatalk_brl(fork_type,
2747 /* If there are any conflicts - sharing violation. */
2748 if ((access_mask & FILE_READ_DATA) &&
2749 netatalk_already_open_with_deny_read) {
2750 return NT_STATUS_SHARING_VIOLATION;
2753 if (!share_for_read &&
2754 netatalk_already_open_for_reading) {
2755 return NT_STATUS_SHARING_VIOLATION;
2758 if ((access_mask & FILE_WRITE_DATA) &&
2759 netatalk_already_open_with_deny_write) {
2760 return NT_STATUS_SHARING_VIOLATION;
2763 if (!share_for_write &&
2764 netatalk_already_open_for_writing) {
2765 return NT_STATUS_SHARING_VIOLATION;
2768 if (!(access_mask & FILE_READ_DATA)) {
2770 * Nothing we can do here, we need read access
2773 return NT_STATUS_OK;
2776 /* Set NetAtalk locks matching our access */
2777 if (access_mask & FILE_READ_DATA) {
2778 struct byte_range_lock *br_lck = NULL;
2780 off = access_to_netatalk_brl(fork_type, FILE_READ_DATA);
2782 handle->conn->sconn->msg_ctx, fsp,
2783 fsp->op->global->open_persistent_id, 1, off,
2784 READ_LOCK, POSIX_LOCK, false,
2787 TALLOC_FREE(br_lck);
2789 if (!NT_STATUS_IS_OK(status)) {
2794 if (!share_for_read) {
2795 struct byte_range_lock *br_lck = NULL;
2797 off = denymode_to_netatalk_brl(fork_type, DENY_READ);
2799 handle->conn->sconn->msg_ctx, fsp,
2800 fsp->op->global->open_persistent_id, 1, off,
2801 READ_LOCK, POSIX_LOCK, false,
2804 TALLOC_FREE(br_lck);
2806 if (!NT_STATUS_IS_OK(status)) {
2811 if (access_mask & FILE_WRITE_DATA) {
2812 struct byte_range_lock *br_lck = NULL;
2814 off = access_to_netatalk_brl(fork_type, FILE_WRITE_DATA);
2816 handle->conn->sconn->msg_ctx, fsp,
2817 fsp->op->global->open_persistent_id, 1, off,
2818 READ_LOCK, POSIX_LOCK, false,
2821 TALLOC_FREE(br_lck);
2823 if (!NT_STATUS_IS_OK(status)) {
2828 if (!share_for_write) {
2829 struct byte_range_lock *br_lck = NULL;
2831 off = denymode_to_netatalk_brl(fork_type, DENY_WRITE);
2833 handle->conn->sconn->msg_ctx, fsp,
2834 fsp->op->global->open_persistent_id, 1, off,
2835 READ_LOCK, POSIX_LOCK, false,
2838 TALLOC_FREE(br_lck);
2840 if (!NT_STATUS_IS_OK(status)) {
2845 return NT_STATUS_OK;
2848 static NTSTATUS check_aapl(vfs_handle_struct *handle,
2849 struct smb_request *req,
2850 const struct smb2_create_blobs *in_context_blobs,
2851 struct smb2_create_blobs *out_context_blobs)
2853 struct fruit_config_data *config;
2855 struct smb2_create_blob *aapl = NULL;
2859 DATA_BLOB blob = data_blob_talloc(req, NULL, 0);
2860 uint64_t req_bitmap, client_caps;
2861 uint64_t server_caps = SMB2_CRTCTX_AAPL_UNIX_BASED;
2865 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
2866 return NT_STATUS_UNSUCCESSFUL);
2868 if (!config->use_aapl
2869 || in_context_blobs == NULL
2870 || out_context_blobs == NULL) {
2871 return NT_STATUS_OK;
2874 aapl = smb2_create_blob_find(in_context_blobs,
2875 SMB2_CREATE_TAG_AAPL);
2877 return NT_STATUS_OK;
2880 if (aapl->data.length != 24) {
2881 DEBUG(1, ("unexpected AAPL ctxt length: %ju\n",
2882 (uintmax_t)aapl->data.length));
2883 return NT_STATUS_INVALID_PARAMETER;
2886 cmd = IVAL(aapl->data.data, 0);
2887 if (cmd != SMB2_CRTCTX_AAPL_SERVER_QUERY) {
2888 DEBUG(1, ("unsupported AAPL cmd: %d\n", cmd));
2889 return NT_STATUS_INVALID_PARAMETER;
2892 req_bitmap = BVAL(aapl->data.data, 8);
2893 client_caps = BVAL(aapl->data.data, 16);
2895 SIVAL(p, 0, SMB2_CRTCTX_AAPL_SERVER_QUERY);
2897 SBVAL(p, 8, req_bitmap);
2898 ok = data_blob_append(req, &blob, p, 16);
2900 return NT_STATUS_UNSUCCESSFUL;
2903 if (req_bitmap & SMB2_CRTCTX_AAPL_SERVER_CAPS) {
2904 if ((client_caps & SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR) &&
2905 (handle->conn->tcon->compat->fs_capabilities & FILE_NAMED_STREAMS)) {
2906 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR;
2907 config->readdir_attr_enabled = true;
2910 if (config->use_copyfile) {
2911 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_OSX_COPYFILE;
2912 config->copyfile_enabled = true;
2916 * The client doesn't set the flag, so we can't check
2917 * for it and just set it unconditionally
2919 if (config->unix_info_enabled) {
2920 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_NFS_ACE;
2923 SBVAL(p, 0, server_caps);
2924 ok = data_blob_append(req, &blob, p, 8);
2926 return NT_STATUS_UNSUCCESSFUL;
2930 if (req_bitmap & SMB2_CRTCTX_AAPL_VOLUME_CAPS) {
2931 int val = lp_case_sensitive(SNUM(handle->conn->tcon->compat));
2939 caps |= SMB2_CRTCTX_AAPL_CASE_SENSITIVE;
2946 if (config->time_machine) {
2947 caps |= SMB2_CRTCTX_AAPL_FULL_SYNC;
2952 ok = data_blob_append(req, &blob, p, 8);
2954 return NT_STATUS_UNSUCCESSFUL;
2958 if (req_bitmap & SMB2_CRTCTX_AAPL_MODEL_INFO) {
2959 ok = convert_string_talloc(req,
2960 CH_UNIX, CH_UTF16LE,
2961 config->model, strlen(config->model),
2964 return NT_STATUS_UNSUCCESSFUL;
2968 SIVAL(p + 4, 0, modellen);
2969 ok = data_blob_append(req, &blob, p, 8);
2972 return NT_STATUS_UNSUCCESSFUL;
2975 ok = data_blob_append(req, &blob, model, modellen);
2978 return NT_STATUS_UNSUCCESSFUL;
2982 status = smb2_create_blob_add(out_context_blobs,
2984 SMB2_CREATE_TAG_AAPL,
2986 if (NT_STATUS_IS_OK(status)) {
2987 global_fruit_config.nego_aapl = true;
2988 if (config->aapl_zero_file_id) {
2989 aapl_force_zero_file_id(handle->conn->sconn);
2996 static bool readdir_attr_meta_finderi_stream(
2997 struct vfs_handle_struct *handle,
2998 const struct smb_filename *smb_fname,
3001 struct smb_filename *stream_name = NULL;
3002 files_struct *fsp = NULL;
3007 uint8_t buf[AFP_INFO_SIZE];
3009 stream_name = synthetic_smb_fname(talloc_tos(),
3010 smb_fname->base_name,
3011 AFPINFO_STREAM_NAME,
3012 NULL, smb_fname->flags);
3013 if (stream_name == NULL) {
3017 ret = SMB_VFS_STAT(handle->conn, stream_name);
3022 status = SMB_VFS_CREATE_FILE(
3023 handle->conn, /* conn */
3025 0, /* root_dir_fid */
3026 stream_name, /* fname */
3027 FILE_READ_DATA, /* access_mask */
3028 (FILE_SHARE_READ | FILE_SHARE_WRITE | /* share_access */
3030 FILE_OPEN, /* create_disposition*/
3031 0, /* create_options */
3032 0, /* file_attributes */
3033 INTERNAL_OPEN_ONLY, /* oplock_request */
3035 0, /* allocation_size */
3036 0, /* private_flags */
3041 NULL, NULL); /* create context */
3043 TALLOC_FREE(stream_name);
3045 if (!NT_STATUS_IS_OK(status)) {
3049 nread = SMB_VFS_PREAD(fsp, &buf[0], AFP_INFO_SIZE, 0);
3050 if (nread != AFP_INFO_SIZE) {
3051 DBG_ERR("short read [%s] [%zd/%d]\n",
3052 smb_fname_str_dbg(stream_name), nread, AFP_INFO_SIZE);
3057 memcpy(&ai->afpi_FinderInfo[0], &buf[AFP_OFF_FinderInfo],
3064 close_file(NULL, fsp, NORMAL_CLOSE);
3070 static bool readdir_attr_meta_finderi_netatalk(
3071 struct vfs_handle_struct *handle,
3072 const struct smb_filename *smb_fname,
3075 struct adouble *ad = NULL;
3078 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
3083 p = ad_get_entry(ad, ADEID_FINDERI);
3085 DBG_ERR("No ADEID_FINDERI for [%s]\n", smb_fname->base_name);
3090 memcpy(&ai->afpi_FinderInfo[0], p, AFP_FinderSize);
3095 static bool readdir_attr_meta_finderi(struct vfs_handle_struct *handle,
3096 const struct smb_filename *smb_fname,
3097 struct readdir_attr_data *attr_data)
3099 struct fruit_config_data *config = NULL;
3100 uint32_t date_added;
3104 SMB_VFS_HANDLE_GET_DATA(handle, config,
3105 struct fruit_config_data,
3108 switch (config->meta) {
3109 case FRUIT_META_NETATALK:
3110 ok = readdir_attr_meta_finderi_netatalk(
3111 handle, smb_fname, &ai);
3114 case FRUIT_META_STREAM:
3115 ok = readdir_attr_meta_finderi_stream(
3116 handle, smb_fname, &ai);
3120 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3125 /* Don't bother with errors, it's likely ENOENT */
3129 if (S_ISREG(smb_fname->st.st_ex_mode)) {
3131 memcpy(&attr_data->attr_data.aapl.finder_info[0],
3132 &ai.afpi_FinderInfo[0], 4);
3134 /* finder_creator */
3135 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 4,
3136 &ai.afpi_FinderInfo[4], 4);
3140 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 8,
3141 &ai.afpi_FinderInfo[8], 2);
3143 /* finder_ext_flags */
3144 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 10,
3145 &ai.afpi_FinderInfo[24], 2);
3148 date_added = convert_time_t_to_uint32_t(
3149 smb_fname->st.st_ex_btime.tv_sec - AD_DATE_DELTA);
3151 RSIVAL(&attr_data->attr_data.aapl.finder_info[0], 12, date_added);
3156 static uint64_t readdir_attr_rfork_size_adouble(
3157 struct vfs_handle_struct *handle,
3158 const struct smb_filename *smb_fname)
3160 struct adouble *ad = NULL;
3161 uint64_t rfork_size;
3163 ad = ad_get(talloc_tos(), handle, smb_fname,
3169 rfork_size = ad_getentrylen(ad, ADEID_RFORK);
3175 static uint64_t readdir_attr_rfork_size_stream(
3176 struct vfs_handle_struct *handle,
3177 const struct smb_filename *smb_fname)
3179 struct smb_filename *stream_name = NULL;
3181 uint64_t rfork_size;
3183 stream_name = synthetic_smb_fname(talloc_tos(),
3184 smb_fname->base_name,
3185 AFPRESOURCE_STREAM_NAME,
3187 if (stream_name == NULL) {
3191 ret = SMB_VFS_STAT(handle->conn, stream_name);
3193 TALLOC_FREE(stream_name);
3197 rfork_size = stream_name->st.st_ex_size;
3198 TALLOC_FREE(stream_name);
3203 static uint64_t readdir_attr_rfork_size(struct vfs_handle_struct *handle,
3204 const struct smb_filename *smb_fname)
3206 struct fruit_config_data *config = NULL;
3207 uint64_t rfork_size;
3209 SMB_VFS_HANDLE_GET_DATA(handle, config,
3210 struct fruit_config_data,
3213 switch (config->rsrc) {
3214 case FRUIT_RSRC_ADFILE:
3215 case FRUIT_RSRC_XATTR:
3216 rfork_size = readdir_attr_rfork_size_adouble(handle,
3220 case FRUIT_META_STREAM:
3221 rfork_size = readdir_attr_rfork_size_stream(handle,
3226 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3234 static NTSTATUS readdir_attr_macmeta(struct vfs_handle_struct *handle,
3235 const struct smb_filename *smb_fname,
3236 struct readdir_attr_data *attr_data)
3238 NTSTATUS status = NT_STATUS_OK;
3239 struct fruit_config_data *config = NULL;
3242 SMB_VFS_HANDLE_GET_DATA(handle, config,
3243 struct fruit_config_data,
3244 return NT_STATUS_UNSUCCESSFUL);
3247 /* Ensure we return a default value in the creation_date field */
3248 RSIVAL(&attr_data->attr_data.aapl.finder_info, 12, AD_DATE_START);
3251 * Resource fork length
3254 if (config->readdir_attr_rsize) {
3255 uint64_t rfork_size;
3257 rfork_size = readdir_attr_rfork_size(handle, smb_fname);
3258 attr_data->attr_data.aapl.rfork_size = rfork_size;
3265 if (config->readdir_attr_finder_info) {
3266 ok = readdir_attr_meta_finderi(handle, smb_fname, attr_data);
3268 status = NT_STATUS_INTERNAL_ERROR;
3275 static NTSTATUS remove_virtual_nfs_aces(struct security_descriptor *psd)
3280 if (psd->dacl == NULL) {
3281 return NT_STATUS_OK;
3284 for (i = 0; i < psd->dacl->num_aces; i++) {
3285 /* MS NFS style mode/uid/gid */
3286 int cmp = dom_sid_compare_domain(
3287 &global_sid_Unix_NFS,
3288 &psd->dacl->aces[i].trustee);
3290 /* Normal ACE entry. */
3295 * security_descriptor_dacl_del()
3296 * *must* return NT_STATUS_OK as we know
3297 * we have something to remove.
3300 status = security_descriptor_dacl_del(psd,
3301 &psd->dacl->aces[i].trustee);
3302 if (!NT_STATUS_IS_OK(status)) {
3303 DBG_WARNING("failed to remove MS NFS style ACE: %s\n",
3309 * security_descriptor_dacl_del() may delete more
3310 * then one entry subsequent to this one if the
3311 * SID matches, but we only need to ensure that
3312 * we stay looking at the same element in the array.
3316 return NT_STATUS_OK;
3319 /* Search MS NFS style ACE with UNIX mode */
3320 static NTSTATUS check_ms_nfs(vfs_handle_struct *handle,
3322 struct security_descriptor *psd,
3327 struct fruit_config_data *config = NULL;
3331 SMB_VFS_HANDLE_GET_DATA(handle, config,
3332 struct fruit_config_data,
3333 return NT_STATUS_UNSUCCESSFUL);
3335 if (!global_fruit_config.nego_aapl) {
3336 return NT_STATUS_OK;
3338 if (psd->dacl == NULL || !config->unix_info_enabled) {
3339 return NT_STATUS_OK;
3342 for (i = 0; i < psd->dacl->num_aces; i++) {
3343 if (dom_sid_compare_domain(
3344 &global_sid_Unix_NFS_Mode,
3345 &psd->dacl->aces[i].trustee) == 0) {
3346 *pmode = (mode_t)psd->dacl->aces[i].trustee.sub_auths[2];
3347 *pmode &= (S_IRWXU | S_IRWXG | S_IRWXO);
3350 DEBUG(10, ("MS NFS chmod request %s, %04o\n",
3351 fsp_str_dbg(fsp), (unsigned)(*pmode)));
3357 * Remove any incoming virtual ACE entries generated by
3358 * fruit_fget_nt_acl().
3361 return remove_virtual_nfs_aces(psd);
3364 /****************************************************************************
3366 ****************************************************************************/
3368 static int fruit_connect(vfs_handle_struct *handle,
3369 const char *service,
3373 char *list = NULL, *newlist = NULL;
3374 struct fruit_config_data *config;
3376 DEBUG(10, ("fruit_connect\n"));
3378 rc = SMB_VFS_NEXT_CONNECT(handle, service, user);
3383 rc = init_fruit_config(handle);
3388 SMB_VFS_HANDLE_GET_DATA(handle, config,
3389 struct fruit_config_data, return -1);
3391 if (config->veto_appledouble) {
3392 list = lp_veto_files(talloc_tos(), SNUM(handle->conn));
3395 if (strstr(list, "/" ADOUBLE_NAME_PREFIX "*/") == NULL) {
3396 newlist = talloc_asprintf(
3398 "%s/" ADOUBLE_NAME_PREFIX "*/",
3400 lp_do_parameter(SNUM(handle->conn),
3405 lp_do_parameter(SNUM(handle->conn),
3407 "/" ADOUBLE_NAME_PREFIX "*/");
3413 if (config->encoding == FRUIT_ENC_NATIVE) {
3414 lp_do_parameter(SNUM(handle->conn),
3419 if (config->time_machine) {
3420 DBG_NOTICE("Enabling durable handles for Time Machine "
3421 "support on [%s]\n", service);
3422 lp_do_parameter(SNUM(handle->conn), "durable handles", "yes");
3423 lp_do_parameter(SNUM(handle->conn), "kernel oplocks", "no");
3424 lp_do_parameter(SNUM(handle->conn), "kernel share modes", "no");
3425 if (!lp_strict_sync(SNUM(handle->conn))) {
3426 DBG_WARNING("Time Machine without strict sync is not "
3429 lp_do_parameter(SNUM(handle->conn), "posix locking", "no");
3435 static int fruit_fake_fd(void)
3442 * Return a valid fd, but ensure any attempt to use it returns
3443 * an error (EPIPE). Once we get a write on the handle, we open
3446 ret = pipe(pipe_fds);
3456 static int fruit_open_meta_stream(vfs_handle_struct *handle,
3457 struct smb_filename *smb_fname,
3462 struct fruit_config_data *config = NULL;
3463 struct fio *fio = NULL;
3464 int open_flags = flags & ~O_CREAT;
3467 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3469 SMB_VFS_HANDLE_GET_DATA(handle, config,
3470 struct fruit_config_data, return -1);
3472 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3473 fio->type = ADOUBLE_META;
3474 fio->config = config;
3476 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, open_flags, mode);
3481 if (!(flags & O_CREAT)) {
3482 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
3486 fd = fruit_fake_fd();
3488 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
3492 fio->fake_fd = true;
3499 static int fruit_open_meta_netatalk(vfs_handle_struct *handle,
3500 struct smb_filename *smb_fname,
3505 struct fruit_config_data *config = NULL;
3506 struct fio *fio = NULL;
3507 struct adouble *ad = NULL;
3508 bool meta_exists = false;
3511 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3513 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
3520 if (!meta_exists && !(flags & O_CREAT)) {
3525 fd = fruit_fake_fd();
3530 SMB_VFS_HANDLE_GET_DATA(handle, config,
3531 struct fruit_config_data, return -1);
3533 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3534 fio->type = ADOUBLE_META;
3535 fio->config = config;
3536 fio->fake_fd = true;
3543 static int fruit_open_meta(vfs_handle_struct *handle,
3544 struct smb_filename *smb_fname,
3545 files_struct *fsp, int flags, mode_t mode)
3548 struct fruit_config_data *config = NULL;
3550 DBG_DEBUG("path [%s]\n", smb_fname_str_dbg(smb_fname));
3552 SMB_VFS_HANDLE_GET_DATA(handle, config,
3553 struct fruit_config_data, return -1);
3555 switch (config->meta) {
3556 case FRUIT_META_STREAM:
3557 fd = fruit_open_meta_stream(handle, smb_fname,
3561 case FRUIT_META_NETATALK:
3562 fd = fruit_open_meta_netatalk(handle, smb_fname,
3567 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3571 DBG_DEBUG("path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3576 static int fruit_open_rsrc_adouble(vfs_handle_struct *handle,
3577 struct smb_filename *smb_fname,
3583 struct adouble *ad = NULL;
3584 struct smb_filename *smb_fname_base = NULL;
3585 struct fruit_config_data *config = NULL;
3588 SMB_VFS_HANDLE_GET_DATA(handle, config,
3589 struct fruit_config_data, return -1);
3591 if ((!(flags & O_CREAT)) &&
3592 S_ISDIR(fsp->base_fsp->fsp_name->st.st_ex_mode))
3594 /* sorry, but directories don't habe a resource fork */
3599 rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_base);
3604 /* We always need read/write access for the metadata header too */
3605 flags &= ~(O_RDONLY | O_WRONLY);
3608 hostfd = SMB_VFS_NEXT_OPEN(handle, smb_fname_base, fsp,
3615 if (flags & (O_CREAT | O_TRUNC)) {
3616 ad = ad_init(fsp, handle, ADOUBLE_RSRC);
3622 fsp->fh->fd = hostfd;
3624 rc = ad_fset(handle, ad, fsp);
3635 TALLOC_FREE(smb_fname_base);
3637 DEBUG(10, ("fruit_open resource fork: rc=%d, fd=%d\n", rc, hostfd));
3639 int saved_errno = errno;
3642 * BUGBUGBUG -- we would need to call
3643 * fd_close_posix here, but we don't have a
3646 fsp->fh->fd = hostfd;
3650 errno = saved_errno;
3655 static int fruit_open_rsrc_xattr(vfs_handle_struct *handle,
3656 struct smb_filename *smb_fname,
3661 #ifdef HAVE_ATTROPEN
3664 fd = attropen(smb_fname->base_name,
3665 AFPRESOURCE_EA_NETATALK,
3680 static int fruit_open_rsrc(vfs_handle_struct *handle,
3681 struct smb_filename *smb_fname,
3682 files_struct *fsp, int flags, mode_t mode)
3685 struct fruit_config_data *config = NULL;
3686 struct fio *fio = NULL;
3688 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3690 SMB_VFS_HANDLE_GET_DATA(handle, config,
3691 struct fruit_config_data, return -1);
3693 switch (config->rsrc) {
3694 case FRUIT_RSRC_STREAM:
3695 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3698 case FRUIT_RSRC_ADFILE:
3699 fd = fruit_open_rsrc_adouble(handle, smb_fname,
3703 case FRUIT_RSRC_XATTR:
3704 fd = fruit_open_rsrc_xattr(handle, smb_fname,
3709 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3713 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3719 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3720 fio->type = ADOUBLE_RSRC;
3721 fio->config = config;
3726 static int fruit_open(vfs_handle_struct *handle,
3727 struct smb_filename *smb_fname,
3728 files_struct *fsp, int flags, mode_t mode)
3732 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3734 if (!is_ntfs_stream_smb_fname(smb_fname)) {
3735 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3738 if (is_afpinfo_stream(smb_fname)) {
3739 fd = fruit_open_meta(handle, smb_fname, fsp, flags, mode);
3740 } else if (is_afpresource_stream(smb_fname)) {
3741 fd = fruit_open_rsrc(handle, smb_fname, fsp, flags, mode);
3743 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3746 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3751 static int fruit_close_meta(vfs_handle_struct *handle,
3755 struct fruit_config_data *config = NULL;
3757 SMB_VFS_HANDLE_GET_DATA(handle, config,
3758 struct fruit_config_data, return -1);
3760 switch (config->meta) {
3761 case FRUIT_META_STREAM:
3762 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3765 case FRUIT_META_NETATALK:
3766 ret = close(fsp->fh->fd);
3771 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3779 static int fruit_close_rsrc(vfs_handle_struct *handle,
3783 struct fruit_config_data *config = NULL;
3785 SMB_VFS_HANDLE_GET_DATA(handle, config,
3786 struct fruit_config_data, return -1);
3788 switch (config->rsrc) {
3789 case FRUIT_RSRC_STREAM:
3790 case FRUIT_RSRC_ADFILE:
3791 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3794 case FRUIT_RSRC_XATTR:
3795 ret = close(fsp->fh->fd);
3800 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3807 static int fruit_close(vfs_handle_struct *handle,
3815 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(fsp->fsp_name), fd);
3817 if (!is_ntfs_stream_smb_fname(fsp->fsp_name)) {
3818 return SMB_VFS_NEXT_CLOSE(handle, fsp);
3821 if (is_afpinfo_stream(fsp->fsp_name)) {
3822 ret = fruit_close_meta(handle, fsp);
3823 } else if (is_afpresource_stream(fsp->fsp_name)) {
3824 ret = fruit_close_rsrc(handle, fsp);
3826 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3832 static int fruit_rename(struct vfs_handle_struct *handle,
3833 const struct smb_filename *smb_fname_src,
3834 const struct smb_filename *smb_fname_dst)
3837 struct fruit_config_data *config = NULL;
3838 struct smb_filename *src_adp_smb_fname = NULL;
3839 struct smb_filename *dst_adp_smb_fname = NULL;
3841 SMB_VFS_HANDLE_GET_DATA(handle, config,
3842 struct fruit_config_data, return -1);
3844 if (!VALID_STAT(smb_fname_src->st)) {
3845 DBG_ERR("Need valid stat for [%s]\n",
3846 smb_fname_str_dbg(smb_fname_src));
3850 rc = SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
3855 if ((config->rsrc != FRUIT_RSRC_ADFILE) ||
3856 (!S_ISREG(smb_fname_src->st.st_ex_mode)))
3861 rc = adouble_path(talloc_tos(), smb_fname_src, &src_adp_smb_fname);
3866 rc = adouble_path(talloc_tos(), smb_fname_dst, &dst_adp_smb_fname);
3871 DBG_DEBUG("%s -> %s\n",
3872 smb_fname_str_dbg(src_adp_smb_fname),
3873 smb_fname_str_dbg(dst_adp_smb_fname));
3875 rc = SMB_VFS_NEXT_RENAME(handle, src_adp_smb_fname, dst_adp_smb_fname);
3876 if (errno == ENOENT) {
3881 TALLOC_FREE(src_adp_smb_fname);
3882 TALLOC_FREE(dst_adp_smb_fname);
3886 static int fruit_unlink_meta_stream(vfs_handle_struct *handle,
3887 const struct smb_filename *smb_fname)
3889 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3892 static int fruit_unlink_meta_netatalk(vfs_handle_struct *handle,
3893 const struct smb_filename *smb_fname)
3895 return SMB_VFS_REMOVEXATTR(handle->conn,
3897 AFPINFO_EA_NETATALK);
3900 static int fruit_unlink_meta(vfs_handle_struct *handle,
3901 const struct smb_filename *smb_fname)
3903 struct fruit_config_data *config = NULL;
3906 SMB_VFS_HANDLE_GET_DATA(handle, config,
3907 struct fruit_config_data, return -1);
3909 switch (config->meta) {
3910 case FRUIT_META_STREAM:
3911 rc = fruit_unlink_meta_stream(handle, smb_fname);
3914 case FRUIT_META_NETATALK:
3915 rc = fruit_unlink_meta_netatalk(handle, smb_fname);
3919 DBG_ERR("Unsupported meta config [%d]\n", config->meta);
3926 static int fruit_unlink_rsrc_stream(vfs_handle_struct *handle,
3927 const struct smb_filename *smb_fname,
3932 if (!force_unlink) {
3933 struct smb_filename *smb_fname_cp = NULL;
3936 smb_fname_cp = cp_smb_filename(talloc_tos(), smb_fname);
3937 if (smb_fname_cp == NULL) {
3942 * 0 byte resource fork streams are not listed by
3943 * vfs_streaminfo, as a result stream cleanup/deletion of file
3944 * deletion doesn't remove the resourcefork stream.
3947 ret = SMB_VFS_NEXT_STAT(handle, smb_fname_cp);
3949 TALLOC_FREE(smb_fname_cp);
3950 DBG_ERR("stat [%s] failed [%s]\n",
3951 smb_fname_str_dbg(smb_fname_cp), strerror(errno));
3955 size = smb_fname_cp->st.st_ex_size;
3956 TALLOC_FREE(smb_fname_cp);
3959 /* OS X ignores resource fork stream delete requests */
3964 ret = SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3965 if ((ret != 0) && (errno == ENOENT) && force_unlink) {
3972 static int fruit_unlink_rsrc_adouble(vfs_handle_struct *handle,
3973 const struct smb_filename *smb_fname,
3977 struct adouble *ad = NULL;
3978 struct smb_filename *adp_smb_fname = NULL;
3980 if (!force_unlink) {
3981 ad = ad_get(talloc_tos(), handle, smb_fname,
3990 * 0 byte resource fork streams are not listed by
3991 * vfs_streaminfo, as a result stream cleanup/deletion of file
3992 * deletion doesn't remove the resourcefork stream.
3995 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
3996 /* OS X ignores resource fork stream delete requests */
4004 rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
4009 rc = SMB_VFS_NEXT_UNLINK(handle, adp_smb_fname);
4010 TALLOC_FREE(adp_smb_fname);
4011 if ((rc != 0) && (errno == ENOENT) && force_unlink) {
4018 static int fruit_unlink_rsrc_xattr(vfs_handle_struct *handle,
4019 const struct smb_filename *smb_fname,
4023 * OS X ignores resource fork stream delete requests, so nothing to do
4024 * here. Removing the file will remove the xattr anyway, so we don't
4025 * have to take care of removing 0 byte resource forks that could be
4031 static int fruit_unlink_rsrc(vfs_handle_struct *handle,
4032 const struct smb_filename *smb_fname,
4035 struct fruit_config_data *config = NULL;
4038 SMB_VFS_HANDLE_GET_DATA(handle, config,
4039 struct fruit_config_data, return -1);
4041 switch (config->rsrc) {
4042 case FRUIT_RSRC_STREAM:
4043 rc = fruit_unlink_rsrc_stream(handle, smb_fname, force_unlink);
4046 case FRUIT_RSRC_ADFILE:
4047 rc = fruit_unlink_rsrc_adouble(handle, smb_fname, force_unlink);
4050 case FRUIT_RSRC_XATTR:
4051 rc = fruit_unlink_rsrc_xattr(handle, smb_fname, force_unlink);
4055 DBG_ERR("Unsupported rsrc config [%d]\n", config->rsrc);
4062 static int fruit_unlink(vfs_handle_struct *handle,
4063 const struct smb_filename *smb_fname)
4066 struct fruit_config_data *config = NULL;
4067 struct smb_filename *rsrc_smb_fname = NULL;
4069 SMB_VFS_HANDLE_GET_DATA(handle, config,
4070 struct fruit_config_data, return -1);
4072 if (is_afpinfo_stream(smb_fname)) {
4073 return fruit_unlink_meta(handle, smb_fname);
4074 } else if (is_afpresource_stream(smb_fname)) {
4075 return fruit_unlink_rsrc(handle, smb_fname, false);
4076 } if (is_ntfs_stream_smb_fname(smb_fname)) {
4077 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4081 * A request to delete the base file. Because 0 byte resource
4082 * fork streams are not listed by fruit_streaminfo,
4083 * delete_all_streams() can't remove 0 byte resource fork
4084 * streams, so we have to cleanup this here.
4086 rsrc_smb_fname = synthetic_smb_fname(talloc_tos(),
4087 smb_fname->base_name,
4088 AFPRESOURCE_STREAM_NAME,
4091 if (rsrc_smb_fname == NULL) {
4095 rc = fruit_unlink_rsrc(handle, rsrc_smb_fname, true);
4096 if ((rc != 0) && (errno != ENOENT)) {
4097 DBG_ERR("Forced unlink of [%s] failed [%s]\n",
4098 smb_fname_str_dbg(rsrc_smb_fname), strerror(errno));
4099 TALLOC_FREE(rsrc_smb_fname);
4102 TALLOC_FREE(rsrc_smb_fname);
4104 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4107 static int fruit_chmod(vfs_handle_struct *handle,
4108 const struct smb_filename *smb_fname,
4112 struct fruit_config_data *config = NULL;
4113 struct smb_filename *smb_fname_adp = NULL;
4115 rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname, mode);
4120 SMB_VFS_HANDLE_GET_DATA(handle, config,
4121 struct fruit_config_data, return -1);
4123 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4127 if (!VALID_STAT(smb_fname->st)) {
4131 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
4135 rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_adp);
4140 DEBUG(10, ("fruit_chmod: %s\n", smb_fname_adp->base_name));
4142 rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname_adp, mode);
4143 if (errno == ENOENT) {
4147 TALLOC_FREE(smb_fname_adp);
4151 static int fruit_chown(vfs_handle_struct *handle,
4152 const struct smb_filename *smb_fname,
4157 struct fruit_config_data *config = NULL;
4158 struct smb_filename *adp_smb_fname = NULL;
4160 rc = SMB_VFS_NEXT_CHOWN(handle, smb_fname, uid, gid);
4165 SMB_VFS_HANDLE_GET_DATA(handle, config,
4166 struct fruit_config_data, return -1);
4168 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4172 if (!VALID_STAT(smb_fname->st)) {
4176 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
4180 rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
4185 DEBUG(10, ("fruit_chown: %s\n", adp_smb_fname->base_name));
4187 rc = SMB_VFS_NEXT_CHOWN(handle, adp_smb_fname, uid, gid);
4188 if (errno == ENOENT) {
4193 TALLOC_FREE(adp_smb_fname);
4197 static int fruit_rmdir(struct vfs_handle_struct *handle,
4198 const struct smb_filename *smb_fname)
4202 struct fruit_config_data *config;
4204 SMB_VFS_HANDLE_GET_DATA(handle, config,
4205 struct fruit_config_data, return -1);
4207 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4212 * Due to there is no way to change bDeleteVetoFiles variable
4213 * from this module, need to clean up ourselves
4216 dh = SMB_VFS_OPENDIR(handle->conn, smb_fname, NULL, 0);
4221 while ((de = SMB_VFS_READDIR(handle->conn, dh, NULL)) != NULL) {
4223 struct adouble *ad = NULL;
4225 struct smb_filename *ad_smb_fname = NULL;
4228 match = strncmp(de->d_name,
4229 ADOUBLE_NAME_PREFIX,
4230 strlen(ADOUBLE_NAME_PREFIX));
4235 p = talloc_asprintf(talloc_tos(), "%s/%s",
4236 smb_fname->base_name, de->d_name);
4238 DBG_ERR("talloc_asprintf failed\n");
4242 ad_smb_fname = synthetic_smb_fname(talloc_tos(), p,
4246 if (ad_smb_fname == NULL) {
4247 DBG_ERR("synthetic_smb_fname failed\n");
4252 * Check whether it's a valid AppleDouble file, if
4253 * yes, delete it, ignore it otherwise.
4255 ad = ad_get(talloc_tos(), handle, ad_smb_fname, ADOUBLE_RSRC);
4257 TALLOC_FREE(ad_smb_fname);
4263 ret = SMB_VFS_NEXT_UNLINK(handle, ad_smb_fname);
4265 DBG_ERR("Deleting [%s] failed\n",
4266 smb_fname_str_dbg(ad_smb_fname));
4268 TALLOC_FREE(ad_smb_fname);
4273 SMB_VFS_CLOSEDIR(handle->conn, dh);
4275 return SMB_VFS_NEXT_RMDIR(handle, smb_fname);
4278 static ssize_t fruit_pread_meta_stream(vfs_handle_struct *handle,
4279 files_struct *fsp, void *data,
4280 size_t n, off_t offset)
4285 nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4286 if (nread == -1 || nread == n) {
4290 DBG_ERR("Removing [%s] after short read [%zd]\n",
4291 fsp_str_dbg(fsp), nread);
4293 ret = SMB_VFS_NEXT_UNLINK(handle, fsp->fsp_name);
4295 DBG_ERR("Removing [%s] failed\n", fsp_str_dbg(fsp));
4303 static ssize_t fruit_pread_meta_adouble(vfs_handle_struct *handle,
4304 files_struct *fsp, void *data,
4305 size_t n, off_t offset)
4308 struct adouble *ad = NULL;
4309 char afpinfo_buf[AFP_INFO_SIZE];
4313 ai = afpinfo_new(talloc_tos());
4318 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
4324 p = ad_get_entry(ad, ADEID_FINDERI);
4326 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
4331 memcpy(&ai->afpi_FinderInfo[0], p, ADEDLEN_FINDERI);
4333 nread = afpinfo_pack(ai, afpinfo_buf);
4334 if (nread != AFP_INFO_SIZE) {
4339 memcpy(data, afpinfo_buf, n);
4347 static ssize_t fruit_pread_meta(vfs_handle_struct *handle,
4348 files_struct *fsp, void *data,
4349 size_t n, off_t offset)
4351 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4356 * OS X has a off-by-1 error in the offset calculation, so we're
4357 * bug compatible here. It won't hurt, as any relevant real
4358 * world read requests from the AFP_AfpInfo stream will be
4359 * offset=0 n=60. offset is ignored anyway, see below.
4361 if ((offset < 0) || (offset >= AFP_INFO_SIZE + 1)) {
4366 DBG_ERR("Failed to fetch fsp extension");
4370 /* Yes, macOS always reads from offset 0 */
4372 to_return = MIN(n, AFP_INFO_SIZE);
4374 switch (fio->config->meta) {
4375 case FRUIT_META_STREAM:
4376 nread = fruit_pread_meta_stream(handle, fsp, data,
4380 case FRUIT_META_NETATALK:
4381 nread = fruit_pread_meta_adouble(handle, fsp, data,
4386 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
4390 if (nread == -1 && fio->created) {
4392 char afpinfo_buf[AFP_INFO_SIZE];
4394 ai = afpinfo_new(talloc_tos());
4399 nread = afpinfo_pack(ai, afpinfo_buf);
4401 if (nread != AFP_INFO_SIZE) {
4405 memcpy(data, afpinfo_buf, to_return);
4412 static ssize_t fruit_pread_rsrc_stream(vfs_handle_struct *handle,
4413 files_struct *fsp, void *data,
4414 size_t n, off_t offset)
4416 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4419 static ssize_t fruit_pread_rsrc_xattr(vfs_handle_struct *handle,
4420 files_struct *fsp, void *data,
4421 size_t n, off_t offset)
4423 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4426 static ssize_t fruit_pread_rsrc_adouble(vfs_handle_struct *handle,
4427 files_struct *fsp, void *data,
4428 size_t n, off_t offset)
4430 struct adouble *ad = NULL;
4433 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
4438 nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n,
4439 offset + ad_getentryoff(ad, ADEID_RFORK));
4445 static ssize_t fruit_pread_rsrc(vfs_handle_struct *handle,
4446 files_struct *fsp, void *data,
4447 size_t n, off_t offset)
4449 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4457 switch (fio->config->rsrc) {
4458 case FRUIT_RSRC_STREAM:
4459 nread = fruit_pread_rsrc_stream(handle, fsp, data, n, offset);
4462 case FRUIT_RSRC_ADFILE:
4463 nread = fruit_pread_rsrc_adouble(handle, fsp, data, n, offset);
4466 case FRUIT_RSRC_XATTR:
4467 nread = fruit_pread_rsrc_xattr(handle, fsp, data, n, offset);
4471 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
4478 static ssize_t fruit_pread(vfs_handle_struct *handle,
4479 files_struct *fsp, void *data,
4480 size_t n, off_t offset)
4482 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4485 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4486 fsp_str_dbg(fsp), (intmax_t)offset, n);
4489 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4492 if (fio->type == ADOUBLE_META) {
4493 nread = fruit_pread_meta(handle, fsp, data, n, offset);
4495 nread = fruit_pread_rsrc(handle, fsp, data, n, offset);
4498 DBG_DEBUG("Path [%s] nread [%zd]\n", fsp_str_dbg(fsp), nread);
4502 static bool fruit_must_handle_aio_stream(struct fio *fio)
4508 if (fio->type == ADOUBLE_META) {
4512 if ((fio->type == ADOUBLE_RSRC) &&
4513 (fio->config->rsrc == FRUIT_RSRC_ADFILE))
4521 struct fruit_pread_state {
4523 struct vfs_aio_state vfs_aio_state;
4526 static void fruit_pread_done(struct tevent_req *subreq);
4528 static struct tevent_req *fruit_pread_send(
4529 struct vfs_handle_struct *handle,
4530 TALLOC_CTX *mem_ctx,
4531 struct tevent_context *ev,
4532 struct files_struct *fsp,
4534 size_t n, off_t offset)
4536 struct tevent_req *req = NULL;
4537 struct tevent_req *subreq = NULL;
4538 struct fruit_pread_state *state = NULL;
4539 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4541 req = tevent_req_create(mem_ctx, &state,
4542 struct fruit_pread_state);
4547 if (fruit_must_handle_aio_stream(fio)) {
4548 state->nread = SMB_VFS_PREAD(fsp, data, n, offset);
4549 if (state->nread != n) {
4550 if (state->nread != -1) {
4553 tevent_req_error(req, errno);
4554 return tevent_req_post(req, ev);
4556 tevent_req_done(req);
4557 return tevent_req_post(req, ev);
4560 subreq = SMB_VFS_NEXT_PREAD_SEND(state, ev, handle, fsp,
4562 if (tevent_req_nomem(req, subreq)) {
4563 return tevent_req_post(req, ev);
4565 tevent_req_set_callback(subreq, fruit_pread_done, req);
4569 static void fruit_pread_done(struct tevent_req *subreq)
4571 struct tevent_req *req = tevent_req_callback_data(
4572 subreq, struct tevent_req);
4573 struct fruit_pread_state *state = tevent_req_data(
4574 req, struct fruit_pread_state);
4576 state->nread = SMB_VFS_PREAD_RECV(subreq, &state->vfs_aio_state);
4577 TALLOC_FREE(subreq);
4579 if (tevent_req_error(req, state->vfs_aio_state.error)) {
4582 tevent_req_done(req);
4585 static ssize_t fruit_pread_recv(struct tevent_req *req,
4586 struct vfs_aio_state *vfs_aio_state)
4588 struct fruit_pread_state *state = tevent_req_data(
4589 req, struct fruit_pread_state);
4591 if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
4595 *vfs_aio_state = state->vfs_aio_state;
4596 return state->nread;
4599 static ssize_t fruit_pwrite_meta_stream(vfs_handle_struct *handle,
4600 files_struct *fsp, const void *data,
4601 size_t n, off_t offset)
4603 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4609 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4610 fsp_str_dbg(fsp), (intmax_t)offset, n);
4619 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
4621 DBG_ERR("Close [%s] failed: %s\n",
4622 fsp_str_dbg(fsp), strerror(errno));
4627 fd = SMB_VFS_NEXT_OPEN(handle,
4633 DBG_ERR("On-demand create [%s] in write failed: %s\n",
4634 fsp_str_dbg(fsp), strerror(errno));
4638 fio->fake_fd = false;
4641 ai = afpinfo_unpack(talloc_tos(), data);
4646 if (ai_empty_finderinfo(ai)) {
4648 * Writing an all 0 blob to the metadata stream results in the
4649 * stream being removed on a macOS server. This ensures we
4650 * behave the same and it verified by the "delete AFP_AfpInfo by
4651 * writing all 0" test.
4653 ret = SMB_VFS_NEXT_FTRUNCATE(handle, fsp, 0);
4655 DBG_ERR("SMB_VFS_NEXT_FTRUNCATE on [%s] failed\n",
4660 ok = set_delete_on_close(
4663 handle->conn->session_info->security_token,
4664 handle->conn->session_info->unix_token);
4666 DBG_ERR("set_delete_on_close on [%s] failed\n",
4673 nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4674 if (nwritten != n) {
4681 static ssize_t fruit_pwrite_meta_netatalk(vfs_handle_struct *handle,
4682 files_struct *fsp, const void *data,
4683 size_t n, off_t offset)
4685 struct adouble *ad = NULL;
4691 ai = afpinfo_unpack(talloc_tos(), data);
4696 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
4698 ad = ad_init(talloc_tos(), handle, ADOUBLE_META);
4703 p = ad_get_entry(ad, ADEID_FINDERI);
4705 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
4710 memcpy(p, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
4712 ret = ad_fset(handle, ad, fsp);
4714 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
4721 if (!ai_empty_finderinfo(ai)) {
4726 * Writing an all 0 blob to the metadata stream results in the stream
4727 * being removed on a macOS server. This ensures we behave the same and
4728 * it verified by the "delete AFP_AfpInfo by writing all 0" test.
4731 ok = set_delete_on_close(
4734 handle->conn->session_info->security_token,
4735 handle->conn->session_info->unix_token);
4737 DBG_ERR("set_delete_on_close on [%s] failed\n",
4745 static ssize_t fruit_pwrite_meta(vfs_handle_struct *handle,
4746 files_struct *fsp, const void *data,
4747 size_t n, off_t offset)
4749 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4751 uint8_t buf[AFP_INFO_SIZE];
4757 DBG_ERR("Failed to fetch fsp extension");
4766 if (offset != 0 && n < 60) {
4771 cmp = memcmp(data, "AFP", 3);
4777 if (n <= AFP_OFF_FinderInfo) {
4779 * Nothing to do here really, just return
4787 if (to_copy > AFP_INFO_SIZE) {
4788 to_copy = AFP_INFO_SIZE;
4790 memcpy(buf, data, to_copy);
4793 if (to_write != AFP_INFO_SIZE) {
4794 to_write = AFP_INFO_SIZE;
4797 switch (fio->config->meta) {
4798 case FRUIT_META_STREAM:
4799 nwritten = fruit_pwrite_meta_stream(handle,
4806 case FRUIT_META_NETATALK:
4807 nwritten = fruit_pwrite_meta_netatalk(handle,
4815 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
4819 if (nwritten != to_write) {
4824 * Return the requested amount, verified against macOS SMB server
4829 static ssize_t fruit_pwrite_rsrc_stream(vfs_handle_struct *handle,
4830 files_struct *fsp, const void *data,
4831 size_t n, off_t offset)
4833 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4836 static ssize_t fruit_pwrite_rsrc_xattr(vfs_handle_struct *handle,
4837 files_struct *fsp, const void *data,
4838 size_t n, off_t offset)
4840 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4843 static ssize_t fruit_pwrite_rsrc_adouble(vfs_handle_struct *handle,
4844 files_struct *fsp, const void *data,
4845 size_t n, off_t offset)
4847 struct adouble *ad = NULL;
4851 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
4853 DBG_ERR("ad_get [%s] failed\n", fsp_str_dbg(fsp));
4857 nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n,
4858 offset + ad_getentryoff(ad, ADEID_RFORK));
4859 if (nwritten != n) {
4860 DBG_ERR("Short write on [%s] [%zd/%zd]\n",
4861 fsp_str_dbg(fsp), nwritten, n);
4866 if ((n + offset) > ad_getentrylen(ad, ADEID_RFORK)) {
4867 ad_setentrylen(ad, ADEID_RFORK, n + offset);
4868 ret = ad_fset(handle, ad, fsp);
4870 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
4880 static ssize_t fruit_pwrite_rsrc(vfs_handle_struct *handle,
4881 files_struct *fsp, const void *data,
4882 size_t n, off_t offset)
4884 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4888 DBG_ERR("Failed to fetch fsp extension");
4892 switch (fio->config->rsrc) {
4893 case FRUIT_RSRC_STREAM:
4894 nwritten = fruit_pwrite_rsrc_stream(handle, fsp, data, n, offset);
4897 case FRUIT_RSRC_ADFILE:
4898 nwritten = fruit_pwrite_rsrc_adouble(handle, fsp, data, n, offset);
4901 case FRUIT_RSRC_XATTR:
4902 nwritten = fruit_pwrite_rsrc_xattr(handle, fsp, data, n, offset);
4906 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
4913 static ssize_t fruit_pwrite(vfs_handle_struct *handle,
4914 files_struct *fsp, const void *data,
4915 size_t n, off_t offset)
4917 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4920 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4921 fsp_str_dbg(fsp), (intmax_t)offset, n);
4924 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4927 if (fio->type == ADOUBLE_META) {
4928 nwritten = fruit_pwrite_meta(handle, fsp, data, n, offset);
4930 nwritten = fruit_pwrite_rsrc(handle, fsp, data, n, offset);
4933 DBG_DEBUG("Path [%s] nwritten=%zd\n", fsp_str_dbg(fsp), nwritten);
4937 struct fruit_pwrite_state {
4939 struct vfs_aio_state vfs_aio_state;
4942 static void fruit_pwrite_done(struct tevent_req *subreq);
4944 static struct tevent_req *fruit_pwrite_send(
4945 struct vfs_handle_struct *handle,
4946 TALLOC_CTX *mem_ctx,
4947 struct tevent_context *ev,
4948 struct files_struct *fsp,
4950 size_t n, off_t offset)
4952 struct tevent_req *req = NULL;
4953 struct tevent_req *subreq = NULL;
4954 struct fruit_pwrite_state *state = NULL;
4955 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4957 req = tevent_req_create(mem_ctx, &state,
4958 struct fruit_pwrite_state);
4963 if (fruit_must_handle_aio_stream(fio)) {
4964 state->nwritten = SMB_VFS_PWRITE(fsp, data, n, offset);
4965 if (state->nwritten != n) {
4966 if (state->nwritten != -1) {
4969 tevent_req_error(req, errno);
4970 return tevent_req_post(req, ev);
4972 tevent_req_done(req);
4973 return tevent_req_post(req, ev);
4976 subreq = SMB_VFS_NEXT_PWRITE_SEND(state, ev, handle, fsp,
4978 if (tevent_req_nomem(req, subreq)) {
4979 return tevent_req_post(req, ev);
4981 tevent_req_set_callback(subreq, fruit_pwrite_done, req);
4985 static void fruit_pwrite_done(struct tevent_req *subreq)
4987 struct tevent_req *req = tevent_req_callback_data(
4988 subreq, struct tevent_req);
4989 struct fruit_pwrite_state *state = tevent_req_data(
4990 req, struct fruit_pwrite_state);
4992 state->nwritten = SMB_VFS_PWRITE_RECV(subreq, &state->vfs_aio_state);
4993 TALLOC_FREE(subreq);
4995 if (tevent_req_error(req, state->vfs_aio_state.error)) {
4998 tevent_req_done(req);
5001 static ssize_t fruit_pwrite_recv(struct tevent_req *req,
5002 struct vfs_aio_state *vfs_aio_state)
5004 struct fruit_pwrite_state *state = tevent_req_data(
5005 req, struct fruit_pwrite_state);
5007 if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
5011 *vfs_aio_state = state->vfs_aio_state;
5012 return state->nwritten;
5016 * Helper to stat/lstat the base file of an smb_fname.
5018 static int fruit_stat_base(vfs_handle_struct *handle,
5019 struct smb_filename *smb_fname,
5022 char *tmp_stream_name;
5025 tmp_stream_name = smb_fname->stream_name;
5026 smb_fname->stream_name = NULL;
5028 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
5030 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5032 smb_fname->stream_name = tmp_stream_name;
5034 DBG_DEBUG("fruit_stat_base [%s] dev [%ju] ino [%ju]\n",
5035 smb_fname->base_name,
5036 (uintmax_t)smb_fname->st.st_ex_dev,
5037 (uintmax_t)smb_fname->st.st_ex_ino);
5041 static int fruit_stat_meta_stream(vfs_handle_struct *handle,
5042 struct smb_filename *smb_fname,
5048 ret = fruit_stat_base(handle, smb_fname, false);
5053 ino = fruit_inode(&smb_fname->st, smb_fname->stream_name);
5056 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
5058 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5061 smb_fname->st.st_ex_ino = ino;
5066 static int fruit_stat_meta_netatalk(vfs_handle_struct *handle,
5067 struct smb_filename *smb_fname,
5070 struct adouble *ad = NULL;
5072 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5074 DBG_INFO("fruit_stat_meta %s: %s\n",
5075 smb_fname_str_dbg(smb_fname), strerror(errno));
5081 /* Populate the stat struct with info from the base file. */
5082 if (fruit_stat_base(handle, smb_fname, follow_links) == -1) {
5085 smb_fname->st.st_ex_size = AFP_INFO_SIZE;
5086 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5087 smb_fname->stream_name);
5091 static int fruit_stat_meta(vfs_handle_struct *handle,
5092 struct smb_filename *smb_fname,
5095 struct fruit_config_data *config = NULL;
5098 SMB_VFS_HANDLE_GET_DATA(handle, config,
5099 struct fruit_config_data, return -1);
5101 switch (config->meta) {
5102 case FRUIT_META_STREAM:
5103 ret = fruit_stat_meta_stream(handle, smb_fname, follow_links);
5106 case FRUIT_META_NETATALK:
5107 ret = fruit_stat_meta_netatalk(handle, smb_fname, follow_links);
5111 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
5118 static int fruit_stat_rsrc_netatalk(vfs_handle_struct *handle,
5119 struct smb_filename *smb_fname,
5122 struct adouble *ad = NULL;
5125 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
5131 /* Populate the stat struct with info from the base file. */
5132 ret = fruit_stat_base(handle, smb_fname, follow_links);
5138 smb_fname->st.st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
5139 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5140 smb_fname->stream_name);
5145 static int fruit_stat_rsrc_stream(vfs_handle_struct *handle,
5146 struct smb_filename *smb_fname,
5152 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
5154 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5160 static int fruit_stat_rsrc_xattr(vfs_handle_struct *handle,
5161 struct smb_filename *smb_fname,
5164 #ifdef HAVE_ATTROPEN
5168 /* Populate the stat struct with info from the base file. */
5169 ret = fruit_stat_base(handle, smb_fname, follow_links);
5174 fd = attropen(smb_fname->base_name,
5175 AFPRESOURCE_EA_NETATALK,
5181 ret = sys_fstat(fd, &smb_fname->st, false);
5184 DBG_ERR("fstat [%s:%s] failed\n", smb_fname->base_name,
5185 AFPRESOURCE_EA_NETATALK);
5191 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5192 smb_fname->stream_name);
5202 static int fruit_stat_rsrc(vfs_handle_struct *handle,
5203 struct smb_filename *smb_fname,
5206 struct fruit_config_data *config = NULL;
5209 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
5211 SMB_VFS_HANDLE_GET_DATA(handle, config,
5212 struct fruit_config_data, return -1);
5214 switch (config->rsrc) {
5215 case FRUIT_RSRC_STREAM:
5216 ret = fruit_stat_rsrc_stream(handle, smb_fname, follow_links);
5219 case FRUIT_RSRC_XATTR:
5220 ret = fruit_stat_rsrc_xattr(handle, smb_fname, follow_links);
5223 case FRUIT_RSRC_ADFILE:
5224 ret = fruit_stat_rsrc_netatalk(handle, smb_fname, follow_links);
5228 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
5235 static int fruit_stat(vfs_handle_struct *handle,
5236 struct smb_filename *smb_fname)
5240 DEBUG(10, ("fruit_stat called for %s\n",
5241 smb_fname_str_dbg(smb_fname)));
5243 if (!is_ntfs_stream_smb_fname(smb_fname)
5244 || is_ntfs_default_stream_smb_fname(smb_fname)) {
5245 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
5247 update_btime(handle, smb_fname);
5253 * Note if lp_posix_paths() is true, we can never
5254 * get here as is_ntfs_stream_smb_fname() is
5255 * always false. So we never need worry about
5256 * not following links here.
5259 if (is_afpinfo_stream(smb_fname)) {
5260 rc = fruit_stat_meta(handle, smb_fname, true);
5261 } else if (is_afpresource_stream(smb_fname)) {
5262 rc = fruit_stat_rsrc(handle, smb_fname, true);
5264 return SMB_VFS_NEXT_STAT(handle, smb_fname);
5268 update_btime(handle, smb_fname);
5269 smb_fname->st.st_ex_mode &= ~S_IFMT;
5270 smb_fname->st.st_ex_mode |= S_IFREG;
5271 smb_fname->st.st_ex_blocks =
5272 smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
5277 static int fruit_lstat(vfs_handle_struct *handle,
5278 struct smb_filename *smb_fname)
5282 DEBUG(10, ("fruit_lstat called for %s\n",
5283 smb_fname_str_dbg(smb_fname)));
5285 if (!is_ntfs_stream_smb_fname(smb_fname)
5286 || is_ntfs_default_stream_smb_fname(smb_fname)) {
5287 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5289 update_btime(handle, smb_fname);
5294 if (is_afpinfo_stream(smb_fname)) {
5295 rc = fruit_stat_meta(handle, smb_fname, false);
5296 } else if (is_afpresource_stream(smb_fname)) {
5297 rc = fruit_stat_rsrc(handle, smb_fname, false);
5299 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5303 update_btime(handle, smb_fname);
5304 smb_fname->st.st_ex_mode &= ~S_IFMT;
5305 smb_fname->st.st_ex_mode |= S_IFREG;
5306 smb_fname->st.st_ex_blocks =
5307 smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
5312 static int fruit_fstat_meta_stream(vfs_handle_struct *handle,
5314 SMB_STRUCT_STAT *sbuf)
5316 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5317 struct smb_filename smb_fname;
5326 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5331 *sbuf = fsp->base_fsp->fsp_name->st;
5332 sbuf->st_ex_size = AFP_INFO_SIZE;
5333 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5337 smb_fname = (struct smb_filename) {
5338 .base_name = fsp->fsp_name->base_name,
5341 ret = fruit_stat_base(handle, &smb_fname, false);
5345 *sbuf = smb_fname.st;
5347 ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5349 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5354 sbuf->st_ex_ino = ino;
5358 static int fruit_fstat_meta_netatalk(vfs_handle_struct *handle,
5360 SMB_STRUCT_STAT *sbuf)
5364 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5369 *sbuf = fsp->base_fsp->fsp_name->st;
5370 sbuf->st_ex_size = AFP_INFO_SIZE;
5371 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5376 static int fruit_fstat_meta(vfs_handle_struct *handle,
5378 SMB_STRUCT_STAT *sbuf,
5383 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
5385 switch (fio->config->meta) {
5386 case FRUIT_META_STREAM:
5387 ret = fruit_fstat_meta_stream(handle, fsp, sbuf);
5390 case FRUIT_META_NETATALK:
5391 ret = fruit_fstat_meta_netatalk(handle, fsp, sbuf);
5395 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
5399 DBG_DEBUG("Path [%s] ret [%d]\n", fsp_str_dbg(fsp), ret);
5403 static int fruit_fstat_rsrc_xattr(vfs_handle_struct *handle,
5405 SMB_STRUCT_STAT *sbuf)
5407 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5410 static int fruit_fstat_rsrc_stream(vfs_handle_struct *handle,
5412 SMB_STRUCT_STAT *sbuf)
5414 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5417 static int fruit_fstat_rsrc_adouble(vfs_handle_struct *handle,
5419 SMB_STRUCT_STAT *sbuf)
5421 struct adouble *ad = NULL;
5424 /* Populate the stat struct with info from the base file. */
5425 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5430 ad = ad_get(talloc_tos(), handle,
5431 fsp->base_fsp->fsp_name,
5434 DBG_ERR("ad_get [%s] failed [%s]\n",
5435 fsp_str_dbg(fsp), strerror(errno));
5439 *sbuf = fsp->base_fsp->fsp_name->st;
5440 sbuf->st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
5441 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5447 static int fruit_fstat_rsrc(vfs_handle_struct *handle, files_struct *fsp,
5448 SMB_STRUCT_STAT *sbuf, struct fio *fio)
5452 switch (fio->config->rsrc) {
5453 case FRUIT_RSRC_STREAM:
5454 ret = fruit_fstat_rsrc_stream(handle, fsp, sbuf);
5457 case FRUIT_RSRC_ADFILE:
5458 ret = fruit_fstat_rsrc_adouble(handle, fsp, sbuf);
5461 case FRUIT_RSRC_XATTR:
5462 ret = fruit_fstat_rsrc_xattr(handle, fsp, sbuf);
5466 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
5473 static int fruit_fstat(vfs_handle_struct *handle, files_struct *fsp,
5474 SMB_STRUCT_STAT *sbuf)
5476 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5480 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5483 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
5485 if (fio->type == ADOUBLE_META) {
5486 rc = fruit_fstat_meta(handle, fsp, sbuf, fio);
5488 rc = fruit_fstat_rsrc(handle, fsp, sbuf, fio);
5492 sbuf->st_ex_mode &= ~S_IFMT;
5493 sbuf->st_ex_mode |= S_IFREG;
5494 sbuf->st_ex_blocks = sbuf->st_ex_size / STAT_ST_BLOCKSIZE + 1;
5497 DBG_DEBUG("Path [%s] rc [%d] size [%"PRIdMAX"]\n",
5498 fsp_str_dbg(fsp), rc, (intmax_t)sbuf->st_ex_size);
5502 static NTSTATUS delete_invalid_meta_stream(
5503 vfs_handle_struct *handle,
5504 const struct smb_filename *smb_fname,
5505 TALLOC_CTX *mem_ctx,
5506 unsigned int *pnum_streams,
5507 struct stream_struct **pstreams,
5510 struct smb_filename *sname = NULL;
5514 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams, AFPINFO_STREAM);
5516 return NT_STATUS_INTERNAL_ERROR;
5520 return NT_STATUS_OK;
5523 sname = synthetic_smb_fname(talloc_tos(),
5524 smb_fname->base_name,
5525 AFPINFO_STREAM_NAME,
5527 if (sname == NULL) {
5528 return NT_STATUS_NO_MEMORY;
5531 ret = SMB_VFS_NEXT_UNLINK(handle, sname);
5534 DBG_ERR("Removing [%s] failed\n", smb_fname_str_dbg(sname));
5535 return map_nt_error_from_unix(errno);
5538 return NT_STATUS_OK;
5541 static NTSTATUS fruit_streaminfo_meta_stream(
5542 vfs_handle_struct *handle,
5543 struct files_struct *fsp,
5544 const struct smb_filename *smb_fname,
5545 TALLOC_CTX *mem_ctx,
5546 unsigned int *pnum_streams,
5547 struct stream_struct **pstreams)
5549 struct stream_struct *stream = *pstreams;
5550 unsigned int num_streams = *pnum_streams;
5553 for (i = 0; i < num_streams; i++) {
5554 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
5559 if (i == num_streams) {
5560 return NT_STATUS_OK;
5563 if (stream[i].size != AFP_INFO_SIZE) {
5564 DBG_ERR("Removing invalid AFPINFO_STREAM size [%jd] from [%s]\n",
5565 (intmax_t)stream[i].size, smb_fname_str_dbg(smb_fname));
5567 return delete_invalid_meta_stream(handle,
5576 return NT_STATUS_OK;
5579 static NTSTATUS fruit_streaminfo_meta_netatalk(
5580 vfs_handle_struct *handle,
5581 struct files_struct *fsp,
5582 const struct smb_filename *smb_fname,
5583 TALLOC_CTX *mem_ctx,
5584 unsigned int *pnum_streams,
5585 struct stream_struct **pstreams)
5587 struct stream_struct *stream = *pstreams;
5588 unsigned int num_streams = *pnum_streams;
5589 struct adouble *ad = NULL;
5594 /* Remove the Netatalk xattr from the list */
5595 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5596 ":" NETATALK_META_XATTR ":$DATA");
5598 return NT_STATUS_NO_MEMORY;
5602 * Check if there's a AFPINFO_STREAM from the VFS streams
5603 * backend and if yes, remove it from the list
5605 for (i = 0; i < num_streams; i++) {
5606 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
5611 if (i < num_streams) {
5612 DBG_WARNING("Unexpected AFPINFO_STREAM on [%s]\n",
5613 smb_fname_str_dbg(smb_fname));
5615 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5618 return NT_STATUS_INTERNAL_ERROR;
5622 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5624 return NT_STATUS_OK;
5627 is_fi_empty = ad_empty_finderinfo(ad);
5631 return NT_STATUS_OK;
5634 ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
5635 AFPINFO_STREAM_NAME, AFP_INFO_SIZE,
5636 smb_roundup(handle->conn, AFP_INFO_SIZE));
5638 return NT_STATUS_NO_MEMORY;
5641 return NT_STATUS_OK;
5644 static NTSTATUS fruit_streaminfo_meta(vfs_handle_struct *handle,
5645 struct files_struct *fsp,
5646 const struct smb_filename *smb_fname,
5647 TALLOC_CTX *mem_ctx,
5648 unsigned int *pnum_streams,
5649 struct stream_struct **pstreams)
5651 struct fruit_config_data *config = NULL;
5654 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5655 return NT_STATUS_INTERNAL_ERROR);
5657 switch (config->meta) {
5658 case FRUIT_META_NETATALK:
5659 status = fruit_streaminfo_meta_netatalk(handle, fsp, smb_fname,
5660 mem_ctx, pnum_streams,
5664 case FRUIT_META_STREAM:
5665 status = fruit_streaminfo_meta_stream(handle, fsp, smb_fname,
5666 mem_ctx, pnum_streams,
5671 return NT_STATUS_INTERNAL_ERROR;
5677 static NTSTATUS fruit_streaminfo_rsrc_stream(
5678 vfs_handle_struct *handle,
5679 struct files_struct *fsp,
5680 const struct smb_filename *smb_fname,
5681 TALLOC_CTX *mem_ctx,
5682 unsigned int *pnum_streams,
5683 struct stream_struct **pstreams)
5687 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
5689 DBG_ERR("Filtering resource stream failed\n");
5690 return NT_STATUS_INTERNAL_ERROR;
5692 return NT_STATUS_OK;
5695 static NTSTATUS fruit_streaminfo_rsrc_xattr(
5696 vfs_handle_struct *handle,
5697 struct files_struct *fsp,
5698 const struct smb_filename *smb_fname,
5699 TALLOC_CTX *mem_ctx,
5700 unsigned int *pnum_streams,
5701 struct stream_struct **pstreams)
5705 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
5707 DBG_ERR("Filtering resource stream failed\n");
5708 return NT_STATUS_INTERNAL_ERROR;
5710 return NT_STATUS_OK;
5713 static NTSTATUS fruit_streaminfo_rsrc_adouble(
5714 vfs_handle_struct *handle,
5715 struct files_struct *fsp,
5716 const struct smb_filename *smb_fname,
5717 TALLOC_CTX *mem_ctx,
5718 unsigned int *pnum_streams,
5719 struct stream_struct **pstreams)
5721 struct stream_struct *stream = *pstreams;
5722 unsigned int num_streams = *pnum_streams;
5723 struct adouble *ad = NULL;
5729 * Check if there's a AFPRESOURCE_STREAM from the VFS streams backend
5730 * and if yes, remove it from the list
5732 for (i = 0; i < num_streams; i++) {
5733 if (strequal_m(stream[i].name, AFPRESOURCE_STREAM)) {
5738 if (i < num_streams) {
5739 DBG_WARNING("Unexpected AFPRESOURCE_STREAM on [%s]\n",
5740 smb_fname_str_dbg(smb_fname));
5742 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5743 AFPRESOURCE_STREAM);
5745 return NT_STATUS_INTERNAL_ERROR;
5749 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
5751 return NT_STATUS_OK;
5754 rlen = ad_getentrylen(ad, ADEID_RFORK);
5758 return NT_STATUS_OK;
5761 ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
5762 AFPRESOURCE_STREAM_NAME, rlen,
5763 smb_roundup(handle->conn, rlen));
5765 return NT_STATUS_NO_MEMORY;
5768 return NT_STATUS_OK;
5771 static NTSTATUS fruit_streaminfo_rsrc(vfs_handle_struct *handle,
5772 struct files_struct *fsp,
5773 const struct smb_filename *smb_fname,
5774 TALLOC_CTX *mem_ctx,
5775 unsigned int *pnum_streams,
5776 struct stream_struct **pstreams)
5778 struct fruit_config_data *config = NULL;
5781 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5782 return NT_STATUS_INTERNAL_ERROR);
5784 switch (config->rsrc) {
5785 case FRUIT_RSRC_STREAM:
5786 status = fruit_streaminfo_rsrc_stream(handle, fsp, smb_fname,
5787 mem_ctx, pnum_streams,
5791 case FRUIT_RSRC_XATTR:
5792 status = fruit_streaminfo_rsrc_xattr(handle, fsp, smb_fname,
5793 mem_ctx, pnum_streams,
5797 case FRUIT_RSRC_ADFILE:
5798 status = fruit_streaminfo_rsrc_adouble(handle, fsp, smb_fname,
5799 mem_ctx, pnum_streams,
5804 return NT_STATUS_INTERNAL_ERROR;
5810 static void fruit_filter_empty_streams(unsigned int *pnum_streams,
5811 struct stream_struct **pstreams)
5813 unsigned num_streams = *pnum_streams;
5814 struct stream_struct *streams = *pstreams;
5817 if (!global_fruit_config.nego_aapl) {
5821 while (i < num_streams) {
5822 struct smb_filename smb_fname = (struct smb_filename) {
5823 .stream_name = streams[i].name,
5826 if (is_ntfs_default_stream_smb_fname(&smb_fname)
5827 || streams[i].size > 0)
5833 streams[i] = streams[num_streams - 1];
5837 *pnum_streams = num_streams;
5840 static NTSTATUS fruit_streaminfo(vfs_handle_struct *handle,
5841 struct files_struct *fsp,
5842 const struct smb_filename *smb_fname,
5843 TALLOC_CTX *mem_ctx,
5844 unsigned int *pnum_streams,
5845 struct stream_struct **pstreams)
5847 struct fruit_config_data *config = NULL;
5850 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5851 return NT_STATUS_UNSUCCESSFUL);
5853 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
5855 status = SMB_VFS_NEXT_STREAMINFO(handle, fsp, smb_fname, mem_ctx,
5856 pnum_streams, pstreams);
5857 if (!NT_STATUS_IS_OK(status)) {
5861 fruit_filter_empty_streams(pnum_streams, pstreams);
5863 status = fruit_streaminfo_meta(handle, fsp, smb_fname,
5864 mem_ctx, pnum_streams, pstreams);
5865 if (!NT_STATUS_IS_OK(status)) {
5869 status = fruit_streaminfo_rsrc(handle, fsp, smb_fname,
5870 mem_ctx, pnum_streams, pstreams);
5871 if (!NT_STATUS_IS_OK(status)) {
5875 return NT_STATUS_OK;
5878 static int fruit_ntimes(vfs_handle_struct *handle,
5879 const struct smb_filename *smb_fname,
5880 struct smb_file_time *ft)
5883 struct adouble *ad = NULL;
5884 struct fruit_config_data *config = NULL;
5886 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5889 if ((config->meta != FRUIT_META_NETATALK) ||
5890 null_timespec(ft->create_time))
5892 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
5895 DEBUG(10,("set btime for %s to %s\n", smb_fname_str_dbg(smb_fname),
5896 time_to_asc(convert_timespec_to_time_t(ft->create_time))));
5898 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5903 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX,
5904 convert_time_t_to_uint32_t(ft->create_time.tv_sec));
5906 rc = ad_set(handle, ad, smb_fname);
5912 DEBUG(1, ("fruit_ntimes: %s\n", smb_fname_str_dbg(smb_fname)));
5915 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
5918 static int fruit_fallocate(struct vfs_handle_struct *handle,
5919 struct files_struct *fsp,
5924 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5927 return SMB_VFS_NEXT_FALLOCATE(handle, fsp, mode, offset, len);
5930 /* Let the pwrite code path handle it. */
5935 static int fruit_ftruncate_rsrc_xattr(struct vfs_handle_struct *handle,
5936 struct files_struct *fsp,
5939 #ifdef HAVE_ATTROPEN
5940 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
5945 static int fruit_ftruncate_rsrc_adouble(struct vfs_handle_struct *handle,
5946 struct files_struct *fsp,
5950 struct adouble *ad = NULL;
5953 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
5955 DBG_DEBUG("ad_get [%s] failed [%s]\n",
5956 fsp_str_dbg(fsp), strerror(errno));
5960 ad_off = ad_getentryoff(ad, ADEID_RFORK);
5962 rc = ftruncate(fsp->fh->fd, offset + ad_off);
5968 ad_setentrylen(ad, ADEID_RFORK, offset);
5970 rc = ad_fset(handle, ad, fsp);
5972 DBG_ERR("ad_fset [%s] failed [%s]\n",
5973 fsp_str_dbg(fsp), strerror(errno));
5982 static int fruit_ftruncate_rsrc_stream(struct vfs_handle_struct *handle,
5983 struct files_struct *fsp,
5986 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
5989 static int fruit_ftruncate_rsrc(struct vfs_handle_struct *handle,
5990 struct files_struct *fsp,
5993 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5997 DBG_ERR("Failed to fetch fsp extension");
6001 switch (fio->config->rsrc) {
6002 case FRUIT_RSRC_XATTR:
6003 ret = fruit_ftruncate_rsrc_xattr(handle, fsp, offset);
6006 case FRUIT_RSRC_ADFILE:
6007 ret = fruit_ftruncate_rsrc_adouble(handle, fsp, offset);
6010 case FRUIT_RSRC_STREAM:
6011 ret = fruit_ftruncate_rsrc_stream(handle, fsp, offset);
6015 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
6023 static int fruit_ftruncate_meta(struct vfs_handle_struct *handle,
6024 struct files_struct *fsp,
6028 DBG_WARNING("ftruncate %s to %jd",
6029 fsp_str_dbg(fsp), (intmax_t)offset);
6030 /* OS X returns NT_STATUS_ALLOTTED_SPACE_EXCEEDED */
6035 /* OS X returns success but does nothing */
6036 DBG_INFO("ignoring ftruncate %s to %jd\n",
6037 fsp_str_dbg(fsp), (intmax_t)offset);
6041 static int fruit_ftruncate(struct vfs_handle_struct *handle,
6042 struct files_struct *fsp,
6045 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
6048 DBG_DEBUG("Path [%s] offset [%"PRIdMAX"]\n", fsp_str_dbg(fsp),
6052 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
6055 if (fio->type == ADOUBLE_META) {
6056 ret = fruit_ftruncate_meta(handle, fsp, offset);
6058 ret = fruit_ftruncate_rsrc(handle, fsp, offset);
6061 DBG_DEBUG("Path [%s] result [%d]\n", fsp_str_dbg(fsp), ret);
6065 static NTSTATUS fruit_create_file(vfs_handle_struct *handle,
6066 struct smb_request *req,
6067 uint16_t root_dir_fid,
6068 struct smb_filename *smb_fname,
6069 uint32_t access_mask,
6070 uint32_t share_access,
6071 uint32_t create_disposition,
6072 uint32_t create_options,
6073 uint32_t file_attributes,
6074 uint32_t oplock_request,
6075 struct smb2_lease *lease,
6076 uint64_t allocation_size,
6077 uint32_t private_flags,
6078 struct security_descriptor *sd,
6079 struct ea_list *ea_list,
6080 files_struct **result,
6082 const struct smb2_create_blobs *in_context_blobs,
6083 struct smb2_create_blobs *out_context_blobs)
6086 struct fruit_config_data *config = NULL;
6087 files_struct *fsp = NULL;
6088 struct fio *fio = NULL;
6089 bool internal_open = (oplock_request & INTERNAL_OPEN_ONLY);
6092 status = check_aapl(handle, req, in_context_blobs, out_context_blobs);
6093 if (!NT_STATUS_IS_OK(status)) {
6097 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
6098 return NT_STATUS_UNSUCCESSFUL);
6100 if (is_apple_stream(smb_fname) && !internal_open) {
6101 ret = ad_convert(handle, smb_fname);
6103 DBG_ERR("ad_convert() failed\n");
6104 return NT_STATUS_UNSUCCESSFUL;
6108 status = SMB_VFS_NEXT_CREATE_FILE(
6109 handle, req, root_dir_fid, smb_fname,
6110 access_mask, share_access,
6111 create_disposition, create_options,
6112 file_attributes, oplock_request,
6114 allocation_size, private_flags,
6115 sd, ea_list, result,
6116 pinfo, in_context_blobs, out_context_blobs);
6117 if (!NT_STATUS_IS_OK(status)) {
6123 if (global_fruit_config.nego_aapl) {
6124 if (config->posix_rename && fsp->is_directory) {
6126 * Enable POSIX directory rename behaviour
6128 fsp->posix_flags |= FSP_POSIX_FLAGS_RENAME;
6133 * If this is a plain open for existing files, opening an 0
6134 * byte size resource fork MUST fail with
6135 * NT_STATUS_OBJECT_NAME_NOT_FOUND.
6137 * Cf the vfs_fruit torture tests in test_rfork_create().
6139 if (global_fruit_config.nego_aapl &&
6140 create_disposition == FILE_OPEN &&
6141 smb_fname->st.st_ex_size == 0 &&
6142 is_ntfs_stream_smb_fname(smb_fname) &&
6143 !(is_ntfs_default_stream_smb_fname(smb_fname)))
6145 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
6149 fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
6150 if (fio != NULL && pinfo != NULL && *pinfo == FILE_WAS_CREATED) {
6151 fio->created = true;
6154 if (is_ntfs_stream_smb_fname(smb_fname)
6155 || fsp->is_directory) {
6159 if (config->locking == FRUIT_LOCKING_NETATALK) {
6160 status = fruit_check_access(
6164 if (!NT_STATUS_IS_OK(status)) {
6172 DEBUG(10, ("fruit_create_file: %s\n", nt_errstr(status)));
6175 close_file(req, fsp, ERROR_CLOSE);
6176 *result = fsp = NULL;
6182 static NTSTATUS fruit_readdir_attr(struct vfs_handle_struct *handle,
6183 const struct smb_filename *fname,
6184 TALLOC_CTX *mem_ctx,
6185 struct readdir_attr_data **pattr_data)
6187 struct fruit_config_data *config = NULL;
6188 struct readdir_attr_data *attr_data;
6192 SMB_VFS_HANDLE_GET_DATA(handle, config,
6193 struct fruit_config_data,
6194 return NT_STATUS_UNSUCCESSFUL);
6196 if (!global_fruit_config.nego_aapl) {
6197 return SMB_VFS_NEXT_READDIR_ATTR(handle, fname, mem_ctx, pattr_data);
6200 DEBUG(10, ("fruit_readdir_attr %s\n", fname->base_name));
6202 ret = ad_convert(handle, fname);
6204 DBG_ERR("ad_convert() failed\n");
6205 return NT_STATUS_UNSUCCESSFUL;
6208 *pattr_data = talloc_zero(mem_ctx, struct readdir_attr_data);
6209 if (*pattr_data == NULL) {
6210 return NT_STATUS_UNSUCCESSFUL;
6212 attr_data = *pattr_data;
6213 attr_data->type = RDATTR_AAPL;
6216 * Mac metadata: compressed FinderInfo, resource fork length
6219 status = readdir_attr_macmeta(handle, fname, attr_data);
6220 if (!NT_STATUS_IS_OK(status)) {
6222 * Error handling is tricky: if we return failure from
6223 * this function, the corresponding directory entry
6224 * will to be passed to the client, so we really just
6225 * want to error out on fatal errors.
6227 if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
6235 if (config->unix_info_enabled) {
6236 attr_data->attr_data.aapl.unix_mode = fname->st.st_ex_mode;
6242 if (!config->readdir_attr_max_access) {
6243 attr_data->attr_data.aapl.max_access = FILE_GENERIC_ALL;
6245 status = smbd_calculate_access_mask(
6249 SEC_FLAG_MAXIMUM_ALLOWED,
6250 &attr_data->attr_data.aapl.max_access);
6251 if (!NT_STATUS_IS_OK(status)) {
6256 return NT_STATUS_OK;
6259 DEBUG(1, ("fruit_readdir_attr %s, error: %s\n",
6260 fname->base_name, nt_errstr(status)));
6261 TALLOC_FREE(*pattr_data);
6265 static NTSTATUS fruit_fget_nt_acl(vfs_handle_struct *handle,
6267 uint32_t security_info,
6268 TALLOC_CTX *mem_ctx,
6269 struct security_descriptor **ppdesc)
6272 struct security_ace ace;
6274 struct fruit_config_data *config;
6276 SMB_VFS_HANDLE_GET_DATA(handle, config,
6277 struct fruit_config_data,
6278 return NT_STATUS_UNSUCCESSFUL);
6280 status = SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
6282 if (!NT_STATUS_IS_OK(status)) {
6287 * Add MS NFS style ACEs with uid, gid and mode
6289 if (!global_fruit_config.nego_aapl) {
6290 return NT_STATUS_OK;
6292 if (!config->unix_info_enabled) {
6293 return NT_STATUS_OK;
6296 /* First remove any existing ACE's with NFS style mode/uid/gid SIDs. */
6297 status = remove_virtual_nfs_aces(*ppdesc);
6298 if (!NT_STATUS_IS_OK(status)) {
6299 DBG_WARNING("failed to remove MS NFS style ACEs\n");
6303 /* MS NFS style mode */
6304 sid_compose(&sid, &global_sid_Unix_NFS_Mode, fsp->fsp_name->st.st_ex_mode);
6305 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6306 status = security_descriptor_dacl_add(*ppdesc, &ace);
6307 if (!NT_STATUS_IS_OK(status)) {
6308 DEBUG(1,("failed to add MS NFS style ACE\n"));
6312 /* MS NFS style uid */
6313 sid_compose(&sid, &global_sid_Unix_NFS_Users, fsp->fsp_name->st.st_ex_uid);
6314 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6315 status = security_descriptor_dacl_add(*ppdesc, &ace);
6316 if (!NT_STATUS_IS_OK(status)) {
6317 DEBUG(1,("failed to add MS NFS style ACE\n"));
6321 /* MS NFS style gid */
6322 sid_compose(&sid, &global_sid_Unix_NFS_Groups, fsp->fsp_name->st.st_ex_gid);
6323 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6324 status = security_descriptor_dacl_add(*ppdesc, &ace);
6325 if (!NT_STATUS_IS_OK(status)) {
6326 DEBUG(1,("failed to add MS NFS style ACE\n"));
6330 return NT_STATUS_OK;
6333 static NTSTATUS fruit_fset_nt_acl(vfs_handle_struct *handle,
6335 uint32_t security_info_sent,
6336 const struct security_descriptor *orig_psd)
6340 mode_t ms_nfs_mode = 0;
6342 struct security_descriptor *psd = NULL;
6343 uint32_t orig_num_aces = 0;
6345 if (orig_psd->dacl != NULL) {
6346 orig_num_aces = orig_psd->dacl->num_aces;
6349 psd = security_descriptor_copy(talloc_tos(), orig_psd);
6351 return NT_STATUS_NO_MEMORY;
6354 DBG_DEBUG("fruit_fset_nt_acl: %s\n", fsp_str_dbg(fsp));
6356 status = check_ms_nfs(handle, fsp, psd, &ms_nfs_mode, &do_chmod);
6357 if (!NT_STATUS_IS_OK(status)) {
6358 DEBUG(1, ("fruit_fset_nt_acl: check_ms_nfs failed%s\n", fsp_str_dbg(fsp)));
6364 * If only ms_nfs ACE entries were sent, ensure we set the DACL
6365 * sent/present flags correctly now we've removed them.
6368 if (orig_num_aces != 0) {
6370 * Are there any ACE's left ?
6372 if (psd->dacl->num_aces == 0) {
6373 /* No - clear the DACL sent/present flags. */
6374 security_info_sent &= ~SECINFO_DACL;
6375 psd->type &= ~SEC_DESC_DACL_PRESENT;
6379 status = SMB_VFS_NEXT_FSET_NT_ACL(handle, fsp, security_info_sent, psd);
6380 if (!NT_STATUS_IS_OK(status)) {
6381 DEBUG(1, ("fruit_fset_nt_acl: SMB_VFS_NEXT_FSET_NT_ACL failed%s\n", fsp_str_dbg(fsp)));
6387 if (fsp->fh->fd != -1) {
6388 result = SMB_VFS_FCHMOD(fsp, ms_nfs_mode);
6390 result = SMB_VFS_CHMOD(fsp->conn,
6396 DEBUG(1, ("chmod: %s, result: %d, %04o error %s\n", fsp_str_dbg(fsp),
6397 result, (unsigned)ms_nfs_mode,
6399 status = map_nt_error_from_unix(errno);
6406 return NT_STATUS_OK;
6409 static struct vfs_offload_ctx *fruit_offload_ctx;
6411 struct fruit_offload_read_state {
6412 struct vfs_handle_struct *handle;
6413 struct tevent_context *ev;
6419 static void fruit_offload_read_done(struct tevent_req *subreq);
6421 static struct tevent_req *fruit_offload_read_send(
6422 TALLOC_CTX *mem_ctx,
6423 struct tevent_context *ev,
6424 struct vfs_handle_struct *handle,
6431 struct tevent_req *req = NULL;
6432 struct tevent_req *subreq = NULL;
6433 struct fruit_offload_read_state *state = NULL;
6435 req = tevent_req_create(mem_ctx, &state,
6436 struct fruit_offload_read_state);
6440 *state = (struct fruit_offload_read_state) {
6447 subreq = SMB_VFS_NEXT_OFFLOAD_READ_SEND(mem_ctx, ev, handle, fsp,
6448 fsctl, ttl, offset, to_copy);
6449 if (tevent_req_nomem(subreq, req)) {
6450 return tevent_req_post(req, ev);
6452 tevent_req_set_callback(subreq, fruit_offload_read_done, req);
6456 static void fruit_offload_read_done(struct tevent_req *subreq)
6458 struct tevent_req *req = tevent_req_callback_data(
6459 subreq, struct tevent_req);
6460 struct fruit_offload_read_state *state = tevent_req_data(
6461 req, struct fruit_offload_read_state);
6464 status = SMB_VFS_NEXT_OFFLOAD_READ_RECV(subreq,
6468 TALLOC_FREE(subreq);
6469 if (tevent_req_nterror(req, status)) {
6473 if (state->fsctl != FSCTL_SRV_REQUEST_RESUME_KEY) {
6474 tevent_req_done(req);
6478 status = vfs_offload_token_ctx_init(state->fsp->conn->sconn->client,
6479 &fruit_offload_ctx);
6480 if (tevent_req_nterror(req, status)) {
6484 status = vfs_offload_token_db_store_fsp(fruit_offload_ctx,
6487 if (tevent_req_nterror(req, status)) {
6491 tevent_req_done(req);
6495 static NTSTATUS fruit_offload_read_recv(struct tevent_req *req,
6496 struct vfs_handle_struct *handle,
6497 TALLOC_CTX *mem_ctx,
6500 struct fruit_offload_read_state *state = tevent_req_data(
6501 req, struct fruit_offload_read_state);
6504 if (tevent_req_is_nterror(req, &status)) {
6505 tevent_req_received(req);
6509 token->length = state->token.length;
6510 token->data = talloc_move(mem_ctx, &state->token.data);
6512 tevent_req_received(req);
6513 return NT_STATUS_OK;
6516 struct fruit_offload_write_state {
6517 struct vfs_handle_struct *handle;
6519 struct files_struct *src_fsp;
6520 struct files_struct *dst_fsp;
6524 static void fruit_offload_write_done(struct tevent_req *subreq);
6525 static struct tevent_req *fruit_offload_write_send(struct vfs_handle_struct *handle,
6526 TALLOC_CTX *mem_ctx,
6527 struct tevent_context *ev,
6530 off_t transfer_offset,
6531 struct files_struct *dest_fsp,
6535 struct tevent_req *req, *subreq;
6536 struct fruit_offload_write_state *state;
6538 struct fruit_config_data *config;
6539 off_t src_off = transfer_offset;
6540 files_struct *src_fsp = NULL;
6541 off_t to_copy = num;
6542 bool copyfile_enabled = false;
6544 DEBUG(10,("soff: %ju, doff: %ju, len: %ju\n",
6545 (uintmax_t)src_off, (uintmax_t)dest_off, (uintmax_t)num));
6547 SMB_VFS_HANDLE_GET_DATA(handle, config,
6548 struct fruit_config_data,
6551 req = tevent_req_create(mem_ctx, &state,
6552 struct fruit_offload_write_state);
6556 state->handle = handle;
6557 state->dst_fsp = dest_fsp;
6560 case FSCTL_SRV_COPYCHUNK:
6561 case FSCTL_SRV_COPYCHUNK_WRITE:
6562 copyfile_enabled = config->copyfile_enabled;
6569 * Check if this a OS X copyfile style copychunk request with
6570 * a requested chunk count of 0 that was translated to a
6571 * offload_write_send VFS call overloading the parameters src_off
6572 * = dest_off = num = 0.
6574 if (copyfile_enabled && num == 0 && src_off == 0 && dest_off == 0) {
6575 status = vfs_offload_token_db_fetch_fsp(
6576 fruit_offload_ctx, token, &src_fsp);
6577 if (tevent_req_nterror(req, status)) {
6578 return tevent_req_post(req, ev);
6580 state->src_fsp = src_fsp;
6582 status = vfs_stat_fsp(src_fsp);
6583 if (tevent_req_nterror(req, status)) {
6584 return tevent_req_post(req, ev);
6587 to_copy = src_fsp->fsp_name->st.st_ex_size;
6588 state->is_copyfile = true;
6591 subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle,
6600 if (tevent_req_nomem(subreq, req)) {
6601 return tevent_req_post(req, ev);
6604 tevent_req_set_callback(subreq, fruit_offload_write_done, req);
6608 static void fruit_offload_write_done(struct tevent_req *subreq)
6610 struct tevent_req *req = tevent_req_callback_data(
6611 subreq, struct tevent_req);
6612 struct fruit_offload_write_state *state = tevent_req_data(
6613 req, struct fruit_offload_write_state);
6615 unsigned int num_streams = 0;
6616 struct stream_struct *streams = NULL;
6618 struct smb_filename *src_fname_tmp = NULL;
6619 struct smb_filename *dst_fname_tmp = NULL;
6621 status = SMB_VFS_NEXT_OFFLOAD_WRITE_RECV(state->handle,
6624 TALLOC_FREE(subreq);
6625 if (tevent_req_nterror(req, status)) {
6629 if (!state->is_copyfile) {
6630 tevent_req_done(req);
6635 * Now copy all remaining streams. We know the share supports
6636 * streams, because we're in vfs_fruit. We don't do this async
6637 * because streams are few and small.
6639 status = vfs_streaminfo(state->handle->conn, state->src_fsp,
6640 state->src_fsp->fsp_name,
6641 req, &num_streams, &streams);
6642 if (tevent_req_nterror(req, status)) {
6646 if (num_streams == 1) {
6647 /* There is always one stream, ::$DATA. */
6648 tevent_req_done(req);
6652 for (i = 0; i < num_streams; i++) {
6653 DEBUG(10, ("%s: stream: '%s'/%zu\n",
6654 __func__, streams[i].name, (size_t)streams[i].size));
6656 src_fname_tmp = synthetic_smb_fname(
6658 state->src_fsp->fsp_name->base_name,
6661 state->src_fsp->fsp_name->flags);
6662 if (tevent_req_nomem(src_fname_tmp, req)) {
6666 if (is_ntfs_default_stream_smb_fname(src_fname_tmp)) {
6667 TALLOC_FREE(src_fname_tmp);
6671 dst_fname_tmp = synthetic_smb_fname(
6673 state->dst_fsp->fsp_name->base_name,
6676 state->dst_fsp->fsp_name->flags);
6677 if (tevent_req_nomem(dst_fname_tmp, req)) {
6678 TALLOC_FREE(src_fname_tmp);
6682 status = copy_file(req,
6683 state->handle->conn,
6686 OPENX_FILE_CREATE_IF_NOT_EXIST,
6688 if (!NT_STATUS_IS_OK(status)) {
6689 DEBUG(1, ("%s: copy %s to %s failed: %s\n", __func__,
6690 smb_fname_str_dbg(src_fname_tmp),
6691 smb_fname_str_dbg(dst_fname_tmp),
6692 nt_errstr(status)));
6693 TALLOC_FREE(src_fname_tmp);
6694 TALLOC_FREE(dst_fname_tmp);
6695 tevent_req_nterror(req, status);
6699 TALLOC_FREE(src_fname_tmp);
6700 TALLOC_FREE(dst_fname_tmp);
6703 TALLOC_FREE(streams);
6704 TALLOC_FREE(src_fname_tmp);
6705 TALLOC_FREE(dst_fname_tmp);
6706 tevent_req_done(req);
6709 static NTSTATUS fruit_offload_write_recv(struct vfs_handle_struct *handle,
6710 struct tevent_req *req,
6713 struct fruit_offload_write_state *state = tevent_req_data(
6714 req, struct fruit_offload_write_state);
6717 if (tevent_req_is_nterror(req, &status)) {
6718 DEBUG(1, ("server side copy chunk failed: %s\n",
6719 nt_errstr(status)));
6721 tevent_req_received(req);
6725 *copied = state->copied;
6726 tevent_req_received(req);
6728 return NT_STATUS_OK;
6731 static char *fruit_get_bandsize_line(char **lines, int numlines)
6734 static bool re_initialized = false;
6738 if (!re_initialized) {
6739 ret = regcomp(&re, "^[[:blank:]]*<key>band-size</key>$", 0);
6743 re_initialized = true;
6746 for (i = 0; i < numlines; i++) {
6747 regmatch_t matches[1];
6749 ret = regexec(&re, lines[i], 1, matches, 0);
6752 * Check if the match was on the last line, sa we want
6753 * the subsequent line.
6755 if (i + 1 == numlines) {
6758 return lines[i + 1];
6760 if (ret != REG_NOMATCH) {
6768 static bool fruit_get_bandsize_from_line(char *line, size_t *_band_size)
6771 static bool re_initialized = false;
6772 regmatch_t matches[2];
6777 if (!re_initialized) {
6780 "<integer>\\([[:digit:]]*\\)</integer>$",
6785 re_initialized = true;
6788 ret = regexec(&re, line, 2, matches, 0);
6790 DBG_ERR("regex failed [%s]\n", line);
6794 line[matches[1].rm_eo] = '\0';
6796 ok = conv_str_u64(&line[matches[1].rm_so], &band_size);
6800 *_band_size = (size_t)band_size;
6805 * This reads and parses an Info.plist from a TM sparsebundle looking for the
6806 * "band-size" key and value.
6808 static bool fruit_get_bandsize(vfs_handle_struct *handle,
6812 #define INFO_PLIST_MAX_SIZE 64*1024
6814 struct smb_filename *smb_fname = NULL;
6815 files_struct *fsp = NULL;
6816 uint8_t *file_data = NULL;
6817 char **lines = NULL;
6818 char *band_size_line = NULL;
6819 size_t plist_file_size;
6826 plist = talloc_asprintf(talloc_tos(),
6828 handle->conn->connectpath,
6830 if (plist == NULL) {
6835 smb_fname = synthetic_smb_fname(talloc_tos(), plist, NULL, NULL, 0);
6836 if (smb_fname == NULL) {
6841 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
6843 DBG_INFO("Ignoring Sparsebundle without Info.plist [%s]\n", dir);
6848 plist_file_size = smb_fname->st.st_ex_size;
6850 if (plist_file_size > INFO_PLIST_MAX_SIZE) {
6851 DBG_INFO("%s is too large, ignoring\n", plist);
6856 status = SMB_VFS_NEXT_CREATE_FILE(
6859 0, /* root_dir_fid */
6860 smb_fname, /* fname */
6861 FILE_GENERIC_READ, /* access_mask */
6862 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
6863 FILE_OPEN, /* create_disposition */
6864 0, /* create_options */
6865 0, /* file_attributes */
6866 INTERNAL_OPEN_ONLY, /* oplock_request */
6868 0, /* allocation_size */
6869 0, /* private_flags */
6874 NULL, NULL); /* create context */
6875 if (!NT_STATUS_IS_OK(status)) {
6876 DBG_INFO("Opening [%s] failed [%s]\n",
6877 smb_fname_str_dbg(smb_fname), nt_errstr(status));
6882 file_data = talloc_array(talloc_tos(), uint8_t, plist_file_size);
6883 if (file_data == NULL) {
6888 nread = SMB_VFS_NEXT_PREAD(handle, fsp, file_data, plist_file_size, 0);
6889 if (nread != plist_file_size) {
6890 DBG_ERR("Short read on [%s]: %zu/%zd\n",
6891 fsp_str_dbg(fsp), nread, plist_file_size);
6897 status = close_file(NULL, fsp, NORMAL_CLOSE);
6899 if (!NT_STATUS_IS_OK(status)) {
6900 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
6905 lines = file_lines_parse((char *)file_data,
6909 if (lines == NULL) {
6914 band_size_line = fruit_get_bandsize_line(lines, numlines);
6915 if (band_size_line == NULL) {
6916 DBG_ERR("Didn't find band-size key in [%s]\n",
6917 smb_fname_str_dbg(smb_fname));
6922 ok = fruit_get_bandsize_from_line(band_size_line, band_size);
6924 DBG_ERR("fruit_get_bandsize_from_line failed\n");
6928 DBG_DEBUG("Parsed band-size [%zu] for [%s]\n", *band_size, plist);
6932 status = close_file(NULL, fsp, NORMAL_CLOSE);
6933 if (!NT_STATUS_IS_OK(status)) {
6934 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
6939 TALLOC_FREE(smb_fname);
6940 TALLOC_FREE(file_data);
6945 struct fruit_disk_free_state {
6949 static bool fruit_get_num_bands(vfs_handle_struct *handle,
6954 struct smb_filename *bands_dir = NULL;
6956 struct dirent *e = NULL;
6960 path = talloc_asprintf(talloc_tos(),
6962 handle->conn->connectpath,
6968 bands_dir = synthetic_smb_fname(talloc_tos(),
6974 if (bands_dir == NULL) {
6978 d = SMB_VFS_NEXT_OPENDIR(handle, bands_dir, NULL, 0);
6980 TALLOC_FREE(bands_dir);
6986 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
6988 e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
6990 if (ISDOT(e->d_name) || ISDOTDOT(e->d_name)) {
6996 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
6998 TALLOC_FREE(bands_dir);
7002 DBG_DEBUG("%zu bands in [%s]\n", nbands, smb_fname_str_dbg(bands_dir));
7004 TALLOC_FREE(bands_dir);
7010 static bool fruit_tmsize_do_dirent(vfs_handle_struct *handle,
7011 struct fruit_disk_free_state *state,
7016 size_t sparsebundle_strlen = strlen("sparsebundle");
7017 size_t bandsize = 0;
7021 p = strstr(e->d_name, "sparsebundle");
7026 if (p[sparsebundle_strlen] != '\0') {
7030 DBG_DEBUG("Processing sparsebundle [%s]\n", e->d_name);
7032 ok = fruit_get_bandsize(handle, e->d_name, &bandsize);
7035 * Beware of race conditions: this may be an uninitialized
7036 * Info.plist that a client is just creating. We don't want let
7037 * this to trigger complete failure.
7039 DBG_ERR("Processing sparsebundle [%s] failed\n", e->d_name);
7043 ok = fruit_get_num_bands(handle, e->d_name, &nbands);
7046 * Beware of race conditions: this may be a backup sparsebundle
7047 * in an early stage lacking a bands subdirectory. We don't want
7048 * let this to trigger complete failure.
7050 DBG_ERR("Processing sparsebundle [%s] failed\n", e->d_name);
7054 if (bandsize > SIZE_MAX/nbands) {
7055 DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
7059 tm_size = bandsize * nbands;
7061 if (state->total_size + tm_size < state->total_size) {
7062 DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
7067 state->total_size += tm_size;
7069 DBG_DEBUG("[%s] tm_size [%jd] total_size [%jd]\n",
7070 e->d_name, (intmax_t)tm_size, (intmax_t)state->total_size);
7076 * Calculate used size of a TimeMachine volume
7078 * This assumes that the volume is used only for TimeMachine.
7080 * - readdir(basedir of share), then
7081 * - for every element that matches regex "^\(.*\)\.sparsebundle$" :
7082 * - parse "\1.sparsebundle/Info.plist" and read the band-size XML key
7083 * - count band files in "\1.sparsebundle/bands/"
7084 * - calculate used size of all bands: band_count * band_size
7086 static uint64_t fruit_disk_free(vfs_handle_struct *handle,
7087 const struct smb_filename *smb_fname,
7092 struct fruit_config_data *config = NULL;
7093 struct fruit_disk_free_state state = {0};
7095 struct dirent *e = NULL;
7101 SMB_VFS_HANDLE_GET_DATA(handle, config,
7102 struct fruit_config_data,
7105 if (!config->time_machine ||
7106 config->time_machine_max_size == 0)
7108 return SMB_VFS_NEXT_DISK_FREE(handle,
7115 d = SMB_VFS_NEXT_OPENDIR(handle, smb_fname, NULL, 0);
7120 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
7122 e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
7124 ok = fruit_tmsize_do_dirent(handle, &state, e);
7126 SMB_VFS_NEXT_CLOSEDIR(handle, d);
7131 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
7136 dsize = config->time_machine_max_size / 512;
7137 dfree = dsize - (state.total_size / 512);
7138 if (dfree > dsize) {
7148 static struct vfs_fn_pointers vfs_fruit_fns = {
7149 .connect_fn = fruit_connect,
7150 .disk_free_fn = fruit_disk_free,
7152 /* File operations */
7153 .chmod_fn = fruit_chmod,
7154 .chown_fn = fruit_chown,
7155 .unlink_fn = fruit_unlink,
7156 .rename_fn = fruit_rename,
7157 .rmdir_fn = fruit_rmdir,
7158 .open_fn = fruit_open,
7159 .close_fn = fruit_close,
7160 .pread_fn = fruit_pread,
7161 .pwrite_fn = fruit_pwrite,
7162 .pread_send_fn = fruit_pread_send,
7163 .pread_recv_fn = fruit_pread_recv,
7164 .pwrite_send_fn = fruit_pwrite_send,
7165 .pwrite_recv_fn = fruit_pwrite_recv,
7166 .stat_fn = fruit_stat,
7167 .lstat_fn = fruit_lstat,
7168 .fstat_fn = fruit_fstat,
7169 .streaminfo_fn = fruit_streaminfo,
7170 .ntimes_fn = fruit_ntimes,
7171 .ftruncate_fn = fruit_ftruncate,
7172 .fallocate_fn = fruit_fallocate,
7173 .create_file_fn = fruit_create_file,
7174 .readdir_attr_fn = fruit_readdir_attr,
7175 .offload_read_send_fn = fruit_offload_read_send,
7176 .offload_read_recv_fn = fruit_offload_read_recv,
7177 .offload_write_send_fn = fruit_offload_write_send,
7178 .offload_write_recv_fn = fruit_offload_write_recv,
7180 /* NT ACL operations */
7181 .fget_nt_acl_fn = fruit_fget_nt_acl,
7182 .fset_nt_acl_fn = fruit_fset_nt_acl,
7186 NTSTATUS vfs_fruit_init(TALLOC_CTX *ctx)
7188 NTSTATUS ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "fruit",
7190 if (!NT_STATUS_IS_OK(ret)) {
7194 vfs_fruit_debug_level = debug_add_class("fruit");
7195 if (vfs_fruit_debug_level == -1) {
7196 vfs_fruit_debug_level = DBGC_VFS;
7197 DEBUG(0, ("%s: Couldn't register custom debugging class!\n",
7200 DEBUG(10, ("%s: Debug class number of '%s': %d\n",
7201 "vfs_fruit_init","fruit",vfs_fruit_debug_level));