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(vfs_handle_struct *handle,
1064 const struct smb_filename *smb_fname,
1065 bool *converted_xattr)
1067 static struct char_mappings **string_replace_cmaps = NULL;
1068 char *map = MAP_FAILED;
1072 int saved_errno = 0;
1077 *converted_xattr = false;
1079 if (ad_getentrylen(ad, ADEID_FINDERI) == ADEDLEN_FINDERI) {
1083 if (string_replace_cmaps == NULL) {
1084 const char **mappings = NULL;
1086 mappings = str_list_make_v3_const(
1087 talloc_tos(), fruit_catia_maps, NULL);
1088 if (mappings == NULL) {
1091 string_replace_cmaps = string_replace_init_map(mappings);
1092 TALLOC_FREE(mappings);
1095 maplen = ad_getentryoff(ad, ADEID_RFORK) +
1096 ad_getentrylen(ad, ADEID_RFORK);
1098 /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
1099 map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
1101 if (map == MAP_FAILED) {
1102 DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
1106 for (i = 0; i < ad->adx_header.adx_num_attrs; i++) {
1107 struct ad_xattr_entry *e = &ad->adx_entries[i];
1108 char *mapped_name = NULL;
1110 struct smb_filename *stream_name = NULL;
1111 files_struct *fsp = NULL;
1114 status = string_replace_allocate(handle->conn,
1116 string_replace_cmaps,
1119 vfs_translate_to_windows);
1120 if (!NT_STATUS_IS_OK(status) &&
1121 !NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))
1123 DBG_ERR("string_replace_allocate failed\n");
1129 mapped_name = talloc_asprintf(talloc_tos(), ":%s", tmp);
1131 if (mapped_name == NULL) {
1136 stream_name = synthetic_smb_fname(talloc_tos(),
1137 smb_fname->base_name,
1141 TALLOC_FREE(mapped_name);
1142 if (stream_name == NULL) {
1143 DBG_ERR("synthetic_smb_fname failed\n");
1148 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1150 status = SMB_VFS_CREATE_FILE(
1151 handle->conn, /* conn */
1153 0, /* root_dir_fid */
1154 stream_name, /* fname */
1155 FILE_GENERIC_WRITE, /* access_mask */
1156 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
1157 FILE_OPEN_IF, /* create_disposition */
1158 0, /* create_options */
1159 0, /* file_attributes */
1160 INTERNAL_OPEN_ONLY, /* oplock_request */
1162 0, /* allocation_size */
1163 0, /* private_flags */
1168 NULL, NULL); /* create context */
1169 TALLOC_FREE(stream_name);
1170 if (!NT_STATUS_IS_OK(status)) {
1171 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1176 nwritten = SMB_VFS_PWRITE(fsp,
1177 map + e->adx_offset,
1180 if (nwritten == -1) {
1181 DBG_ERR("SMB_VFS_PWRITE failed\n");
1182 saved_errno = errno;
1183 close_file(NULL, fsp, ERROR_CLOSE);
1184 errno = saved_errno;
1189 status = close_file(NULL, fsp, NORMAL_CLOSE);
1190 if (!NT_STATUS_IS_OK(status)) {
1197 ad_setentrylen(ad, ADEID_FINDERI, ADEDLEN_FINDERI);
1201 DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name);
1205 len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
1206 if (len != AD_DATASZ_DOT_UND) {
1207 DBG_ERR("%s: bad size: %zd\n", smb_fname->base_name, len);
1212 ok = ad_convert_move_reso(ad, smb_fname);
1217 *converted_xattr = true;
1221 rc = munmap(map, maplen);
1223 DBG_ERR("munmap failed: %s\n", strerror(errno));
1230 static bool ad_convert_finderinfo(vfs_handle_struct *handle,
1232 const struct smb_filename *smb_fname)
1237 struct smb_filename *stream_name = NULL;
1238 files_struct *fsp = NULL;
1242 int saved_errno = 0;
1245 cmp = memcmp(ad->ad_filler, AD_FILLER_TAG_OSX, ADEDLEN_FILLER);
1250 p_ad = ad_get_entry(ad, ADEID_FINDERI);
1255 ai = afpinfo_new(talloc_tos());
1260 memcpy(ai->afpi_FinderInfo, p_ad, ADEDLEN_FINDERI);
1262 aiblob = data_blob_talloc(talloc_tos(), NULL, AFP_INFO_SIZE);
1263 if (aiblob.data == NULL) {
1268 size = afpinfo_pack(ai, (char *)aiblob.data);
1270 if (size != AFP_INFO_SIZE) {
1274 stream_name = synthetic_smb_fname(talloc_tos(),
1275 smb_fname->base_name,
1279 if (stream_name == NULL) {
1280 data_blob_free(&aiblob);
1281 DBG_ERR("synthetic_smb_fname failed\n");
1285 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1287 status = SMB_VFS_CREATE_FILE(
1288 handle->conn, /* conn */
1290 0, /* root_dir_fid */
1291 stream_name, /* fname */
1292 FILE_GENERIC_WRITE, /* access_mask */
1293 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
1294 FILE_OPEN_IF, /* create_disposition */
1295 0, /* create_options */
1296 0, /* file_attributes */
1297 INTERNAL_OPEN_ONLY, /* oplock_request */
1299 0, /* allocation_size */
1300 0, /* private_flags */
1305 NULL, NULL); /* create context */
1306 TALLOC_FREE(stream_name);
1307 if (!NT_STATUS_IS_OK(status)) {
1308 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1312 nwritten = SMB_VFS_PWRITE(fsp,
1316 if (nwritten == -1) {
1317 DBG_ERR("SMB_VFS_PWRITE failed\n");
1318 saved_errno = errno;
1319 close_file(NULL, fsp, ERROR_CLOSE);
1320 errno = saved_errno;
1324 status = close_file(NULL, fsp, NORMAL_CLOSE);
1325 if (!NT_STATUS_IS_OK(status)) {
1333 static bool ad_convert_truncate(struct adouble *ad,
1334 const struct smb_filename *smb_fname)
1339 * FIXME: direct ftruncate(), but we don't have a fsp for the
1342 rc = ftruncate(ad->ad_fd, ADEDOFF_RFORK_DOT_UND +
1343 ad_getentrylen(ad, ADEID_RFORK));
1351 static bool ad_convert_blank_rfork(vfs_handle_struct *handle,
1355 struct fruit_config_data *config = NULL;
1356 uint8_t *map = MAP_FAILED;
1365 SMB_VFS_HANDLE_GET_DATA(handle, config,
1366 struct fruit_config_data, return false);
1368 if (!config->wipe_intentionally_left_blank_rfork) {
1372 if (ad_getentrylen(ad, ADEID_RFORK) != sizeof(empty_resourcefork)) {
1376 maplen = ad_getentryoff(ad, ADEID_RFORK) +
1377 ad_getentrylen(ad, ADEID_RFORK);
1379 /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
1380 map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
1382 if (map == MAP_FAILED) {
1383 DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
1387 cmp = memcmp(map + ADEDOFF_RFORK_DOT_UND,
1389 sizeof(empty_resourcefork));
1390 rc = munmap(map, maplen);
1392 DBG_ERR("munmap failed: %s\n", strerror(errno));
1400 ad_setentrylen(ad, ADEID_RFORK, 0);
1407 len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
1408 if (len != AD_DATASZ_DOT_UND) {
1416 static bool ad_convert_delete_adfile(struct adouble *ad,
1417 const struct smb_filename *smb_fname)
1419 struct fruit_config_data *config = NULL;
1420 struct smb_filename *ad_name = NULL;
1423 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
1427 SMB_VFS_HANDLE_GET_DATA(ad->ad_handle, config,
1428 struct fruit_config_data, return false);
1430 if (!config->delete_empty_adfiles) {
1434 rc = adouble_path(talloc_tos(), smb_fname, &ad_name);
1439 rc = SMB_VFS_NEXT_UNLINK(ad->ad_handle, ad_name);
1441 DBG_ERR("Unlinking [%s] failed: %s\n",
1442 smb_fname_str_dbg(ad_name), strerror(errno));
1443 TALLOC_FREE(ad_name);
1447 DBG_WARNING("Unlinked [%s] after conversion\n", smb_fname_str_dbg(ad_name));
1448 TALLOC_FREE(ad_name);
1454 * Convert from Apple's ._ file to Netatalk
1456 * Apple's AppleDouble may contain a FinderInfo entry longer then 32
1457 * bytes containing packed xattrs.
1459 * @return -1 in case an error occurred, 0 if no conversion was done, 1
1462 static int ad_convert(struct vfs_handle_struct *handle,
1463 const struct smb_filename *smb_fname)
1465 struct adouble *ad = NULL;
1467 bool converted_xattr = false;
1471 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
1476 ok = ad_convert_xattr(handle, ad, smb_fname, &converted_xattr);
1482 ok = ad_convert_blank_rfork(handle, ad, &blank);
1488 if (converted_xattr || blank) {
1489 ok = ad_convert_truncate(ad, smb_fname);
1496 ok = ad_convert_finderinfo(handle, ad, smb_fname);
1498 DBG_ERR("Failed to convert [%s]\n",
1499 smb_fname_str_dbg(smb_fname));
1504 ok = ad_convert_delete_adfile(ad, smb_fname);
1517 * Read and parse Netatalk AppleDouble metadata xattr
1519 static ssize_t ad_read_meta(vfs_handle_struct *handle,
1521 const struct smb_filename *smb_fname)
1527 DEBUG(10, ("reading meta xattr for %s\n", smb_fname->base_name));
1529 ealen = SMB_VFS_GETXATTR(handle->conn, smb_fname,
1530 AFPINFO_EA_NETATALK, ad->ad_data,
1536 if (errno == ENOATTR) {
1542 DEBUG(2, ("error reading meta xattr: %s\n",
1548 if (ealen != AD_DATASZ_XATTR) {
1549 DEBUG(2, ("bad size %zd\n", ealen));
1555 /* Now parse entries */
1556 ok = ad_unpack(ad, ADEID_NUM_XATTR, AD_DATASZ_XATTR);
1558 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1564 if (!ad_getentryoff(ad, ADEID_FINDERI)
1565 || !ad_getentryoff(ad, ADEID_COMMENT)
1566 || !ad_getentryoff(ad, ADEID_FILEDATESI)
1567 || !ad_getentryoff(ad, ADEID_AFPFILEI)
1568 || !ad_getentryoff(ad, ADEID_PRIVDEV)
1569 || !ad_getentryoff(ad, ADEID_PRIVINO)
1570 || !ad_getentryoff(ad, ADEID_PRIVSYN)
1571 || !ad_getentryoff(ad, ADEID_PRIVID)) {
1572 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1579 DEBUG(10, ("reading meta xattr for %s, rc: %d\n",
1580 smb_fname->base_name, rc));
1584 if (errno == EINVAL) {
1586 (void)SMB_VFS_REMOVEXATTR(handle->conn,
1588 AFPINFO_EA_NETATALK);
1596 static int ad_open_rsrc_xattr(const struct smb_filename *smb_fname,
1600 #ifdef HAVE_ATTROPEN
1601 /* FIXME: direct Solaris xattr syscall */
1602 return attropen(smb_fname->base_name,
1603 AFPRESOURCE_EA_NETATALK, flags, mode);
1610 static int ad_open_rsrc_adouble(const struct smb_filename *smb_fname,
1616 struct smb_filename *adp_smb_fname = NULL;
1618 ret = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
1623 fd = open(adp_smb_fname->base_name, flags, mode);
1624 TALLOC_FREE(adp_smb_fname);
1629 static int ad_open_rsrc(vfs_handle_struct *handle,
1630 const struct smb_filename *smb_fname,
1634 struct fruit_config_data *config = NULL;
1637 SMB_VFS_HANDLE_GET_DATA(handle, config,
1638 struct fruit_config_data, return -1);
1640 if (config->rsrc == FRUIT_RSRC_XATTR) {
1641 fd = ad_open_rsrc_xattr(smb_fname, flags, mode);
1643 fd = ad_open_rsrc_adouble(smb_fname, flags, mode);
1650 * Here's the deal: for ADOUBLE_META we can do without an fd as we can issue
1651 * path based xattr calls. For ADOUBLE_RSRC however we need a full-fledged fd
1652 * for file IO on the ._ file.
1654 static int ad_open(vfs_handle_struct *handle,
1657 const struct smb_filename *smb_fname,
1663 DBG_DEBUG("Path [%s] type [%s]\n", smb_fname->base_name,
1664 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
1666 if (ad->ad_type == ADOUBLE_META) {
1670 if ((fsp != NULL) && (fsp->fh != NULL) && (fsp->fh->fd != -1)) {
1671 ad->ad_fd = fsp->fh->fd;
1672 ad->ad_opened = false;
1676 fd = ad_open_rsrc(handle, smb_fname, flags, mode);
1680 ad->ad_opened = true;
1683 DBG_DEBUG("Path [%s] type [%s] fd [%d]\n",
1684 smb_fname->base_name,
1685 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc", fd);
1690 static ssize_t ad_read_rsrc_xattr(vfs_handle_struct *handle,
1696 /* FIXME: direct sys_fstat(), don't have an fsp */
1697 ret = sys_fstat(ad->ad_fd, &st,
1698 lp_fake_directory_create_times(
1699 SNUM(handle->conn)));
1704 ad_setentrylen(ad, ADEID_RFORK, st.st_ex_size);
1705 return st.st_ex_size;
1708 static ssize_t ad_read_rsrc_adouble(vfs_handle_struct *handle,
1710 const struct smb_filename *smb_fname)
1712 SMB_STRUCT_STAT sbuf;
1719 ret = sys_fstat(ad->ad_fd, &sbuf, lp_fake_directory_create_times(
1720 SNUM(handle->conn)));
1726 * AppleDouble file header content and size, two cases:
1728 * - without xattrs it is exactly AD_DATASZ_DOT_UND (82) bytes large
1729 * - with embedded xattrs it can be larger, up to AD_XATTR_MAX_HDR_SIZE
1731 * Read as much as we can up to AD_XATTR_MAX_HDR_SIZE.
1733 size = sbuf.st_ex_size;
1734 if (size > talloc_array_length(ad->ad_data)) {
1735 if (size > AD_XATTR_MAX_HDR_SIZE) {
1736 size = AD_XATTR_MAX_HDR_SIZE;
1738 p_ad = talloc_realloc(ad, ad->ad_data, char, size);
1745 len = sys_pread(ad->ad_fd, ad->ad_data,
1746 talloc_array_length(ad->ad_data), 0);
1747 if (len != talloc_array_length(ad->ad_data)) {
1748 DBG_NOTICE("%s %s: bad size: %zd\n",
1749 smb_fname->base_name, strerror(errno), len);
1753 /* Now parse entries */
1754 ok = ad_unpack(ad, ADEID_NUM_DOT_UND, sbuf.st_ex_size);
1756 DBG_ERR("invalid AppleDouble resource %s\n",
1757 smb_fname->base_name);
1762 if ((ad_getentryoff(ad, ADEID_FINDERI) != ADEDOFF_FINDERI_DOT_UND)
1763 || (ad_getentrylen(ad, ADEID_FINDERI) < ADEDLEN_FINDERI)
1764 || (ad_getentryoff(ad, ADEID_RFORK) < ADEDOFF_RFORK_DOT_UND)) {
1765 DBG_ERR("invalid AppleDouble resource %s\n",
1766 smb_fname->base_name);
1775 * Read and parse resource fork, either ._ AppleDouble file or xattr
1777 static ssize_t ad_read_rsrc(vfs_handle_struct *handle,
1779 const struct smb_filename *smb_fname)
1781 struct fruit_config_data *config = NULL;
1784 SMB_VFS_HANDLE_GET_DATA(handle, config,
1785 struct fruit_config_data, return -1);
1787 if (config->rsrc == FRUIT_RSRC_XATTR) {
1788 len = ad_read_rsrc_xattr(handle, ad);
1790 len = ad_read_rsrc_adouble(handle, ad, smb_fname);
1797 * Read and unpack an AppleDouble metadata xattr or resource
1799 static ssize_t ad_read(vfs_handle_struct *handle,
1801 const struct smb_filename *smb_fname)
1803 switch (ad->ad_type) {
1805 return ad_read_meta(handle, ad, smb_fname);
1807 return ad_read_rsrc(handle, ad, smb_fname);
1813 static int adouble_destructor(struct adouble *ad)
1815 if ((ad->ad_fd != -1) && ad->ad_opened) {
1823 * Allocate a struct adouble without initialiing it
1825 * The struct is either hang of the fsp extension context or if fsp is
1828 * @param[in] ctx talloc context
1829 * @param[in] handle vfs handle
1830 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1832 * @return adouble handle
1834 static struct adouble *ad_alloc(TALLOC_CTX *ctx, vfs_handle_struct *handle,
1835 adouble_type_t type)
1840 struct fruit_config_data *config;
1842 SMB_VFS_HANDLE_GET_DATA(handle, config,
1843 struct fruit_config_data, return NULL);
1847 adsize = AD_DATASZ_XATTR;
1850 if (config->rsrc == FRUIT_RSRC_ADFILE) {
1851 adsize = AD_DATASZ_DOT_UND;
1858 ad = talloc_zero(ctx, struct adouble);
1865 ad->ad_data = talloc_zero_array(ad, char, adsize);
1866 if (ad->ad_data == NULL) {
1872 ad->ad_handle = handle;
1874 ad->ad_magic = AD_MAGIC;
1875 ad->ad_version = AD_VERSION;
1878 talloc_set_destructor(ad, adouble_destructor);
1888 * Allocate and initialize a new struct adouble
1890 * @param[in] ctx talloc context
1891 * @param[in] handle vfs handle
1892 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1894 * @return adouble handle, initialized
1896 static struct adouble *ad_init(TALLOC_CTX *ctx, vfs_handle_struct *handle,
1897 adouble_type_t type)
1900 const struct ad_entry_order *eid;
1901 struct adouble *ad = NULL;
1902 struct fruit_config_data *config;
1903 time_t t = time(NULL);
1905 SMB_VFS_HANDLE_GET_DATA(handle, config,
1906 struct fruit_config_data, return NULL);
1910 eid = entry_order_meta_xattr;
1913 if (config->rsrc == FRUIT_RSRC_ADFILE) {
1914 eid = entry_order_dot_und;
1916 eid = entry_order_rsrc_xattr;
1923 ad = ad_alloc(ctx, handle, type);
1929 ad->ad_eid[eid->id].ade_off = eid->offset;
1930 ad->ad_eid[eid->id].ade_len = eid->len;
1934 /* put something sane in the date fields */
1935 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, t);
1936 ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, t);
1937 ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, t);
1938 ad_setdate(ad, AD_DATE_BACKUP, htonl(AD_DATE_START));
1946 static struct adouble *ad_get_internal(TALLOC_CTX *ctx,
1947 vfs_handle_struct *handle,
1949 const struct smb_filename *smb_fname,
1950 adouble_type_t type)
1954 struct adouble *ad = NULL;
1958 smb_fname = fsp->base_fsp->fsp_name;
1961 DEBUG(10, ("ad_get(%s) called for %s\n",
1962 type == ADOUBLE_META ? "meta" : "rsrc",
1963 smb_fname->base_name));
1965 ad = ad_alloc(ctx, handle, type);
1971 /* Try rw first so we can use the fd in ad_convert() */
1974 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
1975 if (rc == -1 && ((errno == EROFS) || (errno == EACCES))) {
1977 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
1980 DBG_DEBUG("ad_open [%s] error [%s]\n",
1981 smb_fname->base_name, strerror(errno));
1986 len = ad_read(handle, ad, smb_fname);
1988 DEBUG(10, ("error reading AppleDouble for %s\n",
1989 smb_fname->base_name));
1995 DEBUG(10, ("ad_get(%s) for %s returning %d\n",
1996 type == ADOUBLE_META ? "meta" : "rsrc",
1997 smb_fname->base_name, rc));
2006 * Return AppleDouble data for a file
2008 * @param[in] ctx talloc context
2009 * @param[in] handle vfs handle
2010 * @param[in] smb_fname pathname to file or directory
2011 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2013 * @return talloced struct adouble or NULL on error
2015 static struct adouble *ad_get(TALLOC_CTX *ctx,
2016 vfs_handle_struct *handle,
2017 const struct smb_filename *smb_fname,
2018 adouble_type_t type)
2020 return ad_get_internal(ctx, handle, NULL, smb_fname, type);
2024 * Return AppleDouble data for a file
2026 * @param[in] ctx talloc context
2027 * @param[in] handle vfs handle
2028 * @param[in] fsp fsp to use for IO
2029 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2031 * @return talloced struct adouble or NULL on error
2033 static struct adouble *ad_fget(TALLOC_CTX *ctx, vfs_handle_struct *handle,
2034 files_struct *fsp, adouble_type_t type)
2036 return ad_get_internal(ctx, handle, fsp, NULL, type);
2040 * Set AppleDouble metadata on a file or directory
2042 * @param[in] ad adouble handle
2044 * @param[in] smb_fname pathname to file or directory
2046 * @return status code, 0 means success
2048 static int ad_set(vfs_handle_struct *handle,
2050 const struct smb_filename *smb_fname)
2055 DBG_DEBUG("Path [%s]\n", smb_fname->base_name);
2057 if (ad->ad_type != ADOUBLE_META) {
2058 DBG_ERR("ad_set on [%s] used with ADOUBLE_RSRC\n",
2059 smb_fname->base_name);
2068 ret = SMB_VFS_SETXATTR(handle->conn,
2070 AFPINFO_EA_NETATALK,
2072 AD_DATASZ_XATTR, 0);
2074 DBG_DEBUG("Path [%s] ret [%d]\n", smb_fname->base_name, ret);
2080 * Set AppleDouble metadata on a file or directory
2082 * @param[in] ad adouble handle
2083 * @param[in] fsp file handle
2085 * @return status code, 0 means success
2087 static int ad_fset(struct vfs_handle_struct *handle,
2095 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
2098 || (fsp->fh == NULL)
2099 || (fsp->fh->fd == -1))
2101 smb_panic("bad fsp");
2109 switch (ad->ad_type) {
2111 rc = SMB_VFS_NEXT_SETXATTR(handle,
2113 AFPINFO_EA_NETATALK,
2115 AD_DATASZ_XATTR, 0);
2119 len = SMB_VFS_NEXT_PWRITE(handle,
2124 if (len != AD_DATASZ_DOT_UND) {
2125 DBG_ERR("short write on %s: %zd", fsp_str_dbg(fsp), len);
2135 DBG_DEBUG("Path [%s] rc [%d]\n", fsp_str_dbg(fsp), rc);
2140 /*****************************************************************************
2142 *****************************************************************************/
2144 static bool is_afpinfo_stream(const struct smb_filename *smb_fname)
2146 if (strncasecmp_m(smb_fname->stream_name,
2147 AFPINFO_STREAM_NAME,
2148 strlen(AFPINFO_STREAM_NAME)) == 0) {
2154 static bool is_afpresource_stream(const struct smb_filename *smb_fname)
2156 if (strncasecmp_m(smb_fname->stream_name,
2157 AFPRESOURCE_STREAM_NAME,
2158 strlen(AFPRESOURCE_STREAM_NAME)) == 0) {
2165 * Test whether stream is an Apple stream.
2167 static bool is_apple_stream(const struct smb_filename *smb_fname)
2169 if (is_afpinfo_stream(smb_fname)) {
2172 if (is_afpresource_stream(smb_fname)) {
2179 * Initialize config struct from our smb.conf config parameters
2181 static int init_fruit_config(vfs_handle_struct *handle)
2183 struct fruit_config_data *config;
2185 const char *tm_size_str = NULL;
2187 config = talloc_zero(handle->conn, struct fruit_config_data);
2189 DEBUG(1, ("talloc_zero() failed\n"));
2195 * Versions up to Samba 4.5.x had a spelling bug in the
2196 * fruit:resource option calling lp_parm_enum with
2197 * "res*s*ource" (ie two s).
2199 * In Samba 4.6 we accept both the wrong and the correct
2200 * spelling, in Samba 4.7 the bad spelling will be removed.
2202 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2203 "ressource", fruit_rsrc, FRUIT_RSRC_ADFILE);
2204 if (enumval == -1) {
2205 DEBUG(1, ("value for %s: resource type unknown\n",
2206 FRUIT_PARAM_TYPE_NAME));
2209 config->rsrc = (enum fruit_rsrc)enumval;
2211 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2212 "resource", fruit_rsrc, enumval);
2213 if (enumval == -1) {
2214 DEBUG(1, ("value for %s: resource type unknown\n",
2215 FRUIT_PARAM_TYPE_NAME));
2218 config->rsrc = (enum fruit_rsrc)enumval;
2220 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2221 "metadata", fruit_meta, FRUIT_META_NETATALK);
2222 if (enumval == -1) {
2223 DEBUG(1, ("value for %s: metadata type unknown\n",
2224 FRUIT_PARAM_TYPE_NAME));
2227 config->meta = (enum fruit_meta)enumval;
2229 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2230 "locking", fruit_locking, FRUIT_LOCKING_NONE);
2231 if (enumval == -1) {
2232 DEBUG(1, ("value for %s: locking type unknown\n",
2233 FRUIT_PARAM_TYPE_NAME));
2236 config->locking = (enum fruit_locking)enumval;
2238 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2239 "encoding", fruit_encoding, FRUIT_ENC_PRIVATE);
2240 if (enumval == -1) {
2241 DEBUG(1, ("value for %s: encoding type unknown\n",
2242 FRUIT_PARAM_TYPE_NAME));
2245 config->encoding = (enum fruit_encoding)enumval;
2247 if (config->rsrc == FRUIT_RSRC_ADFILE) {
2248 config->veto_appledouble = lp_parm_bool(SNUM(handle->conn),
2249 FRUIT_PARAM_TYPE_NAME,
2254 config->use_aapl = lp_parm_bool(
2255 -1, FRUIT_PARAM_TYPE_NAME, "aapl", true);
2257 config->time_machine = lp_parm_bool(
2258 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "time machine", false);
2260 config->unix_info_enabled = lp_parm_bool(
2261 -1, FRUIT_PARAM_TYPE_NAME, "nfs_aces", true);
2263 config->use_copyfile = lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME,
2266 config->posix_rename = lp_parm_bool(
2267 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "posix_rename", true);
2269 config->aapl_zero_file_id =
2270 lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME, "zero_file_id", true);
2272 config->readdir_attr_rsize = lp_parm_bool(
2273 SNUM(handle->conn), "readdir_attr", "aapl_rsize", true);
2275 config->readdir_attr_finder_info = lp_parm_bool(
2276 SNUM(handle->conn), "readdir_attr", "aapl_finder_info", true);
2278 config->readdir_attr_max_access = lp_parm_bool(
2279 SNUM(handle->conn), "readdir_attr", "aapl_max_access", true);
2281 config->model = lp_parm_const_string(
2282 -1, FRUIT_PARAM_TYPE_NAME, "model", "MacSamba");
2284 tm_size_str = lp_parm_const_string(
2285 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2286 "time machine max size", NULL);
2287 if (tm_size_str != NULL) {
2288 config->time_machine_max_size = conv_str_size(tm_size_str);
2291 config->wipe_intentionally_left_blank_rfork = lp_parm_bool(
2292 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2293 "wipe_intentionally_left_blank_rfork", false);
2295 config->delete_empty_adfiles = lp_parm_bool(
2296 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2297 "delete_empty_adfiles", false);
2299 SMB_VFS_HANDLE_SET_DATA(handle, config,
2300 NULL, struct fruit_config_data,
2307 * Prepend "._" to a basename
2308 * Return a new struct smb_filename with stream_name == NULL.
2310 static int adouble_path(TALLOC_CTX *ctx,
2311 const struct smb_filename *smb_fname_in,
2312 struct smb_filename **pp_smb_fname_out)
2316 struct smb_filename *smb_fname = cp_smb_filename(ctx,
2319 if (smb_fname == NULL) {
2323 /* We need streamname to be NULL */
2324 TALLOC_FREE(smb_fname->stream_name);
2326 /* And we're replacing base_name. */
2327 TALLOC_FREE(smb_fname->base_name);
2329 if (!parent_dirname(smb_fname, smb_fname_in->base_name,
2331 TALLOC_FREE(smb_fname);
2335 smb_fname->base_name = talloc_asprintf(smb_fname,
2336 "%s/._%s", parent, base);
2337 if (smb_fname->base_name == NULL) {
2338 TALLOC_FREE(smb_fname);
2342 *pp_smb_fname_out = smb_fname;
2348 * Allocate and initialize an AfpInfo struct
2350 static AfpInfo *afpinfo_new(TALLOC_CTX *ctx)
2352 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2356 ai->afpi_Signature = AFP_Signature;
2357 ai->afpi_Version = AFP_Version;
2358 ai->afpi_BackupTime = AD_DATE_START;
2363 * Pack an AfpInfo struct into a buffer
2365 * Buffer size must be at least AFP_INFO_SIZE
2366 * Returns size of packed buffer
2368 static ssize_t afpinfo_pack(const AfpInfo *ai, char *buf)
2370 memset(buf, 0, AFP_INFO_SIZE);
2372 RSIVAL(buf, 0, ai->afpi_Signature);
2373 RSIVAL(buf, 4, ai->afpi_Version);
2374 RSIVAL(buf, 12, ai->afpi_BackupTime);
2375 memcpy(buf + 16, ai->afpi_FinderInfo, sizeof(ai->afpi_FinderInfo));
2377 return AFP_INFO_SIZE;
2381 * Unpack a buffer into a AfpInfo structure
2383 * Buffer size must be at least AFP_INFO_SIZE
2384 * Returns allocated AfpInfo struct
2386 static AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data)
2388 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2393 ai->afpi_Signature = RIVAL(data, 0);
2394 ai->afpi_Version = RIVAL(data, 4);
2395 ai->afpi_BackupTime = RIVAL(data, 12);
2396 memcpy(ai->afpi_FinderInfo, (const char *)data + 16,
2397 sizeof(ai->afpi_FinderInfo));
2399 if (ai->afpi_Signature != AFP_Signature
2400 || ai->afpi_Version != AFP_Version) {
2401 DEBUG(1, ("Bad AfpInfo signature or version\n"));
2409 * Fake an inode number from the md5 hash of the (xattr) name
2411 static SMB_INO_T fruit_inode(const SMB_STRUCT_STAT *sbuf, const char *sname)
2413 gnutls_hash_hd_t hash_hnd = NULL;
2414 unsigned char hash[16];
2415 SMB_INO_T result = 0;
2419 DBG_DEBUG("fruit_inode called for %ju/%ju [%s]\n",
2420 (uintmax_t)sbuf->st_ex_dev,
2421 (uintmax_t)sbuf->st_ex_ino, sname);
2423 upper_sname = talloc_strdup_upper(talloc_tos(), sname);
2424 SMB_ASSERT(upper_sname != NULL);
2426 rc = gnutls_hash_init(&hash_hnd, GNUTLS_DIG_MD5);
2431 rc = gnutls_hash(hash_hnd, &(sbuf->st_ex_dev), sizeof(sbuf->st_ex_dev));
2433 gnutls_hash_deinit(hash_hnd, NULL);
2436 rc = gnutls_hash(hash_hnd,
2438 sizeof(sbuf->st_ex_ino));
2440 gnutls_hash_deinit(hash_hnd, NULL);
2443 rc = gnutls_hash(hash_hnd,
2445 talloc_get_size(upper_sname) - 1);
2447 gnutls_hash_deinit(hash_hnd, NULL);
2451 gnutls_hash_deinit(hash_hnd, hash);
2453 /* Hopefully all the variation is in the lower 4 (or 8) bytes! */
2454 memcpy(&result, hash, sizeof(result));
2457 DBG_DEBUG("fruit_inode \"%s\": ino=%ju\n",
2458 sname, (uintmax_t)result);
2461 TALLOC_FREE(upper_sname);
2466 static bool add_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
2467 struct stream_struct **streams,
2468 const char *name, off_t size,
2471 struct stream_struct *tmp;
2473 tmp = talloc_realloc(mem_ctx, *streams, struct stream_struct,
2479 tmp[*num_streams].name = talloc_asprintf(tmp, "%s:$DATA", name);
2480 if (tmp[*num_streams].name == NULL) {
2484 tmp[*num_streams].size = size;
2485 tmp[*num_streams].alloc_size = alloc_size;
2492 static bool filter_empty_rsrc_stream(unsigned int *num_streams,
2493 struct stream_struct **streams)
2495 struct stream_struct *tmp = *streams;
2498 if (*num_streams == 0) {
2502 for (i = 0; i < *num_streams; i++) {
2503 if (strequal_m(tmp[i].name, AFPRESOURCE_STREAM)) {
2508 if (i == *num_streams) {
2512 if (tmp[i].size > 0) {
2516 TALLOC_FREE(tmp[i].name);
2517 if (*num_streams - 1 > i) {
2518 memmove(&tmp[i], &tmp[i+1],
2519 (*num_streams - i - 1) * sizeof(struct stream_struct));
2526 static bool del_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
2527 struct stream_struct **streams,
2530 struct stream_struct *tmp = *streams;
2533 if (*num_streams == 0) {
2537 for (i = 0; i < *num_streams; i++) {
2538 if (strequal_m(tmp[i].name, name)) {
2543 if (i == *num_streams) {
2547 TALLOC_FREE(tmp[i].name);
2548 if (*num_streams - 1 > i) {
2549 memmove(&tmp[i], &tmp[i+1],
2550 (*num_streams - i - 1) * sizeof(struct stream_struct));
2557 static bool ad_empty_finderinfo(const struct adouble *ad)
2560 char emptybuf[ADEDLEN_FINDERI] = {0};
2563 fi = ad_get_entry(ad, ADEID_FINDERI);
2565 DBG_ERR("Missing FinderInfo in struct adouble [%p]\n", ad);
2569 cmp = memcmp(emptybuf, fi, ADEDLEN_FINDERI);
2573 static bool ai_empty_finderinfo(const AfpInfo *ai)
2576 char emptybuf[ADEDLEN_FINDERI] = {0};
2578 cmp = memcmp(emptybuf, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
2583 * Update btime with btime from Netatalk
2585 static void update_btime(vfs_handle_struct *handle,
2586 struct smb_filename *smb_fname)
2589 struct timespec creation_time = {0};
2591 struct fruit_config_data *config = NULL;
2593 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
2596 switch (config->meta) {
2597 case FRUIT_META_STREAM:
2599 case FRUIT_META_NETATALK:
2603 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
2607 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
2611 if (ad_getdate(ad, AD_DATE_UNIX | AD_DATE_CREATE, &t) != 0) {
2617 creation_time.tv_sec = convert_uint32_t_to_time_t(t);
2618 update_stat_ex_create_time(&smb_fname->st, creation_time);
2624 * Map an access mask to a Netatalk single byte byte range lock
2626 static off_t access_to_netatalk_brl(enum apple_fork fork_type,
2627 uint32_t access_mask)
2631 switch (access_mask) {
2632 case FILE_READ_DATA:
2633 offset = AD_FILELOCK_OPEN_RD;
2636 case FILE_WRITE_DATA:
2637 case FILE_APPEND_DATA:
2638 offset = AD_FILELOCK_OPEN_WR;
2642 offset = AD_FILELOCK_OPEN_NONE;
2646 if (fork_type == APPLE_FORK_RSRC) {
2647 if (offset == AD_FILELOCK_OPEN_NONE) {
2648 offset = AD_FILELOCK_RSRC_OPEN_NONE;
2658 * Map a deny mode to a Netatalk brl
2660 static off_t denymode_to_netatalk_brl(enum apple_fork fork_type,
2665 switch (deny_mode) {
2667 offset = AD_FILELOCK_DENY_RD;
2671 offset = AD_FILELOCK_DENY_WR;
2675 smb_panic("denymode_to_netatalk_brl: bad deny mode\n");
2678 if (fork_type == APPLE_FORK_RSRC) {
2686 * Call fcntl() with an exclusive F_GETLK request in order to
2687 * determine if there's an exisiting shared lock
2689 * @return true if the requested lock was found or any error occurred
2690 * false if the lock was not found
2692 static bool test_netatalk_lock(files_struct *fsp, off_t in_offset)
2695 off_t offset = in_offset;
2700 result = SMB_VFS_GETLOCK(fsp, &offset, &len, &type, &pid);
2701 if (result == false) {
2705 if (type != F_UNLCK) {
2712 static NTSTATUS fruit_check_access(vfs_handle_struct *handle,
2714 uint32_t access_mask,
2715 uint32_t share_mode)
2717 NTSTATUS status = NT_STATUS_OK;
2719 bool share_for_read = (share_mode & FILE_SHARE_READ);
2720 bool share_for_write = (share_mode & FILE_SHARE_WRITE);
2721 bool netatalk_already_open_for_reading = false;
2722 bool netatalk_already_open_for_writing = false;
2723 bool netatalk_already_open_with_deny_read = false;
2724 bool netatalk_already_open_with_deny_write = false;
2726 /* FIXME: hardcoded data fork, add resource fork */
2727 enum apple_fork fork_type = APPLE_FORK_DATA;
2729 DBG_DEBUG("fruit_check_access: %s, am: %s/%s, sm: 0x%x\n",
2731 access_mask & FILE_READ_DATA ? "READ" :"-",
2732 access_mask & FILE_WRITE_DATA ? "WRITE" : "-",
2735 if (fsp->fh->fd == -1) {
2736 return NT_STATUS_OK;
2739 /* Read NetATalk opens and deny modes on the file. */
2740 netatalk_already_open_for_reading = test_netatalk_lock(fsp,
2741 access_to_netatalk_brl(fork_type,
2744 netatalk_already_open_with_deny_read = test_netatalk_lock(fsp,
2745 denymode_to_netatalk_brl(fork_type,
2748 netatalk_already_open_for_writing = test_netatalk_lock(fsp,
2749 access_to_netatalk_brl(fork_type,
2752 netatalk_already_open_with_deny_write = test_netatalk_lock(fsp,
2753 denymode_to_netatalk_brl(fork_type,
2756 /* If there are any conflicts - sharing violation. */
2757 if ((access_mask & FILE_READ_DATA) &&
2758 netatalk_already_open_with_deny_read) {
2759 return NT_STATUS_SHARING_VIOLATION;
2762 if (!share_for_read &&
2763 netatalk_already_open_for_reading) {
2764 return NT_STATUS_SHARING_VIOLATION;
2767 if ((access_mask & FILE_WRITE_DATA) &&
2768 netatalk_already_open_with_deny_write) {
2769 return NT_STATUS_SHARING_VIOLATION;
2772 if (!share_for_write &&
2773 netatalk_already_open_for_writing) {
2774 return NT_STATUS_SHARING_VIOLATION;
2777 if (!(access_mask & FILE_READ_DATA)) {
2779 * Nothing we can do here, we need read access
2782 return NT_STATUS_OK;
2785 /* Set NetAtalk locks matching our access */
2786 if (access_mask & FILE_READ_DATA) {
2787 struct byte_range_lock *br_lck = NULL;
2789 off = access_to_netatalk_brl(fork_type, FILE_READ_DATA);
2791 handle->conn->sconn->msg_ctx, fsp,
2792 fsp->op->global->open_persistent_id, 1, off,
2793 READ_LOCK, POSIX_LOCK, false,
2796 TALLOC_FREE(br_lck);
2798 if (!NT_STATUS_IS_OK(status)) {
2803 if (!share_for_read) {
2804 struct byte_range_lock *br_lck = NULL;
2806 off = denymode_to_netatalk_brl(fork_type, DENY_READ);
2808 handle->conn->sconn->msg_ctx, fsp,
2809 fsp->op->global->open_persistent_id, 1, off,
2810 READ_LOCK, POSIX_LOCK, false,
2813 TALLOC_FREE(br_lck);
2815 if (!NT_STATUS_IS_OK(status)) {
2820 if (access_mask & FILE_WRITE_DATA) {
2821 struct byte_range_lock *br_lck = NULL;
2823 off = access_to_netatalk_brl(fork_type, FILE_WRITE_DATA);
2825 handle->conn->sconn->msg_ctx, fsp,
2826 fsp->op->global->open_persistent_id, 1, off,
2827 READ_LOCK, POSIX_LOCK, false,
2830 TALLOC_FREE(br_lck);
2832 if (!NT_STATUS_IS_OK(status)) {
2837 if (!share_for_write) {
2838 struct byte_range_lock *br_lck = NULL;
2840 off = denymode_to_netatalk_brl(fork_type, DENY_WRITE);
2842 handle->conn->sconn->msg_ctx, fsp,
2843 fsp->op->global->open_persistent_id, 1, off,
2844 READ_LOCK, POSIX_LOCK, false,
2847 TALLOC_FREE(br_lck);
2849 if (!NT_STATUS_IS_OK(status)) {
2854 return NT_STATUS_OK;
2857 static NTSTATUS check_aapl(vfs_handle_struct *handle,
2858 struct smb_request *req,
2859 const struct smb2_create_blobs *in_context_blobs,
2860 struct smb2_create_blobs *out_context_blobs)
2862 struct fruit_config_data *config;
2864 struct smb2_create_blob *aapl = NULL;
2868 DATA_BLOB blob = data_blob_talloc(req, NULL, 0);
2869 uint64_t req_bitmap, client_caps;
2870 uint64_t server_caps = SMB2_CRTCTX_AAPL_UNIX_BASED;
2874 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
2875 return NT_STATUS_UNSUCCESSFUL);
2877 if (!config->use_aapl
2878 || in_context_blobs == NULL
2879 || out_context_blobs == NULL) {
2880 return NT_STATUS_OK;
2883 aapl = smb2_create_blob_find(in_context_blobs,
2884 SMB2_CREATE_TAG_AAPL);
2886 return NT_STATUS_OK;
2889 if (aapl->data.length != 24) {
2890 DEBUG(1, ("unexpected AAPL ctxt length: %ju\n",
2891 (uintmax_t)aapl->data.length));
2892 return NT_STATUS_INVALID_PARAMETER;
2895 cmd = IVAL(aapl->data.data, 0);
2896 if (cmd != SMB2_CRTCTX_AAPL_SERVER_QUERY) {
2897 DEBUG(1, ("unsupported AAPL cmd: %d\n", cmd));
2898 return NT_STATUS_INVALID_PARAMETER;
2901 req_bitmap = BVAL(aapl->data.data, 8);
2902 client_caps = BVAL(aapl->data.data, 16);
2904 SIVAL(p, 0, SMB2_CRTCTX_AAPL_SERVER_QUERY);
2906 SBVAL(p, 8, req_bitmap);
2907 ok = data_blob_append(req, &blob, p, 16);
2909 return NT_STATUS_UNSUCCESSFUL;
2912 if (req_bitmap & SMB2_CRTCTX_AAPL_SERVER_CAPS) {
2913 if ((client_caps & SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR) &&
2914 (handle->conn->tcon->compat->fs_capabilities & FILE_NAMED_STREAMS)) {
2915 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR;
2916 config->readdir_attr_enabled = true;
2919 if (config->use_copyfile) {
2920 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_OSX_COPYFILE;
2921 config->copyfile_enabled = true;
2925 * The client doesn't set the flag, so we can't check
2926 * for it and just set it unconditionally
2928 if (config->unix_info_enabled) {
2929 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_NFS_ACE;
2932 SBVAL(p, 0, server_caps);
2933 ok = data_blob_append(req, &blob, p, 8);
2935 return NT_STATUS_UNSUCCESSFUL;
2939 if (req_bitmap & SMB2_CRTCTX_AAPL_VOLUME_CAPS) {
2940 int val = lp_case_sensitive(SNUM(handle->conn->tcon->compat));
2948 caps |= SMB2_CRTCTX_AAPL_CASE_SENSITIVE;
2955 if (config->time_machine) {
2956 caps |= SMB2_CRTCTX_AAPL_FULL_SYNC;
2961 ok = data_blob_append(req, &blob, p, 8);
2963 return NT_STATUS_UNSUCCESSFUL;
2967 if (req_bitmap & SMB2_CRTCTX_AAPL_MODEL_INFO) {
2968 ok = convert_string_talloc(req,
2969 CH_UNIX, CH_UTF16LE,
2970 config->model, strlen(config->model),
2973 return NT_STATUS_UNSUCCESSFUL;
2977 SIVAL(p + 4, 0, modellen);
2978 ok = data_blob_append(req, &blob, p, 8);
2981 return NT_STATUS_UNSUCCESSFUL;
2984 ok = data_blob_append(req, &blob, model, modellen);
2987 return NT_STATUS_UNSUCCESSFUL;
2991 status = smb2_create_blob_add(out_context_blobs,
2993 SMB2_CREATE_TAG_AAPL,
2995 if (NT_STATUS_IS_OK(status)) {
2996 global_fruit_config.nego_aapl = true;
2997 if (config->aapl_zero_file_id) {
2998 aapl_force_zero_file_id(handle->conn->sconn);
3005 static bool readdir_attr_meta_finderi_stream(
3006 struct vfs_handle_struct *handle,
3007 const struct smb_filename *smb_fname,
3010 struct smb_filename *stream_name = NULL;
3011 files_struct *fsp = NULL;
3016 uint8_t buf[AFP_INFO_SIZE];
3018 stream_name = synthetic_smb_fname(talloc_tos(),
3019 smb_fname->base_name,
3020 AFPINFO_STREAM_NAME,
3021 NULL, smb_fname->flags);
3022 if (stream_name == NULL) {
3026 ret = SMB_VFS_STAT(handle->conn, stream_name);
3031 status = SMB_VFS_CREATE_FILE(
3032 handle->conn, /* conn */
3034 0, /* root_dir_fid */
3035 stream_name, /* fname */
3036 FILE_READ_DATA, /* access_mask */
3037 (FILE_SHARE_READ | FILE_SHARE_WRITE | /* share_access */
3039 FILE_OPEN, /* create_disposition*/
3040 0, /* create_options */
3041 0, /* file_attributes */
3042 INTERNAL_OPEN_ONLY, /* oplock_request */
3044 0, /* allocation_size */
3045 0, /* private_flags */
3050 NULL, NULL); /* create context */
3052 TALLOC_FREE(stream_name);
3054 if (!NT_STATUS_IS_OK(status)) {
3058 nread = SMB_VFS_PREAD(fsp, &buf[0], AFP_INFO_SIZE, 0);
3059 if (nread != AFP_INFO_SIZE) {
3060 DBG_ERR("short read [%s] [%zd/%d]\n",
3061 smb_fname_str_dbg(stream_name), nread, AFP_INFO_SIZE);
3066 memcpy(&ai->afpi_FinderInfo[0], &buf[AFP_OFF_FinderInfo],
3073 close_file(NULL, fsp, NORMAL_CLOSE);
3079 static bool readdir_attr_meta_finderi_netatalk(
3080 struct vfs_handle_struct *handle,
3081 const struct smb_filename *smb_fname,
3084 struct adouble *ad = NULL;
3087 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
3092 p = ad_get_entry(ad, ADEID_FINDERI);
3094 DBG_ERR("No ADEID_FINDERI for [%s]\n", smb_fname->base_name);
3099 memcpy(&ai->afpi_FinderInfo[0], p, AFP_FinderSize);
3104 static bool readdir_attr_meta_finderi(struct vfs_handle_struct *handle,
3105 const struct smb_filename *smb_fname,
3106 struct readdir_attr_data *attr_data)
3108 struct fruit_config_data *config = NULL;
3109 uint32_t date_added;
3113 SMB_VFS_HANDLE_GET_DATA(handle, config,
3114 struct fruit_config_data,
3117 switch (config->meta) {
3118 case FRUIT_META_NETATALK:
3119 ok = readdir_attr_meta_finderi_netatalk(
3120 handle, smb_fname, &ai);
3123 case FRUIT_META_STREAM:
3124 ok = readdir_attr_meta_finderi_stream(
3125 handle, smb_fname, &ai);
3129 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3134 /* Don't bother with errors, it's likely ENOENT */
3138 if (S_ISREG(smb_fname->st.st_ex_mode)) {
3140 memcpy(&attr_data->attr_data.aapl.finder_info[0],
3141 &ai.afpi_FinderInfo[0], 4);
3143 /* finder_creator */
3144 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 4,
3145 &ai.afpi_FinderInfo[4], 4);
3149 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 8,
3150 &ai.afpi_FinderInfo[8], 2);
3152 /* finder_ext_flags */
3153 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 10,
3154 &ai.afpi_FinderInfo[24], 2);
3157 date_added = convert_time_t_to_uint32_t(
3158 smb_fname->st.st_ex_btime.tv_sec - AD_DATE_DELTA);
3160 RSIVAL(&attr_data->attr_data.aapl.finder_info[0], 12, date_added);
3165 static uint64_t readdir_attr_rfork_size_adouble(
3166 struct vfs_handle_struct *handle,
3167 const struct smb_filename *smb_fname)
3169 struct adouble *ad = NULL;
3170 uint64_t rfork_size;
3172 ad = ad_get(talloc_tos(), handle, smb_fname,
3178 rfork_size = ad_getentrylen(ad, ADEID_RFORK);
3184 static uint64_t readdir_attr_rfork_size_stream(
3185 struct vfs_handle_struct *handle,
3186 const struct smb_filename *smb_fname)
3188 struct smb_filename *stream_name = NULL;
3190 uint64_t rfork_size;
3192 stream_name = synthetic_smb_fname(talloc_tos(),
3193 smb_fname->base_name,
3194 AFPRESOURCE_STREAM_NAME,
3196 if (stream_name == NULL) {
3200 ret = SMB_VFS_STAT(handle->conn, stream_name);
3202 TALLOC_FREE(stream_name);
3206 rfork_size = stream_name->st.st_ex_size;
3207 TALLOC_FREE(stream_name);
3212 static uint64_t readdir_attr_rfork_size(struct vfs_handle_struct *handle,
3213 const struct smb_filename *smb_fname)
3215 struct fruit_config_data *config = NULL;
3216 uint64_t rfork_size;
3218 SMB_VFS_HANDLE_GET_DATA(handle, config,
3219 struct fruit_config_data,
3222 switch (config->rsrc) {
3223 case FRUIT_RSRC_ADFILE:
3224 case FRUIT_RSRC_XATTR:
3225 rfork_size = readdir_attr_rfork_size_adouble(handle,
3229 case FRUIT_META_STREAM:
3230 rfork_size = readdir_attr_rfork_size_stream(handle,
3235 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3243 static NTSTATUS readdir_attr_macmeta(struct vfs_handle_struct *handle,
3244 const struct smb_filename *smb_fname,
3245 struct readdir_attr_data *attr_data)
3247 NTSTATUS status = NT_STATUS_OK;
3248 struct fruit_config_data *config = NULL;
3251 SMB_VFS_HANDLE_GET_DATA(handle, config,
3252 struct fruit_config_data,
3253 return NT_STATUS_UNSUCCESSFUL);
3256 /* Ensure we return a default value in the creation_date field */
3257 RSIVAL(&attr_data->attr_data.aapl.finder_info, 12, AD_DATE_START);
3260 * Resource fork length
3263 if (config->readdir_attr_rsize) {
3264 uint64_t rfork_size;
3266 rfork_size = readdir_attr_rfork_size(handle, smb_fname);
3267 attr_data->attr_data.aapl.rfork_size = rfork_size;
3274 if (config->readdir_attr_finder_info) {
3275 ok = readdir_attr_meta_finderi(handle, smb_fname, attr_data);
3277 status = NT_STATUS_INTERNAL_ERROR;
3284 static NTSTATUS remove_virtual_nfs_aces(struct security_descriptor *psd)
3289 if (psd->dacl == NULL) {
3290 return NT_STATUS_OK;
3293 for (i = 0; i < psd->dacl->num_aces; i++) {
3294 /* MS NFS style mode/uid/gid */
3295 int cmp = dom_sid_compare_domain(
3296 &global_sid_Unix_NFS,
3297 &psd->dacl->aces[i].trustee);
3299 /* Normal ACE entry. */
3304 * security_descriptor_dacl_del()
3305 * *must* return NT_STATUS_OK as we know
3306 * we have something to remove.
3309 status = security_descriptor_dacl_del(psd,
3310 &psd->dacl->aces[i].trustee);
3311 if (!NT_STATUS_IS_OK(status)) {
3312 DBG_WARNING("failed to remove MS NFS style ACE: %s\n",
3318 * security_descriptor_dacl_del() may delete more
3319 * then one entry subsequent to this one if the
3320 * SID matches, but we only need to ensure that
3321 * we stay looking at the same element in the array.
3325 return NT_STATUS_OK;
3328 /* Search MS NFS style ACE with UNIX mode */
3329 static NTSTATUS check_ms_nfs(vfs_handle_struct *handle,
3331 struct security_descriptor *psd,
3336 struct fruit_config_data *config = NULL;
3340 SMB_VFS_HANDLE_GET_DATA(handle, config,
3341 struct fruit_config_data,
3342 return NT_STATUS_UNSUCCESSFUL);
3344 if (!global_fruit_config.nego_aapl) {
3345 return NT_STATUS_OK;
3347 if (psd->dacl == NULL || !config->unix_info_enabled) {
3348 return NT_STATUS_OK;
3351 for (i = 0; i < psd->dacl->num_aces; i++) {
3352 if (dom_sid_compare_domain(
3353 &global_sid_Unix_NFS_Mode,
3354 &psd->dacl->aces[i].trustee) == 0) {
3355 *pmode = (mode_t)psd->dacl->aces[i].trustee.sub_auths[2];
3356 *pmode &= (S_IRWXU | S_IRWXG | S_IRWXO);
3359 DEBUG(10, ("MS NFS chmod request %s, %04o\n",
3360 fsp_str_dbg(fsp), (unsigned)(*pmode)));
3366 * Remove any incoming virtual ACE entries generated by
3367 * fruit_fget_nt_acl().
3370 return remove_virtual_nfs_aces(psd);
3373 /****************************************************************************
3375 ****************************************************************************/
3377 static int fruit_connect(vfs_handle_struct *handle,
3378 const char *service,
3382 char *list = NULL, *newlist = NULL;
3383 struct fruit_config_data *config;
3385 DEBUG(10, ("fruit_connect\n"));
3387 rc = SMB_VFS_NEXT_CONNECT(handle, service, user);
3392 rc = init_fruit_config(handle);
3397 SMB_VFS_HANDLE_GET_DATA(handle, config,
3398 struct fruit_config_data, return -1);
3400 if (config->veto_appledouble) {
3401 list = lp_veto_files(talloc_tos(), SNUM(handle->conn));
3404 if (strstr(list, "/" ADOUBLE_NAME_PREFIX "*/") == NULL) {
3405 newlist = talloc_asprintf(
3407 "%s/" ADOUBLE_NAME_PREFIX "*/",
3409 lp_do_parameter(SNUM(handle->conn),
3414 lp_do_parameter(SNUM(handle->conn),
3416 "/" ADOUBLE_NAME_PREFIX "*/");
3422 if (config->encoding == FRUIT_ENC_NATIVE) {
3423 lp_do_parameter(SNUM(handle->conn),
3428 if (config->time_machine) {
3429 DBG_NOTICE("Enabling durable handles for Time Machine "
3430 "support on [%s]\n", service);
3431 lp_do_parameter(SNUM(handle->conn), "durable handles", "yes");
3432 lp_do_parameter(SNUM(handle->conn), "kernel oplocks", "no");
3433 lp_do_parameter(SNUM(handle->conn), "kernel share modes", "no");
3434 if (!lp_strict_sync(SNUM(handle->conn))) {
3435 DBG_WARNING("Time Machine without strict sync is not "
3438 lp_do_parameter(SNUM(handle->conn), "posix locking", "no");
3444 static int fruit_fake_fd(void)
3451 * Return a valid fd, but ensure any attempt to use it returns
3452 * an error (EPIPE). Once we get a write on the handle, we open
3455 ret = pipe(pipe_fds);
3465 static int fruit_open_meta_stream(vfs_handle_struct *handle,
3466 struct smb_filename *smb_fname,
3471 struct fruit_config_data *config = NULL;
3472 struct fio *fio = NULL;
3473 int open_flags = flags & ~O_CREAT;
3476 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3478 SMB_VFS_HANDLE_GET_DATA(handle, config,
3479 struct fruit_config_data, return -1);
3481 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3482 fio->type = ADOUBLE_META;
3483 fio->config = config;
3485 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, open_flags, mode);
3490 if (!(flags & O_CREAT)) {
3491 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
3495 fd = fruit_fake_fd();
3497 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
3501 fio->fake_fd = true;
3508 static int fruit_open_meta_netatalk(vfs_handle_struct *handle,
3509 struct smb_filename *smb_fname,
3514 struct fruit_config_data *config = NULL;
3515 struct fio *fio = NULL;
3516 struct adouble *ad = NULL;
3517 bool meta_exists = false;
3520 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3522 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
3529 if (!meta_exists && !(flags & O_CREAT)) {
3534 fd = fruit_fake_fd();
3539 SMB_VFS_HANDLE_GET_DATA(handle, config,
3540 struct fruit_config_data, return -1);
3542 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3543 fio->type = ADOUBLE_META;
3544 fio->config = config;
3545 fio->fake_fd = true;
3552 static int fruit_open_meta(vfs_handle_struct *handle,
3553 struct smb_filename *smb_fname,
3554 files_struct *fsp, int flags, mode_t mode)
3557 struct fruit_config_data *config = NULL;
3559 DBG_DEBUG("path [%s]\n", smb_fname_str_dbg(smb_fname));
3561 SMB_VFS_HANDLE_GET_DATA(handle, config,
3562 struct fruit_config_data, return -1);
3564 switch (config->meta) {
3565 case FRUIT_META_STREAM:
3566 fd = fruit_open_meta_stream(handle, smb_fname,
3570 case FRUIT_META_NETATALK:
3571 fd = fruit_open_meta_netatalk(handle, smb_fname,
3576 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3580 DBG_DEBUG("path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3585 static int fruit_open_rsrc_adouble(vfs_handle_struct *handle,
3586 struct smb_filename *smb_fname,
3592 struct adouble *ad = NULL;
3593 struct smb_filename *smb_fname_base = NULL;
3594 struct fruit_config_data *config = NULL;
3597 SMB_VFS_HANDLE_GET_DATA(handle, config,
3598 struct fruit_config_data, return -1);
3600 if ((!(flags & O_CREAT)) &&
3601 S_ISDIR(fsp->base_fsp->fsp_name->st.st_ex_mode))
3603 /* sorry, but directories don't habe a resource fork */
3608 rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_base);
3613 /* We always need read/write access for the metadata header too */
3614 flags &= ~(O_RDONLY | O_WRONLY);
3617 hostfd = SMB_VFS_NEXT_OPEN(handle, smb_fname_base, fsp,
3624 if (flags & (O_CREAT | O_TRUNC)) {
3625 ad = ad_init(fsp, handle, ADOUBLE_RSRC);
3631 fsp->fh->fd = hostfd;
3633 rc = ad_fset(handle, ad, fsp);
3644 TALLOC_FREE(smb_fname_base);
3646 DEBUG(10, ("fruit_open resource fork: rc=%d, fd=%d\n", rc, hostfd));
3648 int saved_errno = errno;
3651 * BUGBUGBUG -- we would need to call
3652 * fd_close_posix here, but we don't have a
3655 fsp->fh->fd = hostfd;
3659 errno = saved_errno;
3664 static int fruit_open_rsrc_xattr(vfs_handle_struct *handle,
3665 struct smb_filename *smb_fname,
3670 #ifdef HAVE_ATTROPEN
3673 fd = attropen(smb_fname->base_name,
3674 AFPRESOURCE_EA_NETATALK,
3689 static int fruit_open_rsrc(vfs_handle_struct *handle,
3690 struct smb_filename *smb_fname,
3691 files_struct *fsp, int flags, mode_t mode)
3694 struct fruit_config_data *config = NULL;
3695 struct fio *fio = NULL;
3697 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3699 SMB_VFS_HANDLE_GET_DATA(handle, config,
3700 struct fruit_config_data, return -1);
3702 switch (config->rsrc) {
3703 case FRUIT_RSRC_STREAM:
3704 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3707 case FRUIT_RSRC_ADFILE:
3708 fd = fruit_open_rsrc_adouble(handle, smb_fname,
3712 case FRUIT_RSRC_XATTR:
3713 fd = fruit_open_rsrc_xattr(handle, smb_fname,
3718 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3722 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3728 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3729 fio->type = ADOUBLE_RSRC;
3730 fio->config = config;
3735 static int fruit_open(vfs_handle_struct *handle,
3736 struct smb_filename *smb_fname,
3737 files_struct *fsp, int flags, mode_t mode)
3741 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3743 if (!is_ntfs_stream_smb_fname(smb_fname)) {
3744 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3747 if (is_afpinfo_stream(smb_fname)) {
3748 fd = fruit_open_meta(handle, smb_fname, fsp, flags, mode);
3749 } else if (is_afpresource_stream(smb_fname)) {
3750 fd = fruit_open_rsrc(handle, smb_fname, fsp, flags, mode);
3752 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3755 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3760 static int fruit_close_meta(vfs_handle_struct *handle,
3764 struct fruit_config_data *config = NULL;
3766 SMB_VFS_HANDLE_GET_DATA(handle, config,
3767 struct fruit_config_data, return -1);
3769 switch (config->meta) {
3770 case FRUIT_META_STREAM:
3771 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3774 case FRUIT_META_NETATALK:
3775 ret = close(fsp->fh->fd);
3780 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3788 static int fruit_close_rsrc(vfs_handle_struct *handle,
3792 struct fruit_config_data *config = NULL;
3794 SMB_VFS_HANDLE_GET_DATA(handle, config,
3795 struct fruit_config_data, return -1);
3797 switch (config->rsrc) {
3798 case FRUIT_RSRC_STREAM:
3799 case FRUIT_RSRC_ADFILE:
3800 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3803 case FRUIT_RSRC_XATTR:
3804 ret = close(fsp->fh->fd);
3809 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3816 static int fruit_close(vfs_handle_struct *handle,
3824 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(fsp->fsp_name), fd);
3826 if (!is_ntfs_stream_smb_fname(fsp->fsp_name)) {
3827 return SMB_VFS_NEXT_CLOSE(handle, fsp);
3830 if (is_afpinfo_stream(fsp->fsp_name)) {
3831 ret = fruit_close_meta(handle, fsp);
3832 } else if (is_afpresource_stream(fsp->fsp_name)) {
3833 ret = fruit_close_rsrc(handle, fsp);
3835 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3841 static int fruit_rename(struct vfs_handle_struct *handle,
3842 const struct smb_filename *smb_fname_src,
3843 const struct smb_filename *smb_fname_dst)
3846 struct fruit_config_data *config = NULL;
3847 struct smb_filename *src_adp_smb_fname = NULL;
3848 struct smb_filename *dst_adp_smb_fname = NULL;
3850 SMB_VFS_HANDLE_GET_DATA(handle, config,
3851 struct fruit_config_data, return -1);
3853 if (!VALID_STAT(smb_fname_src->st)) {
3854 DBG_ERR("Need valid stat for [%s]\n",
3855 smb_fname_str_dbg(smb_fname_src));
3859 rc = SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
3864 if ((config->rsrc != FRUIT_RSRC_ADFILE) ||
3865 (!S_ISREG(smb_fname_src->st.st_ex_mode)))
3870 rc = adouble_path(talloc_tos(), smb_fname_src, &src_adp_smb_fname);
3875 rc = adouble_path(talloc_tos(), smb_fname_dst, &dst_adp_smb_fname);
3880 DBG_DEBUG("%s -> %s\n",
3881 smb_fname_str_dbg(src_adp_smb_fname),
3882 smb_fname_str_dbg(dst_adp_smb_fname));
3884 rc = SMB_VFS_NEXT_RENAME(handle, src_adp_smb_fname, dst_adp_smb_fname);
3885 if (errno == ENOENT) {
3890 TALLOC_FREE(src_adp_smb_fname);
3891 TALLOC_FREE(dst_adp_smb_fname);
3895 static int fruit_unlink_meta_stream(vfs_handle_struct *handle,
3896 const struct smb_filename *smb_fname)
3898 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3901 static int fruit_unlink_meta_netatalk(vfs_handle_struct *handle,
3902 const struct smb_filename *smb_fname)
3904 return SMB_VFS_REMOVEXATTR(handle->conn,
3906 AFPINFO_EA_NETATALK);
3909 static int fruit_unlink_meta(vfs_handle_struct *handle,
3910 const struct smb_filename *smb_fname)
3912 struct fruit_config_data *config = NULL;
3915 SMB_VFS_HANDLE_GET_DATA(handle, config,
3916 struct fruit_config_data, return -1);
3918 switch (config->meta) {
3919 case FRUIT_META_STREAM:
3920 rc = fruit_unlink_meta_stream(handle, smb_fname);
3923 case FRUIT_META_NETATALK:
3924 rc = fruit_unlink_meta_netatalk(handle, smb_fname);
3928 DBG_ERR("Unsupported meta config [%d]\n", config->meta);
3935 static int fruit_unlink_rsrc_stream(vfs_handle_struct *handle,
3936 const struct smb_filename *smb_fname,
3941 if (!force_unlink) {
3942 struct smb_filename *smb_fname_cp = NULL;
3945 smb_fname_cp = cp_smb_filename(talloc_tos(), smb_fname);
3946 if (smb_fname_cp == NULL) {
3951 * 0 byte resource fork streams are not listed by
3952 * vfs_streaminfo, as a result stream cleanup/deletion of file
3953 * deletion doesn't remove the resourcefork stream.
3956 ret = SMB_VFS_NEXT_STAT(handle, smb_fname_cp);
3958 TALLOC_FREE(smb_fname_cp);
3959 DBG_ERR("stat [%s] failed [%s]\n",
3960 smb_fname_str_dbg(smb_fname_cp), strerror(errno));
3964 size = smb_fname_cp->st.st_ex_size;
3965 TALLOC_FREE(smb_fname_cp);
3968 /* OS X ignores resource fork stream delete requests */
3973 ret = SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3974 if ((ret != 0) && (errno == ENOENT) && force_unlink) {
3981 static int fruit_unlink_rsrc_adouble(vfs_handle_struct *handle,
3982 const struct smb_filename *smb_fname,
3986 struct adouble *ad = NULL;
3987 struct smb_filename *adp_smb_fname = NULL;
3989 if (!force_unlink) {
3990 ad = ad_get(talloc_tos(), handle, smb_fname,
3999 * 0 byte resource fork streams are not listed by
4000 * vfs_streaminfo, as a result stream cleanup/deletion of file
4001 * deletion doesn't remove the resourcefork stream.
4004 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
4005 /* OS X ignores resource fork stream delete requests */
4013 rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
4018 rc = SMB_VFS_NEXT_UNLINK(handle, adp_smb_fname);
4019 TALLOC_FREE(adp_smb_fname);
4020 if ((rc != 0) && (errno == ENOENT) && force_unlink) {
4027 static int fruit_unlink_rsrc_xattr(vfs_handle_struct *handle,
4028 const struct smb_filename *smb_fname,
4032 * OS X ignores resource fork stream delete requests, so nothing to do
4033 * here. Removing the file will remove the xattr anyway, so we don't
4034 * have to take care of removing 0 byte resource forks that could be
4040 static int fruit_unlink_rsrc(vfs_handle_struct *handle,
4041 const struct smb_filename *smb_fname,
4044 struct fruit_config_data *config = NULL;
4047 SMB_VFS_HANDLE_GET_DATA(handle, config,
4048 struct fruit_config_data, return -1);
4050 switch (config->rsrc) {
4051 case FRUIT_RSRC_STREAM:
4052 rc = fruit_unlink_rsrc_stream(handle, smb_fname, force_unlink);
4055 case FRUIT_RSRC_ADFILE:
4056 rc = fruit_unlink_rsrc_adouble(handle, smb_fname, force_unlink);
4059 case FRUIT_RSRC_XATTR:
4060 rc = fruit_unlink_rsrc_xattr(handle, smb_fname, force_unlink);
4064 DBG_ERR("Unsupported rsrc config [%d]\n", config->rsrc);
4071 static int fruit_unlink(vfs_handle_struct *handle,
4072 const struct smb_filename *smb_fname)
4075 struct fruit_config_data *config = NULL;
4076 struct smb_filename *rsrc_smb_fname = NULL;
4078 SMB_VFS_HANDLE_GET_DATA(handle, config,
4079 struct fruit_config_data, return -1);
4081 if (is_afpinfo_stream(smb_fname)) {
4082 return fruit_unlink_meta(handle, smb_fname);
4083 } else if (is_afpresource_stream(smb_fname)) {
4084 return fruit_unlink_rsrc(handle, smb_fname, false);
4085 } if (is_ntfs_stream_smb_fname(smb_fname)) {
4086 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4090 * A request to delete the base file. Because 0 byte resource
4091 * fork streams are not listed by fruit_streaminfo,
4092 * delete_all_streams() can't remove 0 byte resource fork
4093 * streams, so we have to cleanup this here.
4095 rsrc_smb_fname = synthetic_smb_fname(talloc_tos(),
4096 smb_fname->base_name,
4097 AFPRESOURCE_STREAM_NAME,
4100 if (rsrc_smb_fname == NULL) {
4104 rc = fruit_unlink_rsrc(handle, rsrc_smb_fname, true);
4105 if ((rc != 0) && (errno != ENOENT)) {
4106 DBG_ERR("Forced unlink of [%s] failed [%s]\n",
4107 smb_fname_str_dbg(rsrc_smb_fname), strerror(errno));
4108 TALLOC_FREE(rsrc_smb_fname);
4111 TALLOC_FREE(rsrc_smb_fname);
4113 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4116 static int fruit_chmod(vfs_handle_struct *handle,
4117 const struct smb_filename *smb_fname,
4121 struct fruit_config_data *config = NULL;
4122 struct smb_filename *smb_fname_adp = NULL;
4124 rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname, mode);
4129 SMB_VFS_HANDLE_GET_DATA(handle, config,
4130 struct fruit_config_data, return -1);
4132 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4136 if (!VALID_STAT(smb_fname->st)) {
4140 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
4144 rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_adp);
4149 DEBUG(10, ("fruit_chmod: %s\n", smb_fname_adp->base_name));
4151 rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname_adp, mode);
4152 if (errno == ENOENT) {
4156 TALLOC_FREE(smb_fname_adp);
4160 static int fruit_chown(vfs_handle_struct *handle,
4161 const struct smb_filename *smb_fname,
4166 struct fruit_config_data *config = NULL;
4167 struct smb_filename *adp_smb_fname = NULL;
4169 rc = SMB_VFS_NEXT_CHOWN(handle, smb_fname, uid, gid);
4174 SMB_VFS_HANDLE_GET_DATA(handle, config,
4175 struct fruit_config_data, return -1);
4177 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4181 if (!VALID_STAT(smb_fname->st)) {
4185 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
4189 rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
4194 DEBUG(10, ("fruit_chown: %s\n", adp_smb_fname->base_name));
4196 rc = SMB_VFS_NEXT_CHOWN(handle, adp_smb_fname, uid, gid);
4197 if (errno == ENOENT) {
4202 TALLOC_FREE(adp_smb_fname);
4206 static int fruit_rmdir(struct vfs_handle_struct *handle,
4207 const struct smb_filename *smb_fname)
4211 struct fruit_config_data *config;
4213 SMB_VFS_HANDLE_GET_DATA(handle, config,
4214 struct fruit_config_data, return -1);
4216 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4221 * Due to there is no way to change bDeleteVetoFiles variable
4222 * from this module, need to clean up ourselves
4225 dh = SMB_VFS_OPENDIR(handle->conn, smb_fname, NULL, 0);
4230 while ((de = SMB_VFS_READDIR(handle->conn, dh, NULL)) != NULL) {
4232 struct adouble *ad = NULL;
4234 struct smb_filename *ad_smb_fname = NULL;
4237 match = strncmp(de->d_name,
4238 ADOUBLE_NAME_PREFIX,
4239 strlen(ADOUBLE_NAME_PREFIX));
4244 p = talloc_asprintf(talloc_tos(), "%s/%s",
4245 smb_fname->base_name, de->d_name);
4247 DBG_ERR("talloc_asprintf failed\n");
4251 ad_smb_fname = synthetic_smb_fname(talloc_tos(), p,
4255 if (ad_smb_fname == NULL) {
4256 DBG_ERR("synthetic_smb_fname failed\n");
4261 * Check whether it's a valid AppleDouble file, if
4262 * yes, delete it, ignore it otherwise.
4264 ad = ad_get(talloc_tos(), handle, ad_smb_fname, ADOUBLE_RSRC);
4266 TALLOC_FREE(ad_smb_fname);
4272 ret = SMB_VFS_NEXT_UNLINK(handle, ad_smb_fname);
4274 DBG_ERR("Deleting [%s] failed\n",
4275 smb_fname_str_dbg(ad_smb_fname));
4277 TALLOC_FREE(ad_smb_fname);
4282 SMB_VFS_CLOSEDIR(handle->conn, dh);
4284 return SMB_VFS_NEXT_RMDIR(handle, smb_fname);
4287 static ssize_t fruit_pread_meta_stream(vfs_handle_struct *handle,
4288 files_struct *fsp, void *data,
4289 size_t n, off_t offset)
4294 nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4295 if (nread == -1 || nread == n) {
4299 DBG_ERR("Removing [%s] after short read [%zd]\n",
4300 fsp_str_dbg(fsp), nread);
4302 ret = SMB_VFS_NEXT_UNLINK(handle, fsp->fsp_name);
4304 DBG_ERR("Removing [%s] failed\n", fsp_str_dbg(fsp));
4312 static ssize_t fruit_pread_meta_adouble(vfs_handle_struct *handle,
4313 files_struct *fsp, void *data,
4314 size_t n, off_t offset)
4317 struct adouble *ad = NULL;
4318 char afpinfo_buf[AFP_INFO_SIZE];
4322 ai = afpinfo_new(talloc_tos());
4327 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
4333 p = ad_get_entry(ad, ADEID_FINDERI);
4335 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
4340 memcpy(&ai->afpi_FinderInfo[0], p, ADEDLEN_FINDERI);
4342 nread = afpinfo_pack(ai, afpinfo_buf);
4343 if (nread != AFP_INFO_SIZE) {
4348 memcpy(data, afpinfo_buf, n);
4356 static ssize_t fruit_pread_meta(vfs_handle_struct *handle,
4357 files_struct *fsp, void *data,
4358 size_t n, off_t offset)
4360 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4365 * OS X has a off-by-1 error in the offset calculation, so we're
4366 * bug compatible here. It won't hurt, as any relevant real
4367 * world read requests from the AFP_AfpInfo stream will be
4368 * offset=0 n=60. offset is ignored anyway, see below.
4370 if ((offset < 0) || (offset >= AFP_INFO_SIZE + 1)) {
4375 DBG_ERR("Failed to fetch fsp extension");
4379 /* Yes, macOS always reads from offset 0 */
4381 to_return = MIN(n, AFP_INFO_SIZE);
4383 switch (fio->config->meta) {
4384 case FRUIT_META_STREAM:
4385 nread = fruit_pread_meta_stream(handle, fsp, data,
4389 case FRUIT_META_NETATALK:
4390 nread = fruit_pread_meta_adouble(handle, fsp, data,
4395 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
4399 if (nread == -1 && fio->created) {
4401 char afpinfo_buf[AFP_INFO_SIZE];
4403 ai = afpinfo_new(talloc_tos());
4408 nread = afpinfo_pack(ai, afpinfo_buf);
4410 if (nread != AFP_INFO_SIZE) {
4414 memcpy(data, afpinfo_buf, to_return);
4421 static ssize_t fruit_pread_rsrc_stream(vfs_handle_struct *handle,
4422 files_struct *fsp, void *data,
4423 size_t n, off_t offset)
4425 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4428 static ssize_t fruit_pread_rsrc_xattr(vfs_handle_struct *handle,
4429 files_struct *fsp, void *data,
4430 size_t n, off_t offset)
4432 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4435 static ssize_t fruit_pread_rsrc_adouble(vfs_handle_struct *handle,
4436 files_struct *fsp, void *data,
4437 size_t n, off_t offset)
4439 struct adouble *ad = NULL;
4442 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
4447 nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n,
4448 offset + ad_getentryoff(ad, ADEID_RFORK));
4454 static ssize_t fruit_pread_rsrc(vfs_handle_struct *handle,
4455 files_struct *fsp, void *data,
4456 size_t n, off_t offset)
4458 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4466 switch (fio->config->rsrc) {
4467 case FRUIT_RSRC_STREAM:
4468 nread = fruit_pread_rsrc_stream(handle, fsp, data, n, offset);
4471 case FRUIT_RSRC_ADFILE:
4472 nread = fruit_pread_rsrc_adouble(handle, fsp, data, n, offset);
4475 case FRUIT_RSRC_XATTR:
4476 nread = fruit_pread_rsrc_xattr(handle, fsp, data, n, offset);
4480 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
4487 static ssize_t fruit_pread(vfs_handle_struct *handle,
4488 files_struct *fsp, void *data,
4489 size_t n, off_t offset)
4491 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4494 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4495 fsp_str_dbg(fsp), (intmax_t)offset, n);
4498 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4501 if (fio->type == ADOUBLE_META) {
4502 nread = fruit_pread_meta(handle, fsp, data, n, offset);
4504 nread = fruit_pread_rsrc(handle, fsp, data, n, offset);
4507 DBG_DEBUG("Path [%s] nread [%zd]\n", fsp_str_dbg(fsp), nread);
4511 static bool fruit_must_handle_aio_stream(struct fio *fio)
4517 if (fio->type == ADOUBLE_META) {
4521 if ((fio->type == ADOUBLE_RSRC) &&
4522 (fio->config->rsrc == FRUIT_RSRC_ADFILE))
4530 struct fruit_pread_state {
4532 struct vfs_aio_state vfs_aio_state;
4535 static void fruit_pread_done(struct tevent_req *subreq);
4537 static struct tevent_req *fruit_pread_send(
4538 struct vfs_handle_struct *handle,
4539 TALLOC_CTX *mem_ctx,
4540 struct tevent_context *ev,
4541 struct files_struct *fsp,
4543 size_t n, off_t offset)
4545 struct tevent_req *req = NULL;
4546 struct tevent_req *subreq = NULL;
4547 struct fruit_pread_state *state = NULL;
4548 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4550 req = tevent_req_create(mem_ctx, &state,
4551 struct fruit_pread_state);
4556 if (fruit_must_handle_aio_stream(fio)) {
4557 state->nread = SMB_VFS_PREAD(fsp, data, n, offset);
4558 if (state->nread != n) {
4559 if (state->nread != -1) {
4562 tevent_req_error(req, errno);
4563 return tevent_req_post(req, ev);
4565 tevent_req_done(req);
4566 return tevent_req_post(req, ev);
4569 subreq = SMB_VFS_NEXT_PREAD_SEND(state, ev, handle, fsp,
4571 if (tevent_req_nomem(req, subreq)) {
4572 return tevent_req_post(req, ev);
4574 tevent_req_set_callback(subreq, fruit_pread_done, req);
4578 static void fruit_pread_done(struct tevent_req *subreq)
4580 struct tevent_req *req = tevent_req_callback_data(
4581 subreq, struct tevent_req);
4582 struct fruit_pread_state *state = tevent_req_data(
4583 req, struct fruit_pread_state);
4585 state->nread = SMB_VFS_PREAD_RECV(subreq, &state->vfs_aio_state);
4586 TALLOC_FREE(subreq);
4588 if (tevent_req_error(req, state->vfs_aio_state.error)) {
4591 tevent_req_done(req);
4594 static ssize_t fruit_pread_recv(struct tevent_req *req,
4595 struct vfs_aio_state *vfs_aio_state)
4597 struct fruit_pread_state *state = tevent_req_data(
4598 req, struct fruit_pread_state);
4600 if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
4604 *vfs_aio_state = state->vfs_aio_state;
4605 return state->nread;
4608 static ssize_t fruit_pwrite_meta_stream(vfs_handle_struct *handle,
4609 files_struct *fsp, const void *data,
4610 size_t n, off_t offset)
4612 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4618 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4619 fsp_str_dbg(fsp), (intmax_t)offset, n);
4628 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
4630 DBG_ERR("Close [%s] failed: %s\n",
4631 fsp_str_dbg(fsp), strerror(errno));
4636 fd = SMB_VFS_NEXT_OPEN(handle,
4642 DBG_ERR("On-demand create [%s] in write failed: %s\n",
4643 fsp_str_dbg(fsp), strerror(errno));
4647 fio->fake_fd = false;
4650 ai = afpinfo_unpack(talloc_tos(), data);
4655 if (ai_empty_finderinfo(ai)) {
4657 * Writing an all 0 blob to the metadata stream results in the
4658 * stream being removed on a macOS server. This ensures we
4659 * behave the same and it verified by the "delete AFP_AfpInfo by
4660 * writing all 0" test.
4662 ret = SMB_VFS_NEXT_FTRUNCATE(handle, fsp, 0);
4664 DBG_ERR("SMB_VFS_NEXT_FTRUNCATE on [%s] failed\n",
4669 ok = set_delete_on_close(
4672 handle->conn->session_info->security_token,
4673 handle->conn->session_info->unix_token);
4675 DBG_ERR("set_delete_on_close on [%s] failed\n",
4682 nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4683 if (nwritten != n) {
4690 static ssize_t fruit_pwrite_meta_netatalk(vfs_handle_struct *handle,
4691 files_struct *fsp, const void *data,
4692 size_t n, off_t offset)
4694 struct adouble *ad = NULL;
4700 ai = afpinfo_unpack(talloc_tos(), data);
4705 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
4707 ad = ad_init(talloc_tos(), handle, ADOUBLE_META);
4712 p = ad_get_entry(ad, ADEID_FINDERI);
4714 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
4719 memcpy(p, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
4721 ret = ad_fset(handle, ad, fsp);
4723 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
4730 if (!ai_empty_finderinfo(ai)) {
4735 * Writing an all 0 blob to the metadata stream results in the stream
4736 * being removed on a macOS server. This ensures we behave the same and
4737 * it verified by the "delete AFP_AfpInfo by writing all 0" test.
4740 ok = set_delete_on_close(
4743 handle->conn->session_info->security_token,
4744 handle->conn->session_info->unix_token);
4746 DBG_ERR("set_delete_on_close on [%s] failed\n",
4754 static ssize_t fruit_pwrite_meta(vfs_handle_struct *handle,
4755 files_struct *fsp, const void *data,
4756 size_t n, off_t offset)
4758 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4760 uint8_t buf[AFP_INFO_SIZE];
4766 DBG_ERR("Failed to fetch fsp extension");
4775 if (offset != 0 && n < 60) {
4780 cmp = memcmp(data, "AFP", 3);
4786 if (n <= AFP_OFF_FinderInfo) {
4788 * Nothing to do here really, just return
4796 if (to_copy > AFP_INFO_SIZE) {
4797 to_copy = AFP_INFO_SIZE;
4799 memcpy(buf, data, to_copy);
4802 if (to_write != AFP_INFO_SIZE) {
4803 to_write = AFP_INFO_SIZE;
4806 switch (fio->config->meta) {
4807 case FRUIT_META_STREAM:
4808 nwritten = fruit_pwrite_meta_stream(handle,
4815 case FRUIT_META_NETATALK:
4816 nwritten = fruit_pwrite_meta_netatalk(handle,
4824 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
4828 if (nwritten != to_write) {
4833 * Return the requested amount, verified against macOS SMB server
4838 static ssize_t fruit_pwrite_rsrc_stream(vfs_handle_struct *handle,
4839 files_struct *fsp, const void *data,
4840 size_t n, off_t offset)
4842 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4845 static ssize_t fruit_pwrite_rsrc_xattr(vfs_handle_struct *handle,
4846 files_struct *fsp, const void *data,
4847 size_t n, off_t offset)
4849 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4852 static ssize_t fruit_pwrite_rsrc_adouble(vfs_handle_struct *handle,
4853 files_struct *fsp, const void *data,
4854 size_t n, off_t offset)
4856 struct adouble *ad = NULL;
4860 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
4862 DBG_ERR("ad_get [%s] failed\n", fsp_str_dbg(fsp));
4866 nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n,
4867 offset + ad_getentryoff(ad, ADEID_RFORK));
4868 if (nwritten != n) {
4869 DBG_ERR("Short write on [%s] [%zd/%zd]\n",
4870 fsp_str_dbg(fsp), nwritten, n);
4875 if ((n + offset) > ad_getentrylen(ad, ADEID_RFORK)) {
4876 ad_setentrylen(ad, ADEID_RFORK, n + offset);
4877 ret = ad_fset(handle, ad, fsp);
4879 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
4889 static ssize_t fruit_pwrite_rsrc(vfs_handle_struct *handle,
4890 files_struct *fsp, const void *data,
4891 size_t n, off_t offset)
4893 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4897 DBG_ERR("Failed to fetch fsp extension");
4901 switch (fio->config->rsrc) {
4902 case FRUIT_RSRC_STREAM:
4903 nwritten = fruit_pwrite_rsrc_stream(handle, fsp, data, n, offset);
4906 case FRUIT_RSRC_ADFILE:
4907 nwritten = fruit_pwrite_rsrc_adouble(handle, fsp, data, n, offset);
4910 case FRUIT_RSRC_XATTR:
4911 nwritten = fruit_pwrite_rsrc_xattr(handle, fsp, data, n, offset);
4915 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
4922 static ssize_t fruit_pwrite(vfs_handle_struct *handle,
4923 files_struct *fsp, const void *data,
4924 size_t n, off_t offset)
4926 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4929 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4930 fsp_str_dbg(fsp), (intmax_t)offset, n);
4933 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4936 if (fio->type == ADOUBLE_META) {
4937 nwritten = fruit_pwrite_meta(handle, fsp, data, n, offset);
4939 nwritten = fruit_pwrite_rsrc(handle, fsp, data, n, offset);
4942 DBG_DEBUG("Path [%s] nwritten=%zd\n", fsp_str_dbg(fsp), nwritten);
4946 struct fruit_pwrite_state {
4948 struct vfs_aio_state vfs_aio_state;
4951 static void fruit_pwrite_done(struct tevent_req *subreq);
4953 static struct tevent_req *fruit_pwrite_send(
4954 struct vfs_handle_struct *handle,
4955 TALLOC_CTX *mem_ctx,
4956 struct tevent_context *ev,
4957 struct files_struct *fsp,
4959 size_t n, off_t offset)
4961 struct tevent_req *req = NULL;
4962 struct tevent_req *subreq = NULL;
4963 struct fruit_pwrite_state *state = NULL;
4964 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4966 req = tevent_req_create(mem_ctx, &state,
4967 struct fruit_pwrite_state);
4972 if (fruit_must_handle_aio_stream(fio)) {
4973 state->nwritten = SMB_VFS_PWRITE(fsp, data, n, offset);
4974 if (state->nwritten != n) {
4975 if (state->nwritten != -1) {
4978 tevent_req_error(req, errno);
4979 return tevent_req_post(req, ev);
4981 tevent_req_done(req);
4982 return tevent_req_post(req, ev);
4985 subreq = SMB_VFS_NEXT_PWRITE_SEND(state, ev, handle, fsp,
4987 if (tevent_req_nomem(req, subreq)) {
4988 return tevent_req_post(req, ev);
4990 tevent_req_set_callback(subreq, fruit_pwrite_done, req);
4994 static void fruit_pwrite_done(struct tevent_req *subreq)
4996 struct tevent_req *req = tevent_req_callback_data(
4997 subreq, struct tevent_req);
4998 struct fruit_pwrite_state *state = tevent_req_data(
4999 req, struct fruit_pwrite_state);
5001 state->nwritten = SMB_VFS_PWRITE_RECV(subreq, &state->vfs_aio_state);
5002 TALLOC_FREE(subreq);
5004 if (tevent_req_error(req, state->vfs_aio_state.error)) {
5007 tevent_req_done(req);
5010 static ssize_t fruit_pwrite_recv(struct tevent_req *req,
5011 struct vfs_aio_state *vfs_aio_state)
5013 struct fruit_pwrite_state *state = tevent_req_data(
5014 req, struct fruit_pwrite_state);
5016 if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
5020 *vfs_aio_state = state->vfs_aio_state;
5021 return state->nwritten;
5025 * Helper to stat/lstat the base file of an smb_fname.
5027 static int fruit_stat_base(vfs_handle_struct *handle,
5028 struct smb_filename *smb_fname,
5031 char *tmp_stream_name;
5034 tmp_stream_name = smb_fname->stream_name;
5035 smb_fname->stream_name = NULL;
5037 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
5039 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5041 smb_fname->stream_name = tmp_stream_name;
5043 DBG_DEBUG("fruit_stat_base [%s] dev [%ju] ino [%ju]\n",
5044 smb_fname->base_name,
5045 (uintmax_t)smb_fname->st.st_ex_dev,
5046 (uintmax_t)smb_fname->st.st_ex_ino);
5050 static int fruit_stat_meta_stream(vfs_handle_struct *handle,
5051 struct smb_filename *smb_fname,
5057 ret = fruit_stat_base(handle, smb_fname, false);
5062 ino = fruit_inode(&smb_fname->st, smb_fname->stream_name);
5065 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
5067 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5070 smb_fname->st.st_ex_ino = ino;
5075 static int fruit_stat_meta_netatalk(vfs_handle_struct *handle,
5076 struct smb_filename *smb_fname,
5079 struct adouble *ad = NULL;
5081 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5083 DBG_INFO("fruit_stat_meta %s: %s\n",
5084 smb_fname_str_dbg(smb_fname), strerror(errno));
5090 /* Populate the stat struct with info from the base file. */
5091 if (fruit_stat_base(handle, smb_fname, follow_links) == -1) {
5094 smb_fname->st.st_ex_size = AFP_INFO_SIZE;
5095 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5096 smb_fname->stream_name);
5100 static int fruit_stat_meta(vfs_handle_struct *handle,
5101 struct smb_filename *smb_fname,
5104 struct fruit_config_data *config = NULL;
5107 SMB_VFS_HANDLE_GET_DATA(handle, config,
5108 struct fruit_config_data, return -1);
5110 switch (config->meta) {
5111 case FRUIT_META_STREAM:
5112 ret = fruit_stat_meta_stream(handle, smb_fname, follow_links);
5115 case FRUIT_META_NETATALK:
5116 ret = fruit_stat_meta_netatalk(handle, smb_fname, follow_links);
5120 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
5127 static int fruit_stat_rsrc_netatalk(vfs_handle_struct *handle,
5128 struct smb_filename *smb_fname,
5131 struct adouble *ad = NULL;
5134 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
5140 /* Populate the stat struct with info from the base file. */
5141 ret = fruit_stat_base(handle, smb_fname, follow_links);
5147 smb_fname->st.st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
5148 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5149 smb_fname->stream_name);
5154 static int fruit_stat_rsrc_stream(vfs_handle_struct *handle,
5155 struct smb_filename *smb_fname,
5161 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
5163 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5169 static int fruit_stat_rsrc_xattr(vfs_handle_struct *handle,
5170 struct smb_filename *smb_fname,
5173 #ifdef HAVE_ATTROPEN
5177 /* Populate the stat struct with info from the base file. */
5178 ret = fruit_stat_base(handle, smb_fname, follow_links);
5183 fd = attropen(smb_fname->base_name,
5184 AFPRESOURCE_EA_NETATALK,
5190 ret = sys_fstat(fd, &smb_fname->st, false);
5193 DBG_ERR("fstat [%s:%s] failed\n", smb_fname->base_name,
5194 AFPRESOURCE_EA_NETATALK);
5200 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5201 smb_fname->stream_name);
5211 static int fruit_stat_rsrc(vfs_handle_struct *handle,
5212 struct smb_filename *smb_fname,
5215 struct fruit_config_data *config = NULL;
5218 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
5220 SMB_VFS_HANDLE_GET_DATA(handle, config,
5221 struct fruit_config_data, return -1);
5223 switch (config->rsrc) {
5224 case FRUIT_RSRC_STREAM:
5225 ret = fruit_stat_rsrc_stream(handle, smb_fname, follow_links);
5228 case FRUIT_RSRC_XATTR:
5229 ret = fruit_stat_rsrc_xattr(handle, smb_fname, follow_links);
5232 case FRUIT_RSRC_ADFILE:
5233 ret = fruit_stat_rsrc_netatalk(handle, smb_fname, follow_links);
5237 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
5244 static int fruit_stat(vfs_handle_struct *handle,
5245 struct smb_filename *smb_fname)
5249 DEBUG(10, ("fruit_stat called for %s\n",
5250 smb_fname_str_dbg(smb_fname)));
5252 if (!is_ntfs_stream_smb_fname(smb_fname)
5253 || is_ntfs_default_stream_smb_fname(smb_fname)) {
5254 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
5256 update_btime(handle, smb_fname);
5262 * Note if lp_posix_paths() is true, we can never
5263 * get here as is_ntfs_stream_smb_fname() is
5264 * always false. So we never need worry about
5265 * not following links here.
5268 if (is_afpinfo_stream(smb_fname)) {
5269 rc = fruit_stat_meta(handle, smb_fname, true);
5270 } else if (is_afpresource_stream(smb_fname)) {
5271 rc = fruit_stat_rsrc(handle, smb_fname, true);
5273 return SMB_VFS_NEXT_STAT(handle, smb_fname);
5277 update_btime(handle, smb_fname);
5278 smb_fname->st.st_ex_mode &= ~S_IFMT;
5279 smb_fname->st.st_ex_mode |= S_IFREG;
5280 smb_fname->st.st_ex_blocks =
5281 smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
5286 static int fruit_lstat(vfs_handle_struct *handle,
5287 struct smb_filename *smb_fname)
5291 DEBUG(10, ("fruit_lstat called for %s\n",
5292 smb_fname_str_dbg(smb_fname)));
5294 if (!is_ntfs_stream_smb_fname(smb_fname)
5295 || is_ntfs_default_stream_smb_fname(smb_fname)) {
5296 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5298 update_btime(handle, smb_fname);
5303 if (is_afpinfo_stream(smb_fname)) {
5304 rc = fruit_stat_meta(handle, smb_fname, false);
5305 } else if (is_afpresource_stream(smb_fname)) {
5306 rc = fruit_stat_rsrc(handle, smb_fname, false);
5308 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5312 update_btime(handle, smb_fname);
5313 smb_fname->st.st_ex_mode &= ~S_IFMT;
5314 smb_fname->st.st_ex_mode |= S_IFREG;
5315 smb_fname->st.st_ex_blocks =
5316 smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
5321 static int fruit_fstat_meta_stream(vfs_handle_struct *handle,
5323 SMB_STRUCT_STAT *sbuf)
5325 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5326 struct smb_filename smb_fname;
5335 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5340 *sbuf = fsp->base_fsp->fsp_name->st;
5341 sbuf->st_ex_size = AFP_INFO_SIZE;
5342 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5346 smb_fname = (struct smb_filename) {
5347 .base_name = fsp->fsp_name->base_name,
5350 ret = fruit_stat_base(handle, &smb_fname, false);
5354 *sbuf = smb_fname.st;
5356 ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5358 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5363 sbuf->st_ex_ino = ino;
5367 static int fruit_fstat_meta_netatalk(vfs_handle_struct *handle,
5369 SMB_STRUCT_STAT *sbuf)
5373 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5378 *sbuf = fsp->base_fsp->fsp_name->st;
5379 sbuf->st_ex_size = AFP_INFO_SIZE;
5380 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5385 static int fruit_fstat_meta(vfs_handle_struct *handle,
5387 SMB_STRUCT_STAT *sbuf,
5392 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
5394 switch (fio->config->meta) {
5395 case FRUIT_META_STREAM:
5396 ret = fruit_fstat_meta_stream(handle, fsp, sbuf);
5399 case FRUIT_META_NETATALK:
5400 ret = fruit_fstat_meta_netatalk(handle, fsp, sbuf);
5404 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
5408 DBG_DEBUG("Path [%s] ret [%d]\n", fsp_str_dbg(fsp), ret);
5412 static int fruit_fstat_rsrc_xattr(vfs_handle_struct *handle,
5414 SMB_STRUCT_STAT *sbuf)
5416 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5419 static int fruit_fstat_rsrc_stream(vfs_handle_struct *handle,
5421 SMB_STRUCT_STAT *sbuf)
5423 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5426 static int fruit_fstat_rsrc_adouble(vfs_handle_struct *handle,
5428 SMB_STRUCT_STAT *sbuf)
5430 struct adouble *ad = NULL;
5433 /* Populate the stat struct with info from the base file. */
5434 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5439 ad = ad_get(talloc_tos(), handle,
5440 fsp->base_fsp->fsp_name,
5443 DBG_ERR("ad_get [%s] failed [%s]\n",
5444 fsp_str_dbg(fsp), strerror(errno));
5448 *sbuf = fsp->base_fsp->fsp_name->st;
5449 sbuf->st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
5450 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5456 static int fruit_fstat_rsrc(vfs_handle_struct *handle, files_struct *fsp,
5457 SMB_STRUCT_STAT *sbuf, struct fio *fio)
5461 switch (fio->config->rsrc) {
5462 case FRUIT_RSRC_STREAM:
5463 ret = fruit_fstat_rsrc_stream(handle, fsp, sbuf);
5466 case FRUIT_RSRC_ADFILE:
5467 ret = fruit_fstat_rsrc_adouble(handle, fsp, sbuf);
5470 case FRUIT_RSRC_XATTR:
5471 ret = fruit_fstat_rsrc_xattr(handle, fsp, sbuf);
5475 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
5482 static int fruit_fstat(vfs_handle_struct *handle, files_struct *fsp,
5483 SMB_STRUCT_STAT *sbuf)
5485 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5489 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5492 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
5494 if (fio->type == ADOUBLE_META) {
5495 rc = fruit_fstat_meta(handle, fsp, sbuf, fio);
5497 rc = fruit_fstat_rsrc(handle, fsp, sbuf, fio);
5501 sbuf->st_ex_mode &= ~S_IFMT;
5502 sbuf->st_ex_mode |= S_IFREG;
5503 sbuf->st_ex_blocks = sbuf->st_ex_size / STAT_ST_BLOCKSIZE + 1;
5506 DBG_DEBUG("Path [%s] rc [%d] size [%"PRIdMAX"]\n",
5507 fsp_str_dbg(fsp), rc, (intmax_t)sbuf->st_ex_size);
5511 static NTSTATUS delete_invalid_meta_stream(
5512 vfs_handle_struct *handle,
5513 const struct smb_filename *smb_fname,
5514 TALLOC_CTX *mem_ctx,
5515 unsigned int *pnum_streams,
5516 struct stream_struct **pstreams,
5519 struct smb_filename *sname = NULL;
5523 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams, AFPINFO_STREAM);
5525 return NT_STATUS_INTERNAL_ERROR;
5529 return NT_STATUS_OK;
5532 sname = synthetic_smb_fname(talloc_tos(),
5533 smb_fname->base_name,
5534 AFPINFO_STREAM_NAME,
5536 if (sname == NULL) {
5537 return NT_STATUS_NO_MEMORY;
5540 ret = SMB_VFS_NEXT_UNLINK(handle, sname);
5543 DBG_ERR("Removing [%s] failed\n", smb_fname_str_dbg(sname));
5544 return map_nt_error_from_unix(errno);
5547 return NT_STATUS_OK;
5550 static NTSTATUS fruit_streaminfo_meta_stream(
5551 vfs_handle_struct *handle,
5552 struct files_struct *fsp,
5553 const struct smb_filename *smb_fname,
5554 TALLOC_CTX *mem_ctx,
5555 unsigned int *pnum_streams,
5556 struct stream_struct **pstreams)
5558 struct stream_struct *stream = *pstreams;
5559 unsigned int num_streams = *pnum_streams;
5562 for (i = 0; i < num_streams; i++) {
5563 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
5568 if (i == num_streams) {
5569 return NT_STATUS_OK;
5572 if (stream[i].size != AFP_INFO_SIZE) {
5573 DBG_ERR("Removing invalid AFPINFO_STREAM size [%jd] from [%s]\n",
5574 (intmax_t)stream[i].size, smb_fname_str_dbg(smb_fname));
5576 return delete_invalid_meta_stream(handle,
5585 return NT_STATUS_OK;
5588 static NTSTATUS fruit_streaminfo_meta_netatalk(
5589 vfs_handle_struct *handle,
5590 struct files_struct *fsp,
5591 const struct smb_filename *smb_fname,
5592 TALLOC_CTX *mem_ctx,
5593 unsigned int *pnum_streams,
5594 struct stream_struct **pstreams)
5596 struct stream_struct *stream = *pstreams;
5597 unsigned int num_streams = *pnum_streams;
5598 struct adouble *ad = NULL;
5603 /* Remove the Netatalk xattr from the list */
5604 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5605 ":" NETATALK_META_XATTR ":$DATA");
5607 return NT_STATUS_NO_MEMORY;
5611 * Check if there's a AFPINFO_STREAM from the VFS streams
5612 * backend and if yes, remove it from the list
5614 for (i = 0; i < num_streams; i++) {
5615 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
5620 if (i < num_streams) {
5621 DBG_WARNING("Unexpected AFPINFO_STREAM on [%s]\n",
5622 smb_fname_str_dbg(smb_fname));
5624 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5627 return NT_STATUS_INTERNAL_ERROR;
5631 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5633 return NT_STATUS_OK;
5636 is_fi_empty = ad_empty_finderinfo(ad);
5640 return NT_STATUS_OK;
5643 ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
5644 AFPINFO_STREAM_NAME, AFP_INFO_SIZE,
5645 smb_roundup(handle->conn, AFP_INFO_SIZE));
5647 return NT_STATUS_NO_MEMORY;
5650 return NT_STATUS_OK;
5653 static NTSTATUS fruit_streaminfo_meta(vfs_handle_struct *handle,
5654 struct files_struct *fsp,
5655 const struct smb_filename *smb_fname,
5656 TALLOC_CTX *mem_ctx,
5657 unsigned int *pnum_streams,
5658 struct stream_struct **pstreams)
5660 struct fruit_config_data *config = NULL;
5663 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5664 return NT_STATUS_INTERNAL_ERROR);
5666 switch (config->meta) {
5667 case FRUIT_META_NETATALK:
5668 status = fruit_streaminfo_meta_netatalk(handle, fsp, smb_fname,
5669 mem_ctx, pnum_streams,
5673 case FRUIT_META_STREAM:
5674 status = fruit_streaminfo_meta_stream(handle, fsp, smb_fname,
5675 mem_ctx, pnum_streams,
5680 return NT_STATUS_INTERNAL_ERROR;
5686 static NTSTATUS fruit_streaminfo_rsrc_stream(
5687 vfs_handle_struct *handle,
5688 struct files_struct *fsp,
5689 const struct smb_filename *smb_fname,
5690 TALLOC_CTX *mem_ctx,
5691 unsigned int *pnum_streams,
5692 struct stream_struct **pstreams)
5696 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
5698 DBG_ERR("Filtering resource stream failed\n");
5699 return NT_STATUS_INTERNAL_ERROR;
5701 return NT_STATUS_OK;
5704 static NTSTATUS fruit_streaminfo_rsrc_xattr(
5705 vfs_handle_struct *handle,
5706 struct files_struct *fsp,
5707 const struct smb_filename *smb_fname,
5708 TALLOC_CTX *mem_ctx,
5709 unsigned int *pnum_streams,
5710 struct stream_struct **pstreams)
5714 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
5716 DBG_ERR("Filtering resource stream failed\n");
5717 return NT_STATUS_INTERNAL_ERROR;
5719 return NT_STATUS_OK;
5722 static NTSTATUS fruit_streaminfo_rsrc_adouble(
5723 vfs_handle_struct *handle,
5724 struct files_struct *fsp,
5725 const struct smb_filename *smb_fname,
5726 TALLOC_CTX *mem_ctx,
5727 unsigned int *pnum_streams,
5728 struct stream_struct **pstreams)
5730 struct stream_struct *stream = *pstreams;
5731 unsigned int num_streams = *pnum_streams;
5732 struct adouble *ad = NULL;
5738 * Check if there's a AFPRESOURCE_STREAM from the VFS streams backend
5739 * and if yes, remove it from the list
5741 for (i = 0; i < num_streams; i++) {
5742 if (strequal_m(stream[i].name, AFPRESOURCE_STREAM)) {
5747 if (i < num_streams) {
5748 DBG_WARNING("Unexpected AFPRESOURCE_STREAM on [%s]\n",
5749 smb_fname_str_dbg(smb_fname));
5751 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5752 AFPRESOURCE_STREAM);
5754 return NT_STATUS_INTERNAL_ERROR;
5758 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
5760 return NT_STATUS_OK;
5763 rlen = ad_getentrylen(ad, ADEID_RFORK);
5767 return NT_STATUS_OK;
5770 ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
5771 AFPRESOURCE_STREAM_NAME, rlen,
5772 smb_roundup(handle->conn, rlen));
5774 return NT_STATUS_NO_MEMORY;
5777 return NT_STATUS_OK;
5780 static NTSTATUS fruit_streaminfo_rsrc(vfs_handle_struct *handle,
5781 struct files_struct *fsp,
5782 const struct smb_filename *smb_fname,
5783 TALLOC_CTX *mem_ctx,
5784 unsigned int *pnum_streams,
5785 struct stream_struct **pstreams)
5787 struct fruit_config_data *config = NULL;
5790 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5791 return NT_STATUS_INTERNAL_ERROR);
5793 switch (config->rsrc) {
5794 case FRUIT_RSRC_STREAM:
5795 status = fruit_streaminfo_rsrc_stream(handle, fsp, smb_fname,
5796 mem_ctx, pnum_streams,
5800 case FRUIT_RSRC_XATTR:
5801 status = fruit_streaminfo_rsrc_xattr(handle, fsp, smb_fname,
5802 mem_ctx, pnum_streams,
5806 case FRUIT_RSRC_ADFILE:
5807 status = fruit_streaminfo_rsrc_adouble(handle, fsp, smb_fname,
5808 mem_ctx, pnum_streams,
5813 return NT_STATUS_INTERNAL_ERROR;
5819 static void fruit_filter_empty_streams(unsigned int *pnum_streams,
5820 struct stream_struct **pstreams)
5822 unsigned num_streams = *pnum_streams;
5823 struct stream_struct *streams = *pstreams;
5826 if (!global_fruit_config.nego_aapl) {
5830 while (i < num_streams) {
5831 struct smb_filename smb_fname = (struct smb_filename) {
5832 .stream_name = streams[i].name,
5835 if (is_ntfs_default_stream_smb_fname(&smb_fname)
5836 || streams[i].size > 0)
5842 streams[i] = streams[num_streams - 1];
5846 *pnum_streams = num_streams;
5849 static NTSTATUS fruit_streaminfo(vfs_handle_struct *handle,
5850 struct files_struct *fsp,
5851 const struct smb_filename *smb_fname,
5852 TALLOC_CTX *mem_ctx,
5853 unsigned int *pnum_streams,
5854 struct stream_struct **pstreams)
5856 struct fruit_config_data *config = NULL;
5859 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5860 return NT_STATUS_UNSUCCESSFUL);
5862 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
5864 status = SMB_VFS_NEXT_STREAMINFO(handle, fsp, smb_fname, mem_ctx,
5865 pnum_streams, pstreams);
5866 if (!NT_STATUS_IS_OK(status)) {
5870 fruit_filter_empty_streams(pnum_streams, pstreams);
5872 status = fruit_streaminfo_meta(handle, fsp, smb_fname,
5873 mem_ctx, pnum_streams, pstreams);
5874 if (!NT_STATUS_IS_OK(status)) {
5878 status = fruit_streaminfo_rsrc(handle, fsp, smb_fname,
5879 mem_ctx, pnum_streams, pstreams);
5880 if (!NT_STATUS_IS_OK(status)) {
5884 return NT_STATUS_OK;
5887 static int fruit_ntimes(vfs_handle_struct *handle,
5888 const struct smb_filename *smb_fname,
5889 struct smb_file_time *ft)
5892 struct adouble *ad = NULL;
5893 struct fruit_config_data *config = NULL;
5895 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5898 if ((config->meta != FRUIT_META_NETATALK) ||
5899 null_timespec(ft->create_time))
5901 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
5904 DEBUG(10,("set btime for %s to %s\n", smb_fname_str_dbg(smb_fname),
5905 time_to_asc(convert_timespec_to_time_t(ft->create_time))));
5907 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5912 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX,
5913 convert_time_t_to_uint32_t(ft->create_time.tv_sec));
5915 rc = ad_set(handle, ad, smb_fname);
5921 DEBUG(1, ("fruit_ntimes: %s\n", smb_fname_str_dbg(smb_fname)));
5924 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
5927 static int fruit_fallocate(struct vfs_handle_struct *handle,
5928 struct files_struct *fsp,
5933 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5936 return SMB_VFS_NEXT_FALLOCATE(handle, fsp, mode, offset, len);
5939 /* Let the pwrite code path handle it. */
5944 static int fruit_ftruncate_rsrc_xattr(struct vfs_handle_struct *handle,
5945 struct files_struct *fsp,
5948 #ifdef HAVE_ATTROPEN
5949 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
5954 static int fruit_ftruncate_rsrc_adouble(struct vfs_handle_struct *handle,
5955 struct files_struct *fsp,
5959 struct adouble *ad = NULL;
5962 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
5964 DBG_DEBUG("ad_get [%s] failed [%s]\n",
5965 fsp_str_dbg(fsp), strerror(errno));
5969 ad_off = ad_getentryoff(ad, ADEID_RFORK);
5971 rc = ftruncate(fsp->fh->fd, offset + ad_off);
5977 ad_setentrylen(ad, ADEID_RFORK, offset);
5979 rc = ad_fset(handle, ad, fsp);
5981 DBG_ERR("ad_fset [%s] failed [%s]\n",
5982 fsp_str_dbg(fsp), strerror(errno));
5991 static int fruit_ftruncate_rsrc_stream(struct vfs_handle_struct *handle,
5992 struct files_struct *fsp,
5995 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
5998 static int fruit_ftruncate_rsrc(struct vfs_handle_struct *handle,
5999 struct files_struct *fsp,
6002 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
6006 DBG_ERR("Failed to fetch fsp extension");
6010 switch (fio->config->rsrc) {
6011 case FRUIT_RSRC_XATTR:
6012 ret = fruit_ftruncate_rsrc_xattr(handle, fsp, offset);
6015 case FRUIT_RSRC_ADFILE:
6016 ret = fruit_ftruncate_rsrc_adouble(handle, fsp, offset);
6019 case FRUIT_RSRC_STREAM:
6020 ret = fruit_ftruncate_rsrc_stream(handle, fsp, offset);
6024 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
6032 static int fruit_ftruncate_meta(struct vfs_handle_struct *handle,
6033 struct files_struct *fsp,
6037 DBG_WARNING("ftruncate %s to %jd",
6038 fsp_str_dbg(fsp), (intmax_t)offset);
6039 /* OS X returns NT_STATUS_ALLOTTED_SPACE_EXCEEDED */
6044 /* OS X returns success but does nothing */
6045 DBG_INFO("ignoring ftruncate %s to %jd\n",
6046 fsp_str_dbg(fsp), (intmax_t)offset);
6050 static int fruit_ftruncate(struct vfs_handle_struct *handle,
6051 struct files_struct *fsp,
6054 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
6057 DBG_DEBUG("Path [%s] offset [%"PRIdMAX"]\n", fsp_str_dbg(fsp),
6061 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
6064 if (fio->type == ADOUBLE_META) {
6065 ret = fruit_ftruncate_meta(handle, fsp, offset);
6067 ret = fruit_ftruncate_rsrc(handle, fsp, offset);
6070 DBG_DEBUG("Path [%s] result [%d]\n", fsp_str_dbg(fsp), ret);
6074 static NTSTATUS fruit_create_file(vfs_handle_struct *handle,
6075 struct smb_request *req,
6076 uint16_t root_dir_fid,
6077 struct smb_filename *smb_fname,
6078 uint32_t access_mask,
6079 uint32_t share_access,
6080 uint32_t create_disposition,
6081 uint32_t create_options,
6082 uint32_t file_attributes,
6083 uint32_t oplock_request,
6084 struct smb2_lease *lease,
6085 uint64_t allocation_size,
6086 uint32_t private_flags,
6087 struct security_descriptor *sd,
6088 struct ea_list *ea_list,
6089 files_struct **result,
6091 const struct smb2_create_blobs *in_context_blobs,
6092 struct smb2_create_blobs *out_context_blobs)
6095 struct fruit_config_data *config = NULL;
6096 files_struct *fsp = NULL;
6097 struct fio *fio = NULL;
6098 bool internal_open = (oplock_request & INTERNAL_OPEN_ONLY);
6101 status = check_aapl(handle, req, in_context_blobs, out_context_blobs);
6102 if (!NT_STATUS_IS_OK(status)) {
6106 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
6107 return NT_STATUS_UNSUCCESSFUL);
6109 if (is_apple_stream(smb_fname) && !internal_open) {
6110 ret = ad_convert(handle, smb_fname);
6112 DBG_ERR("ad_convert() failed\n");
6113 return NT_STATUS_UNSUCCESSFUL;
6117 status = SMB_VFS_NEXT_CREATE_FILE(
6118 handle, req, root_dir_fid, smb_fname,
6119 access_mask, share_access,
6120 create_disposition, create_options,
6121 file_attributes, oplock_request,
6123 allocation_size, private_flags,
6124 sd, ea_list, result,
6125 pinfo, in_context_blobs, out_context_blobs);
6126 if (!NT_STATUS_IS_OK(status)) {
6132 if (global_fruit_config.nego_aapl) {
6133 if (config->posix_rename && fsp->is_directory) {
6135 * Enable POSIX directory rename behaviour
6137 fsp->posix_flags |= FSP_POSIX_FLAGS_RENAME;
6142 * If this is a plain open for existing files, opening an 0
6143 * byte size resource fork MUST fail with
6144 * NT_STATUS_OBJECT_NAME_NOT_FOUND.
6146 * Cf the vfs_fruit torture tests in test_rfork_create().
6148 if (global_fruit_config.nego_aapl &&
6149 create_disposition == FILE_OPEN &&
6150 smb_fname->st.st_ex_size == 0 &&
6151 is_ntfs_stream_smb_fname(smb_fname) &&
6152 !(is_ntfs_default_stream_smb_fname(smb_fname)))
6154 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
6158 fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
6159 if (fio != NULL && pinfo != NULL && *pinfo == FILE_WAS_CREATED) {
6160 fio->created = true;
6163 if (is_ntfs_stream_smb_fname(smb_fname)
6164 || fsp->is_directory) {
6168 if (config->locking == FRUIT_LOCKING_NETATALK) {
6169 status = fruit_check_access(
6173 if (!NT_STATUS_IS_OK(status)) {
6181 DEBUG(10, ("fruit_create_file: %s\n", nt_errstr(status)));
6184 close_file(req, fsp, ERROR_CLOSE);
6185 *result = fsp = NULL;
6191 static NTSTATUS fruit_readdir_attr(struct vfs_handle_struct *handle,
6192 const struct smb_filename *fname,
6193 TALLOC_CTX *mem_ctx,
6194 struct readdir_attr_data **pattr_data)
6196 struct fruit_config_data *config = NULL;
6197 struct readdir_attr_data *attr_data;
6201 SMB_VFS_HANDLE_GET_DATA(handle, config,
6202 struct fruit_config_data,
6203 return NT_STATUS_UNSUCCESSFUL);
6205 if (!global_fruit_config.nego_aapl) {
6206 return SMB_VFS_NEXT_READDIR_ATTR(handle, fname, mem_ctx, pattr_data);
6209 DEBUG(10, ("fruit_readdir_attr %s\n", fname->base_name));
6211 ret = ad_convert(handle, fname);
6213 DBG_ERR("ad_convert() failed\n");
6214 return NT_STATUS_UNSUCCESSFUL;
6217 *pattr_data = talloc_zero(mem_ctx, struct readdir_attr_data);
6218 if (*pattr_data == NULL) {
6219 return NT_STATUS_UNSUCCESSFUL;
6221 attr_data = *pattr_data;
6222 attr_data->type = RDATTR_AAPL;
6225 * Mac metadata: compressed FinderInfo, resource fork length
6228 status = readdir_attr_macmeta(handle, fname, attr_data);
6229 if (!NT_STATUS_IS_OK(status)) {
6231 * Error handling is tricky: if we return failure from
6232 * this function, the corresponding directory entry
6233 * will to be passed to the client, so we really just
6234 * want to error out on fatal errors.
6236 if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
6244 if (config->unix_info_enabled) {
6245 attr_data->attr_data.aapl.unix_mode = fname->st.st_ex_mode;
6251 if (!config->readdir_attr_max_access) {
6252 attr_data->attr_data.aapl.max_access = FILE_GENERIC_ALL;
6254 status = smbd_calculate_access_mask(
6258 SEC_FLAG_MAXIMUM_ALLOWED,
6259 &attr_data->attr_data.aapl.max_access);
6260 if (!NT_STATUS_IS_OK(status)) {
6265 return NT_STATUS_OK;
6268 DEBUG(1, ("fruit_readdir_attr %s, error: %s\n",
6269 fname->base_name, nt_errstr(status)));
6270 TALLOC_FREE(*pattr_data);
6274 static NTSTATUS fruit_fget_nt_acl(vfs_handle_struct *handle,
6276 uint32_t security_info,
6277 TALLOC_CTX *mem_ctx,
6278 struct security_descriptor **ppdesc)
6281 struct security_ace ace;
6283 struct fruit_config_data *config;
6285 SMB_VFS_HANDLE_GET_DATA(handle, config,
6286 struct fruit_config_data,
6287 return NT_STATUS_UNSUCCESSFUL);
6289 status = SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
6291 if (!NT_STATUS_IS_OK(status)) {
6296 * Add MS NFS style ACEs with uid, gid and mode
6298 if (!global_fruit_config.nego_aapl) {
6299 return NT_STATUS_OK;
6301 if (!config->unix_info_enabled) {
6302 return NT_STATUS_OK;
6305 /* First remove any existing ACE's with NFS style mode/uid/gid SIDs. */
6306 status = remove_virtual_nfs_aces(*ppdesc);
6307 if (!NT_STATUS_IS_OK(status)) {
6308 DBG_WARNING("failed to remove MS NFS style ACEs\n");
6312 /* MS NFS style mode */
6313 sid_compose(&sid, &global_sid_Unix_NFS_Mode, fsp->fsp_name->st.st_ex_mode);
6314 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6315 status = security_descriptor_dacl_add(*ppdesc, &ace);
6316 if (!NT_STATUS_IS_OK(status)) {
6317 DEBUG(1,("failed to add MS NFS style ACE\n"));
6321 /* MS NFS style uid */
6322 sid_compose(&sid, &global_sid_Unix_NFS_Users, fsp->fsp_name->st.st_ex_uid);
6323 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6324 status = security_descriptor_dacl_add(*ppdesc, &ace);
6325 if (!NT_STATUS_IS_OK(status)) {
6326 DEBUG(1,("failed to add MS NFS style ACE\n"));
6330 /* MS NFS style gid */
6331 sid_compose(&sid, &global_sid_Unix_NFS_Groups, fsp->fsp_name->st.st_ex_gid);
6332 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6333 status = security_descriptor_dacl_add(*ppdesc, &ace);
6334 if (!NT_STATUS_IS_OK(status)) {
6335 DEBUG(1,("failed to add MS NFS style ACE\n"));
6339 return NT_STATUS_OK;
6342 static NTSTATUS fruit_fset_nt_acl(vfs_handle_struct *handle,
6344 uint32_t security_info_sent,
6345 const struct security_descriptor *orig_psd)
6349 mode_t ms_nfs_mode = 0;
6351 struct security_descriptor *psd = NULL;
6352 uint32_t orig_num_aces = 0;
6354 if (orig_psd->dacl != NULL) {
6355 orig_num_aces = orig_psd->dacl->num_aces;
6358 psd = security_descriptor_copy(talloc_tos(), orig_psd);
6360 return NT_STATUS_NO_MEMORY;
6363 DBG_DEBUG("fruit_fset_nt_acl: %s\n", fsp_str_dbg(fsp));
6365 status = check_ms_nfs(handle, fsp, psd, &ms_nfs_mode, &do_chmod);
6366 if (!NT_STATUS_IS_OK(status)) {
6367 DEBUG(1, ("fruit_fset_nt_acl: check_ms_nfs failed%s\n", fsp_str_dbg(fsp)));
6373 * If only ms_nfs ACE entries were sent, ensure we set the DACL
6374 * sent/present flags correctly now we've removed them.
6377 if (orig_num_aces != 0) {
6379 * Are there any ACE's left ?
6381 if (psd->dacl->num_aces == 0) {
6382 /* No - clear the DACL sent/present flags. */
6383 security_info_sent &= ~SECINFO_DACL;
6384 psd->type &= ~SEC_DESC_DACL_PRESENT;
6388 status = SMB_VFS_NEXT_FSET_NT_ACL(handle, fsp, security_info_sent, psd);
6389 if (!NT_STATUS_IS_OK(status)) {
6390 DEBUG(1, ("fruit_fset_nt_acl: SMB_VFS_NEXT_FSET_NT_ACL failed%s\n", fsp_str_dbg(fsp)));
6396 if (fsp->fh->fd != -1) {
6397 result = SMB_VFS_FCHMOD(fsp, ms_nfs_mode);
6399 result = SMB_VFS_CHMOD(fsp->conn,
6405 DEBUG(1, ("chmod: %s, result: %d, %04o error %s\n", fsp_str_dbg(fsp),
6406 result, (unsigned)ms_nfs_mode,
6408 status = map_nt_error_from_unix(errno);
6415 return NT_STATUS_OK;
6418 static struct vfs_offload_ctx *fruit_offload_ctx;
6420 struct fruit_offload_read_state {
6421 struct vfs_handle_struct *handle;
6422 struct tevent_context *ev;
6428 static void fruit_offload_read_done(struct tevent_req *subreq);
6430 static struct tevent_req *fruit_offload_read_send(
6431 TALLOC_CTX *mem_ctx,
6432 struct tevent_context *ev,
6433 struct vfs_handle_struct *handle,
6440 struct tevent_req *req = NULL;
6441 struct tevent_req *subreq = NULL;
6442 struct fruit_offload_read_state *state = NULL;
6444 req = tevent_req_create(mem_ctx, &state,
6445 struct fruit_offload_read_state);
6449 *state = (struct fruit_offload_read_state) {
6456 subreq = SMB_VFS_NEXT_OFFLOAD_READ_SEND(mem_ctx, ev, handle, fsp,
6457 fsctl, ttl, offset, to_copy);
6458 if (tevent_req_nomem(subreq, req)) {
6459 return tevent_req_post(req, ev);
6461 tevent_req_set_callback(subreq, fruit_offload_read_done, req);
6465 static void fruit_offload_read_done(struct tevent_req *subreq)
6467 struct tevent_req *req = tevent_req_callback_data(
6468 subreq, struct tevent_req);
6469 struct fruit_offload_read_state *state = tevent_req_data(
6470 req, struct fruit_offload_read_state);
6473 status = SMB_VFS_NEXT_OFFLOAD_READ_RECV(subreq,
6477 TALLOC_FREE(subreq);
6478 if (tevent_req_nterror(req, status)) {
6482 if (state->fsctl != FSCTL_SRV_REQUEST_RESUME_KEY) {
6483 tevent_req_done(req);
6487 status = vfs_offload_token_ctx_init(state->fsp->conn->sconn->client,
6488 &fruit_offload_ctx);
6489 if (tevent_req_nterror(req, status)) {
6493 status = vfs_offload_token_db_store_fsp(fruit_offload_ctx,
6496 if (tevent_req_nterror(req, status)) {
6500 tevent_req_done(req);
6504 static NTSTATUS fruit_offload_read_recv(struct tevent_req *req,
6505 struct vfs_handle_struct *handle,
6506 TALLOC_CTX *mem_ctx,
6509 struct fruit_offload_read_state *state = tevent_req_data(
6510 req, struct fruit_offload_read_state);
6513 if (tevent_req_is_nterror(req, &status)) {
6514 tevent_req_received(req);
6518 token->length = state->token.length;
6519 token->data = talloc_move(mem_ctx, &state->token.data);
6521 tevent_req_received(req);
6522 return NT_STATUS_OK;
6525 struct fruit_offload_write_state {
6526 struct vfs_handle_struct *handle;
6528 struct files_struct *src_fsp;
6529 struct files_struct *dst_fsp;
6533 static void fruit_offload_write_done(struct tevent_req *subreq);
6534 static struct tevent_req *fruit_offload_write_send(struct vfs_handle_struct *handle,
6535 TALLOC_CTX *mem_ctx,
6536 struct tevent_context *ev,
6539 off_t transfer_offset,
6540 struct files_struct *dest_fsp,
6544 struct tevent_req *req, *subreq;
6545 struct fruit_offload_write_state *state;
6547 struct fruit_config_data *config;
6548 off_t src_off = transfer_offset;
6549 files_struct *src_fsp = NULL;
6550 off_t to_copy = num;
6551 bool copyfile_enabled = false;
6553 DEBUG(10,("soff: %ju, doff: %ju, len: %ju\n",
6554 (uintmax_t)src_off, (uintmax_t)dest_off, (uintmax_t)num));
6556 SMB_VFS_HANDLE_GET_DATA(handle, config,
6557 struct fruit_config_data,
6560 req = tevent_req_create(mem_ctx, &state,
6561 struct fruit_offload_write_state);
6565 state->handle = handle;
6566 state->dst_fsp = dest_fsp;
6569 case FSCTL_SRV_COPYCHUNK:
6570 case FSCTL_SRV_COPYCHUNK_WRITE:
6571 copyfile_enabled = config->copyfile_enabled;
6578 * Check if this a OS X copyfile style copychunk request with
6579 * a requested chunk count of 0 that was translated to a
6580 * offload_write_send VFS call overloading the parameters src_off
6581 * = dest_off = num = 0.
6583 if (copyfile_enabled && num == 0 && src_off == 0 && dest_off == 0) {
6584 status = vfs_offload_token_db_fetch_fsp(
6585 fruit_offload_ctx, token, &src_fsp);
6586 if (tevent_req_nterror(req, status)) {
6587 return tevent_req_post(req, ev);
6589 state->src_fsp = src_fsp;
6591 status = vfs_stat_fsp(src_fsp);
6592 if (tevent_req_nterror(req, status)) {
6593 return tevent_req_post(req, ev);
6596 to_copy = src_fsp->fsp_name->st.st_ex_size;
6597 state->is_copyfile = true;
6600 subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle,
6609 if (tevent_req_nomem(subreq, req)) {
6610 return tevent_req_post(req, ev);
6613 tevent_req_set_callback(subreq, fruit_offload_write_done, req);
6617 static void fruit_offload_write_done(struct tevent_req *subreq)
6619 struct tevent_req *req = tevent_req_callback_data(
6620 subreq, struct tevent_req);
6621 struct fruit_offload_write_state *state = tevent_req_data(
6622 req, struct fruit_offload_write_state);
6624 unsigned int num_streams = 0;
6625 struct stream_struct *streams = NULL;
6627 struct smb_filename *src_fname_tmp = NULL;
6628 struct smb_filename *dst_fname_tmp = NULL;
6630 status = SMB_VFS_NEXT_OFFLOAD_WRITE_RECV(state->handle,
6633 TALLOC_FREE(subreq);
6634 if (tevent_req_nterror(req, status)) {
6638 if (!state->is_copyfile) {
6639 tevent_req_done(req);
6644 * Now copy all remaining streams. We know the share supports
6645 * streams, because we're in vfs_fruit. We don't do this async
6646 * because streams are few and small.
6648 status = vfs_streaminfo(state->handle->conn, state->src_fsp,
6649 state->src_fsp->fsp_name,
6650 req, &num_streams, &streams);
6651 if (tevent_req_nterror(req, status)) {
6655 if (num_streams == 1) {
6656 /* There is always one stream, ::$DATA. */
6657 tevent_req_done(req);
6661 for (i = 0; i < num_streams; i++) {
6662 DEBUG(10, ("%s: stream: '%s'/%zu\n",
6663 __func__, streams[i].name, (size_t)streams[i].size));
6665 src_fname_tmp = synthetic_smb_fname(
6667 state->src_fsp->fsp_name->base_name,
6670 state->src_fsp->fsp_name->flags);
6671 if (tevent_req_nomem(src_fname_tmp, req)) {
6675 if (is_ntfs_default_stream_smb_fname(src_fname_tmp)) {
6676 TALLOC_FREE(src_fname_tmp);
6680 dst_fname_tmp = synthetic_smb_fname(
6682 state->dst_fsp->fsp_name->base_name,
6685 state->dst_fsp->fsp_name->flags);
6686 if (tevent_req_nomem(dst_fname_tmp, req)) {
6687 TALLOC_FREE(src_fname_tmp);
6691 status = copy_file(req,
6692 state->handle->conn,
6695 OPENX_FILE_CREATE_IF_NOT_EXIST,
6697 if (!NT_STATUS_IS_OK(status)) {
6698 DEBUG(1, ("%s: copy %s to %s failed: %s\n", __func__,
6699 smb_fname_str_dbg(src_fname_tmp),
6700 smb_fname_str_dbg(dst_fname_tmp),
6701 nt_errstr(status)));
6702 TALLOC_FREE(src_fname_tmp);
6703 TALLOC_FREE(dst_fname_tmp);
6704 tevent_req_nterror(req, status);
6708 TALLOC_FREE(src_fname_tmp);
6709 TALLOC_FREE(dst_fname_tmp);
6712 TALLOC_FREE(streams);
6713 TALLOC_FREE(src_fname_tmp);
6714 TALLOC_FREE(dst_fname_tmp);
6715 tevent_req_done(req);
6718 static NTSTATUS fruit_offload_write_recv(struct vfs_handle_struct *handle,
6719 struct tevent_req *req,
6722 struct fruit_offload_write_state *state = tevent_req_data(
6723 req, struct fruit_offload_write_state);
6726 if (tevent_req_is_nterror(req, &status)) {
6727 DEBUG(1, ("server side copy chunk failed: %s\n",
6728 nt_errstr(status)));
6730 tevent_req_received(req);
6734 *copied = state->copied;
6735 tevent_req_received(req);
6737 return NT_STATUS_OK;
6740 static char *fruit_get_bandsize_line(char **lines, int numlines)
6743 static bool re_initialized = false;
6747 if (!re_initialized) {
6748 ret = regcomp(&re, "^[[:blank:]]*<key>band-size</key>$", 0);
6752 re_initialized = true;
6755 for (i = 0; i < numlines; i++) {
6756 regmatch_t matches[1];
6758 ret = regexec(&re, lines[i], 1, matches, 0);
6761 * Check if the match was on the last line, sa we want
6762 * the subsequent line.
6764 if (i + 1 == numlines) {
6767 return lines[i + 1];
6769 if (ret != REG_NOMATCH) {
6777 static bool fruit_get_bandsize_from_line(char *line, size_t *_band_size)
6780 static bool re_initialized = false;
6781 regmatch_t matches[2];
6786 if (!re_initialized) {
6789 "<integer>\\([[:digit:]]*\\)</integer>$",
6794 re_initialized = true;
6797 ret = regexec(&re, line, 2, matches, 0);
6799 DBG_ERR("regex failed [%s]\n", line);
6803 line[matches[1].rm_eo] = '\0';
6805 ok = conv_str_u64(&line[matches[1].rm_so], &band_size);
6809 *_band_size = (size_t)band_size;
6814 * This reads and parses an Info.plist from a TM sparsebundle looking for the
6815 * "band-size" key and value.
6817 static bool fruit_get_bandsize(vfs_handle_struct *handle,
6821 #define INFO_PLIST_MAX_SIZE 64*1024
6823 struct smb_filename *smb_fname = NULL;
6824 files_struct *fsp = NULL;
6825 uint8_t *file_data = NULL;
6826 char **lines = NULL;
6827 char *band_size_line = NULL;
6828 size_t plist_file_size;
6835 plist = talloc_asprintf(talloc_tos(),
6837 handle->conn->connectpath,
6839 if (plist == NULL) {
6844 smb_fname = synthetic_smb_fname(talloc_tos(), plist, NULL, NULL, 0);
6845 if (smb_fname == NULL) {
6850 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
6852 DBG_INFO("Ignoring Sparsebundle without Info.plist [%s]\n", dir);
6857 plist_file_size = smb_fname->st.st_ex_size;
6859 if (plist_file_size > INFO_PLIST_MAX_SIZE) {
6860 DBG_INFO("%s is too large, ignoring\n", plist);
6865 status = SMB_VFS_NEXT_CREATE_FILE(
6868 0, /* root_dir_fid */
6869 smb_fname, /* fname */
6870 FILE_GENERIC_READ, /* access_mask */
6871 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
6872 FILE_OPEN, /* create_disposition */
6873 0, /* create_options */
6874 0, /* file_attributes */
6875 INTERNAL_OPEN_ONLY, /* oplock_request */
6877 0, /* allocation_size */
6878 0, /* private_flags */
6883 NULL, NULL); /* create context */
6884 if (!NT_STATUS_IS_OK(status)) {
6885 DBG_INFO("Opening [%s] failed [%s]\n",
6886 smb_fname_str_dbg(smb_fname), nt_errstr(status));
6891 file_data = talloc_array(talloc_tos(), uint8_t, plist_file_size);
6892 if (file_data == NULL) {
6897 nread = SMB_VFS_NEXT_PREAD(handle, fsp, file_data, plist_file_size, 0);
6898 if (nread != plist_file_size) {
6899 DBG_ERR("Short read on [%s]: %zu/%zd\n",
6900 fsp_str_dbg(fsp), nread, plist_file_size);
6906 status = close_file(NULL, fsp, NORMAL_CLOSE);
6908 if (!NT_STATUS_IS_OK(status)) {
6909 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
6914 lines = file_lines_parse((char *)file_data,
6918 if (lines == NULL) {
6923 band_size_line = fruit_get_bandsize_line(lines, numlines);
6924 if (band_size_line == NULL) {
6925 DBG_ERR("Didn't find band-size key in [%s]\n",
6926 smb_fname_str_dbg(smb_fname));
6931 ok = fruit_get_bandsize_from_line(band_size_line, band_size);
6933 DBG_ERR("fruit_get_bandsize_from_line failed\n");
6937 DBG_DEBUG("Parsed band-size [%zu] for [%s]\n", *band_size, plist);
6941 status = close_file(NULL, fsp, NORMAL_CLOSE);
6942 if (!NT_STATUS_IS_OK(status)) {
6943 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
6948 TALLOC_FREE(smb_fname);
6949 TALLOC_FREE(file_data);
6954 struct fruit_disk_free_state {
6958 static bool fruit_get_num_bands(vfs_handle_struct *handle,
6963 struct smb_filename *bands_dir = NULL;
6965 struct dirent *e = NULL;
6969 path = talloc_asprintf(talloc_tos(),
6971 handle->conn->connectpath,
6977 bands_dir = synthetic_smb_fname(talloc_tos(),
6983 if (bands_dir == NULL) {
6987 d = SMB_VFS_NEXT_OPENDIR(handle, bands_dir, NULL, 0);
6989 TALLOC_FREE(bands_dir);
6995 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
6997 e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
6999 if (ISDOT(e->d_name) || ISDOTDOT(e->d_name)) {
7005 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
7007 TALLOC_FREE(bands_dir);
7011 DBG_DEBUG("%zu bands in [%s]\n", nbands, smb_fname_str_dbg(bands_dir));
7013 TALLOC_FREE(bands_dir);
7019 static bool fruit_tmsize_do_dirent(vfs_handle_struct *handle,
7020 struct fruit_disk_free_state *state,
7025 size_t sparsebundle_strlen = strlen("sparsebundle");
7026 size_t bandsize = 0;
7030 p = strstr(e->d_name, "sparsebundle");
7035 if (p[sparsebundle_strlen] != '\0') {
7039 DBG_DEBUG("Processing sparsebundle [%s]\n", e->d_name);
7041 ok = fruit_get_bandsize(handle, e->d_name, &bandsize);
7044 * Beware of race conditions: this may be an uninitialized
7045 * Info.plist that a client is just creating. We don't want let
7046 * this to trigger complete failure.
7048 DBG_ERR("Processing sparsebundle [%s] failed\n", e->d_name);
7052 ok = fruit_get_num_bands(handle, e->d_name, &nbands);
7055 * Beware of race conditions: this may be a backup sparsebundle
7056 * in an early stage lacking a bands subdirectory. We don't want
7057 * let this to trigger complete failure.
7059 DBG_ERR("Processing sparsebundle [%s] failed\n", e->d_name);
7063 if (bandsize > SIZE_MAX/nbands) {
7064 DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
7068 tm_size = bandsize * nbands;
7070 if (state->total_size + tm_size < state->total_size) {
7071 DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
7076 state->total_size += tm_size;
7078 DBG_DEBUG("[%s] tm_size [%jd] total_size [%jd]\n",
7079 e->d_name, (intmax_t)tm_size, (intmax_t)state->total_size);
7085 * Calculate used size of a TimeMachine volume
7087 * This assumes that the volume is used only for TimeMachine.
7089 * - readdir(basedir of share), then
7090 * - for every element that matches regex "^\(.*\)\.sparsebundle$" :
7091 * - parse "\1.sparsebundle/Info.plist" and read the band-size XML key
7092 * - count band files in "\1.sparsebundle/bands/"
7093 * - calculate used size of all bands: band_count * band_size
7095 static uint64_t fruit_disk_free(vfs_handle_struct *handle,
7096 const struct smb_filename *smb_fname,
7101 struct fruit_config_data *config = NULL;
7102 struct fruit_disk_free_state state = {0};
7104 struct dirent *e = NULL;
7110 SMB_VFS_HANDLE_GET_DATA(handle, config,
7111 struct fruit_config_data,
7114 if (!config->time_machine ||
7115 config->time_machine_max_size == 0)
7117 return SMB_VFS_NEXT_DISK_FREE(handle,
7124 d = SMB_VFS_NEXT_OPENDIR(handle, smb_fname, NULL, 0);
7129 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
7131 e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
7133 ok = fruit_tmsize_do_dirent(handle, &state, e);
7135 SMB_VFS_NEXT_CLOSEDIR(handle, d);
7140 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
7145 dsize = config->time_machine_max_size / 512;
7146 dfree = dsize - (state.total_size / 512);
7147 if (dfree > dsize) {
7157 static struct vfs_fn_pointers vfs_fruit_fns = {
7158 .connect_fn = fruit_connect,
7159 .disk_free_fn = fruit_disk_free,
7161 /* File operations */
7162 .chmod_fn = fruit_chmod,
7163 .chown_fn = fruit_chown,
7164 .unlink_fn = fruit_unlink,
7165 .rename_fn = fruit_rename,
7166 .rmdir_fn = fruit_rmdir,
7167 .open_fn = fruit_open,
7168 .close_fn = fruit_close,
7169 .pread_fn = fruit_pread,
7170 .pwrite_fn = fruit_pwrite,
7171 .pread_send_fn = fruit_pread_send,
7172 .pread_recv_fn = fruit_pread_recv,
7173 .pwrite_send_fn = fruit_pwrite_send,
7174 .pwrite_recv_fn = fruit_pwrite_recv,
7175 .stat_fn = fruit_stat,
7176 .lstat_fn = fruit_lstat,
7177 .fstat_fn = fruit_fstat,
7178 .streaminfo_fn = fruit_streaminfo,
7179 .ntimes_fn = fruit_ntimes,
7180 .ftruncate_fn = fruit_ftruncate,
7181 .fallocate_fn = fruit_fallocate,
7182 .create_file_fn = fruit_create_file,
7183 .readdir_attr_fn = fruit_readdir_attr,
7184 .offload_read_send_fn = fruit_offload_read_send,
7185 .offload_read_recv_fn = fruit_offload_read_recv,
7186 .offload_write_send_fn = fruit_offload_write_send,
7187 .offload_write_recv_fn = fruit_offload_write_recv,
7189 /* NT ACL operations */
7190 .fget_nt_acl_fn = fruit_fget_nt_acl,
7191 .fset_nt_acl_fn = fruit_fset_nt_acl,
7195 NTSTATUS vfs_fruit_init(TALLOC_CTX *ctx)
7197 NTSTATUS ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "fruit",
7199 if (!NT_STATUS_IS_OK(ret)) {
7203 vfs_fruit_debug_level = debug_add_class("fruit");
7204 if (vfs_fruit_debug_level == -1) {
7205 vfs_fruit_debug_level = DBGC_VFS;
7206 DEBUG(0, ("%s: Couldn't register custom debugging class!\n",
7209 DEBUG(10, ("%s: Debug class number of '%s': %d\n",
7210 "vfs_fruit_init","fruit",vfs_fruit_debug_level));