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(struct adouble *ad,
1353 struct fruit_config_data *config = NULL;
1354 uint8_t *map = MAP_FAILED;
1363 SMB_VFS_HANDLE_GET_DATA(ad->ad_handle, config,
1364 struct fruit_config_data, return false);
1366 if (!config->wipe_intentionally_left_blank_rfork) {
1370 if (ad_getentrylen(ad, ADEID_RFORK) != sizeof(empty_resourcefork)) {
1374 maplen = ad_getentryoff(ad, ADEID_RFORK) +
1375 ad_getentrylen(ad, ADEID_RFORK);
1377 /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
1378 map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
1380 if (map == MAP_FAILED) {
1381 DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
1385 cmp = memcmp(map + ADEDOFF_RFORK_DOT_UND,
1387 sizeof(empty_resourcefork));
1388 rc = munmap(map, maplen);
1390 DBG_ERR("munmap failed: %s\n", strerror(errno));
1398 ad_setentrylen(ad, ADEID_RFORK, 0);
1405 len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
1406 if (len != AD_DATASZ_DOT_UND) {
1414 static bool ad_convert_delete_adfile(struct adouble *ad,
1415 const struct smb_filename *smb_fname)
1417 struct fruit_config_data *config = NULL;
1418 struct smb_filename *ad_name = NULL;
1421 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
1425 SMB_VFS_HANDLE_GET_DATA(ad->ad_handle, config,
1426 struct fruit_config_data, return false);
1428 if (!config->delete_empty_adfiles) {
1432 rc = adouble_path(talloc_tos(), smb_fname, &ad_name);
1437 rc = SMB_VFS_NEXT_UNLINK(ad->ad_handle, ad_name);
1439 DBG_ERR("Unlinking [%s] failed: %s\n",
1440 smb_fname_str_dbg(ad_name), strerror(errno));
1441 TALLOC_FREE(ad_name);
1445 DBG_WARNING("Unlinked [%s] after conversion\n", smb_fname_str_dbg(ad_name));
1446 TALLOC_FREE(ad_name);
1452 * Convert from Apple's ._ file to Netatalk
1454 * Apple's AppleDouble may contain a FinderInfo entry longer then 32
1455 * bytes containing packed xattrs.
1457 * @return -1 in case an error occurred, 0 if no conversion was done, 1
1460 static int ad_convert(struct vfs_handle_struct *handle,
1461 const struct smb_filename *smb_fname)
1463 struct adouble *ad = NULL;
1465 bool converted_xattr = false;
1469 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
1474 ok = ad_convert_xattr(handle, ad, smb_fname, &converted_xattr);
1480 ok = ad_convert_blank_rfork(ad, &blank);
1486 if (converted_xattr || blank) {
1487 ok = ad_convert_truncate(ad, smb_fname);
1494 ok = ad_convert_finderinfo(ad, smb_fname);
1496 DBG_ERR("Failed to convert [%s]\n",
1497 smb_fname_str_dbg(smb_fname));
1502 ok = ad_convert_delete_adfile(ad, smb_fname);
1515 * Read and parse Netatalk AppleDouble metadata xattr
1517 static ssize_t ad_read_meta(vfs_handle_struct *handle,
1519 const struct smb_filename *smb_fname)
1525 DEBUG(10, ("reading meta xattr for %s\n", smb_fname->base_name));
1527 ealen = SMB_VFS_GETXATTR(handle->conn, smb_fname,
1528 AFPINFO_EA_NETATALK, ad->ad_data,
1534 if (errno == ENOATTR) {
1540 DEBUG(2, ("error reading meta xattr: %s\n",
1546 if (ealen != AD_DATASZ_XATTR) {
1547 DEBUG(2, ("bad size %zd\n", ealen));
1553 /* Now parse entries */
1554 ok = ad_unpack(ad, ADEID_NUM_XATTR, AD_DATASZ_XATTR);
1556 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1562 if (!ad_getentryoff(ad, ADEID_FINDERI)
1563 || !ad_getentryoff(ad, ADEID_COMMENT)
1564 || !ad_getentryoff(ad, ADEID_FILEDATESI)
1565 || !ad_getentryoff(ad, ADEID_AFPFILEI)
1566 || !ad_getentryoff(ad, ADEID_PRIVDEV)
1567 || !ad_getentryoff(ad, ADEID_PRIVINO)
1568 || !ad_getentryoff(ad, ADEID_PRIVSYN)
1569 || !ad_getentryoff(ad, ADEID_PRIVID)) {
1570 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1577 DEBUG(10, ("reading meta xattr for %s, rc: %d\n",
1578 smb_fname->base_name, rc));
1582 if (errno == EINVAL) {
1584 (void)SMB_VFS_REMOVEXATTR(handle->conn,
1586 AFPINFO_EA_NETATALK);
1594 static int ad_open_rsrc_xattr(const struct smb_filename *smb_fname,
1598 #ifdef HAVE_ATTROPEN
1599 /* FIXME: direct Solaris xattr syscall */
1600 return attropen(smb_fname->base_name,
1601 AFPRESOURCE_EA_NETATALK, flags, mode);
1608 static int ad_open_rsrc_adouble(const struct smb_filename *smb_fname,
1614 struct smb_filename *adp_smb_fname = NULL;
1616 ret = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
1621 fd = open(adp_smb_fname->base_name, flags, mode);
1622 TALLOC_FREE(adp_smb_fname);
1627 static int ad_open_rsrc(vfs_handle_struct *handle,
1628 const struct smb_filename *smb_fname,
1632 struct fruit_config_data *config = NULL;
1635 SMB_VFS_HANDLE_GET_DATA(handle, config,
1636 struct fruit_config_data, return -1);
1638 if (config->rsrc == FRUIT_RSRC_XATTR) {
1639 fd = ad_open_rsrc_xattr(smb_fname, flags, mode);
1641 fd = ad_open_rsrc_adouble(smb_fname, flags, mode);
1648 * Here's the deal: for ADOUBLE_META we can do without an fd as we can issue
1649 * path based xattr calls. For ADOUBLE_RSRC however we need a full-fledged fd
1650 * for file IO on the ._ file.
1652 static int ad_open(vfs_handle_struct *handle,
1655 const struct smb_filename *smb_fname,
1661 DBG_DEBUG("Path [%s] type [%s]\n", smb_fname->base_name,
1662 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
1664 if (ad->ad_type == ADOUBLE_META) {
1668 if ((fsp != NULL) && (fsp->fh != NULL) && (fsp->fh->fd != -1)) {
1669 ad->ad_fd = fsp->fh->fd;
1670 ad->ad_opened = false;
1674 fd = ad_open_rsrc(handle, smb_fname, flags, mode);
1678 ad->ad_opened = true;
1681 DBG_DEBUG("Path [%s] type [%s] fd [%d]\n",
1682 smb_fname->base_name,
1683 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc", fd);
1688 static ssize_t ad_read_rsrc_xattr(vfs_handle_struct *handle,
1694 /* FIXME: direct sys_fstat(), don't have an fsp */
1695 ret = sys_fstat(ad->ad_fd, &st,
1696 lp_fake_directory_create_times(
1697 SNUM(handle->conn)));
1702 ad_setentrylen(ad, ADEID_RFORK, st.st_ex_size);
1703 return st.st_ex_size;
1706 static ssize_t ad_read_rsrc_adouble(vfs_handle_struct *handle,
1708 const struct smb_filename *smb_fname)
1710 SMB_STRUCT_STAT sbuf;
1717 ret = sys_fstat(ad->ad_fd, &sbuf, lp_fake_directory_create_times(
1718 SNUM(handle->conn)));
1724 * AppleDouble file header content and size, two cases:
1726 * - without xattrs it is exactly AD_DATASZ_DOT_UND (82) bytes large
1727 * - with embedded xattrs it can be larger, up to AD_XATTR_MAX_HDR_SIZE
1729 * Read as much as we can up to AD_XATTR_MAX_HDR_SIZE.
1731 size = sbuf.st_ex_size;
1732 if (size > talloc_array_length(ad->ad_data)) {
1733 if (size > AD_XATTR_MAX_HDR_SIZE) {
1734 size = AD_XATTR_MAX_HDR_SIZE;
1736 p_ad = talloc_realloc(ad, ad->ad_data, char, size);
1743 len = sys_pread(ad->ad_fd, ad->ad_data,
1744 talloc_array_length(ad->ad_data), 0);
1745 if (len != talloc_array_length(ad->ad_data)) {
1746 DBG_NOTICE("%s %s: bad size: %zd\n",
1747 smb_fname->base_name, strerror(errno), len);
1751 /* Now parse entries */
1752 ok = ad_unpack(ad, ADEID_NUM_DOT_UND, sbuf.st_ex_size);
1754 DBG_ERR("invalid AppleDouble resource %s\n",
1755 smb_fname->base_name);
1760 if ((ad_getentryoff(ad, ADEID_FINDERI) != ADEDOFF_FINDERI_DOT_UND)
1761 || (ad_getentrylen(ad, ADEID_FINDERI) < ADEDLEN_FINDERI)
1762 || (ad_getentryoff(ad, ADEID_RFORK) < ADEDOFF_RFORK_DOT_UND)) {
1763 DBG_ERR("invalid AppleDouble resource %s\n",
1764 smb_fname->base_name);
1773 * Read and parse resource fork, either ._ AppleDouble file or xattr
1775 static ssize_t ad_read_rsrc(vfs_handle_struct *handle,
1777 const struct smb_filename *smb_fname)
1779 struct fruit_config_data *config = NULL;
1782 SMB_VFS_HANDLE_GET_DATA(handle, config,
1783 struct fruit_config_data, return -1);
1785 if (config->rsrc == FRUIT_RSRC_XATTR) {
1786 len = ad_read_rsrc_xattr(handle, ad);
1788 len = ad_read_rsrc_adouble(handle, ad, smb_fname);
1795 * Read and unpack an AppleDouble metadata xattr or resource
1797 static ssize_t ad_read(vfs_handle_struct *handle,
1799 const struct smb_filename *smb_fname)
1801 switch (ad->ad_type) {
1803 return ad_read_meta(handle, ad, smb_fname);
1805 return ad_read_rsrc(handle, ad, smb_fname);
1811 static int adouble_destructor(struct adouble *ad)
1813 if ((ad->ad_fd != -1) && ad->ad_opened) {
1821 * Allocate a struct adouble without initialiing it
1823 * The struct is either hang of the fsp extension context or if fsp is
1826 * @param[in] ctx talloc context
1827 * @param[in] handle vfs handle
1828 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1830 * @return adouble handle
1832 static struct adouble *ad_alloc(TALLOC_CTX *ctx, vfs_handle_struct *handle,
1833 adouble_type_t type)
1838 struct fruit_config_data *config;
1840 SMB_VFS_HANDLE_GET_DATA(handle, config,
1841 struct fruit_config_data, return NULL);
1845 adsize = AD_DATASZ_XATTR;
1848 if (config->rsrc == FRUIT_RSRC_ADFILE) {
1849 adsize = AD_DATASZ_DOT_UND;
1856 ad = talloc_zero(ctx, struct adouble);
1863 ad->ad_data = talloc_zero_array(ad, char, adsize);
1864 if (ad->ad_data == NULL) {
1870 ad->ad_handle = handle;
1872 ad->ad_magic = AD_MAGIC;
1873 ad->ad_version = AD_VERSION;
1876 talloc_set_destructor(ad, adouble_destructor);
1886 * Allocate and initialize a new struct adouble
1888 * @param[in] ctx talloc context
1889 * @param[in] handle vfs handle
1890 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1892 * @return adouble handle, initialized
1894 static struct adouble *ad_init(TALLOC_CTX *ctx, vfs_handle_struct *handle,
1895 adouble_type_t type)
1898 const struct ad_entry_order *eid;
1899 struct adouble *ad = NULL;
1900 struct fruit_config_data *config;
1901 time_t t = time(NULL);
1903 SMB_VFS_HANDLE_GET_DATA(handle, config,
1904 struct fruit_config_data, return NULL);
1908 eid = entry_order_meta_xattr;
1911 if (config->rsrc == FRUIT_RSRC_ADFILE) {
1912 eid = entry_order_dot_und;
1914 eid = entry_order_rsrc_xattr;
1921 ad = ad_alloc(ctx, handle, type);
1927 ad->ad_eid[eid->id].ade_off = eid->offset;
1928 ad->ad_eid[eid->id].ade_len = eid->len;
1932 /* put something sane in the date fields */
1933 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, t);
1934 ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, t);
1935 ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, t);
1936 ad_setdate(ad, AD_DATE_BACKUP, htonl(AD_DATE_START));
1944 static struct adouble *ad_get_internal(TALLOC_CTX *ctx,
1945 vfs_handle_struct *handle,
1947 const struct smb_filename *smb_fname,
1948 adouble_type_t type)
1952 struct adouble *ad = NULL;
1956 smb_fname = fsp->base_fsp->fsp_name;
1959 DEBUG(10, ("ad_get(%s) called for %s\n",
1960 type == ADOUBLE_META ? "meta" : "rsrc",
1961 smb_fname->base_name));
1963 ad = ad_alloc(ctx, handle, type);
1969 /* Try rw first so we can use the fd in ad_convert() */
1972 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
1973 if (rc == -1 && ((errno == EROFS) || (errno == EACCES))) {
1975 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
1978 DBG_DEBUG("ad_open [%s] error [%s]\n",
1979 smb_fname->base_name, strerror(errno));
1984 len = ad_read(handle, ad, smb_fname);
1986 DEBUG(10, ("error reading AppleDouble for %s\n",
1987 smb_fname->base_name));
1993 DEBUG(10, ("ad_get(%s) for %s returning %d\n",
1994 type == ADOUBLE_META ? "meta" : "rsrc",
1995 smb_fname->base_name, rc));
2004 * Return AppleDouble data for a file
2006 * @param[in] ctx talloc context
2007 * @param[in] handle vfs handle
2008 * @param[in] smb_fname pathname to file or directory
2009 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2011 * @return talloced struct adouble or NULL on error
2013 static struct adouble *ad_get(TALLOC_CTX *ctx,
2014 vfs_handle_struct *handle,
2015 const struct smb_filename *smb_fname,
2016 adouble_type_t type)
2018 return ad_get_internal(ctx, handle, NULL, smb_fname, type);
2022 * Return AppleDouble data for a file
2024 * @param[in] ctx talloc context
2025 * @param[in] handle vfs handle
2026 * @param[in] fsp fsp to use for IO
2027 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2029 * @return talloced struct adouble or NULL on error
2031 static struct adouble *ad_fget(TALLOC_CTX *ctx, vfs_handle_struct *handle,
2032 files_struct *fsp, adouble_type_t type)
2034 return ad_get_internal(ctx, handle, fsp, NULL, type);
2038 * Set AppleDouble metadata on a file or directory
2040 * @param[in] ad adouble handle
2042 * @param[in] smb_fname pathname to file or directory
2044 * @return status code, 0 means success
2046 static int ad_set(vfs_handle_struct *handle,
2048 const struct smb_filename *smb_fname)
2053 DBG_DEBUG("Path [%s]\n", smb_fname->base_name);
2055 if (ad->ad_type != ADOUBLE_META) {
2056 DBG_ERR("ad_set on [%s] used with ADOUBLE_RSRC\n",
2057 smb_fname->base_name);
2066 ret = SMB_VFS_SETXATTR(handle->conn,
2068 AFPINFO_EA_NETATALK,
2070 AD_DATASZ_XATTR, 0);
2072 DBG_DEBUG("Path [%s] ret [%d]\n", smb_fname->base_name, ret);
2078 * Set AppleDouble metadata on a file or directory
2080 * @param[in] ad adouble handle
2081 * @param[in] fsp file handle
2083 * @return status code, 0 means success
2085 static int ad_fset(struct vfs_handle_struct *handle,
2093 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
2096 || (fsp->fh == NULL)
2097 || (fsp->fh->fd == -1))
2099 smb_panic("bad fsp");
2107 switch (ad->ad_type) {
2109 rc = SMB_VFS_NEXT_SETXATTR(handle,
2111 AFPINFO_EA_NETATALK,
2113 AD_DATASZ_XATTR, 0);
2117 len = SMB_VFS_NEXT_PWRITE(handle,
2122 if (len != AD_DATASZ_DOT_UND) {
2123 DBG_ERR("short write on %s: %zd", fsp_str_dbg(fsp), len);
2133 DBG_DEBUG("Path [%s] rc [%d]\n", fsp_str_dbg(fsp), rc);
2138 /*****************************************************************************
2140 *****************************************************************************/
2142 static bool is_afpinfo_stream(const struct smb_filename *smb_fname)
2144 if (strncasecmp_m(smb_fname->stream_name,
2145 AFPINFO_STREAM_NAME,
2146 strlen(AFPINFO_STREAM_NAME)) == 0) {
2152 static bool is_afpresource_stream(const struct smb_filename *smb_fname)
2154 if (strncasecmp_m(smb_fname->stream_name,
2155 AFPRESOURCE_STREAM_NAME,
2156 strlen(AFPRESOURCE_STREAM_NAME)) == 0) {
2163 * Test whether stream is an Apple stream.
2165 static bool is_apple_stream(const struct smb_filename *smb_fname)
2167 if (is_afpinfo_stream(smb_fname)) {
2170 if (is_afpresource_stream(smb_fname)) {
2177 * Initialize config struct from our smb.conf config parameters
2179 static int init_fruit_config(vfs_handle_struct *handle)
2181 struct fruit_config_data *config;
2183 const char *tm_size_str = NULL;
2185 config = talloc_zero(handle->conn, struct fruit_config_data);
2187 DEBUG(1, ("talloc_zero() failed\n"));
2193 * Versions up to Samba 4.5.x had a spelling bug in the
2194 * fruit:resource option calling lp_parm_enum with
2195 * "res*s*ource" (ie two s).
2197 * In Samba 4.6 we accept both the wrong and the correct
2198 * spelling, in Samba 4.7 the bad spelling will be removed.
2200 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2201 "ressource", fruit_rsrc, FRUIT_RSRC_ADFILE);
2202 if (enumval == -1) {
2203 DEBUG(1, ("value for %s: resource type unknown\n",
2204 FRUIT_PARAM_TYPE_NAME));
2207 config->rsrc = (enum fruit_rsrc)enumval;
2209 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2210 "resource", fruit_rsrc, enumval);
2211 if (enumval == -1) {
2212 DEBUG(1, ("value for %s: resource type unknown\n",
2213 FRUIT_PARAM_TYPE_NAME));
2216 config->rsrc = (enum fruit_rsrc)enumval;
2218 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2219 "metadata", fruit_meta, FRUIT_META_NETATALK);
2220 if (enumval == -1) {
2221 DEBUG(1, ("value for %s: metadata type unknown\n",
2222 FRUIT_PARAM_TYPE_NAME));
2225 config->meta = (enum fruit_meta)enumval;
2227 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2228 "locking", fruit_locking, FRUIT_LOCKING_NONE);
2229 if (enumval == -1) {
2230 DEBUG(1, ("value for %s: locking type unknown\n",
2231 FRUIT_PARAM_TYPE_NAME));
2234 config->locking = (enum fruit_locking)enumval;
2236 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2237 "encoding", fruit_encoding, FRUIT_ENC_PRIVATE);
2238 if (enumval == -1) {
2239 DEBUG(1, ("value for %s: encoding type unknown\n",
2240 FRUIT_PARAM_TYPE_NAME));
2243 config->encoding = (enum fruit_encoding)enumval;
2245 if (config->rsrc == FRUIT_RSRC_ADFILE) {
2246 config->veto_appledouble = lp_parm_bool(SNUM(handle->conn),
2247 FRUIT_PARAM_TYPE_NAME,
2252 config->use_aapl = lp_parm_bool(
2253 -1, FRUIT_PARAM_TYPE_NAME, "aapl", true);
2255 config->time_machine = lp_parm_bool(
2256 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "time machine", false);
2258 config->unix_info_enabled = lp_parm_bool(
2259 -1, FRUIT_PARAM_TYPE_NAME, "nfs_aces", true);
2261 config->use_copyfile = lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME,
2264 config->posix_rename = lp_parm_bool(
2265 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "posix_rename", true);
2267 config->aapl_zero_file_id =
2268 lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME, "zero_file_id", true);
2270 config->readdir_attr_rsize = lp_parm_bool(
2271 SNUM(handle->conn), "readdir_attr", "aapl_rsize", true);
2273 config->readdir_attr_finder_info = lp_parm_bool(
2274 SNUM(handle->conn), "readdir_attr", "aapl_finder_info", true);
2276 config->readdir_attr_max_access = lp_parm_bool(
2277 SNUM(handle->conn), "readdir_attr", "aapl_max_access", true);
2279 config->model = lp_parm_const_string(
2280 -1, FRUIT_PARAM_TYPE_NAME, "model", "MacSamba");
2282 tm_size_str = lp_parm_const_string(
2283 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2284 "time machine max size", NULL);
2285 if (tm_size_str != NULL) {
2286 config->time_machine_max_size = conv_str_size(tm_size_str);
2289 config->wipe_intentionally_left_blank_rfork = lp_parm_bool(
2290 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2291 "wipe_intentionally_left_blank_rfork", false);
2293 config->delete_empty_adfiles = lp_parm_bool(
2294 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2295 "delete_empty_adfiles", false);
2297 SMB_VFS_HANDLE_SET_DATA(handle, config,
2298 NULL, struct fruit_config_data,
2305 * Prepend "._" to a basename
2306 * Return a new struct smb_filename with stream_name == NULL.
2308 static int adouble_path(TALLOC_CTX *ctx,
2309 const struct smb_filename *smb_fname_in,
2310 struct smb_filename **pp_smb_fname_out)
2314 struct smb_filename *smb_fname = cp_smb_filename(ctx,
2317 if (smb_fname == NULL) {
2321 /* We need streamname to be NULL */
2322 TALLOC_FREE(smb_fname->stream_name);
2324 /* And we're replacing base_name. */
2325 TALLOC_FREE(smb_fname->base_name);
2327 if (!parent_dirname(smb_fname, smb_fname_in->base_name,
2329 TALLOC_FREE(smb_fname);
2333 smb_fname->base_name = talloc_asprintf(smb_fname,
2334 "%s/._%s", parent, base);
2335 if (smb_fname->base_name == NULL) {
2336 TALLOC_FREE(smb_fname);
2340 *pp_smb_fname_out = smb_fname;
2346 * Allocate and initialize an AfpInfo struct
2348 static AfpInfo *afpinfo_new(TALLOC_CTX *ctx)
2350 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2354 ai->afpi_Signature = AFP_Signature;
2355 ai->afpi_Version = AFP_Version;
2356 ai->afpi_BackupTime = AD_DATE_START;
2361 * Pack an AfpInfo struct into a buffer
2363 * Buffer size must be at least AFP_INFO_SIZE
2364 * Returns size of packed buffer
2366 static ssize_t afpinfo_pack(const AfpInfo *ai, char *buf)
2368 memset(buf, 0, AFP_INFO_SIZE);
2370 RSIVAL(buf, 0, ai->afpi_Signature);
2371 RSIVAL(buf, 4, ai->afpi_Version);
2372 RSIVAL(buf, 12, ai->afpi_BackupTime);
2373 memcpy(buf + 16, ai->afpi_FinderInfo, sizeof(ai->afpi_FinderInfo));
2375 return AFP_INFO_SIZE;
2379 * Unpack a buffer into a AfpInfo structure
2381 * Buffer size must be at least AFP_INFO_SIZE
2382 * Returns allocated AfpInfo struct
2384 static AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data)
2386 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2391 ai->afpi_Signature = RIVAL(data, 0);
2392 ai->afpi_Version = RIVAL(data, 4);
2393 ai->afpi_BackupTime = RIVAL(data, 12);
2394 memcpy(ai->afpi_FinderInfo, (const char *)data + 16,
2395 sizeof(ai->afpi_FinderInfo));
2397 if (ai->afpi_Signature != AFP_Signature
2398 || ai->afpi_Version != AFP_Version) {
2399 DEBUG(1, ("Bad AfpInfo signature or version\n"));
2407 * Fake an inode number from the md5 hash of the (xattr) name
2409 static SMB_INO_T fruit_inode(const SMB_STRUCT_STAT *sbuf, const char *sname)
2411 gnutls_hash_hd_t hash_hnd = NULL;
2412 unsigned char hash[16];
2413 SMB_INO_T result = 0;
2417 DBG_DEBUG("fruit_inode called for %ju/%ju [%s]\n",
2418 (uintmax_t)sbuf->st_ex_dev,
2419 (uintmax_t)sbuf->st_ex_ino, sname);
2421 upper_sname = talloc_strdup_upper(talloc_tos(), sname);
2422 SMB_ASSERT(upper_sname != NULL);
2424 rc = gnutls_hash_init(&hash_hnd, GNUTLS_DIG_MD5);
2429 rc = gnutls_hash(hash_hnd, &(sbuf->st_ex_dev), sizeof(sbuf->st_ex_dev));
2431 gnutls_hash_deinit(hash_hnd, NULL);
2434 rc = gnutls_hash(hash_hnd,
2436 sizeof(sbuf->st_ex_ino));
2438 gnutls_hash_deinit(hash_hnd, NULL);
2441 rc = gnutls_hash(hash_hnd,
2443 talloc_get_size(upper_sname) - 1);
2445 gnutls_hash_deinit(hash_hnd, NULL);
2449 gnutls_hash_deinit(hash_hnd, hash);
2451 /* Hopefully all the variation is in the lower 4 (or 8) bytes! */
2452 memcpy(&result, hash, sizeof(result));
2455 DBG_DEBUG("fruit_inode \"%s\": ino=%ju\n",
2456 sname, (uintmax_t)result);
2459 TALLOC_FREE(upper_sname);
2464 static bool add_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
2465 struct stream_struct **streams,
2466 const char *name, off_t size,
2469 struct stream_struct *tmp;
2471 tmp = talloc_realloc(mem_ctx, *streams, struct stream_struct,
2477 tmp[*num_streams].name = talloc_asprintf(tmp, "%s:$DATA", name);
2478 if (tmp[*num_streams].name == NULL) {
2482 tmp[*num_streams].size = size;
2483 tmp[*num_streams].alloc_size = alloc_size;
2490 static bool filter_empty_rsrc_stream(unsigned int *num_streams,
2491 struct stream_struct **streams)
2493 struct stream_struct *tmp = *streams;
2496 if (*num_streams == 0) {
2500 for (i = 0; i < *num_streams; i++) {
2501 if (strequal_m(tmp[i].name, AFPRESOURCE_STREAM)) {
2506 if (i == *num_streams) {
2510 if (tmp[i].size > 0) {
2514 TALLOC_FREE(tmp[i].name);
2515 if (*num_streams - 1 > i) {
2516 memmove(&tmp[i], &tmp[i+1],
2517 (*num_streams - i - 1) * sizeof(struct stream_struct));
2524 static bool del_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
2525 struct stream_struct **streams,
2528 struct stream_struct *tmp = *streams;
2531 if (*num_streams == 0) {
2535 for (i = 0; i < *num_streams; i++) {
2536 if (strequal_m(tmp[i].name, name)) {
2541 if (i == *num_streams) {
2545 TALLOC_FREE(tmp[i].name);
2546 if (*num_streams - 1 > i) {
2547 memmove(&tmp[i], &tmp[i+1],
2548 (*num_streams - i - 1) * sizeof(struct stream_struct));
2555 static bool ad_empty_finderinfo(const struct adouble *ad)
2558 char emptybuf[ADEDLEN_FINDERI] = {0};
2561 fi = ad_get_entry(ad, ADEID_FINDERI);
2563 DBG_ERR("Missing FinderInfo in struct adouble [%p]\n", ad);
2567 cmp = memcmp(emptybuf, fi, ADEDLEN_FINDERI);
2571 static bool ai_empty_finderinfo(const AfpInfo *ai)
2574 char emptybuf[ADEDLEN_FINDERI] = {0};
2576 cmp = memcmp(emptybuf, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
2581 * Update btime with btime from Netatalk
2583 static void update_btime(vfs_handle_struct *handle,
2584 struct smb_filename *smb_fname)
2587 struct timespec creation_time = {0};
2589 struct fruit_config_data *config = NULL;
2591 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
2594 switch (config->meta) {
2595 case FRUIT_META_STREAM:
2597 case FRUIT_META_NETATALK:
2601 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
2605 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
2609 if (ad_getdate(ad, AD_DATE_UNIX | AD_DATE_CREATE, &t) != 0) {
2615 creation_time.tv_sec = convert_uint32_t_to_time_t(t);
2616 update_stat_ex_create_time(&smb_fname->st, creation_time);
2622 * Map an access mask to a Netatalk single byte byte range lock
2624 static off_t access_to_netatalk_brl(enum apple_fork fork_type,
2625 uint32_t access_mask)
2629 switch (access_mask) {
2630 case FILE_READ_DATA:
2631 offset = AD_FILELOCK_OPEN_RD;
2634 case FILE_WRITE_DATA:
2635 case FILE_APPEND_DATA:
2636 offset = AD_FILELOCK_OPEN_WR;
2640 offset = AD_FILELOCK_OPEN_NONE;
2644 if (fork_type == APPLE_FORK_RSRC) {
2645 if (offset == AD_FILELOCK_OPEN_NONE) {
2646 offset = AD_FILELOCK_RSRC_OPEN_NONE;
2656 * Map a deny mode to a Netatalk brl
2658 static off_t denymode_to_netatalk_brl(enum apple_fork fork_type,
2663 switch (deny_mode) {
2665 offset = AD_FILELOCK_DENY_RD;
2669 offset = AD_FILELOCK_DENY_WR;
2673 smb_panic("denymode_to_netatalk_brl: bad deny mode\n");
2676 if (fork_type == APPLE_FORK_RSRC) {
2684 * Call fcntl() with an exclusive F_GETLK request in order to
2685 * determine if there's an exisiting shared lock
2687 * @return true if the requested lock was found or any error occurred
2688 * false if the lock was not found
2690 static bool test_netatalk_lock(files_struct *fsp, off_t in_offset)
2693 off_t offset = in_offset;
2698 result = SMB_VFS_GETLOCK(fsp, &offset, &len, &type, &pid);
2699 if (result == false) {
2703 if (type != F_UNLCK) {
2710 static NTSTATUS fruit_check_access(vfs_handle_struct *handle,
2712 uint32_t access_mask,
2713 uint32_t share_mode)
2715 NTSTATUS status = NT_STATUS_OK;
2717 bool share_for_read = (share_mode & FILE_SHARE_READ);
2718 bool share_for_write = (share_mode & FILE_SHARE_WRITE);
2719 bool netatalk_already_open_for_reading = false;
2720 bool netatalk_already_open_for_writing = false;
2721 bool netatalk_already_open_with_deny_read = false;
2722 bool netatalk_already_open_with_deny_write = false;
2724 /* FIXME: hardcoded data fork, add resource fork */
2725 enum apple_fork fork_type = APPLE_FORK_DATA;
2727 DBG_DEBUG("fruit_check_access: %s, am: %s/%s, sm: 0x%x\n",
2729 access_mask & FILE_READ_DATA ? "READ" :"-",
2730 access_mask & FILE_WRITE_DATA ? "WRITE" : "-",
2733 if (fsp->fh->fd == -1) {
2734 return NT_STATUS_OK;
2737 /* Read NetATalk opens and deny modes on the file. */
2738 netatalk_already_open_for_reading = test_netatalk_lock(fsp,
2739 access_to_netatalk_brl(fork_type,
2742 netatalk_already_open_with_deny_read = test_netatalk_lock(fsp,
2743 denymode_to_netatalk_brl(fork_type,
2746 netatalk_already_open_for_writing = test_netatalk_lock(fsp,
2747 access_to_netatalk_brl(fork_type,
2750 netatalk_already_open_with_deny_write = test_netatalk_lock(fsp,
2751 denymode_to_netatalk_brl(fork_type,
2754 /* If there are any conflicts - sharing violation. */
2755 if ((access_mask & FILE_READ_DATA) &&
2756 netatalk_already_open_with_deny_read) {
2757 return NT_STATUS_SHARING_VIOLATION;
2760 if (!share_for_read &&
2761 netatalk_already_open_for_reading) {
2762 return NT_STATUS_SHARING_VIOLATION;
2765 if ((access_mask & FILE_WRITE_DATA) &&
2766 netatalk_already_open_with_deny_write) {
2767 return NT_STATUS_SHARING_VIOLATION;
2770 if (!share_for_write &&
2771 netatalk_already_open_for_writing) {
2772 return NT_STATUS_SHARING_VIOLATION;
2775 if (!(access_mask & FILE_READ_DATA)) {
2777 * Nothing we can do here, we need read access
2780 return NT_STATUS_OK;
2783 /* Set NetAtalk locks matching our access */
2784 if (access_mask & FILE_READ_DATA) {
2785 struct byte_range_lock *br_lck = NULL;
2787 off = access_to_netatalk_brl(fork_type, FILE_READ_DATA);
2789 handle->conn->sconn->msg_ctx, fsp,
2790 fsp->op->global->open_persistent_id, 1, off,
2791 READ_LOCK, POSIX_LOCK, false,
2794 TALLOC_FREE(br_lck);
2796 if (!NT_STATUS_IS_OK(status)) {
2801 if (!share_for_read) {
2802 struct byte_range_lock *br_lck = NULL;
2804 off = denymode_to_netatalk_brl(fork_type, DENY_READ);
2806 handle->conn->sconn->msg_ctx, fsp,
2807 fsp->op->global->open_persistent_id, 1, off,
2808 READ_LOCK, POSIX_LOCK, false,
2811 TALLOC_FREE(br_lck);
2813 if (!NT_STATUS_IS_OK(status)) {
2818 if (access_mask & FILE_WRITE_DATA) {
2819 struct byte_range_lock *br_lck = NULL;
2821 off = access_to_netatalk_brl(fork_type, FILE_WRITE_DATA);
2823 handle->conn->sconn->msg_ctx, fsp,
2824 fsp->op->global->open_persistent_id, 1, off,
2825 READ_LOCK, POSIX_LOCK, false,
2828 TALLOC_FREE(br_lck);
2830 if (!NT_STATUS_IS_OK(status)) {
2835 if (!share_for_write) {
2836 struct byte_range_lock *br_lck = NULL;
2838 off = denymode_to_netatalk_brl(fork_type, DENY_WRITE);
2840 handle->conn->sconn->msg_ctx, fsp,
2841 fsp->op->global->open_persistent_id, 1, off,
2842 READ_LOCK, POSIX_LOCK, false,
2845 TALLOC_FREE(br_lck);
2847 if (!NT_STATUS_IS_OK(status)) {
2852 return NT_STATUS_OK;
2855 static NTSTATUS check_aapl(vfs_handle_struct *handle,
2856 struct smb_request *req,
2857 const struct smb2_create_blobs *in_context_blobs,
2858 struct smb2_create_blobs *out_context_blobs)
2860 struct fruit_config_data *config;
2862 struct smb2_create_blob *aapl = NULL;
2866 DATA_BLOB blob = data_blob_talloc(req, NULL, 0);
2867 uint64_t req_bitmap, client_caps;
2868 uint64_t server_caps = SMB2_CRTCTX_AAPL_UNIX_BASED;
2872 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
2873 return NT_STATUS_UNSUCCESSFUL);
2875 if (!config->use_aapl
2876 || in_context_blobs == NULL
2877 || out_context_blobs == NULL) {
2878 return NT_STATUS_OK;
2881 aapl = smb2_create_blob_find(in_context_blobs,
2882 SMB2_CREATE_TAG_AAPL);
2884 return NT_STATUS_OK;
2887 if (aapl->data.length != 24) {
2888 DEBUG(1, ("unexpected AAPL ctxt length: %ju\n",
2889 (uintmax_t)aapl->data.length));
2890 return NT_STATUS_INVALID_PARAMETER;
2893 cmd = IVAL(aapl->data.data, 0);
2894 if (cmd != SMB2_CRTCTX_AAPL_SERVER_QUERY) {
2895 DEBUG(1, ("unsupported AAPL cmd: %d\n", cmd));
2896 return NT_STATUS_INVALID_PARAMETER;
2899 req_bitmap = BVAL(aapl->data.data, 8);
2900 client_caps = BVAL(aapl->data.data, 16);
2902 SIVAL(p, 0, SMB2_CRTCTX_AAPL_SERVER_QUERY);
2904 SBVAL(p, 8, req_bitmap);
2905 ok = data_blob_append(req, &blob, p, 16);
2907 return NT_STATUS_UNSUCCESSFUL;
2910 if (req_bitmap & SMB2_CRTCTX_AAPL_SERVER_CAPS) {
2911 if ((client_caps & SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR) &&
2912 (handle->conn->tcon->compat->fs_capabilities & FILE_NAMED_STREAMS)) {
2913 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR;
2914 config->readdir_attr_enabled = true;
2917 if (config->use_copyfile) {
2918 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_OSX_COPYFILE;
2919 config->copyfile_enabled = true;
2923 * The client doesn't set the flag, so we can't check
2924 * for it and just set it unconditionally
2926 if (config->unix_info_enabled) {
2927 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_NFS_ACE;
2930 SBVAL(p, 0, server_caps);
2931 ok = data_blob_append(req, &blob, p, 8);
2933 return NT_STATUS_UNSUCCESSFUL;
2937 if (req_bitmap & SMB2_CRTCTX_AAPL_VOLUME_CAPS) {
2938 int val = lp_case_sensitive(SNUM(handle->conn->tcon->compat));
2946 caps |= SMB2_CRTCTX_AAPL_CASE_SENSITIVE;
2953 if (config->time_machine) {
2954 caps |= SMB2_CRTCTX_AAPL_FULL_SYNC;
2959 ok = data_blob_append(req, &blob, p, 8);
2961 return NT_STATUS_UNSUCCESSFUL;
2965 if (req_bitmap & SMB2_CRTCTX_AAPL_MODEL_INFO) {
2966 ok = convert_string_talloc(req,
2967 CH_UNIX, CH_UTF16LE,
2968 config->model, strlen(config->model),
2971 return NT_STATUS_UNSUCCESSFUL;
2975 SIVAL(p + 4, 0, modellen);
2976 ok = data_blob_append(req, &blob, p, 8);
2979 return NT_STATUS_UNSUCCESSFUL;
2982 ok = data_blob_append(req, &blob, model, modellen);
2985 return NT_STATUS_UNSUCCESSFUL;
2989 status = smb2_create_blob_add(out_context_blobs,
2991 SMB2_CREATE_TAG_AAPL,
2993 if (NT_STATUS_IS_OK(status)) {
2994 global_fruit_config.nego_aapl = true;
2995 if (config->aapl_zero_file_id) {
2996 aapl_force_zero_file_id(handle->conn->sconn);
3003 static bool readdir_attr_meta_finderi_stream(
3004 struct vfs_handle_struct *handle,
3005 const struct smb_filename *smb_fname,
3008 struct smb_filename *stream_name = NULL;
3009 files_struct *fsp = NULL;
3014 uint8_t buf[AFP_INFO_SIZE];
3016 stream_name = synthetic_smb_fname(talloc_tos(),
3017 smb_fname->base_name,
3018 AFPINFO_STREAM_NAME,
3019 NULL, smb_fname->flags);
3020 if (stream_name == NULL) {
3024 ret = SMB_VFS_STAT(handle->conn, stream_name);
3029 status = SMB_VFS_CREATE_FILE(
3030 handle->conn, /* conn */
3032 0, /* root_dir_fid */
3033 stream_name, /* fname */
3034 FILE_READ_DATA, /* access_mask */
3035 (FILE_SHARE_READ | FILE_SHARE_WRITE | /* share_access */
3037 FILE_OPEN, /* create_disposition*/
3038 0, /* create_options */
3039 0, /* file_attributes */
3040 INTERNAL_OPEN_ONLY, /* oplock_request */
3042 0, /* allocation_size */
3043 0, /* private_flags */
3048 NULL, NULL); /* create context */
3050 TALLOC_FREE(stream_name);
3052 if (!NT_STATUS_IS_OK(status)) {
3056 nread = SMB_VFS_PREAD(fsp, &buf[0], AFP_INFO_SIZE, 0);
3057 if (nread != AFP_INFO_SIZE) {
3058 DBG_ERR("short read [%s] [%zd/%d]\n",
3059 smb_fname_str_dbg(stream_name), nread, AFP_INFO_SIZE);
3064 memcpy(&ai->afpi_FinderInfo[0], &buf[AFP_OFF_FinderInfo],
3071 close_file(NULL, fsp, NORMAL_CLOSE);
3077 static bool readdir_attr_meta_finderi_netatalk(
3078 struct vfs_handle_struct *handle,
3079 const struct smb_filename *smb_fname,
3082 struct adouble *ad = NULL;
3085 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
3090 p = ad_get_entry(ad, ADEID_FINDERI);
3092 DBG_ERR("No ADEID_FINDERI for [%s]\n", smb_fname->base_name);
3097 memcpy(&ai->afpi_FinderInfo[0], p, AFP_FinderSize);
3102 static bool readdir_attr_meta_finderi(struct vfs_handle_struct *handle,
3103 const struct smb_filename *smb_fname,
3104 struct readdir_attr_data *attr_data)
3106 struct fruit_config_data *config = NULL;
3107 uint32_t date_added;
3111 SMB_VFS_HANDLE_GET_DATA(handle, config,
3112 struct fruit_config_data,
3115 switch (config->meta) {
3116 case FRUIT_META_NETATALK:
3117 ok = readdir_attr_meta_finderi_netatalk(
3118 handle, smb_fname, &ai);
3121 case FRUIT_META_STREAM:
3122 ok = readdir_attr_meta_finderi_stream(
3123 handle, smb_fname, &ai);
3127 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3132 /* Don't bother with errors, it's likely ENOENT */
3136 if (S_ISREG(smb_fname->st.st_ex_mode)) {
3138 memcpy(&attr_data->attr_data.aapl.finder_info[0],
3139 &ai.afpi_FinderInfo[0], 4);
3141 /* finder_creator */
3142 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 4,
3143 &ai.afpi_FinderInfo[4], 4);
3147 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 8,
3148 &ai.afpi_FinderInfo[8], 2);
3150 /* finder_ext_flags */
3151 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 10,
3152 &ai.afpi_FinderInfo[24], 2);
3155 date_added = convert_time_t_to_uint32_t(
3156 smb_fname->st.st_ex_btime.tv_sec - AD_DATE_DELTA);
3158 RSIVAL(&attr_data->attr_data.aapl.finder_info[0], 12, date_added);
3163 static uint64_t readdir_attr_rfork_size_adouble(
3164 struct vfs_handle_struct *handle,
3165 const struct smb_filename *smb_fname)
3167 struct adouble *ad = NULL;
3168 uint64_t rfork_size;
3170 ad = ad_get(talloc_tos(), handle, smb_fname,
3176 rfork_size = ad_getentrylen(ad, ADEID_RFORK);
3182 static uint64_t readdir_attr_rfork_size_stream(
3183 struct vfs_handle_struct *handle,
3184 const struct smb_filename *smb_fname)
3186 struct smb_filename *stream_name = NULL;
3188 uint64_t rfork_size;
3190 stream_name = synthetic_smb_fname(talloc_tos(),
3191 smb_fname->base_name,
3192 AFPRESOURCE_STREAM_NAME,
3194 if (stream_name == NULL) {
3198 ret = SMB_VFS_STAT(handle->conn, stream_name);
3200 TALLOC_FREE(stream_name);
3204 rfork_size = stream_name->st.st_ex_size;
3205 TALLOC_FREE(stream_name);
3210 static uint64_t readdir_attr_rfork_size(struct vfs_handle_struct *handle,
3211 const struct smb_filename *smb_fname)
3213 struct fruit_config_data *config = NULL;
3214 uint64_t rfork_size;
3216 SMB_VFS_HANDLE_GET_DATA(handle, config,
3217 struct fruit_config_data,
3220 switch (config->rsrc) {
3221 case FRUIT_RSRC_ADFILE:
3222 case FRUIT_RSRC_XATTR:
3223 rfork_size = readdir_attr_rfork_size_adouble(handle,
3227 case FRUIT_META_STREAM:
3228 rfork_size = readdir_attr_rfork_size_stream(handle,
3233 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3241 static NTSTATUS readdir_attr_macmeta(struct vfs_handle_struct *handle,
3242 const struct smb_filename *smb_fname,
3243 struct readdir_attr_data *attr_data)
3245 NTSTATUS status = NT_STATUS_OK;
3246 struct fruit_config_data *config = NULL;
3249 SMB_VFS_HANDLE_GET_DATA(handle, config,
3250 struct fruit_config_data,
3251 return NT_STATUS_UNSUCCESSFUL);
3254 /* Ensure we return a default value in the creation_date field */
3255 RSIVAL(&attr_data->attr_data.aapl.finder_info, 12, AD_DATE_START);
3258 * Resource fork length
3261 if (config->readdir_attr_rsize) {
3262 uint64_t rfork_size;
3264 rfork_size = readdir_attr_rfork_size(handle, smb_fname);
3265 attr_data->attr_data.aapl.rfork_size = rfork_size;
3272 if (config->readdir_attr_finder_info) {
3273 ok = readdir_attr_meta_finderi(handle, smb_fname, attr_data);
3275 status = NT_STATUS_INTERNAL_ERROR;
3282 static NTSTATUS remove_virtual_nfs_aces(struct security_descriptor *psd)
3287 if (psd->dacl == NULL) {
3288 return NT_STATUS_OK;
3291 for (i = 0; i < psd->dacl->num_aces; i++) {
3292 /* MS NFS style mode/uid/gid */
3293 int cmp = dom_sid_compare_domain(
3294 &global_sid_Unix_NFS,
3295 &psd->dacl->aces[i].trustee);
3297 /* Normal ACE entry. */
3302 * security_descriptor_dacl_del()
3303 * *must* return NT_STATUS_OK as we know
3304 * we have something to remove.
3307 status = security_descriptor_dacl_del(psd,
3308 &psd->dacl->aces[i].trustee);
3309 if (!NT_STATUS_IS_OK(status)) {
3310 DBG_WARNING("failed to remove MS NFS style ACE: %s\n",
3316 * security_descriptor_dacl_del() may delete more
3317 * then one entry subsequent to this one if the
3318 * SID matches, but we only need to ensure that
3319 * we stay looking at the same element in the array.
3323 return NT_STATUS_OK;
3326 /* Search MS NFS style ACE with UNIX mode */
3327 static NTSTATUS check_ms_nfs(vfs_handle_struct *handle,
3329 struct security_descriptor *psd,
3334 struct fruit_config_data *config = NULL;
3338 SMB_VFS_HANDLE_GET_DATA(handle, config,
3339 struct fruit_config_data,
3340 return NT_STATUS_UNSUCCESSFUL);
3342 if (!global_fruit_config.nego_aapl) {
3343 return NT_STATUS_OK;
3345 if (psd->dacl == NULL || !config->unix_info_enabled) {
3346 return NT_STATUS_OK;
3349 for (i = 0; i < psd->dacl->num_aces; i++) {
3350 if (dom_sid_compare_domain(
3351 &global_sid_Unix_NFS_Mode,
3352 &psd->dacl->aces[i].trustee) == 0) {
3353 *pmode = (mode_t)psd->dacl->aces[i].trustee.sub_auths[2];
3354 *pmode &= (S_IRWXU | S_IRWXG | S_IRWXO);
3357 DEBUG(10, ("MS NFS chmod request %s, %04o\n",
3358 fsp_str_dbg(fsp), (unsigned)(*pmode)));
3364 * Remove any incoming virtual ACE entries generated by
3365 * fruit_fget_nt_acl().
3368 return remove_virtual_nfs_aces(psd);
3371 /****************************************************************************
3373 ****************************************************************************/
3375 static int fruit_connect(vfs_handle_struct *handle,
3376 const char *service,
3380 char *list = NULL, *newlist = NULL;
3381 struct fruit_config_data *config;
3383 DEBUG(10, ("fruit_connect\n"));
3385 rc = SMB_VFS_NEXT_CONNECT(handle, service, user);
3390 rc = init_fruit_config(handle);
3395 SMB_VFS_HANDLE_GET_DATA(handle, config,
3396 struct fruit_config_data, return -1);
3398 if (config->veto_appledouble) {
3399 list = lp_veto_files(talloc_tos(), SNUM(handle->conn));
3402 if (strstr(list, "/" ADOUBLE_NAME_PREFIX "*/") == NULL) {
3403 newlist = talloc_asprintf(
3405 "%s/" ADOUBLE_NAME_PREFIX "*/",
3407 lp_do_parameter(SNUM(handle->conn),
3412 lp_do_parameter(SNUM(handle->conn),
3414 "/" ADOUBLE_NAME_PREFIX "*/");
3420 if (config->encoding == FRUIT_ENC_NATIVE) {
3421 lp_do_parameter(SNUM(handle->conn),
3426 if (config->time_machine) {
3427 DBG_NOTICE("Enabling durable handles for Time Machine "
3428 "support on [%s]\n", service);
3429 lp_do_parameter(SNUM(handle->conn), "durable handles", "yes");
3430 lp_do_parameter(SNUM(handle->conn), "kernel oplocks", "no");
3431 lp_do_parameter(SNUM(handle->conn), "kernel share modes", "no");
3432 if (!lp_strict_sync(SNUM(handle->conn))) {
3433 DBG_WARNING("Time Machine without strict sync is not "
3436 lp_do_parameter(SNUM(handle->conn), "posix locking", "no");
3442 static int fruit_fake_fd(void)
3449 * Return a valid fd, but ensure any attempt to use it returns
3450 * an error (EPIPE). Once we get a write on the handle, we open
3453 ret = pipe(pipe_fds);
3463 static int fruit_open_meta_stream(vfs_handle_struct *handle,
3464 struct smb_filename *smb_fname,
3469 struct fruit_config_data *config = NULL;
3470 struct fio *fio = NULL;
3471 int open_flags = flags & ~O_CREAT;
3474 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3476 SMB_VFS_HANDLE_GET_DATA(handle, config,
3477 struct fruit_config_data, return -1);
3479 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3480 fio->type = ADOUBLE_META;
3481 fio->config = config;
3483 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, open_flags, mode);
3488 if (!(flags & O_CREAT)) {
3489 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
3493 fd = fruit_fake_fd();
3495 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
3499 fio->fake_fd = true;
3506 static int fruit_open_meta_netatalk(vfs_handle_struct *handle,
3507 struct smb_filename *smb_fname,
3512 struct fruit_config_data *config = NULL;
3513 struct fio *fio = NULL;
3514 struct adouble *ad = NULL;
3515 bool meta_exists = false;
3518 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3520 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
3527 if (!meta_exists && !(flags & O_CREAT)) {
3532 fd = fruit_fake_fd();
3537 SMB_VFS_HANDLE_GET_DATA(handle, config,
3538 struct fruit_config_data, return -1);
3540 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3541 fio->type = ADOUBLE_META;
3542 fio->config = config;
3543 fio->fake_fd = true;
3550 static int fruit_open_meta(vfs_handle_struct *handle,
3551 struct smb_filename *smb_fname,
3552 files_struct *fsp, int flags, mode_t mode)
3555 struct fruit_config_data *config = NULL;
3557 DBG_DEBUG("path [%s]\n", smb_fname_str_dbg(smb_fname));
3559 SMB_VFS_HANDLE_GET_DATA(handle, config,
3560 struct fruit_config_data, return -1);
3562 switch (config->meta) {
3563 case FRUIT_META_STREAM:
3564 fd = fruit_open_meta_stream(handle, smb_fname,
3568 case FRUIT_META_NETATALK:
3569 fd = fruit_open_meta_netatalk(handle, smb_fname,
3574 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3578 DBG_DEBUG("path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3583 static int fruit_open_rsrc_adouble(vfs_handle_struct *handle,
3584 struct smb_filename *smb_fname,
3590 struct adouble *ad = NULL;
3591 struct smb_filename *smb_fname_base = NULL;
3592 struct fruit_config_data *config = NULL;
3595 SMB_VFS_HANDLE_GET_DATA(handle, config,
3596 struct fruit_config_data, return -1);
3598 if ((!(flags & O_CREAT)) &&
3599 S_ISDIR(fsp->base_fsp->fsp_name->st.st_ex_mode))
3601 /* sorry, but directories don't habe a resource fork */
3606 rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_base);
3611 /* We always need read/write access for the metadata header too */
3612 flags &= ~(O_RDONLY | O_WRONLY);
3615 hostfd = SMB_VFS_NEXT_OPEN(handle, smb_fname_base, fsp,
3622 if (flags & (O_CREAT | O_TRUNC)) {
3623 ad = ad_init(fsp, handle, ADOUBLE_RSRC);
3629 fsp->fh->fd = hostfd;
3631 rc = ad_fset(handle, ad, fsp);
3642 TALLOC_FREE(smb_fname_base);
3644 DEBUG(10, ("fruit_open resource fork: rc=%d, fd=%d\n", rc, hostfd));
3646 int saved_errno = errno;
3649 * BUGBUGBUG -- we would need to call
3650 * fd_close_posix here, but we don't have a
3653 fsp->fh->fd = hostfd;
3657 errno = saved_errno;
3662 static int fruit_open_rsrc_xattr(vfs_handle_struct *handle,
3663 struct smb_filename *smb_fname,
3668 #ifdef HAVE_ATTROPEN
3671 fd = attropen(smb_fname->base_name,
3672 AFPRESOURCE_EA_NETATALK,
3687 static int fruit_open_rsrc(vfs_handle_struct *handle,
3688 struct smb_filename *smb_fname,
3689 files_struct *fsp, int flags, mode_t mode)
3692 struct fruit_config_data *config = NULL;
3693 struct fio *fio = NULL;
3695 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3697 SMB_VFS_HANDLE_GET_DATA(handle, config,
3698 struct fruit_config_data, return -1);
3700 switch (config->rsrc) {
3701 case FRUIT_RSRC_STREAM:
3702 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3705 case FRUIT_RSRC_ADFILE:
3706 fd = fruit_open_rsrc_adouble(handle, smb_fname,
3710 case FRUIT_RSRC_XATTR:
3711 fd = fruit_open_rsrc_xattr(handle, smb_fname,
3716 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3720 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3726 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3727 fio->type = ADOUBLE_RSRC;
3728 fio->config = config;
3733 static int fruit_open(vfs_handle_struct *handle,
3734 struct smb_filename *smb_fname,
3735 files_struct *fsp, int flags, mode_t mode)
3739 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3741 if (!is_ntfs_stream_smb_fname(smb_fname)) {
3742 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3745 if (is_afpinfo_stream(smb_fname)) {
3746 fd = fruit_open_meta(handle, smb_fname, fsp, flags, mode);
3747 } else if (is_afpresource_stream(smb_fname)) {
3748 fd = fruit_open_rsrc(handle, smb_fname, fsp, flags, mode);
3750 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3753 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3758 static int fruit_close_meta(vfs_handle_struct *handle,
3762 struct fruit_config_data *config = NULL;
3764 SMB_VFS_HANDLE_GET_DATA(handle, config,
3765 struct fruit_config_data, return -1);
3767 switch (config->meta) {
3768 case FRUIT_META_STREAM:
3769 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3772 case FRUIT_META_NETATALK:
3773 ret = close(fsp->fh->fd);
3778 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3786 static int fruit_close_rsrc(vfs_handle_struct *handle,
3790 struct fruit_config_data *config = NULL;
3792 SMB_VFS_HANDLE_GET_DATA(handle, config,
3793 struct fruit_config_data, return -1);
3795 switch (config->rsrc) {
3796 case FRUIT_RSRC_STREAM:
3797 case FRUIT_RSRC_ADFILE:
3798 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3801 case FRUIT_RSRC_XATTR:
3802 ret = close(fsp->fh->fd);
3807 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3814 static int fruit_close(vfs_handle_struct *handle,
3822 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(fsp->fsp_name), fd);
3824 if (!is_ntfs_stream_smb_fname(fsp->fsp_name)) {
3825 return SMB_VFS_NEXT_CLOSE(handle, fsp);
3828 if (is_afpinfo_stream(fsp->fsp_name)) {
3829 ret = fruit_close_meta(handle, fsp);
3830 } else if (is_afpresource_stream(fsp->fsp_name)) {
3831 ret = fruit_close_rsrc(handle, fsp);
3833 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3839 static int fruit_rename(struct vfs_handle_struct *handle,
3840 const struct smb_filename *smb_fname_src,
3841 const struct smb_filename *smb_fname_dst)
3844 struct fruit_config_data *config = NULL;
3845 struct smb_filename *src_adp_smb_fname = NULL;
3846 struct smb_filename *dst_adp_smb_fname = NULL;
3848 SMB_VFS_HANDLE_GET_DATA(handle, config,
3849 struct fruit_config_data, return -1);
3851 if (!VALID_STAT(smb_fname_src->st)) {
3852 DBG_ERR("Need valid stat for [%s]\n",
3853 smb_fname_str_dbg(smb_fname_src));
3857 rc = SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
3862 if ((config->rsrc != FRUIT_RSRC_ADFILE) ||
3863 (!S_ISREG(smb_fname_src->st.st_ex_mode)))
3868 rc = adouble_path(talloc_tos(), smb_fname_src, &src_adp_smb_fname);
3873 rc = adouble_path(talloc_tos(), smb_fname_dst, &dst_adp_smb_fname);
3878 DBG_DEBUG("%s -> %s\n",
3879 smb_fname_str_dbg(src_adp_smb_fname),
3880 smb_fname_str_dbg(dst_adp_smb_fname));
3882 rc = SMB_VFS_NEXT_RENAME(handle, src_adp_smb_fname, dst_adp_smb_fname);
3883 if (errno == ENOENT) {
3888 TALLOC_FREE(src_adp_smb_fname);
3889 TALLOC_FREE(dst_adp_smb_fname);
3893 static int fruit_unlink_meta_stream(vfs_handle_struct *handle,
3894 const struct smb_filename *smb_fname)
3896 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3899 static int fruit_unlink_meta_netatalk(vfs_handle_struct *handle,
3900 const struct smb_filename *smb_fname)
3902 return SMB_VFS_REMOVEXATTR(handle->conn,
3904 AFPINFO_EA_NETATALK);
3907 static int fruit_unlink_meta(vfs_handle_struct *handle,
3908 const struct smb_filename *smb_fname)
3910 struct fruit_config_data *config = NULL;
3913 SMB_VFS_HANDLE_GET_DATA(handle, config,
3914 struct fruit_config_data, return -1);
3916 switch (config->meta) {
3917 case FRUIT_META_STREAM:
3918 rc = fruit_unlink_meta_stream(handle, smb_fname);
3921 case FRUIT_META_NETATALK:
3922 rc = fruit_unlink_meta_netatalk(handle, smb_fname);
3926 DBG_ERR("Unsupported meta config [%d]\n", config->meta);
3933 static int fruit_unlink_rsrc_stream(vfs_handle_struct *handle,
3934 const struct smb_filename *smb_fname,
3939 if (!force_unlink) {
3940 struct smb_filename *smb_fname_cp = NULL;
3943 smb_fname_cp = cp_smb_filename(talloc_tos(), smb_fname);
3944 if (smb_fname_cp == NULL) {
3949 * 0 byte resource fork streams are not listed by
3950 * vfs_streaminfo, as a result stream cleanup/deletion of file
3951 * deletion doesn't remove the resourcefork stream.
3954 ret = SMB_VFS_NEXT_STAT(handle, smb_fname_cp);
3956 TALLOC_FREE(smb_fname_cp);
3957 DBG_ERR("stat [%s] failed [%s]\n",
3958 smb_fname_str_dbg(smb_fname_cp), strerror(errno));
3962 size = smb_fname_cp->st.st_ex_size;
3963 TALLOC_FREE(smb_fname_cp);
3966 /* OS X ignores resource fork stream delete requests */
3971 ret = SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3972 if ((ret != 0) && (errno == ENOENT) && force_unlink) {
3979 static int fruit_unlink_rsrc_adouble(vfs_handle_struct *handle,
3980 const struct smb_filename *smb_fname,
3984 struct adouble *ad = NULL;
3985 struct smb_filename *adp_smb_fname = NULL;
3987 if (!force_unlink) {
3988 ad = ad_get(talloc_tos(), handle, smb_fname,
3997 * 0 byte resource fork streams are not listed by
3998 * vfs_streaminfo, as a result stream cleanup/deletion of file
3999 * deletion doesn't remove the resourcefork stream.
4002 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
4003 /* OS X ignores resource fork stream delete requests */
4011 rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
4016 rc = SMB_VFS_NEXT_UNLINK(handle, adp_smb_fname);
4017 TALLOC_FREE(adp_smb_fname);
4018 if ((rc != 0) && (errno == ENOENT) && force_unlink) {
4025 static int fruit_unlink_rsrc_xattr(vfs_handle_struct *handle,
4026 const struct smb_filename *smb_fname,
4030 * OS X ignores resource fork stream delete requests, so nothing to do
4031 * here. Removing the file will remove the xattr anyway, so we don't
4032 * have to take care of removing 0 byte resource forks that could be
4038 static int fruit_unlink_rsrc(vfs_handle_struct *handle,
4039 const struct smb_filename *smb_fname,
4042 struct fruit_config_data *config = NULL;
4045 SMB_VFS_HANDLE_GET_DATA(handle, config,
4046 struct fruit_config_data, return -1);
4048 switch (config->rsrc) {
4049 case FRUIT_RSRC_STREAM:
4050 rc = fruit_unlink_rsrc_stream(handle, smb_fname, force_unlink);
4053 case FRUIT_RSRC_ADFILE:
4054 rc = fruit_unlink_rsrc_adouble(handle, smb_fname, force_unlink);
4057 case FRUIT_RSRC_XATTR:
4058 rc = fruit_unlink_rsrc_xattr(handle, smb_fname, force_unlink);
4062 DBG_ERR("Unsupported rsrc config [%d]\n", config->rsrc);
4069 static int fruit_unlink(vfs_handle_struct *handle,
4070 const struct smb_filename *smb_fname)
4073 struct fruit_config_data *config = NULL;
4074 struct smb_filename *rsrc_smb_fname = NULL;
4076 SMB_VFS_HANDLE_GET_DATA(handle, config,
4077 struct fruit_config_data, return -1);
4079 if (is_afpinfo_stream(smb_fname)) {
4080 return fruit_unlink_meta(handle, smb_fname);
4081 } else if (is_afpresource_stream(smb_fname)) {
4082 return fruit_unlink_rsrc(handle, smb_fname, false);
4083 } if (is_ntfs_stream_smb_fname(smb_fname)) {
4084 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4088 * A request to delete the base file. Because 0 byte resource
4089 * fork streams are not listed by fruit_streaminfo,
4090 * delete_all_streams() can't remove 0 byte resource fork
4091 * streams, so we have to cleanup this here.
4093 rsrc_smb_fname = synthetic_smb_fname(talloc_tos(),
4094 smb_fname->base_name,
4095 AFPRESOURCE_STREAM_NAME,
4098 if (rsrc_smb_fname == NULL) {
4102 rc = fruit_unlink_rsrc(handle, rsrc_smb_fname, true);
4103 if ((rc != 0) && (errno != ENOENT)) {
4104 DBG_ERR("Forced unlink of [%s] failed [%s]\n",
4105 smb_fname_str_dbg(rsrc_smb_fname), strerror(errno));
4106 TALLOC_FREE(rsrc_smb_fname);
4109 TALLOC_FREE(rsrc_smb_fname);
4111 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4114 static int fruit_chmod(vfs_handle_struct *handle,
4115 const struct smb_filename *smb_fname,
4119 struct fruit_config_data *config = NULL;
4120 struct smb_filename *smb_fname_adp = NULL;
4122 rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname, mode);
4127 SMB_VFS_HANDLE_GET_DATA(handle, config,
4128 struct fruit_config_data, return -1);
4130 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4134 if (!VALID_STAT(smb_fname->st)) {
4138 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
4142 rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_adp);
4147 DEBUG(10, ("fruit_chmod: %s\n", smb_fname_adp->base_name));
4149 rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname_adp, mode);
4150 if (errno == ENOENT) {
4154 TALLOC_FREE(smb_fname_adp);
4158 static int fruit_chown(vfs_handle_struct *handle,
4159 const struct smb_filename *smb_fname,
4164 struct fruit_config_data *config = NULL;
4165 struct smb_filename *adp_smb_fname = NULL;
4167 rc = SMB_VFS_NEXT_CHOWN(handle, smb_fname, uid, gid);
4172 SMB_VFS_HANDLE_GET_DATA(handle, config,
4173 struct fruit_config_data, return -1);
4175 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4179 if (!VALID_STAT(smb_fname->st)) {
4183 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
4187 rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
4192 DEBUG(10, ("fruit_chown: %s\n", adp_smb_fname->base_name));
4194 rc = SMB_VFS_NEXT_CHOWN(handle, adp_smb_fname, uid, gid);
4195 if (errno == ENOENT) {
4200 TALLOC_FREE(adp_smb_fname);
4204 static int fruit_rmdir(struct vfs_handle_struct *handle,
4205 const struct smb_filename *smb_fname)
4209 struct fruit_config_data *config;
4211 SMB_VFS_HANDLE_GET_DATA(handle, config,
4212 struct fruit_config_data, return -1);
4214 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4219 * Due to there is no way to change bDeleteVetoFiles variable
4220 * from this module, need to clean up ourselves
4223 dh = SMB_VFS_OPENDIR(handle->conn, smb_fname, NULL, 0);
4228 while ((de = SMB_VFS_READDIR(handle->conn, dh, NULL)) != NULL) {
4230 struct adouble *ad = NULL;
4232 struct smb_filename *ad_smb_fname = NULL;
4235 match = strncmp(de->d_name,
4236 ADOUBLE_NAME_PREFIX,
4237 strlen(ADOUBLE_NAME_PREFIX));
4242 p = talloc_asprintf(talloc_tos(), "%s/%s",
4243 smb_fname->base_name, de->d_name);
4245 DBG_ERR("talloc_asprintf failed\n");
4249 ad_smb_fname = synthetic_smb_fname(talloc_tos(), p,
4253 if (ad_smb_fname == NULL) {
4254 DBG_ERR("synthetic_smb_fname failed\n");
4259 * Check whether it's a valid AppleDouble file, if
4260 * yes, delete it, ignore it otherwise.
4262 ad = ad_get(talloc_tos(), handle, ad_smb_fname, ADOUBLE_RSRC);
4264 TALLOC_FREE(ad_smb_fname);
4270 ret = SMB_VFS_NEXT_UNLINK(handle, ad_smb_fname);
4272 DBG_ERR("Deleting [%s] failed\n",
4273 smb_fname_str_dbg(ad_smb_fname));
4275 TALLOC_FREE(ad_smb_fname);
4280 SMB_VFS_CLOSEDIR(handle->conn, dh);
4282 return SMB_VFS_NEXT_RMDIR(handle, smb_fname);
4285 static ssize_t fruit_pread_meta_stream(vfs_handle_struct *handle,
4286 files_struct *fsp, void *data,
4287 size_t n, off_t offset)
4292 nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4293 if (nread == -1 || nread == n) {
4297 DBG_ERR("Removing [%s] after short read [%zd]\n",
4298 fsp_str_dbg(fsp), nread);
4300 ret = SMB_VFS_NEXT_UNLINK(handle, fsp->fsp_name);
4302 DBG_ERR("Removing [%s] failed\n", fsp_str_dbg(fsp));
4310 static ssize_t fruit_pread_meta_adouble(vfs_handle_struct *handle,
4311 files_struct *fsp, void *data,
4312 size_t n, off_t offset)
4315 struct adouble *ad = NULL;
4316 char afpinfo_buf[AFP_INFO_SIZE];
4320 ai = afpinfo_new(talloc_tos());
4325 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
4331 p = ad_get_entry(ad, ADEID_FINDERI);
4333 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
4338 memcpy(&ai->afpi_FinderInfo[0], p, ADEDLEN_FINDERI);
4340 nread = afpinfo_pack(ai, afpinfo_buf);
4341 if (nread != AFP_INFO_SIZE) {
4346 memcpy(data, afpinfo_buf, n);
4354 static ssize_t fruit_pread_meta(vfs_handle_struct *handle,
4355 files_struct *fsp, void *data,
4356 size_t n, off_t offset)
4358 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4363 * OS X has a off-by-1 error in the offset calculation, so we're
4364 * bug compatible here. It won't hurt, as any relevant real
4365 * world read requests from the AFP_AfpInfo stream will be
4366 * offset=0 n=60. offset is ignored anyway, see below.
4368 if ((offset < 0) || (offset >= AFP_INFO_SIZE + 1)) {
4373 DBG_ERR("Failed to fetch fsp extension");
4377 /* Yes, macOS always reads from offset 0 */
4379 to_return = MIN(n, AFP_INFO_SIZE);
4381 switch (fio->config->meta) {
4382 case FRUIT_META_STREAM:
4383 nread = fruit_pread_meta_stream(handle, fsp, data,
4387 case FRUIT_META_NETATALK:
4388 nread = fruit_pread_meta_adouble(handle, fsp, data,
4393 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
4397 if (nread == -1 && fio->created) {
4399 char afpinfo_buf[AFP_INFO_SIZE];
4401 ai = afpinfo_new(talloc_tos());
4406 nread = afpinfo_pack(ai, afpinfo_buf);
4408 if (nread != AFP_INFO_SIZE) {
4412 memcpy(data, afpinfo_buf, to_return);
4419 static ssize_t fruit_pread_rsrc_stream(vfs_handle_struct *handle,
4420 files_struct *fsp, void *data,
4421 size_t n, off_t offset)
4423 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4426 static ssize_t fruit_pread_rsrc_xattr(vfs_handle_struct *handle,
4427 files_struct *fsp, void *data,
4428 size_t n, off_t offset)
4430 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4433 static ssize_t fruit_pread_rsrc_adouble(vfs_handle_struct *handle,
4434 files_struct *fsp, void *data,
4435 size_t n, off_t offset)
4437 struct adouble *ad = NULL;
4440 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
4445 nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n,
4446 offset + ad_getentryoff(ad, ADEID_RFORK));
4452 static ssize_t fruit_pread_rsrc(vfs_handle_struct *handle,
4453 files_struct *fsp, void *data,
4454 size_t n, off_t offset)
4456 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4464 switch (fio->config->rsrc) {
4465 case FRUIT_RSRC_STREAM:
4466 nread = fruit_pread_rsrc_stream(handle, fsp, data, n, offset);
4469 case FRUIT_RSRC_ADFILE:
4470 nread = fruit_pread_rsrc_adouble(handle, fsp, data, n, offset);
4473 case FRUIT_RSRC_XATTR:
4474 nread = fruit_pread_rsrc_xattr(handle, fsp, data, n, offset);
4478 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
4485 static ssize_t fruit_pread(vfs_handle_struct *handle,
4486 files_struct *fsp, void *data,
4487 size_t n, off_t offset)
4489 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4492 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4493 fsp_str_dbg(fsp), (intmax_t)offset, n);
4496 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4499 if (fio->type == ADOUBLE_META) {
4500 nread = fruit_pread_meta(handle, fsp, data, n, offset);
4502 nread = fruit_pread_rsrc(handle, fsp, data, n, offset);
4505 DBG_DEBUG("Path [%s] nread [%zd]\n", fsp_str_dbg(fsp), nread);
4509 static bool fruit_must_handle_aio_stream(struct fio *fio)
4515 if (fio->type == ADOUBLE_META) {
4519 if ((fio->type == ADOUBLE_RSRC) &&
4520 (fio->config->rsrc == FRUIT_RSRC_ADFILE))
4528 struct fruit_pread_state {
4530 struct vfs_aio_state vfs_aio_state;
4533 static void fruit_pread_done(struct tevent_req *subreq);
4535 static struct tevent_req *fruit_pread_send(
4536 struct vfs_handle_struct *handle,
4537 TALLOC_CTX *mem_ctx,
4538 struct tevent_context *ev,
4539 struct files_struct *fsp,
4541 size_t n, off_t offset)
4543 struct tevent_req *req = NULL;
4544 struct tevent_req *subreq = NULL;
4545 struct fruit_pread_state *state = NULL;
4546 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4548 req = tevent_req_create(mem_ctx, &state,
4549 struct fruit_pread_state);
4554 if (fruit_must_handle_aio_stream(fio)) {
4555 state->nread = SMB_VFS_PREAD(fsp, data, n, offset);
4556 if (state->nread != n) {
4557 if (state->nread != -1) {
4560 tevent_req_error(req, errno);
4561 return tevent_req_post(req, ev);
4563 tevent_req_done(req);
4564 return tevent_req_post(req, ev);
4567 subreq = SMB_VFS_NEXT_PREAD_SEND(state, ev, handle, fsp,
4569 if (tevent_req_nomem(req, subreq)) {
4570 return tevent_req_post(req, ev);
4572 tevent_req_set_callback(subreq, fruit_pread_done, req);
4576 static void fruit_pread_done(struct tevent_req *subreq)
4578 struct tevent_req *req = tevent_req_callback_data(
4579 subreq, struct tevent_req);
4580 struct fruit_pread_state *state = tevent_req_data(
4581 req, struct fruit_pread_state);
4583 state->nread = SMB_VFS_PREAD_RECV(subreq, &state->vfs_aio_state);
4584 TALLOC_FREE(subreq);
4586 if (tevent_req_error(req, state->vfs_aio_state.error)) {
4589 tevent_req_done(req);
4592 static ssize_t fruit_pread_recv(struct tevent_req *req,
4593 struct vfs_aio_state *vfs_aio_state)
4595 struct fruit_pread_state *state = tevent_req_data(
4596 req, struct fruit_pread_state);
4598 if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
4602 *vfs_aio_state = state->vfs_aio_state;
4603 return state->nread;
4606 static ssize_t fruit_pwrite_meta_stream(vfs_handle_struct *handle,
4607 files_struct *fsp, const void *data,
4608 size_t n, off_t offset)
4610 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4616 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4617 fsp_str_dbg(fsp), (intmax_t)offset, n);
4626 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
4628 DBG_ERR("Close [%s] failed: %s\n",
4629 fsp_str_dbg(fsp), strerror(errno));
4634 fd = SMB_VFS_NEXT_OPEN(handle,
4640 DBG_ERR("On-demand create [%s] in write failed: %s\n",
4641 fsp_str_dbg(fsp), strerror(errno));
4645 fio->fake_fd = false;
4648 ai = afpinfo_unpack(talloc_tos(), data);
4653 if (ai_empty_finderinfo(ai)) {
4655 * Writing an all 0 blob to the metadata stream results in the
4656 * stream being removed on a macOS server. This ensures we
4657 * behave the same and it verified by the "delete AFP_AfpInfo by
4658 * writing all 0" test.
4660 ret = SMB_VFS_NEXT_FTRUNCATE(handle, fsp, 0);
4662 DBG_ERR("SMB_VFS_NEXT_FTRUNCATE on [%s] failed\n",
4667 ok = set_delete_on_close(
4670 handle->conn->session_info->security_token,
4671 handle->conn->session_info->unix_token);
4673 DBG_ERR("set_delete_on_close on [%s] failed\n",
4680 nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4681 if (nwritten != n) {
4688 static ssize_t fruit_pwrite_meta_netatalk(vfs_handle_struct *handle,
4689 files_struct *fsp, const void *data,
4690 size_t n, off_t offset)
4692 struct adouble *ad = NULL;
4698 ai = afpinfo_unpack(talloc_tos(), data);
4703 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
4705 ad = ad_init(talloc_tos(), handle, ADOUBLE_META);
4710 p = ad_get_entry(ad, ADEID_FINDERI);
4712 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
4717 memcpy(p, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
4719 ret = ad_fset(handle, ad, fsp);
4721 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
4728 if (!ai_empty_finderinfo(ai)) {
4733 * Writing an all 0 blob to the metadata stream results in the stream
4734 * being removed on a macOS server. This ensures we behave the same and
4735 * it verified by the "delete AFP_AfpInfo by writing all 0" test.
4738 ok = set_delete_on_close(
4741 handle->conn->session_info->security_token,
4742 handle->conn->session_info->unix_token);
4744 DBG_ERR("set_delete_on_close on [%s] failed\n",
4752 static ssize_t fruit_pwrite_meta(vfs_handle_struct *handle,
4753 files_struct *fsp, const void *data,
4754 size_t n, off_t offset)
4756 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4758 uint8_t buf[AFP_INFO_SIZE];
4764 DBG_ERR("Failed to fetch fsp extension");
4773 if (offset != 0 && n < 60) {
4778 cmp = memcmp(data, "AFP", 3);
4784 if (n <= AFP_OFF_FinderInfo) {
4786 * Nothing to do here really, just return
4794 if (to_copy > AFP_INFO_SIZE) {
4795 to_copy = AFP_INFO_SIZE;
4797 memcpy(buf, data, to_copy);
4800 if (to_write != AFP_INFO_SIZE) {
4801 to_write = AFP_INFO_SIZE;
4804 switch (fio->config->meta) {
4805 case FRUIT_META_STREAM:
4806 nwritten = fruit_pwrite_meta_stream(handle,
4813 case FRUIT_META_NETATALK:
4814 nwritten = fruit_pwrite_meta_netatalk(handle,
4822 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
4826 if (nwritten != to_write) {
4831 * Return the requested amount, verified against macOS SMB server
4836 static ssize_t fruit_pwrite_rsrc_stream(vfs_handle_struct *handle,
4837 files_struct *fsp, const void *data,
4838 size_t n, off_t offset)
4840 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4843 static ssize_t fruit_pwrite_rsrc_xattr(vfs_handle_struct *handle,
4844 files_struct *fsp, const void *data,
4845 size_t n, off_t offset)
4847 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4850 static ssize_t fruit_pwrite_rsrc_adouble(vfs_handle_struct *handle,
4851 files_struct *fsp, const void *data,
4852 size_t n, off_t offset)
4854 struct adouble *ad = NULL;
4858 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
4860 DBG_ERR("ad_get [%s] failed\n", fsp_str_dbg(fsp));
4864 nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n,
4865 offset + ad_getentryoff(ad, ADEID_RFORK));
4866 if (nwritten != n) {
4867 DBG_ERR("Short write on [%s] [%zd/%zd]\n",
4868 fsp_str_dbg(fsp), nwritten, n);
4873 if ((n + offset) > ad_getentrylen(ad, ADEID_RFORK)) {
4874 ad_setentrylen(ad, ADEID_RFORK, n + offset);
4875 ret = ad_fset(handle, ad, fsp);
4877 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
4887 static ssize_t fruit_pwrite_rsrc(vfs_handle_struct *handle,
4888 files_struct *fsp, const void *data,
4889 size_t n, off_t offset)
4891 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4895 DBG_ERR("Failed to fetch fsp extension");
4899 switch (fio->config->rsrc) {
4900 case FRUIT_RSRC_STREAM:
4901 nwritten = fruit_pwrite_rsrc_stream(handle, fsp, data, n, offset);
4904 case FRUIT_RSRC_ADFILE:
4905 nwritten = fruit_pwrite_rsrc_adouble(handle, fsp, data, n, offset);
4908 case FRUIT_RSRC_XATTR:
4909 nwritten = fruit_pwrite_rsrc_xattr(handle, fsp, data, n, offset);
4913 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
4920 static ssize_t fruit_pwrite(vfs_handle_struct *handle,
4921 files_struct *fsp, const void *data,
4922 size_t n, off_t offset)
4924 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4927 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4928 fsp_str_dbg(fsp), (intmax_t)offset, n);
4931 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4934 if (fio->type == ADOUBLE_META) {
4935 nwritten = fruit_pwrite_meta(handle, fsp, data, n, offset);
4937 nwritten = fruit_pwrite_rsrc(handle, fsp, data, n, offset);
4940 DBG_DEBUG("Path [%s] nwritten=%zd\n", fsp_str_dbg(fsp), nwritten);
4944 struct fruit_pwrite_state {
4946 struct vfs_aio_state vfs_aio_state;
4949 static void fruit_pwrite_done(struct tevent_req *subreq);
4951 static struct tevent_req *fruit_pwrite_send(
4952 struct vfs_handle_struct *handle,
4953 TALLOC_CTX *mem_ctx,
4954 struct tevent_context *ev,
4955 struct files_struct *fsp,
4957 size_t n, off_t offset)
4959 struct tevent_req *req = NULL;
4960 struct tevent_req *subreq = NULL;
4961 struct fruit_pwrite_state *state = NULL;
4962 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4964 req = tevent_req_create(mem_ctx, &state,
4965 struct fruit_pwrite_state);
4970 if (fruit_must_handle_aio_stream(fio)) {
4971 state->nwritten = SMB_VFS_PWRITE(fsp, data, n, offset);
4972 if (state->nwritten != n) {
4973 if (state->nwritten != -1) {
4976 tevent_req_error(req, errno);
4977 return tevent_req_post(req, ev);
4979 tevent_req_done(req);
4980 return tevent_req_post(req, ev);
4983 subreq = SMB_VFS_NEXT_PWRITE_SEND(state, ev, handle, fsp,
4985 if (tevent_req_nomem(req, subreq)) {
4986 return tevent_req_post(req, ev);
4988 tevent_req_set_callback(subreq, fruit_pwrite_done, req);
4992 static void fruit_pwrite_done(struct tevent_req *subreq)
4994 struct tevent_req *req = tevent_req_callback_data(
4995 subreq, struct tevent_req);
4996 struct fruit_pwrite_state *state = tevent_req_data(
4997 req, struct fruit_pwrite_state);
4999 state->nwritten = SMB_VFS_PWRITE_RECV(subreq, &state->vfs_aio_state);
5000 TALLOC_FREE(subreq);
5002 if (tevent_req_error(req, state->vfs_aio_state.error)) {
5005 tevent_req_done(req);
5008 static ssize_t fruit_pwrite_recv(struct tevent_req *req,
5009 struct vfs_aio_state *vfs_aio_state)
5011 struct fruit_pwrite_state *state = tevent_req_data(
5012 req, struct fruit_pwrite_state);
5014 if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
5018 *vfs_aio_state = state->vfs_aio_state;
5019 return state->nwritten;
5023 * Helper to stat/lstat the base file of an smb_fname.
5025 static int fruit_stat_base(vfs_handle_struct *handle,
5026 struct smb_filename *smb_fname,
5029 char *tmp_stream_name;
5032 tmp_stream_name = smb_fname->stream_name;
5033 smb_fname->stream_name = NULL;
5035 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
5037 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5039 smb_fname->stream_name = tmp_stream_name;
5041 DBG_DEBUG("fruit_stat_base [%s] dev [%ju] ino [%ju]\n",
5042 smb_fname->base_name,
5043 (uintmax_t)smb_fname->st.st_ex_dev,
5044 (uintmax_t)smb_fname->st.st_ex_ino);
5048 static int fruit_stat_meta_stream(vfs_handle_struct *handle,
5049 struct smb_filename *smb_fname,
5055 ret = fruit_stat_base(handle, smb_fname, false);
5060 ino = fruit_inode(&smb_fname->st, smb_fname->stream_name);
5063 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
5065 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5068 smb_fname->st.st_ex_ino = ino;
5073 static int fruit_stat_meta_netatalk(vfs_handle_struct *handle,
5074 struct smb_filename *smb_fname,
5077 struct adouble *ad = NULL;
5079 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5081 DBG_INFO("fruit_stat_meta %s: %s\n",
5082 smb_fname_str_dbg(smb_fname), strerror(errno));
5088 /* Populate the stat struct with info from the base file. */
5089 if (fruit_stat_base(handle, smb_fname, follow_links) == -1) {
5092 smb_fname->st.st_ex_size = AFP_INFO_SIZE;
5093 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5094 smb_fname->stream_name);
5098 static int fruit_stat_meta(vfs_handle_struct *handle,
5099 struct smb_filename *smb_fname,
5102 struct fruit_config_data *config = NULL;
5105 SMB_VFS_HANDLE_GET_DATA(handle, config,
5106 struct fruit_config_data, return -1);
5108 switch (config->meta) {
5109 case FRUIT_META_STREAM:
5110 ret = fruit_stat_meta_stream(handle, smb_fname, follow_links);
5113 case FRUIT_META_NETATALK:
5114 ret = fruit_stat_meta_netatalk(handle, smb_fname, follow_links);
5118 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
5125 static int fruit_stat_rsrc_netatalk(vfs_handle_struct *handle,
5126 struct smb_filename *smb_fname,
5129 struct adouble *ad = NULL;
5132 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
5138 /* Populate the stat struct with info from the base file. */
5139 ret = fruit_stat_base(handle, smb_fname, follow_links);
5145 smb_fname->st.st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
5146 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5147 smb_fname->stream_name);
5152 static int fruit_stat_rsrc_stream(vfs_handle_struct *handle,
5153 struct smb_filename *smb_fname,
5159 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
5161 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5167 static int fruit_stat_rsrc_xattr(vfs_handle_struct *handle,
5168 struct smb_filename *smb_fname,
5171 #ifdef HAVE_ATTROPEN
5175 /* Populate the stat struct with info from the base file. */
5176 ret = fruit_stat_base(handle, smb_fname, follow_links);
5181 fd = attropen(smb_fname->base_name,
5182 AFPRESOURCE_EA_NETATALK,
5188 ret = sys_fstat(fd, &smb_fname->st, false);
5191 DBG_ERR("fstat [%s:%s] failed\n", smb_fname->base_name,
5192 AFPRESOURCE_EA_NETATALK);
5198 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5199 smb_fname->stream_name);
5209 static int fruit_stat_rsrc(vfs_handle_struct *handle,
5210 struct smb_filename *smb_fname,
5213 struct fruit_config_data *config = NULL;
5216 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
5218 SMB_VFS_HANDLE_GET_DATA(handle, config,
5219 struct fruit_config_data, return -1);
5221 switch (config->rsrc) {
5222 case FRUIT_RSRC_STREAM:
5223 ret = fruit_stat_rsrc_stream(handle, smb_fname, follow_links);
5226 case FRUIT_RSRC_XATTR:
5227 ret = fruit_stat_rsrc_xattr(handle, smb_fname, follow_links);
5230 case FRUIT_RSRC_ADFILE:
5231 ret = fruit_stat_rsrc_netatalk(handle, smb_fname, follow_links);
5235 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
5242 static int fruit_stat(vfs_handle_struct *handle,
5243 struct smb_filename *smb_fname)
5247 DEBUG(10, ("fruit_stat called for %s\n",
5248 smb_fname_str_dbg(smb_fname)));
5250 if (!is_ntfs_stream_smb_fname(smb_fname)
5251 || is_ntfs_default_stream_smb_fname(smb_fname)) {
5252 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
5254 update_btime(handle, smb_fname);
5260 * Note if lp_posix_paths() is true, we can never
5261 * get here as is_ntfs_stream_smb_fname() is
5262 * always false. So we never need worry about
5263 * not following links here.
5266 if (is_afpinfo_stream(smb_fname)) {
5267 rc = fruit_stat_meta(handle, smb_fname, true);
5268 } else if (is_afpresource_stream(smb_fname)) {
5269 rc = fruit_stat_rsrc(handle, smb_fname, true);
5271 return SMB_VFS_NEXT_STAT(handle, smb_fname);
5275 update_btime(handle, smb_fname);
5276 smb_fname->st.st_ex_mode &= ~S_IFMT;
5277 smb_fname->st.st_ex_mode |= S_IFREG;
5278 smb_fname->st.st_ex_blocks =
5279 smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
5284 static int fruit_lstat(vfs_handle_struct *handle,
5285 struct smb_filename *smb_fname)
5289 DEBUG(10, ("fruit_lstat called for %s\n",
5290 smb_fname_str_dbg(smb_fname)));
5292 if (!is_ntfs_stream_smb_fname(smb_fname)
5293 || is_ntfs_default_stream_smb_fname(smb_fname)) {
5294 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5296 update_btime(handle, smb_fname);
5301 if (is_afpinfo_stream(smb_fname)) {
5302 rc = fruit_stat_meta(handle, smb_fname, false);
5303 } else if (is_afpresource_stream(smb_fname)) {
5304 rc = fruit_stat_rsrc(handle, smb_fname, false);
5306 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5310 update_btime(handle, smb_fname);
5311 smb_fname->st.st_ex_mode &= ~S_IFMT;
5312 smb_fname->st.st_ex_mode |= S_IFREG;
5313 smb_fname->st.st_ex_blocks =
5314 smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
5319 static int fruit_fstat_meta_stream(vfs_handle_struct *handle,
5321 SMB_STRUCT_STAT *sbuf)
5323 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5324 struct smb_filename smb_fname;
5333 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5338 *sbuf = fsp->base_fsp->fsp_name->st;
5339 sbuf->st_ex_size = AFP_INFO_SIZE;
5340 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5344 smb_fname = (struct smb_filename) {
5345 .base_name = fsp->fsp_name->base_name,
5348 ret = fruit_stat_base(handle, &smb_fname, false);
5352 *sbuf = smb_fname.st;
5354 ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5356 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5361 sbuf->st_ex_ino = ino;
5365 static int fruit_fstat_meta_netatalk(vfs_handle_struct *handle,
5367 SMB_STRUCT_STAT *sbuf)
5371 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5376 *sbuf = fsp->base_fsp->fsp_name->st;
5377 sbuf->st_ex_size = AFP_INFO_SIZE;
5378 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5383 static int fruit_fstat_meta(vfs_handle_struct *handle,
5385 SMB_STRUCT_STAT *sbuf,
5390 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
5392 switch (fio->config->meta) {
5393 case FRUIT_META_STREAM:
5394 ret = fruit_fstat_meta_stream(handle, fsp, sbuf);
5397 case FRUIT_META_NETATALK:
5398 ret = fruit_fstat_meta_netatalk(handle, fsp, sbuf);
5402 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
5406 DBG_DEBUG("Path [%s] ret [%d]\n", fsp_str_dbg(fsp), ret);
5410 static int fruit_fstat_rsrc_xattr(vfs_handle_struct *handle,
5412 SMB_STRUCT_STAT *sbuf)
5414 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5417 static int fruit_fstat_rsrc_stream(vfs_handle_struct *handle,
5419 SMB_STRUCT_STAT *sbuf)
5421 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5424 static int fruit_fstat_rsrc_adouble(vfs_handle_struct *handle,
5426 SMB_STRUCT_STAT *sbuf)
5428 struct adouble *ad = NULL;
5431 /* Populate the stat struct with info from the base file. */
5432 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5437 ad = ad_get(talloc_tos(), handle,
5438 fsp->base_fsp->fsp_name,
5441 DBG_ERR("ad_get [%s] failed [%s]\n",
5442 fsp_str_dbg(fsp), strerror(errno));
5446 *sbuf = fsp->base_fsp->fsp_name->st;
5447 sbuf->st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
5448 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5454 static int fruit_fstat_rsrc(vfs_handle_struct *handle, files_struct *fsp,
5455 SMB_STRUCT_STAT *sbuf, struct fio *fio)
5459 switch (fio->config->rsrc) {
5460 case FRUIT_RSRC_STREAM:
5461 ret = fruit_fstat_rsrc_stream(handle, fsp, sbuf);
5464 case FRUIT_RSRC_ADFILE:
5465 ret = fruit_fstat_rsrc_adouble(handle, fsp, sbuf);
5468 case FRUIT_RSRC_XATTR:
5469 ret = fruit_fstat_rsrc_xattr(handle, fsp, sbuf);
5473 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
5480 static int fruit_fstat(vfs_handle_struct *handle, files_struct *fsp,
5481 SMB_STRUCT_STAT *sbuf)
5483 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5487 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5490 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
5492 if (fio->type == ADOUBLE_META) {
5493 rc = fruit_fstat_meta(handle, fsp, sbuf, fio);
5495 rc = fruit_fstat_rsrc(handle, fsp, sbuf, fio);
5499 sbuf->st_ex_mode &= ~S_IFMT;
5500 sbuf->st_ex_mode |= S_IFREG;
5501 sbuf->st_ex_blocks = sbuf->st_ex_size / STAT_ST_BLOCKSIZE + 1;
5504 DBG_DEBUG("Path [%s] rc [%d] size [%"PRIdMAX"]\n",
5505 fsp_str_dbg(fsp), rc, (intmax_t)sbuf->st_ex_size);
5509 static NTSTATUS delete_invalid_meta_stream(
5510 vfs_handle_struct *handle,
5511 const struct smb_filename *smb_fname,
5512 TALLOC_CTX *mem_ctx,
5513 unsigned int *pnum_streams,
5514 struct stream_struct **pstreams,
5517 struct smb_filename *sname = NULL;
5521 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams, AFPINFO_STREAM);
5523 return NT_STATUS_INTERNAL_ERROR;
5527 return NT_STATUS_OK;
5530 sname = synthetic_smb_fname(talloc_tos(),
5531 smb_fname->base_name,
5532 AFPINFO_STREAM_NAME,
5534 if (sname == NULL) {
5535 return NT_STATUS_NO_MEMORY;
5538 ret = SMB_VFS_NEXT_UNLINK(handle, sname);
5541 DBG_ERR("Removing [%s] failed\n", smb_fname_str_dbg(sname));
5542 return map_nt_error_from_unix(errno);
5545 return NT_STATUS_OK;
5548 static NTSTATUS fruit_streaminfo_meta_stream(
5549 vfs_handle_struct *handle,
5550 struct files_struct *fsp,
5551 const struct smb_filename *smb_fname,
5552 TALLOC_CTX *mem_ctx,
5553 unsigned int *pnum_streams,
5554 struct stream_struct **pstreams)
5556 struct stream_struct *stream = *pstreams;
5557 unsigned int num_streams = *pnum_streams;
5560 for (i = 0; i < num_streams; i++) {
5561 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
5566 if (i == num_streams) {
5567 return NT_STATUS_OK;
5570 if (stream[i].size != AFP_INFO_SIZE) {
5571 DBG_ERR("Removing invalid AFPINFO_STREAM size [%jd] from [%s]\n",
5572 (intmax_t)stream[i].size, smb_fname_str_dbg(smb_fname));
5574 return delete_invalid_meta_stream(handle,
5583 return NT_STATUS_OK;
5586 static NTSTATUS fruit_streaminfo_meta_netatalk(
5587 vfs_handle_struct *handle,
5588 struct files_struct *fsp,
5589 const struct smb_filename *smb_fname,
5590 TALLOC_CTX *mem_ctx,
5591 unsigned int *pnum_streams,
5592 struct stream_struct **pstreams)
5594 struct stream_struct *stream = *pstreams;
5595 unsigned int num_streams = *pnum_streams;
5596 struct adouble *ad = NULL;
5601 /* Remove the Netatalk xattr from the list */
5602 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5603 ":" NETATALK_META_XATTR ":$DATA");
5605 return NT_STATUS_NO_MEMORY;
5609 * Check if there's a AFPINFO_STREAM from the VFS streams
5610 * backend and if yes, remove it from the list
5612 for (i = 0; i < num_streams; i++) {
5613 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
5618 if (i < num_streams) {
5619 DBG_WARNING("Unexpected AFPINFO_STREAM on [%s]\n",
5620 smb_fname_str_dbg(smb_fname));
5622 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5625 return NT_STATUS_INTERNAL_ERROR;
5629 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5631 return NT_STATUS_OK;
5634 is_fi_empty = ad_empty_finderinfo(ad);
5638 return NT_STATUS_OK;
5641 ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
5642 AFPINFO_STREAM_NAME, AFP_INFO_SIZE,
5643 smb_roundup(handle->conn, AFP_INFO_SIZE));
5645 return NT_STATUS_NO_MEMORY;
5648 return NT_STATUS_OK;
5651 static NTSTATUS fruit_streaminfo_meta(vfs_handle_struct *handle,
5652 struct files_struct *fsp,
5653 const struct smb_filename *smb_fname,
5654 TALLOC_CTX *mem_ctx,
5655 unsigned int *pnum_streams,
5656 struct stream_struct **pstreams)
5658 struct fruit_config_data *config = NULL;
5661 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5662 return NT_STATUS_INTERNAL_ERROR);
5664 switch (config->meta) {
5665 case FRUIT_META_NETATALK:
5666 status = fruit_streaminfo_meta_netatalk(handle, fsp, smb_fname,
5667 mem_ctx, pnum_streams,
5671 case FRUIT_META_STREAM:
5672 status = fruit_streaminfo_meta_stream(handle, fsp, smb_fname,
5673 mem_ctx, pnum_streams,
5678 return NT_STATUS_INTERNAL_ERROR;
5684 static NTSTATUS fruit_streaminfo_rsrc_stream(
5685 vfs_handle_struct *handle,
5686 struct files_struct *fsp,
5687 const struct smb_filename *smb_fname,
5688 TALLOC_CTX *mem_ctx,
5689 unsigned int *pnum_streams,
5690 struct stream_struct **pstreams)
5694 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
5696 DBG_ERR("Filtering resource stream failed\n");
5697 return NT_STATUS_INTERNAL_ERROR;
5699 return NT_STATUS_OK;
5702 static NTSTATUS fruit_streaminfo_rsrc_xattr(
5703 vfs_handle_struct *handle,
5704 struct files_struct *fsp,
5705 const struct smb_filename *smb_fname,
5706 TALLOC_CTX *mem_ctx,
5707 unsigned int *pnum_streams,
5708 struct stream_struct **pstreams)
5712 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
5714 DBG_ERR("Filtering resource stream failed\n");
5715 return NT_STATUS_INTERNAL_ERROR;
5717 return NT_STATUS_OK;
5720 static NTSTATUS fruit_streaminfo_rsrc_adouble(
5721 vfs_handle_struct *handle,
5722 struct files_struct *fsp,
5723 const struct smb_filename *smb_fname,
5724 TALLOC_CTX *mem_ctx,
5725 unsigned int *pnum_streams,
5726 struct stream_struct **pstreams)
5728 struct stream_struct *stream = *pstreams;
5729 unsigned int num_streams = *pnum_streams;
5730 struct adouble *ad = NULL;
5736 * Check if there's a AFPRESOURCE_STREAM from the VFS streams backend
5737 * and if yes, remove it from the list
5739 for (i = 0; i < num_streams; i++) {
5740 if (strequal_m(stream[i].name, AFPRESOURCE_STREAM)) {
5745 if (i < num_streams) {
5746 DBG_WARNING("Unexpected AFPRESOURCE_STREAM on [%s]\n",
5747 smb_fname_str_dbg(smb_fname));
5749 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5750 AFPRESOURCE_STREAM);
5752 return NT_STATUS_INTERNAL_ERROR;
5756 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
5758 return NT_STATUS_OK;
5761 rlen = ad_getentrylen(ad, ADEID_RFORK);
5765 return NT_STATUS_OK;
5768 ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
5769 AFPRESOURCE_STREAM_NAME, rlen,
5770 smb_roundup(handle->conn, rlen));
5772 return NT_STATUS_NO_MEMORY;
5775 return NT_STATUS_OK;
5778 static NTSTATUS fruit_streaminfo_rsrc(vfs_handle_struct *handle,
5779 struct files_struct *fsp,
5780 const struct smb_filename *smb_fname,
5781 TALLOC_CTX *mem_ctx,
5782 unsigned int *pnum_streams,
5783 struct stream_struct **pstreams)
5785 struct fruit_config_data *config = NULL;
5788 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5789 return NT_STATUS_INTERNAL_ERROR);
5791 switch (config->rsrc) {
5792 case FRUIT_RSRC_STREAM:
5793 status = fruit_streaminfo_rsrc_stream(handle, fsp, smb_fname,
5794 mem_ctx, pnum_streams,
5798 case FRUIT_RSRC_XATTR:
5799 status = fruit_streaminfo_rsrc_xattr(handle, fsp, smb_fname,
5800 mem_ctx, pnum_streams,
5804 case FRUIT_RSRC_ADFILE:
5805 status = fruit_streaminfo_rsrc_adouble(handle, fsp, smb_fname,
5806 mem_ctx, pnum_streams,
5811 return NT_STATUS_INTERNAL_ERROR;
5817 static void fruit_filter_empty_streams(unsigned int *pnum_streams,
5818 struct stream_struct **pstreams)
5820 unsigned num_streams = *pnum_streams;
5821 struct stream_struct *streams = *pstreams;
5824 if (!global_fruit_config.nego_aapl) {
5828 while (i < num_streams) {
5829 struct smb_filename smb_fname = (struct smb_filename) {
5830 .stream_name = streams[i].name,
5833 if (is_ntfs_default_stream_smb_fname(&smb_fname)
5834 || streams[i].size > 0)
5840 streams[i] = streams[num_streams - 1];
5844 *pnum_streams = num_streams;
5847 static NTSTATUS fruit_streaminfo(vfs_handle_struct *handle,
5848 struct files_struct *fsp,
5849 const struct smb_filename *smb_fname,
5850 TALLOC_CTX *mem_ctx,
5851 unsigned int *pnum_streams,
5852 struct stream_struct **pstreams)
5854 struct fruit_config_data *config = NULL;
5857 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5858 return NT_STATUS_UNSUCCESSFUL);
5860 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
5862 status = SMB_VFS_NEXT_STREAMINFO(handle, fsp, smb_fname, mem_ctx,
5863 pnum_streams, pstreams);
5864 if (!NT_STATUS_IS_OK(status)) {
5868 fruit_filter_empty_streams(pnum_streams, pstreams);
5870 status = fruit_streaminfo_meta(handle, fsp, smb_fname,
5871 mem_ctx, pnum_streams, pstreams);
5872 if (!NT_STATUS_IS_OK(status)) {
5876 status = fruit_streaminfo_rsrc(handle, fsp, smb_fname,
5877 mem_ctx, pnum_streams, pstreams);
5878 if (!NT_STATUS_IS_OK(status)) {
5882 return NT_STATUS_OK;
5885 static int fruit_ntimes(vfs_handle_struct *handle,
5886 const struct smb_filename *smb_fname,
5887 struct smb_file_time *ft)
5890 struct adouble *ad = NULL;
5891 struct fruit_config_data *config = NULL;
5893 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5896 if ((config->meta != FRUIT_META_NETATALK) ||
5897 null_timespec(ft->create_time))
5899 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
5902 DEBUG(10,("set btime for %s to %s\n", smb_fname_str_dbg(smb_fname),
5903 time_to_asc(convert_timespec_to_time_t(ft->create_time))));
5905 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5910 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX,
5911 convert_time_t_to_uint32_t(ft->create_time.tv_sec));
5913 rc = ad_set(handle, ad, smb_fname);
5919 DEBUG(1, ("fruit_ntimes: %s\n", smb_fname_str_dbg(smb_fname)));
5922 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
5925 static int fruit_fallocate(struct vfs_handle_struct *handle,
5926 struct files_struct *fsp,
5931 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5934 return SMB_VFS_NEXT_FALLOCATE(handle, fsp, mode, offset, len);
5937 /* Let the pwrite code path handle it. */
5942 static int fruit_ftruncate_rsrc_xattr(struct vfs_handle_struct *handle,
5943 struct files_struct *fsp,
5946 #ifdef HAVE_ATTROPEN
5947 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
5952 static int fruit_ftruncate_rsrc_adouble(struct vfs_handle_struct *handle,
5953 struct files_struct *fsp,
5957 struct adouble *ad = NULL;
5960 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
5962 DBG_DEBUG("ad_get [%s] failed [%s]\n",
5963 fsp_str_dbg(fsp), strerror(errno));
5967 ad_off = ad_getentryoff(ad, ADEID_RFORK);
5969 rc = ftruncate(fsp->fh->fd, offset + ad_off);
5975 ad_setentrylen(ad, ADEID_RFORK, offset);
5977 rc = ad_fset(handle, ad, fsp);
5979 DBG_ERR("ad_fset [%s] failed [%s]\n",
5980 fsp_str_dbg(fsp), strerror(errno));
5989 static int fruit_ftruncate_rsrc_stream(struct vfs_handle_struct *handle,
5990 struct files_struct *fsp,
5993 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
5996 static int fruit_ftruncate_rsrc(struct vfs_handle_struct *handle,
5997 struct files_struct *fsp,
6000 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
6004 DBG_ERR("Failed to fetch fsp extension");
6008 switch (fio->config->rsrc) {
6009 case FRUIT_RSRC_XATTR:
6010 ret = fruit_ftruncate_rsrc_xattr(handle, fsp, offset);
6013 case FRUIT_RSRC_ADFILE:
6014 ret = fruit_ftruncate_rsrc_adouble(handle, fsp, offset);
6017 case FRUIT_RSRC_STREAM:
6018 ret = fruit_ftruncate_rsrc_stream(handle, fsp, offset);
6022 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
6030 static int fruit_ftruncate_meta(struct vfs_handle_struct *handle,
6031 struct files_struct *fsp,
6035 DBG_WARNING("ftruncate %s to %jd",
6036 fsp_str_dbg(fsp), (intmax_t)offset);
6037 /* OS X returns NT_STATUS_ALLOTTED_SPACE_EXCEEDED */
6042 /* OS X returns success but does nothing */
6043 DBG_INFO("ignoring ftruncate %s to %jd\n",
6044 fsp_str_dbg(fsp), (intmax_t)offset);
6048 static int fruit_ftruncate(struct vfs_handle_struct *handle,
6049 struct files_struct *fsp,
6052 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
6055 DBG_DEBUG("Path [%s] offset [%"PRIdMAX"]\n", fsp_str_dbg(fsp),
6059 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
6062 if (fio->type == ADOUBLE_META) {
6063 ret = fruit_ftruncate_meta(handle, fsp, offset);
6065 ret = fruit_ftruncate_rsrc(handle, fsp, offset);
6068 DBG_DEBUG("Path [%s] result [%d]\n", fsp_str_dbg(fsp), ret);
6072 static NTSTATUS fruit_create_file(vfs_handle_struct *handle,
6073 struct smb_request *req,
6074 uint16_t root_dir_fid,
6075 struct smb_filename *smb_fname,
6076 uint32_t access_mask,
6077 uint32_t share_access,
6078 uint32_t create_disposition,
6079 uint32_t create_options,
6080 uint32_t file_attributes,
6081 uint32_t oplock_request,
6082 struct smb2_lease *lease,
6083 uint64_t allocation_size,
6084 uint32_t private_flags,
6085 struct security_descriptor *sd,
6086 struct ea_list *ea_list,
6087 files_struct **result,
6089 const struct smb2_create_blobs *in_context_blobs,
6090 struct smb2_create_blobs *out_context_blobs)
6093 struct fruit_config_data *config = NULL;
6094 files_struct *fsp = NULL;
6095 struct fio *fio = NULL;
6096 bool internal_open = (oplock_request & INTERNAL_OPEN_ONLY);
6099 status = check_aapl(handle, req, in_context_blobs, out_context_blobs);
6100 if (!NT_STATUS_IS_OK(status)) {
6104 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
6105 return NT_STATUS_UNSUCCESSFUL);
6107 if (is_apple_stream(smb_fname) && !internal_open) {
6108 ret = ad_convert(handle, smb_fname);
6110 DBG_ERR("ad_convert() failed\n");
6111 return NT_STATUS_UNSUCCESSFUL;
6115 status = SMB_VFS_NEXT_CREATE_FILE(
6116 handle, req, root_dir_fid, smb_fname,
6117 access_mask, share_access,
6118 create_disposition, create_options,
6119 file_attributes, oplock_request,
6121 allocation_size, private_flags,
6122 sd, ea_list, result,
6123 pinfo, in_context_blobs, out_context_blobs);
6124 if (!NT_STATUS_IS_OK(status)) {
6130 if (global_fruit_config.nego_aapl) {
6131 if (config->posix_rename && fsp->is_directory) {
6133 * Enable POSIX directory rename behaviour
6135 fsp->posix_flags |= FSP_POSIX_FLAGS_RENAME;
6140 * If this is a plain open for existing files, opening an 0
6141 * byte size resource fork MUST fail with
6142 * NT_STATUS_OBJECT_NAME_NOT_FOUND.
6144 * Cf the vfs_fruit torture tests in test_rfork_create().
6146 if (global_fruit_config.nego_aapl &&
6147 create_disposition == FILE_OPEN &&
6148 smb_fname->st.st_ex_size == 0 &&
6149 is_ntfs_stream_smb_fname(smb_fname) &&
6150 !(is_ntfs_default_stream_smb_fname(smb_fname)))
6152 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
6156 fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
6157 if (fio != NULL && pinfo != NULL && *pinfo == FILE_WAS_CREATED) {
6158 fio->created = true;
6161 if (is_ntfs_stream_smb_fname(smb_fname)
6162 || fsp->is_directory) {
6166 if (config->locking == FRUIT_LOCKING_NETATALK) {
6167 status = fruit_check_access(
6171 if (!NT_STATUS_IS_OK(status)) {
6179 DEBUG(10, ("fruit_create_file: %s\n", nt_errstr(status)));
6182 close_file(req, fsp, ERROR_CLOSE);
6183 *result = fsp = NULL;
6189 static NTSTATUS fruit_readdir_attr(struct vfs_handle_struct *handle,
6190 const struct smb_filename *fname,
6191 TALLOC_CTX *mem_ctx,
6192 struct readdir_attr_data **pattr_data)
6194 struct fruit_config_data *config = NULL;
6195 struct readdir_attr_data *attr_data;
6199 SMB_VFS_HANDLE_GET_DATA(handle, config,
6200 struct fruit_config_data,
6201 return NT_STATUS_UNSUCCESSFUL);
6203 if (!global_fruit_config.nego_aapl) {
6204 return SMB_VFS_NEXT_READDIR_ATTR(handle, fname, mem_ctx, pattr_data);
6207 DEBUG(10, ("fruit_readdir_attr %s\n", fname->base_name));
6209 ret = ad_convert(handle, fname);
6211 DBG_ERR("ad_convert() failed\n");
6212 return NT_STATUS_UNSUCCESSFUL;
6215 *pattr_data = talloc_zero(mem_ctx, struct readdir_attr_data);
6216 if (*pattr_data == NULL) {
6217 return NT_STATUS_UNSUCCESSFUL;
6219 attr_data = *pattr_data;
6220 attr_data->type = RDATTR_AAPL;
6223 * Mac metadata: compressed FinderInfo, resource fork length
6226 status = readdir_attr_macmeta(handle, fname, attr_data);
6227 if (!NT_STATUS_IS_OK(status)) {
6229 * Error handling is tricky: if we return failure from
6230 * this function, the corresponding directory entry
6231 * will to be passed to the client, so we really just
6232 * want to error out on fatal errors.
6234 if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
6242 if (config->unix_info_enabled) {
6243 attr_data->attr_data.aapl.unix_mode = fname->st.st_ex_mode;
6249 if (!config->readdir_attr_max_access) {
6250 attr_data->attr_data.aapl.max_access = FILE_GENERIC_ALL;
6252 status = smbd_calculate_access_mask(
6256 SEC_FLAG_MAXIMUM_ALLOWED,
6257 &attr_data->attr_data.aapl.max_access);
6258 if (!NT_STATUS_IS_OK(status)) {
6263 return NT_STATUS_OK;
6266 DEBUG(1, ("fruit_readdir_attr %s, error: %s\n",
6267 fname->base_name, nt_errstr(status)));
6268 TALLOC_FREE(*pattr_data);
6272 static NTSTATUS fruit_fget_nt_acl(vfs_handle_struct *handle,
6274 uint32_t security_info,
6275 TALLOC_CTX *mem_ctx,
6276 struct security_descriptor **ppdesc)
6279 struct security_ace ace;
6281 struct fruit_config_data *config;
6283 SMB_VFS_HANDLE_GET_DATA(handle, config,
6284 struct fruit_config_data,
6285 return NT_STATUS_UNSUCCESSFUL);
6287 status = SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
6289 if (!NT_STATUS_IS_OK(status)) {
6294 * Add MS NFS style ACEs with uid, gid and mode
6296 if (!global_fruit_config.nego_aapl) {
6297 return NT_STATUS_OK;
6299 if (!config->unix_info_enabled) {
6300 return NT_STATUS_OK;
6303 /* First remove any existing ACE's with NFS style mode/uid/gid SIDs. */
6304 status = remove_virtual_nfs_aces(*ppdesc);
6305 if (!NT_STATUS_IS_OK(status)) {
6306 DBG_WARNING("failed to remove MS NFS style ACEs\n");
6310 /* MS NFS style mode */
6311 sid_compose(&sid, &global_sid_Unix_NFS_Mode, fsp->fsp_name->st.st_ex_mode);
6312 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6313 status = security_descriptor_dacl_add(*ppdesc, &ace);
6314 if (!NT_STATUS_IS_OK(status)) {
6315 DEBUG(1,("failed to add MS NFS style ACE\n"));
6319 /* MS NFS style uid */
6320 sid_compose(&sid, &global_sid_Unix_NFS_Users, fsp->fsp_name->st.st_ex_uid);
6321 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6322 status = security_descriptor_dacl_add(*ppdesc, &ace);
6323 if (!NT_STATUS_IS_OK(status)) {
6324 DEBUG(1,("failed to add MS NFS style ACE\n"));
6328 /* MS NFS style gid */
6329 sid_compose(&sid, &global_sid_Unix_NFS_Groups, fsp->fsp_name->st.st_ex_gid);
6330 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6331 status = security_descriptor_dacl_add(*ppdesc, &ace);
6332 if (!NT_STATUS_IS_OK(status)) {
6333 DEBUG(1,("failed to add MS NFS style ACE\n"));
6337 return NT_STATUS_OK;
6340 static NTSTATUS fruit_fset_nt_acl(vfs_handle_struct *handle,
6342 uint32_t security_info_sent,
6343 const struct security_descriptor *orig_psd)
6347 mode_t ms_nfs_mode = 0;
6349 struct security_descriptor *psd = NULL;
6350 uint32_t orig_num_aces = 0;
6352 if (orig_psd->dacl != NULL) {
6353 orig_num_aces = orig_psd->dacl->num_aces;
6356 psd = security_descriptor_copy(talloc_tos(), orig_psd);
6358 return NT_STATUS_NO_MEMORY;
6361 DBG_DEBUG("fruit_fset_nt_acl: %s\n", fsp_str_dbg(fsp));
6363 status = check_ms_nfs(handle, fsp, psd, &ms_nfs_mode, &do_chmod);
6364 if (!NT_STATUS_IS_OK(status)) {
6365 DEBUG(1, ("fruit_fset_nt_acl: check_ms_nfs failed%s\n", fsp_str_dbg(fsp)));
6371 * If only ms_nfs ACE entries were sent, ensure we set the DACL
6372 * sent/present flags correctly now we've removed them.
6375 if (orig_num_aces != 0) {
6377 * Are there any ACE's left ?
6379 if (psd->dacl->num_aces == 0) {
6380 /* No - clear the DACL sent/present flags. */
6381 security_info_sent &= ~SECINFO_DACL;
6382 psd->type &= ~SEC_DESC_DACL_PRESENT;
6386 status = SMB_VFS_NEXT_FSET_NT_ACL(handle, fsp, security_info_sent, psd);
6387 if (!NT_STATUS_IS_OK(status)) {
6388 DEBUG(1, ("fruit_fset_nt_acl: SMB_VFS_NEXT_FSET_NT_ACL failed%s\n", fsp_str_dbg(fsp)));
6394 if (fsp->fh->fd != -1) {
6395 result = SMB_VFS_FCHMOD(fsp, ms_nfs_mode);
6397 result = SMB_VFS_CHMOD(fsp->conn,
6403 DEBUG(1, ("chmod: %s, result: %d, %04o error %s\n", fsp_str_dbg(fsp),
6404 result, (unsigned)ms_nfs_mode,
6406 status = map_nt_error_from_unix(errno);
6413 return NT_STATUS_OK;
6416 static struct vfs_offload_ctx *fruit_offload_ctx;
6418 struct fruit_offload_read_state {
6419 struct vfs_handle_struct *handle;
6420 struct tevent_context *ev;
6426 static void fruit_offload_read_done(struct tevent_req *subreq);
6428 static struct tevent_req *fruit_offload_read_send(
6429 TALLOC_CTX *mem_ctx,
6430 struct tevent_context *ev,
6431 struct vfs_handle_struct *handle,
6438 struct tevent_req *req = NULL;
6439 struct tevent_req *subreq = NULL;
6440 struct fruit_offload_read_state *state = NULL;
6442 req = tevent_req_create(mem_ctx, &state,
6443 struct fruit_offload_read_state);
6447 *state = (struct fruit_offload_read_state) {
6454 subreq = SMB_VFS_NEXT_OFFLOAD_READ_SEND(mem_ctx, ev, handle, fsp,
6455 fsctl, ttl, offset, to_copy);
6456 if (tevent_req_nomem(subreq, req)) {
6457 return tevent_req_post(req, ev);
6459 tevent_req_set_callback(subreq, fruit_offload_read_done, req);
6463 static void fruit_offload_read_done(struct tevent_req *subreq)
6465 struct tevent_req *req = tevent_req_callback_data(
6466 subreq, struct tevent_req);
6467 struct fruit_offload_read_state *state = tevent_req_data(
6468 req, struct fruit_offload_read_state);
6471 status = SMB_VFS_NEXT_OFFLOAD_READ_RECV(subreq,
6475 TALLOC_FREE(subreq);
6476 if (tevent_req_nterror(req, status)) {
6480 if (state->fsctl != FSCTL_SRV_REQUEST_RESUME_KEY) {
6481 tevent_req_done(req);
6485 status = vfs_offload_token_ctx_init(state->fsp->conn->sconn->client,
6486 &fruit_offload_ctx);
6487 if (tevent_req_nterror(req, status)) {
6491 status = vfs_offload_token_db_store_fsp(fruit_offload_ctx,
6494 if (tevent_req_nterror(req, status)) {
6498 tevent_req_done(req);
6502 static NTSTATUS fruit_offload_read_recv(struct tevent_req *req,
6503 struct vfs_handle_struct *handle,
6504 TALLOC_CTX *mem_ctx,
6507 struct fruit_offload_read_state *state = tevent_req_data(
6508 req, struct fruit_offload_read_state);
6511 if (tevent_req_is_nterror(req, &status)) {
6512 tevent_req_received(req);
6516 token->length = state->token.length;
6517 token->data = talloc_move(mem_ctx, &state->token.data);
6519 tevent_req_received(req);
6520 return NT_STATUS_OK;
6523 struct fruit_offload_write_state {
6524 struct vfs_handle_struct *handle;
6526 struct files_struct *src_fsp;
6527 struct files_struct *dst_fsp;
6531 static void fruit_offload_write_done(struct tevent_req *subreq);
6532 static struct tevent_req *fruit_offload_write_send(struct vfs_handle_struct *handle,
6533 TALLOC_CTX *mem_ctx,
6534 struct tevent_context *ev,
6537 off_t transfer_offset,
6538 struct files_struct *dest_fsp,
6542 struct tevent_req *req, *subreq;
6543 struct fruit_offload_write_state *state;
6545 struct fruit_config_data *config;
6546 off_t src_off = transfer_offset;
6547 files_struct *src_fsp = NULL;
6548 off_t to_copy = num;
6549 bool copyfile_enabled = false;
6551 DEBUG(10,("soff: %ju, doff: %ju, len: %ju\n",
6552 (uintmax_t)src_off, (uintmax_t)dest_off, (uintmax_t)num));
6554 SMB_VFS_HANDLE_GET_DATA(handle, config,
6555 struct fruit_config_data,
6558 req = tevent_req_create(mem_ctx, &state,
6559 struct fruit_offload_write_state);
6563 state->handle = handle;
6564 state->dst_fsp = dest_fsp;
6567 case FSCTL_SRV_COPYCHUNK:
6568 case FSCTL_SRV_COPYCHUNK_WRITE:
6569 copyfile_enabled = config->copyfile_enabled;
6576 * Check if this a OS X copyfile style copychunk request with
6577 * a requested chunk count of 0 that was translated to a
6578 * offload_write_send VFS call overloading the parameters src_off
6579 * = dest_off = num = 0.
6581 if (copyfile_enabled && num == 0 && src_off == 0 && dest_off == 0) {
6582 status = vfs_offload_token_db_fetch_fsp(
6583 fruit_offload_ctx, token, &src_fsp);
6584 if (tevent_req_nterror(req, status)) {
6585 return tevent_req_post(req, ev);
6587 state->src_fsp = src_fsp;
6589 status = vfs_stat_fsp(src_fsp);
6590 if (tevent_req_nterror(req, status)) {
6591 return tevent_req_post(req, ev);
6594 to_copy = src_fsp->fsp_name->st.st_ex_size;
6595 state->is_copyfile = true;
6598 subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle,
6607 if (tevent_req_nomem(subreq, req)) {
6608 return tevent_req_post(req, ev);
6611 tevent_req_set_callback(subreq, fruit_offload_write_done, req);
6615 static void fruit_offload_write_done(struct tevent_req *subreq)
6617 struct tevent_req *req = tevent_req_callback_data(
6618 subreq, struct tevent_req);
6619 struct fruit_offload_write_state *state = tevent_req_data(
6620 req, struct fruit_offload_write_state);
6622 unsigned int num_streams = 0;
6623 struct stream_struct *streams = NULL;
6625 struct smb_filename *src_fname_tmp = NULL;
6626 struct smb_filename *dst_fname_tmp = NULL;
6628 status = SMB_VFS_NEXT_OFFLOAD_WRITE_RECV(state->handle,
6631 TALLOC_FREE(subreq);
6632 if (tevent_req_nterror(req, status)) {
6636 if (!state->is_copyfile) {
6637 tevent_req_done(req);
6642 * Now copy all remaining streams. We know the share supports
6643 * streams, because we're in vfs_fruit. We don't do this async
6644 * because streams are few and small.
6646 status = vfs_streaminfo(state->handle->conn, state->src_fsp,
6647 state->src_fsp->fsp_name,
6648 req, &num_streams, &streams);
6649 if (tevent_req_nterror(req, status)) {
6653 if (num_streams == 1) {
6654 /* There is always one stream, ::$DATA. */
6655 tevent_req_done(req);
6659 for (i = 0; i < num_streams; i++) {
6660 DEBUG(10, ("%s: stream: '%s'/%zu\n",
6661 __func__, streams[i].name, (size_t)streams[i].size));
6663 src_fname_tmp = synthetic_smb_fname(
6665 state->src_fsp->fsp_name->base_name,
6668 state->src_fsp->fsp_name->flags);
6669 if (tevent_req_nomem(src_fname_tmp, req)) {
6673 if (is_ntfs_default_stream_smb_fname(src_fname_tmp)) {
6674 TALLOC_FREE(src_fname_tmp);
6678 dst_fname_tmp = synthetic_smb_fname(
6680 state->dst_fsp->fsp_name->base_name,
6683 state->dst_fsp->fsp_name->flags);
6684 if (tevent_req_nomem(dst_fname_tmp, req)) {
6685 TALLOC_FREE(src_fname_tmp);
6689 status = copy_file(req,
6690 state->handle->conn,
6693 OPENX_FILE_CREATE_IF_NOT_EXIST,
6695 if (!NT_STATUS_IS_OK(status)) {
6696 DEBUG(1, ("%s: copy %s to %s failed: %s\n", __func__,
6697 smb_fname_str_dbg(src_fname_tmp),
6698 smb_fname_str_dbg(dst_fname_tmp),
6699 nt_errstr(status)));
6700 TALLOC_FREE(src_fname_tmp);
6701 TALLOC_FREE(dst_fname_tmp);
6702 tevent_req_nterror(req, status);
6706 TALLOC_FREE(src_fname_tmp);
6707 TALLOC_FREE(dst_fname_tmp);
6710 TALLOC_FREE(streams);
6711 TALLOC_FREE(src_fname_tmp);
6712 TALLOC_FREE(dst_fname_tmp);
6713 tevent_req_done(req);
6716 static NTSTATUS fruit_offload_write_recv(struct vfs_handle_struct *handle,
6717 struct tevent_req *req,
6720 struct fruit_offload_write_state *state = tevent_req_data(
6721 req, struct fruit_offload_write_state);
6724 if (tevent_req_is_nterror(req, &status)) {
6725 DEBUG(1, ("server side copy chunk failed: %s\n",
6726 nt_errstr(status)));
6728 tevent_req_received(req);
6732 *copied = state->copied;
6733 tevent_req_received(req);
6735 return NT_STATUS_OK;
6738 static char *fruit_get_bandsize_line(char **lines, int numlines)
6741 static bool re_initialized = false;
6745 if (!re_initialized) {
6746 ret = regcomp(&re, "^[[:blank:]]*<key>band-size</key>$", 0);
6750 re_initialized = true;
6753 for (i = 0; i < numlines; i++) {
6754 regmatch_t matches[1];
6756 ret = regexec(&re, lines[i], 1, matches, 0);
6759 * Check if the match was on the last line, sa we want
6760 * the subsequent line.
6762 if (i + 1 == numlines) {
6765 return lines[i + 1];
6767 if (ret != REG_NOMATCH) {
6775 static bool fruit_get_bandsize_from_line(char *line, size_t *_band_size)
6778 static bool re_initialized = false;
6779 regmatch_t matches[2];
6784 if (!re_initialized) {
6787 "<integer>\\([[:digit:]]*\\)</integer>$",
6792 re_initialized = true;
6795 ret = regexec(&re, line, 2, matches, 0);
6797 DBG_ERR("regex failed [%s]\n", line);
6801 line[matches[1].rm_eo] = '\0';
6803 ok = conv_str_u64(&line[matches[1].rm_so], &band_size);
6807 *_band_size = (size_t)band_size;
6812 * This reads and parses an Info.plist from a TM sparsebundle looking for the
6813 * "band-size" key and value.
6815 static bool fruit_get_bandsize(vfs_handle_struct *handle,
6819 #define INFO_PLIST_MAX_SIZE 64*1024
6821 struct smb_filename *smb_fname = NULL;
6822 files_struct *fsp = NULL;
6823 uint8_t *file_data = NULL;
6824 char **lines = NULL;
6825 char *band_size_line = NULL;
6826 size_t plist_file_size;
6833 plist = talloc_asprintf(talloc_tos(),
6835 handle->conn->connectpath,
6837 if (plist == NULL) {
6842 smb_fname = synthetic_smb_fname(talloc_tos(), plist, NULL, NULL, 0);
6843 if (smb_fname == NULL) {
6848 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
6850 DBG_INFO("Ignoring Sparsebundle without Info.plist [%s]\n", dir);
6855 plist_file_size = smb_fname->st.st_ex_size;
6857 if (plist_file_size > INFO_PLIST_MAX_SIZE) {
6858 DBG_INFO("%s is too large, ignoring\n", plist);
6863 status = SMB_VFS_NEXT_CREATE_FILE(
6866 0, /* root_dir_fid */
6867 smb_fname, /* fname */
6868 FILE_GENERIC_READ, /* access_mask */
6869 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
6870 FILE_OPEN, /* create_disposition */
6871 0, /* create_options */
6872 0, /* file_attributes */
6873 INTERNAL_OPEN_ONLY, /* oplock_request */
6875 0, /* allocation_size */
6876 0, /* private_flags */
6881 NULL, NULL); /* create context */
6882 if (!NT_STATUS_IS_OK(status)) {
6883 DBG_INFO("Opening [%s] failed [%s]\n",
6884 smb_fname_str_dbg(smb_fname), nt_errstr(status));
6889 file_data = talloc_array(talloc_tos(), uint8_t, plist_file_size);
6890 if (file_data == NULL) {
6895 nread = SMB_VFS_NEXT_PREAD(handle, fsp, file_data, plist_file_size, 0);
6896 if (nread != plist_file_size) {
6897 DBG_ERR("Short read on [%s]: %zu/%zd\n",
6898 fsp_str_dbg(fsp), nread, plist_file_size);
6904 status = close_file(NULL, fsp, NORMAL_CLOSE);
6906 if (!NT_STATUS_IS_OK(status)) {
6907 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
6912 lines = file_lines_parse((char *)file_data,
6916 if (lines == NULL) {
6921 band_size_line = fruit_get_bandsize_line(lines, numlines);
6922 if (band_size_line == NULL) {
6923 DBG_ERR("Didn't find band-size key in [%s]\n",
6924 smb_fname_str_dbg(smb_fname));
6929 ok = fruit_get_bandsize_from_line(band_size_line, band_size);
6931 DBG_ERR("fruit_get_bandsize_from_line failed\n");
6935 DBG_DEBUG("Parsed band-size [%zu] for [%s]\n", *band_size, plist);
6939 status = close_file(NULL, fsp, NORMAL_CLOSE);
6940 if (!NT_STATUS_IS_OK(status)) {
6941 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
6946 TALLOC_FREE(smb_fname);
6947 TALLOC_FREE(file_data);
6952 struct fruit_disk_free_state {
6956 static bool fruit_get_num_bands(vfs_handle_struct *handle,
6961 struct smb_filename *bands_dir = NULL;
6963 struct dirent *e = NULL;
6967 path = talloc_asprintf(talloc_tos(),
6969 handle->conn->connectpath,
6975 bands_dir = synthetic_smb_fname(talloc_tos(),
6981 if (bands_dir == NULL) {
6985 d = SMB_VFS_NEXT_OPENDIR(handle, bands_dir, NULL, 0);
6987 TALLOC_FREE(bands_dir);
6993 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
6995 e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
6997 if (ISDOT(e->d_name) || ISDOTDOT(e->d_name)) {
7003 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
7005 TALLOC_FREE(bands_dir);
7009 DBG_DEBUG("%zu bands in [%s]\n", nbands, smb_fname_str_dbg(bands_dir));
7011 TALLOC_FREE(bands_dir);
7017 static bool fruit_tmsize_do_dirent(vfs_handle_struct *handle,
7018 struct fruit_disk_free_state *state,
7023 size_t sparsebundle_strlen = strlen("sparsebundle");
7024 size_t bandsize = 0;
7028 p = strstr(e->d_name, "sparsebundle");
7033 if (p[sparsebundle_strlen] != '\0') {
7037 DBG_DEBUG("Processing sparsebundle [%s]\n", e->d_name);
7039 ok = fruit_get_bandsize(handle, e->d_name, &bandsize);
7042 * Beware of race conditions: this may be an uninitialized
7043 * Info.plist that a client is just creating. We don't want let
7044 * this to trigger complete failure.
7046 DBG_ERR("Processing sparsebundle [%s] failed\n", e->d_name);
7050 ok = fruit_get_num_bands(handle, e->d_name, &nbands);
7053 * Beware of race conditions: this may be a backup sparsebundle
7054 * in an early stage lacking a bands subdirectory. We don't want
7055 * let this to trigger complete failure.
7057 DBG_ERR("Processing sparsebundle [%s] failed\n", e->d_name);
7061 if (bandsize > SIZE_MAX/nbands) {
7062 DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
7066 tm_size = bandsize * nbands;
7068 if (state->total_size + tm_size < state->total_size) {
7069 DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
7074 state->total_size += tm_size;
7076 DBG_DEBUG("[%s] tm_size [%jd] total_size [%jd]\n",
7077 e->d_name, (intmax_t)tm_size, (intmax_t)state->total_size);
7083 * Calculate used size of a TimeMachine volume
7085 * This assumes that the volume is used only for TimeMachine.
7087 * - readdir(basedir of share), then
7088 * - for every element that matches regex "^\(.*\)\.sparsebundle$" :
7089 * - parse "\1.sparsebundle/Info.plist" and read the band-size XML key
7090 * - count band files in "\1.sparsebundle/bands/"
7091 * - calculate used size of all bands: band_count * band_size
7093 static uint64_t fruit_disk_free(vfs_handle_struct *handle,
7094 const struct smb_filename *smb_fname,
7099 struct fruit_config_data *config = NULL;
7100 struct fruit_disk_free_state state = {0};
7102 struct dirent *e = NULL;
7108 SMB_VFS_HANDLE_GET_DATA(handle, config,
7109 struct fruit_config_data,
7112 if (!config->time_machine ||
7113 config->time_machine_max_size == 0)
7115 return SMB_VFS_NEXT_DISK_FREE(handle,
7122 d = SMB_VFS_NEXT_OPENDIR(handle, smb_fname, NULL, 0);
7127 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
7129 e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
7131 ok = fruit_tmsize_do_dirent(handle, &state, e);
7133 SMB_VFS_NEXT_CLOSEDIR(handle, d);
7138 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
7143 dsize = config->time_machine_max_size / 512;
7144 dfree = dsize - (state.total_size / 512);
7145 if (dfree > dsize) {
7155 static struct vfs_fn_pointers vfs_fruit_fns = {
7156 .connect_fn = fruit_connect,
7157 .disk_free_fn = fruit_disk_free,
7159 /* File operations */
7160 .chmod_fn = fruit_chmod,
7161 .chown_fn = fruit_chown,
7162 .unlink_fn = fruit_unlink,
7163 .rename_fn = fruit_rename,
7164 .rmdir_fn = fruit_rmdir,
7165 .open_fn = fruit_open,
7166 .close_fn = fruit_close,
7167 .pread_fn = fruit_pread,
7168 .pwrite_fn = fruit_pwrite,
7169 .pread_send_fn = fruit_pread_send,
7170 .pread_recv_fn = fruit_pread_recv,
7171 .pwrite_send_fn = fruit_pwrite_send,
7172 .pwrite_recv_fn = fruit_pwrite_recv,
7173 .stat_fn = fruit_stat,
7174 .lstat_fn = fruit_lstat,
7175 .fstat_fn = fruit_fstat,
7176 .streaminfo_fn = fruit_streaminfo,
7177 .ntimes_fn = fruit_ntimes,
7178 .ftruncate_fn = fruit_ftruncate,
7179 .fallocate_fn = fruit_fallocate,
7180 .create_file_fn = fruit_create_file,
7181 .readdir_attr_fn = fruit_readdir_attr,
7182 .offload_read_send_fn = fruit_offload_read_send,
7183 .offload_read_recv_fn = fruit_offload_read_recv,
7184 .offload_write_send_fn = fruit_offload_write_send,
7185 .offload_write_recv_fn = fruit_offload_write_recv,
7187 /* NT ACL operations */
7188 .fget_nt_acl_fn = fruit_fget_nt_acl,
7189 .fset_nt_acl_fn = fruit_fset_nt_acl,
7193 NTSTATUS vfs_fruit_init(TALLOC_CTX *ctx)
7195 NTSTATUS ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "fruit",
7197 if (!NT_STATUS_IS_OK(ret)) {
7201 vfs_fruit_debug_level = debug_add_class("fruit");
7202 if (vfs_fruit_debug_level == -1) {
7203 vfs_fruit_debug_level = DBGC_VFS;
7204 DEBUG(0, ("%s: Couldn't register custom debugging class!\n",
7207 DEBUG(10, ("%s: Debug class number of '%s': %d\n",
7208 "vfs_fruit_init","fruit",vfs_fruit_debug_level));