2 * OS X and Netatalk interoperability VFS module for Samba-3.x
4 * Copyright (C) Ralph Boehme, 2013, 2014
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
21 #include "MacExtensions.h"
22 #include "smbd/smbd.h"
23 #include "system/filesys.h"
24 #include "lib/util/time.h"
25 #include "system/shmem.h"
26 #include "locking/proto.h"
27 #include "smbd/globals.h"
29 #include "libcli/security/security.h"
30 #include "../libcli/smb/smb2_create_ctx.h"
31 #include "lib/util/sys_rw.h"
32 #include "lib/util/tevent_ntstatus.h"
33 #include "lib/util/tevent_unix.h"
34 #include "offload_token.h"
35 #include "string_replace.h"
37 #include <gnutls/gnutls.h>
38 #include <gnutls/crypto.h>
41 * Enhanced OS X and Netatalk compatibility
42 * ========================================
44 * This modules takes advantage of vfs_streams_xattr and
45 * vfs_catia. VFS modules vfs_fruit and vfs_streams_xattr must be
46 * loaded in the correct order:
48 * vfs modules = catia fruit streams_xattr
50 * The module intercepts the OS X special streams "AFP_AfpInfo" and
51 * "AFP_Resource" and handles them in a special way. All other named
52 * streams are deferred to vfs_streams_xattr.
54 * The OS X client maps all NTFS illegal characters to the Unicode
55 * private range. This module optionally stores the charcters using
56 * their native ASCII encoding using vfs_catia. If you're not enabling
57 * this feature, you can skip catia from vfs modules.
59 * Finally, open modes are optionally checked against Netatalk AFP
62 * The "AFP_AfpInfo" named stream is a binary blob containing OS X
63 * extended metadata for files and directories. This module optionally
64 * reads and stores this metadata in a way compatible with Netatalk 3
65 * which stores the metadata in an EA "org.netatalk.metadata". Cf
66 * source3/include/MacExtensions.h for a description of the binary
69 * The "AFP_Resource" named stream may be arbitrarily large, thus it
70 * can't be stored in an xattr on most filesystem. ZFS on Solaris is
71 * the only available filesystem where xattrs can be of any size and
72 * the OS supports using the file APIs for xattrs.
74 * The AFP_Resource stream is stored in an AppleDouble file prepending
75 * "._" to the filename. On Solaris with ZFS the stream is optionally
76 * stored in an EA "org.netatalk.resource".
82 * The OS X SMB client sends xattrs as ADS too. For xattr interop with
83 * other protocols you may want to adjust the xattr names the VFS
84 * module vfs_streams_xattr uses for storing ADS's. This defaults to
85 * user.DosStream.ADS_NAME:$DATA and can be changed by specifying
86 * these module parameters:
88 * streams_xattr:prefix = user.
89 * streams_xattr:store_stream_type = false
95 * - log diagnostic if any needed VFS module is not loaded
96 * (eg with lp_vfs_objects())
100 static int vfs_fruit_debug_level = DBGC_VFS;
102 static struct global_fruit_config {
103 bool nego_aapl; /* client negotiated AAPL */
105 } global_fruit_config;
108 #define DBGC_CLASS vfs_fruit_debug_level
110 #define FRUIT_PARAM_TYPE_NAME "fruit"
111 #define ADOUBLE_NAME_PREFIX "._"
113 #define NETATALK_META_XATTR "org.netatalk.Metadata"
114 #define NETATALK_RSRC_XATTR "org.netatalk.ResourceFork"
116 #if defined(HAVE_ATTROPEN)
117 #define AFPINFO_EA_NETATALK NETATALK_META_XATTR
118 #define AFPRESOURCE_EA_NETATALK NETATALK_RSRC_XATTR
120 #define AFPINFO_EA_NETATALK "user." NETATALK_META_XATTR
121 #define AFPRESOURCE_EA_NETATALK "user." NETATALK_RSRC_XATTR
124 enum apple_fork {APPLE_FORK_DATA, APPLE_FORK_RSRC};
126 enum fruit_rsrc {FRUIT_RSRC_STREAM, FRUIT_RSRC_ADFILE, FRUIT_RSRC_XATTR};
127 enum fruit_meta {FRUIT_META_STREAM, FRUIT_META_NETATALK};
128 enum fruit_locking {FRUIT_LOCKING_NETATALK, FRUIT_LOCKING_NONE};
129 enum fruit_encoding {FRUIT_ENC_NATIVE, FRUIT_ENC_PRIVATE};
131 struct fruit_config_data {
132 enum fruit_rsrc rsrc;
133 enum fruit_meta meta;
134 enum fruit_locking locking;
135 enum fruit_encoding encoding;
136 bool use_aapl; /* config from smb.conf */
138 bool readdir_attr_enabled;
139 bool unix_info_enabled;
140 bool copyfile_enabled;
141 bool veto_appledouble;
143 bool aapl_zero_file_id;
146 off_t time_machine_max_size;
147 bool wipe_intentionally_left_blank_rfork;
148 bool delete_empty_adfiles;
151 * Additional options, all enabled by default,
152 * possibly useful for analyzing performance. The associated
153 * operations with each of them may be expensive, so having
154 * the chance to disable them individually gives a chance
155 * tweaking the setup for the particular usecase.
157 bool readdir_attr_rsize;
158 bool readdir_attr_finder_info;
159 bool readdir_attr_max_access;
162 static const struct enum_list fruit_rsrc[] = {
163 {FRUIT_RSRC_STREAM, "stream"}, /* pass on to vfs_streams_xattr */
164 {FRUIT_RSRC_ADFILE, "file"}, /* ._ AppleDouble file */
165 {FRUIT_RSRC_XATTR, "xattr"}, /* Netatalk compatible xattr (ZFS only) */
169 static const struct enum_list fruit_meta[] = {
170 {FRUIT_META_STREAM, "stream"}, /* pass on to vfs_streams_xattr */
171 {FRUIT_META_NETATALK, "netatalk"}, /* Netatalk compatible xattr */
175 static const struct enum_list fruit_locking[] = {
176 {FRUIT_LOCKING_NETATALK, "netatalk"}, /* synchronize locks with Netatalk */
177 {FRUIT_LOCKING_NONE, "none"},
181 static const struct enum_list fruit_encoding[] = {
182 {FRUIT_ENC_NATIVE, "native"}, /* map unicode private chars to ASCII */
183 {FRUIT_ENC_PRIVATE, "private"}, /* keep unicode private chars */
187 static const char *fruit_catia_maps =
188 "0x01:0xf001,0x02:0xf002,0x03:0xf003,0x04:0xf004,"
189 "0x05:0xf005,0x06:0xf006,0x07:0xf007,0x08:0xf008,"
190 "0x09:0xf009,0x0a:0xf00a,0x0b:0xf00b,0x0c:0xf00c,"
191 "0x0d:0xf00d,0x0e:0xf00e,0x0f:0xf00f,0x10:0xf010,"
192 "0x11:0xf011,0x12:0xf012,0x13:0xf013,0x14:0xf014,"
193 "0x15:0xf015,0x16:0xf016,0x17:0xf017,0x18:0xf018,"
194 "0x19:0xf019,0x1a:0xf01a,0x1b:0xf01b,0x1c:0xf01c,"
195 "0x1d:0xf01d,0x1e:0xf01e,0x1f:0xf01f,"
196 "0x22:0xf020,0x2a:0xf021,0x3a:0xf022,0x3c:0xf023,"
197 "0x3e:0xf024,0x3f:0xf025,0x5c:0xf026,0x7c:0xf027,"
200 /*****************************************************************************
201 * Defines, functions and data structures that deal with AppleDouble
202 *****************************************************************************/
205 * There are two AppleDouble blobs we deal with:
207 * - ADOUBLE_META - AppleDouble blob used by Netatalk for storing
208 * metadata in an xattr
210 * - ADOUBLE_RSRC - AppleDouble blob used by OS X and Netatalk in
213 typedef enum {ADOUBLE_META, ADOUBLE_RSRC} adouble_type_t;
216 #define AD_VERSION2 0x00020000
217 #define AD_VERSION AD_VERSION2
220 * AppleDouble entry IDs.
222 #define ADEID_DFORK 1
223 #define ADEID_RFORK 2
225 #define ADEID_COMMENT 4
226 #define ADEID_ICONBW 5
227 #define ADEID_ICONCOL 6
228 #define ADEID_FILEI 7
229 #define ADEID_FILEDATESI 8
230 #define ADEID_FINDERI 9
231 #define ADEID_MACFILEI 10
232 #define ADEID_PRODOSFILEI 11
233 #define ADEID_MSDOSFILEI 12
234 #define ADEID_SHORTNAME 13
235 #define ADEID_AFPFILEI 14
238 /* Private Netatalk entries */
239 #define ADEID_PRIVDEV 16
240 #define ADEID_PRIVINO 17
241 #define ADEID_PRIVSYN 18
242 #define ADEID_PRIVID 19
243 #define ADEID_MAX (ADEID_PRIVID + 1)
246 * These are the real ids for the private entries,
247 * as stored in the adouble file
249 #define AD_DEV 0x80444556
250 #define AD_INO 0x80494E4F
251 #define AD_SYN 0x8053594E
252 #define AD_ID 0x8053567E
254 /* Number of actually used entries */
255 #define ADEID_NUM_XATTR 8
256 #define ADEID_NUM_DOT_UND 2
257 #define ADEID_NUM_RSRC_XATTR 1
259 /* AppleDouble magic */
260 #define AD_APPLESINGLE_MAGIC 0x00051600
261 #define AD_APPLEDOUBLE_MAGIC 0x00051607
262 #define AD_MAGIC AD_APPLEDOUBLE_MAGIC
264 /* Sizes of relevant entry bits */
265 #define ADEDLEN_MAGIC 4
266 #define ADEDLEN_VERSION 4
267 #define ADEDLEN_FILLER 16
268 #define AD_FILLER_TAG "Netatalk " /* should be 16 bytes */
269 #define AD_FILLER_TAG_OSX "Mac OS X " /* should be 16 bytes */
270 #define ADEDLEN_NENTRIES 2
271 #define AD_HEADER_LEN (ADEDLEN_MAGIC + ADEDLEN_VERSION + \
272 ADEDLEN_FILLER + ADEDLEN_NENTRIES) /* 26 */
273 #define AD_ENTRY_LEN_EID 4
274 #define AD_ENTRY_LEN_OFF 4
275 #define AD_ENTRY_LEN_LEN 4
276 #define AD_ENTRY_LEN (AD_ENTRY_LEN_EID + AD_ENTRY_LEN_OFF + AD_ENTRY_LEN_LEN)
279 #define ADEDLEN_NAME 255
280 #define ADEDLEN_COMMENT 200
281 #define ADEDLEN_FILEI 16
282 #define ADEDLEN_FINDERI 32
283 #define ADEDLEN_FILEDATESI 16
284 #define ADEDLEN_SHORTNAME 12 /* length up to 8.3 */
285 #define ADEDLEN_AFPFILEI 4
286 #define ADEDLEN_MACFILEI 4
287 #define ADEDLEN_PRODOSFILEI 8
288 #define ADEDLEN_MSDOSFILEI 2
289 #define ADEDLEN_DID 4
290 #define ADEDLEN_PRIVDEV 8
291 #define ADEDLEN_PRIVINO 8
292 #define ADEDLEN_PRIVSYN 8
293 #define ADEDLEN_PRIVID 4
296 #define ADEDOFF_MAGIC 0
297 #define ADEDOFF_VERSION (ADEDOFF_MAGIC + ADEDLEN_MAGIC)
298 #define ADEDOFF_FILLER (ADEDOFF_VERSION + ADEDLEN_VERSION)
299 #define ADEDOFF_NENTRIES (ADEDOFF_FILLER + ADEDLEN_FILLER)
301 #define ADEDOFF_FINDERI_XATTR (AD_HEADER_LEN + \
302 (ADEID_NUM_XATTR * AD_ENTRY_LEN))
303 #define ADEDOFF_COMMENT_XATTR (ADEDOFF_FINDERI_XATTR + ADEDLEN_FINDERI)
304 #define ADEDOFF_FILEDATESI_XATTR (ADEDOFF_COMMENT_XATTR + ADEDLEN_COMMENT)
305 #define ADEDOFF_AFPFILEI_XATTR (ADEDOFF_FILEDATESI_XATTR + \
307 #define ADEDOFF_PRIVDEV_XATTR (ADEDOFF_AFPFILEI_XATTR + ADEDLEN_AFPFILEI)
308 #define ADEDOFF_PRIVINO_XATTR (ADEDOFF_PRIVDEV_XATTR + ADEDLEN_PRIVDEV)
309 #define ADEDOFF_PRIVSYN_XATTR (ADEDOFF_PRIVINO_XATTR + ADEDLEN_PRIVINO)
310 #define ADEDOFF_PRIVID_XATTR (ADEDOFF_PRIVSYN_XATTR + ADEDLEN_PRIVSYN)
312 #define ADEDOFF_FINDERI_DOT_UND (AD_HEADER_LEN + \
313 (ADEID_NUM_DOT_UND * AD_ENTRY_LEN))
314 #define ADEDOFF_RFORK_DOT_UND (ADEDOFF_FINDERI_DOT_UND + ADEDLEN_FINDERI)
316 #define AD_DATASZ_XATTR (AD_HEADER_LEN + \
317 (ADEID_NUM_XATTR * AD_ENTRY_LEN) + \
318 ADEDLEN_FINDERI + ADEDLEN_COMMENT + \
319 ADEDLEN_FILEDATESI + ADEDLEN_AFPFILEI + \
320 ADEDLEN_PRIVDEV + ADEDLEN_PRIVINO + \
321 ADEDLEN_PRIVSYN + ADEDLEN_PRIVID)
323 #if AD_DATASZ_XATTR != 402
324 #error bad size for AD_DATASZ_XATTR
327 #define AD_DATASZ_DOT_UND (AD_HEADER_LEN + \
328 (ADEID_NUM_DOT_UND * AD_ENTRY_LEN) + \
330 #if AD_DATASZ_DOT_UND != 82
331 #error bad size for AD_DATASZ_DOT_UND
335 * Sharemode locks fcntl() offsets
337 #if _FILE_OFFSET_BITS == 64 || defined(HAVE_LARGEFILE)
338 #define AD_FILELOCK_BASE (UINT64_C(0x7FFFFFFFFFFFFFFF) - 9)
340 #define AD_FILELOCK_BASE (UINT32_C(0x7FFFFFFF) - 9)
342 #define BYTELOCK_MAX (AD_FILELOCK_BASE - 1)
344 #define AD_FILELOCK_OPEN_WR (AD_FILELOCK_BASE + 0)
345 #define AD_FILELOCK_OPEN_RD (AD_FILELOCK_BASE + 1)
346 #define AD_FILELOCK_RSRC_OPEN_WR (AD_FILELOCK_BASE + 2)
347 #define AD_FILELOCK_RSRC_OPEN_RD (AD_FILELOCK_BASE + 3)
348 #define AD_FILELOCK_DENY_WR (AD_FILELOCK_BASE + 4)
349 #define AD_FILELOCK_DENY_RD (AD_FILELOCK_BASE + 5)
350 #define AD_FILELOCK_RSRC_DENY_WR (AD_FILELOCK_BASE + 6)
351 #define AD_FILELOCK_RSRC_DENY_RD (AD_FILELOCK_BASE + 7)
352 #define AD_FILELOCK_OPEN_NONE (AD_FILELOCK_BASE + 8)
353 #define AD_FILELOCK_RSRC_OPEN_NONE (AD_FILELOCK_BASE + 9)
355 /* Time stuff we overload the bits a little */
356 #define AD_DATE_CREATE 0
357 #define AD_DATE_MODIFY 4
358 #define AD_DATE_BACKUP 8
359 #define AD_DATE_ACCESS 12
360 #define AD_DATE_MASK (AD_DATE_CREATE | AD_DATE_MODIFY | \
361 AD_DATE_BACKUP | AD_DATE_ACCESS)
362 #define AD_DATE_UNIX (1 << 10)
363 #define AD_DATE_START 0x80000000
364 #define AD_DATE_DELTA 946684800
365 #define AD_DATE_FROM_UNIX(x) (htonl((x) - AD_DATE_DELTA))
366 #define AD_DATE_TO_UNIX(x) (ntohl(x) + AD_DATE_DELTA)
368 #define AD_XATTR_HDR_MAGIC 0x41545452 /* 'ATTR' */
369 #define AD_XATTR_MAX_ENTRIES 1024 /* Some arbitrarily enforced limit */
370 #define AD_XATTR_HDR_SIZE 36
371 #define AD_XATTR_MAX_HDR_SIZE 65536
373 /* Accessor macros */
374 #define ad_getentrylen(ad,eid) ((ad)->ad_eid[(eid)].ade_len)
375 #define ad_getentryoff(ad,eid) ((ad)->ad_eid[(eid)].ade_off)
376 #define ad_setentrylen(ad,eid,len) ((ad)->ad_eid[(eid)].ade_len = (len))
377 #define ad_setentryoff(ad,eid,off) ((ad)->ad_eid[(eid)].ade_off = (off))
380 * Both struct ad_xattr_header and struct ad_xattr_entry describe the in memory
381 * representation as well as the on-disk format.
383 * The ad_xattr_header follows the FinderInfo data in the FinderInfo entry if
384 * the length of the FinderInfo entry is larger then 32 bytes. It is then
385 * preceeded with 2 bytes padding.
387 * Cf: https://opensource.apple.com/source/xnu/xnu-4570.1.46/bsd/vfs/vfs_xattr.c
390 struct ad_xattr_header {
391 uint32_t adx_magic; /* ATTR_HDR_MAGIC */
392 uint32_t adx_debug_tag; /* for debugging == file id of owning file */
393 uint32_t adx_total_size; /* file offset of end of attribute header + entries + data */
394 uint32_t adx_data_start; /* file offset to attribute data area */
395 uint32_t adx_data_length; /* length of attribute data area */
396 uint32_t adx_reserved[3];
398 uint16_t adx_num_attrs;
401 /* On-disk entries are aligned on 4 byte boundaries */
402 struct ad_xattr_entry {
403 uint32_t adx_offset; /* file offset to data */
404 uint32_t adx_length; /* size of attribute data */
406 uint8_t adx_namelen; /* included the NULL terminator */
407 char *adx_name; /* NULL-terminated UTF-8 name */
416 vfs_handle_struct *ad_handle;
419 adouble_type_t ad_type;
422 uint8_t ad_filler[ADEDLEN_FILLER];
423 struct ad_entry ad_eid[ADEID_MAX];
425 struct ad_xattr_header adx_header;
426 struct ad_xattr_entry *adx_entries;
429 struct ad_entry_order {
430 uint32_t id, offset, len;
433 /* Netatalk AppleDouble metadata xattr */
435 struct ad_entry_order entry_order_meta_xattr[ADEID_NUM_XATTR + 1] = {
436 {ADEID_FINDERI, ADEDOFF_FINDERI_XATTR, ADEDLEN_FINDERI},
437 {ADEID_COMMENT, ADEDOFF_COMMENT_XATTR, 0},
438 {ADEID_FILEDATESI, ADEDOFF_FILEDATESI_XATTR, ADEDLEN_FILEDATESI},
439 {ADEID_AFPFILEI, ADEDOFF_AFPFILEI_XATTR, ADEDLEN_AFPFILEI},
440 {ADEID_PRIVDEV, ADEDOFF_PRIVDEV_XATTR, 0},
441 {ADEID_PRIVINO, ADEDOFF_PRIVINO_XATTR, 0},
442 {ADEID_PRIVSYN, ADEDOFF_PRIVSYN_XATTR, 0},
443 {ADEID_PRIVID, ADEDOFF_PRIVID_XATTR, 0},
447 /* AppleDouble resource fork file (the ones prefixed by "._") */
449 struct ad_entry_order entry_order_dot_und[ADEID_NUM_DOT_UND + 1] = {
450 {ADEID_FINDERI, ADEDOFF_FINDERI_DOT_UND, ADEDLEN_FINDERI},
451 {ADEID_RFORK, ADEDOFF_RFORK_DOT_UND, 0},
456 * Fake AppleDouble entry oder for resource fork xattr. The xattr
457 * isn't an AppleDouble file, it simply contains the resource data,
458 * but in order to be able to use some API calls like ad_getentryoff()
459 * we build a fake/helper struct adouble with this entry order struct.
462 struct ad_entry_order entry_order_rsrc_xattr[ADEID_NUM_RSRC_XATTR + 1] = {
467 /* Conversion from enumerated id to on-disk AppleDouble id */
468 #define AD_EID_DISK(a) (set_eid[a])
469 static const uint32_t set_eid[] = {
470 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
471 AD_DEV, AD_INO, AD_SYN, AD_ID
474 static char empty_resourcefork[] = {
475 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
476 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
477 0x54, 0x68, 0x69, 0x73, 0x20, 0x72, 0x65, 0x73,
478 0x6F, 0x75, 0x72, 0x63, 0x65, 0x20, 0x66, 0x6F,
479 0x72, 0x6B, 0x20, 0x69, 0x6E, 0x74, 0x65, 0x6E,
480 0x74, 0x69, 0x6F, 0x6E, 0x61, 0x6C, 0x6C, 0x79,
481 0x20, 0x6C, 0x65, 0x66, 0x74, 0x20, 0x62, 0x6C,
482 0x61, 0x6E, 0x6B, 0x20, 0x20, 0x20, 0x00, 0x00,
483 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
484 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
485 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
486 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
487 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
488 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
489 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
490 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
491 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
492 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
493 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
494 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
495 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
496 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
497 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
498 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
499 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
500 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
501 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
502 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
503 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
504 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
505 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
506 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
507 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
508 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
509 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
510 0x00, 0x1C, 0x00, 0x1E, 0xFF, 0xFF
514 /* tcon config handle */
515 struct fruit_config_data *config;
517 /* Denote stream type, meta or rsrc */
520 /* Whether the create created the stream */
524 * AFP_AfpInfo stream created, but not written yet, thus still a fake
525 * pipe fd. This is set to true in fruit_open_meta if there was no
526 * exisiting stream but the caller requested O_CREAT. It is later set to
527 * false when we get a write on the stream that then does open and
536 * Forward declarations
538 static struct adouble *ad_init(TALLOC_CTX *ctx, vfs_handle_struct *handle,
539 adouble_type_t type);
540 static struct adouble *ad_get(TALLOC_CTX *ctx,
541 vfs_handle_struct *handle,
542 const struct smb_filename *smb_fname,
543 adouble_type_t type);
544 static int ad_set(vfs_handle_struct *handle,
546 const struct smb_filename *smb_fname);
547 static int ad_fset(struct vfs_handle_struct *handle,
550 static int adouble_path(TALLOC_CTX *ctx,
551 const struct smb_filename *smb_fname__in,
552 struct smb_filename **ppsmb_fname_out);
553 static AfpInfo *afpinfo_new(TALLOC_CTX *ctx);
554 static ssize_t afpinfo_pack(const AfpInfo *ai, char *buf);
555 static AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data);
559 * Return a pointer to an AppleDouble entry
561 * Returns NULL if the entry is not present
563 static char *ad_get_entry(const struct adouble *ad, int eid)
565 off_t off = ad_getentryoff(ad, eid);
566 size_t len = ad_getentrylen(ad, eid);
568 if (off == 0 || len == 0) {
572 return ad->ad_data + off;
578 static int ad_getdate(const struct adouble *ad,
579 unsigned int dateoff,
582 bool xlate = (dateoff & AD_DATE_UNIX);
585 dateoff &= AD_DATE_MASK;
586 p = ad_get_entry(ad, ADEID_FILEDATESI);
591 if (dateoff > AD_DATE_ACCESS) {
595 memcpy(date, p + dateoff, sizeof(uint32_t));
598 *date = AD_DATE_TO_UNIX(*date);
606 static int ad_setdate(struct adouble *ad, unsigned int dateoff, uint32_t date)
608 bool xlate = (dateoff & AD_DATE_UNIX);
611 p = ad_get_entry(ad, ADEID_FILEDATESI);
616 dateoff &= AD_DATE_MASK;
618 date = AD_DATE_FROM_UNIX(date);
621 if (dateoff > AD_DATE_ACCESS) {
625 memcpy(p + dateoff, &date, sizeof(date));
632 * Map on-disk AppleDouble id to enumerated id
634 static uint32_t get_eid(uint32_t eid)
642 return ADEID_PRIVDEV;
644 return ADEID_PRIVINO;
646 return ADEID_PRIVSYN;
657 * Pack AppleDouble structure into data buffer
659 static bool ad_pack(struct adouble *ad)
666 bufsize = talloc_get_size(ad->ad_data);
667 if (bufsize < AD_DATASZ_DOT_UND) {
668 DBG_ERR("bad buffer size [0x%" PRIx32 "]\n", bufsize);
672 if (offset + ADEDLEN_MAGIC < offset ||
673 offset + ADEDLEN_MAGIC >= bufsize) {
676 RSIVAL(ad->ad_data, offset, ad->ad_magic);
677 offset += ADEDLEN_MAGIC;
679 if (offset + ADEDLEN_VERSION < offset ||
680 offset + ADEDLEN_VERSION >= bufsize) {
683 RSIVAL(ad->ad_data, offset, ad->ad_version);
684 offset += ADEDLEN_VERSION;
686 if (offset + ADEDLEN_FILLER < offset ||
687 offset + ADEDLEN_FILLER >= bufsize) {
690 if (ad->ad_type == ADOUBLE_RSRC) {
691 memcpy(ad->ad_data + offset, AD_FILLER_TAG, ADEDLEN_FILLER);
693 offset += ADEDLEN_FILLER;
695 if (offset + ADEDLEN_NENTRIES < offset ||
696 offset + ADEDLEN_NENTRIES >= bufsize) {
699 offset += ADEDLEN_NENTRIES;
701 for (eid = 0, nent = 0; eid < ADEID_MAX; eid++) {
702 if (ad->ad_eid[eid].ade_off == 0) {
704 * ade_off is also used as indicator whether a
705 * specific entry is used or not
710 if (offset + AD_ENTRY_LEN_EID < offset ||
711 offset + AD_ENTRY_LEN_EID >= bufsize) {
714 RSIVAL(ad->ad_data, offset, AD_EID_DISK(eid));
715 offset += AD_ENTRY_LEN_EID;
717 if (offset + AD_ENTRY_LEN_OFF < offset ||
718 offset + AD_ENTRY_LEN_OFF >= bufsize) {
721 RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_off);
722 offset += AD_ENTRY_LEN_OFF;
724 if (offset + AD_ENTRY_LEN_LEN < offset ||
725 offset + AD_ENTRY_LEN_LEN >= bufsize) {
728 RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_len);
729 offset += AD_ENTRY_LEN_LEN;
734 if (ADEDOFF_NENTRIES + 2 >= bufsize) {
737 RSSVAL(ad->ad_data, ADEDOFF_NENTRIES, nent);
742 static bool ad_unpack_xattrs(struct adouble *ad)
744 struct ad_xattr_header *h = &ad->adx_header;
745 const char *p = ad->ad_data;
749 if (ad_getentrylen(ad, ADEID_FINDERI) <= ADEDLEN_FINDERI) {
753 /* 2 bytes padding */
754 hoff = ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI + 2;
756 h->adx_magic = RIVAL(p, hoff + 0);
757 h->adx_debug_tag = RIVAL(p, hoff + 4); /* Not used -> not checked */
758 h->adx_total_size = RIVAL(p, hoff + 8);
759 h->adx_data_start = RIVAL(p, hoff + 12);
760 h->adx_data_length = RIVAL(p, hoff + 16);
761 h->adx_flags = RSVAL(p, hoff + 32); /* Not used -> not checked */
762 h->adx_num_attrs = RSVAL(p, hoff + 34);
764 if (h->adx_magic != AD_XATTR_HDR_MAGIC) {
765 DBG_ERR("Bad magic: 0x%" PRIx32 "\n", h->adx_magic);
769 if (h->adx_total_size > ad_getentryoff(ad, ADEID_RFORK)) {
770 DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size);
773 if (h->adx_total_size > AD_XATTR_MAX_HDR_SIZE) {
774 DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size);
778 if (h->adx_data_start < (hoff + AD_XATTR_HDR_SIZE)) {
779 DBG_ERR("Bad start: 0x%" PRIx32 "\n", h->adx_data_start);
783 if ((h->adx_data_start + h->adx_data_length) < h->adx_data_start) {
784 DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length);
787 if ((h->adx_data_start + h->adx_data_length) >
788 ad->adx_header.adx_total_size)
790 DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length);
794 if (h->adx_num_attrs > AD_XATTR_MAX_ENTRIES) {
795 DBG_ERR("Bad num xattrs: %" PRIu16 "\n", h->adx_num_attrs);
799 if (h->adx_num_attrs == 0) {
803 ad->adx_entries = talloc_zero_array(
804 ad, struct ad_xattr_entry, h->adx_num_attrs);
805 if (ad->adx_entries == NULL) {
809 hoff += AD_XATTR_HDR_SIZE;
811 for (i = 0; i < h->adx_num_attrs; i++) {
812 struct ad_xattr_entry *e = &ad->adx_entries[i];
814 hoff = (hoff + 3) & ~3;
816 e->adx_offset = RIVAL(p, hoff + 0);
817 e->adx_length = RIVAL(p, hoff + 4);
818 e->adx_flags = RSVAL(p, hoff + 8);
819 e->adx_namelen = *(p + hoff + 10);
821 if (e->adx_offset >= ad->adx_header.adx_total_size) {
822 DBG_ERR("Bad adx_offset: %" PRIx32 "\n",
827 if ((e->adx_offset + e->adx_length) < e->adx_offset) {
828 DBG_ERR("Bad adx_length: %" PRIx32 "\n",
833 if ((e->adx_offset + e->adx_length) >
834 ad->adx_header.adx_total_size)
836 DBG_ERR("Bad adx_length: %" PRIx32 "\n",
841 if (e->adx_namelen == 0) {
842 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
846 if ((hoff + 11 + e->adx_namelen) < hoff + 11) {
847 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
851 if ((hoff + 11 + e->adx_namelen) >
852 ad->adx_header.adx_data_start)
854 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
859 e->adx_name = talloc_strndup(ad->adx_entries,
862 if (e->adx_name == NULL) {
866 DBG_DEBUG("xattr [%s] offset [0x%x] size [0x%x]\n",
867 e->adx_name, e->adx_offset, e->adx_length);
868 dump_data(10, (uint8_t *)(ad->ad_data + e->adx_offset),
871 hoff += 11 + e->adx_namelen;
878 * Unpack an AppleDouble blob into a struct adoble
880 static bool ad_unpack(struct adouble *ad, const size_t nentries,
883 size_t bufsize = talloc_get_size(ad->ad_data);
885 uint32_t eid, len, off;
889 * The size of the buffer ad->ad_data is checked when read, so
890 * we wouldn't have to check our own offsets, a few extra
891 * checks won't hurt though. We have to check the offsets we
892 * read from the buffer anyway.
895 if (bufsize < (AD_HEADER_LEN + (AD_ENTRY_LEN * nentries))) {
896 DEBUG(1, ("bad size\n"));
900 ad->ad_magic = RIVAL(ad->ad_data, 0);
901 ad->ad_version = RIVAL(ad->ad_data, ADEDOFF_VERSION);
902 if ((ad->ad_magic != AD_MAGIC) || (ad->ad_version != AD_VERSION)) {
903 DEBUG(1, ("wrong magic or version\n"));
907 memcpy(ad->ad_filler, ad->ad_data + ADEDOFF_FILLER, ADEDLEN_FILLER);
909 adentries = RSVAL(ad->ad_data, ADEDOFF_NENTRIES);
910 if (adentries != nentries) {
911 DEBUG(1, ("invalid number of entries: %zu\n",
916 /* now, read in the entry bits */
917 for (i = 0; i < adentries; i++) {
918 eid = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN));
920 off = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 4);
921 len = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 8);
923 if (!eid || eid >= ADEID_MAX) {
924 DEBUG(1, ("bogus eid %d\n", eid));
929 * All entries other than the resource fork are
930 * expected to be read into the ad_data buffer, so
931 * ensure the specified offset is within that bound
933 if ((off > bufsize) && (eid != ADEID_RFORK)) {
934 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
940 * All entries besides FinderInfo and resource fork
941 * must fit into the buffer. FinderInfo is special as
942 * it may be larger then the default 32 bytes (if it
943 * contains marshalled xattrs), but we will fixup that
944 * in ad_convert(). And the resource fork is never
945 * accessed directly by the ad_data buf (also see
946 * comment above) anyway.
948 if ((eid != ADEID_RFORK) &&
949 (eid != ADEID_FINDERI) &&
950 ((off + len) > bufsize)) {
951 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
957 * That would be obviously broken
959 if (off > filesize) {
960 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
966 * Check for any entry that has its end beyond the
969 if (off + len < off) {
970 DEBUG(1, ("offset wrap in eid %d: off: %" PRIu32
971 ", len: %" PRIu32 "\n",
976 if (off + len > filesize) {
978 * If this is the resource fork entry, we fix
979 * up the length, for any other entry we bail
982 if (eid != ADEID_RFORK) {
983 DEBUG(1, ("bogus eid %d: off: %" PRIu32
984 ", len: %" PRIu32 "\n",
990 * Fixup the resource fork entry by limiting
991 * the size to entryoffset - filesize.
993 len = filesize - off;
994 DEBUG(1, ("Limiting ADEID_RFORK: off: %" PRIu32
995 ", len: %" PRIu32 "\n", off, len));
998 ad->ad_eid[eid].ade_off = off;
999 ad->ad_eid[eid].ade_len = len;
1002 ok = ad_unpack_xattrs(ad);
1010 static bool ad_convert_move_reso(struct adouble *ad,
1011 const struct smb_filename *smb_fname)
1013 char *map = MAP_FAILED;
1019 if (ad_getentrylen(ad, ADEID_RFORK) == 0) {
1023 maplen = ad_getentryoff(ad, ADEID_RFORK) +
1024 ad_getentrylen(ad, ADEID_RFORK);
1026 /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
1027 map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
1029 if (map == MAP_FAILED) {
1030 DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
1035 memmove(map + ADEDOFF_RFORK_DOT_UND,
1036 map + ad_getentryoff(ad, ADEID_RFORK),
1037 ad_getentrylen(ad, ADEID_RFORK));
1039 rc = munmap(map, maplen);
1041 DBG_ERR("munmap failed: %s\n", strerror(errno));
1045 ad_setentryoff(ad, ADEID_RFORK, ADEDOFF_RFORK_DOT_UND);
1049 DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name);
1053 len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
1054 if (len != AD_DATASZ_DOT_UND) {
1055 DBG_ERR("%s: bad size: %zd\n", smb_fname->base_name, len);
1062 static bool ad_convert_xattr(struct adouble *ad,
1063 const struct smb_filename *smb_fname,
1064 bool *converted_xattr)
1066 static struct char_mappings **string_replace_cmaps = NULL;
1067 char *map = MAP_FAILED;
1071 int saved_errno = 0;
1076 *converted_xattr = false;
1078 if (ad_getentrylen(ad, ADEID_FINDERI) == ADEDLEN_FINDERI) {
1082 if (string_replace_cmaps == NULL) {
1083 const char **mappings = NULL;
1085 mappings = str_list_make_v3_const(
1086 talloc_tos(), fruit_catia_maps, NULL);
1087 if (mappings == NULL) {
1090 string_replace_cmaps = string_replace_init_map(mappings);
1091 TALLOC_FREE(mappings);
1094 maplen = ad_getentryoff(ad, ADEID_RFORK) +
1095 ad_getentrylen(ad, ADEID_RFORK);
1097 /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
1098 map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
1100 if (map == MAP_FAILED) {
1101 DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
1105 for (i = 0; i < ad->adx_header.adx_num_attrs; i++) {
1106 struct ad_xattr_entry *e = &ad->adx_entries[i];
1107 char *mapped_name = NULL;
1109 struct smb_filename *stream_name = NULL;
1110 files_struct *fsp = NULL;
1113 status = string_replace_allocate(ad->ad_handle->conn,
1115 string_replace_cmaps,
1118 vfs_translate_to_windows);
1119 if (!NT_STATUS_IS_OK(status) &&
1120 !NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))
1122 DBG_ERR("string_replace_allocate failed\n");
1128 mapped_name = talloc_asprintf(talloc_tos(), ":%s", tmp);
1130 if (mapped_name == NULL) {
1135 stream_name = synthetic_smb_fname(talloc_tos(),
1136 smb_fname->base_name,
1140 TALLOC_FREE(mapped_name);
1141 if (stream_name == NULL) {
1142 DBG_ERR("synthetic_smb_fname failed\n");
1147 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1149 status = SMB_VFS_CREATE_FILE(
1150 ad->ad_handle->conn, /* conn */
1152 0, /* root_dir_fid */
1153 stream_name, /* fname */
1154 FILE_GENERIC_WRITE, /* access_mask */
1155 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
1156 FILE_OPEN_IF, /* create_disposition */
1157 0, /* create_options */
1158 0, /* file_attributes */
1159 INTERNAL_OPEN_ONLY, /* oplock_request */
1161 0, /* allocation_size */
1162 0, /* private_flags */
1167 NULL, NULL); /* create context */
1168 TALLOC_FREE(stream_name);
1169 if (!NT_STATUS_IS_OK(status)) {
1170 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1175 nwritten = SMB_VFS_PWRITE(fsp,
1176 map + e->adx_offset,
1179 if (nwritten == -1) {
1180 DBG_ERR("SMB_VFS_PWRITE failed\n");
1181 saved_errno = errno;
1182 close_file(NULL, fsp, ERROR_CLOSE);
1183 errno = saved_errno;
1188 status = close_file(NULL, fsp, NORMAL_CLOSE);
1189 if (!NT_STATUS_IS_OK(status)) {
1196 ad_setentrylen(ad, ADEID_FINDERI, ADEDLEN_FINDERI);
1200 DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name);
1204 len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
1205 if (len != AD_DATASZ_DOT_UND) {
1206 DBG_ERR("%s: bad size: %zd\n", smb_fname->base_name, len);
1211 ok = ad_convert_move_reso(ad, smb_fname);
1216 *converted_xattr = true;
1220 rc = munmap(map, maplen);
1222 DBG_ERR("munmap failed: %s\n", strerror(errno));
1229 static bool ad_convert_finderinfo(struct adouble *ad,
1230 const struct smb_filename *smb_fname)
1235 struct smb_filename *stream_name = NULL;
1236 files_struct *fsp = NULL;
1240 int saved_errno = 0;
1243 cmp = memcmp(ad->ad_filler, AD_FILLER_TAG_OSX, ADEDLEN_FILLER);
1248 p_ad = ad_get_entry(ad, ADEID_FINDERI);
1253 ai = afpinfo_new(talloc_tos());
1258 memcpy(ai->afpi_FinderInfo, p_ad, ADEDLEN_FINDERI);
1260 aiblob = data_blob_talloc(talloc_tos(), NULL, AFP_INFO_SIZE);
1261 if (aiblob.data == NULL) {
1266 size = afpinfo_pack(ai, (char *)aiblob.data);
1268 if (size != AFP_INFO_SIZE) {
1272 stream_name = synthetic_smb_fname(talloc_tos(),
1273 smb_fname->base_name,
1277 if (stream_name == NULL) {
1278 data_blob_free(&aiblob);
1279 DBG_ERR("synthetic_smb_fname failed\n");
1283 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1285 status = SMB_VFS_CREATE_FILE(
1286 ad->ad_handle->conn, /* conn */
1288 0, /* root_dir_fid */
1289 stream_name, /* fname */
1290 FILE_GENERIC_WRITE, /* access_mask */
1291 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
1292 FILE_OPEN_IF, /* create_disposition */
1293 0, /* create_options */
1294 0, /* file_attributes */
1295 INTERNAL_OPEN_ONLY, /* oplock_request */
1297 0, /* allocation_size */
1298 0, /* private_flags */
1303 NULL, NULL); /* create context */
1304 TALLOC_FREE(stream_name);
1305 if (!NT_STATUS_IS_OK(status)) {
1306 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1310 nwritten = SMB_VFS_PWRITE(fsp,
1314 if (nwritten == -1) {
1315 DBG_ERR("SMB_VFS_PWRITE failed\n");
1316 saved_errno = errno;
1317 close_file(NULL, fsp, ERROR_CLOSE);
1318 errno = saved_errno;
1322 status = close_file(NULL, fsp, NORMAL_CLOSE);
1323 if (!NT_STATUS_IS_OK(status)) {
1331 static bool ad_convert_truncate(struct adouble *ad,
1332 const struct smb_filename *smb_fname)
1337 * FIXME: direct ftruncate(), but we don't have a fsp for the
1340 rc = ftruncate(ad->ad_fd, ADEDOFF_RFORK_DOT_UND +
1341 ad_getentrylen(ad, ADEID_RFORK));
1349 static bool ad_convert_blank_rfork(struct adouble *ad,
1352 struct fruit_config_data *config = NULL;
1353 uint8_t *map = MAP_FAILED;
1362 SMB_VFS_HANDLE_GET_DATA(ad->ad_handle, config,
1363 struct fruit_config_data, return false);
1365 if (!config->wipe_intentionally_left_blank_rfork) {
1369 if (ad_getentrylen(ad, ADEID_RFORK) != sizeof(empty_resourcefork)) {
1373 maplen = ad_getentryoff(ad, ADEID_RFORK) +
1374 ad_getentrylen(ad, ADEID_RFORK);
1376 /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
1377 map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
1379 if (map == MAP_FAILED) {
1380 DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
1384 cmp = memcmp(map + ADEDOFF_RFORK_DOT_UND,
1386 sizeof(empty_resourcefork));
1387 rc = munmap(map, maplen);
1389 DBG_ERR("munmap failed: %s\n", strerror(errno));
1397 ad_setentrylen(ad, ADEID_RFORK, 0);
1404 len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
1405 if (len != AD_DATASZ_DOT_UND) {
1413 static bool ad_convert_delete_adfile(struct adouble *ad,
1414 const struct smb_filename *smb_fname)
1416 struct fruit_config_data *config = NULL;
1417 struct smb_filename *ad_name = NULL;
1420 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
1424 SMB_VFS_HANDLE_GET_DATA(ad->ad_handle, config,
1425 struct fruit_config_data, return false);
1427 if (!config->delete_empty_adfiles) {
1431 rc = adouble_path(talloc_tos(), smb_fname, &ad_name);
1436 rc = SMB_VFS_NEXT_UNLINK(ad->ad_handle, ad_name);
1438 DBG_ERR("Unlinking [%s] failed: %s\n",
1439 smb_fname_str_dbg(ad_name), strerror(errno));
1440 TALLOC_FREE(ad_name);
1444 DBG_WARNING("Unlinked [%s] after conversion\n", smb_fname_str_dbg(ad_name));
1445 TALLOC_FREE(ad_name);
1451 * Convert from Apple's ._ file to Netatalk
1453 * Apple's AppleDouble may contain a FinderInfo entry longer then 32
1454 * bytes containing packed xattrs.
1456 * @return -1 in case an error occurred, 0 if no conversion was done, 1
1459 static int ad_convert(struct vfs_handle_struct *handle,
1460 const struct smb_filename *smb_fname)
1462 struct adouble *ad = NULL;
1464 bool converted_xattr = false;
1468 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
1473 ok = ad_convert_xattr(ad, smb_fname, &converted_xattr);
1479 ok = ad_convert_blank_rfork(ad, &blank);
1485 if (converted_xattr || blank) {
1486 ok = ad_convert_truncate(ad, smb_fname);
1493 ok = ad_convert_finderinfo(ad, smb_fname);
1495 DBG_ERR("Failed to convert [%s]\n",
1496 smb_fname_str_dbg(smb_fname));
1501 ok = ad_convert_delete_adfile(ad, smb_fname);
1514 * Read and parse Netatalk AppleDouble metadata xattr
1516 static ssize_t ad_read_meta(vfs_handle_struct *handle,
1518 const struct smb_filename *smb_fname)
1524 DEBUG(10, ("reading meta xattr for %s\n", smb_fname->base_name));
1526 ealen = SMB_VFS_GETXATTR(handle->conn, smb_fname,
1527 AFPINFO_EA_NETATALK, ad->ad_data,
1533 if (errno == ENOATTR) {
1539 DEBUG(2, ("error reading meta xattr: %s\n",
1545 if (ealen != AD_DATASZ_XATTR) {
1546 DEBUG(2, ("bad size %zd\n", ealen));
1552 /* Now parse entries */
1553 ok = ad_unpack(ad, ADEID_NUM_XATTR, AD_DATASZ_XATTR);
1555 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1561 if (!ad_getentryoff(ad, ADEID_FINDERI)
1562 || !ad_getentryoff(ad, ADEID_COMMENT)
1563 || !ad_getentryoff(ad, ADEID_FILEDATESI)
1564 || !ad_getentryoff(ad, ADEID_AFPFILEI)
1565 || !ad_getentryoff(ad, ADEID_PRIVDEV)
1566 || !ad_getentryoff(ad, ADEID_PRIVINO)
1567 || !ad_getentryoff(ad, ADEID_PRIVSYN)
1568 || !ad_getentryoff(ad, ADEID_PRIVID)) {
1569 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1576 DEBUG(10, ("reading meta xattr for %s, rc: %d\n",
1577 smb_fname->base_name, rc));
1581 if (errno == EINVAL) {
1583 (void)SMB_VFS_REMOVEXATTR(handle->conn,
1585 AFPINFO_EA_NETATALK);
1593 static int ad_open_rsrc_xattr(const struct smb_filename *smb_fname,
1597 #ifdef HAVE_ATTROPEN
1598 /* FIXME: direct Solaris xattr syscall */
1599 return attropen(smb_fname->base_name,
1600 AFPRESOURCE_EA_NETATALK, flags, mode);
1607 static int ad_open_rsrc_adouble(const struct smb_filename *smb_fname,
1613 struct smb_filename *adp_smb_fname = NULL;
1615 ret = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
1620 fd = open(adp_smb_fname->base_name, flags, mode);
1621 TALLOC_FREE(adp_smb_fname);
1626 static int ad_open_rsrc(vfs_handle_struct *handle,
1627 const struct smb_filename *smb_fname,
1631 struct fruit_config_data *config = NULL;
1634 SMB_VFS_HANDLE_GET_DATA(handle, config,
1635 struct fruit_config_data, return -1);
1637 if (config->rsrc == FRUIT_RSRC_XATTR) {
1638 fd = ad_open_rsrc_xattr(smb_fname, flags, mode);
1640 fd = ad_open_rsrc_adouble(smb_fname, flags, mode);
1647 * Here's the deal: for ADOUBLE_META we can do without an fd as we can issue
1648 * path based xattr calls. For ADOUBLE_RSRC however we need a full-fledged fd
1649 * for file IO on the ._ file.
1651 static int ad_open(vfs_handle_struct *handle,
1654 const struct smb_filename *smb_fname,
1660 DBG_DEBUG("Path [%s] type [%s]\n", smb_fname->base_name,
1661 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
1663 if (ad->ad_type == ADOUBLE_META) {
1667 if ((fsp != NULL) && (fsp->fh != NULL) && (fsp->fh->fd != -1)) {
1668 ad->ad_fd = fsp->fh->fd;
1669 ad->ad_opened = false;
1673 fd = ad_open_rsrc(handle, smb_fname, flags, mode);
1677 ad->ad_opened = true;
1680 DBG_DEBUG("Path [%s] type [%s] fd [%d]\n",
1681 smb_fname->base_name,
1682 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc", fd);
1687 static ssize_t ad_read_rsrc_xattr(vfs_handle_struct *handle,
1693 /* FIXME: direct sys_fstat(), don't have an fsp */
1694 ret = sys_fstat(ad->ad_fd, &st,
1695 lp_fake_directory_create_times(
1696 SNUM(handle->conn)));
1701 ad_setentrylen(ad, ADEID_RFORK, st.st_ex_size);
1702 return st.st_ex_size;
1705 static ssize_t ad_read_rsrc_adouble(vfs_handle_struct *handle,
1707 const struct smb_filename *smb_fname)
1709 SMB_STRUCT_STAT sbuf;
1716 ret = sys_fstat(ad->ad_fd, &sbuf, lp_fake_directory_create_times(
1717 SNUM(handle->conn)));
1723 * AppleDouble file header content and size, two cases:
1725 * - without xattrs it is exactly AD_DATASZ_DOT_UND (82) bytes large
1726 * - with embedded xattrs it can be larger, up to AD_XATTR_MAX_HDR_SIZE
1728 * Read as much as we can up to AD_XATTR_MAX_HDR_SIZE.
1730 size = sbuf.st_ex_size;
1731 if (size > talloc_array_length(ad->ad_data)) {
1732 if (size > AD_XATTR_MAX_HDR_SIZE) {
1733 size = AD_XATTR_MAX_HDR_SIZE;
1735 p_ad = talloc_realloc(ad, ad->ad_data, char, size);
1742 len = sys_pread(ad->ad_fd, ad->ad_data,
1743 talloc_array_length(ad->ad_data), 0);
1744 if (len != talloc_array_length(ad->ad_data)) {
1745 DBG_NOTICE("%s %s: bad size: %zd\n",
1746 smb_fname->base_name, strerror(errno), len);
1750 /* Now parse entries */
1751 ok = ad_unpack(ad, ADEID_NUM_DOT_UND, sbuf.st_ex_size);
1753 DBG_ERR("invalid AppleDouble resource %s\n",
1754 smb_fname->base_name);
1759 if ((ad_getentryoff(ad, ADEID_FINDERI) != ADEDOFF_FINDERI_DOT_UND)
1760 || (ad_getentrylen(ad, ADEID_FINDERI) < ADEDLEN_FINDERI)
1761 || (ad_getentryoff(ad, ADEID_RFORK) < ADEDOFF_RFORK_DOT_UND)) {
1762 DBG_ERR("invalid AppleDouble resource %s\n",
1763 smb_fname->base_name);
1772 * Read and parse resource fork, either ._ AppleDouble file or xattr
1774 static ssize_t ad_read_rsrc(vfs_handle_struct *handle,
1776 const struct smb_filename *smb_fname)
1778 struct fruit_config_data *config = NULL;
1781 SMB_VFS_HANDLE_GET_DATA(handle, config,
1782 struct fruit_config_data, return -1);
1784 if (config->rsrc == FRUIT_RSRC_XATTR) {
1785 len = ad_read_rsrc_xattr(handle, ad);
1787 len = ad_read_rsrc_adouble(handle, ad, smb_fname);
1794 * Read and unpack an AppleDouble metadata xattr or resource
1796 static ssize_t ad_read(vfs_handle_struct *handle,
1798 const struct smb_filename *smb_fname)
1800 switch (ad->ad_type) {
1802 return ad_read_meta(handle, ad, smb_fname);
1804 return ad_read_rsrc(handle, ad, smb_fname);
1810 static int adouble_destructor(struct adouble *ad)
1812 if ((ad->ad_fd != -1) && ad->ad_opened) {
1820 * Allocate a struct adouble without initialiing it
1822 * The struct is either hang of the fsp extension context or if fsp is
1825 * @param[in] ctx talloc context
1826 * @param[in] handle vfs handle
1827 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1829 * @return adouble handle
1831 static struct adouble *ad_alloc(TALLOC_CTX *ctx, vfs_handle_struct *handle,
1832 adouble_type_t type)
1837 struct fruit_config_data *config;
1839 SMB_VFS_HANDLE_GET_DATA(handle, config,
1840 struct fruit_config_data, return NULL);
1844 adsize = AD_DATASZ_XATTR;
1847 if (config->rsrc == FRUIT_RSRC_ADFILE) {
1848 adsize = AD_DATASZ_DOT_UND;
1855 ad = talloc_zero(ctx, struct adouble);
1862 ad->ad_data = talloc_zero_array(ad, char, adsize);
1863 if (ad->ad_data == NULL) {
1869 ad->ad_handle = handle;
1871 ad->ad_magic = AD_MAGIC;
1872 ad->ad_version = AD_VERSION;
1875 talloc_set_destructor(ad, adouble_destructor);
1885 * Allocate and initialize a new struct adouble
1887 * @param[in] ctx talloc context
1888 * @param[in] handle vfs handle
1889 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1891 * @return adouble handle, initialized
1893 static struct adouble *ad_init(TALLOC_CTX *ctx, vfs_handle_struct *handle,
1894 adouble_type_t type)
1897 const struct ad_entry_order *eid;
1898 struct adouble *ad = NULL;
1899 struct fruit_config_data *config;
1900 time_t t = time(NULL);
1902 SMB_VFS_HANDLE_GET_DATA(handle, config,
1903 struct fruit_config_data, return NULL);
1907 eid = entry_order_meta_xattr;
1910 if (config->rsrc == FRUIT_RSRC_ADFILE) {
1911 eid = entry_order_dot_und;
1913 eid = entry_order_rsrc_xattr;
1920 ad = ad_alloc(ctx, handle, type);
1926 ad->ad_eid[eid->id].ade_off = eid->offset;
1927 ad->ad_eid[eid->id].ade_len = eid->len;
1931 /* put something sane in the date fields */
1932 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, t);
1933 ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, t);
1934 ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, t);
1935 ad_setdate(ad, AD_DATE_BACKUP, htonl(AD_DATE_START));
1943 static struct adouble *ad_get_internal(TALLOC_CTX *ctx,
1944 vfs_handle_struct *handle,
1946 const struct smb_filename *smb_fname,
1947 adouble_type_t type)
1951 struct adouble *ad = NULL;
1955 smb_fname = fsp->base_fsp->fsp_name;
1958 DEBUG(10, ("ad_get(%s) called for %s\n",
1959 type == ADOUBLE_META ? "meta" : "rsrc",
1960 smb_fname->base_name));
1962 ad = ad_alloc(ctx, handle, type);
1968 /* Try rw first so we can use the fd in ad_convert() */
1971 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
1972 if (rc == -1 && ((errno == EROFS) || (errno == EACCES))) {
1974 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
1977 DBG_DEBUG("ad_open [%s] error [%s]\n",
1978 smb_fname->base_name, strerror(errno));
1983 len = ad_read(handle, ad, smb_fname);
1985 DEBUG(10, ("error reading AppleDouble for %s\n",
1986 smb_fname->base_name));
1992 DEBUG(10, ("ad_get(%s) for %s returning %d\n",
1993 type == ADOUBLE_META ? "meta" : "rsrc",
1994 smb_fname->base_name, rc));
2003 * Return AppleDouble data for a file
2005 * @param[in] ctx talloc context
2006 * @param[in] handle vfs handle
2007 * @param[in] smb_fname pathname to file or directory
2008 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2010 * @return talloced struct adouble or NULL on error
2012 static struct adouble *ad_get(TALLOC_CTX *ctx,
2013 vfs_handle_struct *handle,
2014 const struct smb_filename *smb_fname,
2015 adouble_type_t type)
2017 return ad_get_internal(ctx, handle, NULL, smb_fname, type);
2021 * Return AppleDouble data for a file
2023 * @param[in] ctx talloc context
2024 * @param[in] handle vfs handle
2025 * @param[in] fsp fsp to use for IO
2026 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2028 * @return talloced struct adouble or NULL on error
2030 static struct adouble *ad_fget(TALLOC_CTX *ctx, vfs_handle_struct *handle,
2031 files_struct *fsp, adouble_type_t type)
2033 return ad_get_internal(ctx, handle, fsp, NULL, type);
2037 * Set AppleDouble metadata on a file or directory
2039 * @param[in] ad adouble handle
2041 * @param[in] smb_fname pathname to file or directory
2043 * @return status code, 0 means success
2045 static int ad_set(vfs_handle_struct *handle,
2047 const struct smb_filename *smb_fname)
2052 DBG_DEBUG("Path [%s]\n", smb_fname->base_name);
2054 if (ad->ad_type != ADOUBLE_META) {
2055 DBG_ERR("ad_set on [%s] used with ADOUBLE_RSRC\n",
2056 smb_fname->base_name);
2065 ret = SMB_VFS_SETXATTR(handle->conn,
2067 AFPINFO_EA_NETATALK,
2069 AD_DATASZ_XATTR, 0);
2071 DBG_DEBUG("Path [%s] ret [%d]\n", smb_fname->base_name, ret);
2077 * Set AppleDouble metadata on a file or directory
2079 * @param[in] ad adouble handle
2080 * @param[in] fsp file handle
2082 * @return status code, 0 means success
2084 static int ad_fset(struct vfs_handle_struct *handle,
2092 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
2095 || (fsp->fh == NULL)
2096 || (fsp->fh->fd == -1))
2098 smb_panic("bad fsp");
2106 switch (ad->ad_type) {
2108 rc = SMB_VFS_NEXT_SETXATTR(handle,
2110 AFPINFO_EA_NETATALK,
2112 AD_DATASZ_XATTR, 0);
2116 len = SMB_VFS_NEXT_PWRITE(handle,
2121 if (len != AD_DATASZ_DOT_UND) {
2122 DBG_ERR("short write on %s: %zd", fsp_str_dbg(fsp), len);
2132 DBG_DEBUG("Path [%s] rc [%d]\n", fsp_str_dbg(fsp), rc);
2137 /*****************************************************************************
2139 *****************************************************************************/
2141 static bool is_afpinfo_stream(const struct smb_filename *smb_fname)
2143 if (strncasecmp_m(smb_fname->stream_name,
2144 AFPINFO_STREAM_NAME,
2145 strlen(AFPINFO_STREAM_NAME)) == 0) {
2151 static bool is_afpresource_stream(const struct smb_filename *smb_fname)
2153 if (strncasecmp_m(smb_fname->stream_name,
2154 AFPRESOURCE_STREAM_NAME,
2155 strlen(AFPRESOURCE_STREAM_NAME)) == 0) {
2162 * Test whether stream is an Apple stream.
2164 static bool is_apple_stream(const struct smb_filename *smb_fname)
2166 if (is_afpinfo_stream(smb_fname)) {
2169 if (is_afpresource_stream(smb_fname)) {
2176 * Initialize config struct from our smb.conf config parameters
2178 static int init_fruit_config(vfs_handle_struct *handle)
2180 struct fruit_config_data *config;
2182 const char *tm_size_str = NULL;
2184 config = talloc_zero(handle->conn, struct fruit_config_data);
2186 DEBUG(1, ("talloc_zero() failed\n"));
2192 * Versions up to Samba 4.5.x had a spelling bug in the
2193 * fruit:resource option calling lp_parm_enum with
2194 * "res*s*ource" (ie two s).
2196 * In Samba 4.6 we accept both the wrong and the correct
2197 * spelling, in Samba 4.7 the bad spelling will be removed.
2199 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2200 "ressource", fruit_rsrc, FRUIT_RSRC_ADFILE);
2201 if (enumval == -1) {
2202 DEBUG(1, ("value for %s: resource type unknown\n",
2203 FRUIT_PARAM_TYPE_NAME));
2206 config->rsrc = (enum fruit_rsrc)enumval;
2208 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2209 "resource", fruit_rsrc, enumval);
2210 if (enumval == -1) {
2211 DEBUG(1, ("value for %s: resource type unknown\n",
2212 FRUIT_PARAM_TYPE_NAME));
2215 config->rsrc = (enum fruit_rsrc)enumval;
2217 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2218 "metadata", fruit_meta, FRUIT_META_NETATALK);
2219 if (enumval == -1) {
2220 DEBUG(1, ("value for %s: metadata type unknown\n",
2221 FRUIT_PARAM_TYPE_NAME));
2224 config->meta = (enum fruit_meta)enumval;
2226 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2227 "locking", fruit_locking, FRUIT_LOCKING_NONE);
2228 if (enumval == -1) {
2229 DEBUG(1, ("value for %s: locking type unknown\n",
2230 FRUIT_PARAM_TYPE_NAME));
2233 config->locking = (enum fruit_locking)enumval;
2235 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2236 "encoding", fruit_encoding, FRUIT_ENC_PRIVATE);
2237 if (enumval == -1) {
2238 DEBUG(1, ("value for %s: encoding type unknown\n",
2239 FRUIT_PARAM_TYPE_NAME));
2242 config->encoding = (enum fruit_encoding)enumval;
2244 if (config->rsrc == FRUIT_RSRC_ADFILE) {
2245 config->veto_appledouble = lp_parm_bool(SNUM(handle->conn),
2246 FRUIT_PARAM_TYPE_NAME,
2251 config->use_aapl = lp_parm_bool(
2252 -1, FRUIT_PARAM_TYPE_NAME, "aapl", true);
2254 config->time_machine = lp_parm_bool(
2255 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "time machine", false);
2257 config->unix_info_enabled = lp_parm_bool(
2258 -1, FRUIT_PARAM_TYPE_NAME, "nfs_aces", true);
2260 config->use_copyfile = lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME,
2263 config->posix_rename = lp_parm_bool(
2264 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "posix_rename", true);
2266 config->aapl_zero_file_id =
2267 lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME, "zero_file_id", true);
2269 config->readdir_attr_rsize = lp_parm_bool(
2270 SNUM(handle->conn), "readdir_attr", "aapl_rsize", true);
2272 config->readdir_attr_finder_info = lp_parm_bool(
2273 SNUM(handle->conn), "readdir_attr", "aapl_finder_info", true);
2275 config->readdir_attr_max_access = lp_parm_bool(
2276 SNUM(handle->conn), "readdir_attr", "aapl_max_access", true);
2278 config->model = lp_parm_const_string(
2279 -1, FRUIT_PARAM_TYPE_NAME, "model", "MacSamba");
2281 tm_size_str = lp_parm_const_string(
2282 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2283 "time machine max size", NULL);
2284 if (tm_size_str != NULL) {
2285 config->time_machine_max_size = conv_str_size(tm_size_str);
2288 config->wipe_intentionally_left_blank_rfork = lp_parm_bool(
2289 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2290 "wipe_intentionally_left_blank_rfork", false);
2292 config->delete_empty_adfiles = lp_parm_bool(
2293 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2294 "delete_empty_adfiles", false);
2296 SMB_VFS_HANDLE_SET_DATA(handle, config,
2297 NULL, struct fruit_config_data,
2304 * Prepend "._" to a basename
2305 * Return a new struct smb_filename with stream_name == NULL.
2307 static int adouble_path(TALLOC_CTX *ctx,
2308 const struct smb_filename *smb_fname_in,
2309 struct smb_filename **pp_smb_fname_out)
2313 struct smb_filename *smb_fname = cp_smb_filename(ctx,
2316 if (smb_fname == NULL) {
2320 /* We need streamname to be NULL */
2321 TALLOC_FREE(smb_fname->stream_name);
2323 /* And we're replacing base_name. */
2324 TALLOC_FREE(smb_fname->base_name);
2326 if (!parent_dirname(smb_fname, smb_fname_in->base_name,
2328 TALLOC_FREE(smb_fname);
2332 smb_fname->base_name = talloc_asprintf(smb_fname,
2333 "%s/._%s", parent, base);
2334 if (smb_fname->base_name == NULL) {
2335 TALLOC_FREE(smb_fname);
2339 *pp_smb_fname_out = smb_fname;
2345 * Allocate and initialize an AfpInfo struct
2347 static AfpInfo *afpinfo_new(TALLOC_CTX *ctx)
2349 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2353 ai->afpi_Signature = AFP_Signature;
2354 ai->afpi_Version = AFP_Version;
2355 ai->afpi_BackupTime = AD_DATE_START;
2360 * Pack an AfpInfo struct into a buffer
2362 * Buffer size must be at least AFP_INFO_SIZE
2363 * Returns size of packed buffer
2365 static ssize_t afpinfo_pack(const AfpInfo *ai, char *buf)
2367 memset(buf, 0, AFP_INFO_SIZE);
2369 RSIVAL(buf, 0, ai->afpi_Signature);
2370 RSIVAL(buf, 4, ai->afpi_Version);
2371 RSIVAL(buf, 12, ai->afpi_BackupTime);
2372 memcpy(buf + 16, ai->afpi_FinderInfo, sizeof(ai->afpi_FinderInfo));
2374 return AFP_INFO_SIZE;
2378 * Unpack a buffer into a AfpInfo structure
2380 * Buffer size must be at least AFP_INFO_SIZE
2381 * Returns allocated AfpInfo struct
2383 static AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data)
2385 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2390 ai->afpi_Signature = RIVAL(data, 0);
2391 ai->afpi_Version = RIVAL(data, 4);
2392 ai->afpi_BackupTime = RIVAL(data, 12);
2393 memcpy(ai->afpi_FinderInfo, (const char *)data + 16,
2394 sizeof(ai->afpi_FinderInfo));
2396 if (ai->afpi_Signature != AFP_Signature
2397 || ai->afpi_Version != AFP_Version) {
2398 DEBUG(1, ("Bad AfpInfo signature or version\n"));
2406 * Fake an inode number from the md5 hash of the (xattr) name
2408 static SMB_INO_T fruit_inode(const SMB_STRUCT_STAT *sbuf, const char *sname)
2410 gnutls_hash_hd_t hash_hnd = NULL;
2411 unsigned char hash[16];
2412 SMB_INO_T result = 0;
2416 DBG_DEBUG("fruit_inode called for %ju/%ju [%s]\n",
2417 (uintmax_t)sbuf->st_ex_dev,
2418 (uintmax_t)sbuf->st_ex_ino, sname);
2420 upper_sname = talloc_strdup_upper(talloc_tos(), sname);
2421 SMB_ASSERT(upper_sname != NULL);
2423 rc = gnutls_hash_init(&hash_hnd, GNUTLS_DIG_MD5);
2428 rc = gnutls_hash(hash_hnd, &(sbuf->st_ex_dev), sizeof(sbuf->st_ex_dev));
2430 gnutls_hash_deinit(hash_hnd, NULL);
2433 rc = gnutls_hash(hash_hnd,
2435 sizeof(sbuf->st_ex_ino));
2437 gnutls_hash_deinit(hash_hnd, NULL);
2440 rc = gnutls_hash(hash_hnd,
2442 talloc_get_size(upper_sname) - 1);
2444 gnutls_hash_deinit(hash_hnd, NULL);
2448 gnutls_hash_deinit(hash_hnd, hash);
2450 /* Hopefully all the variation is in the lower 4 (or 8) bytes! */
2451 memcpy(&result, hash, sizeof(result));
2454 DBG_DEBUG("fruit_inode \"%s\": ino=%ju\n",
2455 sname, (uintmax_t)result);
2458 TALLOC_FREE(upper_sname);
2463 static bool add_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
2464 struct stream_struct **streams,
2465 const char *name, off_t size,
2468 struct stream_struct *tmp;
2470 tmp = talloc_realloc(mem_ctx, *streams, struct stream_struct,
2476 tmp[*num_streams].name = talloc_asprintf(tmp, "%s:$DATA", name);
2477 if (tmp[*num_streams].name == NULL) {
2481 tmp[*num_streams].size = size;
2482 tmp[*num_streams].alloc_size = alloc_size;
2489 static bool filter_empty_rsrc_stream(unsigned int *num_streams,
2490 struct stream_struct **streams)
2492 struct stream_struct *tmp = *streams;
2495 if (*num_streams == 0) {
2499 for (i = 0; i < *num_streams; i++) {
2500 if (strequal_m(tmp[i].name, AFPRESOURCE_STREAM)) {
2505 if (i == *num_streams) {
2509 if (tmp[i].size > 0) {
2513 TALLOC_FREE(tmp[i].name);
2514 if (*num_streams - 1 > i) {
2515 memmove(&tmp[i], &tmp[i+1],
2516 (*num_streams - i - 1) * sizeof(struct stream_struct));
2523 static bool del_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
2524 struct stream_struct **streams,
2527 struct stream_struct *tmp = *streams;
2530 if (*num_streams == 0) {
2534 for (i = 0; i < *num_streams; i++) {
2535 if (strequal_m(tmp[i].name, name)) {
2540 if (i == *num_streams) {
2544 TALLOC_FREE(tmp[i].name);
2545 if (*num_streams - 1 > i) {
2546 memmove(&tmp[i], &tmp[i+1],
2547 (*num_streams - i - 1) * sizeof(struct stream_struct));
2554 static bool ad_empty_finderinfo(const struct adouble *ad)
2557 char emptybuf[ADEDLEN_FINDERI] = {0};
2560 fi = ad_get_entry(ad, ADEID_FINDERI);
2562 DBG_ERR("Missing FinderInfo in struct adouble [%p]\n", ad);
2566 cmp = memcmp(emptybuf, fi, ADEDLEN_FINDERI);
2570 static bool ai_empty_finderinfo(const AfpInfo *ai)
2573 char emptybuf[ADEDLEN_FINDERI] = {0};
2575 cmp = memcmp(emptybuf, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
2580 * Update btime with btime from Netatalk
2582 static void update_btime(vfs_handle_struct *handle,
2583 struct smb_filename *smb_fname)
2586 struct timespec creation_time = {0};
2588 struct fruit_config_data *config = NULL;
2590 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
2593 switch (config->meta) {
2594 case FRUIT_META_STREAM:
2596 case FRUIT_META_NETATALK:
2600 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
2604 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
2608 if (ad_getdate(ad, AD_DATE_UNIX | AD_DATE_CREATE, &t) != 0) {
2614 creation_time.tv_sec = convert_uint32_t_to_time_t(t);
2615 update_stat_ex_create_time(&smb_fname->st, creation_time);
2621 * Map an access mask to a Netatalk single byte byte range lock
2623 static off_t access_to_netatalk_brl(enum apple_fork fork_type,
2624 uint32_t access_mask)
2628 switch (access_mask) {
2629 case FILE_READ_DATA:
2630 offset = AD_FILELOCK_OPEN_RD;
2633 case FILE_WRITE_DATA:
2634 case FILE_APPEND_DATA:
2635 offset = AD_FILELOCK_OPEN_WR;
2639 offset = AD_FILELOCK_OPEN_NONE;
2643 if (fork_type == APPLE_FORK_RSRC) {
2644 if (offset == AD_FILELOCK_OPEN_NONE) {
2645 offset = AD_FILELOCK_RSRC_OPEN_NONE;
2655 * Map a deny mode to a Netatalk brl
2657 static off_t denymode_to_netatalk_brl(enum apple_fork fork_type,
2662 switch (deny_mode) {
2664 offset = AD_FILELOCK_DENY_RD;
2668 offset = AD_FILELOCK_DENY_WR;
2672 smb_panic("denymode_to_netatalk_brl: bad deny mode\n");
2675 if (fork_type == APPLE_FORK_RSRC) {
2683 * Call fcntl() with an exclusive F_GETLK request in order to
2684 * determine if there's an exisiting shared lock
2686 * @return true if the requested lock was found or any error occurred
2687 * false if the lock was not found
2689 static bool test_netatalk_lock(files_struct *fsp, off_t in_offset)
2692 off_t offset = in_offset;
2697 result = SMB_VFS_GETLOCK(fsp, &offset, &len, &type, &pid);
2698 if (result == false) {
2702 if (type != F_UNLCK) {
2709 static NTSTATUS fruit_check_access(vfs_handle_struct *handle,
2711 uint32_t access_mask,
2712 uint32_t share_mode)
2714 NTSTATUS status = NT_STATUS_OK;
2716 bool share_for_read = (share_mode & FILE_SHARE_READ);
2717 bool share_for_write = (share_mode & FILE_SHARE_WRITE);
2718 bool netatalk_already_open_for_reading = false;
2719 bool netatalk_already_open_for_writing = false;
2720 bool netatalk_already_open_with_deny_read = false;
2721 bool netatalk_already_open_with_deny_write = false;
2723 /* FIXME: hardcoded data fork, add resource fork */
2724 enum apple_fork fork_type = APPLE_FORK_DATA;
2726 DBG_DEBUG("fruit_check_access: %s, am: %s/%s, sm: 0x%x\n",
2728 access_mask & FILE_READ_DATA ? "READ" :"-",
2729 access_mask & FILE_WRITE_DATA ? "WRITE" : "-",
2732 if (fsp->fh->fd == -1) {
2733 return NT_STATUS_OK;
2736 /* Read NetATalk opens and deny modes on the file. */
2737 netatalk_already_open_for_reading = test_netatalk_lock(fsp,
2738 access_to_netatalk_brl(fork_type,
2741 netatalk_already_open_with_deny_read = test_netatalk_lock(fsp,
2742 denymode_to_netatalk_brl(fork_type,
2745 netatalk_already_open_for_writing = test_netatalk_lock(fsp,
2746 access_to_netatalk_brl(fork_type,
2749 netatalk_already_open_with_deny_write = test_netatalk_lock(fsp,
2750 denymode_to_netatalk_brl(fork_type,
2753 /* If there are any conflicts - sharing violation. */
2754 if ((access_mask & FILE_READ_DATA) &&
2755 netatalk_already_open_with_deny_read) {
2756 return NT_STATUS_SHARING_VIOLATION;
2759 if (!share_for_read &&
2760 netatalk_already_open_for_reading) {
2761 return NT_STATUS_SHARING_VIOLATION;
2764 if ((access_mask & FILE_WRITE_DATA) &&
2765 netatalk_already_open_with_deny_write) {
2766 return NT_STATUS_SHARING_VIOLATION;
2769 if (!share_for_write &&
2770 netatalk_already_open_for_writing) {
2771 return NT_STATUS_SHARING_VIOLATION;
2774 if (!(access_mask & FILE_READ_DATA)) {
2776 * Nothing we can do here, we need read access
2779 return NT_STATUS_OK;
2782 /* Set NetAtalk locks matching our access */
2783 if (access_mask & FILE_READ_DATA) {
2784 struct byte_range_lock *br_lck = NULL;
2786 off = access_to_netatalk_brl(fork_type, FILE_READ_DATA);
2788 handle->conn->sconn->msg_ctx, fsp,
2789 fsp->op->global->open_persistent_id, 1, off,
2790 READ_LOCK, POSIX_LOCK, false,
2793 TALLOC_FREE(br_lck);
2795 if (!NT_STATUS_IS_OK(status)) {
2800 if (!share_for_read) {
2801 struct byte_range_lock *br_lck = NULL;
2803 off = denymode_to_netatalk_brl(fork_type, DENY_READ);
2805 handle->conn->sconn->msg_ctx, fsp,
2806 fsp->op->global->open_persistent_id, 1, off,
2807 READ_LOCK, POSIX_LOCK, false,
2810 TALLOC_FREE(br_lck);
2812 if (!NT_STATUS_IS_OK(status)) {
2817 if (access_mask & FILE_WRITE_DATA) {
2818 struct byte_range_lock *br_lck = NULL;
2820 off = access_to_netatalk_brl(fork_type, FILE_WRITE_DATA);
2822 handle->conn->sconn->msg_ctx, fsp,
2823 fsp->op->global->open_persistent_id, 1, off,
2824 READ_LOCK, POSIX_LOCK, false,
2827 TALLOC_FREE(br_lck);
2829 if (!NT_STATUS_IS_OK(status)) {
2834 if (!share_for_write) {
2835 struct byte_range_lock *br_lck = NULL;
2837 off = denymode_to_netatalk_brl(fork_type, DENY_WRITE);
2839 handle->conn->sconn->msg_ctx, fsp,
2840 fsp->op->global->open_persistent_id, 1, off,
2841 READ_LOCK, POSIX_LOCK, false,
2844 TALLOC_FREE(br_lck);
2846 if (!NT_STATUS_IS_OK(status)) {
2851 return NT_STATUS_OK;
2854 static NTSTATUS check_aapl(vfs_handle_struct *handle,
2855 struct smb_request *req,
2856 const struct smb2_create_blobs *in_context_blobs,
2857 struct smb2_create_blobs *out_context_blobs)
2859 struct fruit_config_data *config;
2861 struct smb2_create_blob *aapl = NULL;
2865 DATA_BLOB blob = data_blob_talloc(req, NULL, 0);
2866 uint64_t req_bitmap, client_caps;
2867 uint64_t server_caps = SMB2_CRTCTX_AAPL_UNIX_BASED;
2871 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
2872 return NT_STATUS_UNSUCCESSFUL);
2874 if (!config->use_aapl
2875 || in_context_blobs == NULL
2876 || out_context_blobs == NULL) {
2877 return NT_STATUS_OK;
2880 aapl = smb2_create_blob_find(in_context_blobs,
2881 SMB2_CREATE_TAG_AAPL);
2883 return NT_STATUS_OK;
2886 if (aapl->data.length != 24) {
2887 DEBUG(1, ("unexpected AAPL ctxt length: %ju\n",
2888 (uintmax_t)aapl->data.length));
2889 return NT_STATUS_INVALID_PARAMETER;
2892 cmd = IVAL(aapl->data.data, 0);
2893 if (cmd != SMB2_CRTCTX_AAPL_SERVER_QUERY) {
2894 DEBUG(1, ("unsupported AAPL cmd: %d\n", cmd));
2895 return NT_STATUS_INVALID_PARAMETER;
2898 req_bitmap = BVAL(aapl->data.data, 8);
2899 client_caps = BVAL(aapl->data.data, 16);
2901 SIVAL(p, 0, SMB2_CRTCTX_AAPL_SERVER_QUERY);
2903 SBVAL(p, 8, req_bitmap);
2904 ok = data_blob_append(req, &blob, p, 16);
2906 return NT_STATUS_UNSUCCESSFUL;
2909 if (req_bitmap & SMB2_CRTCTX_AAPL_SERVER_CAPS) {
2910 if ((client_caps & SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR) &&
2911 (handle->conn->tcon->compat->fs_capabilities & FILE_NAMED_STREAMS)) {
2912 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR;
2913 config->readdir_attr_enabled = true;
2916 if (config->use_copyfile) {
2917 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_OSX_COPYFILE;
2918 config->copyfile_enabled = true;
2922 * The client doesn't set the flag, so we can't check
2923 * for it and just set it unconditionally
2925 if (config->unix_info_enabled) {
2926 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_NFS_ACE;
2929 SBVAL(p, 0, server_caps);
2930 ok = data_blob_append(req, &blob, p, 8);
2932 return NT_STATUS_UNSUCCESSFUL;
2936 if (req_bitmap & SMB2_CRTCTX_AAPL_VOLUME_CAPS) {
2937 int val = lp_case_sensitive(SNUM(handle->conn->tcon->compat));
2945 caps |= SMB2_CRTCTX_AAPL_CASE_SENSITIVE;
2952 if (config->time_machine) {
2953 caps |= SMB2_CRTCTX_AAPL_FULL_SYNC;
2958 ok = data_blob_append(req, &blob, p, 8);
2960 return NT_STATUS_UNSUCCESSFUL;
2964 if (req_bitmap & SMB2_CRTCTX_AAPL_MODEL_INFO) {
2965 ok = convert_string_talloc(req,
2966 CH_UNIX, CH_UTF16LE,
2967 config->model, strlen(config->model),
2970 return NT_STATUS_UNSUCCESSFUL;
2974 SIVAL(p + 4, 0, modellen);
2975 ok = data_blob_append(req, &blob, p, 8);
2978 return NT_STATUS_UNSUCCESSFUL;
2981 ok = data_blob_append(req, &blob, model, modellen);
2984 return NT_STATUS_UNSUCCESSFUL;
2988 status = smb2_create_blob_add(out_context_blobs,
2990 SMB2_CREATE_TAG_AAPL,
2992 if (NT_STATUS_IS_OK(status)) {
2993 global_fruit_config.nego_aapl = true;
2994 if (config->aapl_zero_file_id) {
2995 aapl_force_zero_file_id(handle->conn->sconn);
3002 static bool readdir_attr_meta_finderi_stream(
3003 struct vfs_handle_struct *handle,
3004 const struct smb_filename *smb_fname,
3007 struct smb_filename *stream_name = NULL;
3008 files_struct *fsp = NULL;
3013 uint8_t buf[AFP_INFO_SIZE];
3015 stream_name = synthetic_smb_fname(talloc_tos(),
3016 smb_fname->base_name,
3017 AFPINFO_STREAM_NAME,
3018 NULL, smb_fname->flags);
3019 if (stream_name == NULL) {
3023 ret = SMB_VFS_STAT(handle->conn, stream_name);
3028 status = SMB_VFS_CREATE_FILE(
3029 handle->conn, /* conn */
3031 0, /* root_dir_fid */
3032 stream_name, /* fname */
3033 FILE_READ_DATA, /* access_mask */
3034 (FILE_SHARE_READ | FILE_SHARE_WRITE | /* share_access */
3036 FILE_OPEN, /* create_disposition*/
3037 0, /* create_options */
3038 0, /* file_attributes */
3039 INTERNAL_OPEN_ONLY, /* oplock_request */
3041 0, /* allocation_size */
3042 0, /* private_flags */
3047 NULL, NULL); /* create context */
3049 TALLOC_FREE(stream_name);
3051 if (!NT_STATUS_IS_OK(status)) {
3055 nread = SMB_VFS_PREAD(fsp, &buf[0], AFP_INFO_SIZE, 0);
3056 if (nread != AFP_INFO_SIZE) {
3057 DBG_ERR("short read [%s] [%zd/%d]\n",
3058 smb_fname_str_dbg(stream_name), nread, AFP_INFO_SIZE);
3063 memcpy(&ai->afpi_FinderInfo[0], &buf[AFP_OFF_FinderInfo],
3070 close_file(NULL, fsp, NORMAL_CLOSE);
3076 static bool readdir_attr_meta_finderi_netatalk(
3077 struct vfs_handle_struct *handle,
3078 const struct smb_filename *smb_fname,
3081 struct adouble *ad = NULL;
3084 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
3089 p = ad_get_entry(ad, ADEID_FINDERI);
3091 DBG_ERR("No ADEID_FINDERI for [%s]\n", smb_fname->base_name);
3096 memcpy(&ai->afpi_FinderInfo[0], p, AFP_FinderSize);
3101 static bool readdir_attr_meta_finderi(struct vfs_handle_struct *handle,
3102 const struct smb_filename *smb_fname,
3103 struct readdir_attr_data *attr_data)
3105 struct fruit_config_data *config = NULL;
3106 uint32_t date_added;
3110 SMB_VFS_HANDLE_GET_DATA(handle, config,
3111 struct fruit_config_data,
3114 switch (config->meta) {
3115 case FRUIT_META_NETATALK:
3116 ok = readdir_attr_meta_finderi_netatalk(
3117 handle, smb_fname, &ai);
3120 case FRUIT_META_STREAM:
3121 ok = readdir_attr_meta_finderi_stream(
3122 handle, smb_fname, &ai);
3126 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3131 /* Don't bother with errors, it's likely ENOENT */
3135 if (S_ISREG(smb_fname->st.st_ex_mode)) {
3137 memcpy(&attr_data->attr_data.aapl.finder_info[0],
3138 &ai.afpi_FinderInfo[0], 4);
3140 /* finder_creator */
3141 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 4,
3142 &ai.afpi_FinderInfo[4], 4);
3146 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 8,
3147 &ai.afpi_FinderInfo[8], 2);
3149 /* finder_ext_flags */
3150 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 10,
3151 &ai.afpi_FinderInfo[24], 2);
3154 date_added = convert_time_t_to_uint32_t(
3155 smb_fname->st.st_ex_btime.tv_sec - AD_DATE_DELTA);
3157 RSIVAL(&attr_data->attr_data.aapl.finder_info[0], 12, date_added);
3162 static uint64_t readdir_attr_rfork_size_adouble(
3163 struct vfs_handle_struct *handle,
3164 const struct smb_filename *smb_fname)
3166 struct adouble *ad = NULL;
3167 uint64_t rfork_size;
3169 ad = ad_get(talloc_tos(), handle, smb_fname,
3175 rfork_size = ad_getentrylen(ad, ADEID_RFORK);
3181 static uint64_t readdir_attr_rfork_size_stream(
3182 struct vfs_handle_struct *handle,
3183 const struct smb_filename *smb_fname)
3185 struct smb_filename *stream_name = NULL;
3187 uint64_t rfork_size;
3189 stream_name = synthetic_smb_fname(talloc_tos(),
3190 smb_fname->base_name,
3191 AFPRESOURCE_STREAM_NAME,
3193 if (stream_name == NULL) {
3197 ret = SMB_VFS_STAT(handle->conn, stream_name);
3199 TALLOC_FREE(stream_name);
3203 rfork_size = stream_name->st.st_ex_size;
3204 TALLOC_FREE(stream_name);
3209 static uint64_t readdir_attr_rfork_size(struct vfs_handle_struct *handle,
3210 const struct smb_filename *smb_fname)
3212 struct fruit_config_data *config = NULL;
3213 uint64_t rfork_size;
3215 SMB_VFS_HANDLE_GET_DATA(handle, config,
3216 struct fruit_config_data,
3219 switch (config->rsrc) {
3220 case FRUIT_RSRC_ADFILE:
3221 case FRUIT_RSRC_XATTR:
3222 rfork_size = readdir_attr_rfork_size_adouble(handle,
3226 case FRUIT_META_STREAM:
3227 rfork_size = readdir_attr_rfork_size_stream(handle,
3232 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3240 static NTSTATUS readdir_attr_macmeta(struct vfs_handle_struct *handle,
3241 const struct smb_filename *smb_fname,
3242 struct readdir_attr_data *attr_data)
3244 NTSTATUS status = NT_STATUS_OK;
3245 struct fruit_config_data *config = NULL;
3248 SMB_VFS_HANDLE_GET_DATA(handle, config,
3249 struct fruit_config_data,
3250 return NT_STATUS_UNSUCCESSFUL);
3253 /* Ensure we return a default value in the creation_date field */
3254 RSIVAL(&attr_data->attr_data.aapl.finder_info, 12, AD_DATE_START);
3257 * Resource fork length
3260 if (config->readdir_attr_rsize) {
3261 uint64_t rfork_size;
3263 rfork_size = readdir_attr_rfork_size(handle, smb_fname);
3264 attr_data->attr_data.aapl.rfork_size = rfork_size;
3271 if (config->readdir_attr_finder_info) {
3272 ok = readdir_attr_meta_finderi(handle, smb_fname, attr_data);
3274 status = NT_STATUS_INTERNAL_ERROR;
3281 static NTSTATUS remove_virtual_nfs_aces(struct security_descriptor *psd)
3286 if (psd->dacl == NULL) {
3287 return NT_STATUS_OK;
3290 for (i = 0; i < psd->dacl->num_aces; i++) {
3291 /* MS NFS style mode/uid/gid */
3292 int cmp = dom_sid_compare_domain(
3293 &global_sid_Unix_NFS,
3294 &psd->dacl->aces[i].trustee);
3296 /* Normal ACE entry. */
3301 * security_descriptor_dacl_del()
3302 * *must* return NT_STATUS_OK as we know
3303 * we have something to remove.
3306 status = security_descriptor_dacl_del(psd,
3307 &psd->dacl->aces[i].trustee);
3308 if (!NT_STATUS_IS_OK(status)) {
3309 DBG_WARNING("failed to remove MS NFS style ACE: %s\n",
3315 * security_descriptor_dacl_del() may delete more
3316 * then one entry subsequent to this one if the
3317 * SID matches, but we only need to ensure that
3318 * we stay looking at the same element in the array.
3322 return NT_STATUS_OK;
3325 /* Search MS NFS style ACE with UNIX mode */
3326 static NTSTATUS check_ms_nfs(vfs_handle_struct *handle,
3328 struct security_descriptor *psd,
3333 struct fruit_config_data *config = NULL;
3337 SMB_VFS_HANDLE_GET_DATA(handle, config,
3338 struct fruit_config_data,
3339 return NT_STATUS_UNSUCCESSFUL);
3341 if (!global_fruit_config.nego_aapl) {
3342 return NT_STATUS_OK;
3344 if (psd->dacl == NULL || !config->unix_info_enabled) {
3345 return NT_STATUS_OK;
3348 for (i = 0; i < psd->dacl->num_aces; i++) {
3349 if (dom_sid_compare_domain(
3350 &global_sid_Unix_NFS_Mode,
3351 &psd->dacl->aces[i].trustee) == 0) {
3352 *pmode = (mode_t)psd->dacl->aces[i].trustee.sub_auths[2];
3353 *pmode &= (S_IRWXU | S_IRWXG | S_IRWXO);
3356 DEBUG(10, ("MS NFS chmod request %s, %04o\n",
3357 fsp_str_dbg(fsp), (unsigned)(*pmode)));
3363 * Remove any incoming virtual ACE entries generated by
3364 * fruit_fget_nt_acl().
3367 return remove_virtual_nfs_aces(psd);
3370 /****************************************************************************
3372 ****************************************************************************/
3374 static int fruit_connect(vfs_handle_struct *handle,
3375 const char *service,
3379 char *list = NULL, *newlist = NULL;
3380 struct fruit_config_data *config;
3382 DEBUG(10, ("fruit_connect\n"));
3384 rc = SMB_VFS_NEXT_CONNECT(handle, service, user);
3389 rc = init_fruit_config(handle);
3394 SMB_VFS_HANDLE_GET_DATA(handle, config,
3395 struct fruit_config_data, return -1);
3397 if (config->veto_appledouble) {
3398 list = lp_veto_files(talloc_tos(), SNUM(handle->conn));
3401 if (strstr(list, "/" ADOUBLE_NAME_PREFIX "*/") == NULL) {
3402 newlist = talloc_asprintf(
3404 "%s/" ADOUBLE_NAME_PREFIX "*/",
3406 lp_do_parameter(SNUM(handle->conn),
3411 lp_do_parameter(SNUM(handle->conn),
3413 "/" ADOUBLE_NAME_PREFIX "*/");
3419 if (config->encoding == FRUIT_ENC_NATIVE) {
3420 lp_do_parameter(SNUM(handle->conn),
3425 if (config->time_machine) {
3426 DBG_NOTICE("Enabling durable handles for Time Machine "
3427 "support on [%s]\n", service);
3428 lp_do_parameter(SNUM(handle->conn), "durable handles", "yes");
3429 lp_do_parameter(SNUM(handle->conn), "kernel oplocks", "no");
3430 lp_do_parameter(SNUM(handle->conn), "kernel share modes", "no");
3431 if (!lp_strict_sync(SNUM(handle->conn))) {
3432 DBG_WARNING("Time Machine without strict sync is not "
3435 lp_do_parameter(SNUM(handle->conn), "posix locking", "no");
3441 static int fruit_fake_fd(void)
3448 * Return a valid fd, but ensure any attempt to use it returns
3449 * an error (EPIPE). Once we get a write on the handle, we open
3452 ret = pipe(pipe_fds);
3462 static int fruit_open_meta_stream(vfs_handle_struct *handle,
3463 struct smb_filename *smb_fname,
3468 struct fruit_config_data *config = NULL;
3469 struct fio *fio = NULL;
3470 int open_flags = flags & ~O_CREAT;
3473 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3475 SMB_VFS_HANDLE_GET_DATA(handle, config,
3476 struct fruit_config_data, return -1);
3478 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3479 fio->type = ADOUBLE_META;
3480 fio->config = config;
3482 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, open_flags, mode);
3487 if (!(flags & O_CREAT)) {
3488 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
3492 fd = fruit_fake_fd();
3494 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
3498 fio->fake_fd = true;
3505 static int fruit_open_meta_netatalk(vfs_handle_struct *handle,
3506 struct smb_filename *smb_fname,
3511 struct fruit_config_data *config = NULL;
3512 struct fio *fio = NULL;
3513 struct adouble *ad = NULL;
3514 bool meta_exists = false;
3517 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3519 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
3526 if (!meta_exists && !(flags & O_CREAT)) {
3531 fd = fruit_fake_fd();
3536 SMB_VFS_HANDLE_GET_DATA(handle, config,
3537 struct fruit_config_data, return -1);
3539 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3540 fio->type = ADOUBLE_META;
3541 fio->config = config;
3542 fio->fake_fd = true;
3549 static int fruit_open_meta(vfs_handle_struct *handle,
3550 struct smb_filename *smb_fname,
3551 files_struct *fsp, int flags, mode_t mode)
3554 struct fruit_config_data *config = NULL;
3556 DBG_DEBUG("path [%s]\n", smb_fname_str_dbg(smb_fname));
3558 SMB_VFS_HANDLE_GET_DATA(handle, config,
3559 struct fruit_config_data, return -1);
3561 switch (config->meta) {
3562 case FRUIT_META_STREAM:
3563 fd = fruit_open_meta_stream(handle, smb_fname,
3567 case FRUIT_META_NETATALK:
3568 fd = fruit_open_meta_netatalk(handle, smb_fname,
3573 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3577 DBG_DEBUG("path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3582 static int fruit_open_rsrc_adouble(vfs_handle_struct *handle,
3583 struct smb_filename *smb_fname,
3589 struct adouble *ad = NULL;
3590 struct smb_filename *smb_fname_base = NULL;
3591 struct fruit_config_data *config = NULL;
3594 SMB_VFS_HANDLE_GET_DATA(handle, config,
3595 struct fruit_config_data, return -1);
3597 if ((!(flags & O_CREAT)) &&
3598 S_ISDIR(fsp->base_fsp->fsp_name->st.st_ex_mode))
3600 /* sorry, but directories don't habe a resource fork */
3605 rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_base);
3610 /* We always need read/write access for the metadata header too */
3611 flags &= ~(O_RDONLY | O_WRONLY);
3614 hostfd = SMB_VFS_NEXT_OPEN(handle, smb_fname_base, fsp,
3621 if (flags & (O_CREAT | O_TRUNC)) {
3622 ad = ad_init(fsp, handle, ADOUBLE_RSRC);
3628 fsp->fh->fd = hostfd;
3630 rc = ad_fset(handle, ad, fsp);
3641 TALLOC_FREE(smb_fname_base);
3643 DEBUG(10, ("fruit_open resource fork: rc=%d, fd=%d\n", rc, hostfd));
3645 int saved_errno = errno;
3648 * BUGBUGBUG -- we would need to call
3649 * fd_close_posix here, but we don't have a
3652 fsp->fh->fd = hostfd;
3656 errno = saved_errno;
3661 static int fruit_open_rsrc_xattr(vfs_handle_struct *handle,
3662 struct smb_filename *smb_fname,
3667 #ifdef HAVE_ATTROPEN
3670 fd = attropen(smb_fname->base_name,
3671 AFPRESOURCE_EA_NETATALK,
3686 static int fruit_open_rsrc(vfs_handle_struct *handle,
3687 struct smb_filename *smb_fname,
3688 files_struct *fsp, int flags, mode_t mode)
3691 struct fruit_config_data *config = NULL;
3692 struct fio *fio = NULL;
3694 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3696 SMB_VFS_HANDLE_GET_DATA(handle, config,
3697 struct fruit_config_data, return -1);
3699 switch (config->rsrc) {
3700 case FRUIT_RSRC_STREAM:
3701 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3704 case FRUIT_RSRC_ADFILE:
3705 fd = fruit_open_rsrc_adouble(handle, smb_fname,
3709 case FRUIT_RSRC_XATTR:
3710 fd = fruit_open_rsrc_xattr(handle, smb_fname,
3715 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3719 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3725 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3726 fio->type = ADOUBLE_RSRC;
3727 fio->config = config;
3732 static int fruit_open(vfs_handle_struct *handle,
3733 struct smb_filename *smb_fname,
3734 files_struct *fsp, int flags, mode_t mode)
3738 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3740 if (!is_ntfs_stream_smb_fname(smb_fname)) {
3741 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3744 if (is_afpinfo_stream(smb_fname)) {
3745 fd = fruit_open_meta(handle, smb_fname, fsp, flags, mode);
3746 } else if (is_afpresource_stream(smb_fname)) {
3747 fd = fruit_open_rsrc(handle, smb_fname, fsp, flags, mode);
3749 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3752 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3757 static int fruit_close_meta(vfs_handle_struct *handle,
3761 struct fruit_config_data *config = NULL;
3763 SMB_VFS_HANDLE_GET_DATA(handle, config,
3764 struct fruit_config_data, return -1);
3766 switch (config->meta) {
3767 case FRUIT_META_STREAM:
3768 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3771 case FRUIT_META_NETATALK:
3772 ret = close(fsp->fh->fd);
3777 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3785 static int fruit_close_rsrc(vfs_handle_struct *handle,
3789 struct fruit_config_data *config = NULL;
3791 SMB_VFS_HANDLE_GET_DATA(handle, config,
3792 struct fruit_config_data, return -1);
3794 switch (config->rsrc) {
3795 case FRUIT_RSRC_STREAM:
3796 case FRUIT_RSRC_ADFILE:
3797 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3800 case FRUIT_RSRC_XATTR:
3801 ret = close(fsp->fh->fd);
3806 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3813 static int fruit_close(vfs_handle_struct *handle,
3821 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(fsp->fsp_name), fd);
3823 if (!is_ntfs_stream_smb_fname(fsp->fsp_name)) {
3824 return SMB_VFS_NEXT_CLOSE(handle, fsp);
3827 if (is_afpinfo_stream(fsp->fsp_name)) {
3828 ret = fruit_close_meta(handle, fsp);
3829 } else if (is_afpresource_stream(fsp->fsp_name)) {
3830 ret = fruit_close_rsrc(handle, fsp);
3832 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3838 static int fruit_rename(struct vfs_handle_struct *handle,
3839 const struct smb_filename *smb_fname_src,
3840 const struct smb_filename *smb_fname_dst)
3843 struct fruit_config_data *config = NULL;
3844 struct smb_filename *src_adp_smb_fname = NULL;
3845 struct smb_filename *dst_adp_smb_fname = NULL;
3847 SMB_VFS_HANDLE_GET_DATA(handle, config,
3848 struct fruit_config_data, return -1);
3850 if (!VALID_STAT(smb_fname_src->st)) {
3851 DBG_ERR("Need valid stat for [%s]\n",
3852 smb_fname_str_dbg(smb_fname_src));
3856 rc = SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
3861 if ((config->rsrc != FRUIT_RSRC_ADFILE) ||
3862 (!S_ISREG(smb_fname_src->st.st_ex_mode)))
3867 rc = adouble_path(talloc_tos(), smb_fname_src, &src_adp_smb_fname);
3872 rc = adouble_path(talloc_tos(), smb_fname_dst, &dst_adp_smb_fname);
3877 DBG_DEBUG("%s -> %s\n",
3878 smb_fname_str_dbg(src_adp_smb_fname),
3879 smb_fname_str_dbg(dst_adp_smb_fname));
3881 rc = SMB_VFS_NEXT_RENAME(handle, src_adp_smb_fname, dst_adp_smb_fname);
3882 if (errno == ENOENT) {
3887 TALLOC_FREE(src_adp_smb_fname);
3888 TALLOC_FREE(dst_adp_smb_fname);
3892 static int fruit_unlink_meta_stream(vfs_handle_struct *handle,
3893 const struct smb_filename *smb_fname)
3895 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3898 static int fruit_unlink_meta_netatalk(vfs_handle_struct *handle,
3899 const struct smb_filename *smb_fname)
3901 return SMB_VFS_REMOVEXATTR(handle->conn,
3903 AFPINFO_EA_NETATALK);
3906 static int fruit_unlink_meta(vfs_handle_struct *handle,
3907 const struct smb_filename *smb_fname)
3909 struct fruit_config_data *config = NULL;
3912 SMB_VFS_HANDLE_GET_DATA(handle, config,
3913 struct fruit_config_data, return -1);
3915 switch (config->meta) {
3916 case FRUIT_META_STREAM:
3917 rc = fruit_unlink_meta_stream(handle, smb_fname);
3920 case FRUIT_META_NETATALK:
3921 rc = fruit_unlink_meta_netatalk(handle, smb_fname);
3925 DBG_ERR("Unsupported meta config [%d]\n", config->meta);
3932 static int fruit_unlink_rsrc_stream(vfs_handle_struct *handle,
3933 const struct smb_filename *smb_fname,
3938 if (!force_unlink) {
3939 struct smb_filename *smb_fname_cp = NULL;
3942 smb_fname_cp = cp_smb_filename(talloc_tos(), smb_fname);
3943 if (smb_fname_cp == NULL) {
3948 * 0 byte resource fork streams are not listed by
3949 * vfs_streaminfo, as a result stream cleanup/deletion of file
3950 * deletion doesn't remove the resourcefork stream.
3953 ret = SMB_VFS_NEXT_STAT(handle, smb_fname_cp);
3955 TALLOC_FREE(smb_fname_cp);
3956 DBG_ERR("stat [%s] failed [%s]\n",
3957 smb_fname_str_dbg(smb_fname_cp), strerror(errno));
3961 size = smb_fname_cp->st.st_ex_size;
3962 TALLOC_FREE(smb_fname_cp);
3965 /* OS X ignores resource fork stream delete requests */
3970 ret = SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3971 if ((ret != 0) && (errno == ENOENT) && force_unlink) {
3978 static int fruit_unlink_rsrc_adouble(vfs_handle_struct *handle,
3979 const struct smb_filename *smb_fname,
3983 struct adouble *ad = NULL;
3984 struct smb_filename *adp_smb_fname = NULL;
3986 if (!force_unlink) {
3987 ad = ad_get(talloc_tos(), handle, smb_fname,
3996 * 0 byte resource fork streams are not listed by
3997 * vfs_streaminfo, as a result stream cleanup/deletion of file
3998 * deletion doesn't remove the resourcefork stream.
4001 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
4002 /* OS X ignores resource fork stream delete requests */
4010 rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
4015 rc = SMB_VFS_NEXT_UNLINK(handle, adp_smb_fname);
4016 TALLOC_FREE(adp_smb_fname);
4017 if ((rc != 0) && (errno == ENOENT) && force_unlink) {
4024 static int fruit_unlink_rsrc_xattr(vfs_handle_struct *handle,
4025 const struct smb_filename *smb_fname,
4029 * OS X ignores resource fork stream delete requests, so nothing to do
4030 * here. Removing the file will remove the xattr anyway, so we don't
4031 * have to take care of removing 0 byte resource forks that could be
4037 static int fruit_unlink_rsrc(vfs_handle_struct *handle,
4038 const struct smb_filename *smb_fname,
4041 struct fruit_config_data *config = NULL;
4044 SMB_VFS_HANDLE_GET_DATA(handle, config,
4045 struct fruit_config_data, return -1);
4047 switch (config->rsrc) {
4048 case FRUIT_RSRC_STREAM:
4049 rc = fruit_unlink_rsrc_stream(handle, smb_fname, force_unlink);
4052 case FRUIT_RSRC_ADFILE:
4053 rc = fruit_unlink_rsrc_adouble(handle, smb_fname, force_unlink);
4056 case FRUIT_RSRC_XATTR:
4057 rc = fruit_unlink_rsrc_xattr(handle, smb_fname, force_unlink);
4061 DBG_ERR("Unsupported rsrc config [%d]\n", config->rsrc);
4068 static int fruit_unlink(vfs_handle_struct *handle,
4069 const struct smb_filename *smb_fname)
4072 struct fruit_config_data *config = NULL;
4073 struct smb_filename *rsrc_smb_fname = NULL;
4075 SMB_VFS_HANDLE_GET_DATA(handle, config,
4076 struct fruit_config_data, return -1);
4078 if (is_afpinfo_stream(smb_fname)) {
4079 return fruit_unlink_meta(handle, smb_fname);
4080 } else if (is_afpresource_stream(smb_fname)) {
4081 return fruit_unlink_rsrc(handle, smb_fname, false);
4082 } if (is_ntfs_stream_smb_fname(smb_fname)) {
4083 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4087 * A request to delete the base file. Because 0 byte resource
4088 * fork streams are not listed by fruit_streaminfo,
4089 * delete_all_streams() can't remove 0 byte resource fork
4090 * streams, so we have to cleanup this here.
4092 rsrc_smb_fname = synthetic_smb_fname(talloc_tos(),
4093 smb_fname->base_name,
4094 AFPRESOURCE_STREAM_NAME,
4097 if (rsrc_smb_fname == NULL) {
4101 rc = fruit_unlink_rsrc(handle, rsrc_smb_fname, true);
4102 if ((rc != 0) && (errno != ENOENT)) {
4103 DBG_ERR("Forced unlink of [%s] failed [%s]\n",
4104 smb_fname_str_dbg(rsrc_smb_fname), strerror(errno));
4105 TALLOC_FREE(rsrc_smb_fname);
4108 TALLOC_FREE(rsrc_smb_fname);
4110 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4113 static int fruit_chmod(vfs_handle_struct *handle,
4114 const struct smb_filename *smb_fname,
4118 struct fruit_config_data *config = NULL;
4119 struct smb_filename *smb_fname_adp = NULL;
4121 rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname, mode);
4126 SMB_VFS_HANDLE_GET_DATA(handle, config,
4127 struct fruit_config_data, return -1);
4129 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4133 if (!VALID_STAT(smb_fname->st)) {
4137 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
4141 rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_adp);
4146 DEBUG(10, ("fruit_chmod: %s\n", smb_fname_adp->base_name));
4148 rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname_adp, mode);
4149 if (errno == ENOENT) {
4153 TALLOC_FREE(smb_fname_adp);
4157 static int fruit_chown(vfs_handle_struct *handle,
4158 const struct smb_filename *smb_fname,
4163 struct fruit_config_data *config = NULL;
4164 struct smb_filename *adp_smb_fname = NULL;
4166 rc = SMB_VFS_NEXT_CHOWN(handle, smb_fname, uid, gid);
4171 SMB_VFS_HANDLE_GET_DATA(handle, config,
4172 struct fruit_config_data, return -1);
4174 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4178 if (!VALID_STAT(smb_fname->st)) {
4182 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
4186 rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
4191 DEBUG(10, ("fruit_chown: %s\n", adp_smb_fname->base_name));
4193 rc = SMB_VFS_NEXT_CHOWN(handle, adp_smb_fname, uid, gid);
4194 if (errno == ENOENT) {
4199 TALLOC_FREE(adp_smb_fname);
4203 static int fruit_rmdir(struct vfs_handle_struct *handle,
4204 const struct smb_filename *smb_fname)
4208 struct fruit_config_data *config;
4210 SMB_VFS_HANDLE_GET_DATA(handle, config,
4211 struct fruit_config_data, return -1);
4213 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4218 * Due to there is no way to change bDeleteVetoFiles variable
4219 * from this module, need to clean up ourselves
4222 dh = SMB_VFS_OPENDIR(handle->conn, smb_fname, NULL, 0);
4227 while ((de = SMB_VFS_READDIR(handle->conn, dh, NULL)) != NULL) {
4229 struct adouble *ad = NULL;
4231 struct smb_filename *ad_smb_fname = NULL;
4234 match = strncmp(de->d_name,
4235 ADOUBLE_NAME_PREFIX,
4236 strlen(ADOUBLE_NAME_PREFIX));
4241 p = talloc_asprintf(talloc_tos(), "%s/%s",
4242 smb_fname->base_name, de->d_name);
4244 DBG_ERR("talloc_asprintf failed\n");
4248 ad_smb_fname = synthetic_smb_fname(talloc_tos(), p,
4252 if (ad_smb_fname == NULL) {
4253 DBG_ERR("synthetic_smb_fname failed\n");
4258 * Check whether it's a valid AppleDouble file, if
4259 * yes, delete it, ignore it otherwise.
4261 ad = ad_get(talloc_tos(), handle, ad_smb_fname, ADOUBLE_RSRC);
4263 TALLOC_FREE(ad_smb_fname);
4269 ret = SMB_VFS_NEXT_UNLINK(handle, ad_smb_fname);
4271 DBG_ERR("Deleting [%s] failed\n",
4272 smb_fname_str_dbg(ad_smb_fname));
4274 TALLOC_FREE(ad_smb_fname);
4279 SMB_VFS_CLOSEDIR(handle->conn, dh);
4281 return SMB_VFS_NEXT_RMDIR(handle, smb_fname);
4284 static ssize_t fruit_pread_meta_stream(vfs_handle_struct *handle,
4285 files_struct *fsp, void *data,
4286 size_t n, off_t offset)
4291 nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4292 if (nread == -1 || nread == n) {
4296 DBG_ERR("Removing [%s] after short read [%zd]\n",
4297 fsp_str_dbg(fsp), nread);
4299 ret = SMB_VFS_NEXT_UNLINK(handle, fsp->fsp_name);
4301 DBG_ERR("Removing [%s] failed\n", fsp_str_dbg(fsp));
4309 static ssize_t fruit_pread_meta_adouble(vfs_handle_struct *handle,
4310 files_struct *fsp, void *data,
4311 size_t n, off_t offset)
4314 struct adouble *ad = NULL;
4315 char afpinfo_buf[AFP_INFO_SIZE];
4319 ai = afpinfo_new(talloc_tos());
4324 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
4330 p = ad_get_entry(ad, ADEID_FINDERI);
4332 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
4337 memcpy(&ai->afpi_FinderInfo[0], p, ADEDLEN_FINDERI);
4339 nread = afpinfo_pack(ai, afpinfo_buf);
4340 if (nread != AFP_INFO_SIZE) {
4345 memcpy(data, afpinfo_buf, n);
4353 static ssize_t fruit_pread_meta(vfs_handle_struct *handle,
4354 files_struct *fsp, void *data,
4355 size_t n, off_t offset)
4357 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4362 * OS X has a off-by-1 error in the offset calculation, so we're
4363 * bug compatible here. It won't hurt, as any relevant real
4364 * world read requests from the AFP_AfpInfo stream will be
4365 * offset=0 n=60. offset is ignored anyway, see below.
4367 if ((offset < 0) || (offset >= AFP_INFO_SIZE + 1)) {
4372 DBG_ERR("Failed to fetch fsp extension");
4376 /* Yes, macOS always reads from offset 0 */
4378 to_return = MIN(n, AFP_INFO_SIZE);
4380 switch (fio->config->meta) {
4381 case FRUIT_META_STREAM:
4382 nread = fruit_pread_meta_stream(handle, fsp, data,
4386 case FRUIT_META_NETATALK:
4387 nread = fruit_pread_meta_adouble(handle, fsp, data,
4392 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
4396 if (nread == -1 && fio->created) {
4398 char afpinfo_buf[AFP_INFO_SIZE];
4400 ai = afpinfo_new(talloc_tos());
4405 nread = afpinfo_pack(ai, afpinfo_buf);
4407 if (nread != AFP_INFO_SIZE) {
4411 memcpy(data, afpinfo_buf, to_return);
4418 static ssize_t fruit_pread_rsrc_stream(vfs_handle_struct *handle,
4419 files_struct *fsp, void *data,
4420 size_t n, off_t offset)
4422 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4425 static ssize_t fruit_pread_rsrc_xattr(vfs_handle_struct *handle,
4426 files_struct *fsp, void *data,
4427 size_t n, off_t offset)
4429 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4432 static ssize_t fruit_pread_rsrc_adouble(vfs_handle_struct *handle,
4433 files_struct *fsp, void *data,
4434 size_t n, off_t offset)
4436 struct adouble *ad = NULL;
4439 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
4444 nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n,
4445 offset + ad_getentryoff(ad, ADEID_RFORK));
4451 static ssize_t fruit_pread_rsrc(vfs_handle_struct *handle,
4452 files_struct *fsp, void *data,
4453 size_t n, off_t offset)
4455 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4463 switch (fio->config->rsrc) {
4464 case FRUIT_RSRC_STREAM:
4465 nread = fruit_pread_rsrc_stream(handle, fsp, data, n, offset);
4468 case FRUIT_RSRC_ADFILE:
4469 nread = fruit_pread_rsrc_adouble(handle, fsp, data, n, offset);
4472 case FRUIT_RSRC_XATTR:
4473 nread = fruit_pread_rsrc_xattr(handle, fsp, data, n, offset);
4477 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
4484 static ssize_t fruit_pread(vfs_handle_struct *handle,
4485 files_struct *fsp, void *data,
4486 size_t n, off_t offset)
4488 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4491 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4492 fsp_str_dbg(fsp), (intmax_t)offset, n);
4495 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4498 if (fio->type == ADOUBLE_META) {
4499 nread = fruit_pread_meta(handle, fsp, data, n, offset);
4501 nread = fruit_pread_rsrc(handle, fsp, data, n, offset);
4504 DBG_DEBUG("Path [%s] nread [%zd]\n", fsp_str_dbg(fsp), nread);
4508 static bool fruit_must_handle_aio_stream(struct fio *fio)
4514 if (fio->type == ADOUBLE_META) {
4518 if ((fio->type == ADOUBLE_RSRC) &&
4519 (fio->config->rsrc == FRUIT_RSRC_ADFILE))
4527 struct fruit_pread_state {
4529 struct vfs_aio_state vfs_aio_state;
4532 static void fruit_pread_done(struct tevent_req *subreq);
4534 static struct tevent_req *fruit_pread_send(
4535 struct vfs_handle_struct *handle,
4536 TALLOC_CTX *mem_ctx,
4537 struct tevent_context *ev,
4538 struct files_struct *fsp,
4540 size_t n, off_t offset)
4542 struct tevent_req *req = NULL;
4543 struct tevent_req *subreq = NULL;
4544 struct fruit_pread_state *state = NULL;
4545 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4547 req = tevent_req_create(mem_ctx, &state,
4548 struct fruit_pread_state);
4553 if (fruit_must_handle_aio_stream(fio)) {
4554 state->nread = SMB_VFS_PREAD(fsp, data, n, offset);
4555 if (state->nread != n) {
4556 if (state->nread != -1) {
4559 tevent_req_error(req, errno);
4560 return tevent_req_post(req, ev);
4562 tevent_req_done(req);
4563 return tevent_req_post(req, ev);
4566 subreq = SMB_VFS_NEXT_PREAD_SEND(state, ev, handle, fsp,
4568 if (tevent_req_nomem(req, subreq)) {
4569 return tevent_req_post(req, ev);
4571 tevent_req_set_callback(subreq, fruit_pread_done, req);
4575 static void fruit_pread_done(struct tevent_req *subreq)
4577 struct tevent_req *req = tevent_req_callback_data(
4578 subreq, struct tevent_req);
4579 struct fruit_pread_state *state = tevent_req_data(
4580 req, struct fruit_pread_state);
4582 state->nread = SMB_VFS_PREAD_RECV(subreq, &state->vfs_aio_state);
4583 TALLOC_FREE(subreq);
4585 if (tevent_req_error(req, state->vfs_aio_state.error)) {
4588 tevent_req_done(req);
4591 static ssize_t fruit_pread_recv(struct tevent_req *req,
4592 struct vfs_aio_state *vfs_aio_state)
4594 struct fruit_pread_state *state = tevent_req_data(
4595 req, struct fruit_pread_state);
4597 if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
4601 *vfs_aio_state = state->vfs_aio_state;
4602 return state->nread;
4605 static ssize_t fruit_pwrite_meta_stream(vfs_handle_struct *handle,
4606 files_struct *fsp, const void *data,
4607 size_t n, off_t offset)
4609 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4615 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4616 fsp_str_dbg(fsp), (intmax_t)offset, n);
4625 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
4627 DBG_ERR("Close [%s] failed: %s\n",
4628 fsp_str_dbg(fsp), strerror(errno));
4633 fd = SMB_VFS_NEXT_OPEN(handle,
4639 DBG_ERR("On-demand create [%s] in write failed: %s\n",
4640 fsp_str_dbg(fsp), strerror(errno));
4644 fio->fake_fd = false;
4647 ai = afpinfo_unpack(talloc_tos(), data);
4652 if (ai_empty_finderinfo(ai)) {
4654 * Writing an all 0 blob to the metadata stream results in the
4655 * stream being removed on a macOS server. This ensures we
4656 * behave the same and it verified by the "delete AFP_AfpInfo by
4657 * writing all 0" test.
4659 ret = SMB_VFS_NEXT_FTRUNCATE(handle, fsp, 0);
4661 DBG_ERR("SMB_VFS_NEXT_FTRUNCATE on [%s] failed\n",
4666 ok = set_delete_on_close(
4669 handle->conn->session_info->security_token,
4670 handle->conn->session_info->unix_token);
4672 DBG_ERR("set_delete_on_close on [%s] failed\n",
4679 nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4680 if (nwritten != n) {
4687 static ssize_t fruit_pwrite_meta_netatalk(vfs_handle_struct *handle,
4688 files_struct *fsp, const void *data,
4689 size_t n, off_t offset)
4691 struct adouble *ad = NULL;
4697 ai = afpinfo_unpack(talloc_tos(), data);
4702 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
4704 ad = ad_init(talloc_tos(), handle, ADOUBLE_META);
4709 p = ad_get_entry(ad, ADEID_FINDERI);
4711 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
4716 memcpy(p, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
4718 ret = ad_fset(handle, ad, fsp);
4720 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
4727 if (!ai_empty_finderinfo(ai)) {
4732 * Writing an all 0 blob to the metadata stream results in the stream
4733 * being removed on a macOS server. This ensures we behave the same and
4734 * it verified by the "delete AFP_AfpInfo by writing all 0" test.
4737 ok = set_delete_on_close(
4740 handle->conn->session_info->security_token,
4741 handle->conn->session_info->unix_token);
4743 DBG_ERR("set_delete_on_close on [%s] failed\n",
4751 static ssize_t fruit_pwrite_meta(vfs_handle_struct *handle,
4752 files_struct *fsp, const void *data,
4753 size_t n, off_t offset)
4755 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4757 uint8_t buf[AFP_INFO_SIZE];
4763 DBG_ERR("Failed to fetch fsp extension");
4772 if (offset != 0 && n < 60) {
4777 cmp = memcmp(data, "AFP", 3);
4783 if (n <= AFP_OFF_FinderInfo) {
4785 * Nothing to do here really, just return
4793 if (to_copy > AFP_INFO_SIZE) {
4794 to_copy = AFP_INFO_SIZE;
4796 memcpy(buf, data, to_copy);
4799 if (to_write != AFP_INFO_SIZE) {
4800 to_write = AFP_INFO_SIZE;
4803 switch (fio->config->meta) {
4804 case FRUIT_META_STREAM:
4805 nwritten = fruit_pwrite_meta_stream(handle,
4812 case FRUIT_META_NETATALK:
4813 nwritten = fruit_pwrite_meta_netatalk(handle,
4821 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
4825 if (nwritten != to_write) {
4830 * Return the requested amount, verified against macOS SMB server
4835 static ssize_t fruit_pwrite_rsrc_stream(vfs_handle_struct *handle,
4836 files_struct *fsp, const void *data,
4837 size_t n, off_t offset)
4839 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4842 static ssize_t fruit_pwrite_rsrc_xattr(vfs_handle_struct *handle,
4843 files_struct *fsp, const void *data,
4844 size_t n, off_t offset)
4846 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4849 static ssize_t fruit_pwrite_rsrc_adouble(vfs_handle_struct *handle,
4850 files_struct *fsp, const void *data,
4851 size_t n, off_t offset)
4853 struct adouble *ad = NULL;
4857 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
4859 DBG_ERR("ad_get [%s] failed\n", fsp_str_dbg(fsp));
4863 nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n,
4864 offset + ad_getentryoff(ad, ADEID_RFORK));
4865 if (nwritten != n) {
4866 DBG_ERR("Short write on [%s] [%zd/%zd]\n",
4867 fsp_str_dbg(fsp), nwritten, n);
4872 if ((n + offset) > ad_getentrylen(ad, ADEID_RFORK)) {
4873 ad_setentrylen(ad, ADEID_RFORK, n + offset);
4874 ret = ad_fset(handle, ad, fsp);
4876 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
4886 static ssize_t fruit_pwrite_rsrc(vfs_handle_struct *handle,
4887 files_struct *fsp, const void *data,
4888 size_t n, off_t offset)
4890 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4894 DBG_ERR("Failed to fetch fsp extension");
4898 switch (fio->config->rsrc) {
4899 case FRUIT_RSRC_STREAM:
4900 nwritten = fruit_pwrite_rsrc_stream(handle, fsp, data, n, offset);
4903 case FRUIT_RSRC_ADFILE:
4904 nwritten = fruit_pwrite_rsrc_adouble(handle, fsp, data, n, offset);
4907 case FRUIT_RSRC_XATTR:
4908 nwritten = fruit_pwrite_rsrc_xattr(handle, fsp, data, n, offset);
4912 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
4919 static ssize_t fruit_pwrite(vfs_handle_struct *handle,
4920 files_struct *fsp, const void *data,
4921 size_t n, off_t offset)
4923 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4926 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4927 fsp_str_dbg(fsp), (intmax_t)offset, n);
4930 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4933 if (fio->type == ADOUBLE_META) {
4934 nwritten = fruit_pwrite_meta(handle, fsp, data, n, offset);
4936 nwritten = fruit_pwrite_rsrc(handle, fsp, data, n, offset);
4939 DBG_DEBUG("Path [%s] nwritten=%zd\n", fsp_str_dbg(fsp), nwritten);
4943 struct fruit_pwrite_state {
4945 struct vfs_aio_state vfs_aio_state;
4948 static void fruit_pwrite_done(struct tevent_req *subreq);
4950 static struct tevent_req *fruit_pwrite_send(
4951 struct vfs_handle_struct *handle,
4952 TALLOC_CTX *mem_ctx,
4953 struct tevent_context *ev,
4954 struct files_struct *fsp,
4956 size_t n, off_t offset)
4958 struct tevent_req *req = NULL;
4959 struct tevent_req *subreq = NULL;
4960 struct fruit_pwrite_state *state = NULL;
4961 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4963 req = tevent_req_create(mem_ctx, &state,
4964 struct fruit_pwrite_state);
4969 if (fruit_must_handle_aio_stream(fio)) {
4970 state->nwritten = SMB_VFS_PWRITE(fsp, data, n, offset);
4971 if (state->nwritten != n) {
4972 if (state->nwritten != -1) {
4975 tevent_req_error(req, errno);
4976 return tevent_req_post(req, ev);
4978 tevent_req_done(req);
4979 return tevent_req_post(req, ev);
4982 subreq = SMB_VFS_NEXT_PWRITE_SEND(state, ev, handle, fsp,
4984 if (tevent_req_nomem(req, subreq)) {
4985 return tevent_req_post(req, ev);
4987 tevent_req_set_callback(subreq, fruit_pwrite_done, req);
4991 static void fruit_pwrite_done(struct tevent_req *subreq)
4993 struct tevent_req *req = tevent_req_callback_data(
4994 subreq, struct tevent_req);
4995 struct fruit_pwrite_state *state = tevent_req_data(
4996 req, struct fruit_pwrite_state);
4998 state->nwritten = SMB_VFS_PWRITE_RECV(subreq, &state->vfs_aio_state);
4999 TALLOC_FREE(subreq);
5001 if (tevent_req_error(req, state->vfs_aio_state.error)) {
5004 tevent_req_done(req);
5007 static ssize_t fruit_pwrite_recv(struct tevent_req *req,
5008 struct vfs_aio_state *vfs_aio_state)
5010 struct fruit_pwrite_state *state = tevent_req_data(
5011 req, struct fruit_pwrite_state);
5013 if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
5017 *vfs_aio_state = state->vfs_aio_state;
5018 return state->nwritten;
5022 * Helper to stat/lstat the base file of an smb_fname.
5024 static int fruit_stat_base(vfs_handle_struct *handle,
5025 struct smb_filename *smb_fname,
5028 char *tmp_stream_name;
5031 tmp_stream_name = smb_fname->stream_name;
5032 smb_fname->stream_name = NULL;
5034 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
5036 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5038 smb_fname->stream_name = tmp_stream_name;
5040 DBG_DEBUG("fruit_stat_base [%s] dev [%ju] ino [%ju]\n",
5041 smb_fname->base_name,
5042 (uintmax_t)smb_fname->st.st_ex_dev,
5043 (uintmax_t)smb_fname->st.st_ex_ino);
5047 static int fruit_stat_meta_stream(vfs_handle_struct *handle,
5048 struct smb_filename *smb_fname,
5054 ret = fruit_stat_base(handle, smb_fname, false);
5059 ino = fruit_inode(&smb_fname->st, smb_fname->stream_name);
5062 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
5064 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5067 smb_fname->st.st_ex_ino = ino;
5072 static int fruit_stat_meta_netatalk(vfs_handle_struct *handle,
5073 struct smb_filename *smb_fname,
5076 struct adouble *ad = NULL;
5078 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5080 DBG_INFO("fruit_stat_meta %s: %s\n",
5081 smb_fname_str_dbg(smb_fname), strerror(errno));
5087 /* Populate the stat struct with info from the base file. */
5088 if (fruit_stat_base(handle, smb_fname, follow_links) == -1) {
5091 smb_fname->st.st_ex_size = AFP_INFO_SIZE;
5092 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5093 smb_fname->stream_name);
5097 static int fruit_stat_meta(vfs_handle_struct *handle,
5098 struct smb_filename *smb_fname,
5101 struct fruit_config_data *config = NULL;
5104 SMB_VFS_HANDLE_GET_DATA(handle, config,
5105 struct fruit_config_data, return -1);
5107 switch (config->meta) {
5108 case FRUIT_META_STREAM:
5109 ret = fruit_stat_meta_stream(handle, smb_fname, follow_links);
5112 case FRUIT_META_NETATALK:
5113 ret = fruit_stat_meta_netatalk(handle, smb_fname, follow_links);
5117 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
5124 static int fruit_stat_rsrc_netatalk(vfs_handle_struct *handle,
5125 struct smb_filename *smb_fname,
5128 struct adouble *ad = NULL;
5131 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
5137 /* Populate the stat struct with info from the base file. */
5138 ret = fruit_stat_base(handle, smb_fname, follow_links);
5144 smb_fname->st.st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
5145 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5146 smb_fname->stream_name);
5151 static int fruit_stat_rsrc_stream(vfs_handle_struct *handle,
5152 struct smb_filename *smb_fname,
5158 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
5160 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5166 static int fruit_stat_rsrc_xattr(vfs_handle_struct *handle,
5167 struct smb_filename *smb_fname,
5170 #ifdef HAVE_ATTROPEN
5174 /* Populate the stat struct with info from the base file. */
5175 ret = fruit_stat_base(handle, smb_fname, follow_links);
5180 fd = attropen(smb_fname->base_name,
5181 AFPRESOURCE_EA_NETATALK,
5187 ret = sys_fstat(fd, &smb_fname->st, false);
5190 DBG_ERR("fstat [%s:%s] failed\n", smb_fname->base_name,
5191 AFPRESOURCE_EA_NETATALK);
5197 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5198 smb_fname->stream_name);
5208 static int fruit_stat_rsrc(vfs_handle_struct *handle,
5209 struct smb_filename *smb_fname,
5212 struct fruit_config_data *config = NULL;
5215 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
5217 SMB_VFS_HANDLE_GET_DATA(handle, config,
5218 struct fruit_config_data, return -1);
5220 switch (config->rsrc) {
5221 case FRUIT_RSRC_STREAM:
5222 ret = fruit_stat_rsrc_stream(handle, smb_fname, follow_links);
5225 case FRUIT_RSRC_XATTR:
5226 ret = fruit_stat_rsrc_xattr(handle, smb_fname, follow_links);
5229 case FRUIT_RSRC_ADFILE:
5230 ret = fruit_stat_rsrc_netatalk(handle, smb_fname, follow_links);
5234 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
5241 static int fruit_stat(vfs_handle_struct *handle,
5242 struct smb_filename *smb_fname)
5246 DEBUG(10, ("fruit_stat called for %s\n",
5247 smb_fname_str_dbg(smb_fname)));
5249 if (!is_ntfs_stream_smb_fname(smb_fname)
5250 || is_ntfs_default_stream_smb_fname(smb_fname)) {
5251 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
5253 update_btime(handle, smb_fname);
5259 * Note if lp_posix_paths() is true, we can never
5260 * get here as is_ntfs_stream_smb_fname() is
5261 * always false. So we never need worry about
5262 * not following links here.
5265 if (is_afpinfo_stream(smb_fname)) {
5266 rc = fruit_stat_meta(handle, smb_fname, true);
5267 } else if (is_afpresource_stream(smb_fname)) {
5268 rc = fruit_stat_rsrc(handle, smb_fname, true);
5270 return SMB_VFS_NEXT_STAT(handle, smb_fname);
5274 update_btime(handle, smb_fname);
5275 smb_fname->st.st_ex_mode &= ~S_IFMT;
5276 smb_fname->st.st_ex_mode |= S_IFREG;
5277 smb_fname->st.st_ex_blocks =
5278 smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
5283 static int fruit_lstat(vfs_handle_struct *handle,
5284 struct smb_filename *smb_fname)
5288 DEBUG(10, ("fruit_lstat called for %s\n",
5289 smb_fname_str_dbg(smb_fname)));
5291 if (!is_ntfs_stream_smb_fname(smb_fname)
5292 || is_ntfs_default_stream_smb_fname(smb_fname)) {
5293 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5295 update_btime(handle, smb_fname);
5300 if (is_afpinfo_stream(smb_fname)) {
5301 rc = fruit_stat_meta(handle, smb_fname, false);
5302 } else if (is_afpresource_stream(smb_fname)) {
5303 rc = fruit_stat_rsrc(handle, smb_fname, false);
5305 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5309 update_btime(handle, smb_fname);
5310 smb_fname->st.st_ex_mode &= ~S_IFMT;
5311 smb_fname->st.st_ex_mode |= S_IFREG;
5312 smb_fname->st.st_ex_blocks =
5313 smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
5318 static int fruit_fstat_meta_stream(vfs_handle_struct *handle,
5320 SMB_STRUCT_STAT *sbuf)
5322 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5323 struct smb_filename smb_fname;
5332 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5337 *sbuf = fsp->base_fsp->fsp_name->st;
5338 sbuf->st_ex_size = AFP_INFO_SIZE;
5339 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5343 smb_fname = (struct smb_filename) {
5344 .base_name = fsp->fsp_name->base_name,
5347 ret = fruit_stat_base(handle, &smb_fname, false);
5351 *sbuf = smb_fname.st;
5353 ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5355 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5360 sbuf->st_ex_ino = ino;
5364 static int fruit_fstat_meta_netatalk(vfs_handle_struct *handle,
5366 SMB_STRUCT_STAT *sbuf)
5370 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5375 *sbuf = fsp->base_fsp->fsp_name->st;
5376 sbuf->st_ex_size = AFP_INFO_SIZE;
5377 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5382 static int fruit_fstat_meta(vfs_handle_struct *handle,
5384 SMB_STRUCT_STAT *sbuf,
5389 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
5391 switch (fio->config->meta) {
5392 case FRUIT_META_STREAM:
5393 ret = fruit_fstat_meta_stream(handle, fsp, sbuf);
5396 case FRUIT_META_NETATALK:
5397 ret = fruit_fstat_meta_netatalk(handle, fsp, sbuf);
5401 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
5405 DBG_DEBUG("Path [%s] ret [%d]\n", fsp_str_dbg(fsp), ret);
5409 static int fruit_fstat_rsrc_xattr(vfs_handle_struct *handle,
5411 SMB_STRUCT_STAT *sbuf)
5413 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5416 static int fruit_fstat_rsrc_stream(vfs_handle_struct *handle,
5418 SMB_STRUCT_STAT *sbuf)
5420 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5423 static int fruit_fstat_rsrc_adouble(vfs_handle_struct *handle,
5425 SMB_STRUCT_STAT *sbuf)
5427 struct adouble *ad = NULL;
5430 /* Populate the stat struct with info from the base file. */
5431 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5436 ad = ad_get(talloc_tos(), handle,
5437 fsp->base_fsp->fsp_name,
5440 DBG_ERR("ad_get [%s] failed [%s]\n",
5441 fsp_str_dbg(fsp), strerror(errno));
5445 *sbuf = fsp->base_fsp->fsp_name->st;
5446 sbuf->st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
5447 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5453 static int fruit_fstat_rsrc(vfs_handle_struct *handle, files_struct *fsp,
5454 SMB_STRUCT_STAT *sbuf, struct fio *fio)
5458 switch (fio->config->rsrc) {
5459 case FRUIT_RSRC_STREAM:
5460 ret = fruit_fstat_rsrc_stream(handle, fsp, sbuf);
5463 case FRUIT_RSRC_ADFILE:
5464 ret = fruit_fstat_rsrc_adouble(handle, fsp, sbuf);
5467 case FRUIT_RSRC_XATTR:
5468 ret = fruit_fstat_rsrc_xattr(handle, fsp, sbuf);
5472 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
5479 static int fruit_fstat(vfs_handle_struct *handle, files_struct *fsp,
5480 SMB_STRUCT_STAT *sbuf)
5482 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5486 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5489 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
5491 if (fio->type == ADOUBLE_META) {
5492 rc = fruit_fstat_meta(handle, fsp, sbuf, fio);
5494 rc = fruit_fstat_rsrc(handle, fsp, sbuf, fio);
5498 sbuf->st_ex_mode &= ~S_IFMT;
5499 sbuf->st_ex_mode |= S_IFREG;
5500 sbuf->st_ex_blocks = sbuf->st_ex_size / STAT_ST_BLOCKSIZE + 1;
5503 DBG_DEBUG("Path [%s] rc [%d] size [%"PRIdMAX"]\n",
5504 fsp_str_dbg(fsp), rc, (intmax_t)sbuf->st_ex_size);
5508 static NTSTATUS delete_invalid_meta_stream(
5509 vfs_handle_struct *handle,
5510 const struct smb_filename *smb_fname,
5511 TALLOC_CTX *mem_ctx,
5512 unsigned int *pnum_streams,
5513 struct stream_struct **pstreams,
5516 struct smb_filename *sname = NULL;
5520 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams, AFPINFO_STREAM);
5522 return NT_STATUS_INTERNAL_ERROR;
5526 return NT_STATUS_OK;
5529 sname = synthetic_smb_fname(talloc_tos(),
5530 smb_fname->base_name,
5531 AFPINFO_STREAM_NAME,
5533 if (sname == NULL) {
5534 return NT_STATUS_NO_MEMORY;
5537 ret = SMB_VFS_NEXT_UNLINK(handle, sname);
5540 DBG_ERR("Removing [%s] failed\n", smb_fname_str_dbg(sname));
5541 return map_nt_error_from_unix(errno);
5544 return NT_STATUS_OK;
5547 static NTSTATUS fruit_streaminfo_meta_stream(
5548 vfs_handle_struct *handle,
5549 struct files_struct *fsp,
5550 const struct smb_filename *smb_fname,
5551 TALLOC_CTX *mem_ctx,
5552 unsigned int *pnum_streams,
5553 struct stream_struct **pstreams)
5555 struct stream_struct *stream = *pstreams;
5556 unsigned int num_streams = *pnum_streams;
5559 for (i = 0; i < num_streams; i++) {
5560 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
5565 if (i == num_streams) {
5566 return NT_STATUS_OK;
5569 if (stream[i].size != AFP_INFO_SIZE) {
5570 DBG_ERR("Removing invalid AFPINFO_STREAM size [%jd] from [%s]\n",
5571 (intmax_t)stream[i].size, smb_fname_str_dbg(smb_fname));
5573 return delete_invalid_meta_stream(handle,
5582 return NT_STATUS_OK;
5585 static NTSTATUS fruit_streaminfo_meta_netatalk(
5586 vfs_handle_struct *handle,
5587 struct files_struct *fsp,
5588 const struct smb_filename *smb_fname,
5589 TALLOC_CTX *mem_ctx,
5590 unsigned int *pnum_streams,
5591 struct stream_struct **pstreams)
5593 struct stream_struct *stream = *pstreams;
5594 unsigned int num_streams = *pnum_streams;
5595 struct adouble *ad = NULL;
5600 /* Remove the Netatalk xattr from the list */
5601 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5602 ":" NETATALK_META_XATTR ":$DATA");
5604 return NT_STATUS_NO_MEMORY;
5608 * Check if there's a AFPINFO_STREAM from the VFS streams
5609 * backend and if yes, remove it from the list
5611 for (i = 0; i < num_streams; i++) {
5612 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
5617 if (i < num_streams) {
5618 DBG_WARNING("Unexpected AFPINFO_STREAM on [%s]\n",
5619 smb_fname_str_dbg(smb_fname));
5621 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5624 return NT_STATUS_INTERNAL_ERROR;
5628 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5630 return NT_STATUS_OK;
5633 is_fi_empty = ad_empty_finderinfo(ad);
5637 return NT_STATUS_OK;
5640 ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
5641 AFPINFO_STREAM_NAME, AFP_INFO_SIZE,
5642 smb_roundup(handle->conn, AFP_INFO_SIZE));
5644 return NT_STATUS_NO_MEMORY;
5647 return NT_STATUS_OK;
5650 static NTSTATUS fruit_streaminfo_meta(vfs_handle_struct *handle,
5651 struct files_struct *fsp,
5652 const struct smb_filename *smb_fname,
5653 TALLOC_CTX *mem_ctx,
5654 unsigned int *pnum_streams,
5655 struct stream_struct **pstreams)
5657 struct fruit_config_data *config = NULL;
5660 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5661 return NT_STATUS_INTERNAL_ERROR);
5663 switch (config->meta) {
5664 case FRUIT_META_NETATALK:
5665 status = fruit_streaminfo_meta_netatalk(handle, fsp, smb_fname,
5666 mem_ctx, pnum_streams,
5670 case FRUIT_META_STREAM:
5671 status = fruit_streaminfo_meta_stream(handle, fsp, smb_fname,
5672 mem_ctx, pnum_streams,
5677 return NT_STATUS_INTERNAL_ERROR;
5683 static NTSTATUS fruit_streaminfo_rsrc_stream(
5684 vfs_handle_struct *handle,
5685 struct files_struct *fsp,
5686 const struct smb_filename *smb_fname,
5687 TALLOC_CTX *mem_ctx,
5688 unsigned int *pnum_streams,
5689 struct stream_struct **pstreams)
5693 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
5695 DBG_ERR("Filtering resource stream failed\n");
5696 return NT_STATUS_INTERNAL_ERROR;
5698 return NT_STATUS_OK;
5701 static NTSTATUS fruit_streaminfo_rsrc_xattr(
5702 vfs_handle_struct *handle,
5703 struct files_struct *fsp,
5704 const struct smb_filename *smb_fname,
5705 TALLOC_CTX *mem_ctx,
5706 unsigned int *pnum_streams,
5707 struct stream_struct **pstreams)
5711 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
5713 DBG_ERR("Filtering resource stream failed\n");
5714 return NT_STATUS_INTERNAL_ERROR;
5716 return NT_STATUS_OK;
5719 static NTSTATUS fruit_streaminfo_rsrc_adouble(
5720 vfs_handle_struct *handle,
5721 struct files_struct *fsp,
5722 const struct smb_filename *smb_fname,
5723 TALLOC_CTX *mem_ctx,
5724 unsigned int *pnum_streams,
5725 struct stream_struct **pstreams)
5727 struct stream_struct *stream = *pstreams;
5728 unsigned int num_streams = *pnum_streams;
5729 struct adouble *ad = NULL;
5735 * Check if there's a AFPRESOURCE_STREAM from the VFS streams backend
5736 * and if yes, remove it from the list
5738 for (i = 0; i < num_streams; i++) {
5739 if (strequal_m(stream[i].name, AFPRESOURCE_STREAM)) {
5744 if (i < num_streams) {
5745 DBG_WARNING("Unexpected AFPRESOURCE_STREAM on [%s]\n",
5746 smb_fname_str_dbg(smb_fname));
5748 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5749 AFPRESOURCE_STREAM);
5751 return NT_STATUS_INTERNAL_ERROR;
5755 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
5757 return NT_STATUS_OK;
5760 rlen = ad_getentrylen(ad, ADEID_RFORK);
5764 return NT_STATUS_OK;
5767 ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
5768 AFPRESOURCE_STREAM_NAME, rlen,
5769 smb_roundup(handle->conn, rlen));
5771 return NT_STATUS_NO_MEMORY;
5774 return NT_STATUS_OK;
5777 static NTSTATUS fruit_streaminfo_rsrc(vfs_handle_struct *handle,
5778 struct files_struct *fsp,
5779 const struct smb_filename *smb_fname,
5780 TALLOC_CTX *mem_ctx,
5781 unsigned int *pnum_streams,
5782 struct stream_struct **pstreams)
5784 struct fruit_config_data *config = NULL;
5787 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5788 return NT_STATUS_INTERNAL_ERROR);
5790 switch (config->rsrc) {
5791 case FRUIT_RSRC_STREAM:
5792 status = fruit_streaminfo_rsrc_stream(handle, fsp, smb_fname,
5793 mem_ctx, pnum_streams,
5797 case FRUIT_RSRC_XATTR:
5798 status = fruit_streaminfo_rsrc_xattr(handle, fsp, smb_fname,
5799 mem_ctx, pnum_streams,
5803 case FRUIT_RSRC_ADFILE:
5804 status = fruit_streaminfo_rsrc_adouble(handle, fsp, smb_fname,
5805 mem_ctx, pnum_streams,
5810 return NT_STATUS_INTERNAL_ERROR;
5816 static void fruit_filter_empty_streams(unsigned int *pnum_streams,
5817 struct stream_struct **pstreams)
5819 unsigned num_streams = *pnum_streams;
5820 struct stream_struct *streams = *pstreams;
5823 if (!global_fruit_config.nego_aapl) {
5827 while (i < num_streams) {
5828 struct smb_filename smb_fname = (struct smb_filename) {
5829 .stream_name = streams[i].name,
5832 if (is_ntfs_default_stream_smb_fname(&smb_fname)
5833 || streams[i].size > 0)
5839 streams[i] = streams[num_streams - 1];
5843 *pnum_streams = num_streams;
5846 static NTSTATUS fruit_streaminfo(vfs_handle_struct *handle,
5847 struct files_struct *fsp,
5848 const struct smb_filename *smb_fname,
5849 TALLOC_CTX *mem_ctx,
5850 unsigned int *pnum_streams,
5851 struct stream_struct **pstreams)
5853 struct fruit_config_data *config = NULL;
5856 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5857 return NT_STATUS_UNSUCCESSFUL);
5859 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
5861 status = SMB_VFS_NEXT_STREAMINFO(handle, fsp, smb_fname, mem_ctx,
5862 pnum_streams, pstreams);
5863 if (!NT_STATUS_IS_OK(status)) {
5867 fruit_filter_empty_streams(pnum_streams, pstreams);
5869 status = fruit_streaminfo_meta(handle, fsp, smb_fname,
5870 mem_ctx, pnum_streams, pstreams);
5871 if (!NT_STATUS_IS_OK(status)) {
5875 status = fruit_streaminfo_rsrc(handle, fsp, smb_fname,
5876 mem_ctx, pnum_streams, pstreams);
5877 if (!NT_STATUS_IS_OK(status)) {
5881 return NT_STATUS_OK;
5884 static int fruit_ntimes(vfs_handle_struct *handle,
5885 const struct smb_filename *smb_fname,
5886 struct smb_file_time *ft)
5889 struct adouble *ad = NULL;
5890 struct fruit_config_data *config = NULL;
5892 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5895 if ((config->meta != FRUIT_META_NETATALK) ||
5896 null_timespec(ft->create_time))
5898 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
5901 DEBUG(10,("set btime for %s to %s\n", smb_fname_str_dbg(smb_fname),
5902 time_to_asc(convert_timespec_to_time_t(ft->create_time))));
5904 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5909 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX,
5910 convert_time_t_to_uint32_t(ft->create_time.tv_sec));
5912 rc = ad_set(handle, ad, smb_fname);
5918 DEBUG(1, ("fruit_ntimes: %s\n", smb_fname_str_dbg(smb_fname)));
5921 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
5924 static int fruit_fallocate(struct vfs_handle_struct *handle,
5925 struct files_struct *fsp,
5930 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5933 return SMB_VFS_NEXT_FALLOCATE(handle, fsp, mode, offset, len);
5936 /* Let the pwrite code path handle it. */
5941 static int fruit_ftruncate_rsrc_xattr(struct vfs_handle_struct *handle,
5942 struct files_struct *fsp,
5945 #ifdef HAVE_ATTROPEN
5946 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
5951 static int fruit_ftruncate_rsrc_adouble(struct vfs_handle_struct *handle,
5952 struct files_struct *fsp,
5956 struct adouble *ad = NULL;
5959 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
5961 DBG_DEBUG("ad_get [%s] failed [%s]\n",
5962 fsp_str_dbg(fsp), strerror(errno));
5966 ad_off = ad_getentryoff(ad, ADEID_RFORK);
5968 rc = ftruncate(fsp->fh->fd, offset + ad_off);
5974 ad_setentrylen(ad, ADEID_RFORK, offset);
5976 rc = ad_fset(handle, ad, fsp);
5978 DBG_ERR("ad_fset [%s] failed [%s]\n",
5979 fsp_str_dbg(fsp), strerror(errno));
5988 static int fruit_ftruncate_rsrc_stream(struct vfs_handle_struct *handle,
5989 struct files_struct *fsp,
5992 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
5995 static int fruit_ftruncate_rsrc(struct vfs_handle_struct *handle,
5996 struct files_struct *fsp,
5999 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
6003 DBG_ERR("Failed to fetch fsp extension");
6007 switch (fio->config->rsrc) {
6008 case FRUIT_RSRC_XATTR:
6009 ret = fruit_ftruncate_rsrc_xattr(handle, fsp, offset);
6012 case FRUIT_RSRC_ADFILE:
6013 ret = fruit_ftruncate_rsrc_adouble(handle, fsp, offset);
6016 case FRUIT_RSRC_STREAM:
6017 ret = fruit_ftruncate_rsrc_stream(handle, fsp, offset);
6021 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
6029 static int fruit_ftruncate_meta(struct vfs_handle_struct *handle,
6030 struct files_struct *fsp,
6034 DBG_WARNING("ftruncate %s to %jd",
6035 fsp_str_dbg(fsp), (intmax_t)offset);
6036 /* OS X returns NT_STATUS_ALLOTTED_SPACE_EXCEEDED */
6041 /* OS X returns success but does nothing */
6042 DBG_INFO("ignoring ftruncate %s to %jd\n",
6043 fsp_str_dbg(fsp), (intmax_t)offset);
6047 static int fruit_ftruncate(struct vfs_handle_struct *handle,
6048 struct files_struct *fsp,
6051 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
6054 DBG_DEBUG("Path [%s] offset [%"PRIdMAX"]\n", fsp_str_dbg(fsp),
6058 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
6061 if (fio->type == ADOUBLE_META) {
6062 ret = fruit_ftruncate_meta(handle, fsp, offset);
6064 ret = fruit_ftruncate_rsrc(handle, fsp, offset);
6067 DBG_DEBUG("Path [%s] result [%d]\n", fsp_str_dbg(fsp), ret);
6071 static NTSTATUS fruit_create_file(vfs_handle_struct *handle,
6072 struct smb_request *req,
6073 uint16_t root_dir_fid,
6074 struct smb_filename *smb_fname,
6075 uint32_t access_mask,
6076 uint32_t share_access,
6077 uint32_t create_disposition,
6078 uint32_t create_options,
6079 uint32_t file_attributes,
6080 uint32_t oplock_request,
6081 struct smb2_lease *lease,
6082 uint64_t allocation_size,
6083 uint32_t private_flags,
6084 struct security_descriptor *sd,
6085 struct ea_list *ea_list,
6086 files_struct **result,
6088 const struct smb2_create_blobs *in_context_blobs,
6089 struct smb2_create_blobs *out_context_blobs)
6092 struct fruit_config_data *config = NULL;
6093 files_struct *fsp = NULL;
6094 struct fio *fio = NULL;
6095 bool internal_open = (oplock_request & INTERNAL_OPEN_ONLY);
6098 status = check_aapl(handle, req, in_context_blobs, out_context_blobs);
6099 if (!NT_STATUS_IS_OK(status)) {
6103 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
6104 return NT_STATUS_UNSUCCESSFUL);
6106 if (is_apple_stream(smb_fname) && !internal_open) {
6107 ret = ad_convert(handle, smb_fname);
6109 DBG_ERR("ad_convert() failed\n");
6110 return NT_STATUS_UNSUCCESSFUL;
6114 status = SMB_VFS_NEXT_CREATE_FILE(
6115 handle, req, root_dir_fid, smb_fname,
6116 access_mask, share_access,
6117 create_disposition, create_options,
6118 file_attributes, oplock_request,
6120 allocation_size, private_flags,
6121 sd, ea_list, result,
6122 pinfo, in_context_blobs, out_context_blobs);
6123 if (!NT_STATUS_IS_OK(status)) {
6129 if (global_fruit_config.nego_aapl) {
6130 if (config->posix_rename && fsp->is_directory) {
6132 * Enable POSIX directory rename behaviour
6134 fsp->posix_flags |= FSP_POSIX_FLAGS_RENAME;
6139 * If this is a plain open for existing files, opening an 0
6140 * byte size resource fork MUST fail with
6141 * NT_STATUS_OBJECT_NAME_NOT_FOUND.
6143 * Cf the vfs_fruit torture tests in test_rfork_create().
6145 if (global_fruit_config.nego_aapl &&
6146 create_disposition == FILE_OPEN &&
6147 smb_fname->st.st_ex_size == 0 &&
6148 is_ntfs_stream_smb_fname(smb_fname) &&
6149 !(is_ntfs_default_stream_smb_fname(smb_fname)))
6151 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
6155 fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
6156 if (fio != NULL && pinfo != NULL && *pinfo == FILE_WAS_CREATED) {
6157 fio->created = true;
6160 if (is_ntfs_stream_smb_fname(smb_fname)
6161 || fsp->is_directory) {
6165 if (config->locking == FRUIT_LOCKING_NETATALK) {
6166 status = fruit_check_access(
6170 if (!NT_STATUS_IS_OK(status)) {
6178 DEBUG(10, ("fruit_create_file: %s\n", nt_errstr(status)));
6181 close_file(req, fsp, ERROR_CLOSE);
6182 *result = fsp = NULL;
6188 static NTSTATUS fruit_readdir_attr(struct vfs_handle_struct *handle,
6189 const struct smb_filename *fname,
6190 TALLOC_CTX *mem_ctx,
6191 struct readdir_attr_data **pattr_data)
6193 struct fruit_config_data *config = NULL;
6194 struct readdir_attr_data *attr_data;
6198 SMB_VFS_HANDLE_GET_DATA(handle, config,
6199 struct fruit_config_data,
6200 return NT_STATUS_UNSUCCESSFUL);
6202 if (!global_fruit_config.nego_aapl) {
6203 return SMB_VFS_NEXT_READDIR_ATTR(handle, fname, mem_ctx, pattr_data);
6206 DEBUG(10, ("fruit_readdir_attr %s\n", fname->base_name));
6208 ret = ad_convert(handle, fname);
6210 DBG_ERR("ad_convert() failed\n");
6211 return NT_STATUS_UNSUCCESSFUL;
6214 *pattr_data = talloc_zero(mem_ctx, struct readdir_attr_data);
6215 if (*pattr_data == NULL) {
6216 return NT_STATUS_UNSUCCESSFUL;
6218 attr_data = *pattr_data;
6219 attr_data->type = RDATTR_AAPL;
6222 * Mac metadata: compressed FinderInfo, resource fork length
6225 status = readdir_attr_macmeta(handle, fname, attr_data);
6226 if (!NT_STATUS_IS_OK(status)) {
6228 * Error handling is tricky: if we return failure from
6229 * this function, the corresponding directory entry
6230 * will to be passed to the client, so we really just
6231 * want to error out on fatal errors.
6233 if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
6241 if (config->unix_info_enabled) {
6242 attr_data->attr_data.aapl.unix_mode = fname->st.st_ex_mode;
6248 if (!config->readdir_attr_max_access) {
6249 attr_data->attr_data.aapl.max_access = FILE_GENERIC_ALL;
6251 status = smbd_calculate_access_mask(
6255 SEC_FLAG_MAXIMUM_ALLOWED,
6256 &attr_data->attr_data.aapl.max_access);
6257 if (!NT_STATUS_IS_OK(status)) {
6262 return NT_STATUS_OK;
6265 DEBUG(1, ("fruit_readdir_attr %s, error: %s\n",
6266 fname->base_name, nt_errstr(status)));
6267 TALLOC_FREE(*pattr_data);
6271 static NTSTATUS fruit_fget_nt_acl(vfs_handle_struct *handle,
6273 uint32_t security_info,
6274 TALLOC_CTX *mem_ctx,
6275 struct security_descriptor **ppdesc)
6278 struct security_ace ace;
6280 struct fruit_config_data *config;
6282 SMB_VFS_HANDLE_GET_DATA(handle, config,
6283 struct fruit_config_data,
6284 return NT_STATUS_UNSUCCESSFUL);
6286 status = SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
6288 if (!NT_STATUS_IS_OK(status)) {
6293 * Add MS NFS style ACEs with uid, gid and mode
6295 if (!global_fruit_config.nego_aapl) {
6296 return NT_STATUS_OK;
6298 if (!config->unix_info_enabled) {
6299 return NT_STATUS_OK;
6302 /* First remove any existing ACE's with NFS style mode/uid/gid SIDs. */
6303 status = remove_virtual_nfs_aces(*ppdesc);
6304 if (!NT_STATUS_IS_OK(status)) {
6305 DBG_WARNING("failed to remove MS NFS style ACEs\n");
6309 /* MS NFS style mode */
6310 sid_compose(&sid, &global_sid_Unix_NFS_Mode, fsp->fsp_name->st.st_ex_mode);
6311 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6312 status = security_descriptor_dacl_add(*ppdesc, &ace);
6313 if (!NT_STATUS_IS_OK(status)) {
6314 DEBUG(1,("failed to add MS NFS style ACE\n"));
6318 /* MS NFS style uid */
6319 sid_compose(&sid, &global_sid_Unix_NFS_Users, fsp->fsp_name->st.st_ex_uid);
6320 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6321 status = security_descriptor_dacl_add(*ppdesc, &ace);
6322 if (!NT_STATUS_IS_OK(status)) {
6323 DEBUG(1,("failed to add MS NFS style ACE\n"));
6327 /* MS NFS style gid */
6328 sid_compose(&sid, &global_sid_Unix_NFS_Groups, fsp->fsp_name->st.st_ex_gid);
6329 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6330 status = security_descriptor_dacl_add(*ppdesc, &ace);
6331 if (!NT_STATUS_IS_OK(status)) {
6332 DEBUG(1,("failed to add MS NFS style ACE\n"));
6336 return NT_STATUS_OK;
6339 static NTSTATUS fruit_fset_nt_acl(vfs_handle_struct *handle,
6341 uint32_t security_info_sent,
6342 const struct security_descriptor *orig_psd)
6346 mode_t ms_nfs_mode = 0;
6348 struct security_descriptor *psd = NULL;
6349 uint32_t orig_num_aces = 0;
6351 if (orig_psd->dacl != NULL) {
6352 orig_num_aces = orig_psd->dacl->num_aces;
6355 psd = security_descriptor_copy(talloc_tos(), orig_psd);
6357 return NT_STATUS_NO_MEMORY;
6360 DBG_DEBUG("fruit_fset_nt_acl: %s\n", fsp_str_dbg(fsp));
6362 status = check_ms_nfs(handle, fsp, psd, &ms_nfs_mode, &do_chmod);
6363 if (!NT_STATUS_IS_OK(status)) {
6364 DEBUG(1, ("fruit_fset_nt_acl: check_ms_nfs failed%s\n", fsp_str_dbg(fsp)));
6370 * If only ms_nfs ACE entries were sent, ensure we set the DACL
6371 * sent/present flags correctly now we've removed them.
6374 if (orig_num_aces != 0) {
6376 * Are there any ACE's left ?
6378 if (psd->dacl->num_aces == 0) {
6379 /* No - clear the DACL sent/present flags. */
6380 security_info_sent &= ~SECINFO_DACL;
6381 psd->type &= ~SEC_DESC_DACL_PRESENT;
6385 status = SMB_VFS_NEXT_FSET_NT_ACL(handle, fsp, security_info_sent, psd);
6386 if (!NT_STATUS_IS_OK(status)) {
6387 DEBUG(1, ("fruit_fset_nt_acl: SMB_VFS_NEXT_FSET_NT_ACL failed%s\n", fsp_str_dbg(fsp)));
6393 if (fsp->fh->fd != -1) {
6394 result = SMB_VFS_FCHMOD(fsp, ms_nfs_mode);
6396 result = SMB_VFS_CHMOD(fsp->conn,
6402 DEBUG(1, ("chmod: %s, result: %d, %04o error %s\n", fsp_str_dbg(fsp),
6403 result, (unsigned)ms_nfs_mode,
6405 status = map_nt_error_from_unix(errno);
6412 return NT_STATUS_OK;
6415 static struct vfs_offload_ctx *fruit_offload_ctx;
6417 struct fruit_offload_read_state {
6418 struct vfs_handle_struct *handle;
6419 struct tevent_context *ev;
6425 static void fruit_offload_read_done(struct tevent_req *subreq);
6427 static struct tevent_req *fruit_offload_read_send(
6428 TALLOC_CTX *mem_ctx,
6429 struct tevent_context *ev,
6430 struct vfs_handle_struct *handle,
6437 struct tevent_req *req = NULL;
6438 struct tevent_req *subreq = NULL;
6439 struct fruit_offload_read_state *state = NULL;
6441 req = tevent_req_create(mem_ctx, &state,
6442 struct fruit_offload_read_state);
6446 *state = (struct fruit_offload_read_state) {
6453 subreq = SMB_VFS_NEXT_OFFLOAD_READ_SEND(mem_ctx, ev, handle, fsp,
6454 fsctl, ttl, offset, to_copy);
6455 if (tevent_req_nomem(subreq, req)) {
6456 return tevent_req_post(req, ev);
6458 tevent_req_set_callback(subreq, fruit_offload_read_done, req);
6462 static void fruit_offload_read_done(struct tevent_req *subreq)
6464 struct tevent_req *req = tevent_req_callback_data(
6465 subreq, struct tevent_req);
6466 struct fruit_offload_read_state *state = tevent_req_data(
6467 req, struct fruit_offload_read_state);
6470 status = SMB_VFS_NEXT_OFFLOAD_READ_RECV(subreq,
6474 TALLOC_FREE(subreq);
6475 if (tevent_req_nterror(req, status)) {
6479 if (state->fsctl != FSCTL_SRV_REQUEST_RESUME_KEY) {
6480 tevent_req_done(req);
6484 status = vfs_offload_token_ctx_init(state->fsp->conn->sconn->client,
6485 &fruit_offload_ctx);
6486 if (tevent_req_nterror(req, status)) {
6490 status = vfs_offload_token_db_store_fsp(fruit_offload_ctx,
6493 if (tevent_req_nterror(req, status)) {
6497 tevent_req_done(req);
6501 static NTSTATUS fruit_offload_read_recv(struct tevent_req *req,
6502 struct vfs_handle_struct *handle,
6503 TALLOC_CTX *mem_ctx,
6506 struct fruit_offload_read_state *state = tevent_req_data(
6507 req, struct fruit_offload_read_state);
6510 if (tevent_req_is_nterror(req, &status)) {
6511 tevent_req_received(req);
6515 token->length = state->token.length;
6516 token->data = talloc_move(mem_ctx, &state->token.data);
6518 tevent_req_received(req);
6519 return NT_STATUS_OK;
6522 struct fruit_offload_write_state {
6523 struct vfs_handle_struct *handle;
6525 struct files_struct *src_fsp;
6526 struct files_struct *dst_fsp;
6530 static void fruit_offload_write_done(struct tevent_req *subreq);
6531 static struct tevent_req *fruit_offload_write_send(struct vfs_handle_struct *handle,
6532 TALLOC_CTX *mem_ctx,
6533 struct tevent_context *ev,
6536 off_t transfer_offset,
6537 struct files_struct *dest_fsp,
6541 struct tevent_req *req, *subreq;
6542 struct fruit_offload_write_state *state;
6544 struct fruit_config_data *config;
6545 off_t src_off = transfer_offset;
6546 files_struct *src_fsp = NULL;
6547 off_t to_copy = num;
6548 bool copyfile_enabled = false;
6550 DEBUG(10,("soff: %ju, doff: %ju, len: %ju\n",
6551 (uintmax_t)src_off, (uintmax_t)dest_off, (uintmax_t)num));
6553 SMB_VFS_HANDLE_GET_DATA(handle, config,
6554 struct fruit_config_data,
6557 req = tevent_req_create(mem_ctx, &state,
6558 struct fruit_offload_write_state);
6562 state->handle = handle;
6563 state->dst_fsp = dest_fsp;
6566 case FSCTL_SRV_COPYCHUNK:
6567 case FSCTL_SRV_COPYCHUNK_WRITE:
6568 copyfile_enabled = config->copyfile_enabled;
6575 * Check if this a OS X copyfile style copychunk request with
6576 * a requested chunk count of 0 that was translated to a
6577 * offload_write_send VFS call overloading the parameters src_off
6578 * = dest_off = num = 0.
6580 if (copyfile_enabled && num == 0 && src_off == 0 && dest_off == 0) {
6581 status = vfs_offload_token_db_fetch_fsp(
6582 fruit_offload_ctx, token, &src_fsp);
6583 if (tevent_req_nterror(req, status)) {
6584 return tevent_req_post(req, ev);
6586 state->src_fsp = src_fsp;
6588 status = vfs_stat_fsp(src_fsp);
6589 if (tevent_req_nterror(req, status)) {
6590 return tevent_req_post(req, ev);
6593 to_copy = src_fsp->fsp_name->st.st_ex_size;
6594 state->is_copyfile = true;
6597 subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle,
6606 if (tevent_req_nomem(subreq, req)) {
6607 return tevent_req_post(req, ev);
6610 tevent_req_set_callback(subreq, fruit_offload_write_done, req);
6614 static void fruit_offload_write_done(struct tevent_req *subreq)
6616 struct tevent_req *req = tevent_req_callback_data(
6617 subreq, struct tevent_req);
6618 struct fruit_offload_write_state *state = tevent_req_data(
6619 req, struct fruit_offload_write_state);
6621 unsigned int num_streams = 0;
6622 struct stream_struct *streams = NULL;
6624 struct smb_filename *src_fname_tmp = NULL;
6625 struct smb_filename *dst_fname_tmp = NULL;
6627 status = SMB_VFS_NEXT_OFFLOAD_WRITE_RECV(state->handle,
6630 TALLOC_FREE(subreq);
6631 if (tevent_req_nterror(req, status)) {
6635 if (!state->is_copyfile) {
6636 tevent_req_done(req);
6641 * Now copy all remaining streams. We know the share supports
6642 * streams, because we're in vfs_fruit. We don't do this async
6643 * because streams are few and small.
6645 status = vfs_streaminfo(state->handle->conn, state->src_fsp,
6646 state->src_fsp->fsp_name,
6647 req, &num_streams, &streams);
6648 if (tevent_req_nterror(req, status)) {
6652 if (num_streams == 1) {
6653 /* There is always one stream, ::$DATA. */
6654 tevent_req_done(req);
6658 for (i = 0; i < num_streams; i++) {
6659 DEBUG(10, ("%s: stream: '%s'/%zu\n",
6660 __func__, streams[i].name, (size_t)streams[i].size));
6662 src_fname_tmp = synthetic_smb_fname(
6664 state->src_fsp->fsp_name->base_name,
6667 state->src_fsp->fsp_name->flags);
6668 if (tevent_req_nomem(src_fname_tmp, req)) {
6672 if (is_ntfs_default_stream_smb_fname(src_fname_tmp)) {
6673 TALLOC_FREE(src_fname_tmp);
6677 dst_fname_tmp = synthetic_smb_fname(
6679 state->dst_fsp->fsp_name->base_name,
6682 state->dst_fsp->fsp_name->flags);
6683 if (tevent_req_nomem(dst_fname_tmp, req)) {
6684 TALLOC_FREE(src_fname_tmp);
6688 status = copy_file(req,
6689 state->handle->conn,
6692 OPENX_FILE_CREATE_IF_NOT_EXIST,
6694 if (!NT_STATUS_IS_OK(status)) {
6695 DEBUG(1, ("%s: copy %s to %s failed: %s\n", __func__,
6696 smb_fname_str_dbg(src_fname_tmp),
6697 smb_fname_str_dbg(dst_fname_tmp),
6698 nt_errstr(status)));
6699 TALLOC_FREE(src_fname_tmp);
6700 TALLOC_FREE(dst_fname_tmp);
6701 tevent_req_nterror(req, status);
6705 TALLOC_FREE(src_fname_tmp);
6706 TALLOC_FREE(dst_fname_tmp);
6709 TALLOC_FREE(streams);
6710 TALLOC_FREE(src_fname_tmp);
6711 TALLOC_FREE(dst_fname_tmp);
6712 tevent_req_done(req);
6715 static NTSTATUS fruit_offload_write_recv(struct vfs_handle_struct *handle,
6716 struct tevent_req *req,
6719 struct fruit_offload_write_state *state = tevent_req_data(
6720 req, struct fruit_offload_write_state);
6723 if (tevent_req_is_nterror(req, &status)) {
6724 DEBUG(1, ("server side copy chunk failed: %s\n",
6725 nt_errstr(status)));
6727 tevent_req_received(req);
6731 *copied = state->copied;
6732 tevent_req_received(req);
6734 return NT_STATUS_OK;
6737 static char *fruit_get_bandsize_line(char **lines, int numlines)
6740 static bool re_initialized = false;
6744 if (!re_initialized) {
6745 ret = regcomp(&re, "^[[:blank:]]*<key>band-size</key>$", 0);
6749 re_initialized = true;
6752 for (i = 0; i < numlines; i++) {
6753 regmatch_t matches[1];
6755 ret = regexec(&re, lines[i], 1, matches, 0);
6758 * Check if the match was on the last line, sa we want
6759 * the subsequent line.
6761 if (i + 1 == numlines) {
6764 return lines[i + 1];
6766 if (ret != REG_NOMATCH) {
6774 static bool fruit_get_bandsize_from_line(char *line, size_t *_band_size)
6777 static bool re_initialized = false;
6778 regmatch_t matches[2];
6783 if (!re_initialized) {
6786 "<integer>\\([[:digit:]]*\\)</integer>$",
6791 re_initialized = true;
6794 ret = regexec(&re, line, 2, matches, 0);
6796 DBG_ERR("regex failed [%s]\n", line);
6800 line[matches[1].rm_eo] = '\0';
6802 ok = conv_str_u64(&line[matches[1].rm_so], &band_size);
6806 *_band_size = (size_t)band_size;
6811 * This reads and parses an Info.plist from a TM sparsebundle looking for the
6812 * "band-size" key and value.
6814 static bool fruit_get_bandsize(vfs_handle_struct *handle,
6818 #define INFO_PLIST_MAX_SIZE 64*1024
6820 struct smb_filename *smb_fname = NULL;
6821 files_struct *fsp = NULL;
6822 uint8_t *file_data = NULL;
6823 char **lines = NULL;
6824 char *band_size_line = NULL;
6825 size_t plist_file_size;
6832 plist = talloc_asprintf(talloc_tos(),
6834 handle->conn->connectpath,
6836 if (plist == NULL) {
6841 smb_fname = synthetic_smb_fname(talloc_tos(), plist, NULL, NULL, 0);
6842 if (smb_fname == NULL) {
6847 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
6849 DBG_INFO("Ignoring Sparsebundle without Info.plist [%s]\n", dir);
6854 plist_file_size = smb_fname->st.st_ex_size;
6856 if (plist_file_size > INFO_PLIST_MAX_SIZE) {
6857 DBG_INFO("%s is too large, ignoring\n", plist);
6862 status = SMB_VFS_NEXT_CREATE_FILE(
6865 0, /* root_dir_fid */
6866 smb_fname, /* fname */
6867 FILE_GENERIC_READ, /* access_mask */
6868 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
6869 FILE_OPEN, /* create_disposition */
6870 0, /* create_options */
6871 0, /* file_attributes */
6872 INTERNAL_OPEN_ONLY, /* oplock_request */
6874 0, /* allocation_size */
6875 0, /* private_flags */
6880 NULL, NULL); /* create context */
6881 if (!NT_STATUS_IS_OK(status)) {
6882 DBG_INFO("Opening [%s] failed [%s]\n",
6883 smb_fname_str_dbg(smb_fname), nt_errstr(status));
6888 file_data = talloc_array(talloc_tos(), uint8_t, plist_file_size);
6889 if (file_data == NULL) {
6894 nread = SMB_VFS_NEXT_PREAD(handle, fsp, file_data, plist_file_size, 0);
6895 if (nread != plist_file_size) {
6896 DBG_ERR("Short read on [%s]: %zu/%zd\n",
6897 fsp_str_dbg(fsp), nread, plist_file_size);
6903 status = close_file(NULL, fsp, NORMAL_CLOSE);
6905 if (!NT_STATUS_IS_OK(status)) {
6906 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
6911 lines = file_lines_parse((char *)file_data,
6915 if (lines == NULL) {
6920 band_size_line = fruit_get_bandsize_line(lines, numlines);
6921 if (band_size_line == NULL) {
6922 DBG_ERR("Didn't find band-size key in [%s]\n",
6923 smb_fname_str_dbg(smb_fname));
6928 ok = fruit_get_bandsize_from_line(band_size_line, band_size);
6930 DBG_ERR("fruit_get_bandsize_from_line failed\n");
6934 DBG_DEBUG("Parsed band-size [%zu] for [%s]\n", *band_size, plist);
6938 status = close_file(NULL, fsp, NORMAL_CLOSE);
6939 if (!NT_STATUS_IS_OK(status)) {
6940 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
6945 TALLOC_FREE(smb_fname);
6946 TALLOC_FREE(file_data);
6951 struct fruit_disk_free_state {
6955 static bool fruit_get_num_bands(vfs_handle_struct *handle,
6960 struct smb_filename *bands_dir = NULL;
6962 struct dirent *e = NULL;
6966 path = talloc_asprintf(talloc_tos(),
6968 handle->conn->connectpath,
6974 bands_dir = synthetic_smb_fname(talloc_tos(),
6980 if (bands_dir == NULL) {
6984 d = SMB_VFS_NEXT_OPENDIR(handle, bands_dir, NULL, 0);
6986 TALLOC_FREE(bands_dir);
6992 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
6994 e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
6996 if (ISDOT(e->d_name) || ISDOTDOT(e->d_name)) {
7002 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
7004 TALLOC_FREE(bands_dir);
7008 DBG_DEBUG("%zu bands in [%s]\n", nbands, smb_fname_str_dbg(bands_dir));
7010 TALLOC_FREE(bands_dir);
7016 static bool fruit_tmsize_do_dirent(vfs_handle_struct *handle,
7017 struct fruit_disk_free_state *state,
7022 size_t sparsebundle_strlen = strlen("sparsebundle");
7023 size_t bandsize = 0;
7027 p = strstr(e->d_name, "sparsebundle");
7032 if (p[sparsebundle_strlen] != '\0') {
7036 DBG_DEBUG("Processing sparsebundle [%s]\n", e->d_name);
7038 ok = fruit_get_bandsize(handle, e->d_name, &bandsize);
7041 * Beware of race conditions: this may be an uninitialized
7042 * Info.plist that a client is just creating. We don't want let
7043 * this to trigger complete failure.
7045 DBG_ERR("Processing sparsebundle [%s] failed\n", e->d_name);
7049 ok = fruit_get_num_bands(handle, e->d_name, &nbands);
7052 * Beware of race conditions: this may be a backup sparsebundle
7053 * in an early stage lacking a bands subdirectory. We don't want
7054 * let this to trigger complete failure.
7056 DBG_ERR("Processing sparsebundle [%s] failed\n", e->d_name);
7060 if (bandsize > SIZE_MAX/nbands) {
7061 DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
7065 tm_size = bandsize * nbands;
7067 if (state->total_size + tm_size < state->total_size) {
7068 DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
7073 state->total_size += tm_size;
7075 DBG_DEBUG("[%s] tm_size [%jd] total_size [%jd]\n",
7076 e->d_name, (intmax_t)tm_size, (intmax_t)state->total_size);
7082 * Calculate used size of a TimeMachine volume
7084 * This assumes that the volume is used only for TimeMachine.
7086 * - readdir(basedir of share), then
7087 * - for every element that matches regex "^\(.*\)\.sparsebundle$" :
7088 * - parse "\1.sparsebundle/Info.plist" and read the band-size XML key
7089 * - count band files in "\1.sparsebundle/bands/"
7090 * - calculate used size of all bands: band_count * band_size
7092 static uint64_t fruit_disk_free(vfs_handle_struct *handle,
7093 const struct smb_filename *smb_fname,
7098 struct fruit_config_data *config = NULL;
7099 struct fruit_disk_free_state state = {0};
7101 struct dirent *e = NULL;
7107 SMB_VFS_HANDLE_GET_DATA(handle, config,
7108 struct fruit_config_data,
7111 if (!config->time_machine ||
7112 config->time_machine_max_size == 0)
7114 return SMB_VFS_NEXT_DISK_FREE(handle,
7121 d = SMB_VFS_NEXT_OPENDIR(handle, smb_fname, NULL, 0);
7126 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
7128 e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
7130 ok = fruit_tmsize_do_dirent(handle, &state, e);
7132 SMB_VFS_NEXT_CLOSEDIR(handle, d);
7137 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
7142 dsize = config->time_machine_max_size / 512;
7143 dfree = dsize - (state.total_size / 512);
7144 if (dfree > dsize) {
7154 static struct vfs_fn_pointers vfs_fruit_fns = {
7155 .connect_fn = fruit_connect,
7156 .disk_free_fn = fruit_disk_free,
7158 /* File operations */
7159 .chmod_fn = fruit_chmod,
7160 .chown_fn = fruit_chown,
7161 .unlink_fn = fruit_unlink,
7162 .rename_fn = fruit_rename,
7163 .rmdir_fn = fruit_rmdir,
7164 .open_fn = fruit_open,
7165 .close_fn = fruit_close,
7166 .pread_fn = fruit_pread,
7167 .pwrite_fn = fruit_pwrite,
7168 .pread_send_fn = fruit_pread_send,
7169 .pread_recv_fn = fruit_pread_recv,
7170 .pwrite_send_fn = fruit_pwrite_send,
7171 .pwrite_recv_fn = fruit_pwrite_recv,
7172 .stat_fn = fruit_stat,
7173 .lstat_fn = fruit_lstat,
7174 .fstat_fn = fruit_fstat,
7175 .streaminfo_fn = fruit_streaminfo,
7176 .ntimes_fn = fruit_ntimes,
7177 .ftruncate_fn = fruit_ftruncate,
7178 .fallocate_fn = fruit_fallocate,
7179 .create_file_fn = fruit_create_file,
7180 .readdir_attr_fn = fruit_readdir_attr,
7181 .offload_read_send_fn = fruit_offload_read_send,
7182 .offload_read_recv_fn = fruit_offload_read_recv,
7183 .offload_write_send_fn = fruit_offload_write_send,
7184 .offload_write_recv_fn = fruit_offload_write_recv,
7186 /* NT ACL operations */
7187 .fget_nt_acl_fn = fruit_fget_nt_acl,
7188 .fset_nt_acl_fn = fruit_fset_nt_acl,
7192 NTSTATUS vfs_fruit_init(TALLOC_CTX *ctx)
7194 NTSTATUS ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "fruit",
7196 if (!NT_STATUS_IS_OK(ret)) {
7200 vfs_fruit_debug_level = debug_add_class("fruit");
7201 if (vfs_fruit_debug_level == -1) {
7202 vfs_fruit_debug_level = DBGC_VFS;
7203 DEBUG(0, ("%s: Couldn't register custom debugging class!\n",
7206 DEBUG(10, ("%s: Debug class number of '%s': %d\n",
7207 "vfs_fruit_init","fruit",vfs_fruit_debug_level));