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 files_struct *ad_fsp;
418 adouble_type_t ad_type;
421 uint8_t ad_filler[ADEDLEN_FILLER];
422 struct ad_entry ad_eid[ADEID_MAX];
424 struct ad_xattr_header adx_header;
425 struct ad_xattr_entry *adx_entries;
428 struct ad_entry_order {
429 uint32_t id, offset, len;
432 /* Netatalk AppleDouble metadata xattr */
434 struct ad_entry_order entry_order_meta_xattr[ADEID_NUM_XATTR + 1] = {
435 {ADEID_FINDERI, ADEDOFF_FINDERI_XATTR, ADEDLEN_FINDERI},
436 {ADEID_COMMENT, ADEDOFF_COMMENT_XATTR, 0},
437 {ADEID_FILEDATESI, ADEDOFF_FILEDATESI_XATTR, ADEDLEN_FILEDATESI},
438 {ADEID_AFPFILEI, ADEDOFF_AFPFILEI_XATTR, ADEDLEN_AFPFILEI},
439 {ADEID_PRIVDEV, ADEDOFF_PRIVDEV_XATTR, 0},
440 {ADEID_PRIVINO, ADEDOFF_PRIVINO_XATTR, 0},
441 {ADEID_PRIVSYN, ADEDOFF_PRIVSYN_XATTR, 0},
442 {ADEID_PRIVID, ADEDOFF_PRIVID_XATTR, 0},
446 /* AppleDouble resource fork file (the ones prefixed by "._") */
448 struct ad_entry_order entry_order_dot_und[ADEID_NUM_DOT_UND + 1] = {
449 {ADEID_FINDERI, ADEDOFF_FINDERI_DOT_UND, ADEDLEN_FINDERI},
450 {ADEID_RFORK, ADEDOFF_RFORK_DOT_UND, 0},
454 /* Conversion from enumerated id to on-disk AppleDouble id */
455 #define AD_EID_DISK(a) (set_eid[a])
456 static const uint32_t set_eid[] = {
457 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
458 AD_DEV, AD_INO, AD_SYN, AD_ID
461 static char empty_resourcefork[] = {
462 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
463 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
464 0x54, 0x68, 0x69, 0x73, 0x20, 0x72, 0x65, 0x73,
465 0x6F, 0x75, 0x72, 0x63, 0x65, 0x20, 0x66, 0x6F,
466 0x72, 0x6B, 0x20, 0x69, 0x6E, 0x74, 0x65, 0x6E,
467 0x74, 0x69, 0x6F, 0x6E, 0x61, 0x6C, 0x6C, 0x79,
468 0x20, 0x6C, 0x65, 0x66, 0x74, 0x20, 0x62, 0x6C,
469 0x61, 0x6E, 0x6B, 0x20, 0x20, 0x20, 0x00, 0x00,
470 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
471 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
472 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
473 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
474 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
475 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
476 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
477 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
478 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
479 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
480 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
481 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
482 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
495 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
496 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
497 0x00, 0x1C, 0x00, 0x1E, 0xFF, 0xFF
501 /* tcon config handle */
502 struct fruit_config_data *config;
504 /* Denote stream type, meta or rsrc */
507 /* Whether the create created the stream */
511 * AFP_AfpInfo stream created, but not written yet, thus still a fake
512 * pipe fd. This is set to true in fruit_open_meta if there was no
513 * exisiting stream but the caller requested O_CREAT. It is later set to
514 * false when we get a write on the stream that then does open and
523 * Forward declarations
525 static struct adouble *ad_init(TALLOC_CTX *ctx,
526 adouble_type_t type);
527 static struct adouble *ad_get(TALLOC_CTX *ctx,
528 vfs_handle_struct *handle,
529 const struct smb_filename *smb_fname,
530 adouble_type_t type);
531 static int ad_set(vfs_handle_struct *handle,
533 const struct smb_filename *smb_fname);
534 static int ad_fset(struct vfs_handle_struct *handle,
537 static int adouble_path(TALLOC_CTX *ctx,
538 const struct smb_filename *smb_fname__in,
539 struct smb_filename **ppsmb_fname_out);
540 static AfpInfo *afpinfo_new(TALLOC_CTX *ctx);
541 static ssize_t afpinfo_pack(const AfpInfo *ai, char *buf);
542 static AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data);
546 * Return a pointer to an AppleDouble entry
548 * Returns NULL if the entry is not present
550 static char *ad_get_entry(const struct adouble *ad, int eid)
552 off_t off = ad_getentryoff(ad, eid);
553 size_t len = ad_getentrylen(ad, eid);
555 if (off == 0 || len == 0) {
559 return ad->ad_data + off;
565 static int ad_getdate(const struct adouble *ad,
566 unsigned int dateoff,
569 bool xlate = (dateoff & AD_DATE_UNIX);
572 dateoff &= AD_DATE_MASK;
573 p = ad_get_entry(ad, ADEID_FILEDATESI);
578 if (dateoff > AD_DATE_ACCESS) {
582 memcpy(date, p + dateoff, sizeof(uint32_t));
585 *date = AD_DATE_TO_UNIX(*date);
593 static int ad_setdate(struct adouble *ad, unsigned int dateoff, uint32_t date)
595 bool xlate = (dateoff & AD_DATE_UNIX);
598 p = ad_get_entry(ad, ADEID_FILEDATESI);
603 dateoff &= AD_DATE_MASK;
605 date = AD_DATE_FROM_UNIX(date);
608 if (dateoff > AD_DATE_ACCESS) {
612 memcpy(p + dateoff, &date, sizeof(date));
619 * Map on-disk AppleDouble id to enumerated id
621 static uint32_t get_eid(uint32_t eid)
629 return ADEID_PRIVDEV;
631 return ADEID_PRIVINO;
633 return ADEID_PRIVSYN;
644 * Pack AppleDouble structure into data buffer
646 static bool ad_pack(struct adouble *ad)
653 bufsize = talloc_get_size(ad->ad_data);
654 if (bufsize < AD_DATASZ_DOT_UND) {
655 DBG_ERR("bad buffer size [0x%" PRIx32 "]\n", bufsize);
659 if (offset + ADEDLEN_MAGIC < offset ||
660 offset + ADEDLEN_MAGIC >= bufsize) {
663 RSIVAL(ad->ad_data, offset, ad->ad_magic);
664 offset += ADEDLEN_MAGIC;
666 if (offset + ADEDLEN_VERSION < offset ||
667 offset + ADEDLEN_VERSION >= bufsize) {
670 RSIVAL(ad->ad_data, offset, ad->ad_version);
671 offset += ADEDLEN_VERSION;
673 if (offset + ADEDLEN_FILLER < offset ||
674 offset + ADEDLEN_FILLER >= bufsize) {
677 if (ad->ad_type == ADOUBLE_RSRC) {
678 memcpy(ad->ad_data + offset, AD_FILLER_TAG, ADEDLEN_FILLER);
680 offset += ADEDLEN_FILLER;
682 if (offset + ADEDLEN_NENTRIES < offset ||
683 offset + ADEDLEN_NENTRIES >= bufsize) {
686 offset += ADEDLEN_NENTRIES;
688 for (eid = 0, nent = 0; eid < ADEID_MAX; eid++) {
689 if (ad->ad_eid[eid].ade_off == 0) {
691 * ade_off is also used as indicator whether a
692 * specific entry is used or not
697 if (offset + AD_ENTRY_LEN_EID < offset ||
698 offset + AD_ENTRY_LEN_EID >= bufsize) {
701 RSIVAL(ad->ad_data, offset, AD_EID_DISK(eid));
702 offset += AD_ENTRY_LEN_EID;
704 if (offset + AD_ENTRY_LEN_OFF < offset ||
705 offset + AD_ENTRY_LEN_OFF >= bufsize) {
708 RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_off);
709 offset += AD_ENTRY_LEN_OFF;
711 if (offset + AD_ENTRY_LEN_LEN < offset ||
712 offset + AD_ENTRY_LEN_LEN >= bufsize) {
715 RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_len);
716 offset += AD_ENTRY_LEN_LEN;
721 if (ADEDOFF_NENTRIES + 2 >= bufsize) {
724 RSSVAL(ad->ad_data, ADEDOFF_NENTRIES, nent);
729 static bool ad_unpack_xattrs(struct adouble *ad)
731 struct ad_xattr_header *h = &ad->adx_header;
732 const char *p = ad->ad_data;
736 if (ad_getentrylen(ad, ADEID_FINDERI) <= ADEDLEN_FINDERI) {
740 /* 2 bytes padding */
741 hoff = ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI + 2;
743 h->adx_magic = RIVAL(p, hoff + 0);
744 h->adx_debug_tag = RIVAL(p, hoff + 4); /* Not used -> not checked */
745 h->adx_total_size = RIVAL(p, hoff + 8);
746 h->adx_data_start = RIVAL(p, hoff + 12);
747 h->adx_data_length = RIVAL(p, hoff + 16);
748 h->adx_flags = RSVAL(p, hoff + 32); /* Not used -> not checked */
749 h->adx_num_attrs = RSVAL(p, hoff + 34);
751 if (h->adx_magic != AD_XATTR_HDR_MAGIC) {
752 DBG_ERR("Bad magic: 0x%" PRIx32 "\n", h->adx_magic);
756 if (h->adx_total_size > ad_getentryoff(ad, ADEID_RFORK)) {
757 DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size);
760 if (h->adx_total_size > AD_XATTR_MAX_HDR_SIZE) {
761 DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size);
765 if (h->adx_data_start < (hoff + AD_XATTR_HDR_SIZE)) {
766 DBG_ERR("Bad start: 0x%" PRIx32 "\n", h->adx_data_start);
770 if ((h->adx_data_start + h->adx_data_length) < h->adx_data_start) {
771 DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length);
774 if ((h->adx_data_start + h->adx_data_length) >
775 ad->adx_header.adx_total_size)
777 DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length);
781 if (h->adx_num_attrs > AD_XATTR_MAX_ENTRIES) {
782 DBG_ERR("Bad num xattrs: %" PRIu16 "\n", h->adx_num_attrs);
786 if (h->adx_num_attrs == 0) {
790 ad->adx_entries = talloc_zero_array(
791 ad, struct ad_xattr_entry, h->adx_num_attrs);
792 if (ad->adx_entries == NULL) {
796 hoff += AD_XATTR_HDR_SIZE;
798 for (i = 0; i < h->adx_num_attrs; i++) {
799 struct ad_xattr_entry *e = &ad->adx_entries[i];
801 hoff = (hoff + 3) & ~3;
803 e->adx_offset = RIVAL(p, hoff + 0);
804 e->adx_length = RIVAL(p, hoff + 4);
805 e->adx_flags = RSVAL(p, hoff + 8);
806 e->adx_namelen = *(p + hoff + 10);
808 if (e->adx_offset >= ad->adx_header.adx_total_size) {
809 DBG_ERR("Bad adx_offset: %" PRIx32 "\n",
814 if ((e->adx_offset + e->adx_length) < e->adx_offset) {
815 DBG_ERR("Bad adx_length: %" PRIx32 "\n",
820 if ((e->adx_offset + e->adx_length) >
821 ad->adx_header.adx_total_size)
823 DBG_ERR("Bad adx_length: %" PRIx32 "\n",
828 if (e->adx_namelen == 0) {
829 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
833 if ((hoff + 11 + e->adx_namelen) < hoff + 11) {
834 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
838 if ((hoff + 11 + e->adx_namelen) >
839 ad->adx_header.adx_data_start)
841 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
846 e->adx_name = talloc_strndup(ad->adx_entries,
849 if (e->adx_name == NULL) {
853 DBG_DEBUG("xattr [%s] offset [0x%x] size [0x%x]\n",
854 e->adx_name, e->adx_offset, e->adx_length);
855 dump_data(10, (uint8_t *)(ad->ad_data + e->adx_offset),
858 hoff += 11 + e->adx_namelen;
865 * Unpack an AppleDouble blob into a struct adoble
867 static bool ad_unpack(struct adouble *ad, const size_t nentries,
870 size_t bufsize = talloc_get_size(ad->ad_data);
872 uint32_t eid, len, off;
876 * The size of the buffer ad->ad_data is checked when read, so
877 * we wouldn't have to check our own offsets, a few extra
878 * checks won't hurt though. We have to check the offsets we
879 * read from the buffer anyway.
882 if (bufsize < (AD_HEADER_LEN + (AD_ENTRY_LEN * nentries))) {
883 DEBUG(1, ("bad size\n"));
887 ad->ad_magic = RIVAL(ad->ad_data, 0);
888 ad->ad_version = RIVAL(ad->ad_data, ADEDOFF_VERSION);
889 if ((ad->ad_magic != AD_MAGIC) || (ad->ad_version != AD_VERSION)) {
890 DEBUG(1, ("wrong magic or version\n"));
894 memcpy(ad->ad_filler, ad->ad_data + ADEDOFF_FILLER, ADEDLEN_FILLER);
896 adentries = RSVAL(ad->ad_data, ADEDOFF_NENTRIES);
897 if (adentries != nentries) {
898 DEBUG(1, ("invalid number of entries: %zu\n",
903 /* now, read in the entry bits */
904 for (i = 0; i < adentries; i++) {
905 eid = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN));
907 off = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 4);
908 len = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 8);
910 if (!eid || eid >= ADEID_MAX) {
911 DEBUG(1, ("bogus eid %d\n", eid));
916 * All entries other than the resource fork are
917 * expected to be read into the ad_data buffer, so
918 * ensure the specified offset is within that bound
920 if ((off > bufsize) && (eid != ADEID_RFORK)) {
921 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
927 * All entries besides FinderInfo and resource fork
928 * must fit into the buffer. FinderInfo is special as
929 * it may be larger then the default 32 bytes (if it
930 * contains marshalled xattrs), but we will fixup that
931 * in ad_convert(). And the resource fork is never
932 * accessed directly by the ad_data buf (also see
933 * comment above) anyway.
935 if ((eid != ADEID_RFORK) &&
936 (eid != ADEID_FINDERI) &&
937 ((off + len) > bufsize)) {
938 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
944 * That would be obviously broken
946 if (off > filesize) {
947 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
953 * Check for any entry that has its end beyond the
956 if (off + len < off) {
957 DEBUG(1, ("offset wrap in eid %d: off: %" PRIu32
958 ", len: %" PRIu32 "\n",
963 if (off + len > filesize) {
965 * If this is the resource fork entry, we fix
966 * up the length, for any other entry we bail
969 if (eid != ADEID_RFORK) {
970 DEBUG(1, ("bogus eid %d: off: %" PRIu32
971 ", len: %" PRIu32 "\n",
977 * Fixup the resource fork entry by limiting
978 * the size to entryoffset - filesize.
980 len = filesize - off;
981 DEBUG(1, ("Limiting ADEID_RFORK: off: %" PRIu32
982 ", len: %" PRIu32 "\n", off, len));
985 ad->ad_eid[eid].ade_off = off;
986 ad->ad_eid[eid].ade_len = len;
989 ok = ad_unpack_xattrs(ad);
997 static bool ad_convert_move_reso(vfs_handle_struct *handle,
999 const struct smb_filename *smb_fname)
1008 rforklen = ad_getentrylen(ad, ADEID_RFORK);
1009 if (rforklen == 0) {
1013 buf = talloc_size(ad, rforklen);
1016 * This allocates a buffer for reading the resource fork data in
1017 * one big swoop. Resource forks won't be larger then, say, 64
1018 * MB, I swear, so just doing the allocation with the talloc
1019 * limit as safeguard seems safe.
1021 DBG_ERR("Failed to allocate %zu bytes for rfork\n",
1026 rforkoff = ad_getentryoff(ad, ADEID_RFORK);
1028 n = SMB_VFS_PREAD(ad->ad_fsp, buf, rforklen, rforkoff);
1029 if (n != rforklen) {
1030 DBG_ERR("Reading %zu bytes from rfork [%s] failed: %s\n",
1031 rforklen, fsp_str_dbg(ad->ad_fsp), strerror(errno));
1035 rforkoff = ADEDOFF_RFORK_DOT_UND;
1037 n = SMB_VFS_PWRITE(ad->ad_fsp, buf, rforklen, rforkoff);
1038 if (n != rforklen) {
1039 DBG_ERR("Writing %zu bytes to rfork [%s] failed: %s\n",
1040 rforklen, fsp_str_dbg(ad->ad_fsp), strerror(errno));
1044 ad_setentryoff(ad, ADEID_RFORK, ADEDOFF_RFORK_DOT_UND);
1047 DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name);
1051 ret = ad_fset(handle, ad, ad->ad_fsp);
1053 DBG_ERR("ad_fset on [%s] failed\n", fsp_str_dbg(ad->ad_fsp));
1060 static bool ad_convert_xattr(vfs_handle_struct *handle,
1062 const struct smb_filename *smb_fname,
1063 bool *converted_xattr)
1065 static struct char_mappings **string_replace_cmaps = NULL;
1067 int saved_errno = 0;
1072 *converted_xattr = false;
1074 if (ad_getentrylen(ad, ADEID_FINDERI) == ADEDLEN_FINDERI) {
1078 if (string_replace_cmaps == NULL) {
1079 const char **mappings = NULL;
1081 mappings = str_list_make_v3_const(
1082 talloc_tos(), fruit_catia_maps, NULL);
1083 if (mappings == NULL) {
1086 string_replace_cmaps = string_replace_init_map(mappings);
1087 TALLOC_FREE(mappings);
1090 for (i = 0; i < ad->adx_header.adx_num_attrs; i++) {
1091 struct ad_xattr_entry *e = &ad->adx_entries[i];
1092 char *mapped_name = NULL;
1094 struct smb_filename *stream_name = NULL;
1095 files_struct *fsp = NULL;
1098 status = string_replace_allocate(handle->conn,
1100 string_replace_cmaps,
1103 vfs_translate_to_windows);
1104 if (!NT_STATUS_IS_OK(status) &&
1105 !NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))
1107 DBG_ERR("string_replace_allocate failed\n");
1113 mapped_name = talloc_asprintf(talloc_tos(), ":%s", tmp);
1115 if (mapped_name == NULL) {
1120 stream_name = synthetic_smb_fname(talloc_tos(),
1121 smb_fname->base_name,
1125 TALLOC_FREE(mapped_name);
1126 if (stream_name == NULL) {
1127 DBG_ERR("synthetic_smb_fname failed\n");
1132 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1134 status = SMB_VFS_CREATE_FILE(
1135 handle->conn, /* conn */
1137 0, /* root_dir_fid */
1138 stream_name, /* fname */
1139 FILE_GENERIC_WRITE, /* access_mask */
1140 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
1141 FILE_OPEN_IF, /* create_disposition */
1142 0, /* create_options */
1143 0, /* file_attributes */
1144 INTERNAL_OPEN_ONLY, /* oplock_request */
1146 0, /* allocation_size */
1147 0, /* private_flags */
1152 NULL, NULL); /* create context */
1153 TALLOC_FREE(stream_name);
1154 if (!NT_STATUS_IS_OK(status)) {
1155 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1160 nwritten = SMB_VFS_PWRITE(fsp,
1161 ad->ad_data + e->adx_offset,
1164 if (nwritten == -1) {
1165 DBG_ERR("SMB_VFS_PWRITE failed\n");
1166 saved_errno = errno;
1167 close_file(NULL, fsp, ERROR_CLOSE);
1168 errno = saved_errno;
1173 status = close_file(NULL, fsp, NORMAL_CLOSE);
1174 if (!NT_STATUS_IS_OK(status)) {
1181 ad_setentrylen(ad, ADEID_FINDERI, ADEDLEN_FINDERI);
1185 DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name);
1189 rc = ad_fset(handle, ad, ad->ad_fsp);
1191 DBG_ERR("ad_fset on [%s] failed: %s\n",
1192 fsp_str_dbg(ad->ad_fsp), strerror(errno));
1197 ok = ad_convert_move_reso(handle, ad, smb_fname);
1202 *converted_xattr = true;
1209 static bool ad_convert_finderinfo(vfs_handle_struct *handle,
1211 const struct smb_filename *smb_fname)
1216 struct smb_filename *stream_name = NULL;
1217 files_struct *fsp = NULL;
1221 int saved_errno = 0;
1224 cmp = memcmp(ad->ad_filler, AD_FILLER_TAG_OSX, ADEDLEN_FILLER);
1229 p_ad = ad_get_entry(ad, ADEID_FINDERI);
1234 ai = afpinfo_new(talloc_tos());
1239 memcpy(ai->afpi_FinderInfo, p_ad, ADEDLEN_FINDERI);
1241 aiblob = data_blob_talloc(talloc_tos(), NULL, AFP_INFO_SIZE);
1242 if (aiblob.data == NULL) {
1247 size = afpinfo_pack(ai, (char *)aiblob.data);
1249 if (size != AFP_INFO_SIZE) {
1253 stream_name = synthetic_smb_fname(talloc_tos(),
1254 smb_fname->base_name,
1258 if (stream_name == NULL) {
1259 data_blob_free(&aiblob);
1260 DBG_ERR("synthetic_smb_fname failed\n");
1264 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1266 status = SMB_VFS_CREATE_FILE(
1267 handle->conn, /* conn */
1269 0, /* root_dir_fid */
1270 stream_name, /* fname */
1271 FILE_GENERIC_WRITE, /* access_mask */
1272 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
1273 FILE_OPEN_IF, /* create_disposition */
1274 0, /* create_options */
1275 0, /* file_attributes */
1276 INTERNAL_OPEN_ONLY, /* oplock_request */
1278 0, /* allocation_size */
1279 0, /* private_flags */
1284 NULL, NULL); /* create context */
1285 TALLOC_FREE(stream_name);
1286 if (!NT_STATUS_IS_OK(status)) {
1287 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1291 nwritten = SMB_VFS_PWRITE(fsp,
1295 if (nwritten == -1) {
1296 DBG_ERR("SMB_VFS_PWRITE failed\n");
1297 saved_errno = errno;
1298 close_file(NULL, fsp, ERROR_CLOSE);
1299 errno = saved_errno;
1303 status = close_file(NULL, fsp, NORMAL_CLOSE);
1304 if (!NT_STATUS_IS_OK(status)) {
1312 static bool ad_convert_truncate(vfs_handle_struct *handle,
1314 const struct smb_filename *smb_fname)
1319 newlen = ADEDOFF_RFORK_DOT_UND + ad_getentrylen(ad, ADEID_RFORK);
1321 rc = SMB_VFS_FTRUNCATE(ad->ad_fsp, newlen);
1329 static bool ad_convert_blank_rfork(vfs_handle_struct *handle,
1333 struct fruit_config_data *config = NULL;
1334 uint8_t *map = MAP_FAILED;
1343 SMB_VFS_HANDLE_GET_DATA(handle, config,
1344 struct fruit_config_data, return false);
1346 if (!config->wipe_intentionally_left_blank_rfork) {
1350 if (ad_getentrylen(ad, ADEID_RFORK) != sizeof(empty_resourcefork)) {
1354 maplen = ad_getentryoff(ad, ADEID_RFORK) +
1355 ad_getentrylen(ad, ADEID_RFORK);
1357 /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
1358 map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
1359 ad->ad_fsp->fh->fd, 0);
1360 if (map == MAP_FAILED) {
1361 DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
1365 cmp = memcmp(map + ADEDOFF_RFORK_DOT_UND,
1367 sizeof(empty_resourcefork));
1368 rc = munmap(map, maplen);
1370 DBG_ERR("munmap failed: %s\n", strerror(errno));
1378 ad_setentrylen(ad, ADEID_RFORK, 0);
1385 len = sys_pwrite(ad->ad_fsp->fh->fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
1386 if (len != AD_DATASZ_DOT_UND) {
1394 static bool ad_convert_delete_adfile(vfs_handle_struct *handle,
1396 const struct smb_filename *smb_fname)
1398 struct fruit_config_data *config = NULL;
1399 struct smb_filename *ad_name = NULL;
1402 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
1406 SMB_VFS_HANDLE_GET_DATA(handle, config,
1407 struct fruit_config_data, return false);
1409 if (!config->delete_empty_adfiles) {
1413 rc = adouble_path(talloc_tos(), smb_fname, &ad_name);
1418 rc = SMB_VFS_NEXT_UNLINK(handle, ad_name);
1420 DBG_ERR("Unlinking [%s] failed: %s\n",
1421 smb_fname_str_dbg(ad_name), strerror(errno));
1422 TALLOC_FREE(ad_name);
1426 DBG_WARNING("Unlinked [%s] after conversion\n", smb_fname_str_dbg(ad_name));
1427 TALLOC_FREE(ad_name);
1433 * Convert from Apple's ._ file to Netatalk
1435 * Apple's AppleDouble may contain a FinderInfo entry longer then 32
1436 * bytes containing packed xattrs.
1438 * @return -1 in case an error occurred, 0 if no conversion was done, 1
1441 static int ad_convert(struct vfs_handle_struct *handle,
1442 const struct smb_filename *smb_fname)
1444 struct adouble *ad = NULL;
1446 bool converted_xattr = false;
1450 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
1455 ok = ad_convert_xattr(handle, ad, smb_fname, &converted_xattr);
1461 ok = ad_convert_blank_rfork(handle, ad, &blank);
1467 if (converted_xattr || blank) {
1468 ok = ad_convert_truncate(handle, ad, smb_fname);
1475 ok = ad_convert_finderinfo(handle, ad, smb_fname);
1477 DBG_ERR("Failed to convert [%s]\n",
1478 smb_fname_str_dbg(smb_fname));
1483 ok = ad_convert_delete_adfile(handle, ad, smb_fname);
1496 * Read and parse Netatalk AppleDouble metadata xattr
1498 static ssize_t ad_read_meta(vfs_handle_struct *handle,
1500 const struct smb_filename *smb_fname)
1506 DEBUG(10, ("reading meta xattr for %s\n", smb_fname->base_name));
1508 ealen = SMB_VFS_GETXATTR(handle->conn, smb_fname,
1509 AFPINFO_EA_NETATALK, ad->ad_data,
1515 if (errno == ENOATTR) {
1521 DEBUG(2, ("error reading meta xattr: %s\n",
1527 if (ealen != AD_DATASZ_XATTR) {
1528 DEBUG(2, ("bad size %zd\n", ealen));
1534 /* Now parse entries */
1535 ok = ad_unpack(ad, ADEID_NUM_XATTR, AD_DATASZ_XATTR);
1537 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1543 if (!ad_getentryoff(ad, ADEID_FINDERI)
1544 || !ad_getentryoff(ad, ADEID_COMMENT)
1545 || !ad_getentryoff(ad, ADEID_FILEDATESI)
1546 || !ad_getentryoff(ad, ADEID_AFPFILEI)
1547 || !ad_getentryoff(ad, ADEID_PRIVDEV)
1548 || !ad_getentryoff(ad, ADEID_PRIVINO)
1549 || !ad_getentryoff(ad, ADEID_PRIVSYN)
1550 || !ad_getentryoff(ad, ADEID_PRIVID)) {
1551 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1558 DEBUG(10, ("reading meta xattr for %s, rc: %d\n",
1559 smb_fname->base_name, rc));
1563 if (errno == EINVAL) {
1565 (void)SMB_VFS_REMOVEXATTR(handle->conn,
1567 AFPINFO_EA_NETATALK);
1575 static int ad_open_rsrc(vfs_handle_struct *handle,
1576 const struct smb_filename *smb_fname,
1579 files_struct **_fsp)
1582 struct smb_filename *adp_smb_fname = NULL;
1583 files_struct *fsp = NULL;
1584 uint32_t access_mask;
1585 uint32_t share_access;
1586 uint32_t create_disposition;
1589 ret = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
1594 ret = SMB_VFS_STAT(handle->conn, adp_smb_fname);
1596 TALLOC_FREE(adp_smb_fname);
1600 access_mask = FILE_GENERIC_READ;
1601 share_access = FILE_SHARE_READ | FILE_SHARE_WRITE;
1602 create_disposition = FILE_OPEN;
1604 if (flags & O_RDWR) {
1605 access_mask |= FILE_GENERIC_WRITE;
1606 share_access &= ~FILE_SHARE_WRITE;
1609 status = SMB_VFS_CREATE_FILE(
1610 handle->conn, /* conn */
1612 0, /* root_dir_fid */
1617 0, /* create_options */
1618 0, /* file_attributes */
1619 INTERNAL_OPEN_ONLY, /* oplock_request */
1621 0, /* allocation_size */
1622 0, /* private_flags */
1627 NULL, NULL); /* create context */
1628 TALLOC_FREE(adp_smb_fname);
1629 if (!NT_STATUS_IS_OK(status)) {
1630 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1639 * Here's the deal: for ADOUBLE_META we can do without an fd as we can issue
1640 * path based xattr calls. For ADOUBLE_RSRC however we need a full-fledged fd
1641 * for file IO on the ._ file.
1643 static int ad_open(vfs_handle_struct *handle,
1646 const struct smb_filename *smb_fname,
1652 DBG_DEBUG("Path [%s] type [%s]\n", smb_fname->base_name,
1653 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
1655 if (ad->ad_type == ADOUBLE_META) {
1661 ad->ad_opened = false;
1665 ret = ad_open_rsrc(handle, smb_fname, flags, mode, &ad->ad_fsp);
1669 ad->ad_opened = true;
1671 DBG_DEBUG("Path [%s] type [%s]\n",
1672 smb_fname->base_name,
1673 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
1678 static ssize_t ad_read_rsrc_adouble(vfs_handle_struct *handle,
1680 const struct smb_filename *smb_fname)
1682 SMB_STRUCT_STAT sbuf;
1689 ret = sys_fstat(ad->ad_fsp->fh->fd, &sbuf, lp_fake_directory_create_times(
1690 SNUM(handle->conn)));
1696 * AppleDouble file header content and size, two cases:
1698 * - without xattrs it is exactly AD_DATASZ_DOT_UND (82) bytes large
1699 * - with embedded xattrs it can be larger, up to AD_XATTR_MAX_HDR_SIZE
1701 * Read as much as we can up to AD_XATTR_MAX_HDR_SIZE.
1703 size = sbuf.st_ex_size;
1704 if (size > talloc_array_length(ad->ad_data)) {
1705 if (size > AD_XATTR_MAX_HDR_SIZE) {
1706 size = AD_XATTR_MAX_HDR_SIZE;
1708 p_ad = talloc_realloc(ad, ad->ad_data, char, size);
1715 len = sys_pread(ad->ad_fsp->fh->fd, ad->ad_data,
1716 talloc_array_length(ad->ad_data), 0);
1717 if (len != talloc_array_length(ad->ad_data)) {
1718 DBG_NOTICE("%s %s: bad size: %zd\n",
1719 smb_fname->base_name, strerror(errno), len);
1723 /* Now parse entries */
1724 ok = ad_unpack(ad, ADEID_NUM_DOT_UND, sbuf.st_ex_size);
1726 DBG_ERR("invalid AppleDouble resource %s\n",
1727 smb_fname->base_name);
1732 if ((ad_getentryoff(ad, ADEID_FINDERI) != ADEDOFF_FINDERI_DOT_UND)
1733 || (ad_getentrylen(ad, ADEID_FINDERI) < ADEDLEN_FINDERI)
1734 || (ad_getentryoff(ad, ADEID_RFORK) < ADEDOFF_RFORK_DOT_UND)) {
1735 DBG_ERR("invalid AppleDouble resource %s\n",
1736 smb_fname->base_name);
1745 * Read and parse resource fork, either ._ AppleDouble file or xattr
1747 static ssize_t ad_read_rsrc(vfs_handle_struct *handle,
1749 const struct smb_filename *smb_fname)
1751 return ad_read_rsrc_adouble(handle, ad, smb_fname);
1755 * Read and unpack an AppleDouble metadata xattr or resource
1757 static ssize_t ad_read(vfs_handle_struct *handle,
1759 const struct smb_filename *smb_fname)
1761 switch (ad->ad_type) {
1763 return ad_read_meta(handle, ad, smb_fname);
1765 return ad_read_rsrc(handle, ad, smb_fname);
1771 static int adouble_destructor(struct adouble *ad)
1775 if (!ad->ad_opened) {
1779 SMB_ASSERT(ad->ad_fsp != NULL);
1781 status = close_file(NULL, ad->ad_fsp, NORMAL_CLOSE);
1782 if (!NT_STATUS_IS_OK(status)) {
1783 DBG_ERR("Closing [%s] failed: %s\n",
1784 fsp_str_dbg(ad->ad_fsp), nt_errstr(status));
1791 * Allocate a struct adouble without initialiing it
1793 * The struct is either hang of the fsp extension context or if fsp is
1796 * @param[in] ctx talloc context
1797 * @param[in] handle vfs handle
1798 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1800 * @return adouble handle
1802 static struct adouble *ad_alloc(TALLOC_CTX *ctx,
1803 adouble_type_t type)
1811 adsize = AD_DATASZ_XATTR;
1814 adsize = AD_DATASZ_DOT_UND;
1820 ad = talloc_zero(ctx, struct adouble);
1827 ad->ad_data = talloc_zero_array(ad, char, adsize);
1828 if (ad->ad_data == NULL) {
1835 ad->ad_magic = AD_MAGIC;
1836 ad->ad_version = AD_VERSION;
1838 talloc_set_destructor(ad, adouble_destructor);
1848 * Allocate and initialize a new struct adouble
1850 * @param[in] ctx talloc context
1851 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1853 * @return adouble handle, initialized
1855 static struct adouble *ad_init(TALLOC_CTX *ctx,
1856 adouble_type_t type)
1859 const struct ad_entry_order *eid;
1860 struct adouble *ad = NULL;
1861 time_t t = time(NULL);
1865 eid = entry_order_meta_xattr;
1868 eid = entry_order_dot_und;
1874 ad = ad_alloc(ctx, type);
1880 ad->ad_eid[eid->id].ade_off = eid->offset;
1881 ad->ad_eid[eid->id].ade_len = eid->len;
1885 /* put something sane in the date fields */
1886 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, t);
1887 ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, t);
1888 ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, t);
1889 ad_setdate(ad, AD_DATE_BACKUP, htonl(AD_DATE_START));
1897 static struct adouble *ad_get_internal(TALLOC_CTX *ctx,
1898 vfs_handle_struct *handle,
1900 const struct smb_filename *smb_fname,
1901 adouble_type_t type)
1905 struct adouble *ad = NULL;
1909 smb_fname = fsp->base_fsp->fsp_name;
1912 DEBUG(10, ("ad_get(%s) called for %s\n",
1913 type == ADOUBLE_META ? "meta" : "rsrc",
1914 smb_fname->base_name));
1916 ad = ad_alloc(ctx, type);
1922 /* Try rw first so we can use the fd in ad_convert() */
1925 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
1926 if (rc == -1 && ((errno == EROFS) || (errno == EACCES))) {
1928 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
1931 DBG_DEBUG("ad_open [%s] error [%s]\n",
1932 smb_fname->base_name, strerror(errno));
1937 len = ad_read(handle, ad, smb_fname);
1939 DEBUG(10, ("error reading AppleDouble for %s\n",
1940 smb_fname->base_name));
1946 DEBUG(10, ("ad_get(%s) for %s returning %d\n",
1947 type == ADOUBLE_META ? "meta" : "rsrc",
1948 smb_fname->base_name, rc));
1957 * Return AppleDouble data for a file
1959 * @param[in] ctx talloc context
1960 * @param[in] handle vfs handle
1961 * @param[in] smb_fname pathname to file or directory
1962 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1964 * @return talloced struct adouble or NULL on error
1966 static struct adouble *ad_get(TALLOC_CTX *ctx,
1967 vfs_handle_struct *handle,
1968 const struct smb_filename *smb_fname,
1969 adouble_type_t type)
1971 return ad_get_internal(ctx, handle, NULL, smb_fname, type);
1975 * Return AppleDouble data for a file
1977 * @param[in] ctx talloc context
1978 * @param[in] handle vfs handle
1979 * @param[in] fsp fsp to use for IO
1980 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1982 * @return talloced struct adouble or NULL on error
1984 static struct adouble *ad_fget(TALLOC_CTX *ctx, vfs_handle_struct *handle,
1985 files_struct *fsp, adouble_type_t type)
1987 return ad_get_internal(ctx, handle, fsp, NULL, type);
1991 * Set AppleDouble metadata on a file or directory
1993 * @param[in] ad adouble handle
1995 * @param[in] smb_fname pathname to file or directory
1997 * @return status code, 0 means success
1999 static int ad_set(vfs_handle_struct *handle,
2001 const struct smb_filename *smb_fname)
2006 DBG_DEBUG("Path [%s]\n", smb_fname->base_name);
2008 if (ad->ad_type != ADOUBLE_META) {
2009 DBG_ERR("ad_set on [%s] used with ADOUBLE_RSRC\n",
2010 smb_fname->base_name);
2019 ret = SMB_VFS_SETXATTR(handle->conn,
2021 AFPINFO_EA_NETATALK,
2023 AD_DATASZ_XATTR, 0);
2025 DBG_DEBUG("Path [%s] ret [%d]\n", smb_fname->base_name, ret);
2031 * Set AppleDouble metadata on a file or directory
2033 * @param[in] ad adouble handle
2034 * @param[in] fsp file handle
2036 * @return status code, 0 means success
2038 static int ad_fset(struct vfs_handle_struct *handle,
2046 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
2049 || (fsp->fh == NULL)
2050 || (fsp->fh->fd == -1))
2052 smb_panic("bad fsp");
2060 switch (ad->ad_type) {
2062 rc = SMB_VFS_NEXT_SETXATTR(handle,
2064 AFPINFO_EA_NETATALK,
2066 AD_DATASZ_XATTR, 0);
2070 len = SMB_VFS_NEXT_PWRITE(handle,
2075 if (len != AD_DATASZ_DOT_UND) {
2076 DBG_ERR("short write on %s: %zd", fsp_str_dbg(fsp), len);
2086 DBG_DEBUG("Path [%s] rc [%d]\n", fsp_str_dbg(fsp), rc);
2091 /*****************************************************************************
2093 *****************************************************************************/
2095 static bool is_afpinfo_stream(const struct smb_filename *smb_fname)
2097 if (strncasecmp_m(smb_fname->stream_name,
2098 AFPINFO_STREAM_NAME,
2099 strlen(AFPINFO_STREAM_NAME)) == 0) {
2105 static bool is_afpresource_stream(const struct smb_filename *smb_fname)
2107 if (strncasecmp_m(smb_fname->stream_name,
2108 AFPRESOURCE_STREAM_NAME,
2109 strlen(AFPRESOURCE_STREAM_NAME)) == 0) {
2116 * Test whether stream is an Apple stream.
2118 static bool is_apple_stream(const struct smb_filename *smb_fname)
2120 if (is_afpinfo_stream(smb_fname)) {
2123 if (is_afpresource_stream(smb_fname)) {
2129 static bool is_adouble_file(const char *path)
2131 const char *p = NULL;
2134 p = strrchr(path, '/');
2142 ADOUBLE_NAME_PREFIX,
2143 strlen(ADOUBLE_NAME_PREFIX));
2151 * Initialize config struct from our smb.conf config parameters
2153 static int init_fruit_config(vfs_handle_struct *handle)
2155 struct fruit_config_data *config;
2157 const char *tm_size_str = NULL;
2159 config = talloc_zero(handle->conn, struct fruit_config_data);
2161 DEBUG(1, ("talloc_zero() failed\n"));
2167 * Versions up to Samba 4.5.x had a spelling bug in the
2168 * fruit:resource option calling lp_parm_enum with
2169 * "res*s*ource" (ie two s).
2171 * In Samba 4.6 we accept both the wrong and the correct
2172 * spelling, in Samba 4.7 the bad spelling will be removed.
2174 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2175 "ressource", fruit_rsrc, FRUIT_RSRC_ADFILE);
2176 if (enumval == -1) {
2177 DEBUG(1, ("value for %s: resource type unknown\n",
2178 FRUIT_PARAM_TYPE_NAME));
2181 config->rsrc = (enum fruit_rsrc)enumval;
2183 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2184 "resource", fruit_rsrc, enumval);
2185 if (enumval == -1) {
2186 DEBUG(1, ("value for %s: resource type unknown\n",
2187 FRUIT_PARAM_TYPE_NAME));
2190 config->rsrc = (enum fruit_rsrc)enumval;
2192 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2193 "metadata", fruit_meta, FRUIT_META_NETATALK);
2194 if (enumval == -1) {
2195 DEBUG(1, ("value for %s: metadata type unknown\n",
2196 FRUIT_PARAM_TYPE_NAME));
2199 config->meta = (enum fruit_meta)enumval;
2201 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2202 "locking", fruit_locking, FRUIT_LOCKING_NONE);
2203 if (enumval == -1) {
2204 DEBUG(1, ("value for %s: locking type unknown\n",
2205 FRUIT_PARAM_TYPE_NAME));
2208 config->locking = (enum fruit_locking)enumval;
2210 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2211 "encoding", fruit_encoding, FRUIT_ENC_PRIVATE);
2212 if (enumval == -1) {
2213 DEBUG(1, ("value for %s: encoding type unknown\n",
2214 FRUIT_PARAM_TYPE_NAME));
2217 config->encoding = (enum fruit_encoding)enumval;
2219 if (config->rsrc == FRUIT_RSRC_ADFILE) {
2220 config->veto_appledouble = lp_parm_bool(SNUM(handle->conn),
2221 FRUIT_PARAM_TYPE_NAME,
2226 config->use_aapl = lp_parm_bool(
2227 -1, FRUIT_PARAM_TYPE_NAME, "aapl", true);
2229 config->time_machine = lp_parm_bool(
2230 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "time machine", false);
2232 config->unix_info_enabled = lp_parm_bool(
2233 -1, FRUIT_PARAM_TYPE_NAME, "nfs_aces", true);
2235 config->use_copyfile = lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME,
2238 config->posix_rename = lp_parm_bool(
2239 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "posix_rename", true);
2241 config->aapl_zero_file_id =
2242 lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME, "zero_file_id", true);
2244 config->readdir_attr_rsize = lp_parm_bool(
2245 SNUM(handle->conn), "readdir_attr", "aapl_rsize", true);
2247 config->readdir_attr_finder_info = lp_parm_bool(
2248 SNUM(handle->conn), "readdir_attr", "aapl_finder_info", true);
2250 config->readdir_attr_max_access = lp_parm_bool(
2251 SNUM(handle->conn), "readdir_attr", "aapl_max_access", true);
2253 config->model = lp_parm_const_string(
2254 -1, FRUIT_PARAM_TYPE_NAME, "model", "MacSamba");
2256 tm_size_str = lp_parm_const_string(
2257 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2258 "time machine max size", NULL);
2259 if (tm_size_str != NULL) {
2260 config->time_machine_max_size = conv_str_size(tm_size_str);
2263 config->wipe_intentionally_left_blank_rfork = lp_parm_bool(
2264 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2265 "wipe_intentionally_left_blank_rfork", false);
2267 config->delete_empty_adfiles = lp_parm_bool(
2268 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2269 "delete_empty_adfiles", false);
2271 SMB_VFS_HANDLE_SET_DATA(handle, config,
2272 NULL, struct fruit_config_data,
2279 * Prepend "._" to a basename
2280 * Return a new struct smb_filename with stream_name == NULL.
2282 static int adouble_path(TALLOC_CTX *ctx,
2283 const struct smb_filename *smb_fname_in,
2284 struct smb_filename **pp_smb_fname_out)
2288 struct smb_filename *smb_fname = cp_smb_filename(ctx,
2291 if (smb_fname == NULL) {
2295 /* We need streamname to be NULL */
2296 TALLOC_FREE(smb_fname->stream_name);
2298 /* And we're replacing base_name. */
2299 TALLOC_FREE(smb_fname->base_name);
2301 if (!parent_dirname(smb_fname, smb_fname_in->base_name,
2303 TALLOC_FREE(smb_fname);
2307 smb_fname->base_name = talloc_asprintf(smb_fname,
2308 "%s/._%s", parent, base);
2309 if (smb_fname->base_name == NULL) {
2310 TALLOC_FREE(smb_fname);
2314 *pp_smb_fname_out = smb_fname;
2320 * Allocate and initialize an AfpInfo struct
2322 static AfpInfo *afpinfo_new(TALLOC_CTX *ctx)
2324 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2328 ai->afpi_Signature = AFP_Signature;
2329 ai->afpi_Version = AFP_Version;
2330 ai->afpi_BackupTime = AD_DATE_START;
2335 * Pack an AfpInfo struct into a buffer
2337 * Buffer size must be at least AFP_INFO_SIZE
2338 * Returns size of packed buffer
2340 static ssize_t afpinfo_pack(const AfpInfo *ai, char *buf)
2342 memset(buf, 0, AFP_INFO_SIZE);
2344 RSIVAL(buf, 0, ai->afpi_Signature);
2345 RSIVAL(buf, 4, ai->afpi_Version);
2346 RSIVAL(buf, 12, ai->afpi_BackupTime);
2347 memcpy(buf + 16, ai->afpi_FinderInfo, sizeof(ai->afpi_FinderInfo));
2349 return AFP_INFO_SIZE;
2353 * Unpack a buffer into a AfpInfo structure
2355 * Buffer size must be at least AFP_INFO_SIZE
2356 * Returns allocated AfpInfo struct
2358 static AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data)
2360 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2365 ai->afpi_Signature = RIVAL(data, 0);
2366 ai->afpi_Version = RIVAL(data, 4);
2367 ai->afpi_BackupTime = RIVAL(data, 12);
2368 memcpy(ai->afpi_FinderInfo, (const char *)data + 16,
2369 sizeof(ai->afpi_FinderInfo));
2371 if (ai->afpi_Signature != AFP_Signature
2372 || ai->afpi_Version != AFP_Version) {
2373 DEBUG(1, ("Bad AfpInfo signature or version\n"));
2381 * Fake an inode number from the md5 hash of the (xattr) name
2383 static SMB_INO_T fruit_inode(const SMB_STRUCT_STAT *sbuf, const char *sname)
2385 gnutls_hash_hd_t hash_hnd = NULL;
2386 unsigned char hash[16];
2387 SMB_INO_T result = 0;
2391 DBG_DEBUG("fruit_inode called for %ju/%ju [%s]\n",
2392 (uintmax_t)sbuf->st_ex_dev,
2393 (uintmax_t)sbuf->st_ex_ino, sname);
2395 upper_sname = talloc_strdup_upper(talloc_tos(), sname);
2396 SMB_ASSERT(upper_sname != NULL);
2398 rc = gnutls_hash_init(&hash_hnd, GNUTLS_DIG_MD5);
2403 rc = gnutls_hash(hash_hnd, &(sbuf->st_ex_dev), sizeof(sbuf->st_ex_dev));
2405 gnutls_hash_deinit(hash_hnd, NULL);
2408 rc = gnutls_hash(hash_hnd,
2410 sizeof(sbuf->st_ex_ino));
2412 gnutls_hash_deinit(hash_hnd, NULL);
2415 rc = gnutls_hash(hash_hnd,
2417 talloc_get_size(upper_sname) - 1);
2419 gnutls_hash_deinit(hash_hnd, NULL);
2423 gnutls_hash_deinit(hash_hnd, hash);
2425 /* Hopefully all the variation is in the lower 4 (or 8) bytes! */
2426 memcpy(&result, hash, sizeof(result));
2429 DBG_DEBUG("fruit_inode \"%s\": ino=%ju\n",
2430 sname, (uintmax_t)result);
2433 TALLOC_FREE(upper_sname);
2438 static bool add_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
2439 struct stream_struct **streams,
2440 const char *name, off_t size,
2443 struct stream_struct *tmp;
2445 tmp = talloc_realloc(mem_ctx, *streams, struct stream_struct,
2451 tmp[*num_streams].name = talloc_asprintf(tmp, "%s:$DATA", name);
2452 if (tmp[*num_streams].name == NULL) {
2456 tmp[*num_streams].size = size;
2457 tmp[*num_streams].alloc_size = alloc_size;
2464 static bool filter_empty_rsrc_stream(unsigned int *num_streams,
2465 struct stream_struct **streams)
2467 struct stream_struct *tmp = *streams;
2470 if (*num_streams == 0) {
2474 for (i = 0; i < *num_streams; i++) {
2475 if (strequal_m(tmp[i].name, AFPRESOURCE_STREAM)) {
2480 if (i == *num_streams) {
2484 if (tmp[i].size > 0) {
2488 TALLOC_FREE(tmp[i].name);
2489 if (*num_streams - 1 > i) {
2490 memmove(&tmp[i], &tmp[i+1],
2491 (*num_streams - i - 1) * sizeof(struct stream_struct));
2498 static bool del_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
2499 struct stream_struct **streams,
2502 struct stream_struct *tmp = *streams;
2505 if (*num_streams == 0) {
2509 for (i = 0; i < *num_streams; i++) {
2510 if (strequal_m(tmp[i].name, name)) {
2515 if (i == *num_streams) {
2519 TALLOC_FREE(tmp[i].name);
2520 if (*num_streams - 1 > i) {
2521 memmove(&tmp[i], &tmp[i+1],
2522 (*num_streams - i - 1) * sizeof(struct stream_struct));
2529 static bool ad_empty_finderinfo(const struct adouble *ad)
2532 char emptybuf[ADEDLEN_FINDERI] = {0};
2535 fi = ad_get_entry(ad, ADEID_FINDERI);
2537 DBG_ERR("Missing FinderInfo in struct adouble [%p]\n", ad);
2541 cmp = memcmp(emptybuf, fi, ADEDLEN_FINDERI);
2545 static bool ai_empty_finderinfo(const AfpInfo *ai)
2548 char emptybuf[ADEDLEN_FINDERI] = {0};
2550 cmp = memcmp(emptybuf, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
2555 * Update btime with btime from Netatalk
2557 static void update_btime(vfs_handle_struct *handle,
2558 struct smb_filename *smb_fname)
2561 struct timespec creation_time = {0};
2563 struct fruit_config_data *config = NULL;
2565 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
2568 switch (config->meta) {
2569 case FRUIT_META_STREAM:
2571 case FRUIT_META_NETATALK:
2575 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
2579 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
2583 if (ad_getdate(ad, AD_DATE_UNIX | AD_DATE_CREATE, &t) != 0) {
2589 creation_time.tv_sec = convert_uint32_t_to_time_t(t);
2590 update_stat_ex_create_time(&smb_fname->st, creation_time);
2596 * Map an access mask to a Netatalk single byte byte range lock
2598 static off_t access_to_netatalk_brl(enum apple_fork fork_type,
2599 uint32_t access_mask)
2603 switch (access_mask) {
2604 case FILE_READ_DATA:
2605 offset = AD_FILELOCK_OPEN_RD;
2608 case FILE_WRITE_DATA:
2609 case FILE_APPEND_DATA:
2610 offset = AD_FILELOCK_OPEN_WR;
2614 offset = AD_FILELOCK_OPEN_NONE;
2618 if (fork_type == APPLE_FORK_RSRC) {
2619 if (offset == AD_FILELOCK_OPEN_NONE) {
2620 offset = AD_FILELOCK_RSRC_OPEN_NONE;
2630 * Map a deny mode to a Netatalk brl
2632 static off_t denymode_to_netatalk_brl(enum apple_fork fork_type,
2637 switch (deny_mode) {
2639 offset = AD_FILELOCK_DENY_RD;
2643 offset = AD_FILELOCK_DENY_WR;
2647 smb_panic("denymode_to_netatalk_brl: bad deny mode\n");
2650 if (fork_type == APPLE_FORK_RSRC) {
2658 * Call fcntl() with an exclusive F_GETLK request in order to
2659 * determine if there's an exisiting shared lock
2661 * @return true if the requested lock was found or any error occurred
2662 * false if the lock was not found
2664 static bool test_netatalk_lock(files_struct *fsp, off_t in_offset)
2667 off_t offset = in_offset;
2672 result = SMB_VFS_GETLOCK(fsp, &offset, &len, &type, &pid);
2673 if (result == false) {
2677 if (type != F_UNLCK) {
2684 static NTSTATUS fruit_check_access(vfs_handle_struct *handle,
2686 uint32_t access_mask,
2687 uint32_t share_mode)
2689 NTSTATUS status = NT_STATUS_OK;
2691 bool share_for_read = (share_mode & FILE_SHARE_READ);
2692 bool share_for_write = (share_mode & FILE_SHARE_WRITE);
2693 bool netatalk_already_open_for_reading = false;
2694 bool netatalk_already_open_for_writing = false;
2695 bool netatalk_already_open_with_deny_read = false;
2696 bool netatalk_already_open_with_deny_write = false;
2698 /* FIXME: hardcoded data fork, add resource fork */
2699 enum apple_fork fork_type = APPLE_FORK_DATA;
2701 DBG_DEBUG("fruit_check_access: %s, am: %s/%s, sm: 0x%x\n",
2703 access_mask & FILE_READ_DATA ? "READ" :"-",
2704 access_mask & FILE_WRITE_DATA ? "WRITE" : "-",
2707 if (fsp->fh->fd == -1) {
2708 return NT_STATUS_OK;
2711 /* Read NetATalk opens and deny modes on the file. */
2712 netatalk_already_open_for_reading = test_netatalk_lock(fsp,
2713 access_to_netatalk_brl(fork_type,
2716 netatalk_already_open_with_deny_read = test_netatalk_lock(fsp,
2717 denymode_to_netatalk_brl(fork_type,
2720 netatalk_already_open_for_writing = test_netatalk_lock(fsp,
2721 access_to_netatalk_brl(fork_type,
2724 netatalk_already_open_with_deny_write = test_netatalk_lock(fsp,
2725 denymode_to_netatalk_brl(fork_type,
2728 /* If there are any conflicts - sharing violation. */
2729 if ((access_mask & FILE_READ_DATA) &&
2730 netatalk_already_open_with_deny_read) {
2731 return NT_STATUS_SHARING_VIOLATION;
2734 if (!share_for_read &&
2735 netatalk_already_open_for_reading) {
2736 return NT_STATUS_SHARING_VIOLATION;
2739 if ((access_mask & FILE_WRITE_DATA) &&
2740 netatalk_already_open_with_deny_write) {
2741 return NT_STATUS_SHARING_VIOLATION;
2744 if (!share_for_write &&
2745 netatalk_already_open_for_writing) {
2746 return NT_STATUS_SHARING_VIOLATION;
2749 if (!(access_mask & FILE_READ_DATA)) {
2751 * Nothing we can do here, we need read access
2754 return NT_STATUS_OK;
2757 /* Set NetAtalk locks matching our access */
2758 if (access_mask & FILE_READ_DATA) {
2759 struct byte_range_lock *br_lck = NULL;
2761 off = access_to_netatalk_brl(fork_type, FILE_READ_DATA);
2763 handle->conn->sconn->msg_ctx, fsp,
2764 fsp->op->global->open_persistent_id, 1, off,
2765 READ_LOCK, POSIX_LOCK, false,
2768 TALLOC_FREE(br_lck);
2770 if (!NT_STATUS_IS_OK(status)) {
2775 if (!share_for_read) {
2776 struct byte_range_lock *br_lck = NULL;
2778 off = denymode_to_netatalk_brl(fork_type, DENY_READ);
2780 handle->conn->sconn->msg_ctx, fsp,
2781 fsp->op->global->open_persistent_id, 1, off,
2782 READ_LOCK, POSIX_LOCK, false,
2785 TALLOC_FREE(br_lck);
2787 if (!NT_STATUS_IS_OK(status)) {
2792 if (access_mask & FILE_WRITE_DATA) {
2793 struct byte_range_lock *br_lck = NULL;
2795 off = access_to_netatalk_brl(fork_type, FILE_WRITE_DATA);
2797 handle->conn->sconn->msg_ctx, fsp,
2798 fsp->op->global->open_persistent_id, 1, off,
2799 READ_LOCK, POSIX_LOCK, false,
2802 TALLOC_FREE(br_lck);
2804 if (!NT_STATUS_IS_OK(status)) {
2809 if (!share_for_write) {
2810 struct byte_range_lock *br_lck = NULL;
2812 off = denymode_to_netatalk_brl(fork_type, DENY_WRITE);
2814 handle->conn->sconn->msg_ctx, fsp,
2815 fsp->op->global->open_persistent_id, 1, off,
2816 READ_LOCK, POSIX_LOCK, false,
2819 TALLOC_FREE(br_lck);
2821 if (!NT_STATUS_IS_OK(status)) {
2826 return NT_STATUS_OK;
2829 static NTSTATUS check_aapl(vfs_handle_struct *handle,
2830 struct smb_request *req,
2831 const struct smb2_create_blobs *in_context_blobs,
2832 struct smb2_create_blobs *out_context_blobs)
2834 struct fruit_config_data *config;
2836 struct smb2_create_blob *aapl = NULL;
2840 DATA_BLOB blob = data_blob_talloc(req, NULL, 0);
2841 uint64_t req_bitmap, client_caps;
2842 uint64_t server_caps = SMB2_CRTCTX_AAPL_UNIX_BASED;
2846 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
2847 return NT_STATUS_UNSUCCESSFUL);
2849 if (!config->use_aapl
2850 || in_context_blobs == NULL
2851 || out_context_blobs == NULL) {
2852 return NT_STATUS_OK;
2855 aapl = smb2_create_blob_find(in_context_blobs,
2856 SMB2_CREATE_TAG_AAPL);
2858 return NT_STATUS_OK;
2861 if (aapl->data.length != 24) {
2862 DEBUG(1, ("unexpected AAPL ctxt length: %ju\n",
2863 (uintmax_t)aapl->data.length));
2864 return NT_STATUS_INVALID_PARAMETER;
2867 cmd = IVAL(aapl->data.data, 0);
2868 if (cmd != SMB2_CRTCTX_AAPL_SERVER_QUERY) {
2869 DEBUG(1, ("unsupported AAPL cmd: %d\n", cmd));
2870 return NT_STATUS_INVALID_PARAMETER;
2873 req_bitmap = BVAL(aapl->data.data, 8);
2874 client_caps = BVAL(aapl->data.data, 16);
2876 SIVAL(p, 0, SMB2_CRTCTX_AAPL_SERVER_QUERY);
2878 SBVAL(p, 8, req_bitmap);
2879 ok = data_blob_append(req, &blob, p, 16);
2881 return NT_STATUS_UNSUCCESSFUL;
2884 if (req_bitmap & SMB2_CRTCTX_AAPL_SERVER_CAPS) {
2885 if ((client_caps & SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR) &&
2886 (handle->conn->tcon->compat->fs_capabilities & FILE_NAMED_STREAMS)) {
2887 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR;
2888 config->readdir_attr_enabled = true;
2891 if (config->use_copyfile) {
2892 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_OSX_COPYFILE;
2893 config->copyfile_enabled = true;
2897 * The client doesn't set the flag, so we can't check
2898 * for it and just set it unconditionally
2900 if (config->unix_info_enabled) {
2901 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_NFS_ACE;
2904 SBVAL(p, 0, server_caps);
2905 ok = data_blob_append(req, &blob, p, 8);
2907 return NT_STATUS_UNSUCCESSFUL;
2911 if (req_bitmap & SMB2_CRTCTX_AAPL_VOLUME_CAPS) {
2912 int val = lp_case_sensitive(SNUM(handle->conn->tcon->compat));
2920 caps |= SMB2_CRTCTX_AAPL_CASE_SENSITIVE;
2927 if (config->time_machine) {
2928 caps |= SMB2_CRTCTX_AAPL_FULL_SYNC;
2933 ok = data_blob_append(req, &blob, p, 8);
2935 return NT_STATUS_UNSUCCESSFUL;
2939 if (req_bitmap & SMB2_CRTCTX_AAPL_MODEL_INFO) {
2940 ok = convert_string_talloc(req,
2941 CH_UNIX, CH_UTF16LE,
2942 config->model, strlen(config->model),
2945 return NT_STATUS_UNSUCCESSFUL;
2949 SIVAL(p + 4, 0, modellen);
2950 ok = data_blob_append(req, &blob, p, 8);
2953 return NT_STATUS_UNSUCCESSFUL;
2956 ok = data_blob_append(req, &blob, model, modellen);
2959 return NT_STATUS_UNSUCCESSFUL;
2963 status = smb2_create_blob_add(out_context_blobs,
2965 SMB2_CREATE_TAG_AAPL,
2967 if (NT_STATUS_IS_OK(status)) {
2968 global_fruit_config.nego_aapl = true;
2969 if (config->aapl_zero_file_id) {
2970 aapl_force_zero_file_id(handle->conn->sconn);
2977 static bool readdir_attr_meta_finderi_stream(
2978 struct vfs_handle_struct *handle,
2979 const struct smb_filename *smb_fname,
2982 struct smb_filename *stream_name = NULL;
2983 files_struct *fsp = NULL;
2988 uint8_t buf[AFP_INFO_SIZE];
2990 stream_name = synthetic_smb_fname(talloc_tos(),
2991 smb_fname->base_name,
2992 AFPINFO_STREAM_NAME,
2993 NULL, smb_fname->flags);
2994 if (stream_name == NULL) {
2998 ret = SMB_VFS_STAT(handle->conn, stream_name);
3003 status = SMB_VFS_CREATE_FILE(
3004 handle->conn, /* conn */
3006 0, /* root_dir_fid */
3007 stream_name, /* fname */
3008 FILE_READ_DATA, /* access_mask */
3009 (FILE_SHARE_READ | FILE_SHARE_WRITE | /* share_access */
3011 FILE_OPEN, /* create_disposition*/
3012 0, /* create_options */
3013 0, /* file_attributes */
3014 INTERNAL_OPEN_ONLY, /* oplock_request */
3016 0, /* allocation_size */
3017 0, /* private_flags */
3022 NULL, NULL); /* create context */
3024 TALLOC_FREE(stream_name);
3026 if (!NT_STATUS_IS_OK(status)) {
3030 nread = SMB_VFS_PREAD(fsp, &buf[0], AFP_INFO_SIZE, 0);
3031 if (nread != AFP_INFO_SIZE) {
3032 DBG_ERR("short read [%s] [%zd/%d]\n",
3033 smb_fname_str_dbg(stream_name), nread, AFP_INFO_SIZE);
3038 memcpy(&ai->afpi_FinderInfo[0], &buf[AFP_OFF_FinderInfo],
3045 close_file(NULL, fsp, NORMAL_CLOSE);
3051 static bool readdir_attr_meta_finderi_netatalk(
3052 struct vfs_handle_struct *handle,
3053 const struct smb_filename *smb_fname,
3056 struct adouble *ad = NULL;
3059 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
3064 p = ad_get_entry(ad, ADEID_FINDERI);
3066 DBG_ERR("No ADEID_FINDERI for [%s]\n", smb_fname->base_name);
3071 memcpy(&ai->afpi_FinderInfo[0], p, AFP_FinderSize);
3076 static bool readdir_attr_meta_finderi(struct vfs_handle_struct *handle,
3077 const struct smb_filename *smb_fname,
3078 struct readdir_attr_data *attr_data)
3080 struct fruit_config_data *config = NULL;
3081 uint32_t date_added;
3085 SMB_VFS_HANDLE_GET_DATA(handle, config,
3086 struct fruit_config_data,
3089 switch (config->meta) {
3090 case FRUIT_META_NETATALK:
3091 ok = readdir_attr_meta_finderi_netatalk(
3092 handle, smb_fname, &ai);
3095 case FRUIT_META_STREAM:
3096 ok = readdir_attr_meta_finderi_stream(
3097 handle, smb_fname, &ai);
3101 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3106 /* Don't bother with errors, it's likely ENOENT */
3110 if (S_ISREG(smb_fname->st.st_ex_mode)) {
3112 memcpy(&attr_data->attr_data.aapl.finder_info[0],
3113 &ai.afpi_FinderInfo[0], 4);
3115 /* finder_creator */
3116 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 4,
3117 &ai.afpi_FinderInfo[4], 4);
3121 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 8,
3122 &ai.afpi_FinderInfo[8], 2);
3124 /* finder_ext_flags */
3125 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 10,
3126 &ai.afpi_FinderInfo[24], 2);
3129 date_added = convert_time_t_to_uint32_t(
3130 smb_fname->st.st_ex_btime.tv_sec - AD_DATE_DELTA);
3132 RSIVAL(&attr_data->attr_data.aapl.finder_info[0], 12, date_added);
3137 static uint64_t readdir_attr_rfork_size_adouble(
3138 struct vfs_handle_struct *handle,
3139 const struct smb_filename *smb_fname)
3141 struct adouble *ad = NULL;
3142 uint64_t rfork_size;
3144 ad = ad_get(talloc_tos(), handle, smb_fname,
3150 rfork_size = ad_getentrylen(ad, ADEID_RFORK);
3156 static uint64_t readdir_attr_rfork_size_stream(
3157 struct vfs_handle_struct *handle,
3158 const struct smb_filename *smb_fname)
3160 struct smb_filename *stream_name = NULL;
3162 uint64_t rfork_size;
3164 stream_name = synthetic_smb_fname(talloc_tos(),
3165 smb_fname->base_name,
3166 AFPRESOURCE_STREAM_NAME,
3168 if (stream_name == NULL) {
3172 ret = SMB_VFS_STAT(handle->conn, stream_name);
3174 TALLOC_FREE(stream_name);
3178 rfork_size = stream_name->st.st_ex_size;
3179 TALLOC_FREE(stream_name);
3184 static uint64_t readdir_attr_rfork_size(struct vfs_handle_struct *handle,
3185 const struct smb_filename *smb_fname)
3187 struct fruit_config_data *config = NULL;
3188 uint64_t rfork_size;
3190 SMB_VFS_HANDLE_GET_DATA(handle, config,
3191 struct fruit_config_data,
3194 switch (config->rsrc) {
3195 case FRUIT_RSRC_ADFILE:
3196 rfork_size = readdir_attr_rfork_size_adouble(handle,
3200 case FRUIT_RSRC_XATTR:
3201 case FRUIT_RSRC_STREAM:
3202 rfork_size = readdir_attr_rfork_size_stream(handle,
3207 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3215 static NTSTATUS readdir_attr_macmeta(struct vfs_handle_struct *handle,
3216 const struct smb_filename *smb_fname,
3217 struct readdir_attr_data *attr_data)
3219 NTSTATUS status = NT_STATUS_OK;
3220 struct fruit_config_data *config = NULL;
3223 SMB_VFS_HANDLE_GET_DATA(handle, config,
3224 struct fruit_config_data,
3225 return NT_STATUS_UNSUCCESSFUL);
3228 /* Ensure we return a default value in the creation_date field */
3229 RSIVAL(&attr_data->attr_data.aapl.finder_info, 12, AD_DATE_START);
3232 * Resource fork length
3235 if (config->readdir_attr_rsize) {
3236 uint64_t rfork_size;
3238 rfork_size = readdir_attr_rfork_size(handle, smb_fname);
3239 attr_data->attr_data.aapl.rfork_size = rfork_size;
3246 if (config->readdir_attr_finder_info) {
3247 ok = readdir_attr_meta_finderi(handle, smb_fname, attr_data);
3249 status = NT_STATUS_INTERNAL_ERROR;
3256 static NTSTATUS remove_virtual_nfs_aces(struct security_descriptor *psd)
3261 if (psd->dacl == NULL) {
3262 return NT_STATUS_OK;
3265 for (i = 0; i < psd->dacl->num_aces; i++) {
3266 /* MS NFS style mode/uid/gid */
3267 int cmp = dom_sid_compare_domain(
3268 &global_sid_Unix_NFS,
3269 &psd->dacl->aces[i].trustee);
3271 /* Normal ACE entry. */
3276 * security_descriptor_dacl_del()
3277 * *must* return NT_STATUS_OK as we know
3278 * we have something to remove.
3281 status = security_descriptor_dacl_del(psd,
3282 &psd->dacl->aces[i].trustee);
3283 if (!NT_STATUS_IS_OK(status)) {
3284 DBG_WARNING("failed to remove MS NFS style ACE: %s\n",
3290 * security_descriptor_dacl_del() may delete more
3291 * then one entry subsequent to this one if the
3292 * SID matches, but we only need to ensure that
3293 * we stay looking at the same element in the array.
3297 return NT_STATUS_OK;
3300 /* Search MS NFS style ACE with UNIX mode */
3301 static NTSTATUS check_ms_nfs(vfs_handle_struct *handle,
3303 struct security_descriptor *psd,
3308 struct fruit_config_data *config = NULL;
3312 SMB_VFS_HANDLE_GET_DATA(handle, config,
3313 struct fruit_config_data,
3314 return NT_STATUS_UNSUCCESSFUL);
3316 if (!global_fruit_config.nego_aapl) {
3317 return NT_STATUS_OK;
3319 if (psd->dacl == NULL || !config->unix_info_enabled) {
3320 return NT_STATUS_OK;
3323 for (i = 0; i < psd->dacl->num_aces; i++) {
3324 if (dom_sid_compare_domain(
3325 &global_sid_Unix_NFS_Mode,
3326 &psd->dacl->aces[i].trustee) == 0) {
3327 *pmode = (mode_t)psd->dacl->aces[i].trustee.sub_auths[2];
3328 *pmode &= (S_IRWXU | S_IRWXG | S_IRWXO);
3331 DEBUG(10, ("MS NFS chmod request %s, %04o\n",
3332 fsp_str_dbg(fsp), (unsigned)(*pmode)));
3338 * Remove any incoming virtual ACE entries generated by
3339 * fruit_fget_nt_acl().
3342 return remove_virtual_nfs_aces(psd);
3345 /****************************************************************************
3347 ****************************************************************************/
3349 static int fruit_connect(vfs_handle_struct *handle,
3350 const char *service,
3354 char *list = NULL, *newlist = NULL;
3355 struct fruit_config_data *config;
3357 DEBUG(10, ("fruit_connect\n"));
3359 rc = SMB_VFS_NEXT_CONNECT(handle, service, user);
3364 rc = init_fruit_config(handle);
3369 SMB_VFS_HANDLE_GET_DATA(handle, config,
3370 struct fruit_config_data, return -1);
3372 if (config->veto_appledouble) {
3373 list = lp_veto_files(talloc_tos(), SNUM(handle->conn));
3376 if (strstr(list, "/" ADOUBLE_NAME_PREFIX "*/") == NULL) {
3377 newlist = talloc_asprintf(
3379 "%s/" ADOUBLE_NAME_PREFIX "*/",
3381 lp_do_parameter(SNUM(handle->conn),
3386 lp_do_parameter(SNUM(handle->conn),
3388 "/" ADOUBLE_NAME_PREFIX "*/");
3394 if (config->encoding == FRUIT_ENC_NATIVE) {
3395 lp_do_parameter(SNUM(handle->conn),
3400 if (config->time_machine) {
3401 DBG_NOTICE("Enabling durable handles for Time Machine "
3402 "support on [%s]\n", service);
3403 lp_do_parameter(SNUM(handle->conn), "durable handles", "yes");
3404 lp_do_parameter(SNUM(handle->conn), "kernel oplocks", "no");
3405 lp_do_parameter(SNUM(handle->conn), "kernel share modes", "no");
3406 if (!lp_strict_sync(SNUM(handle->conn))) {
3407 DBG_WARNING("Time Machine without strict sync is not "
3410 lp_do_parameter(SNUM(handle->conn), "posix locking", "no");
3416 static int fruit_fake_fd(void)
3423 * Return a valid fd, but ensure any attempt to use it returns
3424 * an error (EPIPE). Once we get a write on the handle, we open
3427 ret = pipe(pipe_fds);
3437 static int fruit_open_meta_stream(vfs_handle_struct *handle,
3438 struct smb_filename *smb_fname,
3443 struct fruit_config_data *config = NULL;
3444 struct fio *fio = NULL;
3445 int open_flags = flags & ~O_CREAT;
3448 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3450 SMB_VFS_HANDLE_GET_DATA(handle, config,
3451 struct fruit_config_data, return -1);
3453 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3454 fio->type = ADOUBLE_META;
3455 fio->config = config;
3457 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, open_flags, mode);
3462 if (!(flags & O_CREAT)) {
3463 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
3467 fd = fruit_fake_fd();
3469 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
3473 fio->fake_fd = true;
3480 static int fruit_open_meta_netatalk(vfs_handle_struct *handle,
3481 struct smb_filename *smb_fname,
3486 struct fruit_config_data *config = NULL;
3487 struct fio *fio = NULL;
3488 struct adouble *ad = NULL;
3489 bool meta_exists = false;
3492 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3494 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
3501 if (!meta_exists && !(flags & O_CREAT)) {
3506 fd = fruit_fake_fd();
3511 SMB_VFS_HANDLE_GET_DATA(handle, config,
3512 struct fruit_config_data, return -1);
3514 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3515 fio->type = ADOUBLE_META;
3516 fio->config = config;
3517 fio->fake_fd = true;
3524 static int fruit_open_meta(vfs_handle_struct *handle,
3525 struct smb_filename *smb_fname,
3526 files_struct *fsp, int flags, mode_t mode)
3529 struct fruit_config_data *config = NULL;
3531 DBG_DEBUG("path [%s]\n", smb_fname_str_dbg(smb_fname));
3533 SMB_VFS_HANDLE_GET_DATA(handle, config,
3534 struct fruit_config_data, return -1);
3536 switch (config->meta) {
3537 case FRUIT_META_STREAM:
3538 fd = fruit_open_meta_stream(handle, smb_fname,
3542 case FRUIT_META_NETATALK:
3543 fd = fruit_open_meta_netatalk(handle, smb_fname,
3548 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3552 DBG_DEBUG("path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3557 static int fruit_open_rsrc_adouble(vfs_handle_struct *handle,
3558 struct smb_filename *smb_fname,
3564 struct adouble *ad = NULL;
3565 struct smb_filename *smb_fname_base = NULL;
3566 struct fruit_config_data *config = NULL;
3569 SMB_VFS_HANDLE_GET_DATA(handle, config,
3570 struct fruit_config_data, return -1);
3572 if ((!(flags & O_CREAT)) &&
3573 S_ISDIR(fsp->base_fsp->fsp_name->st.st_ex_mode))
3575 /* sorry, but directories don't habe a resource fork */
3580 rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_base);
3585 /* We always need read/write access for the metadata header too */
3586 flags &= ~(O_RDONLY | O_WRONLY);
3589 hostfd = SMB_VFS_NEXT_OPEN(handle, smb_fname_base, fsp,
3596 if (flags & (O_CREAT | O_TRUNC)) {
3597 ad = ad_init(fsp, ADOUBLE_RSRC);
3603 fsp->fh->fd = hostfd;
3605 rc = ad_fset(handle, ad, fsp);
3616 TALLOC_FREE(smb_fname_base);
3618 DEBUG(10, ("fruit_open resource fork: rc=%d, fd=%d\n", rc, hostfd));
3620 int saved_errno = errno;
3623 * BUGBUGBUG -- we would need to call
3624 * fd_close_posix here, but we don't have a
3627 fsp->fh->fd = hostfd;
3631 errno = saved_errno;
3636 static int fruit_open_rsrc_xattr(vfs_handle_struct *handle,
3637 struct smb_filename *smb_fname,
3642 #ifdef HAVE_ATTROPEN
3645 fd = attropen(smb_fname->base_name,
3646 AFPRESOURCE_EA_NETATALK,
3661 static int fruit_open_rsrc(vfs_handle_struct *handle,
3662 struct smb_filename *smb_fname,
3663 files_struct *fsp, int flags, mode_t mode)
3666 struct fruit_config_data *config = NULL;
3667 struct fio *fio = NULL;
3669 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3671 SMB_VFS_HANDLE_GET_DATA(handle, config,
3672 struct fruit_config_data, return -1);
3674 switch (config->rsrc) {
3675 case FRUIT_RSRC_STREAM:
3676 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3679 case FRUIT_RSRC_ADFILE:
3680 fd = fruit_open_rsrc_adouble(handle, smb_fname,
3684 case FRUIT_RSRC_XATTR:
3685 fd = fruit_open_rsrc_xattr(handle, smb_fname,
3690 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3694 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3700 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3701 fio->type = ADOUBLE_RSRC;
3702 fio->config = config;
3707 static int fruit_open(vfs_handle_struct *handle,
3708 struct smb_filename *smb_fname,
3709 files_struct *fsp, int flags, mode_t mode)
3713 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3715 if (!is_ntfs_stream_smb_fname(smb_fname)) {
3716 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3719 if (is_afpinfo_stream(smb_fname)) {
3720 fd = fruit_open_meta(handle, smb_fname, fsp, flags, mode);
3721 } else if (is_afpresource_stream(smb_fname)) {
3722 fd = fruit_open_rsrc(handle, smb_fname, fsp, flags, mode);
3724 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3727 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3732 static int fruit_close_meta(vfs_handle_struct *handle,
3736 struct fruit_config_data *config = NULL;
3738 SMB_VFS_HANDLE_GET_DATA(handle, config,
3739 struct fruit_config_data, return -1);
3741 switch (config->meta) {
3742 case FRUIT_META_STREAM:
3743 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3746 case FRUIT_META_NETATALK:
3747 ret = close(fsp->fh->fd);
3752 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3760 static int fruit_close_rsrc(vfs_handle_struct *handle,
3764 struct fruit_config_data *config = NULL;
3766 SMB_VFS_HANDLE_GET_DATA(handle, config,
3767 struct fruit_config_data, return -1);
3769 switch (config->rsrc) {
3770 case FRUIT_RSRC_STREAM:
3771 case FRUIT_RSRC_ADFILE:
3772 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3775 case FRUIT_RSRC_XATTR:
3776 ret = close(fsp->fh->fd);
3781 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3788 static int fruit_close(vfs_handle_struct *handle,
3796 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(fsp->fsp_name), fd);
3798 if (!is_ntfs_stream_smb_fname(fsp->fsp_name)) {
3799 return SMB_VFS_NEXT_CLOSE(handle, fsp);
3802 if (is_afpinfo_stream(fsp->fsp_name)) {
3803 ret = fruit_close_meta(handle, fsp);
3804 } else if (is_afpresource_stream(fsp->fsp_name)) {
3805 ret = fruit_close_rsrc(handle, fsp);
3807 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3813 static int fruit_rename(struct vfs_handle_struct *handle,
3814 const struct smb_filename *smb_fname_src,
3815 const struct smb_filename *smb_fname_dst)
3818 struct fruit_config_data *config = NULL;
3819 struct smb_filename *src_adp_smb_fname = NULL;
3820 struct smb_filename *dst_adp_smb_fname = NULL;
3822 SMB_VFS_HANDLE_GET_DATA(handle, config,
3823 struct fruit_config_data, return -1);
3825 if (!VALID_STAT(smb_fname_src->st)) {
3826 DBG_ERR("Need valid stat for [%s]\n",
3827 smb_fname_str_dbg(smb_fname_src));
3831 rc = SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
3836 if ((config->rsrc != FRUIT_RSRC_ADFILE) ||
3837 (!S_ISREG(smb_fname_src->st.st_ex_mode)))
3842 rc = adouble_path(talloc_tos(), smb_fname_src, &src_adp_smb_fname);
3847 rc = adouble_path(talloc_tos(), smb_fname_dst, &dst_adp_smb_fname);
3852 DBG_DEBUG("%s -> %s\n",
3853 smb_fname_str_dbg(src_adp_smb_fname),
3854 smb_fname_str_dbg(dst_adp_smb_fname));
3856 rc = SMB_VFS_NEXT_RENAME(handle, src_adp_smb_fname, dst_adp_smb_fname);
3857 if (errno == ENOENT) {
3862 TALLOC_FREE(src_adp_smb_fname);
3863 TALLOC_FREE(dst_adp_smb_fname);
3867 static int fruit_unlink_meta_stream(vfs_handle_struct *handle,
3868 const struct smb_filename *smb_fname)
3870 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3873 static int fruit_unlink_meta_netatalk(vfs_handle_struct *handle,
3874 const struct smb_filename *smb_fname)
3876 return SMB_VFS_REMOVEXATTR(handle->conn,
3878 AFPINFO_EA_NETATALK);
3881 static int fruit_unlink_meta(vfs_handle_struct *handle,
3882 const struct smb_filename *smb_fname)
3884 struct fruit_config_data *config = NULL;
3887 SMB_VFS_HANDLE_GET_DATA(handle, config,
3888 struct fruit_config_data, return -1);
3890 switch (config->meta) {
3891 case FRUIT_META_STREAM:
3892 rc = fruit_unlink_meta_stream(handle, smb_fname);
3895 case FRUIT_META_NETATALK:
3896 rc = fruit_unlink_meta_netatalk(handle, smb_fname);
3900 DBG_ERR("Unsupported meta config [%d]\n", config->meta);
3907 static int fruit_unlink_rsrc_stream(vfs_handle_struct *handle,
3908 const struct smb_filename *smb_fname,
3913 if (!force_unlink) {
3914 struct smb_filename *smb_fname_cp = NULL;
3917 smb_fname_cp = cp_smb_filename(talloc_tos(), smb_fname);
3918 if (smb_fname_cp == NULL) {
3923 * 0 byte resource fork streams are not listed by
3924 * vfs_streaminfo, as a result stream cleanup/deletion of file
3925 * deletion doesn't remove the resourcefork stream.
3928 ret = SMB_VFS_NEXT_STAT(handle, smb_fname_cp);
3930 TALLOC_FREE(smb_fname_cp);
3931 DBG_ERR("stat [%s] failed [%s]\n",
3932 smb_fname_str_dbg(smb_fname_cp), strerror(errno));
3936 size = smb_fname_cp->st.st_ex_size;
3937 TALLOC_FREE(smb_fname_cp);
3940 /* OS X ignores resource fork stream delete requests */
3945 ret = SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3946 if ((ret != 0) && (errno == ENOENT) && force_unlink) {
3953 static int fruit_unlink_rsrc_adouble(vfs_handle_struct *handle,
3954 const struct smb_filename *smb_fname,
3958 struct adouble *ad = NULL;
3959 struct smb_filename *adp_smb_fname = NULL;
3961 if (!force_unlink) {
3962 ad = ad_get(talloc_tos(), handle, smb_fname,
3971 * 0 byte resource fork streams are not listed by
3972 * vfs_streaminfo, as a result stream cleanup/deletion of file
3973 * deletion doesn't remove the resourcefork stream.
3976 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
3977 /* OS X ignores resource fork stream delete requests */
3985 rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
3990 rc = SMB_VFS_NEXT_UNLINK(handle, adp_smb_fname);
3991 TALLOC_FREE(adp_smb_fname);
3992 if ((rc != 0) && (errno == ENOENT) && force_unlink) {
3999 static int fruit_unlink_rsrc_xattr(vfs_handle_struct *handle,
4000 const struct smb_filename *smb_fname,
4004 * OS X ignores resource fork stream delete requests, so nothing to do
4005 * here. Removing the file will remove the xattr anyway, so we don't
4006 * have to take care of removing 0 byte resource forks that could be
4012 static int fruit_unlink_rsrc(vfs_handle_struct *handle,
4013 const struct smb_filename *smb_fname,
4016 struct fruit_config_data *config = NULL;
4019 SMB_VFS_HANDLE_GET_DATA(handle, config,
4020 struct fruit_config_data, return -1);
4022 switch (config->rsrc) {
4023 case FRUIT_RSRC_STREAM:
4024 rc = fruit_unlink_rsrc_stream(handle, smb_fname, force_unlink);
4027 case FRUIT_RSRC_ADFILE:
4028 rc = fruit_unlink_rsrc_adouble(handle, smb_fname, force_unlink);
4031 case FRUIT_RSRC_XATTR:
4032 rc = fruit_unlink_rsrc_xattr(handle, smb_fname, force_unlink);
4036 DBG_ERR("Unsupported rsrc config [%d]\n", config->rsrc);
4043 static int fruit_unlink(vfs_handle_struct *handle,
4044 const struct smb_filename *smb_fname)
4047 struct fruit_config_data *config = NULL;
4048 struct smb_filename *rsrc_smb_fname = NULL;
4050 SMB_VFS_HANDLE_GET_DATA(handle, config,
4051 struct fruit_config_data, return -1);
4053 if (is_afpinfo_stream(smb_fname)) {
4054 return fruit_unlink_meta(handle, smb_fname);
4055 } else if (is_afpresource_stream(smb_fname)) {
4056 return fruit_unlink_rsrc(handle, smb_fname, false);
4057 } else if (is_ntfs_stream_smb_fname(smb_fname)) {
4058 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4059 } else if (is_adouble_file(smb_fname->base_name)) {
4060 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4064 * A request to delete the base file. Because 0 byte resource
4065 * fork streams are not listed by fruit_streaminfo,
4066 * delete_all_streams() can't remove 0 byte resource fork
4067 * streams, so we have to cleanup this here.
4069 rsrc_smb_fname = synthetic_smb_fname(talloc_tos(),
4070 smb_fname->base_name,
4071 AFPRESOURCE_STREAM_NAME,
4074 if (rsrc_smb_fname == NULL) {
4078 rc = fruit_unlink_rsrc(handle, rsrc_smb_fname, true);
4079 if ((rc != 0) && (errno != ENOENT)) {
4080 DBG_ERR("Forced unlink of [%s] failed [%s]\n",
4081 smb_fname_str_dbg(rsrc_smb_fname), strerror(errno));
4082 TALLOC_FREE(rsrc_smb_fname);
4085 TALLOC_FREE(rsrc_smb_fname);
4087 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4090 static int fruit_chmod(vfs_handle_struct *handle,
4091 const struct smb_filename *smb_fname,
4095 struct fruit_config_data *config = NULL;
4096 struct smb_filename *smb_fname_adp = NULL;
4098 rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname, mode);
4103 SMB_VFS_HANDLE_GET_DATA(handle, config,
4104 struct fruit_config_data, return -1);
4106 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4110 if (!VALID_STAT(smb_fname->st)) {
4114 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
4118 rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_adp);
4123 DEBUG(10, ("fruit_chmod: %s\n", smb_fname_adp->base_name));
4125 rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname_adp, mode);
4126 if (errno == ENOENT) {
4130 TALLOC_FREE(smb_fname_adp);
4134 static int fruit_chown(vfs_handle_struct *handle,
4135 const struct smb_filename *smb_fname,
4140 struct fruit_config_data *config = NULL;
4141 struct smb_filename *adp_smb_fname = NULL;
4143 rc = SMB_VFS_NEXT_CHOWN(handle, smb_fname, uid, gid);
4148 SMB_VFS_HANDLE_GET_DATA(handle, config,
4149 struct fruit_config_data, return -1);
4151 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4155 if (!VALID_STAT(smb_fname->st)) {
4159 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
4163 rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
4168 DEBUG(10, ("fruit_chown: %s\n", adp_smb_fname->base_name));
4170 rc = SMB_VFS_NEXT_CHOWN(handle, adp_smb_fname, uid, gid);
4171 if (errno == ENOENT) {
4176 TALLOC_FREE(adp_smb_fname);
4180 static int fruit_rmdir(struct vfs_handle_struct *handle,
4181 const struct smb_filename *smb_fname)
4185 struct fruit_config_data *config;
4187 SMB_VFS_HANDLE_GET_DATA(handle, config,
4188 struct fruit_config_data, return -1);
4190 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4195 * Due to there is no way to change bDeleteVetoFiles variable
4196 * from this module, need to clean up ourselves
4199 dh = SMB_VFS_OPENDIR(handle->conn, smb_fname, NULL, 0);
4204 while ((de = SMB_VFS_READDIR(handle->conn, dh, NULL)) != NULL) {
4205 struct adouble *ad = NULL;
4207 struct smb_filename *ad_smb_fname = NULL;
4210 if (!is_adouble_file(de->d_name)) {
4214 p = talloc_asprintf(talloc_tos(), "%s/%s",
4215 smb_fname->base_name, de->d_name);
4217 DBG_ERR("talloc_asprintf failed\n");
4221 ad_smb_fname = synthetic_smb_fname(talloc_tos(), p,
4225 if (ad_smb_fname == NULL) {
4226 DBG_ERR("synthetic_smb_fname failed\n");
4231 * Check whether it's a valid AppleDouble file, if
4232 * yes, delete it, ignore it otherwise.
4234 ad = ad_get(talloc_tos(), handle, ad_smb_fname, ADOUBLE_RSRC);
4236 TALLOC_FREE(ad_smb_fname);
4242 ret = SMB_VFS_NEXT_UNLINK(handle, ad_smb_fname);
4244 DBG_ERR("Deleting [%s] failed\n",
4245 smb_fname_str_dbg(ad_smb_fname));
4247 TALLOC_FREE(ad_smb_fname);
4252 SMB_VFS_CLOSEDIR(handle->conn, dh);
4254 return SMB_VFS_NEXT_RMDIR(handle, smb_fname);
4257 static ssize_t fruit_pread_meta_stream(vfs_handle_struct *handle,
4258 files_struct *fsp, void *data,
4259 size_t n, off_t offset)
4264 nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4265 if (nread == -1 || nread == n) {
4269 DBG_ERR("Removing [%s] after short read [%zd]\n",
4270 fsp_str_dbg(fsp), nread);
4272 ret = SMB_VFS_NEXT_UNLINK(handle, fsp->fsp_name);
4274 DBG_ERR("Removing [%s] failed\n", fsp_str_dbg(fsp));
4282 static ssize_t fruit_pread_meta_adouble(vfs_handle_struct *handle,
4283 files_struct *fsp, void *data,
4284 size_t n, off_t offset)
4287 struct adouble *ad = NULL;
4288 char afpinfo_buf[AFP_INFO_SIZE];
4292 ai = afpinfo_new(talloc_tos());
4297 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
4303 p = ad_get_entry(ad, ADEID_FINDERI);
4305 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
4310 memcpy(&ai->afpi_FinderInfo[0], p, ADEDLEN_FINDERI);
4312 nread = afpinfo_pack(ai, afpinfo_buf);
4313 if (nread != AFP_INFO_SIZE) {
4318 memcpy(data, afpinfo_buf, n);
4326 static ssize_t fruit_pread_meta(vfs_handle_struct *handle,
4327 files_struct *fsp, void *data,
4328 size_t n, off_t offset)
4330 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4335 * OS X has a off-by-1 error in the offset calculation, so we're
4336 * bug compatible here. It won't hurt, as any relevant real
4337 * world read requests from the AFP_AfpInfo stream will be
4338 * offset=0 n=60. offset is ignored anyway, see below.
4340 if ((offset < 0) || (offset >= AFP_INFO_SIZE + 1)) {
4345 DBG_ERR("Failed to fetch fsp extension");
4349 /* Yes, macOS always reads from offset 0 */
4351 to_return = MIN(n, AFP_INFO_SIZE);
4353 switch (fio->config->meta) {
4354 case FRUIT_META_STREAM:
4355 nread = fruit_pread_meta_stream(handle, fsp, data,
4359 case FRUIT_META_NETATALK:
4360 nread = fruit_pread_meta_adouble(handle, fsp, data,
4365 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
4369 if (nread == -1 && fio->created) {
4371 char afpinfo_buf[AFP_INFO_SIZE];
4373 ai = afpinfo_new(talloc_tos());
4378 nread = afpinfo_pack(ai, afpinfo_buf);
4380 if (nread != AFP_INFO_SIZE) {
4384 memcpy(data, afpinfo_buf, to_return);
4391 static ssize_t fruit_pread_rsrc_stream(vfs_handle_struct *handle,
4392 files_struct *fsp, void *data,
4393 size_t n, off_t offset)
4395 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4398 static ssize_t fruit_pread_rsrc_xattr(vfs_handle_struct *handle,
4399 files_struct *fsp, void *data,
4400 size_t n, off_t offset)
4402 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4405 static ssize_t fruit_pread_rsrc_adouble(vfs_handle_struct *handle,
4406 files_struct *fsp, void *data,
4407 size_t n, off_t offset)
4409 struct adouble *ad = NULL;
4412 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
4417 nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n,
4418 offset + ad_getentryoff(ad, ADEID_RFORK));
4424 static ssize_t fruit_pread_rsrc(vfs_handle_struct *handle,
4425 files_struct *fsp, void *data,
4426 size_t n, off_t offset)
4428 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4436 switch (fio->config->rsrc) {
4437 case FRUIT_RSRC_STREAM:
4438 nread = fruit_pread_rsrc_stream(handle, fsp, data, n, offset);
4441 case FRUIT_RSRC_ADFILE:
4442 nread = fruit_pread_rsrc_adouble(handle, fsp, data, n, offset);
4445 case FRUIT_RSRC_XATTR:
4446 nread = fruit_pread_rsrc_xattr(handle, fsp, data, n, offset);
4450 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
4457 static ssize_t fruit_pread(vfs_handle_struct *handle,
4458 files_struct *fsp, void *data,
4459 size_t n, off_t offset)
4461 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4464 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4465 fsp_str_dbg(fsp), (intmax_t)offset, n);
4468 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4471 if (fio->type == ADOUBLE_META) {
4472 nread = fruit_pread_meta(handle, fsp, data, n, offset);
4474 nread = fruit_pread_rsrc(handle, fsp, data, n, offset);
4477 DBG_DEBUG("Path [%s] nread [%zd]\n", fsp_str_dbg(fsp), nread);
4481 static bool fruit_must_handle_aio_stream(struct fio *fio)
4487 if (fio->type == ADOUBLE_META) {
4491 if ((fio->type == ADOUBLE_RSRC) &&
4492 (fio->config->rsrc == FRUIT_RSRC_ADFILE))
4500 struct fruit_pread_state {
4502 struct vfs_aio_state vfs_aio_state;
4505 static void fruit_pread_done(struct tevent_req *subreq);
4507 static struct tevent_req *fruit_pread_send(
4508 struct vfs_handle_struct *handle,
4509 TALLOC_CTX *mem_ctx,
4510 struct tevent_context *ev,
4511 struct files_struct *fsp,
4513 size_t n, off_t offset)
4515 struct tevent_req *req = NULL;
4516 struct tevent_req *subreq = NULL;
4517 struct fruit_pread_state *state = NULL;
4518 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4520 req = tevent_req_create(mem_ctx, &state,
4521 struct fruit_pread_state);
4526 if (fruit_must_handle_aio_stream(fio)) {
4527 state->nread = SMB_VFS_PREAD(fsp, data, n, offset);
4528 if (state->nread != n) {
4529 if (state->nread != -1) {
4532 tevent_req_error(req, errno);
4533 return tevent_req_post(req, ev);
4535 tevent_req_done(req);
4536 return tevent_req_post(req, ev);
4539 subreq = SMB_VFS_NEXT_PREAD_SEND(state, ev, handle, fsp,
4541 if (tevent_req_nomem(req, subreq)) {
4542 return tevent_req_post(req, ev);
4544 tevent_req_set_callback(subreq, fruit_pread_done, req);
4548 static void fruit_pread_done(struct tevent_req *subreq)
4550 struct tevent_req *req = tevent_req_callback_data(
4551 subreq, struct tevent_req);
4552 struct fruit_pread_state *state = tevent_req_data(
4553 req, struct fruit_pread_state);
4555 state->nread = SMB_VFS_PREAD_RECV(subreq, &state->vfs_aio_state);
4556 TALLOC_FREE(subreq);
4558 if (tevent_req_error(req, state->vfs_aio_state.error)) {
4561 tevent_req_done(req);
4564 static ssize_t fruit_pread_recv(struct tevent_req *req,
4565 struct vfs_aio_state *vfs_aio_state)
4567 struct fruit_pread_state *state = tevent_req_data(
4568 req, struct fruit_pread_state);
4570 if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
4574 *vfs_aio_state = state->vfs_aio_state;
4575 return state->nread;
4578 static ssize_t fruit_pwrite_meta_stream(vfs_handle_struct *handle,
4579 files_struct *fsp, const void *data,
4580 size_t n, off_t offset)
4582 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4588 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4589 fsp_str_dbg(fsp), (intmax_t)offset, n);
4598 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
4600 DBG_ERR("Close [%s] failed: %s\n",
4601 fsp_str_dbg(fsp), strerror(errno));
4606 fd = SMB_VFS_NEXT_OPEN(handle,
4612 DBG_ERR("On-demand create [%s] in write failed: %s\n",
4613 fsp_str_dbg(fsp), strerror(errno));
4617 fio->fake_fd = false;
4620 ai = afpinfo_unpack(talloc_tos(), data);
4625 if (ai_empty_finderinfo(ai)) {
4627 * Writing an all 0 blob to the metadata stream results in the
4628 * stream being removed on a macOS server. This ensures we
4629 * behave the same and it verified by the "delete AFP_AfpInfo by
4630 * writing all 0" test.
4632 ret = SMB_VFS_NEXT_FTRUNCATE(handle, fsp, 0);
4634 DBG_ERR("SMB_VFS_NEXT_FTRUNCATE on [%s] failed\n",
4639 ok = set_delete_on_close(
4642 handle->conn->session_info->security_token,
4643 handle->conn->session_info->unix_token);
4645 DBG_ERR("set_delete_on_close on [%s] failed\n",
4652 nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4653 if (nwritten != n) {
4660 static ssize_t fruit_pwrite_meta_netatalk(vfs_handle_struct *handle,
4661 files_struct *fsp, const void *data,
4662 size_t n, off_t offset)
4664 struct adouble *ad = NULL;
4670 ai = afpinfo_unpack(talloc_tos(), data);
4675 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
4677 ad = ad_init(talloc_tos(), ADOUBLE_META);
4682 p = ad_get_entry(ad, ADEID_FINDERI);
4684 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
4689 memcpy(p, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
4691 ret = ad_fset(handle, ad, fsp);
4693 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
4700 if (!ai_empty_finderinfo(ai)) {
4705 * Writing an all 0 blob to the metadata stream results in the stream
4706 * being removed on a macOS server. This ensures we behave the same and
4707 * it verified by the "delete AFP_AfpInfo by writing all 0" test.
4710 ok = set_delete_on_close(
4713 handle->conn->session_info->security_token,
4714 handle->conn->session_info->unix_token);
4716 DBG_ERR("set_delete_on_close on [%s] failed\n",
4724 static ssize_t fruit_pwrite_meta(vfs_handle_struct *handle,
4725 files_struct *fsp, const void *data,
4726 size_t n, off_t offset)
4728 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4730 uint8_t buf[AFP_INFO_SIZE];
4736 DBG_ERR("Failed to fetch fsp extension");
4745 if (offset != 0 && n < 60) {
4750 cmp = memcmp(data, "AFP", 3);
4756 if (n <= AFP_OFF_FinderInfo) {
4758 * Nothing to do here really, just return
4766 if (to_copy > AFP_INFO_SIZE) {
4767 to_copy = AFP_INFO_SIZE;
4769 memcpy(buf, data, to_copy);
4772 if (to_write != AFP_INFO_SIZE) {
4773 to_write = AFP_INFO_SIZE;
4776 switch (fio->config->meta) {
4777 case FRUIT_META_STREAM:
4778 nwritten = fruit_pwrite_meta_stream(handle,
4785 case FRUIT_META_NETATALK:
4786 nwritten = fruit_pwrite_meta_netatalk(handle,
4794 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
4798 if (nwritten != to_write) {
4803 * Return the requested amount, verified against macOS SMB server
4808 static ssize_t fruit_pwrite_rsrc_stream(vfs_handle_struct *handle,
4809 files_struct *fsp, const void *data,
4810 size_t n, off_t offset)
4812 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4815 static ssize_t fruit_pwrite_rsrc_xattr(vfs_handle_struct *handle,
4816 files_struct *fsp, const void *data,
4817 size_t n, off_t offset)
4819 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4822 static ssize_t fruit_pwrite_rsrc_adouble(vfs_handle_struct *handle,
4823 files_struct *fsp, const void *data,
4824 size_t n, off_t offset)
4826 struct adouble *ad = NULL;
4830 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
4832 DBG_ERR("ad_get [%s] failed\n", fsp_str_dbg(fsp));
4836 nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n,
4837 offset + ad_getentryoff(ad, ADEID_RFORK));
4838 if (nwritten != n) {
4839 DBG_ERR("Short write on [%s] [%zd/%zd]\n",
4840 fsp_str_dbg(fsp), nwritten, n);
4845 if ((n + offset) > ad_getentrylen(ad, ADEID_RFORK)) {
4846 ad_setentrylen(ad, ADEID_RFORK, n + offset);
4847 ret = ad_fset(handle, ad, fsp);
4849 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
4859 static ssize_t fruit_pwrite_rsrc(vfs_handle_struct *handle,
4860 files_struct *fsp, const void *data,
4861 size_t n, off_t offset)
4863 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4867 DBG_ERR("Failed to fetch fsp extension");
4871 switch (fio->config->rsrc) {
4872 case FRUIT_RSRC_STREAM:
4873 nwritten = fruit_pwrite_rsrc_stream(handle, fsp, data, n, offset);
4876 case FRUIT_RSRC_ADFILE:
4877 nwritten = fruit_pwrite_rsrc_adouble(handle, fsp, data, n, offset);
4880 case FRUIT_RSRC_XATTR:
4881 nwritten = fruit_pwrite_rsrc_xattr(handle, fsp, data, n, offset);
4885 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
4892 static ssize_t fruit_pwrite(vfs_handle_struct *handle,
4893 files_struct *fsp, const void *data,
4894 size_t n, off_t offset)
4896 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4899 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4900 fsp_str_dbg(fsp), (intmax_t)offset, n);
4903 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4906 if (fio->type == ADOUBLE_META) {
4907 nwritten = fruit_pwrite_meta(handle, fsp, data, n, offset);
4909 nwritten = fruit_pwrite_rsrc(handle, fsp, data, n, offset);
4912 DBG_DEBUG("Path [%s] nwritten=%zd\n", fsp_str_dbg(fsp), nwritten);
4916 struct fruit_pwrite_state {
4918 struct vfs_aio_state vfs_aio_state;
4921 static void fruit_pwrite_done(struct tevent_req *subreq);
4923 static struct tevent_req *fruit_pwrite_send(
4924 struct vfs_handle_struct *handle,
4925 TALLOC_CTX *mem_ctx,
4926 struct tevent_context *ev,
4927 struct files_struct *fsp,
4929 size_t n, off_t offset)
4931 struct tevent_req *req = NULL;
4932 struct tevent_req *subreq = NULL;
4933 struct fruit_pwrite_state *state = NULL;
4934 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4936 req = tevent_req_create(mem_ctx, &state,
4937 struct fruit_pwrite_state);
4942 if (fruit_must_handle_aio_stream(fio)) {
4943 state->nwritten = SMB_VFS_PWRITE(fsp, data, n, offset);
4944 if (state->nwritten != n) {
4945 if (state->nwritten != -1) {
4948 tevent_req_error(req, errno);
4949 return tevent_req_post(req, ev);
4951 tevent_req_done(req);
4952 return tevent_req_post(req, ev);
4955 subreq = SMB_VFS_NEXT_PWRITE_SEND(state, ev, handle, fsp,
4957 if (tevent_req_nomem(req, subreq)) {
4958 return tevent_req_post(req, ev);
4960 tevent_req_set_callback(subreq, fruit_pwrite_done, req);
4964 static void fruit_pwrite_done(struct tevent_req *subreq)
4966 struct tevent_req *req = tevent_req_callback_data(
4967 subreq, struct tevent_req);
4968 struct fruit_pwrite_state *state = tevent_req_data(
4969 req, struct fruit_pwrite_state);
4971 state->nwritten = SMB_VFS_PWRITE_RECV(subreq, &state->vfs_aio_state);
4972 TALLOC_FREE(subreq);
4974 if (tevent_req_error(req, state->vfs_aio_state.error)) {
4977 tevent_req_done(req);
4980 static ssize_t fruit_pwrite_recv(struct tevent_req *req,
4981 struct vfs_aio_state *vfs_aio_state)
4983 struct fruit_pwrite_state *state = tevent_req_data(
4984 req, struct fruit_pwrite_state);
4986 if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
4990 *vfs_aio_state = state->vfs_aio_state;
4991 return state->nwritten;
4995 * Helper to stat/lstat the base file of an smb_fname.
4997 static int fruit_stat_base(vfs_handle_struct *handle,
4998 struct smb_filename *smb_fname,
5001 char *tmp_stream_name;
5004 tmp_stream_name = smb_fname->stream_name;
5005 smb_fname->stream_name = NULL;
5007 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
5009 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5011 smb_fname->stream_name = tmp_stream_name;
5013 DBG_DEBUG("fruit_stat_base [%s] dev [%ju] ino [%ju]\n",
5014 smb_fname->base_name,
5015 (uintmax_t)smb_fname->st.st_ex_dev,
5016 (uintmax_t)smb_fname->st.st_ex_ino);
5020 static int fruit_stat_meta_stream(vfs_handle_struct *handle,
5021 struct smb_filename *smb_fname,
5027 ret = fruit_stat_base(handle, smb_fname, false);
5032 ino = fruit_inode(&smb_fname->st, smb_fname->stream_name);
5035 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
5037 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5040 smb_fname->st.st_ex_ino = ino;
5045 static int fruit_stat_meta_netatalk(vfs_handle_struct *handle,
5046 struct smb_filename *smb_fname,
5049 struct adouble *ad = NULL;
5051 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5053 DBG_INFO("fruit_stat_meta %s: %s\n",
5054 smb_fname_str_dbg(smb_fname), strerror(errno));
5060 /* Populate the stat struct with info from the base file. */
5061 if (fruit_stat_base(handle, smb_fname, follow_links) == -1) {
5064 smb_fname->st.st_ex_size = AFP_INFO_SIZE;
5065 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5066 smb_fname->stream_name);
5070 static int fruit_stat_meta(vfs_handle_struct *handle,
5071 struct smb_filename *smb_fname,
5074 struct fruit_config_data *config = NULL;
5077 SMB_VFS_HANDLE_GET_DATA(handle, config,
5078 struct fruit_config_data, return -1);
5080 switch (config->meta) {
5081 case FRUIT_META_STREAM:
5082 ret = fruit_stat_meta_stream(handle, smb_fname, follow_links);
5085 case FRUIT_META_NETATALK:
5086 ret = fruit_stat_meta_netatalk(handle, smb_fname, follow_links);
5090 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
5097 static int fruit_stat_rsrc_netatalk(vfs_handle_struct *handle,
5098 struct smb_filename *smb_fname,
5101 struct adouble *ad = NULL;
5104 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
5110 /* Populate the stat struct with info from the base file. */
5111 ret = fruit_stat_base(handle, smb_fname, follow_links);
5117 smb_fname->st.st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
5118 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5119 smb_fname->stream_name);
5124 static int fruit_stat_rsrc_stream(vfs_handle_struct *handle,
5125 struct smb_filename *smb_fname,
5131 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
5133 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5139 static int fruit_stat_rsrc_xattr(vfs_handle_struct *handle,
5140 struct smb_filename *smb_fname,
5143 #ifdef HAVE_ATTROPEN
5147 /* Populate the stat struct with info from the base file. */
5148 ret = fruit_stat_base(handle, smb_fname, follow_links);
5153 fd = attropen(smb_fname->base_name,
5154 AFPRESOURCE_EA_NETATALK,
5160 ret = sys_fstat(fd, &smb_fname->st, false);
5163 DBG_ERR("fstat [%s:%s] failed\n", smb_fname->base_name,
5164 AFPRESOURCE_EA_NETATALK);
5170 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5171 smb_fname->stream_name);
5181 static int fruit_stat_rsrc(vfs_handle_struct *handle,
5182 struct smb_filename *smb_fname,
5185 struct fruit_config_data *config = NULL;
5188 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
5190 SMB_VFS_HANDLE_GET_DATA(handle, config,
5191 struct fruit_config_data, return -1);
5193 switch (config->rsrc) {
5194 case FRUIT_RSRC_STREAM:
5195 ret = fruit_stat_rsrc_stream(handle, smb_fname, follow_links);
5198 case FRUIT_RSRC_XATTR:
5199 ret = fruit_stat_rsrc_xattr(handle, smb_fname, follow_links);
5202 case FRUIT_RSRC_ADFILE:
5203 ret = fruit_stat_rsrc_netatalk(handle, smb_fname, follow_links);
5207 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
5214 static int fruit_stat(vfs_handle_struct *handle,
5215 struct smb_filename *smb_fname)
5219 DEBUG(10, ("fruit_stat called for %s\n",
5220 smb_fname_str_dbg(smb_fname)));
5222 if (!is_ntfs_stream_smb_fname(smb_fname)
5223 || is_ntfs_default_stream_smb_fname(smb_fname)) {
5224 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
5226 update_btime(handle, smb_fname);
5232 * Note if lp_posix_paths() is true, we can never
5233 * get here as is_ntfs_stream_smb_fname() is
5234 * always false. So we never need worry about
5235 * not following links here.
5238 if (is_afpinfo_stream(smb_fname)) {
5239 rc = fruit_stat_meta(handle, smb_fname, true);
5240 } else if (is_afpresource_stream(smb_fname)) {
5241 rc = fruit_stat_rsrc(handle, smb_fname, true);
5243 return SMB_VFS_NEXT_STAT(handle, smb_fname);
5247 update_btime(handle, smb_fname);
5248 smb_fname->st.st_ex_mode &= ~S_IFMT;
5249 smb_fname->st.st_ex_mode |= S_IFREG;
5250 smb_fname->st.st_ex_blocks =
5251 smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
5256 static int fruit_lstat(vfs_handle_struct *handle,
5257 struct smb_filename *smb_fname)
5261 DEBUG(10, ("fruit_lstat called for %s\n",
5262 smb_fname_str_dbg(smb_fname)));
5264 if (!is_ntfs_stream_smb_fname(smb_fname)
5265 || is_ntfs_default_stream_smb_fname(smb_fname)) {
5266 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5268 update_btime(handle, smb_fname);
5273 if (is_afpinfo_stream(smb_fname)) {
5274 rc = fruit_stat_meta(handle, smb_fname, false);
5275 } else if (is_afpresource_stream(smb_fname)) {
5276 rc = fruit_stat_rsrc(handle, smb_fname, false);
5278 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5282 update_btime(handle, smb_fname);
5283 smb_fname->st.st_ex_mode &= ~S_IFMT;
5284 smb_fname->st.st_ex_mode |= S_IFREG;
5285 smb_fname->st.st_ex_blocks =
5286 smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
5291 static int fruit_fstat_meta_stream(vfs_handle_struct *handle,
5293 SMB_STRUCT_STAT *sbuf)
5295 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5296 struct smb_filename smb_fname;
5305 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5310 *sbuf = fsp->base_fsp->fsp_name->st;
5311 sbuf->st_ex_size = AFP_INFO_SIZE;
5312 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5316 smb_fname = (struct smb_filename) {
5317 .base_name = fsp->fsp_name->base_name,
5320 ret = fruit_stat_base(handle, &smb_fname, false);
5324 *sbuf = smb_fname.st;
5326 ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5328 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5333 sbuf->st_ex_ino = ino;
5337 static int fruit_fstat_meta_netatalk(vfs_handle_struct *handle,
5339 SMB_STRUCT_STAT *sbuf)
5343 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5348 *sbuf = fsp->base_fsp->fsp_name->st;
5349 sbuf->st_ex_size = AFP_INFO_SIZE;
5350 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5355 static int fruit_fstat_meta(vfs_handle_struct *handle,
5357 SMB_STRUCT_STAT *sbuf,
5362 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
5364 switch (fio->config->meta) {
5365 case FRUIT_META_STREAM:
5366 ret = fruit_fstat_meta_stream(handle, fsp, sbuf);
5369 case FRUIT_META_NETATALK:
5370 ret = fruit_fstat_meta_netatalk(handle, fsp, sbuf);
5374 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
5378 DBG_DEBUG("Path [%s] ret [%d]\n", fsp_str_dbg(fsp), ret);
5382 static int fruit_fstat_rsrc_xattr(vfs_handle_struct *handle,
5384 SMB_STRUCT_STAT *sbuf)
5386 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5389 static int fruit_fstat_rsrc_stream(vfs_handle_struct *handle,
5391 SMB_STRUCT_STAT *sbuf)
5393 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5396 static int fruit_fstat_rsrc_adouble(vfs_handle_struct *handle,
5398 SMB_STRUCT_STAT *sbuf)
5400 struct adouble *ad = NULL;
5403 /* Populate the stat struct with info from the base file. */
5404 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5409 ad = ad_get(talloc_tos(), handle,
5410 fsp->base_fsp->fsp_name,
5413 DBG_ERR("ad_get [%s] failed [%s]\n",
5414 fsp_str_dbg(fsp), strerror(errno));
5418 *sbuf = fsp->base_fsp->fsp_name->st;
5419 sbuf->st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
5420 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5426 static int fruit_fstat_rsrc(vfs_handle_struct *handle, files_struct *fsp,
5427 SMB_STRUCT_STAT *sbuf, struct fio *fio)
5431 switch (fio->config->rsrc) {
5432 case FRUIT_RSRC_STREAM:
5433 ret = fruit_fstat_rsrc_stream(handle, fsp, sbuf);
5436 case FRUIT_RSRC_ADFILE:
5437 ret = fruit_fstat_rsrc_adouble(handle, fsp, sbuf);
5440 case FRUIT_RSRC_XATTR:
5441 ret = fruit_fstat_rsrc_xattr(handle, fsp, sbuf);
5445 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
5452 static int fruit_fstat(vfs_handle_struct *handle, files_struct *fsp,
5453 SMB_STRUCT_STAT *sbuf)
5455 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5459 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5462 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
5464 if (fio->type == ADOUBLE_META) {
5465 rc = fruit_fstat_meta(handle, fsp, sbuf, fio);
5467 rc = fruit_fstat_rsrc(handle, fsp, sbuf, fio);
5471 sbuf->st_ex_mode &= ~S_IFMT;
5472 sbuf->st_ex_mode |= S_IFREG;
5473 sbuf->st_ex_blocks = sbuf->st_ex_size / STAT_ST_BLOCKSIZE + 1;
5476 DBG_DEBUG("Path [%s] rc [%d] size [%"PRIdMAX"]\n",
5477 fsp_str_dbg(fsp), rc, (intmax_t)sbuf->st_ex_size);
5481 static NTSTATUS delete_invalid_meta_stream(
5482 vfs_handle_struct *handle,
5483 const struct smb_filename *smb_fname,
5484 TALLOC_CTX *mem_ctx,
5485 unsigned int *pnum_streams,
5486 struct stream_struct **pstreams,
5489 struct smb_filename *sname = NULL;
5493 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams, AFPINFO_STREAM);
5495 return NT_STATUS_INTERNAL_ERROR;
5499 return NT_STATUS_OK;
5502 sname = synthetic_smb_fname(talloc_tos(),
5503 smb_fname->base_name,
5504 AFPINFO_STREAM_NAME,
5506 if (sname == NULL) {
5507 return NT_STATUS_NO_MEMORY;
5510 ret = SMB_VFS_NEXT_UNLINK(handle, sname);
5513 DBG_ERR("Removing [%s] failed\n", smb_fname_str_dbg(sname));
5514 return map_nt_error_from_unix(errno);
5517 return NT_STATUS_OK;
5520 static NTSTATUS fruit_streaminfo_meta_stream(
5521 vfs_handle_struct *handle,
5522 struct files_struct *fsp,
5523 const struct smb_filename *smb_fname,
5524 TALLOC_CTX *mem_ctx,
5525 unsigned int *pnum_streams,
5526 struct stream_struct **pstreams)
5528 struct stream_struct *stream = *pstreams;
5529 unsigned int num_streams = *pnum_streams;
5532 for (i = 0; i < num_streams; i++) {
5533 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
5538 if (i == num_streams) {
5539 return NT_STATUS_OK;
5542 if (stream[i].size != AFP_INFO_SIZE) {
5543 DBG_ERR("Removing invalid AFPINFO_STREAM size [%jd] from [%s]\n",
5544 (intmax_t)stream[i].size, smb_fname_str_dbg(smb_fname));
5546 return delete_invalid_meta_stream(handle,
5555 return NT_STATUS_OK;
5558 static NTSTATUS fruit_streaminfo_meta_netatalk(
5559 vfs_handle_struct *handle,
5560 struct files_struct *fsp,
5561 const struct smb_filename *smb_fname,
5562 TALLOC_CTX *mem_ctx,
5563 unsigned int *pnum_streams,
5564 struct stream_struct **pstreams)
5566 struct stream_struct *stream = *pstreams;
5567 unsigned int num_streams = *pnum_streams;
5568 struct adouble *ad = NULL;
5573 /* Remove the Netatalk xattr from the list */
5574 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5575 ":" NETATALK_META_XATTR ":$DATA");
5577 return NT_STATUS_NO_MEMORY;
5581 * Check if there's a AFPINFO_STREAM from the VFS streams
5582 * backend and if yes, remove it from the list
5584 for (i = 0; i < num_streams; i++) {
5585 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
5590 if (i < num_streams) {
5591 DBG_WARNING("Unexpected AFPINFO_STREAM on [%s]\n",
5592 smb_fname_str_dbg(smb_fname));
5594 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5597 return NT_STATUS_INTERNAL_ERROR;
5601 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5603 return NT_STATUS_OK;
5606 is_fi_empty = ad_empty_finderinfo(ad);
5610 return NT_STATUS_OK;
5613 ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
5614 AFPINFO_STREAM_NAME, AFP_INFO_SIZE,
5615 smb_roundup(handle->conn, AFP_INFO_SIZE));
5617 return NT_STATUS_NO_MEMORY;
5620 return NT_STATUS_OK;
5623 static NTSTATUS fruit_streaminfo_meta(vfs_handle_struct *handle,
5624 struct files_struct *fsp,
5625 const struct smb_filename *smb_fname,
5626 TALLOC_CTX *mem_ctx,
5627 unsigned int *pnum_streams,
5628 struct stream_struct **pstreams)
5630 struct fruit_config_data *config = NULL;
5633 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5634 return NT_STATUS_INTERNAL_ERROR);
5636 switch (config->meta) {
5637 case FRUIT_META_NETATALK:
5638 status = fruit_streaminfo_meta_netatalk(handle, fsp, smb_fname,
5639 mem_ctx, pnum_streams,
5643 case FRUIT_META_STREAM:
5644 status = fruit_streaminfo_meta_stream(handle, fsp, smb_fname,
5645 mem_ctx, pnum_streams,
5650 return NT_STATUS_INTERNAL_ERROR;
5656 static NTSTATUS fruit_streaminfo_rsrc_stream(
5657 vfs_handle_struct *handle,
5658 struct files_struct *fsp,
5659 const struct smb_filename *smb_fname,
5660 TALLOC_CTX *mem_ctx,
5661 unsigned int *pnum_streams,
5662 struct stream_struct **pstreams)
5666 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
5668 DBG_ERR("Filtering resource stream failed\n");
5669 return NT_STATUS_INTERNAL_ERROR;
5671 return NT_STATUS_OK;
5674 static NTSTATUS fruit_streaminfo_rsrc_xattr(
5675 vfs_handle_struct *handle,
5676 struct files_struct *fsp,
5677 const struct smb_filename *smb_fname,
5678 TALLOC_CTX *mem_ctx,
5679 unsigned int *pnum_streams,
5680 struct stream_struct **pstreams)
5684 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
5686 DBG_ERR("Filtering resource stream failed\n");
5687 return NT_STATUS_INTERNAL_ERROR;
5689 return NT_STATUS_OK;
5692 static NTSTATUS fruit_streaminfo_rsrc_adouble(
5693 vfs_handle_struct *handle,
5694 struct files_struct *fsp,
5695 const struct smb_filename *smb_fname,
5696 TALLOC_CTX *mem_ctx,
5697 unsigned int *pnum_streams,
5698 struct stream_struct **pstreams)
5700 struct stream_struct *stream = *pstreams;
5701 unsigned int num_streams = *pnum_streams;
5702 struct adouble *ad = NULL;
5708 * Check if there's a AFPRESOURCE_STREAM from the VFS streams backend
5709 * and if yes, remove it from the list
5711 for (i = 0; i < num_streams; i++) {
5712 if (strequal_m(stream[i].name, AFPRESOURCE_STREAM)) {
5717 if (i < num_streams) {
5718 DBG_WARNING("Unexpected AFPRESOURCE_STREAM on [%s]\n",
5719 smb_fname_str_dbg(smb_fname));
5721 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5722 AFPRESOURCE_STREAM);
5724 return NT_STATUS_INTERNAL_ERROR;
5728 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
5730 return NT_STATUS_OK;
5733 rlen = ad_getentrylen(ad, ADEID_RFORK);
5737 return NT_STATUS_OK;
5740 ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
5741 AFPRESOURCE_STREAM_NAME, rlen,
5742 smb_roundup(handle->conn, rlen));
5744 return NT_STATUS_NO_MEMORY;
5747 return NT_STATUS_OK;
5750 static NTSTATUS fruit_streaminfo_rsrc(vfs_handle_struct *handle,
5751 struct files_struct *fsp,
5752 const struct smb_filename *smb_fname,
5753 TALLOC_CTX *mem_ctx,
5754 unsigned int *pnum_streams,
5755 struct stream_struct **pstreams)
5757 struct fruit_config_data *config = NULL;
5760 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5761 return NT_STATUS_INTERNAL_ERROR);
5763 switch (config->rsrc) {
5764 case FRUIT_RSRC_STREAM:
5765 status = fruit_streaminfo_rsrc_stream(handle, fsp, smb_fname,
5766 mem_ctx, pnum_streams,
5770 case FRUIT_RSRC_XATTR:
5771 status = fruit_streaminfo_rsrc_xattr(handle, fsp, smb_fname,
5772 mem_ctx, pnum_streams,
5776 case FRUIT_RSRC_ADFILE:
5777 status = fruit_streaminfo_rsrc_adouble(handle, fsp, smb_fname,
5778 mem_ctx, pnum_streams,
5783 return NT_STATUS_INTERNAL_ERROR;
5789 static void fruit_filter_empty_streams(unsigned int *pnum_streams,
5790 struct stream_struct **pstreams)
5792 unsigned num_streams = *pnum_streams;
5793 struct stream_struct *streams = *pstreams;
5796 if (!global_fruit_config.nego_aapl) {
5800 while (i < num_streams) {
5801 struct smb_filename smb_fname = (struct smb_filename) {
5802 .stream_name = streams[i].name,
5805 if (is_ntfs_default_stream_smb_fname(&smb_fname)
5806 || streams[i].size > 0)
5812 streams[i] = streams[num_streams - 1];
5816 *pnum_streams = num_streams;
5819 static NTSTATUS fruit_streaminfo(vfs_handle_struct *handle,
5820 struct files_struct *fsp,
5821 const struct smb_filename *smb_fname,
5822 TALLOC_CTX *mem_ctx,
5823 unsigned int *pnum_streams,
5824 struct stream_struct **pstreams)
5826 struct fruit_config_data *config = NULL;
5829 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5830 return NT_STATUS_UNSUCCESSFUL);
5832 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
5834 status = SMB_VFS_NEXT_STREAMINFO(handle, fsp, smb_fname, mem_ctx,
5835 pnum_streams, pstreams);
5836 if (!NT_STATUS_IS_OK(status)) {
5840 fruit_filter_empty_streams(pnum_streams, pstreams);
5842 status = fruit_streaminfo_meta(handle, fsp, smb_fname,
5843 mem_ctx, pnum_streams, pstreams);
5844 if (!NT_STATUS_IS_OK(status)) {
5848 status = fruit_streaminfo_rsrc(handle, fsp, smb_fname,
5849 mem_ctx, pnum_streams, pstreams);
5850 if (!NT_STATUS_IS_OK(status)) {
5854 return NT_STATUS_OK;
5857 static int fruit_ntimes(vfs_handle_struct *handle,
5858 const struct smb_filename *smb_fname,
5859 struct smb_file_time *ft)
5862 struct adouble *ad = NULL;
5863 struct fruit_config_data *config = NULL;
5865 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5868 if ((config->meta != FRUIT_META_NETATALK) ||
5869 null_timespec(ft->create_time))
5871 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
5874 DEBUG(10,("set btime for %s to %s\n", smb_fname_str_dbg(smb_fname),
5875 time_to_asc(convert_timespec_to_time_t(ft->create_time))));
5877 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5882 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX,
5883 convert_time_t_to_uint32_t(ft->create_time.tv_sec));
5885 rc = ad_set(handle, ad, smb_fname);
5891 DEBUG(1, ("fruit_ntimes: %s\n", smb_fname_str_dbg(smb_fname)));
5894 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
5897 static int fruit_fallocate(struct vfs_handle_struct *handle,
5898 struct files_struct *fsp,
5903 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5906 return SMB_VFS_NEXT_FALLOCATE(handle, fsp, mode, offset, len);
5909 /* Let the pwrite code path handle it. */
5914 static int fruit_ftruncate_rsrc_xattr(struct vfs_handle_struct *handle,
5915 struct files_struct *fsp,
5918 #ifdef HAVE_ATTROPEN
5919 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
5924 static int fruit_ftruncate_rsrc_adouble(struct vfs_handle_struct *handle,
5925 struct files_struct *fsp,
5929 struct adouble *ad = NULL;
5932 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
5934 DBG_DEBUG("ad_get [%s] failed [%s]\n",
5935 fsp_str_dbg(fsp), strerror(errno));
5939 ad_off = ad_getentryoff(ad, ADEID_RFORK);
5941 rc = ftruncate(fsp->fh->fd, offset + ad_off);
5947 ad_setentrylen(ad, ADEID_RFORK, offset);
5949 rc = ad_fset(handle, ad, fsp);
5951 DBG_ERR("ad_fset [%s] failed [%s]\n",
5952 fsp_str_dbg(fsp), strerror(errno));
5961 static int fruit_ftruncate_rsrc_stream(struct vfs_handle_struct *handle,
5962 struct files_struct *fsp,
5965 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
5968 static int fruit_ftruncate_rsrc(struct vfs_handle_struct *handle,
5969 struct files_struct *fsp,
5972 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5976 DBG_ERR("Failed to fetch fsp extension");
5980 switch (fio->config->rsrc) {
5981 case FRUIT_RSRC_XATTR:
5982 ret = fruit_ftruncate_rsrc_xattr(handle, fsp, offset);
5985 case FRUIT_RSRC_ADFILE:
5986 ret = fruit_ftruncate_rsrc_adouble(handle, fsp, offset);
5989 case FRUIT_RSRC_STREAM:
5990 ret = fruit_ftruncate_rsrc_stream(handle, fsp, offset);
5994 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
6002 static int fruit_ftruncate_meta(struct vfs_handle_struct *handle,
6003 struct files_struct *fsp,
6007 DBG_WARNING("ftruncate %s to %jd",
6008 fsp_str_dbg(fsp), (intmax_t)offset);
6009 /* OS X returns NT_STATUS_ALLOTTED_SPACE_EXCEEDED */
6014 /* OS X returns success but does nothing */
6015 DBG_INFO("ignoring ftruncate %s to %jd\n",
6016 fsp_str_dbg(fsp), (intmax_t)offset);
6020 static int fruit_ftruncate(struct vfs_handle_struct *handle,
6021 struct files_struct *fsp,
6024 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
6027 DBG_DEBUG("Path [%s] offset [%"PRIdMAX"]\n", fsp_str_dbg(fsp),
6031 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
6034 if (fio->type == ADOUBLE_META) {
6035 ret = fruit_ftruncate_meta(handle, fsp, offset);
6037 ret = fruit_ftruncate_rsrc(handle, fsp, offset);
6040 DBG_DEBUG("Path [%s] result [%d]\n", fsp_str_dbg(fsp), ret);
6044 static NTSTATUS fruit_create_file(vfs_handle_struct *handle,
6045 struct smb_request *req,
6046 uint16_t root_dir_fid,
6047 struct smb_filename *smb_fname,
6048 uint32_t access_mask,
6049 uint32_t share_access,
6050 uint32_t create_disposition,
6051 uint32_t create_options,
6052 uint32_t file_attributes,
6053 uint32_t oplock_request,
6054 struct smb2_lease *lease,
6055 uint64_t allocation_size,
6056 uint32_t private_flags,
6057 struct security_descriptor *sd,
6058 struct ea_list *ea_list,
6059 files_struct **result,
6061 const struct smb2_create_blobs *in_context_blobs,
6062 struct smb2_create_blobs *out_context_blobs)
6065 struct fruit_config_data *config = NULL;
6066 files_struct *fsp = NULL;
6067 struct fio *fio = NULL;
6068 bool internal_open = (oplock_request & INTERNAL_OPEN_ONLY);
6071 status = check_aapl(handle, req, in_context_blobs, out_context_blobs);
6072 if (!NT_STATUS_IS_OK(status)) {
6076 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
6077 return NT_STATUS_UNSUCCESSFUL);
6079 if (is_apple_stream(smb_fname) && !internal_open) {
6080 ret = ad_convert(handle, smb_fname);
6082 DBG_ERR("ad_convert() failed\n");
6083 return NT_STATUS_UNSUCCESSFUL;
6087 status = SMB_VFS_NEXT_CREATE_FILE(
6088 handle, req, root_dir_fid, smb_fname,
6089 access_mask, share_access,
6090 create_disposition, create_options,
6091 file_attributes, oplock_request,
6093 allocation_size, private_flags,
6094 sd, ea_list, result,
6095 pinfo, in_context_blobs, out_context_blobs);
6096 if (!NT_STATUS_IS_OK(status)) {
6102 if (global_fruit_config.nego_aapl) {
6103 if (config->posix_rename && fsp->is_directory) {
6105 * Enable POSIX directory rename behaviour
6107 fsp->posix_flags |= FSP_POSIX_FLAGS_RENAME;
6112 * If this is a plain open for existing files, opening an 0
6113 * byte size resource fork MUST fail with
6114 * NT_STATUS_OBJECT_NAME_NOT_FOUND.
6116 * Cf the vfs_fruit torture tests in test_rfork_create().
6118 if (global_fruit_config.nego_aapl &&
6119 create_disposition == FILE_OPEN &&
6120 smb_fname->st.st_ex_size == 0 &&
6121 is_ntfs_stream_smb_fname(smb_fname) &&
6122 !(is_ntfs_default_stream_smb_fname(smb_fname)))
6124 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
6128 fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
6129 if (fio != NULL && pinfo != NULL && *pinfo == FILE_WAS_CREATED) {
6130 fio->created = true;
6133 if (is_ntfs_stream_smb_fname(smb_fname)
6134 || fsp->is_directory) {
6138 if ((config->locking == FRUIT_LOCKING_NETATALK) &&
6141 status = fruit_check_access(
6145 if (!NT_STATUS_IS_OK(status)) {
6153 DEBUG(10, ("fruit_create_file: %s\n", nt_errstr(status)));
6156 close_file(req, fsp, ERROR_CLOSE);
6157 *result = fsp = NULL;
6163 static NTSTATUS fruit_readdir_attr(struct vfs_handle_struct *handle,
6164 const struct smb_filename *fname,
6165 TALLOC_CTX *mem_ctx,
6166 struct readdir_attr_data **pattr_data)
6168 struct fruit_config_data *config = NULL;
6169 struct readdir_attr_data *attr_data;
6173 SMB_VFS_HANDLE_GET_DATA(handle, config,
6174 struct fruit_config_data,
6175 return NT_STATUS_UNSUCCESSFUL);
6177 if (!global_fruit_config.nego_aapl) {
6178 return SMB_VFS_NEXT_READDIR_ATTR(handle, fname, mem_ctx, pattr_data);
6181 DEBUG(10, ("fruit_readdir_attr %s\n", fname->base_name));
6183 ret = ad_convert(handle, fname);
6185 DBG_ERR("ad_convert() failed\n");
6186 return NT_STATUS_UNSUCCESSFUL;
6189 *pattr_data = talloc_zero(mem_ctx, struct readdir_attr_data);
6190 if (*pattr_data == NULL) {
6191 return NT_STATUS_UNSUCCESSFUL;
6193 attr_data = *pattr_data;
6194 attr_data->type = RDATTR_AAPL;
6197 * Mac metadata: compressed FinderInfo, resource fork length
6200 status = readdir_attr_macmeta(handle, fname, attr_data);
6201 if (!NT_STATUS_IS_OK(status)) {
6203 * Error handling is tricky: if we return failure from
6204 * this function, the corresponding directory entry
6205 * will to be passed to the client, so we really just
6206 * want to error out on fatal errors.
6208 if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
6216 if (config->unix_info_enabled) {
6217 attr_data->attr_data.aapl.unix_mode = fname->st.st_ex_mode;
6223 if (!config->readdir_attr_max_access) {
6224 attr_data->attr_data.aapl.max_access = FILE_GENERIC_ALL;
6226 status = smbd_calculate_access_mask(
6230 SEC_FLAG_MAXIMUM_ALLOWED,
6231 &attr_data->attr_data.aapl.max_access);
6232 if (!NT_STATUS_IS_OK(status)) {
6237 return NT_STATUS_OK;
6240 DEBUG(1, ("fruit_readdir_attr %s, error: %s\n",
6241 fname->base_name, nt_errstr(status)));
6242 TALLOC_FREE(*pattr_data);
6246 static NTSTATUS fruit_fget_nt_acl(vfs_handle_struct *handle,
6248 uint32_t security_info,
6249 TALLOC_CTX *mem_ctx,
6250 struct security_descriptor **ppdesc)
6253 struct security_ace ace;
6255 struct fruit_config_data *config;
6257 SMB_VFS_HANDLE_GET_DATA(handle, config,
6258 struct fruit_config_data,
6259 return NT_STATUS_UNSUCCESSFUL);
6261 status = SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
6263 if (!NT_STATUS_IS_OK(status)) {
6268 * Add MS NFS style ACEs with uid, gid and mode
6270 if (!global_fruit_config.nego_aapl) {
6271 return NT_STATUS_OK;
6273 if (!config->unix_info_enabled) {
6274 return NT_STATUS_OK;
6277 /* First remove any existing ACE's with NFS style mode/uid/gid SIDs. */
6278 status = remove_virtual_nfs_aces(*ppdesc);
6279 if (!NT_STATUS_IS_OK(status)) {
6280 DBG_WARNING("failed to remove MS NFS style ACEs\n");
6284 /* MS NFS style mode */
6285 sid_compose(&sid, &global_sid_Unix_NFS_Mode, fsp->fsp_name->st.st_ex_mode);
6286 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6287 status = security_descriptor_dacl_add(*ppdesc, &ace);
6288 if (!NT_STATUS_IS_OK(status)) {
6289 DEBUG(1,("failed to add MS NFS style ACE\n"));
6293 /* MS NFS style uid */
6294 sid_compose(&sid, &global_sid_Unix_NFS_Users, fsp->fsp_name->st.st_ex_uid);
6295 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6296 status = security_descriptor_dacl_add(*ppdesc, &ace);
6297 if (!NT_STATUS_IS_OK(status)) {
6298 DEBUG(1,("failed to add MS NFS style ACE\n"));
6302 /* MS NFS style gid */
6303 sid_compose(&sid, &global_sid_Unix_NFS_Groups, fsp->fsp_name->st.st_ex_gid);
6304 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6305 status = security_descriptor_dacl_add(*ppdesc, &ace);
6306 if (!NT_STATUS_IS_OK(status)) {
6307 DEBUG(1,("failed to add MS NFS style ACE\n"));
6311 return NT_STATUS_OK;
6314 static NTSTATUS fruit_fset_nt_acl(vfs_handle_struct *handle,
6316 uint32_t security_info_sent,
6317 const struct security_descriptor *orig_psd)
6321 mode_t ms_nfs_mode = 0;
6323 struct security_descriptor *psd = NULL;
6324 uint32_t orig_num_aces = 0;
6326 if (orig_psd->dacl != NULL) {
6327 orig_num_aces = orig_psd->dacl->num_aces;
6330 psd = security_descriptor_copy(talloc_tos(), orig_psd);
6332 return NT_STATUS_NO_MEMORY;
6335 DBG_DEBUG("fruit_fset_nt_acl: %s\n", fsp_str_dbg(fsp));
6337 status = check_ms_nfs(handle, fsp, psd, &ms_nfs_mode, &do_chmod);
6338 if (!NT_STATUS_IS_OK(status)) {
6339 DEBUG(1, ("fruit_fset_nt_acl: check_ms_nfs failed%s\n", fsp_str_dbg(fsp)));
6345 * If only ms_nfs ACE entries were sent, ensure we set the DACL
6346 * sent/present flags correctly now we've removed them.
6349 if (orig_num_aces != 0) {
6351 * Are there any ACE's left ?
6353 if (psd->dacl->num_aces == 0) {
6354 /* No - clear the DACL sent/present flags. */
6355 security_info_sent &= ~SECINFO_DACL;
6356 psd->type &= ~SEC_DESC_DACL_PRESENT;
6360 status = SMB_VFS_NEXT_FSET_NT_ACL(handle, fsp, security_info_sent, psd);
6361 if (!NT_STATUS_IS_OK(status)) {
6362 DEBUG(1, ("fruit_fset_nt_acl: SMB_VFS_NEXT_FSET_NT_ACL failed%s\n", fsp_str_dbg(fsp)));
6368 if (fsp->fh->fd != -1) {
6369 result = SMB_VFS_FCHMOD(fsp, ms_nfs_mode);
6371 result = SMB_VFS_CHMOD(fsp->conn,
6377 DEBUG(1, ("chmod: %s, result: %d, %04o error %s\n", fsp_str_dbg(fsp),
6378 result, (unsigned)ms_nfs_mode,
6380 status = map_nt_error_from_unix(errno);
6387 return NT_STATUS_OK;
6390 static struct vfs_offload_ctx *fruit_offload_ctx;
6392 struct fruit_offload_read_state {
6393 struct vfs_handle_struct *handle;
6394 struct tevent_context *ev;
6400 static void fruit_offload_read_done(struct tevent_req *subreq);
6402 static struct tevent_req *fruit_offload_read_send(
6403 TALLOC_CTX *mem_ctx,
6404 struct tevent_context *ev,
6405 struct vfs_handle_struct *handle,
6412 struct tevent_req *req = NULL;
6413 struct tevent_req *subreq = NULL;
6414 struct fruit_offload_read_state *state = NULL;
6416 req = tevent_req_create(mem_ctx, &state,
6417 struct fruit_offload_read_state);
6421 *state = (struct fruit_offload_read_state) {
6428 subreq = SMB_VFS_NEXT_OFFLOAD_READ_SEND(mem_ctx, ev, handle, fsp,
6429 fsctl, ttl, offset, to_copy);
6430 if (tevent_req_nomem(subreq, req)) {
6431 return tevent_req_post(req, ev);
6433 tevent_req_set_callback(subreq, fruit_offload_read_done, req);
6437 static void fruit_offload_read_done(struct tevent_req *subreq)
6439 struct tevent_req *req = tevent_req_callback_data(
6440 subreq, struct tevent_req);
6441 struct fruit_offload_read_state *state = tevent_req_data(
6442 req, struct fruit_offload_read_state);
6445 status = SMB_VFS_NEXT_OFFLOAD_READ_RECV(subreq,
6449 TALLOC_FREE(subreq);
6450 if (tevent_req_nterror(req, status)) {
6454 if (state->fsctl != FSCTL_SRV_REQUEST_RESUME_KEY) {
6455 tevent_req_done(req);
6459 status = vfs_offload_token_ctx_init(state->fsp->conn->sconn->client,
6460 &fruit_offload_ctx);
6461 if (tevent_req_nterror(req, status)) {
6465 status = vfs_offload_token_db_store_fsp(fruit_offload_ctx,
6468 if (tevent_req_nterror(req, status)) {
6472 tevent_req_done(req);
6476 static NTSTATUS fruit_offload_read_recv(struct tevent_req *req,
6477 struct vfs_handle_struct *handle,
6478 TALLOC_CTX *mem_ctx,
6481 struct fruit_offload_read_state *state = tevent_req_data(
6482 req, struct fruit_offload_read_state);
6485 if (tevent_req_is_nterror(req, &status)) {
6486 tevent_req_received(req);
6490 token->length = state->token.length;
6491 token->data = talloc_move(mem_ctx, &state->token.data);
6493 tevent_req_received(req);
6494 return NT_STATUS_OK;
6497 struct fruit_offload_write_state {
6498 struct vfs_handle_struct *handle;
6500 struct files_struct *src_fsp;
6501 struct files_struct *dst_fsp;
6505 static void fruit_offload_write_done(struct tevent_req *subreq);
6506 static struct tevent_req *fruit_offload_write_send(struct vfs_handle_struct *handle,
6507 TALLOC_CTX *mem_ctx,
6508 struct tevent_context *ev,
6511 off_t transfer_offset,
6512 struct files_struct *dest_fsp,
6516 struct tevent_req *req, *subreq;
6517 struct fruit_offload_write_state *state;
6519 struct fruit_config_data *config;
6520 off_t src_off = transfer_offset;
6521 files_struct *src_fsp = NULL;
6522 off_t to_copy = num;
6523 bool copyfile_enabled = false;
6525 DEBUG(10,("soff: %ju, doff: %ju, len: %ju\n",
6526 (uintmax_t)src_off, (uintmax_t)dest_off, (uintmax_t)num));
6528 SMB_VFS_HANDLE_GET_DATA(handle, config,
6529 struct fruit_config_data,
6532 req = tevent_req_create(mem_ctx, &state,
6533 struct fruit_offload_write_state);
6537 state->handle = handle;
6538 state->dst_fsp = dest_fsp;
6541 case FSCTL_SRV_COPYCHUNK:
6542 case FSCTL_SRV_COPYCHUNK_WRITE:
6543 copyfile_enabled = config->copyfile_enabled;
6550 * Check if this a OS X copyfile style copychunk request with
6551 * a requested chunk count of 0 that was translated to a
6552 * offload_write_send VFS call overloading the parameters src_off
6553 * = dest_off = num = 0.
6555 if (copyfile_enabled && num == 0 && src_off == 0 && dest_off == 0) {
6556 status = vfs_offload_token_db_fetch_fsp(
6557 fruit_offload_ctx, token, &src_fsp);
6558 if (tevent_req_nterror(req, status)) {
6559 return tevent_req_post(req, ev);
6561 state->src_fsp = src_fsp;
6563 status = vfs_stat_fsp(src_fsp);
6564 if (tevent_req_nterror(req, status)) {
6565 return tevent_req_post(req, ev);
6568 to_copy = src_fsp->fsp_name->st.st_ex_size;
6569 state->is_copyfile = true;
6572 subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle,
6581 if (tevent_req_nomem(subreq, req)) {
6582 return tevent_req_post(req, ev);
6585 tevent_req_set_callback(subreq, fruit_offload_write_done, req);
6589 static void fruit_offload_write_done(struct tevent_req *subreq)
6591 struct tevent_req *req = tevent_req_callback_data(
6592 subreq, struct tevent_req);
6593 struct fruit_offload_write_state *state = tevent_req_data(
6594 req, struct fruit_offload_write_state);
6596 unsigned int num_streams = 0;
6597 struct stream_struct *streams = NULL;
6599 struct smb_filename *src_fname_tmp = NULL;
6600 struct smb_filename *dst_fname_tmp = NULL;
6602 status = SMB_VFS_NEXT_OFFLOAD_WRITE_RECV(state->handle,
6605 TALLOC_FREE(subreq);
6606 if (tevent_req_nterror(req, status)) {
6610 if (!state->is_copyfile) {
6611 tevent_req_done(req);
6616 * Now copy all remaining streams. We know the share supports
6617 * streams, because we're in vfs_fruit. We don't do this async
6618 * because streams are few and small.
6620 status = vfs_streaminfo(state->handle->conn, state->src_fsp,
6621 state->src_fsp->fsp_name,
6622 req, &num_streams, &streams);
6623 if (tevent_req_nterror(req, status)) {
6627 if (num_streams == 1) {
6628 /* There is always one stream, ::$DATA. */
6629 tevent_req_done(req);
6633 for (i = 0; i < num_streams; i++) {
6634 DEBUG(10, ("%s: stream: '%s'/%zu\n",
6635 __func__, streams[i].name, (size_t)streams[i].size));
6637 src_fname_tmp = synthetic_smb_fname(
6639 state->src_fsp->fsp_name->base_name,
6642 state->src_fsp->fsp_name->flags);
6643 if (tevent_req_nomem(src_fname_tmp, req)) {
6647 if (is_ntfs_default_stream_smb_fname(src_fname_tmp)) {
6648 TALLOC_FREE(src_fname_tmp);
6652 dst_fname_tmp = synthetic_smb_fname(
6654 state->dst_fsp->fsp_name->base_name,
6657 state->dst_fsp->fsp_name->flags);
6658 if (tevent_req_nomem(dst_fname_tmp, req)) {
6659 TALLOC_FREE(src_fname_tmp);
6663 status = copy_file(req,
6664 state->handle->conn,
6667 OPENX_FILE_CREATE_IF_NOT_EXIST,
6669 if (!NT_STATUS_IS_OK(status)) {
6670 DEBUG(1, ("%s: copy %s to %s failed: %s\n", __func__,
6671 smb_fname_str_dbg(src_fname_tmp),
6672 smb_fname_str_dbg(dst_fname_tmp),
6673 nt_errstr(status)));
6674 TALLOC_FREE(src_fname_tmp);
6675 TALLOC_FREE(dst_fname_tmp);
6676 tevent_req_nterror(req, status);
6680 TALLOC_FREE(src_fname_tmp);
6681 TALLOC_FREE(dst_fname_tmp);
6684 TALLOC_FREE(streams);
6685 TALLOC_FREE(src_fname_tmp);
6686 TALLOC_FREE(dst_fname_tmp);
6687 tevent_req_done(req);
6690 static NTSTATUS fruit_offload_write_recv(struct vfs_handle_struct *handle,
6691 struct tevent_req *req,
6694 struct fruit_offload_write_state *state = tevent_req_data(
6695 req, struct fruit_offload_write_state);
6698 if (tevent_req_is_nterror(req, &status)) {
6699 DEBUG(1, ("server side copy chunk failed: %s\n",
6700 nt_errstr(status)));
6702 tevent_req_received(req);
6706 *copied = state->copied;
6707 tevent_req_received(req);
6709 return NT_STATUS_OK;
6712 static char *fruit_get_bandsize_line(char **lines, int numlines)
6715 static bool re_initialized = false;
6719 if (!re_initialized) {
6720 ret = regcomp(&re, "^[[:blank:]]*<key>band-size</key>$", 0);
6724 re_initialized = true;
6727 for (i = 0; i < numlines; i++) {
6728 regmatch_t matches[1];
6730 ret = regexec(&re, lines[i], 1, matches, 0);
6733 * Check if the match was on the last line, sa we want
6734 * the subsequent line.
6736 if (i + 1 == numlines) {
6739 return lines[i + 1];
6741 if (ret != REG_NOMATCH) {
6749 static bool fruit_get_bandsize_from_line(char *line, size_t *_band_size)
6752 static bool re_initialized = false;
6753 regmatch_t matches[2];
6758 if (!re_initialized) {
6761 "<integer>\\([[:digit:]]*\\)</integer>$",
6766 re_initialized = true;
6769 ret = regexec(&re, line, 2, matches, 0);
6771 DBG_ERR("regex failed [%s]\n", line);
6775 line[matches[1].rm_eo] = '\0';
6777 ok = conv_str_u64(&line[matches[1].rm_so], &band_size);
6781 *_band_size = (size_t)band_size;
6786 * This reads and parses an Info.plist from a TM sparsebundle looking for the
6787 * "band-size" key and value.
6789 static bool fruit_get_bandsize(vfs_handle_struct *handle,
6793 #define INFO_PLIST_MAX_SIZE 64*1024
6795 struct smb_filename *smb_fname = NULL;
6796 files_struct *fsp = NULL;
6797 uint8_t *file_data = NULL;
6798 char **lines = NULL;
6799 char *band_size_line = NULL;
6800 size_t plist_file_size;
6807 plist = talloc_asprintf(talloc_tos(),
6809 handle->conn->connectpath,
6811 if (plist == NULL) {
6816 smb_fname = synthetic_smb_fname(talloc_tos(), plist, NULL, NULL, 0);
6817 if (smb_fname == NULL) {
6822 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
6824 DBG_INFO("Ignoring Sparsebundle without Info.plist [%s]\n", dir);
6829 plist_file_size = smb_fname->st.st_ex_size;
6831 if (plist_file_size > INFO_PLIST_MAX_SIZE) {
6832 DBG_INFO("%s is too large, ignoring\n", plist);
6837 status = SMB_VFS_NEXT_CREATE_FILE(
6840 0, /* root_dir_fid */
6841 smb_fname, /* fname */
6842 FILE_GENERIC_READ, /* access_mask */
6843 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
6844 FILE_OPEN, /* create_disposition */
6845 0, /* create_options */
6846 0, /* file_attributes */
6847 INTERNAL_OPEN_ONLY, /* oplock_request */
6849 0, /* allocation_size */
6850 0, /* private_flags */
6855 NULL, NULL); /* create context */
6856 if (!NT_STATUS_IS_OK(status)) {
6857 DBG_INFO("Opening [%s] failed [%s]\n",
6858 smb_fname_str_dbg(smb_fname), nt_errstr(status));
6863 file_data = talloc_array(talloc_tos(), uint8_t, plist_file_size);
6864 if (file_data == NULL) {
6869 nread = SMB_VFS_NEXT_PREAD(handle, fsp, file_data, plist_file_size, 0);
6870 if (nread != plist_file_size) {
6871 DBG_ERR("Short read on [%s]: %zu/%zd\n",
6872 fsp_str_dbg(fsp), nread, plist_file_size);
6878 status = close_file(NULL, fsp, NORMAL_CLOSE);
6880 if (!NT_STATUS_IS_OK(status)) {
6881 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
6886 lines = file_lines_parse((char *)file_data,
6890 if (lines == NULL) {
6895 band_size_line = fruit_get_bandsize_line(lines, numlines);
6896 if (band_size_line == NULL) {
6897 DBG_ERR("Didn't find band-size key in [%s]\n",
6898 smb_fname_str_dbg(smb_fname));
6903 ok = fruit_get_bandsize_from_line(band_size_line, band_size);
6905 DBG_ERR("fruit_get_bandsize_from_line failed\n");
6909 DBG_DEBUG("Parsed band-size [%zu] for [%s]\n", *band_size, plist);
6913 status = close_file(NULL, fsp, NORMAL_CLOSE);
6914 if (!NT_STATUS_IS_OK(status)) {
6915 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
6920 TALLOC_FREE(smb_fname);
6921 TALLOC_FREE(file_data);
6926 struct fruit_disk_free_state {
6930 static bool fruit_get_num_bands(vfs_handle_struct *handle,
6935 struct smb_filename *bands_dir = NULL;
6937 struct dirent *e = NULL;
6941 path = talloc_asprintf(talloc_tos(),
6943 handle->conn->connectpath,
6949 bands_dir = synthetic_smb_fname(talloc_tos(),
6955 if (bands_dir == NULL) {
6959 d = SMB_VFS_NEXT_OPENDIR(handle, bands_dir, NULL, 0);
6961 TALLOC_FREE(bands_dir);
6967 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
6969 e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
6971 if (ISDOT(e->d_name) || ISDOTDOT(e->d_name)) {
6977 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
6979 TALLOC_FREE(bands_dir);
6983 DBG_DEBUG("%zu bands in [%s]\n", nbands, smb_fname_str_dbg(bands_dir));
6985 TALLOC_FREE(bands_dir);
6991 static bool fruit_tmsize_do_dirent(vfs_handle_struct *handle,
6992 struct fruit_disk_free_state *state,
6997 size_t sparsebundle_strlen = strlen("sparsebundle");
6998 size_t bandsize = 0;
7002 p = strstr(e->d_name, "sparsebundle");
7007 if (p[sparsebundle_strlen] != '\0') {
7011 DBG_DEBUG("Processing sparsebundle [%s]\n", e->d_name);
7013 ok = fruit_get_bandsize(handle, e->d_name, &bandsize);
7016 * Beware of race conditions: this may be an uninitialized
7017 * Info.plist that a client is just creating. We don't want let
7018 * this to trigger complete failure.
7020 DBG_ERR("Processing sparsebundle [%s] failed\n", e->d_name);
7024 ok = fruit_get_num_bands(handle, e->d_name, &nbands);
7027 * Beware of race conditions: this may be a backup sparsebundle
7028 * in an early stage lacking a bands subdirectory. We don't want
7029 * let this to trigger complete failure.
7031 DBG_ERR("Processing sparsebundle [%s] failed\n", e->d_name);
7035 if (bandsize > SIZE_MAX/nbands) {
7036 DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
7040 tm_size = bandsize * nbands;
7042 if (state->total_size + tm_size < state->total_size) {
7043 DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
7048 state->total_size += tm_size;
7050 DBG_DEBUG("[%s] tm_size [%jd] total_size [%jd]\n",
7051 e->d_name, (intmax_t)tm_size, (intmax_t)state->total_size);
7057 * Calculate used size of a TimeMachine volume
7059 * This assumes that the volume is used only for TimeMachine.
7061 * - readdir(basedir of share), then
7062 * - for every element that matches regex "^\(.*\)\.sparsebundle$" :
7063 * - parse "\1.sparsebundle/Info.plist" and read the band-size XML key
7064 * - count band files in "\1.sparsebundle/bands/"
7065 * - calculate used size of all bands: band_count * band_size
7067 static uint64_t fruit_disk_free(vfs_handle_struct *handle,
7068 const struct smb_filename *smb_fname,
7073 struct fruit_config_data *config = NULL;
7074 struct fruit_disk_free_state state = {0};
7076 struct dirent *e = NULL;
7082 SMB_VFS_HANDLE_GET_DATA(handle, config,
7083 struct fruit_config_data,
7086 if (!config->time_machine ||
7087 config->time_machine_max_size == 0)
7089 return SMB_VFS_NEXT_DISK_FREE(handle,
7096 d = SMB_VFS_NEXT_OPENDIR(handle, smb_fname, NULL, 0);
7101 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
7103 e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
7105 ok = fruit_tmsize_do_dirent(handle, &state, e);
7107 SMB_VFS_NEXT_CLOSEDIR(handle, d);
7112 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
7117 dsize = config->time_machine_max_size / 512;
7118 dfree = dsize - (state.total_size / 512);
7119 if (dfree > dsize) {
7129 static struct vfs_fn_pointers vfs_fruit_fns = {
7130 .connect_fn = fruit_connect,
7131 .disk_free_fn = fruit_disk_free,
7133 /* File operations */
7134 .chmod_fn = fruit_chmod,
7135 .chown_fn = fruit_chown,
7136 .unlink_fn = fruit_unlink,
7137 .rename_fn = fruit_rename,
7138 .rmdir_fn = fruit_rmdir,
7139 .open_fn = fruit_open,
7140 .close_fn = fruit_close,
7141 .pread_fn = fruit_pread,
7142 .pwrite_fn = fruit_pwrite,
7143 .pread_send_fn = fruit_pread_send,
7144 .pread_recv_fn = fruit_pread_recv,
7145 .pwrite_send_fn = fruit_pwrite_send,
7146 .pwrite_recv_fn = fruit_pwrite_recv,
7147 .stat_fn = fruit_stat,
7148 .lstat_fn = fruit_lstat,
7149 .fstat_fn = fruit_fstat,
7150 .streaminfo_fn = fruit_streaminfo,
7151 .ntimes_fn = fruit_ntimes,
7152 .ftruncate_fn = fruit_ftruncate,
7153 .fallocate_fn = fruit_fallocate,
7154 .create_file_fn = fruit_create_file,
7155 .readdir_attr_fn = fruit_readdir_attr,
7156 .offload_read_send_fn = fruit_offload_read_send,
7157 .offload_read_recv_fn = fruit_offload_read_recv,
7158 .offload_write_send_fn = fruit_offload_write_send,
7159 .offload_write_recv_fn = fruit_offload_write_recv,
7161 /* NT ACL operations */
7162 .fget_nt_acl_fn = fruit_fget_nt_acl,
7163 .fset_nt_acl_fn = fruit_fset_nt_acl,
7167 NTSTATUS vfs_fruit_init(TALLOC_CTX *ctx)
7169 NTSTATUS ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "fruit",
7171 if (!NT_STATUS_IS_OK(ret)) {
7175 vfs_fruit_debug_level = debug_add_class("fruit");
7176 if (vfs_fruit_debug_level == -1) {
7177 vfs_fruit_debug_level = DBGC_VFS;
7178 DEBUG(0, ("%s: Couldn't register custom debugging class!\n",
7181 DEBUG(10, ("%s: Debug class number of '%s': %d\n",
7182 "vfs_fruit_init","fruit",vfs_fruit_debug_level));