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(struct adouble *ad,
1231 const struct smb_filename *smb_fname)
1236 struct smb_filename *stream_name = NULL;
1237 files_struct *fsp = NULL;
1241 int saved_errno = 0;
1244 cmp = memcmp(ad->ad_filler, AD_FILLER_TAG_OSX, ADEDLEN_FILLER);
1249 p_ad = ad_get_entry(ad, ADEID_FINDERI);
1254 ai = afpinfo_new(talloc_tos());
1259 memcpy(ai->afpi_FinderInfo, p_ad, ADEDLEN_FINDERI);
1261 aiblob = data_blob_talloc(talloc_tos(), NULL, AFP_INFO_SIZE);
1262 if (aiblob.data == NULL) {
1267 size = afpinfo_pack(ai, (char *)aiblob.data);
1269 if (size != AFP_INFO_SIZE) {
1273 stream_name = synthetic_smb_fname(talloc_tos(),
1274 smb_fname->base_name,
1278 if (stream_name == NULL) {
1279 data_blob_free(&aiblob);
1280 DBG_ERR("synthetic_smb_fname failed\n");
1284 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1286 status = SMB_VFS_CREATE_FILE(
1287 ad->ad_handle->conn, /* conn */
1289 0, /* root_dir_fid */
1290 stream_name, /* fname */
1291 FILE_GENERIC_WRITE, /* access_mask */
1292 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
1293 FILE_OPEN_IF, /* create_disposition */
1294 0, /* create_options */
1295 0, /* file_attributes */
1296 INTERNAL_OPEN_ONLY, /* oplock_request */
1298 0, /* allocation_size */
1299 0, /* private_flags */
1304 NULL, NULL); /* create context */
1305 TALLOC_FREE(stream_name);
1306 if (!NT_STATUS_IS_OK(status)) {
1307 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1311 nwritten = SMB_VFS_PWRITE(fsp,
1315 if (nwritten == -1) {
1316 DBG_ERR("SMB_VFS_PWRITE failed\n");
1317 saved_errno = errno;
1318 close_file(NULL, fsp, ERROR_CLOSE);
1319 errno = saved_errno;
1323 status = close_file(NULL, fsp, NORMAL_CLOSE);
1324 if (!NT_STATUS_IS_OK(status)) {
1332 static bool ad_convert_truncate(struct adouble *ad,
1333 const struct smb_filename *smb_fname)
1338 * FIXME: direct ftruncate(), but we don't have a fsp for the
1341 rc = ftruncate(ad->ad_fd, ADEDOFF_RFORK_DOT_UND +
1342 ad_getentrylen(ad, ADEID_RFORK));
1350 static bool ad_convert_blank_rfork(vfs_handle_struct *handle,
1354 struct fruit_config_data *config = NULL;
1355 uint8_t *map = MAP_FAILED;
1364 SMB_VFS_HANDLE_GET_DATA(handle, config,
1365 struct fruit_config_data, return false);
1367 if (!config->wipe_intentionally_left_blank_rfork) {
1371 if (ad_getentrylen(ad, ADEID_RFORK) != sizeof(empty_resourcefork)) {
1375 maplen = ad_getentryoff(ad, ADEID_RFORK) +
1376 ad_getentrylen(ad, ADEID_RFORK);
1378 /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
1379 map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
1381 if (map == MAP_FAILED) {
1382 DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
1386 cmp = memcmp(map + ADEDOFF_RFORK_DOT_UND,
1388 sizeof(empty_resourcefork));
1389 rc = munmap(map, maplen);
1391 DBG_ERR("munmap failed: %s\n", strerror(errno));
1399 ad_setentrylen(ad, ADEID_RFORK, 0);
1406 len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
1407 if (len != AD_DATASZ_DOT_UND) {
1415 static bool ad_convert_delete_adfile(struct adouble *ad,
1416 const struct smb_filename *smb_fname)
1418 struct fruit_config_data *config = NULL;
1419 struct smb_filename *ad_name = NULL;
1422 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
1426 SMB_VFS_HANDLE_GET_DATA(ad->ad_handle, config,
1427 struct fruit_config_data, return false);
1429 if (!config->delete_empty_adfiles) {
1433 rc = adouble_path(talloc_tos(), smb_fname, &ad_name);
1438 rc = SMB_VFS_NEXT_UNLINK(ad->ad_handle, ad_name);
1440 DBG_ERR("Unlinking [%s] failed: %s\n",
1441 smb_fname_str_dbg(ad_name), strerror(errno));
1442 TALLOC_FREE(ad_name);
1446 DBG_WARNING("Unlinked [%s] after conversion\n", smb_fname_str_dbg(ad_name));
1447 TALLOC_FREE(ad_name);
1453 * Convert from Apple's ._ file to Netatalk
1455 * Apple's AppleDouble may contain a FinderInfo entry longer then 32
1456 * bytes containing packed xattrs.
1458 * @return -1 in case an error occurred, 0 if no conversion was done, 1
1461 static int ad_convert(struct vfs_handle_struct *handle,
1462 const struct smb_filename *smb_fname)
1464 struct adouble *ad = NULL;
1466 bool converted_xattr = false;
1470 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
1475 ok = ad_convert_xattr(handle, ad, smb_fname, &converted_xattr);
1481 ok = ad_convert_blank_rfork(handle, ad, &blank);
1487 if (converted_xattr || blank) {
1488 ok = ad_convert_truncate(ad, smb_fname);
1495 ok = ad_convert_finderinfo(ad, smb_fname);
1497 DBG_ERR("Failed to convert [%s]\n",
1498 smb_fname_str_dbg(smb_fname));
1503 ok = ad_convert_delete_adfile(ad, smb_fname);
1516 * Read and parse Netatalk AppleDouble metadata xattr
1518 static ssize_t ad_read_meta(vfs_handle_struct *handle,
1520 const struct smb_filename *smb_fname)
1526 DEBUG(10, ("reading meta xattr for %s\n", smb_fname->base_name));
1528 ealen = SMB_VFS_GETXATTR(handle->conn, smb_fname,
1529 AFPINFO_EA_NETATALK, ad->ad_data,
1535 if (errno == ENOATTR) {
1541 DEBUG(2, ("error reading meta xattr: %s\n",
1547 if (ealen != AD_DATASZ_XATTR) {
1548 DEBUG(2, ("bad size %zd\n", ealen));
1554 /* Now parse entries */
1555 ok = ad_unpack(ad, ADEID_NUM_XATTR, AD_DATASZ_XATTR);
1557 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1563 if (!ad_getentryoff(ad, ADEID_FINDERI)
1564 || !ad_getentryoff(ad, ADEID_COMMENT)
1565 || !ad_getentryoff(ad, ADEID_FILEDATESI)
1566 || !ad_getentryoff(ad, ADEID_AFPFILEI)
1567 || !ad_getentryoff(ad, ADEID_PRIVDEV)
1568 || !ad_getentryoff(ad, ADEID_PRIVINO)
1569 || !ad_getentryoff(ad, ADEID_PRIVSYN)
1570 || !ad_getentryoff(ad, ADEID_PRIVID)) {
1571 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1578 DEBUG(10, ("reading meta xattr for %s, rc: %d\n",
1579 smb_fname->base_name, rc));
1583 if (errno == EINVAL) {
1585 (void)SMB_VFS_REMOVEXATTR(handle->conn,
1587 AFPINFO_EA_NETATALK);
1595 static int ad_open_rsrc_xattr(const struct smb_filename *smb_fname,
1599 #ifdef HAVE_ATTROPEN
1600 /* FIXME: direct Solaris xattr syscall */
1601 return attropen(smb_fname->base_name,
1602 AFPRESOURCE_EA_NETATALK, flags, mode);
1609 static int ad_open_rsrc_adouble(const struct smb_filename *smb_fname,
1615 struct smb_filename *adp_smb_fname = NULL;
1617 ret = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
1622 fd = open(adp_smb_fname->base_name, flags, mode);
1623 TALLOC_FREE(adp_smb_fname);
1628 static int ad_open_rsrc(vfs_handle_struct *handle,
1629 const struct smb_filename *smb_fname,
1633 struct fruit_config_data *config = NULL;
1636 SMB_VFS_HANDLE_GET_DATA(handle, config,
1637 struct fruit_config_data, return -1);
1639 if (config->rsrc == FRUIT_RSRC_XATTR) {
1640 fd = ad_open_rsrc_xattr(smb_fname, flags, mode);
1642 fd = ad_open_rsrc_adouble(smb_fname, flags, mode);
1649 * Here's the deal: for ADOUBLE_META we can do without an fd as we can issue
1650 * path based xattr calls. For ADOUBLE_RSRC however we need a full-fledged fd
1651 * for file IO on the ._ file.
1653 static int ad_open(vfs_handle_struct *handle,
1656 const struct smb_filename *smb_fname,
1662 DBG_DEBUG("Path [%s] type [%s]\n", smb_fname->base_name,
1663 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
1665 if (ad->ad_type == ADOUBLE_META) {
1669 if ((fsp != NULL) && (fsp->fh != NULL) && (fsp->fh->fd != -1)) {
1670 ad->ad_fd = fsp->fh->fd;
1671 ad->ad_opened = false;
1675 fd = ad_open_rsrc(handle, smb_fname, flags, mode);
1679 ad->ad_opened = true;
1682 DBG_DEBUG("Path [%s] type [%s] fd [%d]\n",
1683 smb_fname->base_name,
1684 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc", fd);
1689 static ssize_t ad_read_rsrc_xattr(vfs_handle_struct *handle,
1695 /* FIXME: direct sys_fstat(), don't have an fsp */
1696 ret = sys_fstat(ad->ad_fd, &st,
1697 lp_fake_directory_create_times(
1698 SNUM(handle->conn)));
1703 ad_setentrylen(ad, ADEID_RFORK, st.st_ex_size);
1704 return st.st_ex_size;
1707 static ssize_t ad_read_rsrc_adouble(vfs_handle_struct *handle,
1709 const struct smb_filename *smb_fname)
1711 SMB_STRUCT_STAT sbuf;
1718 ret = sys_fstat(ad->ad_fd, &sbuf, lp_fake_directory_create_times(
1719 SNUM(handle->conn)));
1725 * AppleDouble file header content and size, two cases:
1727 * - without xattrs it is exactly AD_DATASZ_DOT_UND (82) bytes large
1728 * - with embedded xattrs it can be larger, up to AD_XATTR_MAX_HDR_SIZE
1730 * Read as much as we can up to AD_XATTR_MAX_HDR_SIZE.
1732 size = sbuf.st_ex_size;
1733 if (size > talloc_array_length(ad->ad_data)) {
1734 if (size > AD_XATTR_MAX_HDR_SIZE) {
1735 size = AD_XATTR_MAX_HDR_SIZE;
1737 p_ad = talloc_realloc(ad, ad->ad_data, char, size);
1744 len = sys_pread(ad->ad_fd, ad->ad_data,
1745 talloc_array_length(ad->ad_data), 0);
1746 if (len != talloc_array_length(ad->ad_data)) {
1747 DBG_NOTICE("%s %s: bad size: %zd\n",
1748 smb_fname->base_name, strerror(errno), len);
1752 /* Now parse entries */
1753 ok = ad_unpack(ad, ADEID_NUM_DOT_UND, sbuf.st_ex_size);
1755 DBG_ERR("invalid AppleDouble resource %s\n",
1756 smb_fname->base_name);
1761 if ((ad_getentryoff(ad, ADEID_FINDERI) != ADEDOFF_FINDERI_DOT_UND)
1762 || (ad_getentrylen(ad, ADEID_FINDERI) < ADEDLEN_FINDERI)
1763 || (ad_getentryoff(ad, ADEID_RFORK) < ADEDOFF_RFORK_DOT_UND)) {
1764 DBG_ERR("invalid AppleDouble resource %s\n",
1765 smb_fname->base_name);
1774 * Read and parse resource fork, either ._ AppleDouble file or xattr
1776 static ssize_t ad_read_rsrc(vfs_handle_struct *handle,
1778 const struct smb_filename *smb_fname)
1780 struct fruit_config_data *config = NULL;
1783 SMB_VFS_HANDLE_GET_DATA(handle, config,
1784 struct fruit_config_data, return -1);
1786 if (config->rsrc == FRUIT_RSRC_XATTR) {
1787 len = ad_read_rsrc_xattr(handle, ad);
1789 len = ad_read_rsrc_adouble(handle, ad, smb_fname);
1796 * Read and unpack an AppleDouble metadata xattr or resource
1798 static ssize_t ad_read(vfs_handle_struct *handle,
1800 const struct smb_filename *smb_fname)
1802 switch (ad->ad_type) {
1804 return ad_read_meta(handle, ad, smb_fname);
1806 return ad_read_rsrc(handle, ad, smb_fname);
1812 static int adouble_destructor(struct adouble *ad)
1814 if ((ad->ad_fd != -1) && ad->ad_opened) {
1822 * Allocate a struct adouble without initialiing it
1824 * The struct is either hang of the fsp extension context or if fsp is
1827 * @param[in] ctx talloc context
1828 * @param[in] handle vfs handle
1829 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1831 * @return adouble handle
1833 static struct adouble *ad_alloc(TALLOC_CTX *ctx, vfs_handle_struct *handle,
1834 adouble_type_t type)
1839 struct fruit_config_data *config;
1841 SMB_VFS_HANDLE_GET_DATA(handle, config,
1842 struct fruit_config_data, return NULL);
1846 adsize = AD_DATASZ_XATTR;
1849 if (config->rsrc == FRUIT_RSRC_ADFILE) {
1850 adsize = AD_DATASZ_DOT_UND;
1857 ad = talloc_zero(ctx, struct adouble);
1864 ad->ad_data = talloc_zero_array(ad, char, adsize);
1865 if (ad->ad_data == NULL) {
1871 ad->ad_handle = handle;
1873 ad->ad_magic = AD_MAGIC;
1874 ad->ad_version = AD_VERSION;
1877 talloc_set_destructor(ad, adouble_destructor);
1887 * Allocate and initialize a new struct adouble
1889 * @param[in] ctx talloc context
1890 * @param[in] handle vfs handle
1891 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1893 * @return adouble handle, initialized
1895 static struct adouble *ad_init(TALLOC_CTX *ctx, vfs_handle_struct *handle,
1896 adouble_type_t type)
1899 const struct ad_entry_order *eid;
1900 struct adouble *ad = NULL;
1901 struct fruit_config_data *config;
1902 time_t t = time(NULL);
1904 SMB_VFS_HANDLE_GET_DATA(handle, config,
1905 struct fruit_config_data, return NULL);
1909 eid = entry_order_meta_xattr;
1912 if (config->rsrc == FRUIT_RSRC_ADFILE) {
1913 eid = entry_order_dot_und;
1915 eid = entry_order_rsrc_xattr;
1922 ad = ad_alloc(ctx, handle, type);
1928 ad->ad_eid[eid->id].ade_off = eid->offset;
1929 ad->ad_eid[eid->id].ade_len = eid->len;
1933 /* put something sane in the date fields */
1934 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, t);
1935 ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, t);
1936 ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, t);
1937 ad_setdate(ad, AD_DATE_BACKUP, htonl(AD_DATE_START));
1945 static struct adouble *ad_get_internal(TALLOC_CTX *ctx,
1946 vfs_handle_struct *handle,
1948 const struct smb_filename *smb_fname,
1949 adouble_type_t type)
1953 struct adouble *ad = NULL;
1957 smb_fname = fsp->base_fsp->fsp_name;
1960 DEBUG(10, ("ad_get(%s) called for %s\n",
1961 type == ADOUBLE_META ? "meta" : "rsrc",
1962 smb_fname->base_name));
1964 ad = ad_alloc(ctx, handle, type);
1970 /* Try rw first so we can use the fd in ad_convert() */
1973 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
1974 if (rc == -1 && ((errno == EROFS) || (errno == EACCES))) {
1976 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
1979 DBG_DEBUG("ad_open [%s] error [%s]\n",
1980 smb_fname->base_name, strerror(errno));
1985 len = ad_read(handle, ad, smb_fname);
1987 DEBUG(10, ("error reading AppleDouble for %s\n",
1988 smb_fname->base_name));
1994 DEBUG(10, ("ad_get(%s) for %s returning %d\n",
1995 type == ADOUBLE_META ? "meta" : "rsrc",
1996 smb_fname->base_name, rc));
2005 * Return AppleDouble data for a file
2007 * @param[in] ctx talloc context
2008 * @param[in] handle vfs handle
2009 * @param[in] smb_fname pathname to file or directory
2010 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2012 * @return talloced struct adouble or NULL on error
2014 static struct adouble *ad_get(TALLOC_CTX *ctx,
2015 vfs_handle_struct *handle,
2016 const struct smb_filename *smb_fname,
2017 adouble_type_t type)
2019 return ad_get_internal(ctx, handle, NULL, smb_fname, type);
2023 * Return AppleDouble data for a file
2025 * @param[in] ctx talloc context
2026 * @param[in] handle vfs handle
2027 * @param[in] fsp fsp to use for IO
2028 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2030 * @return talloced struct adouble or NULL on error
2032 static struct adouble *ad_fget(TALLOC_CTX *ctx, vfs_handle_struct *handle,
2033 files_struct *fsp, adouble_type_t type)
2035 return ad_get_internal(ctx, handle, fsp, NULL, type);
2039 * Set AppleDouble metadata on a file or directory
2041 * @param[in] ad adouble handle
2043 * @param[in] smb_fname pathname to file or directory
2045 * @return status code, 0 means success
2047 static int ad_set(vfs_handle_struct *handle,
2049 const struct smb_filename *smb_fname)
2054 DBG_DEBUG("Path [%s]\n", smb_fname->base_name);
2056 if (ad->ad_type != ADOUBLE_META) {
2057 DBG_ERR("ad_set on [%s] used with ADOUBLE_RSRC\n",
2058 smb_fname->base_name);
2067 ret = SMB_VFS_SETXATTR(handle->conn,
2069 AFPINFO_EA_NETATALK,
2071 AD_DATASZ_XATTR, 0);
2073 DBG_DEBUG("Path [%s] ret [%d]\n", smb_fname->base_name, ret);
2079 * Set AppleDouble metadata on a file or directory
2081 * @param[in] ad adouble handle
2082 * @param[in] fsp file handle
2084 * @return status code, 0 means success
2086 static int ad_fset(struct vfs_handle_struct *handle,
2094 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
2097 || (fsp->fh == NULL)
2098 || (fsp->fh->fd == -1))
2100 smb_panic("bad fsp");
2108 switch (ad->ad_type) {
2110 rc = SMB_VFS_NEXT_SETXATTR(handle,
2112 AFPINFO_EA_NETATALK,
2114 AD_DATASZ_XATTR, 0);
2118 len = SMB_VFS_NEXT_PWRITE(handle,
2123 if (len != AD_DATASZ_DOT_UND) {
2124 DBG_ERR("short write on %s: %zd", fsp_str_dbg(fsp), len);
2134 DBG_DEBUG("Path [%s] rc [%d]\n", fsp_str_dbg(fsp), rc);
2139 /*****************************************************************************
2141 *****************************************************************************/
2143 static bool is_afpinfo_stream(const struct smb_filename *smb_fname)
2145 if (strncasecmp_m(smb_fname->stream_name,
2146 AFPINFO_STREAM_NAME,
2147 strlen(AFPINFO_STREAM_NAME)) == 0) {
2153 static bool is_afpresource_stream(const struct smb_filename *smb_fname)
2155 if (strncasecmp_m(smb_fname->stream_name,
2156 AFPRESOURCE_STREAM_NAME,
2157 strlen(AFPRESOURCE_STREAM_NAME)) == 0) {
2164 * Test whether stream is an Apple stream.
2166 static bool is_apple_stream(const struct smb_filename *smb_fname)
2168 if (is_afpinfo_stream(smb_fname)) {
2171 if (is_afpresource_stream(smb_fname)) {
2178 * Initialize config struct from our smb.conf config parameters
2180 static int init_fruit_config(vfs_handle_struct *handle)
2182 struct fruit_config_data *config;
2184 const char *tm_size_str = NULL;
2186 config = talloc_zero(handle->conn, struct fruit_config_data);
2188 DEBUG(1, ("talloc_zero() failed\n"));
2194 * Versions up to Samba 4.5.x had a spelling bug in the
2195 * fruit:resource option calling lp_parm_enum with
2196 * "res*s*ource" (ie two s).
2198 * In Samba 4.6 we accept both the wrong and the correct
2199 * spelling, in Samba 4.7 the bad spelling will be removed.
2201 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2202 "ressource", fruit_rsrc, FRUIT_RSRC_ADFILE);
2203 if (enumval == -1) {
2204 DEBUG(1, ("value for %s: resource type unknown\n",
2205 FRUIT_PARAM_TYPE_NAME));
2208 config->rsrc = (enum fruit_rsrc)enumval;
2210 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2211 "resource", fruit_rsrc, enumval);
2212 if (enumval == -1) {
2213 DEBUG(1, ("value for %s: resource type unknown\n",
2214 FRUIT_PARAM_TYPE_NAME));
2217 config->rsrc = (enum fruit_rsrc)enumval;
2219 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2220 "metadata", fruit_meta, FRUIT_META_NETATALK);
2221 if (enumval == -1) {
2222 DEBUG(1, ("value for %s: metadata type unknown\n",
2223 FRUIT_PARAM_TYPE_NAME));
2226 config->meta = (enum fruit_meta)enumval;
2228 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2229 "locking", fruit_locking, FRUIT_LOCKING_NONE);
2230 if (enumval == -1) {
2231 DEBUG(1, ("value for %s: locking type unknown\n",
2232 FRUIT_PARAM_TYPE_NAME));
2235 config->locking = (enum fruit_locking)enumval;
2237 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2238 "encoding", fruit_encoding, FRUIT_ENC_PRIVATE);
2239 if (enumval == -1) {
2240 DEBUG(1, ("value for %s: encoding type unknown\n",
2241 FRUIT_PARAM_TYPE_NAME));
2244 config->encoding = (enum fruit_encoding)enumval;
2246 if (config->rsrc == FRUIT_RSRC_ADFILE) {
2247 config->veto_appledouble = lp_parm_bool(SNUM(handle->conn),
2248 FRUIT_PARAM_TYPE_NAME,
2253 config->use_aapl = lp_parm_bool(
2254 -1, FRUIT_PARAM_TYPE_NAME, "aapl", true);
2256 config->time_machine = lp_parm_bool(
2257 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "time machine", false);
2259 config->unix_info_enabled = lp_parm_bool(
2260 -1, FRUIT_PARAM_TYPE_NAME, "nfs_aces", true);
2262 config->use_copyfile = lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME,
2265 config->posix_rename = lp_parm_bool(
2266 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "posix_rename", true);
2268 config->aapl_zero_file_id =
2269 lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME, "zero_file_id", true);
2271 config->readdir_attr_rsize = lp_parm_bool(
2272 SNUM(handle->conn), "readdir_attr", "aapl_rsize", true);
2274 config->readdir_attr_finder_info = lp_parm_bool(
2275 SNUM(handle->conn), "readdir_attr", "aapl_finder_info", true);
2277 config->readdir_attr_max_access = lp_parm_bool(
2278 SNUM(handle->conn), "readdir_attr", "aapl_max_access", true);
2280 config->model = lp_parm_const_string(
2281 -1, FRUIT_PARAM_TYPE_NAME, "model", "MacSamba");
2283 tm_size_str = lp_parm_const_string(
2284 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2285 "time machine max size", NULL);
2286 if (tm_size_str != NULL) {
2287 config->time_machine_max_size = conv_str_size(tm_size_str);
2290 config->wipe_intentionally_left_blank_rfork = lp_parm_bool(
2291 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2292 "wipe_intentionally_left_blank_rfork", false);
2294 config->delete_empty_adfiles = lp_parm_bool(
2295 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2296 "delete_empty_adfiles", false);
2298 SMB_VFS_HANDLE_SET_DATA(handle, config,
2299 NULL, struct fruit_config_data,
2306 * Prepend "._" to a basename
2307 * Return a new struct smb_filename with stream_name == NULL.
2309 static int adouble_path(TALLOC_CTX *ctx,
2310 const struct smb_filename *smb_fname_in,
2311 struct smb_filename **pp_smb_fname_out)
2315 struct smb_filename *smb_fname = cp_smb_filename(ctx,
2318 if (smb_fname == NULL) {
2322 /* We need streamname to be NULL */
2323 TALLOC_FREE(smb_fname->stream_name);
2325 /* And we're replacing base_name. */
2326 TALLOC_FREE(smb_fname->base_name);
2328 if (!parent_dirname(smb_fname, smb_fname_in->base_name,
2330 TALLOC_FREE(smb_fname);
2334 smb_fname->base_name = talloc_asprintf(smb_fname,
2335 "%s/._%s", parent, base);
2336 if (smb_fname->base_name == NULL) {
2337 TALLOC_FREE(smb_fname);
2341 *pp_smb_fname_out = smb_fname;
2347 * Allocate and initialize an AfpInfo struct
2349 static AfpInfo *afpinfo_new(TALLOC_CTX *ctx)
2351 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2355 ai->afpi_Signature = AFP_Signature;
2356 ai->afpi_Version = AFP_Version;
2357 ai->afpi_BackupTime = AD_DATE_START;
2362 * Pack an AfpInfo struct into a buffer
2364 * Buffer size must be at least AFP_INFO_SIZE
2365 * Returns size of packed buffer
2367 static ssize_t afpinfo_pack(const AfpInfo *ai, char *buf)
2369 memset(buf, 0, AFP_INFO_SIZE);
2371 RSIVAL(buf, 0, ai->afpi_Signature);
2372 RSIVAL(buf, 4, ai->afpi_Version);
2373 RSIVAL(buf, 12, ai->afpi_BackupTime);
2374 memcpy(buf + 16, ai->afpi_FinderInfo, sizeof(ai->afpi_FinderInfo));
2376 return AFP_INFO_SIZE;
2380 * Unpack a buffer into a AfpInfo structure
2382 * Buffer size must be at least AFP_INFO_SIZE
2383 * Returns allocated AfpInfo struct
2385 static AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data)
2387 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2392 ai->afpi_Signature = RIVAL(data, 0);
2393 ai->afpi_Version = RIVAL(data, 4);
2394 ai->afpi_BackupTime = RIVAL(data, 12);
2395 memcpy(ai->afpi_FinderInfo, (const char *)data + 16,
2396 sizeof(ai->afpi_FinderInfo));
2398 if (ai->afpi_Signature != AFP_Signature
2399 || ai->afpi_Version != AFP_Version) {
2400 DEBUG(1, ("Bad AfpInfo signature or version\n"));
2408 * Fake an inode number from the md5 hash of the (xattr) name
2410 static SMB_INO_T fruit_inode(const SMB_STRUCT_STAT *sbuf, const char *sname)
2412 gnutls_hash_hd_t hash_hnd = NULL;
2413 unsigned char hash[16];
2414 SMB_INO_T result = 0;
2418 DBG_DEBUG("fruit_inode called for %ju/%ju [%s]\n",
2419 (uintmax_t)sbuf->st_ex_dev,
2420 (uintmax_t)sbuf->st_ex_ino, sname);
2422 upper_sname = talloc_strdup_upper(talloc_tos(), sname);
2423 SMB_ASSERT(upper_sname != NULL);
2425 rc = gnutls_hash_init(&hash_hnd, GNUTLS_DIG_MD5);
2430 rc = gnutls_hash(hash_hnd, &(sbuf->st_ex_dev), sizeof(sbuf->st_ex_dev));
2432 gnutls_hash_deinit(hash_hnd, NULL);
2435 rc = gnutls_hash(hash_hnd,
2437 sizeof(sbuf->st_ex_ino));
2439 gnutls_hash_deinit(hash_hnd, NULL);
2442 rc = gnutls_hash(hash_hnd,
2444 talloc_get_size(upper_sname) - 1);
2446 gnutls_hash_deinit(hash_hnd, NULL);
2450 gnutls_hash_deinit(hash_hnd, hash);
2452 /* Hopefully all the variation is in the lower 4 (or 8) bytes! */
2453 memcpy(&result, hash, sizeof(result));
2456 DBG_DEBUG("fruit_inode \"%s\": ino=%ju\n",
2457 sname, (uintmax_t)result);
2460 TALLOC_FREE(upper_sname);
2465 static bool add_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
2466 struct stream_struct **streams,
2467 const char *name, off_t size,
2470 struct stream_struct *tmp;
2472 tmp = talloc_realloc(mem_ctx, *streams, struct stream_struct,
2478 tmp[*num_streams].name = talloc_asprintf(tmp, "%s:$DATA", name);
2479 if (tmp[*num_streams].name == NULL) {
2483 tmp[*num_streams].size = size;
2484 tmp[*num_streams].alloc_size = alloc_size;
2491 static bool filter_empty_rsrc_stream(unsigned int *num_streams,
2492 struct stream_struct **streams)
2494 struct stream_struct *tmp = *streams;
2497 if (*num_streams == 0) {
2501 for (i = 0; i < *num_streams; i++) {
2502 if (strequal_m(tmp[i].name, AFPRESOURCE_STREAM)) {
2507 if (i == *num_streams) {
2511 if (tmp[i].size > 0) {
2515 TALLOC_FREE(tmp[i].name);
2516 if (*num_streams - 1 > i) {
2517 memmove(&tmp[i], &tmp[i+1],
2518 (*num_streams - i - 1) * sizeof(struct stream_struct));
2525 static bool del_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
2526 struct stream_struct **streams,
2529 struct stream_struct *tmp = *streams;
2532 if (*num_streams == 0) {
2536 for (i = 0; i < *num_streams; i++) {
2537 if (strequal_m(tmp[i].name, name)) {
2542 if (i == *num_streams) {
2546 TALLOC_FREE(tmp[i].name);
2547 if (*num_streams - 1 > i) {
2548 memmove(&tmp[i], &tmp[i+1],
2549 (*num_streams - i - 1) * sizeof(struct stream_struct));
2556 static bool ad_empty_finderinfo(const struct adouble *ad)
2559 char emptybuf[ADEDLEN_FINDERI] = {0};
2562 fi = ad_get_entry(ad, ADEID_FINDERI);
2564 DBG_ERR("Missing FinderInfo in struct adouble [%p]\n", ad);
2568 cmp = memcmp(emptybuf, fi, ADEDLEN_FINDERI);
2572 static bool ai_empty_finderinfo(const AfpInfo *ai)
2575 char emptybuf[ADEDLEN_FINDERI] = {0};
2577 cmp = memcmp(emptybuf, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
2582 * Update btime with btime from Netatalk
2584 static void update_btime(vfs_handle_struct *handle,
2585 struct smb_filename *smb_fname)
2588 struct timespec creation_time = {0};
2590 struct fruit_config_data *config = NULL;
2592 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
2595 switch (config->meta) {
2596 case FRUIT_META_STREAM:
2598 case FRUIT_META_NETATALK:
2602 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
2606 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
2610 if (ad_getdate(ad, AD_DATE_UNIX | AD_DATE_CREATE, &t) != 0) {
2616 creation_time.tv_sec = convert_uint32_t_to_time_t(t);
2617 update_stat_ex_create_time(&smb_fname->st, creation_time);
2623 * Map an access mask to a Netatalk single byte byte range lock
2625 static off_t access_to_netatalk_brl(enum apple_fork fork_type,
2626 uint32_t access_mask)
2630 switch (access_mask) {
2631 case FILE_READ_DATA:
2632 offset = AD_FILELOCK_OPEN_RD;
2635 case FILE_WRITE_DATA:
2636 case FILE_APPEND_DATA:
2637 offset = AD_FILELOCK_OPEN_WR;
2641 offset = AD_FILELOCK_OPEN_NONE;
2645 if (fork_type == APPLE_FORK_RSRC) {
2646 if (offset == AD_FILELOCK_OPEN_NONE) {
2647 offset = AD_FILELOCK_RSRC_OPEN_NONE;
2657 * Map a deny mode to a Netatalk brl
2659 static off_t denymode_to_netatalk_brl(enum apple_fork fork_type,
2664 switch (deny_mode) {
2666 offset = AD_FILELOCK_DENY_RD;
2670 offset = AD_FILELOCK_DENY_WR;
2674 smb_panic("denymode_to_netatalk_brl: bad deny mode\n");
2677 if (fork_type == APPLE_FORK_RSRC) {
2685 * Call fcntl() with an exclusive F_GETLK request in order to
2686 * determine if there's an exisiting shared lock
2688 * @return true if the requested lock was found or any error occurred
2689 * false if the lock was not found
2691 static bool test_netatalk_lock(files_struct *fsp, off_t in_offset)
2694 off_t offset = in_offset;
2699 result = SMB_VFS_GETLOCK(fsp, &offset, &len, &type, &pid);
2700 if (result == false) {
2704 if (type != F_UNLCK) {
2711 static NTSTATUS fruit_check_access(vfs_handle_struct *handle,
2713 uint32_t access_mask,
2714 uint32_t share_mode)
2716 NTSTATUS status = NT_STATUS_OK;
2718 bool share_for_read = (share_mode & FILE_SHARE_READ);
2719 bool share_for_write = (share_mode & FILE_SHARE_WRITE);
2720 bool netatalk_already_open_for_reading = false;
2721 bool netatalk_already_open_for_writing = false;
2722 bool netatalk_already_open_with_deny_read = false;
2723 bool netatalk_already_open_with_deny_write = false;
2725 /* FIXME: hardcoded data fork, add resource fork */
2726 enum apple_fork fork_type = APPLE_FORK_DATA;
2728 DBG_DEBUG("fruit_check_access: %s, am: %s/%s, sm: 0x%x\n",
2730 access_mask & FILE_READ_DATA ? "READ" :"-",
2731 access_mask & FILE_WRITE_DATA ? "WRITE" : "-",
2734 if (fsp->fh->fd == -1) {
2735 return NT_STATUS_OK;
2738 /* Read NetATalk opens and deny modes on the file. */
2739 netatalk_already_open_for_reading = test_netatalk_lock(fsp,
2740 access_to_netatalk_brl(fork_type,
2743 netatalk_already_open_with_deny_read = test_netatalk_lock(fsp,
2744 denymode_to_netatalk_brl(fork_type,
2747 netatalk_already_open_for_writing = test_netatalk_lock(fsp,
2748 access_to_netatalk_brl(fork_type,
2751 netatalk_already_open_with_deny_write = test_netatalk_lock(fsp,
2752 denymode_to_netatalk_brl(fork_type,
2755 /* If there are any conflicts - sharing violation. */
2756 if ((access_mask & FILE_READ_DATA) &&
2757 netatalk_already_open_with_deny_read) {
2758 return NT_STATUS_SHARING_VIOLATION;
2761 if (!share_for_read &&
2762 netatalk_already_open_for_reading) {
2763 return NT_STATUS_SHARING_VIOLATION;
2766 if ((access_mask & FILE_WRITE_DATA) &&
2767 netatalk_already_open_with_deny_write) {
2768 return NT_STATUS_SHARING_VIOLATION;
2771 if (!share_for_write &&
2772 netatalk_already_open_for_writing) {
2773 return NT_STATUS_SHARING_VIOLATION;
2776 if (!(access_mask & FILE_READ_DATA)) {
2778 * Nothing we can do here, we need read access
2781 return NT_STATUS_OK;
2784 /* Set NetAtalk locks matching our access */
2785 if (access_mask & FILE_READ_DATA) {
2786 struct byte_range_lock *br_lck = NULL;
2788 off = access_to_netatalk_brl(fork_type, FILE_READ_DATA);
2790 handle->conn->sconn->msg_ctx, fsp,
2791 fsp->op->global->open_persistent_id, 1, off,
2792 READ_LOCK, POSIX_LOCK, false,
2795 TALLOC_FREE(br_lck);
2797 if (!NT_STATUS_IS_OK(status)) {
2802 if (!share_for_read) {
2803 struct byte_range_lock *br_lck = NULL;
2805 off = denymode_to_netatalk_brl(fork_type, DENY_READ);
2807 handle->conn->sconn->msg_ctx, fsp,
2808 fsp->op->global->open_persistent_id, 1, off,
2809 READ_LOCK, POSIX_LOCK, false,
2812 TALLOC_FREE(br_lck);
2814 if (!NT_STATUS_IS_OK(status)) {
2819 if (access_mask & FILE_WRITE_DATA) {
2820 struct byte_range_lock *br_lck = NULL;
2822 off = access_to_netatalk_brl(fork_type, FILE_WRITE_DATA);
2824 handle->conn->sconn->msg_ctx, fsp,
2825 fsp->op->global->open_persistent_id, 1, off,
2826 READ_LOCK, POSIX_LOCK, false,
2829 TALLOC_FREE(br_lck);
2831 if (!NT_STATUS_IS_OK(status)) {
2836 if (!share_for_write) {
2837 struct byte_range_lock *br_lck = NULL;
2839 off = denymode_to_netatalk_brl(fork_type, DENY_WRITE);
2841 handle->conn->sconn->msg_ctx, fsp,
2842 fsp->op->global->open_persistent_id, 1, off,
2843 READ_LOCK, POSIX_LOCK, false,
2846 TALLOC_FREE(br_lck);
2848 if (!NT_STATUS_IS_OK(status)) {
2853 return NT_STATUS_OK;
2856 static NTSTATUS check_aapl(vfs_handle_struct *handle,
2857 struct smb_request *req,
2858 const struct smb2_create_blobs *in_context_blobs,
2859 struct smb2_create_blobs *out_context_blobs)
2861 struct fruit_config_data *config;
2863 struct smb2_create_blob *aapl = NULL;
2867 DATA_BLOB blob = data_blob_talloc(req, NULL, 0);
2868 uint64_t req_bitmap, client_caps;
2869 uint64_t server_caps = SMB2_CRTCTX_AAPL_UNIX_BASED;
2873 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
2874 return NT_STATUS_UNSUCCESSFUL);
2876 if (!config->use_aapl
2877 || in_context_blobs == NULL
2878 || out_context_blobs == NULL) {
2879 return NT_STATUS_OK;
2882 aapl = smb2_create_blob_find(in_context_blobs,
2883 SMB2_CREATE_TAG_AAPL);
2885 return NT_STATUS_OK;
2888 if (aapl->data.length != 24) {
2889 DEBUG(1, ("unexpected AAPL ctxt length: %ju\n",
2890 (uintmax_t)aapl->data.length));
2891 return NT_STATUS_INVALID_PARAMETER;
2894 cmd = IVAL(aapl->data.data, 0);
2895 if (cmd != SMB2_CRTCTX_AAPL_SERVER_QUERY) {
2896 DEBUG(1, ("unsupported AAPL cmd: %d\n", cmd));
2897 return NT_STATUS_INVALID_PARAMETER;
2900 req_bitmap = BVAL(aapl->data.data, 8);
2901 client_caps = BVAL(aapl->data.data, 16);
2903 SIVAL(p, 0, SMB2_CRTCTX_AAPL_SERVER_QUERY);
2905 SBVAL(p, 8, req_bitmap);
2906 ok = data_blob_append(req, &blob, p, 16);
2908 return NT_STATUS_UNSUCCESSFUL;
2911 if (req_bitmap & SMB2_CRTCTX_AAPL_SERVER_CAPS) {
2912 if ((client_caps & SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR) &&
2913 (handle->conn->tcon->compat->fs_capabilities & FILE_NAMED_STREAMS)) {
2914 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR;
2915 config->readdir_attr_enabled = true;
2918 if (config->use_copyfile) {
2919 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_OSX_COPYFILE;
2920 config->copyfile_enabled = true;
2924 * The client doesn't set the flag, so we can't check
2925 * for it and just set it unconditionally
2927 if (config->unix_info_enabled) {
2928 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_NFS_ACE;
2931 SBVAL(p, 0, server_caps);
2932 ok = data_blob_append(req, &blob, p, 8);
2934 return NT_STATUS_UNSUCCESSFUL;
2938 if (req_bitmap & SMB2_CRTCTX_AAPL_VOLUME_CAPS) {
2939 int val = lp_case_sensitive(SNUM(handle->conn->tcon->compat));
2947 caps |= SMB2_CRTCTX_AAPL_CASE_SENSITIVE;
2954 if (config->time_machine) {
2955 caps |= SMB2_CRTCTX_AAPL_FULL_SYNC;
2960 ok = data_blob_append(req, &blob, p, 8);
2962 return NT_STATUS_UNSUCCESSFUL;
2966 if (req_bitmap & SMB2_CRTCTX_AAPL_MODEL_INFO) {
2967 ok = convert_string_talloc(req,
2968 CH_UNIX, CH_UTF16LE,
2969 config->model, strlen(config->model),
2972 return NT_STATUS_UNSUCCESSFUL;
2976 SIVAL(p + 4, 0, modellen);
2977 ok = data_blob_append(req, &blob, p, 8);
2980 return NT_STATUS_UNSUCCESSFUL;
2983 ok = data_blob_append(req, &blob, model, modellen);
2986 return NT_STATUS_UNSUCCESSFUL;
2990 status = smb2_create_blob_add(out_context_blobs,
2992 SMB2_CREATE_TAG_AAPL,
2994 if (NT_STATUS_IS_OK(status)) {
2995 global_fruit_config.nego_aapl = true;
2996 if (config->aapl_zero_file_id) {
2997 aapl_force_zero_file_id(handle->conn->sconn);
3004 static bool readdir_attr_meta_finderi_stream(
3005 struct vfs_handle_struct *handle,
3006 const struct smb_filename *smb_fname,
3009 struct smb_filename *stream_name = NULL;
3010 files_struct *fsp = NULL;
3015 uint8_t buf[AFP_INFO_SIZE];
3017 stream_name = synthetic_smb_fname(talloc_tos(),
3018 smb_fname->base_name,
3019 AFPINFO_STREAM_NAME,
3020 NULL, smb_fname->flags);
3021 if (stream_name == NULL) {
3025 ret = SMB_VFS_STAT(handle->conn, stream_name);
3030 status = SMB_VFS_CREATE_FILE(
3031 handle->conn, /* conn */
3033 0, /* root_dir_fid */
3034 stream_name, /* fname */
3035 FILE_READ_DATA, /* access_mask */
3036 (FILE_SHARE_READ | FILE_SHARE_WRITE | /* share_access */
3038 FILE_OPEN, /* create_disposition*/
3039 0, /* create_options */
3040 0, /* file_attributes */
3041 INTERNAL_OPEN_ONLY, /* oplock_request */
3043 0, /* allocation_size */
3044 0, /* private_flags */
3049 NULL, NULL); /* create context */
3051 TALLOC_FREE(stream_name);
3053 if (!NT_STATUS_IS_OK(status)) {
3057 nread = SMB_VFS_PREAD(fsp, &buf[0], AFP_INFO_SIZE, 0);
3058 if (nread != AFP_INFO_SIZE) {
3059 DBG_ERR("short read [%s] [%zd/%d]\n",
3060 smb_fname_str_dbg(stream_name), nread, AFP_INFO_SIZE);
3065 memcpy(&ai->afpi_FinderInfo[0], &buf[AFP_OFF_FinderInfo],
3072 close_file(NULL, fsp, NORMAL_CLOSE);
3078 static bool readdir_attr_meta_finderi_netatalk(
3079 struct vfs_handle_struct *handle,
3080 const struct smb_filename *smb_fname,
3083 struct adouble *ad = NULL;
3086 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
3091 p = ad_get_entry(ad, ADEID_FINDERI);
3093 DBG_ERR("No ADEID_FINDERI for [%s]\n", smb_fname->base_name);
3098 memcpy(&ai->afpi_FinderInfo[0], p, AFP_FinderSize);
3103 static bool readdir_attr_meta_finderi(struct vfs_handle_struct *handle,
3104 const struct smb_filename *smb_fname,
3105 struct readdir_attr_data *attr_data)
3107 struct fruit_config_data *config = NULL;
3108 uint32_t date_added;
3112 SMB_VFS_HANDLE_GET_DATA(handle, config,
3113 struct fruit_config_data,
3116 switch (config->meta) {
3117 case FRUIT_META_NETATALK:
3118 ok = readdir_attr_meta_finderi_netatalk(
3119 handle, smb_fname, &ai);
3122 case FRUIT_META_STREAM:
3123 ok = readdir_attr_meta_finderi_stream(
3124 handle, smb_fname, &ai);
3128 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3133 /* Don't bother with errors, it's likely ENOENT */
3137 if (S_ISREG(smb_fname->st.st_ex_mode)) {
3139 memcpy(&attr_data->attr_data.aapl.finder_info[0],
3140 &ai.afpi_FinderInfo[0], 4);
3142 /* finder_creator */
3143 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 4,
3144 &ai.afpi_FinderInfo[4], 4);
3148 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 8,
3149 &ai.afpi_FinderInfo[8], 2);
3151 /* finder_ext_flags */
3152 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 10,
3153 &ai.afpi_FinderInfo[24], 2);
3156 date_added = convert_time_t_to_uint32_t(
3157 smb_fname->st.st_ex_btime.tv_sec - AD_DATE_DELTA);
3159 RSIVAL(&attr_data->attr_data.aapl.finder_info[0], 12, date_added);
3164 static uint64_t readdir_attr_rfork_size_adouble(
3165 struct vfs_handle_struct *handle,
3166 const struct smb_filename *smb_fname)
3168 struct adouble *ad = NULL;
3169 uint64_t rfork_size;
3171 ad = ad_get(talloc_tos(), handle, smb_fname,
3177 rfork_size = ad_getentrylen(ad, ADEID_RFORK);
3183 static uint64_t readdir_attr_rfork_size_stream(
3184 struct vfs_handle_struct *handle,
3185 const struct smb_filename *smb_fname)
3187 struct smb_filename *stream_name = NULL;
3189 uint64_t rfork_size;
3191 stream_name = synthetic_smb_fname(talloc_tos(),
3192 smb_fname->base_name,
3193 AFPRESOURCE_STREAM_NAME,
3195 if (stream_name == NULL) {
3199 ret = SMB_VFS_STAT(handle->conn, stream_name);
3201 TALLOC_FREE(stream_name);
3205 rfork_size = stream_name->st.st_ex_size;
3206 TALLOC_FREE(stream_name);
3211 static uint64_t readdir_attr_rfork_size(struct vfs_handle_struct *handle,
3212 const struct smb_filename *smb_fname)
3214 struct fruit_config_data *config = NULL;
3215 uint64_t rfork_size;
3217 SMB_VFS_HANDLE_GET_DATA(handle, config,
3218 struct fruit_config_data,
3221 switch (config->rsrc) {
3222 case FRUIT_RSRC_ADFILE:
3223 case FRUIT_RSRC_XATTR:
3224 rfork_size = readdir_attr_rfork_size_adouble(handle,
3228 case FRUIT_META_STREAM:
3229 rfork_size = readdir_attr_rfork_size_stream(handle,
3234 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3242 static NTSTATUS readdir_attr_macmeta(struct vfs_handle_struct *handle,
3243 const struct smb_filename *smb_fname,
3244 struct readdir_attr_data *attr_data)
3246 NTSTATUS status = NT_STATUS_OK;
3247 struct fruit_config_data *config = NULL;
3250 SMB_VFS_HANDLE_GET_DATA(handle, config,
3251 struct fruit_config_data,
3252 return NT_STATUS_UNSUCCESSFUL);
3255 /* Ensure we return a default value in the creation_date field */
3256 RSIVAL(&attr_data->attr_data.aapl.finder_info, 12, AD_DATE_START);
3259 * Resource fork length
3262 if (config->readdir_attr_rsize) {
3263 uint64_t rfork_size;
3265 rfork_size = readdir_attr_rfork_size(handle, smb_fname);
3266 attr_data->attr_data.aapl.rfork_size = rfork_size;
3273 if (config->readdir_attr_finder_info) {
3274 ok = readdir_attr_meta_finderi(handle, smb_fname, attr_data);
3276 status = NT_STATUS_INTERNAL_ERROR;
3283 static NTSTATUS remove_virtual_nfs_aces(struct security_descriptor *psd)
3288 if (psd->dacl == NULL) {
3289 return NT_STATUS_OK;
3292 for (i = 0; i < psd->dacl->num_aces; i++) {
3293 /* MS NFS style mode/uid/gid */
3294 int cmp = dom_sid_compare_domain(
3295 &global_sid_Unix_NFS,
3296 &psd->dacl->aces[i].trustee);
3298 /* Normal ACE entry. */
3303 * security_descriptor_dacl_del()
3304 * *must* return NT_STATUS_OK as we know
3305 * we have something to remove.
3308 status = security_descriptor_dacl_del(psd,
3309 &psd->dacl->aces[i].trustee);
3310 if (!NT_STATUS_IS_OK(status)) {
3311 DBG_WARNING("failed to remove MS NFS style ACE: %s\n",
3317 * security_descriptor_dacl_del() may delete more
3318 * then one entry subsequent to this one if the
3319 * SID matches, but we only need to ensure that
3320 * we stay looking at the same element in the array.
3324 return NT_STATUS_OK;
3327 /* Search MS NFS style ACE with UNIX mode */
3328 static NTSTATUS check_ms_nfs(vfs_handle_struct *handle,
3330 struct security_descriptor *psd,
3335 struct fruit_config_data *config = NULL;
3339 SMB_VFS_HANDLE_GET_DATA(handle, config,
3340 struct fruit_config_data,
3341 return NT_STATUS_UNSUCCESSFUL);
3343 if (!global_fruit_config.nego_aapl) {
3344 return NT_STATUS_OK;
3346 if (psd->dacl == NULL || !config->unix_info_enabled) {
3347 return NT_STATUS_OK;
3350 for (i = 0; i < psd->dacl->num_aces; i++) {
3351 if (dom_sid_compare_domain(
3352 &global_sid_Unix_NFS_Mode,
3353 &psd->dacl->aces[i].trustee) == 0) {
3354 *pmode = (mode_t)psd->dacl->aces[i].trustee.sub_auths[2];
3355 *pmode &= (S_IRWXU | S_IRWXG | S_IRWXO);
3358 DEBUG(10, ("MS NFS chmod request %s, %04o\n",
3359 fsp_str_dbg(fsp), (unsigned)(*pmode)));
3365 * Remove any incoming virtual ACE entries generated by
3366 * fruit_fget_nt_acl().
3369 return remove_virtual_nfs_aces(psd);
3372 /****************************************************************************
3374 ****************************************************************************/
3376 static int fruit_connect(vfs_handle_struct *handle,
3377 const char *service,
3381 char *list = NULL, *newlist = NULL;
3382 struct fruit_config_data *config;
3384 DEBUG(10, ("fruit_connect\n"));
3386 rc = SMB_VFS_NEXT_CONNECT(handle, service, user);
3391 rc = init_fruit_config(handle);
3396 SMB_VFS_HANDLE_GET_DATA(handle, config,
3397 struct fruit_config_data, return -1);
3399 if (config->veto_appledouble) {
3400 list = lp_veto_files(talloc_tos(), SNUM(handle->conn));
3403 if (strstr(list, "/" ADOUBLE_NAME_PREFIX "*/") == NULL) {
3404 newlist = talloc_asprintf(
3406 "%s/" ADOUBLE_NAME_PREFIX "*/",
3408 lp_do_parameter(SNUM(handle->conn),
3413 lp_do_parameter(SNUM(handle->conn),
3415 "/" ADOUBLE_NAME_PREFIX "*/");
3421 if (config->encoding == FRUIT_ENC_NATIVE) {
3422 lp_do_parameter(SNUM(handle->conn),
3427 if (config->time_machine) {
3428 DBG_NOTICE("Enabling durable handles for Time Machine "
3429 "support on [%s]\n", service);
3430 lp_do_parameter(SNUM(handle->conn), "durable handles", "yes");
3431 lp_do_parameter(SNUM(handle->conn), "kernel oplocks", "no");
3432 lp_do_parameter(SNUM(handle->conn), "kernel share modes", "no");
3433 if (!lp_strict_sync(SNUM(handle->conn))) {
3434 DBG_WARNING("Time Machine without strict sync is not "
3437 lp_do_parameter(SNUM(handle->conn), "posix locking", "no");
3443 static int fruit_fake_fd(void)
3450 * Return a valid fd, but ensure any attempt to use it returns
3451 * an error (EPIPE). Once we get a write on the handle, we open
3454 ret = pipe(pipe_fds);
3464 static int fruit_open_meta_stream(vfs_handle_struct *handle,
3465 struct smb_filename *smb_fname,
3470 struct fruit_config_data *config = NULL;
3471 struct fio *fio = NULL;
3472 int open_flags = flags & ~O_CREAT;
3475 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3477 SMB_VFS_HANDLE_GET_DATA(handle, config,
3478 struct fruit_config_data, return -1);
3480 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3481 fio->type = ADOUBLE_META;
3482 fio->config = config;
3484 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, open_flags, mode);
3489 if (!(flags & O_CREAT)) {
3490 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
3494 fd = fruit_fake_fd();
3496 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
3500 fio->fake_fd = true;
3507 static int fruit_open_meta_netatalk(vfs_handle_struct *handle,
3508 struct smb_filename *smb_fname,
3513 struct fruit_config_data *config = NULL;
3514 struct fio *fio = NULL;
3515 struct adouble *ad = NULL;
3516 bool meta_exists = false;
3519 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3521 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
3528 if (!meta_exists && !(flags & O_CREAT)) {
3533 fd = fruit_fake_fd();
3538 SMB_VFS_HANDLE_GET_DATA(handle, config,
3539 struct fruit_config_data, return -1);
3541 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3542 fio->type = ADOUBLE_META;
3543 fio->config = config;
3544 fio->fake_fd = true;
3551 static int fruit_open_meta(vfs_handle_struct *handle,
3552 struct smb_filename *smb_fname,
3553 files_struct *fsp, int flags, mode_t mode)
3556 struct fruit_config_data *config = NULL;
3558 DBG_DEBUG("path [%s]\n", smb_fname_str_dbg(smb_fname));
3560 SMB_VFS_HANDLE_GET_DATA(handle, config,
3561 struct fruit_config_data, return -1);
3563 switch (config->meta) {
3564 case FRUIT_META_STREAM:
3565 fd = fruit_open_meta_stream(handle, smb_fname,
3569 case FRUIT_META_NETATALK:
3570 fd = fruit_open_meta_netatalk(handle, smb_fname,
3575 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3579 DBG_DEBUG("path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3584 static int fruit_open_rsrc_adouble(vfs_handle_struct *handle,
3585 struct smb_filename *smb_fname,
3591 struct adouble *ad = NULL;
3592 struct smb_filename *smb_fname_base = NULL;
3593 struct fruit_config_data *config = NULL;
3596 SMB_VFS_HANDLE_GET_DATA(handle, config,
3597 struct fruit_config_data, return -1);
3599 if ((!(flags & O_CREAT)) &&
3600 S_ISDIR(fsp->base_fsp->fsp_name->st.st_ex_mode))
3602 /* sorry, but directories don't habe a resource fork */
3607 rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_base);
3612 /* We always need read/write access for the metadata header too */
3613 flags &= ~(O_RDONLY | O_WRONLY);
3616 hostfd = SMB_VFS_NEXT_OPEN(handle, smb_fname_base, fsp,
3623 if (flags & (O_CREAT | O_TRUNC)) {
3624 ad = ad_init(fsp, handle, ADOUBLE_RSRC);
3630 fsp->fh->fd = hostfd;
3632 rc = ad_fset(handle, ad, fsp);
3643 TALLOC_FREE(smb_fname_base);
3645 DEBUG(10, ("fruit_open resource fork: rc=%d, fd=%d\n", rc, hostfd));
3647 int saved_errno = errno;
3650 * BUGBUGBUG -- we would need to call
3651 * fd_close_posix here, but we don't have a
3654 fsp->fh->fd = hostfd;
3658 errno = saved_errno;
3663 static int fruit_open_rsrc_xattr(vfs_handle_struct *handle,
3664 struct smb_filename *smb_fname,
3669 #ifdef HAVE_ATTROPEN
3672 fd = attropen(smb_fname->base_name,
3673 AFPRESOURCE_EA_NETATALK,
3688 static int fruit_open_rsrc(vfs_handle_struct *handle,
3689 struct smb_filename *smb_fname,
3690 files_struct *fsp, int flags, mode_t mode)
3693 struct fruit_config_data *config = NULL;
3694 struct fio *fio = NULL;
3696 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3698 SMB_VFS_HANDLE_GET_DATA(handle, config,
3699 struct fruit_config_data, return -1);
3701 switch (config->rsrc) {
3702 case FRUIT_RSRC_STREAM:
3703 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3706 case FRUIT_RSRC_ADFILE:
3707 fd = fruit_open_rsrc_adouble(handle, smb_fname,
3711 case FRUIT_RSRC_XATTR:
3712 fd = fruit_open_rsrc_xattr(handle, smb_fname,
3717 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3721 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3727 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3728 fio->type = ADOUBLE_RSRC;
3729 fio->config = config;
3734 static int fruit_open(vfs_handle_struct *handle,
3735 struct smb_filename *smb_fname,
3736 files_struct *fsp, int flags, mode_t mode)
3740 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3742 if (!is_ntfs_stream_smb_fname(smb_fname)) {
3743 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3746 if (is_afpinfo_stream(smb_fname)) {
3747 fd = fruit_open_meta(handle, smb_fname, fsp, flags, mode);
3748 } else if (is_afpresource_stream(smb_fname)) {
3749 fd = fruit_open_rsrc(handle, smb_fname, fsp, flags, mode);
3751 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3754 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3759 static int fruit_close_meta(vfs_handle_struct *handle,
3763 struct fruit_config_data *config = NULL;
3765 SMB_VFS_HANDLE_GET_DATA(handle, config,
3766 struct fruit_config_data, return -1);
3768 switch (config->meta) {
3769 case FRUIT_META_STREAM:
3770 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3773 case FRUIT_META_NETATALK:
3774 ret = close(fsp->fh->fd);
3779 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3787 static int fruit_close_rsrc(vfs_handle_struct *handle,
3791 struct fruit_config_data *config = NULL;
3793 SMB_VFS_HANDLE_GET_DATA(handle, config,
3794 struct fruit_config_data, return -1);
3796 switch (config->rsrc) {
3797 case FRUIT_RSRC_STREAM:
3798 case FRUIT_RSRC_ADFILE:
3799 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3802 case FRUIT_RSRC_XATTR:
3803 ret = close(fsp->fh->fd);
3808 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3815 static int fruit_close(vfs_handle_struct *handle,
3823 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(fsp->fsp_name), fd);
3825 if (!is_ntfs_stream_smb_fname(fsp->fsp_name)) {
3826 return SMB_VFS_NEXT_CLOSE(handle, fsp);
3829 if (is_afpinfo_stream(fsp->fsp_name)) {
3830 ret = fruit_close_meta(handle, fsp);
3831 } else if (is_afpresource_stream(fsp->fsp_name)) {
3832 ret = fruit_close_rsrc(handle, fsp);
3834 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3840 static int fruit_rename(struct vfs_handle_struct *handle,
3841 const struct smb_filename *smb_fname_src,
3842 const struct smb_filename *smb_fname_dst)
3845 struct fruit_config_data *config = NULL;
3846 struct smb_filename *src_adp_smb_fname = NULL;
3847 struct smb_filename *dst_adp_smb_fname = NULL;
3849 SMB_VFS_HANDLE_GET_DATA(handle, config,
3850 struct fruit_config_data, return -1);
3852 if (!VALID_STAT(smb_fname_src->st)) {
3853 DBG_ERR("Need valid stat for [%s]\n",
3854 smb_fname_str_dbg(smb_fname_src));
3858 rc = SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
3863 if ((config->rsrc != FRUIT_RSRC_ADFILE) ||
3864 (!S_ISREG(smb_fname_src->st.st_ex_mode)))
3869 rc = adouble_path(talloc_tos(), smb_fname_src, &src_adp_smb_fname);
3874 rc = adouble_path(talloc_tos(), smb_fname_dst, &dst_adp_smb_fname);
3879 DBG_DEBUG("%s -> %s\n",
3880 smb_fname_str_dbg(src_adp_smb_fname),
3881 smb_fname_str_dbg(dst_adp_smb_fname));
3883 rc = SMB_VFS_NEXT_RENAME(handle, src_adp_smb_fname, dst_adp_smb_fname);
3884 if (errno == ENOENT) {
3889 TALLOC_FREE(src_adp_smb_fname);
3890 TALLOC_FREE(dst_adp_smb_fname);
3894 static int fruit_unlink_meta_stream(vfs_handle_struct *handle,
3895 const struct smb_filename *smb_fname)
3897 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3900 static int fruit_unlink_meta_netatalk(vfs_handle_struct *handle,
3901 const struct smb_filename *smb_fname)
3903 return SMB_VFS_REMOVEXATTR(handle->conn,
3905 AFPINFO_EA_NETATALK);
3908 static int fruit_unlink_meta(vfs_handle_struct *handle,
3909 const struct smb_filename *smb_fname)
3911 struct fruit_config_data *config = NULL;
3914 SMB_VFS_HANDLE_GET_DATA(handle, config,
3915 struct fruit_config_data, return -1);
3917 switch (config->meta) {
3918 case FRUIT_META_STREAM:
3919 rc = fruit_unlink_meta_stream(handle, smb_fname);
3922 case FRUIT_META_NETATALK:
3923 rc = fruit_unlink_meta_netatalk(handle, smb_fname);
3927 DBG_ERR("Unsupported meta config [%d]\n", config->meta);
3934 static int fruit_unlink_rsrc_stream(vfs_handle_struct *handle,
3935 const struct smb_filename *smb_fname,
3940 if (!force_unlink) {
3941 struct smb_filename *smb_fname_cp = NULL;
3944 smb_fname_cp = cp_smb_filename(talloc_tos(), smb_fname);
3945 if (smb_fname_cp == NULL) {
3950 * 0 byte resource fork streams are not listed by
3951 * vfs_streaminfo, as a result stream cleanup/deletion of file
3952 * deletion doesn't remove the resourcefork stream.
3955 ret = SMB_VFS_NEXT_STAT(handle, smb_fname_cp);
3957 TALLOC_FREE(smb_fname_cp);
3958 DBG_ERR("stat [%s] failed [%s]\n",
3959 smb_fname_str_dbg(smb_fname_cp), strerror(errno));
3963 size = smb_fname_cp->st.st_ex_size;
3964 TALLOC_FREE(smb_fname_cp);
3967 /* OS X ignores resource fork stream delete requests */
3972 ret = SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3973 if ((ret != 0) && (errno == ENOENT) && force_unlink) {
3980 static int fruit_unlink_rsrc_adouble(vfs_handle_struct *handle,
3981 const struct smb_filename *smb_fname,
3985 struct adouble *ad = NULL;
3986 struct smb_filename *adp_smb_fname = NULL;
3988 if (!force_unlink) {
3989 ad = ad_get(talloc_tos(), handle, smb_fname,
3998 * 0 byte resource fork streams are not listed by
3999 * vfs_streaminfo, as a result stream cleanup/deletion of file
4000 * deletion doesn't remove the resourcefork stream.
4003 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
4004 /* OS X ignores resource fork stream delete requests */
4012 rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
4017 rc = SMB_VFS_NEXT_UNLINK(handle, adp_smb_fname);
4018 TALLOC_FREE(adp_smb_fname);
4019 if ((rc != 0) && (errno == ENOENT) && force_unlink) {
4026 static int fruit_unlink_rsrc_xattr(vfs_handle_struct *handle,
4027 const struct smb_filename *smb_fname,
4031 * OS X ignores resource fork stream delete requests, so nothing to do
4032 * here. Removing the file will remove the xattr anyway, so we don't
4033 * have to take care of removing 0 byte resource forks that could be
4039 static int fruit_unlink_rsrc(vfs_handle_struct *handle,
4040 const struct smb_filename *smb_fname,
4043 struct fruit_config_data *config = NULL;
4046 SMB_VFS_HANDLE_GET_DATA(handle, config,
4047 struct fruit_config_data, return -1);
4049 switch (config->rsrc) {
4050 case FRUIT_RSRC_STREAM:
4051 rc = fruit_unlink_rsrc_stream(handle, smb_fname, force_unlink);
4054 case FRUIT_RSRC_ADFILE:
4055 rc = fruit_unlink_rsrc_adouble(handle, smb_fname, force_unlink);
4058 case FRUIT_RSRC_XATTR:
4059 rc = fruit_unlink_rsrc_xattr(handle, smb_fname, force_unlink);
4063 DBG_ERR("Unsupported rsrc config [%d]\n", config->rsrc);
4070 static int fruit_unlink(vfs_handle_struct *handle,
4071 const struct smb_filename *smb_fname)
4074 struct fruit_config_data *config = NULL;
4075 struct smb_filename *rsrc_smb_fname = NULL;
4077 SMB_VFS_HANDLE_GET_DATA(handle, config,
4078 struct fruit_config_data, return -1);
4080 if (is_afpinfo_stream(smb_fname)) {
4081 return fruit_unlink_meta(handle, smb_fname);
4082 } else if (is_afpresource_stream(smb_fname)) {
4083 return fruit_unlink_rsrc(handle, smb_fname, false);
4084 } if (is_ntfs_stream_smb_fname(smb_fname)) {
4085 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4089 * A request to delete the base file. Because 0 byte resource
4090 * fork streams are not listed by fruit_streaminfo,
4091 * delete_all_streams() can't remove 0 byte resource fork
4092 * streams, so we have to cleanup this here.
4094 rsrc_smb_fname = synthetic_smb_fname(talloc_tos(),
4095 smb_fname->base_name,
4096 AFPRESOURCE_STREAM_NAME,
4099 if (rsrc_smb_fname == NULL) {
4103 rc = fruit_unlink_rsrc(handle, rsrc_smb_fname, true);
4104 if ((rc != 0) && (errno != ENOENT)) {
4105 DBG_ERR("Forced unlink of [%s] failed [%s]\n",
4106 smb_fname_str_dbg(rsrc_smb_fname), strerror(errno));
4107 TALLOC_FREE(rsrc_smb_fname);
4110 TALLOC_FREE(rsrc_smb_fname);
4112 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4115 static int fruit_chmod(vfs_handle_struct *handle,
4116 const struct smb_filename *smb_fname,
4120 struct fruit_config_data *config = NULL;
4121 struct smb_filename *smb_fname_adp = NULL;
4123 rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname, mode);
4128 SMB_VFS_HANDLE_GET_DATA(handle, config,
4129 struct fruit_config_data, return -1);
4131 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4135 if (!VALID_STAT(smb_fname->st)) {
4139 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
4143 rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_adp);
4148 DEBUG(10, ("fruit_chmod: %s\n", smb_fname_adp->base_name));
4150 rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname_adp, mode);
4151 if (errno == ENOENT) {
4155 TALLOC_FREE(smb_fname_adp);
4159 static int fruit_chown(vfs_handle_struct *handle,
4160 const struct smb_filename *smb_fname,
4165 struct fruit_config_data *config = NULL;
4166 struct smb_filename *adp_smb_fname = NULL;
4168 rc = SMB_VFS_NEXT_CHOWN(handle, smb_fname, uid, gid);
4173 SMB_VFS_HANDLE_GET_DATA(handle, config,
4174 struct fruit_config_data, return -1);
4176 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4180 if (!VALID_STAT(smb_fname->st)) {
4184 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
4188 rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
4193 DEBUG(10, ("fruit_chown: %s\n", adp_smb_fname->base_name));
4195 rc = SMB_VFS_NEXT_CHOWN(handle, adp_smb_fname, uid, gid);
4196 if (errno == ENOENT) {
4201 TALLOC_FREE(adp_smb_fname);
4205 static int fruit_rmdir(struct vfs_handle_struct *handle,
4206 const struct smb_filename *smb_fname)
4210 struct fruit_config_data *config;
4212 SMB_VFS_HANDLE_GET_DATA(handle, config,
4213 struct fruit_config_data, return -1);
4215 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4220 * Due to there is no way to change bDeleteVetoFiles variable
4221 * from this module, need to clean up ourselves
4224 dh = SMB_VFS_OPENDIR(handle->conn, smb_fname, NULL, 0);
4229 while ((de = SMB_VFS_READDIR(handle->conn, dh, NULL)) != NULL) {
4231 struct adouble *ad = NULL;
4233 struct smb_filename *ad_smb_fname = NULL;
4236 match = strncmp(de->d_name,
4237 ADOUBLE_NAME_PREFIX,
4238 strlen(ADOUBLE_NAME_PREFIX));
4243 p = talloc_asprintf(talloc_tos(), "%s/%s",
4244 smb_fname->base_name, de->d_name);
4246 DBG_ERR("talloc_asprintf failed\n");
4250 ad_smb_fname = synthetic_smb_fname(talloc_tos(), p,
4254 if (ad_smb_fname == NULL) {
4255 DBG_ERR("synthetic_smb_fname failed\n");
4260 * Check whether it's a valid AppleDouble file, if
4261 * yes, delete it, ignore it otherwise.
4263 ad = ad_get(talloc_tos(), handle, ad_smb_fname, ADOUBLE_RSRC);
4265 TALLOC_FREE(ad_smb_fname);
4271 ret = SMB_VFS_NEXT_UNLINK(handle, ad_smb_fname);
4273 DBG_ERR("Deleting [%s] failed\n",
4274 smb_fname_str_dbg(ad_smb_fname));
4276 TALLOC_FREE(ad_smb_fname);
4281 SMB_VFS_CLOSEDIR(handle->conn, dh);
4283 return SMB_VFS_NEXT_RMDIR(handle, smb_fname);
4286 static ssize_t fruit_pread_meta_stream(vfs_handle_struct *handle,
4287 files_struct *fsp, void *data,
4288 size_t n, off_t offset)
4293 nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4294 if (nread == -1 || nread == n) {
4298 DBG_ERR("Removing [%s] after short read [%zd]\n",
4299 fsp_str_dbg(fsp), nread);
4301 ret = SMB_VFS_NEXT_UNLINK(handle, fsp->fsp_name);
4303 DBG_ERR("Removing [%s] failed\n", fsp_str_dbg(fsp));
4311 static ssize_t fruit_pread_meta_adouble(vfs_handle_struct *handle,
4312 files_struct *fsp, void *data,
4313 size_t n, off_t offset)
4316 struct adouble *ad = NULL;
4317 char afpinfo_buf[AFP_INFO_SIZE];
4321 ai = afpinfo_new(talloc_tos());
4326 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
4332 p = ad_get_entry(ad, ADEID_FINDERI);
4334 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
4339 memcpy(&ai->afpi_FinderInfo[0], p, ADEDLEN_FINDERI);
4341 nread = afpinfo_pack(ai, afpinfo_buf);
4342 if (nread != AFP_INFO_SIZE) {
4347 memcpy(data, afpinfo_buf, n);
4355 static ssize_t fruit_pread_meta(vfs_handle_struct *handle,
4356 files_struct *fsp, void *data,
4357 size_t n, off_t offset)
4359 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4364 * OS X has a off-by-1 error in the offset calculation, so we're
4365 * bug compatible here. It won't hurt, as any relevant real
4366 * world read requests from the AFP_AfpInfo stream will be
4367 * offset=0 n=60. offset is ignored anyway, see below.
4369 if ((offset < 0) || (offset >= AFP_INFO_SIZE + 1)) {
4374 DBG_ERR("Failed to fetch fsp extension");
4378 /* Yes, macOS always reads from offset 0 */
4380 to_return = MIN(n, AFP_INFO_SIZE);
4382 switch (fio->config->meta) {
4383 case FRUIT_META_STREAM:
4384 nread = fruit_pread_meta_stream(handle, fsp, data,
4388 case FRUIT_META_NETATALK:
4389 nread = fruit_pread_meta_adouble(handle, fsp, data,
4394 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
4398 if (nread == -1 && fio->created) {
4400 char afpinfo_buf[AFP_INFO_SIZE];
4402 ai = afpinfo_new(talloc_tos());
4407 nread = afpinfo_pack(ai, afpinfo_buf);
4409 if (nread != AFP_INFO_SIZE) {
4413 memcpy(data, afpinfo_buf, to_return);
4420 static ssize_t fruit_pread_rsrc_stream(vfs_handle_struct *handle,
4421 files_struct *fsp, void *data,
4422 size_t n, off_t offset)
4424 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4427 static ssize_t fruit_pread_rsrc_xattr(vfs_handle_struct *handle,
4428 files_struct *fsp, void *data,
4429 size_t n, off_t offset)
4431 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4434 static ssize_t fruit_pread_rsrc_adouble(vfs_handle_struct *handle,
4435 files_struct *fsp, void *data,
4436 size_t n, off_t offset)
4438 struct adouble *ad = NULL;
4441 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
4446 nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n,
4447 offset + ad_getentryoff(ad, ADEID_RFORK));
4453 static ssize_t fruit_pread_rsrc(vfs_handle_struct *handle,
4454 files_struct *fsp, void *data,
4455 size_t n, off_t offset)
4457 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4465 switch (fio->config->rsrc) {
4466 case FRUIT_RSRC_STREAM:
4467 nread = fruit_pread_rsrc_stream(handle, fsp, data, n, offset);
4470 case FRUIT_RSRC_ADFILE:
4471 nread = fruit_pread_rsrc_adouble(handle, fsp, data, n, offset);
4474 case FRUIT_RSRC_XATTR:
4475 nread = fruit_pread_rsrc_xattr(handle, fsp, data, n, offset);
4479 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
4486 static ssize_t fruit_pread(vfs_handle_struct *handle,
4487 files_struct *fsp, void *data,
4488 size_t n, off_t offset)
4490 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4493 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4494 fsp_str_dbg(fsp), (intmax_t)offset, n);
4497 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4500 if (fio->type == ADOUBLE_META) {
4501 nread = fruit_pread_meta(handle, fsp, data, n, offset);
4503 nread = fruit_pread_rsrc(handle, fsp, data, n, offset);
4506 DBG_DEBUG("Path [%s] nread [%zd]\n", fsp_str_dbg(fsp), nread);
4510 static bool fruit_must_handle_aio_stream(struct fio *fio)
4516 if (fio->type == ADOUBLE_META) {
4520 if ((fio->type == ADOUBLE_RSRC) &&
4521 (fio->config->rsrc == FRUIT_RSRC_ADFILE))
4529 struct fruit_pread_state {
4531 struct vfs_aio_state vfs_aio_state;
4534 static void fruit_pread_done(struct tevent_req *subreq);
4536 static struct tevent_req *fruit_pread_send(
4537 struct vfs_handle_struct *handle,
4538 TALLOC_CTX *mem_ctx,
4539 struct tevent_context *ev,
4540 struct files_struct *fsp,
4542 size_t n, off_t offset)
4544 struct tevent_req *req = NULL;
4545 struct tevent_req *subreq = NULL;
4546 struct fruit_pread_state *state = NULL;
4547 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4549 req = tevent_req_create(mem_ctx, &state,
4550 struct fruit_pread_state);
4555 if (fruit_must_handle_aio_stream(fio)) {
4556 state->nread = SMB_VFS_PREAD(fsp, data, n, offset);
4557 if (state->nread != n) {
4558 if (state->nread != -1) {
4561 tevent_req_error(req, errno);
4562 return tevent_req_post(req, ev);
4564 tevent_req_done(req);
4565 return tevent_req_post(req, ev);
4568 subreq = SMB_VFS_NEXT_PREAD_SEND(state, ev, handle, fsp,
4570 if (tevent_req_nomem(req, subreq)) {
4571 return tevent_req_post(req, ev);
4573 tevent_req_set_callback(subreq, fruit_pread_done, req);
4577 static void fruit_pread_done(struct tevent_req *subreq)
4579 struct tevent_req *req = tevent_req_callback_data(
4580 subreq, struct tevent_req);
4581 struct fruit_pread_state *state = tevent_req_data(
4582 req, struct fruit_pread_state);
4584 state->nread = SMB_VFS_PREAD_RECV(subreq, &state->vfs_aio_state);
4585 TALLOC_FREE(subreq);
4587 if (tevent_req_error(req, state->vfs_aio_state.error)) {
4590 tevent_req_done(req);
4593 static ssize_t fruit_pread_recv(struct tevent_req *req,
4594 struct vfs_aio_state *vfs_aio_state)
4596 struct fruit_pread_state *state = tevent_req_data(
4597 req, struct fruit_pread_state);
4599 if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
4603 *vfs_aio_state = state->vfs_aio_state;
4604 return state->nread;
4607 static ssize_t fruit_pwrite_meta_stream(vfs_handle_struct *handle,
4608 files_struct *fsp, const void *data,
4609 size_t n, off_t offset)
4611 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4617 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4618 fsp_str_dbg(fsp), (intmax_t)offset, n);
4627 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
4629 DBG_ERR("Close [%s] failed: %s\n",
4630 fsp_str_dbg(fsp), strerror(errno));
4635 fd = SMB_VFS_NEXT_OPEN(handle,
4641 DBG_ERR("On-demand create [%s] in write failed: %s\n",
4642 fsp_str_dbg(fsp), strerror(errno));
4646 fio->fake_fd = false;
4649 ai = afpinfo_unpack(talloc_tos(), data);
4654 if (ai_empty_finderinfo(ai)) {
4656 * Writing an all 0 blob to the metadata stream results in the
4657 * stream being removed on a macOS server. This ensures we
4658 * behave the same and it verified by the "delete AFP_AfpInfo by
4659 * writing all 0" test.
4661 ret = SMB_VFS_NEXT_FTRUNCATE(handle, fsp, 0);
4663 DBG_ERR("SMB_VFS_NEXT_FTRUNCATE on [%s] failed\n",
4668 ok = set_delete_on_close(
4671 handle->conn->session_info->security_token,
4672 handle->conn->session_info->unix_token);
4674 DBG_ERR("set_delete_on_close on [%s] failed\n",
4681 nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4682 if (nwritten != n) {
4689 static ssize_t fruit_pwrite_meta_netatalk(vfs_handle_struct *handle,
4690 files_struct *fsp, const void *data,
4691 size_t n, off_t offset)
4693 struct adouble *ad = NULL;
4699 ai = afpinfo_unpack(talloc_tos(), data);
4704 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
4706 ad = ad_init(talloc_tos(), handle, ADOUBLE_META);
4711 p = ad_get_entry(ad, ADEID_FINDERI);
4713 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
4718 memcpy(p, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
4720 ret = ad_fset(handle, ad, fsp);
4722 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
4729 if (!ai_empty_finderinfo(ai)) {
4734 * Writing an all 0 blob to the metadata stream results in the stream
4735 * being removed on a macOS server. This ensures we behave the same and
4736 * it verified by the "delete AFP_AfpInfo by writing all 0" test.
4739 ok = set_delete_on_close(
4742 handle->conn->session_info->security_token,
4743 handle->conn->session_info->unix_token);
4745 DBG_ERR("set_delete_on_close on [%s] failed\n",
4753 static ssize_t fruit_pwrite_meta(vfs_handle_struct *handle,
4754 files_struct *fsp, const void *data,
4755 size_t n, off_t offset)
4757 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4759 uint8_t buf[AFP_INFO_SIZE];
4765 DBG_ERR("Failed to fetch fsp extension");
4774 if (offset != 0 && n < 60) {
4779 cmp = memcmp(data, "AFP", 3);
4785 if (n <= AFP_OFF_FinderInfo) {
4787 * Nothing to do here really, just return
4795 if (to_copy > AFP_INFO_SIZE) {
4796 to_copy = AFP_INFO_SIZE;
4798 memcpy(buf, data, to_copy);
4801 if (to_write != AFP_INFO_SIZE) {
4802 to_write = AFP_INFO_SIZE;
4805 switch (fio->config->meta) {
4806 case FRUIT_META_STREAM:
4807 nwritten = fruit_pwrite_meta_stream(handle,
4814 case FRUIT_META_NETATALK:
4815 nwritten = fruit_pwrite_meta_netatalk(handle,
4823 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
4827 if (nwritten != to_write) {
4832 * Return the requested amount, verified against macOS SMB server
4837 static ssize_t fruit_pwrite_rsrc_stream(vfs_handle_struct *handle,
4838 files_struct *fsp, const void *data,
4839 size_t n, off_t offset)
4841 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4844 static ssize_t fruit_pwrite_rsrc_xattr(vfs_handle_struct *handle,
4845 files_struct *fsp, const void *data,
4846 size_t n, off_t offset)
4848 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4851 static ssize_t fruit_pwrite_rsrc_adouble(vfs_handle_struct *handle,
4852 files_struct *fsp, const void *data,
4853 size_t n, off_t offset)
4855 struct adouble *ad = NULL;
4859 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
4861 DBG_ERR("ad_get [%s] failed\n", fsp_str_dbg(fsp));
4865 nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n,
4866 offset + ad_getentryoff(ad, ADEID_RFORK));
4867 if (nwritten != n) {
4868 DBG_ERR("Short write on [%s] [%zd/%zd]\n",
4869 fsp_str_dbg(fsp), nwritten, n);
4874 if ((n + offset) > ad_getentrylen(ad, ADEID_RFORK)) {
4875 ad_setentrylen(ad, ADEID_RFORK, n + offset);
4876 ret = ad_fset(handle, ad, fsp);
4878 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
4888 static ssize_t fruit_pwrite_rsrc(vfs_handle_struct *handle,
4889 files_struct *fsp, const void *data,
4890 size_t n, off_t offset)
4892 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4896 DBG_ERR("Failed to fetch fsp extension");
4900 switch (fio->config->rsrc) {
4901 case FRUIT_RSRC_STREAM:
4902 nwritten = fruit_pwrite_rsrc_stream(handle, fsp, data, n, offset);
4905 case FRUIT_RSRC_ADFILE:
4906 nwritten = fruit_pwrite_rsrc_adouble(handle, fsp, data, n, offset);
4909 case FRUIT_RSRC_XATTR:
4910 nwritten = fruit_pwrite_rsrc_xattr(handle, fsp, data, n, offset);
4914 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
4921 static ssize_t fruit_pwrite(vfs_handle_struct *handle,
4922 files_struct *fsp, const void *data,
4923 size_t n, off_t offset)
4925 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4928 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4929 fsp_str_dbg(fsp), (intmax_t)offset, n);
4932 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4935 if (fio->type == ADOUBLE_META) {
4936 nwritten = fruit_pwrite_meta(handle, fsp, data, n, offset);
4938 nwritten = fruit_pwrite_rsrc(handle, fsp, data, n, offset);
4941 DBG_DEBUG("Path [%s] nwritten=%zd\n", fsp_str_dbg(fsp), nwritten);
4945 struct fruit_pwrite_state {
4947 struct vfs_aio_state vfs_aio_state;
4950 static void fruit_pwrite_done(struct tevent_req *subreq);
4952 static struct tevent_req *fruit_pwrite_send(
4953 struct vfs_handle_struct *handle,
4954 TALLOC_CTX *mem_ctx,
4955 struct tevent_context *ev,
4956 struct files_struct *fsp,
4958 size_t n, off_t offset)
4960 struct tevent_req *req = NULL;
4961 struct tevent_req *subreq = NULL;
4962 struct fruit_pwrite_state *state = NULL;
4963 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4965 req = tevent_req_create(mem_ctx, &state,
4966 struct fruit_pwrite_state);
4971 if (fruit_must_handle_aio_stream(fio)) {
4972 state->nwritten = SMB_VFS_PWRITE(fsp, data, n, offset);
4973 if (state->nwritten != n) {
4974 if (state->nwritten != -1) {
4977 tevent_req_error(req, errno);
4978 return tevent_req_post(req, ev);
4980 tevent_req_done(req);
4981 return tevent_req_post(req, ev);
4984 subreq = SMB_VFS_NEXT_PWRITE_SEND(state, ev, handle, fsp,
4986 if (tevent_req_nomem(req, subreq)) {
4987 return tevent_req_post(req, ev);
4989 tevent_req_set_callback(subreq, fruit_pwrite_done, req);
4993 static void fruit_pwrite_done(struct tevent_req *subreq)
4995 struct tevent_req *req = tevent_req_callback_data(
4996 subreq, struct tevent_req);
4997 struct fruit_pwrite_state *state = tevent_req_data(
4998 req, struct fruit_pwrite_state);
5000 state->nwritten = SMB_VFS_PWRITE_RECV(subreq, &state->vfs_aio_state);
5001 TALLOC_FREE(subreq);
5003 if (tevent_req_error(req, state->vfs_aio_state.error)) {
5006 tevent_req_done(req);
5009 static ssize_t fruit_pwrite_recv(struct tevent_req *req,
5010 struct vfs_aio_state *vfs_aio_state)
5012 struct fruit_pwrite_state *state = tevent_req_data(
5013 req, struct fruit_pwrite_state);
5015 if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
5019 *vfs_aio_state = state->vfs_aio_state;
5020 return state->nwritten;
5024 * Helper to stat/lstat the base file of an smb_fname.
5026 static int fruit_stat_base(vfs_handle_struct *handle,
5027 struct smb_filename *smb_fname,
5030 char *tmp_stream_name;
5033 tmp_stream_name = smb_fname->stream_name;
5034 smb_fname->stream_name = NULL;
5036 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
5038 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5040 smb_fname->stream_name = tmp_stream_name;
5042 DBG_DEBUG("fruit_stat_base [%s] dev [%ju] ino [%ju]\n",
5043 smb_fname->base_name,
5044 (uintmax_t)smb_fname->st.st_ex_dev,
5045 (uintmax_t)smb_fname->st.st_ex_ino);
5049 static int fruit_stat_meta_stream(vfs_handle_struct *handle,
5050 struct smb_filename *smb_fname,
5056 ret = fruit_stat_base(handle, smb_fname, false);
5061 ino = fruit_inode(&smb_fname->st, smb_fname->stream_name);
5064 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
5066 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5069 smb_fname->st.st_ex_ino = ino;
5074 static int fruit_stat_meta_netatalk(vfs_handle_struct *handle,
5075 struct smb_filename *smb_fname,
5078 struct adouble *ad = NULL;
5080 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5082 DBG_INFO("fruit_stat_meta %s: %s\n",
5083 smb_fname_str_dbg(smb_fname), strerror(errno));
5089 /* Populate the stat struct with info from the base file. */
5090 if (fruit_stat_base(handle, smb_fname, follow_links) == -1) {
5093 smb_fname->st.st_ex_size = AFP_INFO_SIZE;
5094 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5095 smb_fname->stream_name);
5099 static int fruit_stat_meta(vfs_handle_struct *handle,
5100 struct smb_filename *smb_fname,
5103 struct fruit_config_data *config = NULL;
5106 SMB_VFS_HANDLE_GET_DATA(handle, config,
5107 struct fruit_config_data, return -1);
5109 switch (config->meta) {
5110 case FRUIT_META_STREAM:
5111 ret = fruit_stat_meta_stream(handle, smb_fname, follow_links);
5114 case FRUIT_META_NETATALK:
5115 ret = fruit_stat_meta_netatalk(handle, smb_fname, follow_links);
5119 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
5126 static int fruit_stat_rsrc_netatalk(vfs_handle_struct *handle,
5127 struct smb_filename *smb_fname,
5130 struct adouble *ad = NULL;
5133 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
5139 /* Populate the stat struct with info from the base file. */
5140 ret = fruit_stat_base(handle, smb_fname, follow_links);
5146 smb_fname->st.st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
5147 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5148 smb_fname->stream_name);
5153 static int fruit_stat_rsrc_stream(vfs_handle_struct *handle,
5154 struct smb_filename *smb_fname,
5160 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
5162 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5168 static int fruit_stat_rsrc_xattr(vfs_handle_struct *handle,
5169 struct smb_filename *smb_fname,
5172 #ifdef HAVE_ATTROPEN
5176 /* Populate the stat struct with info from the base file. */
5177 ret = fruit_stat_base(handle, smb_fname, follow_links);
5182 fd = attropen(smb_fname->base_name,
5183 AFPRESOURCE_EA_NETATALK,
5189 ret = sys_fstat(fd, &smb_fname->st, false);
5192 DBG_ERR("fstat [%s:%s] failed\n", smb_fname->base_name,
5193 AFPRESOURCE_EA_NETATALK);
5199 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5200 smb_fname->stream_name);
5210 static int fruit_stat_rsrc(vfs_handle_struct *handle,
5211 struct smb_filename *smb_fname,
5214 struct fruit_config_data *config = NULL;
5217 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
5219 SMB_VFS_HANDLE_GET_DATA(handle, config,
5220 struct fruit_config_data, return -1);
5222 switch (config->rsrc) {
5223 case FRUIT_RSRC_STREAM:
5224 ret = fruit_stat_rsrc_stream(handle, smb_fname, follow_links);
5227 case FRUIT_RSRC_XATTR:
5228 ret = fruit_stat_rsrc_xattr(handle, smb_fname, follow_links);
5231 case FRUIT_RSRC_ADFILE:
5232 ret = fruit_stat_rsrc_netatalk(handle, smb_fname, follow_links);
5236 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
5243 static int fruit_stat(vfs_handle_struct *handle,
5244 struct smb_filename *smb_fname)
5248 DEBUG(10, ("fruit_stat called for %s\n",
5249 smb_fname_str_dbg(smb_fname)));
5251 if (!is_ntfs_stream_smb_fname(smb_fname)
5252 || is_ntfs_default_stream_smb_fname(smb_fname)) {
5253 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
5255 update_btime(handle, smb_fname);
5261 * Note if lp_posix_paths() is true, we can never
5262 * get here as is_ntfs_stream_smb_fname() is
5263 * always false. So we never need worry about
5264 * not following links here.
5267 if (is_afpinfo_stream(smb_fname)) {
5268 rc = fruit_stat_meta(handle, smb_fname, true);
5269 } else if (is_afpresource_stream(smb_fname)) {
5270 rc = fruit_stat_rsrc(handle, smb_fname, true);
5272 return SMB_VFS_NEXT_STAT(handle, smb_fname);
5276 update_btime(handle, smb_fname);
5277 smb_fname->st.st_ex_mode &= ~S_IFMT;
5278 smb_fname->st.st_ex_mode |= S_IFREG;
5279 smb_fname->st.st_ex_blocks =
5280 smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
5285 static int fruit_lstat(vfs_handle_struct *handle,
5286 struct smb_filename *smb_fname)
5290 DEBUG(10, ("fruit_lstat called for %s\n",
5291 smb_fname_str_dbg(smb_fname)));
5293 if (!is_ntfs_stream_smb_fname(smb_fname)
5294 || is_ntfs_default_stream_smb_fname(smb_fname)) {
5295 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5297 update_btime(handle, smb_fname);
5302 if (is_afpinfo_stream(smb_fname)) {
5303 rc = fruit_stat_meta(handle, smb_fname, false);
5304 } else if (is_afpresource_stream(smb_fname)) {
5305 rc = fruit_stat_rsrc(handle, smb_fname, false);
5307 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5311 update_btime(handle, smb_fname);
5312 smb_fname->st.st_ex_mode &= ~S_IFMT;
5313 smb_fname->st.st_ex_mode |= S_IFREG;
5314 smb_fname->st.st_ex_blocks =
5315 smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
5320 static int fruit_fstat_meta_stream(vfs_handle_struct *handle,
5322 SMB_STRUCT_STAT *sbuf)
5324 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5325 struct smb_filename smb_fname;
5334 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5339 *sbuf = fsp->base_fsp->fsp_name->st;
5340 sbuf->st_ex_size = AFP_INFO_SIZE;
5341 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5345 smb_fname = (struct smb_filename) {
5346 .base_name = fsp->fsp_name->base_name,
5349 ret = fruit_stat_base(handle, &smb_fname, false);
5353 *sbuf = smb_fname.st;
5355 ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5357 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5362 sbuf->st_ex_ino = ino;
5366 static int fruit_fstat_meta_netatalk(vfs_handle_struct *handle,
5368 SMB_STRUCT_STAT *sbuf)
5372 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5377 *sbuf = fsp->base_fsp->fsp_name->st;
5378 sbuf->st_ex_size = AFP_INFO_SIZE;
5379 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5384 static int fruit_fstat_meta(vfs_handle_struct *handle,
5386 SMB_STRUCT_STAT *sbuf,
5391 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
5393 switch (fio->config->meta) {
5394 case FRUIT_META_STREAM:
5395 ret = fruit_fstat_meta_stream(handle, fsp, sbuf);
5398 case FRUIT_META_NETATALK:
5399 ret = fruit_fstat_meta_netatalk(handle, fsp, sbuf);
5403 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
5407 DBG_DEBUG("Path [%s] ret [%d]\n", fsp_str_dbg(fsp), ret);
5411 static int fruit_fstat_rsrc_xattr(vfs_handle_struct *handle,
5413 SMB_STRUCT_STAT *sbuf)
5415 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5418 static int fruit_fstat_rsrc_stream(vfs_handle_struct *handle,
5420 SMB_STRUCT_STAT *sbuf)
5422 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5425 static int fruit_fstat_rsrc_adouble(vfs_handle_struct *handle,
5427 SMB_STRUCT_STAT *sbuf)
5429 struct adouble *ad = NULL;
5432 /* Populate the stat struct with info from the base file. */
5433 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5438 ad = ad_get(talloc_tos(), handle,
5439 fsp->base_fsp->fsp_name,
5442 DBG_ERR("ad_get [%s] failed [%s]\n",
5443 fsp_str_dbg(fsp), strerror(errno));
5447 *sbuf = fsp->base_fsp->fsp_name->st;
5448 sbuf->st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
5449 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5455 static int fruit_fstat_rsrc(vfs_handle_struct *handle, files_struct *fsp,
5456 SMB_STRUCT_STAT *sbuf, struct fio *fio)
5460 switch (fio->config->rsrc) {
5461 case FRUIT_RSRC_STREAM:
5462 ret = fruit_fstat_rsrc_stream(handle, fsp, sbuf);
5465 case FRUIT_RSRC_ADFILE:
5466 ret = fruit_fstat_rsrc_adouble(handle, fsp, sbuf);
5469 case FRUIT_RSRC_XATTR:
5470 ret = fruit_fstat_rsrc_xattr(handle, fsp, sbuf);
5474 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
5481 static int fruit_fstat(vfs_handle_struct *handle, files_struct *fsp,
5482 SMB_STRUCT_STAT *sbuf)
5484 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5488 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5491 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
5493 if (fio->type == ADOUBLE_META) {
5494 rc = fruit_fstat_meta(handle, fsp, sbuf, fio);
5496 rc = fruit_fstat_rsrc(handle, fsp, sbuf, fio);
5500 sbuf->st_ex_mode &= ~S_IFMT;
5501 sbuf->st_ex_mode |= S_IFREG;
5502 sbuf->st_ex_blocks = sbuf->st_ex_size / STAT_ST_BLOCKSIZE + 1;
5505 DBG_DEBUG("Path [%s] rc [%d] size [%"PRIdMAX"]\n",
5506 fsp_str_dbg(fsp), rc, (intmax_t)sbuf->st_ex_size);
5510 static NTSTATUS delete_invalid_meta_stream(
5511 vfs_handle_struct *handle,
5512 const struct smb_filename *smb_fname,
5513 TALLOC_CTX *mem_ctx,
5514 unsigned int *pnum_streams,
5515 struct stream_struct **pstreams,
5518 struct smb_filename *sname = NULL;
5522 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams, AFPINFO_STREAM);
5524 return NT_STATUS_INTERNAL_ERROR;
5528 return NT_STATUS_OK;
5531 sname = synthetic_smb_fname(talloc_tos(),
5532 smb_fname->base_name,
5533 AFPINFO_STREAM_NAME,
5535 if (sname == NULL) {
5536 return NT_STATUS_NO_MEMORY;
5539 ret = SMB_VFS_NEXT_UNLINK(handle, sname);
5542 DBG_ERR("Removing [%s] failed\n", smb_fname_str_dbg(sname));
5543 return map_nt_error_from_unix(errno);
5546 return NT_STATUS_OK;
5549 static NTSTATUS fruit_streaminfo_meta_stream(
5550 vfs_handle_struct *handle,
5551 struct files_struct *fsp,
5552 const struct smb_filename *smb_fname,
5553 TALLOC_CTX *mem_ctx,
5554 unsigned int *pnum_streams,
5555 struct stream_struct **pstreams)
5557 struct stream_struct *stream = *pstreams;
5558 unsigned int num_streams = *pnum_streams;
5561 for (i = 0; i < num_streams; i++) {
5562 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
5567 if (i == num_streams) {
5568 return NT_STATUS_OK;
5571 if (stream[i].size != AFP_INFO_SIZE) {
5572 DBG_ERR("Removing invalid AFPINFO_STREAM size [%jd] from [%s]\n",
5573 (intmax_t)stream[i].size, smb_fname_str_dbg(smb_fname));
5575 return delete_invalid_meta_stream(handle,
5584 return NT_STATUS_OK;
5587 static NTSTATUS fruit_streaminfo_meta_netatalk(
5588 vfs_handle_struct *handle,
5589 struct files_struct *fsp,
5590 const struct smb_filename *smb_fname,
5591 TALLOC_CTX *mem_ctx,
5592 unsigned int *pnum_streams,
5593 struct stream_struct **pstreams)
5595 struct stream_struct *stream = *pstreams;
5596 unsigned int num_streams = *pnum_streams;
5597 struct adouble *ad = NULL;
5602 /* Remove the Netatalk xattr from the list */
5603 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5604 ":" NETATALK_META_XATTR ":$DATA");
5606 return NT_STATUS_NO_MEMORY;
5610 * Check if there's a AFPINFO_STREAM from the VFS streams
5611 * backend and if yes, remove it from the list
5613 for (i = 0; i < num_streams; i++) {
5614 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
5619 if (i < num_streams) {
5620 DBG_WARNING("Unexpected AFPINFO_STREAM on [%s]\n",
5621 smb_fname_str_dbg(smb_fname));
5623 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5626 return NT_STATUS_INTERNAL_ERROR;
5630 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5632 return NT_STATUS_OK;
5635 is_fi_empty = ad_empty_finderinfo(ad);
5639 return NT_STATUS_OK;
5642 ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
5643 AFPINFO_STREAM_NAME, AFP_INFO_SIZE,
5644 smb_roundup(handle->conn, AFP_INFO_SIZE));
5646 return NT_STATUS_NO_MEMORY;
5649 return NT_STATUS_OK;
5652 static NTSTATUS fruit_streaminfo_meta(vfs_handle_struct *handle,
5653 struct files_struct *fsp,
5654 const struct smb_filename *smb_fname,
5655 TALLOC_CTX *mem_ctx,
5656 unsigned int *pnum_streams,
5657 struct stream_struct **pstreams)
5659 struct fruit_config_data *config = NULL;
5662 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5663 return NT_STATUS_INTERNAL_ERROR);
5665 switch (config->meta) {
5666 case FRUIT_META_NETATALK:
5667 status = fruit_streaminfo_meta_netatalk(handle, fsp, smb_fname,
5668 mem_ctx, pnum_streams,
5672 case FRUIT_META_STREAM:
5673 status = fruit_streaminfo_meta_stream(handle, fsp, smb_fname,
5674 mem_ctx, pnum_streams,
5679 return NT_STATUS_INTERNAL_ERROR;
5685 static NTSTATUS fruit_streaminfo_rsrc_stream(
5686 vfs_handle_struct *handle,
5687 struct files_struct *fsp,
5688 const struct smb_filename *smb_fname,
5689 TALLOC_CTX *mem_ctx,
5690 unsigned int *pnum_streams,
5691 struct stream_struct **pstreams)
5695 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
5697 DBG_ERR("Filtering resource stream failed\n");
5698 return NT_STATUS_INTERNAL_ERROR;
5700 return NT_STATUS_OK;
5703 static NTSTATUS fruit_streaminfo_rsrc_xattr(
5704 vfs_handle_struct *handle,
5705 struct files_struct *fsp,
5706 const struct smb_filename *smb_fname,
5707 TALLOC_CTX *mem_ctx,
5708 unsigned int *pnum_streams,
5709 struct stream_struct **pstreams)
5713 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
5715 DBG_ERR("Filtering resource stream failed\n");
5716 return NT_STATUS_INTERNAL_ERROR;
5718 return NT_STATUS_OK;
5721 static NTSTATUS fruit_streaminfo_rsrc_adouble(
5722 vfs_handle_struct *handle,
5723 struct files_struct *fsp,
5724 const struct smb_filename *smb_fname,
5725 TALLOC_CTX *mem_ctx,
5726 unsigned int *pnum_streams,
5727 struct stream_struct **pstreams)
5729 struct stream_struct *stream = *pstreams;
5730 unsigned int num_streams = *pnum_streams;
5731 struct adouble *ad = NULL;
5737 * Check if there's a AFPRESOURCE_STREAM from the VFS streams backend
5738 * and if yes, remove it from the list
5740 for (i = 0; i < num_streams; i++) {
5741 if (strequal_m(stream[i].name, AFPRESOURCE_STREAM)) {
5746 if (i < num_streams) {
5747 DBG_WARNING("Unexpected AFPRESOURCE_STREAM on [%s]\n",
5748 smb_fname_str_dbg(smb_fname));
5750 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5751 AFPRESOURCE_STREAM);
5753 return NT_STATUS_INTERNAL_ERROR;
5757 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
5759 return NT_STATUS_OK;
5762 rlen = ad_getentrylen(ad, ADEID_RFORK);
5766 return NT_STATUS_OK;
5769 ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
5770 AFPRESOURCE_STREAM_NAME, rlen,
5771 smb_roundup(handle->conn, rlen));
5773 return NT_STATUS_NO_MEMORY;
5776 return NT_STATUS_OK;
5779 static NTSTATUS fruit_streaminfo_rsrc(vfs_handle_struct *handle,
5780 struct files_struct *fsp,
5781 const struct smb_filename *smb_fname,
5782 TALLOC_CTX *mem_ctx,
5783 unsigned int *pnum_streams,
5784 struct stream_struct **pstreams)
5786 struct fruit_config_data *config = NULL;
5789 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5790 return NT_STATUS_INTERNAL_ERROR);
5792 switch (config->rsrc) {
5793 case FRUIT_RSRC_STREAM:
5794 status = fruit_streaminfo_rsrc_stream(handle, fsp, smb_fname,
5795 mem_ctx, pnum_streams,
5799 case FRUIT_RSRC_XATTR:
5800 status = fruit_streaminfo_rsrc_xattr(handle, fsp, smb_fname,
5801 mem_ctx, pnum_streams,
5805 case FRUIT_RSRC_ADFILE:
5806 status = fruit_streaminfo_rsrc_adouble(handle, fsp, smb_fname,
5807 mem_ctx, pnum_streams,
5812 return NT_STATUS_INTERNAL_ERROR;
5818 static void fruit_filter_empty_streams(unsigned int *pnum_streams,
5819 struct stream_struct **pstreams)
5821 unsigned num_streams = *pnum_streams;
5822 struct stream_struct *streams = *pstreams;
5825 if (!global_fruit_config.nego_aapl) {
5829 while (i < num_streams) {
5830 struct smb_filename smb_fname = (struct smb_filename) {
5831 .stream_name = streams[i].name,
5834 if (is_ntfs_default_stream_smb_fname(&smb_fname)
5835 || streams[i].size > 0)
5841 streams[i] = streams[num_streams - 1];
5845 *pnum_streams = num_streams;
5848 static NTSTATUS fruit_streaminfo(vfs_handle_struct *handle,
5849 struct files_struct *fsp,
5850 const struct smb_filename *smb_fname,
5851 TALLOC_CTX *mem_ctx,
5852 unsigned int *pnum_streams,
5853 struct stream_struct **pstreams)
5855 struct fruit_config_data *config = NULL;
5858 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5859 return NT_STATUS_UNSUCCESSFUL);
5861 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
5863 status = SMB_VFS_NEXT_STREAMINFO(handle, fsp, smb_fname, mem_ctx,
5864 pnum_streams, pstreams);
5865 if (!NT_STATUS_IS_OK(status)) {
5869 fruit_filter_empty_streams(pnum_streams, pstreams);
5871 status = fruit_streaminfo_meta(handle, fsp, smb_fname,
5872 mem_ctx, pnum_streams, pstreams);
5873 if (!NT_STATUS_IS_OK(status)) {
5877 status = fruit_streaminfo_rsrc(handle, fsp, smb_fname,
5878 mem_ctx, pnum_streams, pstreams);
5879 if (!NT_STATUS_IS_OK(status)) {
5883 return NT_STATUS_OK;
5886 static int fruit_ntimes(vfs_handle_struct *handle,
5887 const struct smb_filename *smb_fname,
5888 struct smb_file_time *ft)
5891 struct adouble *ad = NULL;
5892 struct fruit_config_data *config = NULL;
5894 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5897 if ((config->meta != FRUIT_META_NETATALK) ||
5898 null_timespec(ft->create_time))
5900 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
5903 DEBUG(10,("set btime for %s to %s\n", smb_fname_str_dbg(smb_fname),
5904 time_to_asc(convert_timespec_to_time_t(ft->create_time))));
5906 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5911 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX,
5912 convert_time_t_to_uint32_t(ft->create_time.tv_sec));
5914 rc = ad_set(handle, ad, smb_fname);
5920 DEBUG(1, ("fruit_ntimes: %s\n", smb_fname_str_dbg(smb_fname)));
5923 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
5926 static int fruit_fallocate(struct vfs_handle_struct *handle,
5927 struct files_struct *fsp,
5932 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5935 return SMB_VFS_NEXT_FALLOCATE(handle, fsp, mode, offset, len);
5938 /* Let the pwrite code path handle it. */
5943 static int fruit_ftruncate_rsrc_xattr(struct vfs_handle_struct *handle,
5944 struct files_struct *fsp,
5947 #ifdef HAVE_ATTROPEN
5948 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
5953 static int fruit_ftruncate_rsrc_adouble(struct vfs_handle_struct *handle,
5954 struct files_struct *fsp,
5958 struct adouble *ad = NULL;
5961 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
5963 DBG_DEBUG("ad_get [%s] failed [%s]\n",
5964 fsp_str_dbg(fsp), strerror(errno));
5968 ad_off = ad_getentryoff(ad, ADEID_RFORK);
5970 rc = ftruncate(fsp->fh->fd, offset + ad_off);
5976 ad_setentrylen(ad, ADEID_RFORK, offset);
5978 rc = ad_fset(handle, ad, fsp);
5980 DBG_ERR("ad_fset [%s] failed [%s]\n",
5981 fsp_str_dbg(fsp), strerror(errno));
5990 static int fruit_ftruncate_rsrc_stream(struct vfs_handle_struct *handle,
5991 struct files_struct *fsp,
5994 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
5997 static int fruit_ftruncate_rsrc(struct vfs_handle_struct *handle,
5998 struct files_struct *fsp,
6001 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
6005 DBG_ERR("Failed to fetch fsp extension");
6009 switch (fio->config->rsrc) {
6010 case FRUIT_RSRC_XATTR:
6011 ret = fruit_ftruncate_rsrc_xattr(handle, fsp, offset);
6014 case FRUIT_RSRC_ADFILE:
6015 ret = fruit_ftruncate_rsrc_adouble(handle, fsp, offset);
6018 case FRUIT_RSRC_STREAM:
6019 ret = fruit_ftruncate_rsrc_stream(handle, fsp, offset);
6023 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
6031 static int fruit_ftruncate_meta(struct vfs_handle_struct *handle,
6032 struct files_struct *fsp,
6036 DBG_WARNING("ftruncate %s to %jd",
6037 fsp_str_dbg(fsp), (intmax_t)offset);
6038 /* OS X returns NT_STATUS_ALLOTTED_SPACE_EXCEEDED */
6043 /* OS X returns success but does nothing */
6044 DBG_INFO("ignoring ftruncate %s to %jd\n",
6045 fsp_str_dbg(fsp), (intmax_t)offset);
6049 static int fruit_ftruncate(struct vfs_handle_struct *handle,
6050 struct files_struct *fsp,
6053 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
6056 DBG_DEBUG("Path [%s] offset [%"PRIdMAX"]\n", fsp_str_dbg(fsp),
6060 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
6063 if (fio->type == ADOUBLE_META) {
6064 ret = fruit_ftruncate_meta(handle, fsp, offset);
6066 ret = fruit_ftruncate_rsrc(handle, fsp, offset);
6069 DBG_DEBUG("Path [%s] result [%d]\n", fsp_str_dbg(fsp), ret);
6073 static NTSTATUS fruit_create_file(vfs_handle_struct *handle,
6074 struct smb_request *req,
6075 uint16_t root_dir_fid,
6076 struct smb_filename *smb_fname,
6077 uint32_t access_mask,
6078 uint32_t share_access,
6079 uint32_t create_disposition,
6080 uint32_t create_options,
6081 uint32_t file_attributes,
6082 uint32_t oplock_request,
6083 struct smb2_lease *lease,
6084 uint64_t allocation_size,
6085 uint32_t private_flags,
6086 struct security_descriptor *sd,
6087 struct ea_list *ea_list,
6088 files_struct **result,
6090 const struct smb2_create_blobs *in_context_blobs,
6091 struct smb2_create_blobs *out_context_blobs)
6094 struct fruit_config_data *config = NULL;
6095 files_struct *fsp = NULL;
6096 struct fio *fio = NULL;
6097 bool internal_open = (oplock_request & INTERNAL_OPEN_ONLY);
6100 status = check_aapl(handle, req, in_context_blobs, out_context_blobs);
6101 if (!NT_STATUS_IS_OK(status)) {
6105 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
6106 return NT_STATUS_UNSUCCESSFUL);
6108 if (is_apple_stream(smb_fname) && !internal_open) {
6109 ret = ad_convert(handle, smb_fname);
6111 DBG_ERR("ad_convert() failed\n");
6112 return NT_STATUS_UNSUCCESSFUL;
6116 status = SMB_VFS_NEXT_CREATE_FILE(
6117 handle, req, root_dir_fid, smb_fname,
6118 access_mask, share_access,
6119 create_disposition, create_options,
6120 file_attributes, oplock_request,
6122 allocation_size, private_flags,
6123 sd, ea_list, result,
6124 pinfo, in_context_blobs, out_context_blobs);
6125 if (!NT_STATUS_IS_OK(status)) {
6131 if (global_fruit_config.nego_aapl) {
6132 if (config->posix_rename && fsp->is_directory) {
6134 * Enable POSIX directory rename behaviour
6136 fsp->posix_flags |= FSP_POSIX_FLAGS_RENAME;
6141 * If this is a plain open for existing files, opening an 0
6142 * byte size resource fork MUST fail with
6143 * NT_STATUS_OBJECT_NAME_NOT_FOUND.
6145 * Cf the vfs_fruit torture tests in test_rfork_create().
6147 if (global_fruit_config.nego_aapl &&
6148 create_disposition == FILE_OPEN &&
6149 smb_fname->st.st_ex_size == 0 &&
6150 is_ntfs_stream_smb_fname(smb_fname) &&
6151 !(is_ntfs_default_stream_smb_fname(smb_fname)))
6153 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
6157 fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
6158 if (fio != NULL && pinfo != NULL && *pinfo == FILE_WAS_CREATED) {
6159 fio->created = true;
6162 if (is_ntfs_stream_smb_fname(smb_fname)
6163 || fsp->is_directory) {
6167 if (config->locking == FRUIT_LOCKING_NETATALK) {
6168 status = fruit_check_access(
6172 if (!NT_STATUS_IS_OK(status)) {
6180 DEBUG(10, ("fruit_create_file: %s\n", nt_errstr(status)));
6183 close_file(req, fsp, ERROR_CLOSE);
6184 *result = fsp = NULL;
6190 static NTSTATUS fruit_readdir_attr(struct vfs_handle_struct *handle,
6191 const struct smb_filename *fname,
6192 TALLOC_CTX *mem_ctx,
6193 struct readdir_attr_data **pattr_data)
6195 struct fruit_config_data *config = NULL;
6196 struct readdir_attr_data *attr_data;
6200 SMB_VFS_HANDLE_GET_DATA(handle, config,
6201 struct fruit_config_data,
6202 return NT_STATUS_UNSUCCESSFUL);
6204 if (!global_fruit_config.nego_aapl) {
6205 return SMB_VFS_NEXT_READDIR_ATTR(handle, fname, mem_ctx, pattr_data);
6208 DEBUG(10, ("fruit_readdir_attr %s\n", fname->base_name));
6210 ret = ad_convert(handle, fname);
6212 DBG_ERR("ad_convert() failed\n");
6213 return NT_STATUS_UNSUCCESSFUL;
6216 *pattr_data = talloc_zero(mem_ctx, struct readdir_attr_data);
6217 if (*pattr_data == NULL) {
6218 return NT_STATUS_UNSUCCESSFUL;
6220 attr_data = *pattr_data;
6221 attr_data->type = RDATTR_AAPL;
6224 * Mac metadata: compressed FinderInfo, resource fork length
6227 status = readdir_attr_macmeta(handle, fname, attr_data);
6228 if (!NT_STATUS_IS_OK(status)) {
6230 * Error handling is tricky: if we return failure from
6231 * this function, the corresponding directory entry
6232 * will to be passed to the client, so we really just
6233 * want to error out on fatal errors.
6235 if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
6243 if (config->unix_info_enabled) {
6244 attr_data->attr_data.aapl.unix_mode = fname->st.st_ex_mode;
6250 if (!config->readdir_attr_max_access) {
6251 attr_data->attr_data.aapl.max_access = FILE_GENERIC_ALL;
6253 status = smbd_calculate_access_mask(
6257 SEC_FLAG_MAXIMUM_ALLOWED,
6258 &attr_data->attr_data.aapl.max_access);
6259 if (!NT_STATUS_IS_OK(status)) {
6264 return NT_STATUS_OK;
6267 DEBUG(1, ("fruit_readdir_attr %s, error: %s\n",
6268 fname->base_name, nt_errstr(status)));
6269 TALLOC_FREE(*pattr_data);
6273 static NTSTATUS fruit_fget_nt_acl(vfs_handle_struct *handle,
6275 uint32_t security_info,
6276 TALLOC_CTX *mem_ctx,
6277 struct security_descriptor **ppdesc)
6280 struct security_ace ace;
6282 struct fruit_config_data *config;
6284 SMB_VFS_HANDLE_GET_DATA(handle, config,
6285 struct fruit_config_data,
6286 return NT_STATUS_UNSUCCESSFUL);
6288 status = SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
6290 if (!NT_STATUS_IS_OK(status)) {
6295 * Add MS NFS style ACEs with uid, gid and mode
6297 if (!global_fruit_config.nego_aapl) {
6298 return NT_STATUS_OK;
6300 if (!config->unix_info_enabled) {
6301 return NT_STATUS_OK;
6304 /* First remove any existing ACE's with NFS style mode/uid/gid SIDs. */
6305 status = remove_virtual_nfs_aces(*ppdesc);
6306 if (!NT_STATUS_IS_OK(status)) {
6307 DBG_WARNING("failed to remove MS NFS style ACEs\n");
6311 /* MS NFS style mode */
6312 sid_compose(&sid, &global_sid_Unix_NFS_Mode, fsp->fsp_name->st.st_ex_mode);
6313 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6314 status = security_descriptor_dacl_add(*ppdesc, &ace);
6315 if (!NT_STATUS_IS_OK(status)) {
6316 DEBUG(1,("failed to add MS NFS style ACE\n"));
6320 /* MS NFS style uid */
6321 sid_compose(&sid, &global_sid_Unix_NFS_Users, fsp->fsp_name->st.st_ex_uid);
6322 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6323 status = security_descriptor_dacl_add(*ppdesc, &ace);
6324 if (!NT_STATUS_IS_OK(status)) {
6325 DEBUG(1,("failed to add MS NFS style ACE\n"));
6329 /* MS NFS style gid */
6330 sid_compose(&sid, &global_sid_Unix_NFS_Groups, fsp->fsp_name->st.st_ex_gid);
6331 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6332 status = security_descriptor_dacl_add(*ppdesc, &ace);
6333 if (!NT_STATUS_IS_OK(status)) {
6334 DEBUG(1,("failed to add MS NFS style ACE\n"));
6338 return NT_STATUS_OK;
6341 static NTSTATUS fruit_fset_nt_acl(vfs_handle_struct *handle,
6343 uint32_t security_info_sent,
6344 const struct security_descriptor *orig_psd)
6348 mode_t ms_nfs_mode = 0;
6350 struct security_descriptor *psd = NULL;
6351 uint32_t orig_num_aces = 0;
6353 if (orig_psd->dacl != NULL) {
6354 orig_num_aces = orig_psd->dacl->num_aces;
6357 psd = security_descriptor_copy(talloc_tos(), orig_psd);
6359 return NT_STATUS_NO_MEMORY;
6362 DBG_DEBUG("fruit_fset_nt_acl: %s\n", fsp_str_dbg(fsp));
6364 status = check_ms_nfs(handle, fsp, psd, &ms_nfs_mode, &do_chmod);
6365 if (!NT_STATUS_IS_OK(status)) {
6366 DEBUG(1, ("fruit_fset_nt_acl: check_ms_nfs failed%s\n", fsp_str_dbg(fsp)));
6372 * If only ms_nfs ACE entries were sent, ensure we set the DACL
6373 * sent/present flags correctly now we've removed them.
6376 if (orig_num_aces != 0) {
6378 * Are there any ACE's left ?
6380 if (psd->dacl->num_aces == 0) {
6381 /* No - clear the DACL sent/present flags. */
6382 security_info_sent &= ~SECINFO_DACL;
6383 psd->type &= ~SEC_DESC_DACL_PRESENT;
6387 status = SMB_VFS_NEXT_FSET_NT_ACL(handle, fsp, security_info_sent, psd);
6388 if (!NT_STATUS_IS_OK(status)) {
6389 DEBUG(1, ("fruit_fset_nt_acl: SMB_VFS_NEXT_FSET_NT_ACL failed%s\n", fsp_str_dbg(fsp)));
6395 if (fsp->fh->fd != -1) {
6396 result = SMB_VFS_FCHMOD(fsp, ms_nfs_mode);
6398 result = SMB_VFS_CHMOD(fsp->conn,
6404 DEBUG(1, ("chmod: %s, result: %d, %04o error %s\n", fsp_str_dbg(fsp),
6405 result, (unsigned)ms_nfs_mode,
6407 status = map_nt_error_from_unix(errno);
6414 return NT_STATUS_OK;
6417 static struct vfs_offload_ctx *fruit_offload_ctx;
6419 struct fruit_offload_read_state {
6420 struct vfs_handle_struct *handle;
6421 struct tevent_context *ev;
6427 static void fruit_offload_read_done(struct tevent_req *subreq);
6429 static struct tevent_req *fruit_offload_read_send(
6430 TALLOC_CTX *mem_ctx,
6431 struct tevent_context *ev,
6432 struct vfs_handle_struct *handle,
6439 struct tevent_req *req = NULL;
6440 struct tevent_req *subreq = NULL;
6441 struct fruit_offload_read_state *state = NULL;
6443 req = tevent_req_create(mem_ctx, &state,
6444 struct fruit_offload_read_state);
6448 *state = (struct fruit_offload_read_state) {
6455 subreq = SMB_VFS_NEXT_OFFLOAD_READ_SEND(mem_ctx, ev, handle, fsp,
6456 fsctl, ttl, offset, to_copy);
6457 if (tevent_req_nomem(subreq, req)) {
6458 return tevent_req_post(req, ev);
6460 tevent_req_set_callback(subreq, fruit_offload_read_done, req);
6464 static void fruit_offload_read_done(struct tevent_req *subreq)
6466 struct tevent_req *req = tevent_req_callback_data(
6467 subreq, struct tevent_req);
6468 struct fruit_offload_read_state *state = tevent_req_data(
6469 req, struct fruit_offload_read_state);
6472 status = SMB_VFS_NEXT_OFFLOAD_READ_RECV(subreq,
6476 TALLOC_FREE(subreq);
6477 if (tevent_req_nterror(req, status)) {
6481 if (state->fsctl != FSCTL_SRV_REQUEST_RESUME_KEY) {
6482 tevent_req_done(req);
6486 status = vfs_offload_token_ctx_init(state->fsp->conn->sconn->client,
6487 &fruit_offload_ctx);
6488 if (tevent_req_nterror(req, status)) {
6492 status = vfs_offload_token_db_store_fsp(fruit_offload_ctx,
6495 if (tevent_req_nterror(req, status)) {
6499 tevent_req_done(req);
6503 static NTSTATUS fruit_offload_read_recv(struct tevent_req *req,
6504 struct vfs_handle_struct *handle,
6505 TALLOC_CTX *mem_ctx,
6508 struct fruit_offload_read_state *state = tevent_req_data(
6509 req, struct fruit_offload_read_state);
6512 if (tevent_req_is_nterror(req, &status)) {
6513 tevent_req_received(req);
6517 token->length = state->token.length;
6518 token->data = talloc_move(mem_ctx, &state->token.data);
6520 tevent_req_received(req);
6521 return NT_STATUS_OK;
6524 struct fruit_offload_write_state {
6525 struct vfs_handle_struct *handle;
6527 struct files_struct *src_fsp;
6528 struct files_struct *dst_fsp;
6532 static void fruit_offload_write_done(struct tevent_req *subreq);
6533 static struct tevent_req *fruit_offload_write_send(struct vfs_handle_struct *handle,
6534 TALLOC_CTX *mem_ctx,
6535 struct tevent_context *ev,
6538 off_t transfer_offset,
6539 struct files_struct *dest_fsp,
6543 struct tevent_req *req, *subreq;
6544 struct fruit_offload_write_state *state;
6546 struct fruit_config_data *config;
6547 off_t src_off = transfer_offset;
6548 files_struct *src_fsp = NULL;
6549 off_t to_copy = num;
6550 bool copyfile_enabled = false;
6552 DEBUG(10,("soff: %ju, doff: %ju, len: %ju\n",
6553 (uintmax_t)src_off, (uintmax_t)dest_off, (uintmax_t)num));
6555 SMB_VFS_HANDLE_GET_DATA(handle, config,
6556 struct fruit_config_data,
6559 req = tevent_req_create(mem_ctx, &state,
6560 struct fruit_offload_write_state);
6564 state->handle = handle;
6565 state->dst_fsp = dest_fsp;
6568 case FSCTL_SRV_COPYCHUNK:
6569 case FSCTL_SRV_COPYCHUNK_WRITE:
6570 copyfile_enabled = config->copyfile_enabled;
6577 * Check if this a OS X copyfile style copychunk request with
6578 * a requested chunk count of 0 that was translated to a
6579 * offload_write_send VFS call overloading the parameters src_off
6580 * = dest_off = num = 0.
6582 if (copyfile_enabled && num == 0 && src_off == 0 && dest_off == 0) {
6583 status = vfs_offload_token_db_fetch_fsp(
6584 fruit_offload_ctx, token, &src_fsp);
6585 if (tevent_req_nterror(req, status)) {
6586 return tevent_req_post(req, ev);
6588 state->src_fsp = src_fsp;
6590 status = vfs_stat_fsp(src_fsp);
6591 if (tevent_req_nterror(req, status)) {
6592 return tevent_req_post(req, ev);
6595 to_copy = src_fsp->fsp_name->st.st_ex_size;
6596 state->is_copyfile = true;
6599 subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle,
6608 if (tevent_req_nomem(subreq, req)) {
6609 return tevent_req_post(req, ev);
6612 tevent_req_set_callback(subreq, fruit_offload_write_done, req);
6616 static void fruit_offload_write_done(struct tevent_req *subreq)
6618 struct tevent_req *req = tevent_req_callback_data(
6619 subreq, struct tevent_req);
6620 struct fruit_offload_write_state *state = tevent_req_data(
6621 req, struct fruit_offload_write_state);
6623 unsigned int num_streams = 0;
6624 struct stream_struct *streams = NULL;
6626 struct smb_filename *src_fname_tmp = NULL;
6627 struct smb_filename *dst_fname_tmp = NULL;
6629 status = SMB_VFS_NEXT_OFFLOAD_WRITE_RECV(state->handle,
6632 TALLOC_FREE(subreq);
6633 if (tevent_req_nterror(req, status)) {
6637 if (!state->is_copyfile) {
6638 tevent_req_done(req);
6643 * Now copy all remaining streams. We know the share supports
6644 * streams, because we're in vfs_fruit. We don't do this async
6645 * because streams are few and small.
6647 status = vfs_streaminfo(state->handle->conn, state->src_fsp,
6648 state->src_fsp->fsp_name,
6649 req, &num_streams, &streams);
6650 if (tevent_req_nterror(req, status)) {
6654 if (num_streams == 1) {
6655 /* There is always one stream, ::$DATA. */
6656 tevent_req_done(req);
6660 for (i = 0; i < num_streams; i++) {
6661 DEBUG(10, ("%s: stream: '%s'/%zu\n",
6662 __func__, streams[i].name, (size_t)streams[i].size));
6664 src_fname_tmp = synthetic_smb_fname(
6666 state->src_fsp->fsp_name->base_name,
6669 state->src_fsp->fsp_name->flags);
6670 if (tevent_req_nomem(src_fname_tmp, req)) {
6674 if (is_ntfs_default_stream_smb_fname(src_fname_tmp)) {
6675 TALLOC_FREE(src_fname_tmp);
6679 dst_fname_tmp = synthetic_smb_fname(
6681 state->dst_fsp->fsp_name->base_name,
6684 state->dst_fsp->fsp_name->flags);
6685 if (tevent_req_nomem(dst_fname_tmp, req)) {
6686 TALLOC_FREE(src_fname_tmp);
6690 status = copy_file(req,
6691 state->handle->conn,
6694 OPENX_FILE_CREATE_IF_NOT_EXIST,
6696 if (!NT_STATUS_IS_OK(status)) {
6697 DEBUG(1, ("%s: copy %s to %s failed: %s\n", __func__,
6698 smb_fname_str_dbg(src_fname_tmp),
6699 smb_fname_str_dbg(dst_fname_tmp),
6700 nt_errstr(status)));
6701 TALLOC_FREE(src_fname_tmp);
6702 TALLOC_FREE(dst_fname_tmp);
6703 tevent_req_nterror(req, status);
6707 TALLOC_FREE(src_fname_tmp);
6708 TALLOC_FREE(dst_fname_tmp);
6711 TALLOC_FREE(streams);
6712 TALLOC_FREE(src_fname_tmp);
6713 TALLOC_FREE(dst_fname_tmp);
6714 tevent_req_done(req);
6717 static NTSTATUS fruit_offload_write_recv(struct vfs_handle_struct *handle,
6718 struct tevent_req *req,
6721 struct fruit_offload_write_state *state = tevent_req_data(
6722 req, struct fruit_offload_write_state);
6725 if (tevent_req_is_nterror(req, &status)) {
6726 DEBUG(1, ("server side copy chunk failed: %s\n",
6727 nt_errstr(status)));
6729 tevent_req_received(req);
6733 *copied = state->copied;
6734 tevent_req_received(req);
6736 return NT_STATUS_OK;
6739 static char *fruit_get_bandsize_line(char **lines, int numlines)
6742 static bool re_initialized = false;
6746 if (!re_initialized) {
6747 ret = regcomp(&re, "^[[:blank:]]*<key>band-size</key>$", 0);
6751 re_initialized = true;
6754 for (i = 0; i < numlines; i++) {
6755 regmatch_t matches[1];
6757 ret = regexec(&re, lines[i], 1, matches, 0);
6760 * Check if the match was on the last line, sa we want
6761 * the subsequent line.
6763 if (i + 1 == numlines) {
6766 return lines[i + 1];
6768 if (ret != REG_NOMATCH) {
6776 static bool fruit_get_bandsize_from_line(char *line, size_t *_band_size)
6779 static bool re_initialized = false;
6780 regmatch_t matches[2];
6785 if (!re_initialized) {
6788 "<integer>\\([[:digit:]]*\\)</integer>$",
6793 re_initialized = true;
6796 ret = regexec(&re, line, 2, matches, 0);
6798 DBG_ERR("regex failed [%s]\n", line);
6802 line[matches[1].rm_eo] = '\0';
6804 ok = conv_str_u64(&line[matches[1].rm_so], &band_size);
6808 *_band_size = (size_t)band_size;
6813 * This reads and parses an Info.plist from a TM sparsebundle looking for the
6814 * "band-size" key and value.
6816 static bool fruit_get_bandsize(vfs_handle_struct *handle,
6820 #define INFO_PLIST_MAX_SIZE 64*1024
6822 struct smb_filename *smb_fname = NULL;
6823 files_struct *fsp = NULL;
6824 uint8_t *file_data = NULL;
6825 char **lines = NULL;
6826 char *band_size_line = NULL;
6827 size_t plist_file_size;
6834 plist = talloc_asprintf(talloc_tos(),
6836 handle->conn->connectpath,
6838 if (plist == NULL) {
6843 smb_fname = synthetic_smb_fname(talloc_tos(), plist, NULL, NULL, 0);
6844 if (smb_fname == NULL) {
6849 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
6851 DBG_INFO("Ignoring Sparsebundle without Info.plist [%s]\n", dir);
6856 plist_file_size = smb_fname->st.st_ex_size;
6858 if (plist_file_size > INFO_PLIST_MAX_SIZE) {
6859 DBG_INFO("%s is too large, ignoring\n", plist);
6864 status = SMB_VFS_NEXT_CREATE_FILE(
6867 0, /* root_dir_fid */
6868 smb_fname, /* fname */
6869 FILE_GENERIC_READ, /* access_mask */
6870 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
6871 FILE_OPEN, /* create_disposition */
6872 0, /* create_options */
6873 0, /* file_attributes */
6874 INTERNAL_OPEN_ONLY, /* oplock_request */
6876 0, /* allocation_size */
6877 0, /* private_flags */
6882 NULL, NULL); /* create context */
6883 if (!NT_STATUS_IS_OK(status)) {
6884 DBG_INFO("Opening [%s] failed [%s]\n",
6885 smb_fname_str_dbg(smb_fname), nt_errstr(status));
6890 file_data = talloc_array(talloc_tos(), uint8_t, plist_file_size);
6891 if (file_data == NULL) {
6896 nread = SMB_VFS_NEXT_PREAD(handle, fsp, file_data, plist_file_size, 0);
6897 if (nread != plist_file_size) {
6898 DBG_ERR("Short read on [%s]: %zu/%zd\n",
6899 fsp_str_dbg(fsp), nread, plist_file_size);
6905 status = close_file(NULL, fsp, NORMAL_CLOSE);
6907 if (!NT_STATUS_IS_OK(status)) {
6908 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
6913 lines = file_lines_parse((char *)file_data,
6917 if (lines == NULL) {
6922 band_size_line = fruit_get_bandsize_line(lines, numlines);
6923 if (band_size_line == NULL) {
6924 DBG_ERR("Didn't find band-size key in [%s]\n",
6925 smb_fname_str_dbg(smb_fname));
6930 ok = fruit_get_bandsize_from_line(band_size_line, band_size);
6932 DBG_ERR("fruit_get_bandsize_from_line failed\n");
6936 DBG_DEBUG("Parsed band-size [%zu] for [%s]\n", *band_size, plist);
6940 status = close_file(NULL, fsp, NORMAL_CLOSE);
6941 if (!NT_STATUS_IS_OK(status)) {
6942 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
6947 TALLOC_FREE(smb_fname);
6948 TALLOC_FREE(file_data);
6953 struct fruit_disk_free_state {
6957 static bool fruit_get_num_bands(vfs_handle_struct *handle,
6962 struct smb_filename *bands_dir = NULL;
6964 struct dirent *e = NULL;
6968 path = talloc_asprintf(talloc_tos(),
6970 handle->conn->connectpath,
6976 bands_dir = synthetic_smb_fname(talloc_tos(),
6982 if (bands_dir == NULL) {
6986 d = SMB_VFS_NEXT_OPENDIR(handle, bands_dir, NULL, 0);
6988 TALLOC_FREE(bands_dir);
6994 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
6996 e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
6998 if (ISDOT(e->d_name) || ISDOTDOT(e->d_name)) {
7004 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
7006 TALLOC_FREE(bands_dir);
7010 DBG_DEBUG("%zu bands in [%s]\n", nbands, smb_fname_str_dbg(bands_dir));
7012 TALLOC_FREE(bands_dir);
7018 static bool fruit_tmsize_do_dirent(vfs_handle_struct *handle,
7019 struct fruit_disk_free_state *state,
7024 size_t sparsebundle_strlen = strlen("sparsebundle");
7025 size_t bandsize = 0;
7029 p = strstr(e->d_name, "sparsebundle");
7034 if (p[sparsebundle_strlen] != '\0') {
7038 DBG_DEBUG("Processing sparsebundle [%s]\n", e->d_name);
7040 ok = fruit_get_bandsize(handle, e->d_name, &bandsize);
7043 * Beware of race conditions: this may be an uninitialized
7044 * Info.plist that a client is just creating. We don't want let
7045 * this to trigger complete failure.
7047 DBG_ERR("Processing sparsebundle [%s] failed\n", e->d_name);
7051 ok = fruit_get_num_bands(handle, e->d_name, &nbands);
7054 * Beware of race conditions: this may be a backup sparsebundle
7055 * in an early stage lacking a bands subdirectory. We don't want
7056 * let this to trigger complete failure.
7058 DBG_ERR("Processing sparsebundle [%s] failed\n", e->d_name);
7062 if (bandsize > SIZE_MAX/nbands) {
7063 DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
7067 tm_size = bandsize * nbands;
7069 if (state->total_size + tm_size < state->total_size) {
7070 DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
7075 state->total_size += tm_size;
7077 DBG_DEBUG("[%s] tm_size [%jd] total_size [%jd]\n",
7078 e->d_name, (intmax_t)tm_size, (intmax_t)state->total_size);
7084 * Calculate used size of a TimeMachine volume
7086 * This assumes that the volume is used only for TimeMachine.
7088 * - readdir(basedir of share), then
7089 * - for every element that matches regex "^\(.*\)\.sparsebundle$" :
7090 * - parse "\1.sparsebundle/Info.plist" and read the band-size XML key
7091 * - count band files in "\1.sparsebundle/bands/"
7092 * - calculate used size of all bands: band_count * band_size
7094 static uint64_t fruit_disk_free(vfs_handle_struct *handle,
7095 const struct smb_filename *smb_fname,
7100 struct fruit_config_data *config = NULL;
7101 struct fruit_disk_free_state state = {0};
7103 struct dirent *e = NULL;
7109 SMB_VFS_HANDLE_GET_DATA(handle, config,
7110 struct fruit_config_data,
7113 if (!config->time_machine ||
7114 config->time_machine_max_size == 0)
7116 return SMB_VFS_NEXT_DISK_FREE(handle,
7123 d = SMB_VFS_NEXT_OPENDIR(handle, smb_fname, NULL, 0);
7128 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
7130 e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
7132 ok = fruit_tmsize_do_dirent(handle, &state, e);
7134 SMB_VFS_NEXT_CLOSEDIR(handle, d);
7139 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
7144 dsize = config->time_machine_max_size / 512;
7145 dfree = dsize - (state.total_size / 512);
7146 if (dfree > dsize) {
7156 static struct vfs_fn_pointers vfs_fruit_fns = {
7157 .connect_fn = fruit_connect,
7158 .disk_free_fn = fruit_disk_free,
7160 /* File operations */
7161 .chmod_fn = fruit_chmod,
7162 .chown_fn = fruit_chown,
7163 .unlink_fn = fruit_unlink,
7164 .rename_fn = fruit_rename,
7165 .rmdir_fn = fruit_rmdir,
7166 .open_fn = fruit_open,
7167 .close_fn = fruit_close,
7168 .pread_fn = fruit_pread,
7169 .pwrite_fn = fruit_pwrite,
7170 .pread_send_fn = fruit_pread_send,
7171 .pread_recv_fn = fruit_pread_recv,
7172 .pwrite_send_fn = fruit_pwrite_send,
7173 .pwrite_recv_fn = fruit_pwrite_recv,
7174 .stat_fn = fruit_stat,
7175 .lstat_fn = fruit_lstat,
7176 .fstat_fn = fruit_fstat,
7177 .streaminfo_fn = fruit_streaminfo,
7178 .ntimes_fn = fruit_ntimes,
7179 .ftruncate_fn = fruit_ftruncate,
7180 .fallocate_fn = fruit_fallocate,
7181 .create_file_fn = fruit_create_file,
7182 .readdir_attr_fn = fruit_readdir_attr,
7183 .offload_read_send_fn = fruit_offload_read_send,
7184 .offload_read_recv_fn = fruit_offload_read_recv,
7185 .offload_write_send_fn = fruit_offload_write_send,
7186 .offload_write_recv_fn = fruit_offload_write_recv,
7188 /* NT ACL operations */
7189 .fget_nt_acl_fn = fruit_fget_nt_acl,
7190 .fset_nt_acl_fn = fruit_fset_nt_acl,
7194 NTSTATUS vfs_fruit_init(TALLOC_CTX *ctx)
7196 NTSTATUS ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "fruit",
7198 if (!NT_STATUS_IS_OK(ret)) {
7202 vfs_fruit_debug_level = debug_add_class("fruit");
7203 if (vfs_fruit_debug_level == -1) {
7204 vfs_fruit_debug_level = DBGC_VFS;
7205 DEBUG(0, ("%s: Couldn't register custom debugging class!\n",
7208 DEBUG(10, ("%s: Debug class number of '%s': %d\n",
7209 "vfs_fruit_init","fruit",vfs_fruit_debug_level));