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 * FIXME: direct ftruncate(), but we don't have a fsp for the
1322 rc = ftruncate(ad->ad_fsp->fh->fd, ADEDOFF_RFORK_DOT_UND +
1323 ad_getentrylen(ad, ADEID_RFORK));
1331 static bool ad_convert_blank_rfork(vfs_handle_struct *handle,
1335 struct fruit_config_data *config = NULL;
1336 uint8_t *map = MAP_FAILED;
1345 SMB_VFS_HANDLE_GET_DATA(handle, config,
1346 struct fruit_config_data, return false);
1348 if (!config->wipe_intentionally_left_blank_rfork) {
1352 if (ad_getentrylen(ad, ADEID_RFORK) != sizeof(empty_resourcefork)) {
1356 maplen = ad_getentryoff(ad, ADEID_RFORK) +
1357 ad_getentrylen(ad, ADEID_RFORK);
1359 /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
1360 map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
1361 ad->ad_fsp->fh->fd, 0);
1362 if (map == MAP_FAILED) {
1363 DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
1367 cmp = memcmp(map + ADEDOFF_RFORK_DOT_UND,
1369 sizeof(empty_resourcefork));
1370 rc = munmap(map, maplen);
1372 DBG_ERR("munmap failed: %s\n", strerror(errno));
1380 ad_setentrylen(ad, ADEID_RFORK, 0);
1387 len = sys_pwrite(ad->ad_fsp->fh->fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
1388 if (len != AD_DATASZ_DOT_UND) {
1396 static bool ad_convert_delete_adfile(vfs_handle_struct *handle,
1398 const struct smb_filename *smb_fname)
1400 struct fruit_config_data *config = NULL;
1401 struct smb_filename *ad_name = NULL;
1404 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
1408 SMB_VFS_HANDLE_GET_DATA(handle, config,
1409 struct fruit_config_data, return false);
1411 if (!config->delete_empty_adfiles) {
1415 rc = adouble_path(talloc_tos(), smb_fname, &ad_name);
1420 rc = SMB_VFS_NEXT_UNLINK(handle, ad_name);
1422 DBG_ERR("Unlinking [%s] failed: %s\n",
1423 smb_fname_str_dbg(ad_name), strerror(errno));
1424 TALLOC_FREE(ad_name);
1428 DBG_WARNING("Unlinked [%s] after conversion\n", smb_fname_str_dbg(ad_name));
1429 TALLOC_FREE(ad_name);
1435 * Convert from Apple's ._ file to Netatalk
1437 * Apple's AppleDouble may contain a FinderInfo entry longer then 32
1438 * bytes containing packed xattrs.
1440 * @return -1 in case an error occurred, 0 if no conversion was done, 1
1443 static int ad_convert(struct vfs_handle_struct *handle,
1444 const struct smb_filename *smb_fname)
1446 struct adouble *ad = NULL;
1448 bool converted_xattr = false;
1452 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
1457 ok = ad_convert_xattr(handle, ad, smb_fname, &converted_xattr);
1463 ok = ad_convert_blank_rfork(handle, ad, &blank);
1469 if (converted_xattr || blank) {
1470 ok = ad_convert_truncate(handle, ad, smb_fname);
1477 ok = ad_convert_finderinfo(handle, ad, smb_fname);
1479 DBG_ERR("Failed to convert [%s]\n",
1480 smb_fname_str_dbg(smb_fname));
1485 ok = ad_convert_delete_adfile(handle, ad, smb_fname);
1498 * Read and parse Netatalk AppleDouble metadata xattr
1500 static ssize_t ad_read_meta(vfs_handle_struct *handle,
1502 const struct smb_filename *smb_fname)
1508 DEBUG(10, ("reading meta xattr for %s\n", smb_fname->base_name));
1510 ealen = SMB_VFS_GETXATTR(handle->conn, smb_fname,
1511 AFPINFO_EA_NETATALK, ad->ad_data,
1517 if (errno == ENOATTR) {
1523 DEBUG(2, ("error reading meta xattr: %s\n",
1529 if (ealen != AD_DATASZ_XATTR) {
1530 DEBUG(2, ("bad size %zd\n", ealen));
1536 /* Now parse entries */
1537 ok = ad_unpack(ad, ADEID_NUM_XATTR, AD_DATASZ_XATTR);
1539 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1545 if (!ad_getentryoff(ad, ADEID_FINDERI)
1546 || !ad_getentryoff(ad, ADEID_COMMENT)
1547 || !ad_getentryoff(ad, ADEID_FILEDATESI)
1548 || !ad_getentryoff(ad, ADEID_AFPFILEI)
1549 || !ad_getentryoff(ad, ADEID_PRIVDEV)
1550 || !ad_getentryoff(ad, ADEID_PRIVINO)
1551 || !ad_getentryoff(ad, ADEID_PRIVSYN)
1552 || !ad_getentryoff(ad, ADEID_PRIVID)) {
1553 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1560 DEBUG(10, ("reading meta xattr for %s, rc: %d\n",
1561 smb_fname->base_name, rc));
1565 if (errno == EINVAL) {
1567 (void)SMB_VFS_REMOVEXATTR(handle->conn,
1569 AFPINFO_EA_NETATALK);
1577 static int ad_open_rsrc(vfs_handle_struct *handle,
1578 const struct smb_filename *smb_fname,
1581 files_struct **_fsp)
1584 struct smb_filename *adp_smb_fname = NULL;
1585 files_struct *fsp = NULL;
1586 uint32_t access_mask;
1587 uint32_t share_access;
1588 uint32_t create_disposition;
1591 ret = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
1596 ret = SMB_VFS_STAT(handle->conn, adp_smb_fname);
1598 TALLOC_FREE(adp_smb_fname);
1602 access_mask = FILE_GENERIC_READ;
1603 share_access = FILE_SHARE_READ | FILE_SHARE_WRITE;
1604 create_disposition = FILE_OPEN;
1606 if (flags & O_RDWR) {
1607 access_mask |= FILE_GENERIC_WRITE;
1608 share_access &= ~FILE_SHARE_WRITE;
1611 status = SMB_VFS_CREATE_FILE(
1612 handle->conn, /* conn */
1614 0, /* root_dir_fid */
1619 0, /* create_options */
1620 0, /* file_attributes */
1621 INTERNAL_OPEN_ONLY, /* oplock_request */
1623 0, /* allocation_size */
1624 0, /* private_flags */
1629 NULL, NULL); /* create context */
1630 TALLOC_FREE(adp_smb_fname);
1631 if (!NT_STATUS_IS_OK(status)) {
1632 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1641 * Here's the deal: for ADOUBLE_META we can do without an fd as we can issue
1642 * path based xattr calls. For ADOUBLE_RSRC however we need a full-fledged fd
1643 * for file IO on the ._ file.
1645 static int ad_open(vfs_handle_struct *handle,
1648 const struct smb_filename *smb_fname,
1654 DBG_DEBUG("Path [%s] type [%s]\n", smb_fname->base_name,
1655 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
1657 if (ad->ad_type == ADOUBLE_META) {
1663 ad->ad_opened = false;
1667 ret = ad_open_rsrc(handle, smb_fname, flags, mode, &ad->ad_fsp);
1671 ad->ad_opened = true;
1673 DBG_DEBUG("Path [%s] type [%s]\n",
1674 smb_fname->base_name,
1675 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
1680 static ssize_t ad_read_rsrc_adouble(vfs_handle_struct *handle,
1682 const struct smb_filename *smb_fname)
1684 SMB_STRUCT_STAT sbuf;
1691 ret = sys_fstat(ad->ad_fsp->fh->fd, &sbuf, lp_fake_directory_create_times(
1692 SNUM(handle->conn)));
1698 * AppleDouble file header content and size, two cases:
1700 * - without xattrs it is exactly AD_DATASZ_DOT_UND (82) bytes large
1701 * - with embedded xattrs it can be larger, up to AD_XATTR_MAX_HDR_SIZE
1703 * Read as much as we can up to AD_XATTR_MAX_HDR_SIZE.
1705 size = sbuf.st_ex_size;
1706 if (size > talloc_array_length(ad->ad_data)) {
1707 if (size > AD_XATTR_MAX_HDR_SIZE) {
1708 size = AD_XATTR_MAX_HDR_SIZE;
1710 p_ad = talloc_realloc(ad, ad->ad_data, char, size);
1717 len = sys_pread(ad->ad_fsp->fh->fd, ad->ad_data,
1718 talloc_array_length(ad->ad_data), 0);
1719 if (len != talloc_array_length(ad->ad_data)) {
1720 DBG_NOTICE("%s %s: bad size: %zd\n",
1721 smb_fname->base_name, strerror(errno), len);
1725 /* Now parse entries */
1726 ok = ad_unpack(ad, ADEID_NUM_DOT_UND, sbuf.st_ex_size);
1728 DBG_ERR("invalid AppleDouble resource %s\n",
1729 smb_fname->base_name);
1734 if ((ad_getentryoff(ad, ADEID_FINDERI) != ADEDOFF_FINDERI_DOT_UND)
1735 || (ad_getentrylen(ad, ADEID_FINDERI) < ADEDLEN_FINDERI)
1736 || (ad_getentryoff(ad, ADEID_RFORK) < ADEDOFF_RFORK_DOT_UND)) {
1737 DBG_ERR("invalid AppleDouble resource %s\n",
1738 smb_fname->base_name);
1747 * Read and parse resource fork, either ._ AppleDouble file or xattr
1749 static ssize_t ad_read_rsrc(vfs_handle_struct *handle,
1751 const struct smb_filename *smb_fname)
1753 return ad_read_rsrc_adouble(handle, ad, smb_fname);
1757 * Read and unpack an AppleDouble metadata xattr or resource
1759 static ssize_t ad_read(vfs_handle_struct *handle,
1761 const struct smb_filename *smb_fname)
1763 switch (ad->ad_type) {
1765 return ad_read_meta(handle, ad, smb_fname);
1767 return ad_read_rsrc(handle, ad, smb_fname);
1773 static int adouble_destructor(struct adouble *ad)
1777 if (!ad->ad_opened) {
1781 SMB_ASSERT(ad->ad_fsp != NULL);
1783 status = close_file(NULL, ad->ad_fsp, NORMAL_CLOSE);
1784 if (!NT_STATUS_IS_OK(status)) {
1785 DBG_ERR("Closing [%s] failed: %s\n",
1786 fsp_str_dbg(ad->ad_fsp), nt_errstr(status));
1793 * Allocate a struct adouble without initialiing it
1795 * The struct is either hang of the fsp extension context or if fsp is
1798 * @param[in] ctx talloc context
1799 * @param[in] handle vfs handle
1800 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1802 * @return adouble handle
1804 static struct adouble *ad_alloc(TALLOC_CTX *ctx,
1805 adouble_type_t type)
1813 adsize = AD_DATASZ_XATTR;
1816 adsize = AD_DATASZ_DOT_UND;
1822 ad = talloc_zero(ctx, struct adouble);
1829 ad->ad_data = talloc_zero_array(ad, char, adsize);
1830 if (ad->ad_data == NULL) {
1837 ad->ad_magic = AD_MAGIC;
1838 ad->ad_version = AD_VERSION;
1840 talloc_set_destructor(ad, adouble_destructor);
1850 * Allocate and initialize a new struct adouble
1852 * @param[in] ctx talloc context
1853 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1855 * @return adouble handle, initialized
1857 static struct adouble *ad_init(TALLOC_CTX *ctx,
1858 adouble_type_t type)
1861 const struct ad_entry_order *eid;
1862 struct adouble *ad = NULL;
1863 time_t t = time(NULL);
1867 eid = entry_order_meta_xattr;
1870 eid = entry_order_dot_und;
1876 ad = ad_alloc(ctx, type);
1882 ad->ad_eid[eid->id].ade_off = eid->offset;
1883 ad->ad_eid[eid->id].ade_len = eid->len;
1887 /* put something sane in the date fields */
1888 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, t);
1889 ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, t);
1890 ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, t);
1891 ad_setdate(ad, AD_DATE_BACKUP, htonl(AD_DATE_START));
1899 static struct adouble *ad_get_internal(TALLOC_CTX *ctx,
1900 vfs_handle_struct *handle,
1902 const struct smb_filename *smb_fname,
1903 adouble_type_t type)
1907 struct adouble *ad = NULL;
1911 smb_fname = fsp->base_fsp->fsp_name;
1914 DEBUG(10, ("ad_get(%s) called for %s\n",
1915 type == ADOUBLE_META ? "meta" : "rsrc",
1916 smb_fname->base_name));
1918 ad = ad_alloc(ctx, type);
1924 /* Try rw first so we can use the fd in ad_convert() */
1927 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
1928 if (rc == -1 && ((errno == EROFS) || (errno == EACCES))) {
1930 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
1933 DBG_DEBUG("ad_open [%s] error [%s]\n",
1934 smb_fname->base_name, strerror(errno));
1939 len = ad_read(handle, ad, smb_fname);
1941 DEBUG(10, ("error reading AppleDouble for %s\n",
1942 smb_fname->base_name));
1948 DEBUG(10, ("ad_get(%s) for %s returning %d\n",
1949 type == ADOUBLE_META ? "meta" : "rsrc",
1950 smb_fname->base_name, rc));
1959 * Return AppleDouble data for a file
1961 * @param[in] ctx talloc context
1962 * @param[in] handle vfs handle
1963 * @param[in] smb_fname pathname to file or directory
1964 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1966 * @return talloced struct adouble or NULL on error
1968 static struct adouble *ad_get(TALLOC_CTX *ctx,
1969 vfs_handle_struct *handle,
1970 const struct smb_filename *smb_fname,
1971 adouble_type_t type)
1973 return ad_get_internal(ctx, handle, NULL, smb_fname, type);
1977 * Return AppleDouble data for a file
1979 * @param[in] ctx talloc context
1980 * @param[in] handle vfs handle
1981 * @param[in] fsp fsp to use for IO
1982 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1984 * @return talloced struct adouble or NULL on error
1986 static struct adouble *ad_fget(TALLOC_CTX *ctx, vfs_handle_struct *handle,
1987 files_struct *fsp, adouble_type_t type)
1989 return ad_get_internal(ctx, handle, fsp, NULL, type);
1993 * Set AppleDouble metadata on a file or directory
1995 * @param[in] ad adouble handle
1997 * @param[in] smb_fname pathname to file or directory
1999 * @return status code, 0 means success
2001 static int ad_set(vfs_handle_struct *handle,
2003 const struct smb_filename *smb_fname)
2008 DBG_DEBUG("Path [%s]\n", smb_fname->base_name);
2010 if (ad->ad_type != ADOUBLE_META) {
2011 DBG_ERR("ad_set on [%s] used with ADOUBLE_RSRC\n",
2012 smb_fname->base_name);
2021 ret = SMB_VFS_SETXATTR(handle->conn,
2023 AFPINFO_EA_NETATALK,
2025 AD_DATASZ_XATTR, 0);
2027 DBG_DEBUG("Path [%s] ret [%d]\n", smb_fname->base_name, ret);
2033 * Set AppleDouble metadata on a file or directory
2035 * @param[in] ad adouble handle
2036 * @param[in] fsp file handle
2038 * @return status code, 0 means success
2040 static int ad_fset(struct vfs_handle_struct *handle,
2048 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
2051 || (fsp->fh == NULL)
2052 || (fsp->fh->fd == -1))
2054 smb_panic("bad fsp");
2062 switch (ad->ad_type) {
2064 rc = SMB_VFS_NEXT_SETXATTR(handle,
2066 AFPINFO_EA_NETATALK,
2068 AD_DATASZ_XATTR, 0);
2072 len = SMB_VFS_NEXT_PWRITE(handle,
2077 if (len != AD_DATASZ_DOT_UND) {
2078 DBG_ERR("short write on %s: %zd", fsp_str_dbg(fsp), len);
2088 DBG_DEBUG("Path [%s] rc [%d]\n", fsp_str_dbg(fsp), rc);
2093 /*****************************************************************************
2095 *****************************************************************************/
2097 static bool is_afpinfo_stream(const struct smb_filename *smb_fname)
2099 if (strncasecmp_m(smb_fname->stream_name,
2100 AFPINFO_STREAM_NAME,
2101 strlen(AFPINFO_STREAM_NAME)) == 0) {
2107 static bool is_afpresource_stream(const struct smb_filename *smb_fname)
2109 if (strncasecmp_m(smb_fname->stream_name,
2110 AFPRESOURCE_STREAM_NAME,
2111 strlen(AFPRESOURCE_STREAM_NAME)) == 0) {
2118 * Test whether stream is an Apple stream.
2120 static bool is_apple_stream(const struct smb_filename *smb_fname)
2122 if (is_afpinfo_stream(smb_fname)) {
2125 if (is_afpresource_stream(smb_fname)) {
2131 static bool is_adouble_file(const char *path)
2133 const char *p = NULL;
2136 p = strrchr(path, '/');
2144 ADOUBLE_NAME_PREFIX,
2145 strlen(ADOUBLE_NAME_PREFIX));
2153 * Initialize config struct from our smb.conf config parameters
2155 static int init_fruit_config(vfs_handle_struct *handle)
2157 struct fruit_config_data *config;
2159 const char *tm_size_str = NULL;
2161 config = talloc_zero(handle->conn, struct fruit_config_data);
2163 DEBUG(1, ("talloc_zero() failed\n"));
2169 * Versions up to Samba 4.5.x had a spelling bug in the
2170 * fruit:resource option calling lp_parm_enum with
2171 * "res*s*ource" (ie two s).
2173 * In Samba 4.6 we accept both the wrong and the correct
2174 * spelling, in Samba 4.7 the bad spelling will be removed.
2176 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2177 "ressource", fruit_rsrc, FRUIT_RSRC_ADFILE);
2178 if (enumval == -1) {
2179 DEBUG(1, ("value for %s: resource type unknown\n",
2180 FRUIT_PARAM_TYPE_NAME));
2183 config->rsrc = (enum fruit_rsrc)enumval;
2185 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2186 "resource", fruit_rsrc, enumval);
2187 if (enumval == -1) {
2188 DEBUG(1, ("value for %s: resource type unknown\n",
2189 FRUIT_PARAM_TYPE_NAME));
2192 config->rsrc = (enum fruit_rsrc)enumval;
2194 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2195 "metadata", fruit_meta, FRUIT_META_NETATALK);
2196 if (enumval == -1) {
2197 DEBUG(1, ("value for %s: metadata type unknown\n",
2198 FRUIT_PARAM_TYPE_NAME));
2201 config->meta = (enum fruit_meta)enumval;
2203 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2204 "locking", fruit_locking, FRUIT_LOCKING_NONE);
2205 if (enumval == -1) {
2206 DEBUG(1, ("value for %s: locking type unknown\n",
2207 FRUIT_PARAM_TYPE_NAME));
2210 config->locking = (enum fruit_locking)enumval;
2212 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2213 "encoding", fruit_encoding, FRUIT_ENC_PRIVATE);
2214 if (enumval == -1) {
2215 DEBUG(1, ("value for %s: encoding type unknown\n",
2216 FRUIT_PARAM_TYPE_NAME));
2219 config->encoding = (enum fruit_encoding)enumval;
2221 if (config->rsrc == FRUIT_RSRC_ADFILE) {
2222 config->veto_appledouble = lp_parm_bool(SNUM(handle->conn),
2223 FRUIT_PARAM_TYPE_NAME,
2228 config->use_aapl = lp_parm_bool(
2229 -1, FRUIT_PARAM_TYPE_NAME, "aapl", true);
2231 config->time_machine = lp_parm_bool(
2232 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "time machine", false);
2234 config->unix_info_enabled = lp_parm_bool(
2235 -1, FRUIT_PARAM_TYPE_NAME, "nfs_aces", true);
2237 config->use_copyfile = lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME,
2240 config->posix_rename = lp_parm_bool(
2241 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "posix_rename", true);
2243 config->aapl_zero_file_id =
2244 lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME, "zero_file_id", true);
2246 config->readdir_attr_rsize = lp_parm_bool(
2247 SNUM(handle->conn), "readdir_attr", "aapl_rsize", true);
2249 config->readdir_attr_finder_info = lp_parm_bool(
2250 SNUM(handle->conn), "readdir_attr", "aapl_finder_info", true);
2252 config->readdir_attr_max_access = lp_parm_bool(
2253 SNUM(handle->conn), "readdir_attr", "aapl_max_access", true);
2255 config->model = lp_parm_const_string(
2256 -1, FRUIT_PARAM_TYPE_NAME, "model", "MacSamba");
2258 tm_size_str = lp_parm_const_string(
2259 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2260 "time machine max size", NULL);
2261 if (tm_size_str != NULL) {
2262 config->time_machine_max_size = conv_str_size(tm_size_str);
2265 config->wipe_intentionally_left_blank_rfork = lp_parm_bool(
2266 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2267 "wipe_intentionally_left_blank_rfork", false);
2269 config->delete_empty_adfiles = lp_parm_bool(
2270 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2271 "delete_empty_adfiles", false);
2273 SMB_VFS_HANDLE_SET_DATA(handle, config,
2274 NULL, struct fruit_config_data,
2281 * Prepend "._" to a basename
2282 * Return a new struct smb_filename with stream_name == NULL.
2284 static int adouble_path(TALLOC_CTX *ctx,
2285 const struct smb_filename *smb_fname_in,
2286 struct smb_filename **pp_smb_fname_out)
2290 struct smb_filename *smb_fname = cp_smb_filename(ctx,
2293 if (smb_fname == NULL) {
2297 /* We need streamname to be NULL */
2298 TALLOC_FREE(smb_fname->stream_name);
2300 /* And we're replacing base_name. */
2301 TALLOC_FREE(smb_fname->base_name);
2303 if (!parent_dirname(smb_fname, smb_fname_in->base_name,
2305 TALLOC_FREE(smb_fname);
2309 smb_fname->base_name = talloc_asprintf(smb_fname,
2310 "%s/._%s", parent, base);
2311 if (smb_fname->base_name == NULL) {
2312 TALLOC_FREE(smb_fname);
2316 *pp_smb_fname_out = smb_fname;
2322 * Allocate and initialize an AfpInfo struct
2324 static AfpInfo *afpinfo_new(TALLOC_CTX *ctx)
2326 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2330 ai->afpi_Signature = AFP_Signature;
2331 ai->afpi_Version = AFP_Version;
2332 ai->afpi_BackupTime = AD_DATE_START;
2337 * Pack an AfpInfo struct into a buffer
2339 * Buffer size must be at least AFP_INFO_SIZE
2340 * Returns size of packed buffer
2342 static ssize_t afpinfo_pack(const AfpInfo *ai, char *buf)
2344 memset(buf, 0, AFP_INFO_SIZE);
2346 RSIVAL(buf, 0, ai->afpi_Signature);
2347 RSIVAL(buf, 4, ai->afpi_Version);
2348 RSIVAL(buf, 12, ai->afpi_BackupTime);
2349 memcpy(buf + 16, ai->afpi_FinderInfo, sizeof(ai->afpi_FinderInfo));
2351 return AFP_INFO_SIZE;
2355 * Unpack a buffer into a AfpInfo structure
2357 * Buffer size must be at least AFP_INFO_SIZE
2358 * Returns allocated AfpInfo struct
2360 static AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data)
2362 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2367 ai->afpi_Signature = RIVAL(data, 0);
2368 ai->afpi_Version = RIVAL(data, 4);
2369 ai->afpi_BackupTime = RIVAL(data, 12);
2370 memcpy(ai->afpi_FinderInfo, (const char *)data + 16,
2371 sizeof(ai->afpi_FinderInfo));
2373 if (ai->afpi_Signature != AFP_Signature
2374 || ai->afpi_Version != AFP_Version) {
2375 DEBUG(1, ("Bad AfpInfo signature or version\n"));
2383 * Fake an inode number from the md5 hash of the (xattr) name
2385 static SMB_INO_T fruit_inode(const SMB_STRUCT_STAT *sbuf, const char *sname)
2387 gnutls_hash_hd_t hash_hnd = NULL;
2388 unsigned char hash[16];
2389 SMB_INO_T result = 0;
2393 DBG_DEBUG("fruit_inode called for %ju/%ju [%s]\n",
2394 (uintmax_t)sbuf->st_ex_dev,
2395 (uintmax_t)sbuf->st_ex_ino, sname);
2397 upper_sname = talloc_strdup_upper(talloc_tos(), sname);
2398 SMB_ASSERT(upper_sname != NULL);
2400 rc = gnutls_hash_init(&hash_hnd, GNUTLS_DIG_MD5);
2405 rc = gnutls_hash(hash_hnd, &(sbuf->st_ex_dev), sizeof(sbuf->st_ex_dev));
2407 gnutls_hash_deinit(hash_hnd, NULL);
2410 rc = gnutls_hash(hash_hnd,
2412 sizeof(sbuf->st_ex_ino));
2414 gnutls_hash_deinit(hash_hnd, NULL);
2417 rc = gnutls_hash(hash_hnd,
2419 talloc_get_size(upper_sname) - 1);
2421 gnutls_hash_deinit(hash_hnd, NULL);
2425 gnutls_hash_deinit(hash_hnd, hash);
2427 /* Hopefully all the variation is in the lower 4 (or 8) bytes! */
2428 memcpy(&result, hash, sizeof(result));
2431 DBG_DEBUG("fruit_inode \"%s\": ino=%ju\n",
2432 sname, (uintmax_t)result);
2435 TALLOC_FREE(upper_sname);
2440 static bool add_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
2441 struct stream_struct **streams,
2442 const char *name, off_t size,
2445 struct stream_struct *tmp;
2447 tmp = talloc_realloc(mem_ctx, *streams, struct stream_struct,
2453 tmp[*num_streams].name = talloc_asprintf(tmp, "%s:$DATA", name);
2454 if (tmp[*num_streams].name == NULL) {
2458 tmp[*num_streams].size = size;
2459 tmp[*num_streams].alloc_size = alloc_size;
2466 static bool filter_empty_rsrc_stream(unsigned int *num_streams,
2467 struct stream_struct **streams)
2469 struct stream_struct *tmp = *streams;
2472 if (*num_streams == 0) {
2476 for (i = 0; i < *num_streams; i++) {
2477 if (strequal_m(tmp[i].name, AFPRESOURCE_STREAM)) {
2482 if (i == *num_streams) {
2486 if (tmp[i].size > 0) {
2490 TALLOC_FREE(tmp[i].name);
2491 if (*num_streams - 1 > i) {
2492 memmove(&tmp[i], &tmp[i+1],
2493 (*num_streams - i - 1) * sizeof(struct stream_struct));
2500 static bool del_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
2501 struct stream_struct **streams,
2504 struct stream_struct *tmp = *streams;
2507 if (*num_streams == 0) {
2511 for (i = 0; i < *num_streams; i++) {
2512 if (strequal_m(tmp[i].name, name)) {
2517 if (i == *num_streams) {
2521 TALLOC_FREE(tmp[i].name);
2522 if (*num_streams - 1 > i) {
2523 memmove(&tmp[i], &tmp[i+1],
2524 (*num_streams - i - 1) * sizeof(struct stream_struct));
2531 static bool ad_empty_finderinfo(const struct adouble *ad)
2534 char emptybuf[ADEDLEN_FINDERI] = {0};
2537 fi = ad_get_entry(ad, ADEID_FINDERI);
2539 DBG_ERR("Missing FinderInfo in struct adouble [%p]\n", ad);
2543 cmp = memcmp(emptybuf, fi, ADEDLEN_FINDERI);
2547 static bool ai_empty_finderinfo(const AfpInfo *ai)
2550 char emptybuf[ADEDLEN_FINDERI] = {0};
2552 cmp = memcmp(emptybuf, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
2557 * Update btime with btime from Netatalk
2559 static void update_btime(vfs_handle_struct *handle,
2560 struct smb_filename *smb_fname)
2563 struct timespec creation_time = {0};
2565 struct fruit_config_data *config = NULL;
2567 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
2570 switch (config->meta) {
2571 case FRUIT_META_STREAM:
2573 case FRUIT_META_NETATALK:
2577 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
2581 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
2585 if (ad_getdate(ad, AD_DATE_UNIX | AD_DATE_CREATE, &t) != 0) {
2591 creation_time.tv_sec = convert_uint32_t_to_time_t(t);
2592 update_stat_ex_create_time(&smb_fname->st, creation_time);
2598 * Map an access mask to a Netatalk single byte byte range lock
2600 static off_t access_to_netatalk_brl(enum apple_fork fork_type,
2601 uint32_t access_mask)
2605 switch (access_mask) {
2606 case FILE_READ_DATA:
2607 offset = AD_FILELOCK_OPEN_RD;
2610 case FILE_WRITE_DATA:
2611 case FILE_APPEND_DATA:
2612 offset = AD_FILELOCK_OPEN_WR;
2616 offset = AD_FILELOCK_OPEN_NONE;
2620 if (fork_type == APPLE_FORK_RSRC) {
2621 if (offset == AD_FILELOCK_OPEN_NONE) {
2622 offset = AD_FILELOCK_RSRC_OPEN_NONE;
2632 * Map a deny mode to a Netatalk brl
2634 static off_t denymode_to_netatalk_brl(enum apple_fork fork_type,
2639 switch (deny_mode) {
2641 offset = AD_FILELOCK_DENY_RD;
2645 offset = AD_FILELOCK_DENY_WR;
2649 smb_panic("denymode_to_netatalk_brl: bad deny mode\n");
2652 if (fork_type == APPLE_FORK_RSRC) {
2660 * Call fcntl() with an exclusive F_GETLK request in order to
2661 * determine if there's an exisiting shared lock
2663 * @return true if the requested lock was found or any error occurred
2664 * false if the lock was not found
2666 static bool test_netatalk_lock(files_struct *fsp, off_t in_offset)
2669 off_t offset = in_offset;
2674 result = SMB_VFS_GETLOCK(fsp, &offset, &len, &type, &pid);
2675 if (result == false) {
2679 if (type != F_UNLCK) {
2686 static NTSTATUS fruit_check_access(vfs_handle_struct *handle,
2688 uint32_t access_mask,
2689 uint32_t share_mode)
2691 NTSTATUS status = NT_STATUS_OK;
2693 bool share_for_read = (share_mode & FILE_SHARE_READ);
2694 bool share_for_write = (share_mode & FILE_SHARE_WRITE);
2695 bool netatalk_already_open_for_reading = false;
2696 bool netatalk_already_open_for_writing = false;
2697 bool netatalk_already_open_with_deny_read = false;
2698 bool netatalk_already_open_with_deny_write = false;
2700 /* FIXME: hardcoded data fork, add resource fork */
2701 enum apple_fork fork_type = APPLE_FORK_DATA;
2703 DBG_DEBUG("fruit_check_access: %s, am: %s/%s, sm: 0x%x\n",
2705 access_mask & FILE_READ_DATA ? "READ" :"-",
2706 access_mask & FILE_WRITE_DATA ? "WRITE" : "-",
2709 if (fsp->fh->fd == -1) {
2710 return NT_STATUS_OK;
2713 /* Read NetATalk opens and deny modes on the file. */
2714 netatalk_already_open_for_reading = test_netatalk_lock(fsp,
2715 access_to_netatalk_brl(fork_type,
2718 netatalk_already_open_with_deny_read = test_netatalk_lock(fsp,
2719 denymode_to_netatalk_brl(fork_type,
2722 netatalk_already_open_for_writing = test_netatalk_lock(fsp,
2723 access_to_netatalk_brl(fork_type,
2726 netatalk_already_open_with_deny_write = test_netatalk_lock(fsp,
2727 denymode_to_netatalk_brl(fork_type,
2730 /* If there are any conflicts - sharing violation. */
2731 if ((access_mask & FILE_READ_DATA) &&
2732 netatalk_already_open_with_deny_read) {
2733 return NT_STATUS_SHARING_VIOLATION;
2736 if (!share_for_read &&
2737 netatalk_already_open_for_reading) {
2738 return NT_STATUS_SHARING_VIOLATION;
2741 if ((access_mask & FILE_WRITE_DATA) &&
2742 netatalk_already_open_with_deny_write) {
2743 return NT_STATUS_SHARING_VIOLATION;
2746 if (!share_for_write &&
2747 netatalk_already_open_for_writing) {
2748 return NT_STATUS_SHARING_VIOLATION;
2751 if (!(access_mask & FILE_READ_DATA)) {
2753 * Nothing we can do here, we need read access
2756 return NT_STATUS_OK;
2759 /* Set NetAtalk locks matching our access */
2760 if (access_mask & FILE_READ_DATA) {
2761 struct byte_range_lock *br_lck = NULL;
2763 off = access_to_netatalk_brl(fork_type, FILE_READ_DATA);
2765 handle->conn->sconn->msg_ctx, fsp,
2766 fsp->op->global->open_persistent_id, 1, off,
2767 READ_LOCK, POSIX_LOCK, false,
2770 TALLOC_FREE(br_lck);
2772 if (!NT_STATUS_IS_OK(status)) {
2777 if (!share_for_read) {
2778 struct byte_range_lock *br_lck = NULL;
2780 off = denymode_to_netatalk_brl(fork_type, DENY_READ);
2782 handle->conn->sconn->msg_ctx, fsp,
2783 fsp->op->global->open_persistent_id, 1, off,
2784 READ_LOCK, POSIX_LOCK, false,
2787 TALLOC_FREE(br_lck);
2789 if (!NT_STATUS_IS_OK(status)) {
2794 if (access_mask & FILE_WRITE_DATA) {
2795 struct byte_range_lock *br_lck = NULL;
2797 off = access_to_netatalk_brl(fork_type, FILE_WRITE_DATA);
2799 handle->conn->sconn->msg_ctx, fsp,
2800 fsp->op->global->open_persistent_id, 1, off,
2801 READ_LOCK, POSIX_LOCK, false,
2804 TALLOC_FREE(br_lck);
2806 if (!NT_STATUS_IS_OK(status)) {
2811 if (!share_for_write) {
2812 struct byte_range_lock *br_lck = NULL;
2814 off = denymode_to_netatalk_brl(fork_type, DENY_WRITE);
2816 handle->conn->sconn->msg_ctx, fsp,
2817 fsp->op->global->open_persistent_id, 1, off,
2818 READ_LOCK, POSIX_LOCK, false,
2821 TALLOC_FREE(br_lck);
2823 if (!NT_STATUS_IS_OK(status)) {
2828 return NT_STATUS_OK;
2831 static NTSTATUS check_aapl(vfs_handle_struct *handle,
2832 struct smb_request *req,
2833 const struct smb2_create_blobs *in_context_blobs,
2834 struct smb2_create_blobs *out_context_blobs)
2836 struct fruit_config_data *config;
2838 struct smb2_create_blob *aapl = NULL;
2842 DATA_BLOB blob = data_blob_talloc(req, NULL, 0);
2843 uint64_t req_bitmap, client_caps;
2844 uint64_t server_caps = SMB2_CRTCTX_AAPL_UNIX_BASED;
2848 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
2849 return NT_STATUS_UNSUCCESSFUL);
2851 if (!config->use_aapl
2852 || in_context_blobs == NULL
2853 || out_context_blobs == NULL) {
2854 return NT_STATUS_OK;
2857 aapl = smb2_create_blob_find(in_context_blobs,
2858 SMB2_CREATE_TAG_AAPL);
2860 return NT_STATUS_OK;
2863 if (aapl->data.length != 24) {
2864 DEBUG(1, ("unexpected AAPL ctxt length: %ju\n",
2865 (uintmax_t)aapl->data.length));
2866 return NT_STATUS_INVALID_PARAMETER;
2869 cmd = IVAL(aapl->data.data, 0);
2870 if (cmd != SMB2_CRTCTX_AAPL_SERVER_QUERY) {
2871 DEBUG(1, ("unsupported AAPL cmd: %d\n", cmd));
2872 return NT_STATUS_INVALID_PARAMETER;
2875 req_bitmap = BVAL(aapl->data.data, 8);
2876 client_caps = BVAL(aapl->data.data, 16);
2878 SIVAL(p, 0, SMB2_CRTCTX_AAPL_SERVER_QUERY);
2880 SBVAL(p, 8, req_bitmap);
2881 ok = data_blob_append(req, &blob, p, 16);
2883 return NT_STATUS_UNSUCCESSFUL;
2886 if (req_bitmap & SMB2_CRTCTX_AAPL_SERVER_CAPS) {
2887 if ((client_caps & SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR) &&
2888 (handle->conn->tcon->compat->fs_capabilities & FILE_NAMED_STREAMS)) {
2889 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR;
2890 config->readdir_attr_enabled = true;
2893 if (config->use_copyfile) {
2894 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_OSX_COPYFILE;
2895 config->copyfile_enabled = true;
2899 * The client doesn't set the flag, so we can't check
2900 * for it and just set it unconditionally
2902 if (config->unix_info_enabled) {
2903 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_NFS_ACE;
2906 SBVAL(p, 0, server_caps);
2907 ok = data_blob_append(req, &blob, p, 8);
2909 return NT_STATUS_UNSUCCESSFUL;
2913 if (req_bitmap & SMB2_CRTCTX_AAPL_VOLUME_CAPS) {
2914 int val = lp_case_sensitive(SNUM(handle->conn->tcon->compat));
2922 caps |= SMB2_CRTCTX_AAPL_CASE_SENSITIVE;
2929 if (config->time_machine) {
2930 caps |= SMB2_CRTCTX_AAPL_FULL_SYNC;
2935 ok = data_blob_append(req, &blob, p, 8);
2937 return NT_STATUS_UNSUCCESSFUL;
2941 if (req_bitmap & SMB2_CRTCTX_AAPL_MODEL_INFO) {
2942 ok = convert_string_talloc(req,
2943 CH_UNIX, CH_UTF16LE,
2944 config->model, strlen(config->model),
2947 return NT_STATUS_UNSUCCESSFUL;
2951 SIVAL(p + 4, 0, modellen);
2952 ok = data_blob_append(req, &blob, p, 8);
2955 return NT_STATUS_UNSUCCESSFUL;
2958 ok = data_blob_append(req, &blob, model, modellen);
2961 return NT_STATUS_UNSUCCESSFUL;
2965 status = smb2_create_blob_add(out_context_blobs,
2967 SMB2_CREATE_TAG_AAPL,
2969 if (NT_STATUS_IS_OK(status)) {
2970 global_fruit_config.nego_aapl = true;
2971 if (config->aapl_zero_file_id) {
2972 aapl_force_zero_file_id(handle->conn->sconn);
2979 static bool readdir_attr_meta_finderi_stream(
2980 struct vfs_handle_struct *handle,
2981 const struct smb_filename *smb_fname,
2984 struct smb_filename *stream_name = NULL;
2985 files_struct *fsp = NULL;
2990 uint8_t buf[AFP_INFO_SIZE];
2992 stream_name = synthetic_smb_fname(talloc_tos(),
2993 smb_fname->base_name,
2994 AFPINFO_STREAM_NAME,
2995 NULL, smb_fname->flags);
2996 if (stream_name == NULL) {
3000 ret = SMB_VFS_STAT(handle->conn, stream_name);
3005 status = SMB_VFS_CREATE_FILE(
3006 handle->conn, /* conn */
3008 0, /* root_dir_fid */
3009 stream_name, /* fname */
3010 FILE_READ_DATA, /* access_mask */
3011 (FILE_SHARE_READ | FILE_SHARE_WRITE | /* share_access */
3013 FILE_OPEN, /* create_disposition*/
3014 0, /* create_options */
3015 0, /* file_attributes */
3016 INTERNAL_OPEN_ONLY, /* oplock_request */
3018 0, /* allocation_size */
3019 0, /* private_flags */
3024 NULL, NULL); /* create context */
3026 TALLOC_FREE(stream_name);
3028 if (!NT_STATUS_IS_OK(status)) {
3032 nread = SMB_VFS_PREAD(fsp, &buf[0], AFP_INFO_SIZE, 0);
3033 if (nread != AFP_INFO_SIZE) {
3034 DBG_ERR("short read [%s] [%zd/%d]\n",
3035 smb_fname_str_dbg(stream_name), nread, AFP_INFO_SIZE);
3040 memcpy(&ai->afpi_FinderInfo[0], &buf[AFP_OFF_FinderInfo],
3047 close_file(NULL, fsp, NORMAL_CLOSE);
3053 static bool readdir_attr_meta_finderi_netatalk(
3054 struct vfs_handle_struct *handle,
3055 const struct smb_filename *smb_fname,
3058 struct adouble *ad = NULL;
3061 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
3066 p = ad_get_entry(ad, ADEID_FINDERI);
3068 DBG_ERR("No ADEID_FINDERI for [%s]\n", smb_fname->base_name);
3073 memcpy(&ai->afpi_FinderInfo[0], p, AFP_FinderSize);
3078 static bool readdir_attr_meta_finderi(struct vfs_handle_struct *handle,
3079 const struct smb_filename *smb_fname,
3080 struct readdir_attr_data *attr_data)
3082 struct fruit_config_data *config = NULL;
3083 uint32_t date_added;
3087 SMB_VFS_HANDLE_GET_DATA(handle, config,
3088 struct fruit_config_data,
3091 switch (config->meta) {
3092 case FRUIT_META_NETATALK:
3093 ok = readdir_attr_meta_finderi_netatalk(
3094 handle, smb_fname, &ai);
3097 case FRUIT_META_STREAM:
3098 ok = readdir_attr_meta_finderi_stream(
3099 handle, smb_fname, &ai);
3103 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3108 /* Don't bother with errors, it's likely ENOENT */
3112 if (S_ISREG(smb_fname->st.st_ex_mode)) {
3114 memcpy(&attr_data->attr_data.aapl.finder_info[0],
3115 &ai.afpi_FinderInfo[0], 4);
3117 /* finder_creator */
3118 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 4,
3119 &ai.afpi_FinderInfo[4], 4);
3123 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 8,
3124 &ai.afpi_FinderInfo[8], 2);
3126 /* finder_ext_flags */
3127 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 10,
3128 &ai.afpi_FinderInfo[24], 2);
3131 date_added = convert_time_t_to_uint32_t(
3132 smb_fname->st.st_ex_btime.tv_sec - AD_DATE_DELTA);
3134 RSIVAL(&attr_data->attr_data.aapl.finder_info[0], 12, date_added);
3139 static uint64_t readdir_attr_rfork_size_adouble(
3140 struct vfs_handle_struct *handle,
3141 const struct smb_filename *smb_fname)
3143 struct adouble *ad = NULL;
3144 uint64_t rfork_size;
3146 ad = ad_get(talloc_tos(), handle, smb_fname,
3152 rfork_size = ad_getentrylen(ad, ADEID_RFORK);
3158 static uint64_t readdir_attr_rfork_size_stream(
3159 struct vfs_handle_struct *handle,
3160 const struct smb_filename *smb_fname)
3162 struct smb_filename *stream_name = NULL;
3164 uint64_t rfork_size;
3166 stream_name = synthetic_smb_fname(talloc_tos(),
3167 smb_fname->base_name,
3168 AFPRESOURCE_STREAM_NAME,
3170 if (stream_name == NULL) {
3174 ret = SMB_VFS_STAT(handle->conn, stream_name);
3176 TALLOC_FREE(stream_name);
3180 rfork_size = stream_name->st.st_ex_size;
3181 TALLOC_FREE(stream_name);
3186 static uint64_t readdir_attr_rfork_size(struct vfs_handle_struct *handle,
3187 const struct smb_filename *smb_fname)
3189 struct fruit_config_data *config = NULL;
3190 uint64_t rfork_size;
3192 SMB_VFS_HANDLE_GET_DATA(handle, config,
3193 struct fruit_config_data,
3196 switch (config->rsrc) {
3197 case FRUIT_RSRC_ADFILE:
3198 rfork_size = readdir_attr_rfork_size_adouble(handle,
3202 case FRUIT_RSRC_XATTR:
3203 case FRUIT_RSRC_STREAM:
3204 rfork_size = readdir_attr_rfork_size_stream(handle,
3209 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3217 static NTSTATUS readdir_attr_macmeta(struct vfs_handle_struct *handle,
3218 const struct smb_filename *smb_fname,
3219 struct readdir_attr_data *attr_data)
3221 NTSTATUS status = NT_STATUS_OK;
3222 struct fruit_config_data *config = NULL;
3225 SMB_VFS_HANDLE_GET_DATA(handle, config,
3226 struct fruit_config_data,
3227 return NT_STATUS_UNSUCCESSFUL);
3230 /* Ensure we return a default value in the creation_date field */
3231 RSIVAL(&attr_data->attr_data.aapl.finder_info, 12, AD_DATE_START);
3234 * Resource fork length
3237 if (config->readdir_attr_rsize) {
3238 uint64_t rfork_size;
3240 rfork_size = readdir_attr_rfork_size(handle, smb_fname);
3241 attr_data->attr_data.aapl.rfork_size = rfork_size;
3248 if (config->readdir_attr_finder_info) {
3249 ok = readdir_attr_meta_finderi(handle, smb_fname, attr_data);
3251 status = NT_STATUS_INTERNAL_ERROR;
3258 static NTSTATUS remove_virtual_nfs_aces(struct security_descriptor *psd)
3263 if (psd->dacl == NULL) {
3264 return NT_STATUS_OK;
3267 for (i = 0; i < psd->dacl->num_aces; i++) {
3268 /* MS NFS style mode/uid/gid */
3269 int cmp = dom_sid_compare_domain(
3270 &global_sid_Unix_NFS,
3271 &psd->dacl->aces[i].trustee);
3273 /* Normal ACE entry. */
3278 * security_descriptor_dacl_del()
3279 * *must* return NT_STATUS_OK as we know
3280 * we have something to remove.
3283 status = security_descriptor_dacl_del(psd,
3284 &psd->dacl->aces[i].trustee);
3285 if (!NT_STATUS_IS_OK(status)) {
3286 DBG_WARNING("failed to remove MS NFS style ACE: %s\n",
3292 * security_descriptor_dacl_del() may delete more
3293 * then one entry subsequent to this one if the
3294 * SID matches, but we only need to ensure that
3295 * we stay looking at the same element in the array.
3299 return NT_STATUS_OK;
3302 /* Search MS NFS style ACE with UNIX mode */
3303 static NTSTATUS check_ms_nfs(vfs_handle_struct *handle,
3305 struct security_descriptor *psd,
3310 struct fruit_config_data *config = NULL;
3314 SMB_VFS_HANDLE_GET_DATA(handle, config,
3315 struct fruit_config_data,
3316 return NT_STATUS_UNSUCCESSFUL);
3318 if (!global_fruit_config.nego_aapl) {
3319 return NT_STATUS_OK;
3321 if (psd->dacl == NULL || !config->unix_info_enabled) {
3322 return NT_STATUS_OK;
3325 for (i = 0; i < psd->dacl->num_aces; i++) {
3326 if (dom_sid_compare_domain(
3327 &global_sid_Unix_NFS_Mode,
3328 &psd->dacl->aces[i].trustee) == 0) {
3329 *pmode = (mode_t)psd->dacl->aces[i].trustee.sub_auths[2];
3330 *pmode &= (S_IRWXU | S_IRWXG | S_IRWXO);
3333 DEBUG(10, ("MS NFS chmod request %s, %04o\n",
3334 fsp_str_dbg(fsp), (unsigned)(*pmode)));
3340 * Remove any incoming virtual ACE entries generated by
3341 * fruit_fget_nt_acl().
3344 return remove_virtual_nfs_aces(psd);
3347 /****************************************************************************
3349 ****************************************************************************/
3351 static int fruit_connect(vfs_handle_struct *handle,
3352 const char *service,
3356 char *list = NULL, *newlist = NULL;
3357 struct fruit_config_data *config;
3359 DEBUG(10, ("fruit_connect\n"));
3361 rc = SMB_VFS_NEXT_CONNECT(handle, service, user);
3366 rc = init_fruit_config(handle);
3371 SMB_VFS_HANDLE_GET_DATA(handle, config,
3372 struct fruit_config_data, return -1);
3374 if (config->veto_appledouble) {
3375 list = lp_veto_files(talloc_tos(), SNUM(handle->conn));
3378 if (strstr(list, "/" ADOUBLE_NAME_PREFIX "*/") == NULL) {
3379 newlist = talloc_asprintf(
3381 "%s/" ADOUBLE_NAME_PREFIX "*/",
3383 lp_do_parameter(SNUM(handle->conn),
3388 lp_do_parameter(SNUM(handle->conn),
3390 "/" ADOUBLE_NAME_PREFIX "*/");
3396 if (config->encoding == FRUIT_ENC_NATIVE) {
3397 lp_do_parameter(SNUM(handle->conn),
3402 if (config->time_machine) {
3403 DBG_NOTICE("Enabling durable handles for Time Machine "
3404 "support on [%s]\n", service);
3405 lp_do_parameter(SNUM(handle->conn), "durable handles", "yes");
3406 lp_do_parameter(SNUM(handle->conn), "kernel oplocks", "no");
3407 lp_do_parameter(SNUM(handle->conn), "kernel share modes", "no");
3408 if (!lp_strict_sync(SNUM(handle->conn))) {
3409 DBG_WARNING("Time Machine without strict sync is not "
3412 lp_do_parameter(SNUM(handle->conn), "posix locking", "no");
3418 static int fruit_fake_fd(void)
3425 * Return a valid fd, but ensure any attempt to use it returns
3426 * an error (EPIPE). Once we get a write on the handle, we open
3429 ret = pipe(pipe_fds);
3439 static int fruit_open_meta_stream(vfs_handle_struct *handle,
3440 struct smb_filename *smb_fname,
3445 struct fruit_config_data *config = NULL;
3446 struct fio *fio = NULL;
3447 int open_flags = flags & ~O_CREAT;
3450 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3452 SMB_VFS_HANDLE_GET_DATA(handle, config,
3453 struct fruit_config_data, return -1);
3455 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3456 fio->type = ADOUBLE_META;
3457 fio->config = config;
3459 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, open_flags, mode);
3464 if (!(flags & O_CREAT)) {
3465 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
3469 fd = fruit_fake_fd();
3471 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
3475 fio->fake_fd = true;
3482 static int fruit_open_meta_netatalk(vfs_handle_struct *handle,
3483 struct smb_filename *smb_fname,
3488 struct fruit_config_data *config = NULL;
3489 struct fio *fio = NULL;
3490 struct adouble *ad = NULL;
3491 bool meta_exists = false;
3494 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3496 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
3503 if (!meta_exists && !(flags & O_CREAT)) {
3508 fd = fruit_fake_fd();
3513 SMB_VFS_HANDLE_GET_DATA(handle, config,
3514 struct fruit_config_data, return -1);
3516 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3517 fio->type = ADOUBLE_META;
3518 fio->config = config;
3519 fio->fake_fd = true;
3526 static int fruit_open_meta(vfs_handle_struct *handle,
3527 struct smb_filename *smb_fname,
3528 files_struct *fsp, int flags, mode_t mode)
3531 struct fruit_config_data *config = NULL;
3533 DBG_DEBUG("path [%s]\n", smb_fname_str_dbg(smb_fname));
3535 SMB_VFS_HANDLE_GET_DATA(handle, config,
3536 struct fruit_config_data, return -1);
3538 switch (config->meta) {
3539 case FRUIT_META_STREAM:
3540 fd = fruit_open_meta_stream(handle, smb_fname,
3544 case FRUIT_META_NETATALK:
3545 fd = fruit_open_meta_netatalk(handle, smb_fname,
3550 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3554 DBG_DEBUG("path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3559 static int fruit_open_rsrc_adouble(vfs_handle_struct *handle,
3560 struct smb_filename *smb_fname,
3566 struct adouble *ad = NULL;
3567 struct smb_filename *smb_fname_base = NULL;
3568 struct fruit_config_data *config = NULL;
3571 SMB_VFS_HANDLE_GET_DATA(handle, config,
3572 struct fruit_config_data, return -1);
3574 if ((!(flags & O_CREAT)) &&
3575 S_ISDIR(fsp->base_fsp->fsp_name->st.st_ex_mode))
3577 /* sorry, but directories don't habe a resource fork */
3582 rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_base);
3587 /* We always need read/write access for the metadata header too */
3588 flags &= ~(O_RDONLY | O_WRONLY);
3591 hostfd = SMB_VFS_NEXT_OPEN(handle, smb_fname_base, fsp,
3598 if (flags & (O_CREAT | O_TRUNC)) {
3599 ad = ad_init(fsp, ADOUBLE_RSRC);
3605 fsp->fh->fd = hostfd;
3607 rc = ad_fset(handle, ad, fsp);
3618 TALLOC_FREE(smb_fname_base);
3620 DEBUG(10, ("fruit_open resource fork: rc=%d, fd=%d\n", rc, hostfd));
3622 int saved_errno = errno;
3625 * BUGBUGBUG -- we would need to call
3626 * fd_close_posix here, but we don't have a
3629 fsp->fh->fd = hostfd;
3633 errno = saved_errno;
3638 static int fruit_open_rsrc_xattr(vfs_handle_struct *handle,
3639 struct smb_filename *smb_fname,
3644 #ifdef HAVE_ATTROPEN
3647 fd = attropen(smb_fname->base_name,
3648 AFPRESOURCE_EA_NETATALK,
3663 static int fruit_open_rsrc(vfs_handle_struct *handle,
3664 struct smb_filename *smb_fname,
3665 files_struct *fsp, int flags, mode_t mode)
3668 struct fruit_config_data *config = NULL;
3669 struct fio *fio = NULL;
3671 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3673 SMB_VFS_HANDLE_GET_DATA(handle, config,
3674 struct fruit_config_data, return -1);
3676 switch (config->rsrc) {
3677 case FRUIT_RSRC_STREAM:
3678 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3681 case FRUIT_RSRC_ADFILE:
3682 fd = fruit_open_rsrc_adouble(handle, smb_fname,
3686 case FRUIT_RSRC_XATTR:
3687 fd = fruit_open_rsrc_xattr(handle, smb_fname,
3692 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3696 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3702 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3703 fio->type = ADOUBLE_RSRC;
3704 fio->config = config;
3709 static int fruit_open(vfs_handle_struct *handle,
3710 struct smb_filename *smb_fname,
3711 files_struct *fsp, int flags, mode_t mode)
3715 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3717 if (!is_ntfs_stream_smb_fname(smb_fname)) {
3718 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3721 if (is_afpinfo_stream(smb_fname)) {
3722 fd = fruit_open_meta(handle, smb_fname, fsp, flags, mode);
3723 } else if (is_afpresource_stream(smb_fname)) {
3724 fd = fruit_open_rsrc(handle, smb_fname, fsp, flags, mode);
3726 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3729 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3734 static int fruit_close_meta(vfs_handle_struct *handle,
3738 struct fruit_config_data *config = NULL;
3740 SMB_VFS_HANDLE_GET_DATA(handle, config,
3741 struct fruit_config_data, return -1);
3743 switch (config->meta) {
3744 case FRUIT_META_STREAM:
3745 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3748 case FRUIT_META_NETATALK:
3749 ret = close(fsp->fh->fd);
3754 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3762 static int fruit_close_rsrc(vfs_handle_struct *handle,
3766 struct fruit_config_data *config = NULL;
3768 SMB_VFS_HANDLE_GET_DATA(handle, config,
3769 struct fruit_config_data, return -1);
3771 switch (config->rsrc) {
3772 case FRUIT_RSRC_STREAM:
3773 case FRUIT_RSRC_ADFILE:
3774 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3777 case FRUIT_RSRC_XATTR:
3778 ret = close(fsp->fh->fd);
3783 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3790 static int fruit_close(vfs_handle_struct *handle,
3798 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(fsp->fsp_name), fd);
3800 if (!is_ntfs_stream_smb_fname(fsp->fsp_name)) {
3801 return SMB_VFS_NEXT_CLOSE(handle, fsp);
3804 if (is_afpinfo_stream(fsp->fsp_name)) {
3805 ret = fruit_close_meta(handle, fsp);
3806 } else if (is_afpresource_stream(fsp->fsp_name)) {
3807 ret = fruit_close_rsrc(handle, fsp);
3809 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3815 static int fruit_rename(struct vfs_handle_struct *handle,
3816 const struct smb_filename *smb_fname_src,
3817 const struct smb_filename *smb_fname_dst)
3820 struct fruit_config_data *config = NULL;
3821 struct smb_filename *src_adp_smb_fname = NULL;
3822 struct smb_filename *dst_adp_smb_fname = NULL;
3824 SMB_VFS_HANDLE_GET_DATA(handle, config,
3825 struct fruit_config_data, return -1);
3827 if (!VALID_STAT(smb_fname_src->st)) {
3828 DBG_ERR("Need valid stat for [%s]\n",
3829 smb_fname_str_dbg(smb_fname_src));
3833 rc = SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
3838 if ((config->rsrc != FRUIT_RSRC_ADFILE) ||
3839 (!S_ISREG(smb_fname_src->st.st_ex_mode)))
3844 rc = adouble_path(talloc_tos(), smb_fname_src, &src_adp_smb_fname);
3849 rc = adouble_path(talloc_tos(), smb_fname_dst, &dst_adp_smb_fname);
3854 DBG_DEBUG("%s -> %s\n",
3855 smb_fname_str_dbg(src_adp_smb_fname),
3856 smb_fname_str_dbg(dst_adp_smb_fname));
3858 rc = SMB_VFS_NEXT_RENAME(handle, src_adp_smb_fname, dst_adp_smb_fname);
3859 if (errno == ENOENT) {
3864 TALLOC_FREE(src_adp_smb_fname);
3865 TALLOC_FREE(dst_adp_smb_fname);
3869 static int fruit_unlink_meta_stream(vfs_handle_struct *handle,
3870 const struct smb_filename *smb_fname)
3872 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3875 static int fruit_unlink_meta_netatalk(vfs_handle_struct *handle,
3876 const struct smb_filename *smb_fname)
3878 return SMB_VFS_REMOVEXATTR(handle->conn,
3880 AFPINFO_EA_NETATALK);
3883 static int fruit_unlink_meta(vfs_handle_struct *handle,
3884 const struct smb_filename *smb_fname)
3886 struct fruit_config_data *config = NULL;
3889 SMB_VFS_HANDLE_GET_DATA(handle, config,
3890 struct fruit_config_data, return -1);
3892 switch (config->meta) {
3893 case FRUIT_META_STREAM:
3894 rc = fruit_unlink_meta_stream(handle, smb_fname);
3897 case FRUIT_META_NETATALK:
3898 rc = fruit_unlink_meta_netatalk(handle, smb_fname);
3902 DBG_ERR("Unsupported meta config [%d]\n", config->meta);
3909 static int fruit_unlink_rsrc_stream(vfs_handle_struct *handle,
3910 const struct smb_filename *smb_fname,
3915 if (!force_unlink) {
3916 struct smb_filename *smb_fname_cp = NULL;
3919 smb_fname_cp = cp_smb_filename(talloc_tos(), smb_fname);
3920 if (smb_fname_cp == NULL) {
3925 * 0 byte resource fork streams are not listed by
3926 * vfs_streaminfo, as a result stream cleanup/deletion of file
3927 * deletion doesn't remove the resourcefork stream.
3930 ret = SMB_VFS_NEXT_STAT(handle, smb_fname_cp);
3932 TALLOC_FREE(smb_fname_cp);
3933 DBG_ERR("stat [%s] failed [%s]\n",
3934 smb_fname_str_dbg(smb_fname_cp), strerror(errno));
3938 size = smb_fname_cp->st.st_ex_size;
3939 TALLOC_FREE(smb_fname_cp);
3942 /* OS X ignores resource fork stream delete requests */
3947 ret = SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3948 if ((ret != 0) && (errno == ENOENT) && force_unlink) {
3955 static int fruit_unlink_rsrc_adouble(vfs_handle_struct *handle,
3956 const struct smb_filename *smb_fname,
3960 struct adouble *ad = NULL;
3961 struct smb_filename *adp_smb_fname = NULL;
3963 if (!force_unlink) {
3964 ad = ad_get(talloc_tos(), handle, smb_fname,
3973 * 0 byte resource fork streams are not listed by
3974 * vfs_streaminfo, as a result stream cleanup/deletion of file
3975 * deletion doesn't remove the resourcefork stream.
3978 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
3979 /* OS X ignores resource fork stream delete requests */
3987 rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
3992 rc = SMB_VFS_NEXT_UNLINK(handle, adp_smb_fname);
3993 TALLOC_FREE(adp_smb_fname);
3994 if ((rc != 0) && (errno == ENOENT) && force_unlink) {
4001 static int fruit_unlink_rsrc_xattr(vfs_handle_struct *handle,
4002 const struct smb_filename *smb_fname,
4006 * OS X ignores resource fork stream delete requests, so nothing to do
4007 * here. Removing the file will remove the xattr anyway, so we don't
4008 * have to take care of removing 0 byte resource forks that could be
4014 static int fruit_unlink_rsrc(vfs_handle_struct *handle,
4015 const struct smb_filename *smb_fname,
4018 struct fruit_config_data *config = NULL;
4021 SMB_VFS_HANDLE_GET_DATA(handle, config,
4022 struct fruit_config_data, return -1);
4024 switch (config->rsrc) {
4025 case FRUIT_RSRC_STREAM:
4026 rc = fruit_unlink_rsrc_stream(handle, smb_fname, force_unlink);
4029 case FRUIT_RSRC_ADFILE:
4030 rc = fruit_unlink_rsrc_adouble(handle, smb_fname, force_unlink);
4033 case FRUIT_RSRC_XATTR:
4034 rc = fruit_unlink_rsrc_xattr(handle, smb_fname, force_unlink);
4038 DBG_ERR("Unsupported rsrc config [%d]\n", config->rsrc);
4045 static int fruit_unlink(vfs_handle_struct *handle,
4046 const struct smb_filename *smb_fname)
4049 struct fruit_config_data *config = NULL;
4050 struct smb_filename *rsrc_smb_fname = NULL;
4052 SMB_VFS_HANDLE_GET_DATA(handle, config,
4053 struct fruit_config_data, return -1);
4055 if (is_afpinfo_stream(smb_fname)) {
4056 return fruit_unlink_meta(handle, smb_fname);
4057 } else if (is_afpresource_stream(smb_fname)) {
4058 return fruit_unlink_rsrc(handle, smb_fname, false);
4059 } else if (is_ntfs_stream_smb_fname(smb_fname)) {
4060 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4061 } else if (is_adouble_file(smb_fname->base_name)) {
4062 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4066 * A request to delete the base file. Because 0 byte resource
4067 * fork streams are not listed by fruit_streaminfo,
4068 * delete_all_streams() can't remove 0 byte resource fork
4069 * streams, so we have to cleanup this here.
4071 rsrc_smb_fname = synthetic_smb_fname(talloc_tos(),
4072 smb_fname->base_name,
4073 AFPRESOURCE_STREAM_NAME,
4076 if (rsrc_smb_fname == NULL) {
4080 rc = fruit_unlink_rsrc(handle, rsrc_smb_fname, true);
4081 if ((rc != 0) && (errno != ENOENT)) {
4082 DBG_ERR("Forced unlink of [%s] failed [%s]\n",
4083 smb_fname_str_dbg(rsrc_smb_fname), strerror(errno));
4084 TALLOC_FREE(rsrc_smb_fname);
4087 TALLOC_FREE(rsrc_smb_fname);
4089 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4092 static int fruit_chmod(vfs_handle_struct *handle,
4093 const struct smb_filename *smb_fname,
4097 struct fruit_config_data *config = NULL;
4098 struct smb_filename *smb_fname_adp = NULL;
4100 rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname, mode);
4105 SMB_VFS_HANDLE_GET_DATA(handle, config,
4106 struct fruit_config_data, return -1);
4108 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4112 if (!VALID_STAT(smb_fname->st)) {
4116 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
4120 rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_adp);
4125 DEBUG(10, ("fruit_chmod: %s\n", smb_fname_adp->base_name));
4127 rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname_adp, mode);
4128 if (errno == ENOENT) {
4132 TALLOC_FREE(smb_fname_adp);
4136 static int fruit_chown(vfs_handle_struct *handle,
4137 const struct smb_filename *smb_fname,
4142 struct fruit_config_data *config = NULL;
4143 struct smb_filename *adp_smb_fname = NULL;
4145 rc = SMB_VFS_NEXT_CHOWN(handle, smb_fname, uid, gid);
4150 SMB_VFS_HANDLE_GET_DATA(handle, config,
4151 struct fruit_config_data, return -1);
4153 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4157 if (!VALID_STAT(smb_fname->st)) {
4161 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
4165 rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
4170 DEBUG(10, ("fruit_chown: %s\n", adp_smb_fname->base_name));
4172 rc = SMB_VFS_NEXT_CHOWN(handle, adp_smb_fname, uid, gid);
4173 if (errno == ENOENT) {
4178 TALLOC_FREE(adp_smb_fname);
4182 static int fruit_rmdir(struct vfs_handle_struct *handle,
4183 const struct smb_filename *smb_fname)
4187 struct fruit_config_data *config;
4189 SMB_VFS_HANDLE_GET_DATA(handle, config,
4190 struct fruit_config_data, return -1);
4192 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4197 * Due to there is no way to change bDeleteVetoFiles variable
4198 * from this module, need to clean up ourselves
4201 dh = SMB_VFS_OPENDIR(handle->conn, smb_fname, NULL, 0);
4206 while ((de = SMB_VFS_READDIR(handle->conn, dh, NULL)) != NULL) {
4207 struct adouble *ad = NULL;
4209 struct smb_filename *ad_smb_fname = NULL;
4212 if (!is_adouble_file(de->d_name)) {
4216 p = talloc_asprintf(talloc_tos(), "%s/%s",
4217 smb_fname->base_name, de->d_name);
4219 DBG_ERR("talloc_asprintf failed\n");
4223 ad_smb_fname = synthetic_smb_fname(talloc_tos(), p,
4227 if (ad_smb_fname == NULL) {
4228 DBG_ERR("synthetic_smb_fname failed\n");
4233 * Check whether it's a valid AppleDouble file, if
4234 * yes, delete it, ignore it otherwise.
4236 ad = ad_get(talloc_tos(), handle, ad_smb_fname, ADOUBLE_RSRC);
4238 TALLOC_FREE(ad_smb_fname);
4244 ret = SMB_VFS_NEXT_UNLINK(handle, ad_smb_fname);
4246 DBG_ERR("Deleting [%s] failed\n",
4247 smb_fname_str_dbg(ad_smb_fname));
4249 TALLOC_FREE(ad_smb_fname);
4254 SMB_VFS_CLOSEDIR(handle->conn, dh);
4256 return SMB_VFS_NEXT_RMDIR(handle, smb_fname);
4259 static ssize_t fruit_pread_meta_stream(vfs_handle_struct *handle,
4260 files_struct *fsp, void *data,
4261 size_t n, off_t offset)
4266 nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4267 if (nread == -1 || nread == n) {
4271 DBG_ERR("Removing [%s] after short read [%zd]\n",
4272 fsp_str_dbg(fsp), nread);
4274 ret = SMB_VFS_NEXT_UNLINK(handle, fsp->fsp_name);
4276 DBG_ERR("Removing [%s] failed\n", fsp_str_dbg(fsp));
4284 static ssize_t fruit_pread_meta_adouble(vfs_handle_struct *handle,
4285 files_struct *fsp, void *data,
4286 size_t n, off_t offset)
4289 struct adouble *ad = NULL;
4290 char afpinfo_buf[AFP_INFO_SIZE];
4294 ai = afpinfo_new(talloc_tos());
4299 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
4305 p = ad_get_entry(ad, ADEID_FINDERI);
4307 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
4312 memcpy(&ai->afpi_FinderInfo[0], p, ADEDLEN_FINDERI);
4314 nread = afpinfo_pack(ai, afpinfo_buf);
4315 if (nread != AFP_INFO_SIZE) {
4320 memcpy(data, afpinfo_buf, n);
4328 static ssize_t fruit_pread_meta(vfs_handle_struct *handle,
4329 files_struct *fsp, void *data,
4330 size_t n, off_t offset)
4332 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4337 * OS X has a off-by-1 error in the offset calculation, so we're
4338 * bug compatible here. It won't hurt, as any relevant real
4339 * world read requests from the AFP_AfpInfo stream will be
4340 * offset=0 n=60. offset is ignored anyway, see below.
4342 if ((offset < 0) || (offset >= AFP_INFO_SIZE + 1)) {
4347 DBG_ERR("Failed to fetch fsp extension");
4351 /* Yes, macOS always reads from offset 0 */
4353 to_return = MIN(n, AFP_INFO_SIZE);
4355 switch (fio->config->meta) {
4356 case FRUIT_META_STREAM:
4357 nread = fruit_pread_meta_stream(handle, fsp, data,
4361 case FRUIT_META_NETATALK:
4362 nread = fruit_pread_meta_adouble(handle, fsp, data,
4367 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
4371 if (nread == -1 && fio->created) {
4373 char afpinfo_buf[AFP_INFO_SIZE];
4375 ai = afpinfo_new(talloc_tos());
4380 nread = afpinfo_pack(ai, afpinfo_buf);
4382 if (nread != AFP_INFO_SIZE) {
4386 memcpy(data, afpinfo_buf, to_return);
4393 static ssize_t fruit_pread_rsrc_stream(vfs_handle_struct *handle,
4394 files_struct *fsp, void *data,
4395 size_t n, off_t offset)
4397 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4400 static ssize_t fruit_pread_rsrc_xattr(vfs_handle_struct *handle,
4401 files_struct *fsp, void *data,
4402 size_t n, off_t offset)
4404 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4407 static ssize_t fruit_pread_rsrc_adouble(vfs_handle_struct *handle,
4408 files_struct *fsp, void *data,
4409 size_t n, off_t offset)
4411 struct adouble *ad = NULL;
4414 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
4419 nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n,
4420 offset + ad_getentryoff(ad, ADEID_RFORK));
4426 static ssize_t fruit_pread_rsrc(vfs_handle_struct *handle,
4427 files_struct *fsp, void *data,
4428 size_t n, off_t offset)
4430 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4438 switch (fio->config->rsrc) {
4439 case FRUIT_RSRC_STREAM:
4440 nread = fruit_pread_rsrc_stream(handle, fsp, data, n, offset);
4443 case FRUIT_RSRC_ADFILE:
4444 nread = fruit_pread_rsrc_adouble(handle, fsp, data, n, offset);
4447 case FRUIT_RSRC_XATTR:
4448 nread = fruit_pread_rsrc_xattr(handle, fsp, data, n, offset);
4452 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
4459 static ssize_t fruit_pread(vfs_handle_struct *handle,
4460 files_struct *fsp, void *data,
4461 size_t n, off_t offset)
4463 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4466 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4467 fsp_str_dbg(fsp), (intmax_t)offset, n);
4470 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4473 if (fio->type == ADOUBLE_META) {
4474 nread = fruit_pread_meta(handle, fsp, data, n, offset);
4476 nread = fruit_pread_rsrc(handle, fsp, data, n, offset);
4479 DBG_DEBUG("Path [%s] nread [%zd]\n", fsp_str_dbg(fsp), nread);
4483 static bool fruit_must_handle_aio_stream(struct fio *fio)
4489 if (fio->type == ADOUBLE_META) {
4493 if ((fio->type == ADOUBLE_RSRC) &&
4494 (fio->config->rsrc == FRUIT_RSRC_ADFILE))
4502 struct fruit_pread_state {
4504 struct vfs_aio_state vfs_aio_state;
4507 static void fruit_pread_done(struct tevent_req *subreq);
4509 static struct tevent_req *fruit_pread_send(
4510 struct vfs_handle_struct *handle,
4511 TALLOC_CTX *mem_ctx,
4512 struct tevent_context *ev,
4513 struct files_struct *fsp,
4515 size_t n, off_t offset)
4517 struct tevent_req *req = NULL;
4518 struct tevent_req *subreq = NULL;
4519 struct fruit_pread_state *state = NULL;
4520 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4522 req = tevent_req_create(mem_ctx, &state,
4523 struct fruit_pread_state);
4528 if (fruit_must_handle_aio_stream(fio)) {
4529 state->nread = SMB_VFS_PREAD(fsp, data, n, offset);
4530 if (state->nread != n) {
4531 if (state->nread != -1) {
4534 tevent_req_error(req, errno);
4535 return tevent_req_post(req, ev);
4537 tevent_req_done(req);
4538 return tevent_req_post(req, ev);
4541 subreq = SMB_VFS_NEXT_PREAD_SEND(state, ev, handle, fsp,
4543 if (tevent_req_nomem(req, subreq)) {
4544 return tevent_req_post(req, ev);
4546 tevent_req_set_callback(subreq, fruit_pread_done, req);
4550 static void fruit_pread_done(struct tevent_req *subreq)
4552 struct tevent_req *req = tevent_req_callback_data(
4553 subreq, struct tevent_req);
4554 struct fruit_pread_state *state = tevent_req_data(
4555 req, struct fruit_pread_state);
4557 state->nread = SMB_VFS_PREAD_RECV(subreq, &state->vfs_aio_state);
4558 TALLOC_FREE(subreq);
4560 if (tevent_req_error(req, state->vfs_aio_state.error)) {
4563 tevent_req_done(req);
4566 static ssize_t fruit_pread_recv(struct tevent_req *req,
4567 struct vfs_aio_state *vfs_aio_state)
4569 struct fruit_pread_state *state = tevent_req_data(
4570 req, struct fruit_pread_state);
4572 if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
4576 *vfs_aio_state = state->vfs_aio_state;
4577 return state->nread;
4580 static ssize_t fruit_pwrite_meta_stream(vfs_handle_struct *handle,
4581 files_struct *fsp, const void *data,
4582 size_t n, off_t offset)
4584 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4590 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4591 fsp_str_dbg(fsp), (intmax_t)offset, n);
4600 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
4602 DBG_ERR("Close [%s] failed: %s\n",
4603 fsp_str_dbg(fsp), strerror(errno));
4608 fd = SMB_VFS_NEXT_OPEN(handle,
4614 DBG_ERR("On-demand create [%s] in write failed: %s\n",
4615 fsp_str_dbg(fsp), strerror(errno));
4619 fio->fake_fd = false;
4622 ai = afpinfo_unpack(talloc_tos(), data);
4627 if (ai_empty_finderinfo(ai)) {
4629 * Writing an all 0 blob to the metadata stream results in the
4630 * stream being removed on a macOS server. This ensures we
4631 * behave the same and it verified by the "delete AFP_AfpInfo by
4632 * writing all 0" test.
4634 ret = SMB_VFS_NEXT_FTRUNCATE(handle, fsp, 0);
4636 DBG_ERR("SMB_VFS_NEXT_FTRUNCATE on [%s] failed\n",
4641 ok = set_delete_on_close(
4644 handle->conn->session_info->security_token,
4645 handle->conn->session_info->unix_token);
4647 DBG_ERR("set_delete_on_close on [%s] failed\n",
4654 nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4655 if (nwritten != n) {
4662 static ssize_t fruit_pwrite_meta_netatalk(vfs_handle_struct *handle,
4663 files_struct *fsp, const void *data,
4664 size_t n, off_t offset)
4666 struct adouble *ad = NULL;
4672 ai = afpinfo_unpack(talloc_tos(), data);
4677 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
4679 ad = ad_init(talloc_tos(), ADOUBLE_META);
4684 p = ad_get_entry(ad, ADEID_FINDERI);
4686 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
4691 memcpy(p, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
4693 ret = ad_fset(handle, ad, fsp);
4695 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
4702 if (!ai_empty_finderinfo(ai)) {
4707 * Writing an all 0 blob to the metadata stream results in the stream
4708 * being removed on a macOS server. This ensures we behave the same and
4709 * it verified by the "delete AFP_AfpInfo by writing all 0" test.
4712 ok = set_delete_on_close(
4715 handle->conn->session_info->security_token,
4716 handle->conn->session_info->unix_token);
4718 DBG_ERR("set_delete_on_close on [%s] failed\n",
4726 static ssize_t fruit_pwrite_meta(vfs_handle_struct *handle,
4727 files_struct *fsp, const void *data,
4728 size_t n, off_t offset)
4730 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4732 uint8_t buf[AFP_INFO_SIZE];
4738 DBG_ERR("Failed to fetch fsp extension");
4747 if (offset != 0 && n < 60) {
4752 cmp = memcmp(data, "AFP", 3);
4758 if (n <= AFP_OFF_FinderInfo) {
4760 * Nothing to do here really, just return
4768 if (to_copy > AFP_INFO_SIZE) {
4769 to_copy = AFP_INFO_SIZE;
4771 memcpy(buf, data, to_copy);
4774 if (to_write != AFP_INFO_SIZE) {
4775 to_write = AFP_INFO_SIZE;
4778 switch (fio->config->meta) {
4779 case FRUIT_META_STREAM:
4780 nwritten = fruit_pwrite_meta_stream(handle,
4787 case FRUIT_META_NETATALK:
4788 nwritten = fruit_pwrite_meta_netatalk(handle,
4796 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
4800 if (nwritten != to_write) {
4805 * Return the requested amount, verified against macOS SMB server
4810 static ssize_t fruit_pwrite_rsrc_stream(vfs_handle_struct *handle,
4811 files_struct *fsp, const void *data,
4812 size_t n, off_t offset)
4814 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4817 static ssize_t fruit_pwrite_rsrc_xattr(vfs_handle_struct *handle,
4818 files_struct *fsp, const void *data,
4819 size_t n, off_t offset)
4821 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4824 static ssize_t fruit_pwrite_rsrc_adouble(vfs_handle_struct *handle,
4825 files_struct *fsp, const void *data,
4826 size_t n, off_t offset)
4828 struct adouble *ad = NULL;
4832 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
4834 DBG_ERR("ad_get [%s] failed\n", fsp_str_dbg(fsp));
4838 nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n,
4839 offset + ad_getentryoff(ad, ADEID_RFORK));
4840 if (nwritten != n) {
4841 DBG_ERR("Short write on [%s] [%zd/%zd]\n",
4842 fsp_str_dbg(fsp), nwritten, n);
4847 if ((n + offset) > ad_getentrylen(ad, ADEID_RFORK)) {
4848 ad_setentrylen(ad, ADEID_RFORK, n + offset);
4849 ret = ad_fset(handle, ad, fsp);
4851 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
4861 static ssize_t fruit_pwrite_rsrc(vfs_handle_struct *handle,
4862 files_struct *fsp, const void *data,
4863 size_t n, off_t offset)
4865 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4869 DBG_ERR("Failed to fetch fsp extension");
4873 switch (fio->config->rsrc) {
4874 case FRUIT_RSRC_STREAM:
4875 nwritten = fruit_pwrite_rsrc_stream(handle, fsp, data, n, offset);
4878 case FRUIT_RSRC_ADFILE:
4879 nwritten = fruit_pwrite_rsrc_adouble(handle, fsp, data, n, offset);
4882 case FRUIT_RSRC_XATTR:
4883 nwritten = fruit_pwrite_rsrc_xattr(handle, fsp, data, n, offset);
4887 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
4894 static ssize_t fruit_pwrite(vfs_handle_struct *handle,
4895 files_struct *fsp, const void *data,
4896 size_t n, off_t offset)
4898 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4901 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4902 fsp_str_dbg(fsp), (intmax_t)offset, n);
4905 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4908 if (fio->type == ADOUBLE_META) {
4909 nwritten = fruit_pwrite_meta(handle, fsp, data, n, offset);
4911 nwritten = fruit_pwrite_rsrc(handle, fsp, data, n, offset);
4914 DBG_DEBUG("Path [%s] nwritten=%zd\n", fsp_str_dbg(fsp), nwritten);
4918 struct fruit_pwrite_state {
4920 struct vfs_aio_state vfs_aio_state;
4923 static void fruit_pwrite_done(struct tevent_req *subreq);
4925 static struct tevent_req *fruit_pwrite_send(
4926 struct vfs_handle_struct *handle,
4927 TALLOC_CTX *mem_ctx,
4928 struct tevent_context *ev,
4929 struct files_struct *fsp,
4931 size_t n, off_t offset)
4933 struct tevent_req *req = NULL;
4934 struct tevent_req *subreq = NULL;
4935 struct fruit_pwrite_state *state = NULL;
4936 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4938 req = tevent_req_create(mem_ctx, &state,
4939 struct fruit_pwrite_state);
4944 if (fruit_must_handle_aio_stream(fio)) {
4945 state->nwritten = SMB_VFS_PWRITE(fsp, data, n, offset);
4946 if (state->nwritten != n) {
4947 if (state->nwritten != -1) {
4950 tevent_req_error(req, errno);
4951 return tevent_req_post(req, ev);
4953 tevent_req_done(req);
4954 return tevent_req_post(req, ev);
4957 subreq = SMB_VFS_NEXT_PWRITE_SEND(state, ev, handle, fsp,
4959 if (tevent_req_nomem(req, subreq)) {
4960 return tevent_req_post(req, ev);
4962 tevent_req_set_callback(subreq, fruit_pwrite_done, req);
4966 static void fruit_pwrite_done(struct tevent_req *subreq)
4968 struct tevent_req *req = tevent_req_callback_data(
4969 subreq, struct tevent_req);
4970 struct fruit_pwrite_state *state = tevent_req_data(
4971 req, struct fruit_pwrite_state);
4973 state->nwritten = SMB_VFS_PWRITE_RECV(subreq, &state->vfs_aio_state);
4974 TALLOC_FREE(subreq);
4976 if (tevent_req_error(req, state->vfs_aio_state.error)) {
4979 tevent_req_done(req);
4982 static ssize_t fruit_pwrite_recv(struct tevent_req *req,
4983 struct vfs_aio_state *vfs_aio_state)
4985 struct fruit_pwrite_state *state = tevent_req_data(
4986 req, struct fruit_pwrite_state);
4988 if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
4992 *vfs_aio_state = state->vfs_aio_state;
4993 return state->nwritten;
4997 * Helper to stat/lstat the base file of an smb_fname.
4999 static int fruit_stat_base(vfs_handle_struct *handle,
5000 struct smb_filename *smb_fname,
5003 char *tmp_stream_name;
5006 tmp_stream_name = smb_fname->stream_name;
5007 smb_fname->stream_name = NULL;
5009 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
5011 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5013 smb_fname->stream_name = tmp_stream_name;
5015 DBG_DEBUG("fruit_stat_base [%s] dev [%ju] ino [%ju]\n",
5016 smb_fname->base_name,
5017 (uintmax_t)smb_fname->st.st_ex_dev,
5018 (uintmax_t)smb_fname->st.st_ex_ino);
5022 static int fruit_stat_meta_stream(vfs_handle_struct *handle,
5023 struct smb_filename *smb_fname,
5029 ret = fruit_stat_base(handle, smb_fname, false);
5034 ino = fruit_inode(&smb_fname->st, smb_fname->stream_name);
5037 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
5039 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5042 smb_fname->st.st_ex_ino = ino;
5047 static int fruit_stat_meta_netatalk(vfs_handle_struct *handle,
5048 struct smb_filename *smb_fname,
5051 struct adouble *ad = NULL;
5053 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5055 DBG_INFO("fruit_stat_meta %s: %s\n",
5056 smb_fname_str_dbg(smb_fname), strerror(errno));
5062 /* Populate the stat struct with info from the base file. */
5063 if (fruit_stat_base(handle, smb_fname, follow_links) == -1) {
5066 smb_fname->st.st_ex_size = AFP_INFO_SIZE;
5067 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5068 smb_fname->stream_name);
5072 static int fruit_stat_meta(vfs_handle_struct *handle,
5073 struct smb_filename *smb_fname,
5076 struct fruit_config_data *config = NULL;
5079 SMB_VFS_HANDLE_GET_DATA(handle, config,
5080 struct fruit_config_data, return -1);
5082 switch (config->meta) {
5083 case FRUIT_META_STREAM:
5084 ret = fruit_stat_meta_stream(handle, smb_fname, follow_links);
5087 case FRUIT_META_NETATALK:
5088 ret = fruit_stat_meta_netatalk(handle, smb_fname, follow_links);
5092 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
5099 static int fruit_stat_rsrc_netatalk(vfs_handle_struct *handle,
5100 struct smb_filename *smb_fname,
5103 struct adouble *ad = NULL;
5106 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
5112 /* Populate the stat struct with info from the base file. */
5113 ret = fruit_stat_base(handle, smb_fname, follow_links);
5119 smb_fname->st.st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
5120 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5121 smb_fname->stream_name);
5126 static int fruit_stat_rsrc_stream(vfs_handle_struct *handle,
5127 struct smb_filename *smb_fname,
5133 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
5135 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5141 static int fruit_stat_rsrc_xattr(vfs_handle_struct *handle,
5142 struct smb_filename *smb_fname,
5145 #ifdef HAVE_ATTROPEN
5149 /* Populate the stat struct with info from the base file. */
5150 ret = fruit_stat_base(handle, smb_fname, follow_links);
5155 fd = attropen(smb_fname->base_name,
5156 AFPRESOURCE_EA_NETATALK,
5162 ret = sys_fstat(fd, &smb_fname->st, false);
5165 DBG_ERR("fstat [%s:%s] failed\n", smb_fname->base_name,
5166 AFPRESOURCE_EA_NETATALK);
5172 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5173 smb_fname->stream_name);
5183 static int fruit_stat_rsrc(vfs_handle_struct *handle,
5184 struct smb_filename *smb_fname,
5187 struct fruit_config_data *config = NULL;
5190 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
5192 SMB_VFS_HANDLE_GET_DATA(handle, config,
5193 struct fruit_config_data, return -1);
5195 switch (config->rsrc) {
5196 case FRUIT_RSRC_STREAM:
5197 ret = fruit_stat_rsrc_stream(handle, smb_fname, follow_links);
5200 case FRUIT_RSRC_XATTR:
5201 ret = fruit_stat_rsrc_xattr(handle, smb_fname, follow_links);
5204 case FRUIT_RSRC_ADFILE:
5205 ret = fruit_stat_rsrc_netatalk(handle, smb_fname, follow_links);
5209 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
5216 static int fruit_stat(vfs_handle_struct *handle,
5217 struct smb_filename *smb_fname)
5221 DEBUG(10, ("fruit_stat called for %s\n",
5222 smb_fname_str_dbg(smb_fname)));
5224 if (!is_ntfs_stream_smb_fname(smb_fname)
5225 || is_ntfs_default_stream_smb_fname(smb_fname)) {
5226 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
5228 update_btime(handle, smb_fname);
5234 * Note if lp_posix_paths() is true, we can never
5235 * get here as is_ntfs_stream_smb_fname() is
5236 * always false. So we never need worry about
5237 * not following links here.
5240 if (is_afpinfo_stream(smb_fname)) {
5241 rc = fruit_stat_meta(handle, smb_fname, true);
5242 } else if (is_afpresource_stream(smb_fname)) {
5243 rc = fruit_stat_rsrc(handle, smb_fname, true);
5245 return SMB_VFS_NEXT_STAT(handle, smb_fname);
5249 update_btime(handle, smb_fname);
5250 smb_fname->st.st_ex_mode &= ~S_IFMT;
5251 smb_fname->st.st_ex_mode |= S_IFREG;
5252 smb_fname->st.st_ex_blocks =
5253 smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
5258 static int fruit_lstat(vfs_handle_struct *handle,
5259 struct smb_filename *smb_fname)
5263 DEBUG(10, ("fruit_lstat called for %s\n",
5264 smb_fname_str_dbg(smb_fname)));
5266 if (!is_ntfs_stream_smb_fname(smb_fname)
5267 || is_ntfs_default_stream_smb_fname(smb_fname)) {
5268 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5270 update_btime(handle, smb_fname);
5275 if (is_afpinfo_stream(smb_fname)) {
5276 rc = fruit_stat_meta(handle, smb_fname, false);
5277 } else if (is_afpresource_stream(smb_fname)) {
5278 rc = fruit_stat_rsrc(handle, smb_fname, false);
5280 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5284 update_btime(handle, smb_fname);
5285 smb_fname->st.st_ex_mode &= ~S_IFMT;
5286 smb_fname->st.st_ex_mode |= S_IFREG;
5287 smb_fname->st.st_ex_blocks =
5288 smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
5293 static int fruit_fstat_meta_stream(vfs_handle_struct *handle,
5295 SMB_STRUCT_STAT *sbuf)
5297 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5298 struct smb_filename smb_fname;
5307 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5312 *sbuf = fsp->base_fsp->fsp_name->st;
5313 sbuf->st_ex_size = AFP_INFO_SIZE;
5314 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5318 smb_fname = (struct smb_filename) {
5319 .base_name = fsp->fsp_name->base_name,
5322 ret = fruit_stat_base(handle, &smb_fname, false);
5326 *sbuf = smb_fname.st;
5328 ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5330 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5335 sbuf->st_ex_ino = ino;
5339 static int fruit_fstat_meta_netatalk(vfs_handle_struct *handle,
5341 SMB_STRUCT_STAT *sbuf)
5345 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5350 *sbuf = fsp->base_fsp->fsp_name->st;
5351 sbuf->st_ex_size = AFP_INFO_SIZE;
5352 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5357 static int fruit_fstat_meta(vfs_handle_struct *handle,
5359 SMB_STRUCT_STAT *sbuf,
5364 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
5366 switch (fio->config->meta) {
5367 case FRUIT_META_STREAM:
5368 ret = fruit_fstat_meta_stream(handle, fsp, sbuf);
5371 case FRUIT_META_NETATALK:
5372 ret = fruit_fstat_meta_netatalk(handle, fsp, sbuf);
5376 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
5380 DBG_DEBUG("Path [%s] ret [%d]\n", fsp_str_dbg(fsp), ret);
5384 static int fruit_fstat_rsrc_xattr(vfs_handle_struct *handle,
5386 SMB_STRUCT_STAT *sbuf)
5388 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5391 static int fruit_fstat_rsrc_stream(vfs_handle_struct *handle,
5393 SMB_STRUCT_STAT *sbuf)
5395 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5398 static int fruit_fstat_rsrc_adouble(vfs_handle_struct *handle,
5400 SMB_STRUCT_STAT *sbuf)
5402 struct adouble *ad = NULL;
5405 /* Populate the stat struct with info from the base file. */
5406 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5411 ad = ad_get(talloc_tos(), handle,
5412 fsp->base_fsp->fsp_name,
5415 DBG_ERR("ad_get [%s] failed [%s]\n",
5416 fsp_str_dbg(fsp), strerror(errno));
5420 *sbuf = fsp->base_fsp->fsp_name->st;
5421 sbuf->st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
5422 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5428 static int fruit_fstat_rsrc(vfs_handle_struct *handle, files_struct *fsp,
5429 SMB_STRUCT_STAT *sbuf, struct fio *fio)
5433 switch (fio->config->rsrc) {
5434 case FRUIT_RSRC_STREAM:
5435 ret = fruit_fstat_rsrc_stream(handle, fsp, sbuf);
5438 case FRUIT_RSRC_ADFILE:
5439 ret = fruit_fstat_rsrc_adouble(handle, fsp, sbuf);
5442 case FRUIT_RSRC_XATTR:
5443 ret = fruit_fstat_rsrc_xattr(handle, fsp, sbuf);
5447 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
5454 static int fruit_fstat(vfs_handle_struct *handle, files_struct *fsp,
5455 SMB_STRUCT_STAT *sbuf)
5457 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5461 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5464 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
5466 if (fio->type == ADOUBLE_META) {
5467 rc = fruit_fstat_meta(handle, fsp, sbuf, fio);
5469 rc = fruit_fstat_rsrc(handle, fsp, sbuf, fio);
5473 sbuf->st_ex_mode &= ~S_IFMT;
5474 sbuf->st_ex_mode |= S_IFREG;
5475 sbuf->st_ex_blocks = sbuf->st_ex_size / STAT_ST_BLOCKSIZE + 1;
5478 DBG_DEBUG("Path [%s] rc [%d] size [%"PRIdMAX"]\n",
5479 fsp_str_dbg(fsp), rc, (intmax_t)sbuf->st_ex_size);
5483 static NTSTATUS delete_invalid_meta_stream(
5484 vfs_handle_struct *handle,
5485 const struct smb_filename *smb_fname,
5486 TALLOC_CTX *mem_ctx,
5487 unsigned int *pnum_streams,
5488 struct stream_struct **pstreams,
5491 struct smb_filename *sname = NULL;
5495 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams, AFPINFO_STREAM);
5497 return NT_STATUS_INTERNAL_ERROR;
5501 return NT_STATUS_OK;
5504 sname = synthetic_smb_fname(talloc_tos(),
5505 smb_fname->base_name,
5506 AFPINFO_STREAM_NAME,
5508 if (sname == NULL) {
5509 return NT_STATUS_NO_MEMORY;
5512 ret = SMB_VFS_NEXT_UNLINK(handle, sname);
5515 DBG_ERR("Removing [%s] failed\n", smb_fname_str_dbg(sname));
5516 return map_nt_error_from_unix(errno);
5519 return NT_STATUS_OK;
5522 static NTSTATUS fruit_streaminfo_meta_stream(
5523 vfs_handle_struct *handle,
5524 struct files_struct *fsp,
5525 const struct smb_filename *smb_fname,
5526 TALLOC_CTX *mem_ctx,
5527 unsigned int *pnum_streams,
5528 struct stream_struct **pstreams)
5530 struct stream_struct *stream = *pstreams;
5531 unsigned int num_streams = *pnum_streams;
5534 for (i = 0; i < num_streams; i++) {
5535 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
5540 if (i == num_streams) {
5541 return NT_STATUS_OK;
5544 if (stream[i].size != AFP_INFO_SIZE) {
5545 DBG_ERR("Removing invalid AFPINFO_STREAM size [%jd] from [%s]\n",
5546 (intmax_t)stream[i].size, smb_fname_str_dbg(smb_fname));
5548 return delete_invalid_meta_stream(handle,
5557 return NT_STATUS_OK;
5560 static NTSTATUS fruit_streaminfo_meta_netatalk(
5561 vfs_handle_struct *handle,
5562 struct files_struct *fsp,
5563 const struct smb_filename *smb_fname,
5564 TALLOC_CTX *mem_ctx,
5565 unsigned int *pnum_streams,
5566 struct stream_struct **pstreams)
5568 struct stream_struct *stream = *pstreams;
5569 unsigned int num_streams = *pnum_streams;
5570 struct adouble *ad = NULL;
5575 /* Remove the Netatalk xattr from the list */
5576 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5577 ":" NETATALK_META_XATTR ":$DATA");
5579 return NT_STATUS_NO_MEMORY;
5583 * Check if there's a AFPINFO_STREAM from the VFS streams
5584 * backend and if yes, remove it from the list
5586 for (i = 0; i < num_streams; i++) {
5587 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
5592 if (i < num_streams) {
5593 DBG_WARNING("Unexpected AFPINFO_STREAM on [%s]\n",
5594 smb_fname_str_dbg(smb_fname));
5596 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5599 return NT_STATUS_INTERNAL_ERROR;
5603 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5605 return NT_STATUS_OK;
5608 is_fi_empty = ad_empty_finderinfo(ad);
5612 return NT_STATUS_OK;
5615 ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
5616 AFPINFO_STREAM_NAME, AFP_INFO_SIZE,
5617 smb_roundup(handle->conn, AFP_INFO_SIZE));
5619 return NT_STATUS_NO_MEMORY;
5622 return NT_STATUS_OK;
5625 static NTSTATUS fruit_streaminfo_meta(vfs_handle_struct *handle,
5626 struct files_struct *fsp,
5627 const struct smb_filename *smb_fname,
5628 TALLOC_CTX *mem_ctx,
5629 unsigned int *pnum_streams,
5630 struct stream_struct **pstreams)
5632 struct fruit_config_data *config = NULL;
5635 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5636 return NT_STATUS_INTERNAL_ERROR);
5638 switch (config->meta) {
5639 case FRUIT_META_NETATALK:
5640 status = fruit_streaminfo_meta_netatalk(handle, fsp, smb_fname,
5641 mem_ctx, pnum_streams,
5645 case FRUIT_META_STREAM:
5646 status = fruit_streaminfo_meta_stream(handle, fsp, smb_fname,
5647 mem_ctx, pnum_streams,
5652 return NT_STATUS_INTERNAL_ERROR;
5658 static NTSTATUS fruit_streaminfo_rsrc_stream(
5659 vfs_handle_struct *handle,
5660 struct files_struct *fsp,
5661 const struct smb_filename *smb_fname,
5662 TALLOC_CTX *mem_ctx,
5663 unsigned int *pnum_streams,
5664 struct stream_struct **pstreams)
5668 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
5670 DBG_ERR("Filtering resource stream failed\n");
5671 return NT_STATUS_INTERNAL_ERROR;
5673 return NT_STATUS_OK;
5676 static NTSTATUS fruit_streaminfo_rsrc_xattr(
5677 vfs_handle_struct *handle,
5678 struct files_struct *fsp,
5679 const struct smb_filename *smb_fname,
5680 TALLOC_CTX *mem_ctx,
5681 unsigned int *pnum_streams,
5682 struct stream_struct **pstreams)
5686 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
5688 DBG_ERR("Filtering resource stream failed\n");
5689 return NT_STATUS_INTERNAL_ERROR;
5691 return NT_STATUS_OK;
5694 static NTSTATUS fruit_streaminfo_rsrc_adouble(
5695 vfs_handle_struct *handle,
5696 struct files_struct *fsp,
5697 const struct smb_filename *smb_fname,
5698 TALLOC_CTX *mem_ctx,
5699 unsigned int *pnum_streams,
5700 struct stream_struct **pstreams)
5702 struct stream_struct *stream = *pstreams;
5703 unsigned int num_streams = *pnum_streams;
5704 struct adouble *ad = NULL;
5710 * Check if there's a AFPRESOURCE_STREAM from the VFS streams backend
5711 * and if yes, remove it from the list
5713 for (i = 0; i < num_streams; i++) {
5714 if (strequal_m(stream[i].name, AFPRESOURCE_STREAM)) {
5719 if (i < num_streams) {
5720 DBG_WARNING("Unexpected AFPRESOURCE_STREAM on [%s]\n",
5721 smb_fname_str_dbg(smb_fname));
5723 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5724 AFPRESOURCE_STREAM);
5726 return NT_STATUS_INTERNAL_ERROR;
5730 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
5732 return NT_STATUS_OK;
5735 rlen = ad_getentrylen(ad, ADEID_RFORK);
5739 return NT_STATUS_OK;
5742 ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
5743 AFPRESOURCE_STREAM_NAME, rlen,
5744 smb_roundup(handle->conn, rlen));
5746 return NT_STATUS_NO_MEMORY;
5749 return NT_STATUS_OK;
5752 static NTSTATUS fruit_streaminfo_rsrc(vfs_handle_struct *handle,
5753 struct files_struct *fsp,
5754 const struct smb_filename *smb_fname,
5755 TALLOC_CTX *mem_ctx,
5756 unsigned int *pnum_streams,
5757 struct stream_struct **pstreams)
5759 struct fruit_config_data *config = NULL;
5762 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5763 return NT_STATUS_INTERNAL_ERROR);
5765 switch (config->rsrc) {
5766 case FRUIT_RSRC_STREAM:
5767 status = fruit_streaminfo_rsrc_stream(handle, fsp, smb_fname,
5768 mem_ctx, pnum_streams,
5772 case FRUIT_RSRC_XATTR:
5773 status = fruit_streaminfo_rsrc_xattr(handle, fsp, smb_fname,
5774 mem_ctx, pnum_streams,
5778 case FRUIT_RSRC_ADFILE:
5779 status = fruit_streaminfo_rsrc_adouble(handle, fsp, smb_fname,
5780 mem_ctx, pnum_streams,
5785 return NT_STATUS_INTERNAL_ERROR;
5791 static void fruit_filter_empty_streams(unsigned int *pnum_streams,
5792 struct stream_struct **pstreams)
5794 unsigned num_streams = *pnum_streams;
5795 struct stream_struct *streams = *pstreams;
5798 if (!global_fruit_config.nego_aapl) {
5802 while (i < num_streams) {
5803 struct smb_filename smb_fname = (struct smb_filename) {
5804 .stream_name = streams[i].name,
5807 if (is_ntfs_default_stream_smb_fname(&smb_fname)
5808 || streams[i].size > 0)
5814 streams[i] = streams[num_streams - 1];
5818 *pnum_streams = num_streams;
5821 static NTSTATUS fruit_streaminfo(vfs_handle_struct *handle,
5822 struct files_struct *fsp,
5823 const struct smb_filename *smb_fname,
5824 TALLOC_CTX *mem_ctx,
5825 unsigned int *pnum_streams,
5826 struct stream_struct **pstreams)
5828 struct fruit_config_data *config = NULL;
5831 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5832 return NT_STATUS_UNSUCCESSFUL);
5834 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
5836 status = SMB_VFS_NEXT_STREAMINFO(handle, fsp, smb_fname, mem_ctx,
5837 pnum_streams, pstreams);
5838 if (!NT_STATUS_IS_OK(status)) {
5842 fruit_filter_empty_streams(pnum_streams, pstreams);
5844 status = fruit_streaminfo_meta(handle, fsp, smb_fname,
5845 mem_ctx, pnum_streams, pstreams);
5846 if (!NT_STATUS_IS_OK(status)) {
5850 status = fruit_streaminfo_rsrc(handle, fsp, smb_fname,
5851 mem_ctx, pnum_streams, pstreams);
5852 if (!NT_STATUS_IS_OK(status)) {
5856 return NT_STATUS_OK;
5859 static int fruit_ntimes(vfs_handle_struct *handle,
5860 const struct smb_filename *smb_fname,
5861 struct smb_file_time *ft)
5864 struct adouble *ad = NULL;
5865 struct fruit_config_data *config = NULL;
5867 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5870 if ((config->meta != FRUIT_META_NETATALK) ||
5871 null_timespec(ft->create_time))
5873 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
5876 DEBUG(10,("set btime for %s to %s\n", smb_fname_str_dbg(smb_fname),
5877 time_to_asc(convert_timespec_to_time_t(ft->create_time))));
5879 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5884 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX,
5885 convert_time_t_to_uint32_t(ft->create_time.tv_sec));
5887 rc = ad_set(handle, ad, smb_fname);
5893 DEBUG(1, ("fruit_ntimes: %s\n", smb_fname_str_dbg(smb_fname)));
5896 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
5899 static int fruit_fallocate(struct vfs_handle_struct *handle,
5900 struct files_struct *fsp,
5905 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5908 return SMB_VFS_NEXT_FALLOCATE(handle, fsp, mode, offset, len);
5911 /* Let the pwrite code path handle it. */
5916 static int fruit_ftruncate_rsrc_xattr(struct vfs_handle_struct *handle,
5917 struct files_struct *fsp,
5920 #ifdef HAVE_ATTROPEN
5921 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
5926 static int fruit_ftruncate_rsrc_adouble(struct vfs_handle_struct *handle,
5927 struct files_struct *fsp,
5931 struct adouble *ad = NULL;
5934 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
5936 DBG_DEBUG("ad_get [%s] failed [%s]\n",
5937 fsp_str_dbg(fsp), strerror(errno));
5941 ad_off = ad_getentryoff(ad, ADEID_RFORK);
5943 rc = ftruncate(fsp->fh->fd, offset + ad_off);
5949 ad_setentrylen(ad, ADEID_RFORK, offset);
5951 rc = ad_fset(handle, ad, fsp);
5953 DBG_ERR("ad_fset [%s] failed [%s]\n",
5954 fsp_str_dbg(fsp), strerror(errno));
5963 static int fruit_ftruncate_rsrc_stream(struct vfs_handle_struct *handle,
5964 struct files_struct *fsp,
5967 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
5970 static int fruit_ftruncate_rsrc(struct vfs_handle_struct *handle,
5971 struct files_struct *fsp,
5974 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5978 DBG_ERR("Failed to fetch fsp extension");
5982 switch (fio->config->rsrc) {
5983 case FRUIT_RSRC_XATTR:
5984 ret = fruit_ftruncate_rsrc_xattr(handle, fsp, offset);
5987 case FRUIT_RSRC_ADFILE:
5988 ret = fruit_ftruncate_rsrc_adouble(handle, fsp, offset);
5991 case FRUIT_RSRC_STREAM:
5992 ret = fruit_ftruncate_rsrc_stream(handle, fsp, offset);
5996 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
6004 static int fruit_ftruncate_meta(struct vfs_handle_struct *handle,
6005 struct files_struct *fsp,
6009 DBG_WARNING("ftruncate %s to %jd",
6010 fsp_str_dbg(fsp), (intmax_t)offset);
6011 /* OS X returns NT_STATUS_ALLOTTED_SPACE_EXCEEDED */
6016 /* OS X returns success but does nothing */
6017 DBG_INFO("ignoring ftruncate %s to %jd\n",
6018 fsp_str_dbg(fsp), (intmax_t)offset);
6022 static int fruit_ftruncate(struct vfs_handle_struct *handle,
6023 struct files_struct *fsp,
6026 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
6029 DBG_DEBUG("Path [%s] offset [%"PRIdMAX"]\n", fsp_str_dbg(fsp),
6033 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
6036 if (fio->type == ADOUBLE_META) {
6037 ret = fruit_ftruncate_meta(handle, fsp, offset);
6039 ret = fruit_ftruncate_rsrc(handle, fsp, offset);
6042 DBG_DEBUG("Path [%s] result [%d]\n", fsp_str_dbg(fsp), ret);
6046 static NTSTATUS fruit_create_file(vfs_handle_struct *handle,
6047 struct smb_request *req,
6048 uint16_t root_dir_fid,
6049 struct smb_filename *smb_fname,
6050 uint32_t access_mask,
6051 uint32_t share_access,
6052 uint32_t create_disposition,
6053 uint32_t create_options,
6054 uint32_t file_attributes,
6055 uint32_t oplock_request,
6056 struct smb2_lease *lease,
6057 uint64_t allocation_size,
6058 uint32_t private_flags,
6059 struct security_descriptor *sd,
6060 struct ea_list *ea_list,
6061 files_struct **result,
6063 const struct smb2_create_blobs *in_context_blobs,
6064 struct smb2_create_blobs *out_context_blobs)
6067 struct fruit_config_data *config = NULL;
6068 files_struct *fsp = NULL;
6069 struct fio *fio = NULL;
6070 bool internal_open = (oplock_request & INTERNAL_OPEN_ONLY);
6073 status = check_aapl(handle, req, in_context_blobs, out_context_blobs);
6074 if (!NT_STATUS_IS_OK(status)) {
6078 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
6079 return NT_STATUS_UNSUCCESSFUL);
6081 if (is_apple_stream(smb_fname) && !internal_open) {
6082 ret = ad_convert(handle, smb_fname);
6084 DBG_ERR("ad_convert() failed\n");
6085 return NT_STATUS_UNSUCCESSFUL;
6089 status = SMB_VFS_NEXT_CREATE_FILE(
6090 handle, req, root_dir_fid, smb_fname,
6091 access_mask, share_access,
6092 create_disposition, create_options,
6093 file_attributes, oplock_request,
6095 allocation_size, private_flags,
6096 sd, ea_list, result,
6097 pinfo, in_context_blobs, out_context_blobs);
6098 if (!NT_STATUS_IS_OK(status)) {
6104 if (global_fruit_config.nego_aapl) {
6105 if (config->posix_rename && fsp->is_directory) {
6107 * Enable POSIX directory rename behaviour
6109 fsp->posix_flags |= FSP_POSIX_FLAGS_RENAME;
6114 * If this is a plain open for existing files, opening an 0
6115 * byte size resource fork MUST fail with
6116 * NT_STATUS_OBJECT_NAME_NOT_FOUND.
6118 * Cf the vfs_fruit torture tests in test_rfork_create().
6120 if (global_fruit_config.nego_aapl &&
6121 create_disposition == FILE_OPEN &&
6122 smb_fname->st.st_ex_size == 0 &&
6123 is_ntfs_stream_smb_fname(smb_fname) &&
6124 !(is_ntfs_default_stream_smb_fname(smb_fname)))
6126 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
6130 fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
6131 if (fio != NULL && pinfo != NULL && *pinfo == FILE_WAS_CREATED) {
6132 fio->created = true;
6135 if (is_ntfs_stream_smb_fname(smb_fname)
6136 || fsp->is_directory) {
6140 if ((config->locking == FRUIT_LOCKING_NETATALK) &&
6143 status = fruit_check_access(
6147 if (!NT_STATUS_IS_OK(status)) {
6155 DEBUG(10, ("fruit_create_file: %s\n", nt_errstr(status)));
6158 close_file(req, fsp, ERROR_CLOSE);
6159 *result = fsp = NULL;
6165 static NTSTATUS fruit_readdir_attr(struct vfs_handle_struct *handle,
6166 const struct smb_filename *fname,
6167 TALLOC_CTX *mem_ctx,
6168 struct readdir_attr_data **pattr_data)
6170 struct fruit_config_data *config = NULL;
6171 struct readdir_attr_data *attr_data;
6175 SMB_VFS_HANDLE_GET_DATA(handle, config,
6176 struct fruit_config_data,
6177 return NT_STATUS_UNSUCCESSFUL);
6179 if (!global_fruit_config.nego_aapl) {
6180 return SMB_VFS_NEXT_READDIR_ATTR(handle, fname, mem_ctx, pattr_data);
6183 DEBUG(10, ("fruit_readdir_attr %s\n", fname->base_name));
6185 ret = ad_convert(handle, fname);
6187 DBG_ERR("ad_convert() failed\n");
6188 return NT_STATUS_UNSUCCESSFUL;
6191 *pattr_data = talloc_zero(mem_ctx, struct readdir_attr_data);
6192 if (*pattr_data == NULL) {
6193 return NT_STATUS_UNSUCCESSFUL;
6195 attr_data = *pattr_data;
6196 attr_data->type = RDATTR_AAPL;
6199 * Mac metadata: compressed FinderInfo, resource fork length
6202 status = readdir_attr_macmeta(handle, fname, attr_data);
6203 if (!NT_STATUS_IS_OK(status)) {
6205 * Error handling is tricky: if we return failure from
6206 * this function, the corresponding directory entry
6207 * will to be passed to the client, so we really just
6208 * want to error out on fatal errors.
6210 if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
6218 if (config->unix_info_enabled) {
6219 attr_data->attr_data.aapl.unix_mode = fname->st.st_ex_mode;
6225 if (!config->readdir_attr_max_access) {
6226 attr_data->attr_data.aapl.max_access = FILE_GENERIC_ALL;
6228 status = smbd_calculate_access_mask(
6232 SEC_FLAG_MAXIMUM_ALLOWED,
6233 &attr_data->attr_data.aapl.max_access);
6234 if (!NT_STATUS_IS_OK(status)) {
6239 return NT_STATUS_OK;
6242 DEBUG(1, ("fruit_readdir_attr %s, error: %s\n",
6243 fname->base_name, nt_errstr(status)));
6244 TALLOC_FREE(*pattr_data);
6248 static NTSTATUS fruit_fget_nt_acl(vfs_handle_struct *handle,
6250 uint32_t security_info,
6251 TALLOC_CTX *mem_ctx,
6252 struct security_descriptor **ppdesc)
6255 struct security_ace ace;
6257 struct fruit_config_data *config;
6259 SMB_VFS_HANDLE_GET_DATA(handle, config,
6260 struct fruit_config_data,
6261 return NT_STATUS_UNSUCCESSFUL);
6263 status = SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
6265 if (!NT_STATUS_IS_OK(status)) {
6270 * Add MS NFS style ACEs with uid, gid and mode
6272 if (!global_fruit_config.nego_aapl) {
6273 return NT_STATUS_OK;
6275 if (!config->unix_info_enabled) {
6276 return NT_STATUS_OK;
6279 /* First remove any existing ACE's with NFS style mode/uid/gid SIDs. */
6280 status = remove_virtual_nfs_aces(*ppdesc);
6281 if (!NT_STATUS_IS_OK(status)) {
6282 DBG_WARNING("failed to remove MS NFS style ACEs\n");
6286 /* MS NFS style mode */
6287 sid_compose(&sid, &global_sid_Unix_NFS_Mode, fsp->fsp_name->st.st_ex_mode);
6288 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6289 status = security_descriptor_dacl_add(*ppdesc, &ace);
6290 if (!NT_STATUS_IS_OK(status)) {
6291 DEBUG(1,("failed to add MS NFS style ACE\n"));
6295 /* MS NFS style uid */
6296 sid_compose(&sid, &global_sid_Unix_NFS_Users, fsp->fsp_name->st.st_ex_uid);
6297 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6298 status = security_descriptor_dacl_add(*ppdesc, &ace);
6299 if (!NT_STATUS_IS_OK(status)) {
6300 DEBUG(1,("failed to add MS NFS style ACE\n"));
6304 /* MS NFS style gid */
6305 sid_compose(&sid, &global_sid_Unix_NFS_Groups, fsp->fsp_name->st.st_ex_gid);
6306 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6307 status = security_descriptor_dacl_add(*ppdesc, &ace);
6308 if (!NT_STATUS_IS_OK(status)) {
6309 DEBUG(1,("failed to add MS NFS style ACE\n"));
6313 return NT_STATUS_OK;
6316 static NTSTATUS fruit_fset_nt_acl(vfs_handle_struct *handle,
6318 uint32_t security_info_sent,
6319 const struct security_descriptor *orig_psd)
6323 mode_t ms_nfs_mode = 0;
6325 struct security_descriptor *psd = NULL;
6326 uint32_t orig_num_aces = 0;
6328 if (orig_psd->dacl != NULL) {
6329 orig_num_aces = orig_psd->dacl->num_aces;
6332 psd = security_descriptor_copy(talloc_tos(), orig_psd);
6334 return NT_STATUS_NO_MEMORY;
6337 DBG_DEBUG("fruit_fset_nt_acl: %s\n", fsp_str_dbg(fsp));
6339 status = check_ms_nfs(handle, fsp, psd, &ms_nfs_mode, &do_chmod);
6340 if (!NT_STATUS_IS_OK(status)) {
6341 DEBUG(1, ("fruit_fset_nt_acl: check_ms_nfs failed%s\n", fsp_str_dbg(fsp)));
6347 * If only ms_nfs ACE entries were sent, ensure we set the DACL
6348 * sent/present flags correctly now we've removed them.
6351 if (orig_num_aces != 0) {
6353 * Are there any ACE's left ?
6355 if (psd->dacl->num_aces == 0) {
6356 /* No - clear the DACL sent/present flags. */
6357 security_info_sent &= ~SECINFO_DACL;
6358 psd->type &= ~SEC_DESC_DACL_PRESENT;
6362 status = SMB_VFS_NEXT_FSET_NT_ACL(handle, fsp, security_info_sent, psd);
6363 if (!NT_STATUS_IS_OK(status)) {
6364 DEBUG(1, ("fruit_fset_nt_acl: SMB_VFS_NEXT_FSET_NT_ACL failed%s\n", fsp_str_dbg(fsp)));
6370 if (fsp->fh->fd != -1) {
6371 result = SMB_VFS_FCHMOD(fsp, ms_nfs_mode);
6373 result = SMB_VFS_CHMOD(fsp->conn,
6379 DEBUG(1, ("chmod: %s, result: %d, %04o error %s\n", fsp_str_dbg(fsp),
6380 result, (unsigned)ms_nfs_mode,
6382 status = map_nt_error_from_unix(errno);
6389 return NT_STATUS_OK;
6392 static struct vfs_offload_ctx *fruit_offload_ctx;
6394 struct fruit_offload_read_state {
6395 struct vfs_handle_struct *handle;
6396 struct tevent_context *ev;
6402 static void fruit_offload_read_done(struct tevent_req *subreq);
6404 static struct tevent_req *fruit_offload_read_send(
6405 TALLOC_CTX *mem_ctx,
6406 struct tevent_context *ev,
6407 struct vfs_handle_struct *handle,
6414 struct tevent_req *req = NULL;
6415 struct tevent_req *subreq = NULL;
6416 struct fruit_offload_read_state *state = NULL;
6418 req = tevent_req_create(mem_ctx, &state,
6419 struct fruit_offload_read_state);
6423 *state = (struct fruit_offload_read_state) {
6430 subreq = SMB_VFS_NEXT_OFFLOAD_READ_SEND(mem_ctx, ev, handle, fsp,
6431 fsctl, ttl, offset, to_copy);
6432 if (tevent_req_nomem(subreq, req)) {
6433 return tevent_req_post(req, ev);
6435 tevent_req_set_callback(subreq, fruit_offload_read_done, req);
6439 static void fruit_offload_read_done(struct tevent_req *subreq)
6441 struct tevent_req *req = tevent_req_callback_data(
6442 subreq, struct tevent_req);
6443 struct fruit_offload_read_state *state = tevent_req_data(
6444 req, struct fruit_offload_read_state);
6447 status = SMB_VFS_NEXT_OFFLOAD_READ_RECV(subreq,
6451 TALLOC_FREE(subreq);
6452 if (tevent_req_nterror(req, status)) {
6456 if (state->fsctl != FSCTL_SRV_REQUEST_RESUME_KEY) {
6457 tevent_req_done(req);
6461 status = vfs_offload_token_ctx_init(state->fsp->conn->sconn->client,
6462 &fruit_offload_ctx);
6463 if (tevent_req_nterror(req, status)) {
6467 status = vfs_offload_token_db_store_fsp(fruit_offload_ctx,
6470 if (tevent_req_nterror(req, status)) {
6474 tevent_req_done(req);
6478 static NTSTATUS fruit_offload_read_recv(struct tevent_req *req,
6479 struct vfs_handle_struct *handle,
6480 TALLOC_CTX *mem_ctx,
6483 struct fruit_offload_read_state *state = tevent_req_data(
6484 req, struct fruit_offload_read_state);
6487 if (tevent_req_is_nterror(req, &status)) {
6488 tevent_req_received(req);
6492 token->length = state->token.length;
6493 token->data = talloc_move(mem_ctx, &state->token.data);
6495 tevent_req_received(req);
6496 return NT_STATUS_OK;
6499 struct fruit_offload_write_state {
6500 struct vfs_handle_struct *handle;
6502 struct files_struct *src_fsp;
6503 struct files_struct *dst_fsp;
6507 static void fruit_offload_write_done(struct tevent_req *subreq);
6508 static struct tevent_req *fruit_offload_write_send(struct vfs_handle_struct *handle,
6509 TALLOC_CTX *mem_ctx,
6510 struct tevent_context *ev,
6513 off_t transfer_offset,
6514 struct files_struct *dest_fsp,
6518 struct tevent_req *req, *subreq;
6519 struct fruit_offload_write_state *state;
6521 struct fruit_config_data *config;
6522 off_t src_off = transfer_offset;
6523 files_struct *src_fsp = NULL;
6524 off_t to_copy = num;
6525 bool copyfile_enabled = false;
6527 DEBUG(10,("soff: %ju, doff: %ju, len: %ju\n",
6528 (uintmax_t)src_off, (uintmax_t)dest_off, (uintmax_t)num));
6530 SMB_VFS_HANDLE_GET_DATA(handle, config,
6531 struct fruit_config_data,
6534 req = tevent_req_create(mem_ctx, &state,
6535 struct fruit_offload_write_state);
6539 state->handle = handle;
6540 state->dst_fsp = dest_fsp;
6543 case FSCTL_SRV_COPYCHUNK:
6544 case FSCTL_SRV_COPYCHUNK_WRITE:
6545 copyfile_enabled = config->copyfile_enabled;
6552 * Check if this a OS X copyfile style copychunk request with
6553 * a requested chunk count of 0 that was translated to a
6554 * offload_write_send VFS call overloading the parameters src_off
6555 * = dest_off = num = 0.
6557 if (copyfile_enabled && num == 0 && src_off == 0 && dest_off == 0) {
6558 status = vfs_offload_token_db_fetch_fsp(
6559 fruit_offload_ctx, token, &src_fsp);
6560 if (tevent_req_nterror(req, status)) {
6561 return tevent_req_post(req, ev);
6563 state->src_fsp = src_fsp;
6565 status = vfs_stat_fsp(src_fsp);
6566 if (tevent_req_nterror(req, status)) {
6567 return tevent_req_post(req, ev);
6570 to_copy = src_fsp->fsp_name->st.st_ex_size;
6571 state->is_copyfile = true;
6574 subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle,
6583 if (tevent_req_nomem(subreq, req)) {
6584 return tevent_req_post(req, ev);
6587 tevent_req_set_callback(subreq, fruit_offload_write_done, req);
6591 static void fruit_offload_write_done(struct tevent_req *subreq)
6593 struct tevent_req *req = tevent_req_callback_data(
6594 subreq, struct tevent_req);
6595 struct fruit_offload_write_state *state = tevent_req_data(
6596 req, struct fruit_offload_write_state);
6598 unsigned int num_streams = 0;
6599 struct stream_struct *streams = NULL;
6601 struct smb_filename *src_fname_tmp = NULL;
6602 struct smb_filename *dst_fname_tmp = NULL;
6604 status = SMB_VFS_NEXT_OFFLOAD_WRITE_RECV(state->handle,
6607 TALLOC_FREE(subreq);
6608 if (tevent_req_nterror(req, status)) {
6612 if (!state->is_copyfile) {
6613 tevent_req_done(req);
6618 * Now copy all remaining streams. We know the share supports
6619 * streams, because we're in vfs_fruit. We don't do this async
6620 * because streams are few and small.
6622 status = vfs_streaminfo(state->handle->conn, state->src_fsp,
6623 state->src_fsp->fsp_name,
6624 req, &num_streams, &streams);
6625 if (tevent_req_nterror(req, status)) {
6629 if (num_streams == 1) {
6630 /* There is always one stream, ::$DATA. */
6631 tevent_req_done(req);
6635 for (i = 0; i < num_streams; i++) {
6636 DEBUG(10, ("%s: stream: '%s'/%zu\n",
6637 __func__, streams[i].name, (size_t)streams[i].size));
6639 src_fname_tmp = synthetic_smb_fname(
6641 state->src_fsp->fsp_name->base_name,
6644 state->src_fsp->fsp_name->flags);
6645 if (tevent_req_nomem(src_fname_tmp, req)) {
6649 if (is_ntfs_default_stream_smb_fname(src_fname_tmp)) {
6650 TALLOC_FREE(src_fname_tmp);
6654 dst_fname_tmp = synthetic_smb_fname(
6656 state->dst_fsp->fsp_name->base_name,
6659 state->dst_fsp->fsp_name->flags);
6660 if (tevent_req_nomem(dst_fname_tmp, req)) {
6661 TALLOC_FREE(src_fname_tmp);
6665 status = copy_file(req,
6666 state->handle->conn,
6669 OPENX_FILE_CREATE_IF_NOT_EXIST,
6671 if (!NT_STATUS_IS_OK(status)) {
6672 DEBUG(1, ("%s: copy %s to %s failed: %s\n", __func__,
6673 smb_fname_str_dbg(src_fname_tmp),
6674 smb_fname_str_dbg(dst_fname_tmp),
6675 nt_errstr(status)));
6676 TALLOC_FREE(src_fname_tmp);
6677 TALLOC_FREE(dst_fname_tmp);
6678 tevent_req_nterror(req, status);
6682 TALLOC_FREE(src_fname_tmp);
6683 TALLOC_FREE(dst_fname_tmp);
6686 TALLOC_FREE(streams);
6687 TALLOC_FREE(src_fname_tmp);
6688 TALLOC_FREE(dst_fname_tmp);
6689 tevent_req_done(req);
6692 static NTSTATUS fruit_offload_write_recv(struct vfs_handle_struct *handle,
6693 struct tevent_req *req,
6696 struct fruit_offload_write_state *state = tevent_req_data(
6697 req, struct fruit_offload_write_state);
6700 if (tevent_req_is_nterror(req, &status)) {
6701 DEBUG(1, ("server side copy chunk failed: %s\n",
6702 nt_errstr(status)));
6704 tevent_req_received(req);
6708 *copied = state->copied;
6709 tevent_req_received(req);
6711 return NT_STATUS_OK;
6714 static char *fruit_get_bandsize_line(char **lines, int numlines)
6717 static bool re_initialized = false;
6721 if (!re_initialized) {
6722 ret = regcomp(&re, "^[[:blank:]]*<key>band-size</key>$", 0);
6726 re_initialized = true;
6729 for (i = 0; i < numlines; i++) {
6730 regmatch_t matches[1];
6732 ret = regexec(&re, lines[i], 1, matches, 0);
6735 * Check if the match was on the last line, sa we want
6736 * the subsequent line.
6738 if (i + 1 == numlines) {
6741 return lines[i + 1];
6743 if (ret != REG_NOMATCH) {
6751 static bool fruit_get_bandsize_from_line(char *line, size_t *_band_size)
6754 static bool re_initialized = false;
6755 regmatch_t matches[2];
6760 if (!re_initialized) {
6763 "<integer>\\([[:digit:]]*\\)</integer>$",
6768 re_initialized = true;
6771 ret = regexec(&re, line, 2, matches, 0);
6773 DBG_ERR("regex failed [%s]\n", line);
6777 line[matches[1].rm_eo] = '\0';
6779 ok = conv_str_u64(&line[matches[1].rm_so], &band_size);
6783 *_band_size = (size_t)band_size;
6788 * This reads and parses an Info.plist from a TM sparsebundle looking for the
6789 * "band-size" key and value.
6791 static bool fruit_get_bandsize(vfs_handle_struct *handle,
6795 #define INFO_PLIST_MAX_SIZE 64*1024
6797 struct smb_filename *smb_fname = NULL;
6798 files_struct *fsp = NULL;
6799 uint8_t *file_data = NULL;
6800 char **lines = NULL;
6801 char *band_size_line = NULL;
6802 size_t plist_file_size;
6809 plist = talloc_asprintf(talloc_tos(),
6811 handle->conn->connectpath,
6813 if (plist == NULL) {
6818 smb_fname = synthetic_smb_fname(talloc_tos(), plist, NULL, NULL, 0);
6819 if (smb_fname == NULL) {
6824 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
6826 DBG_INFO("Ignoring Sparsebundle without Info.plist [%s]\n", dir);
6831 plist_file_size = smb_fname->st.st_ex_size;
6833 if (plist_file_size > INFO_PLIST_MAX_SIZE) {
6834 DBG_INFO("%s is too large, ignoring\n", plist);
6839 status = SMB_VFS_NEXT_CREATE_FILE(
6842 0, /* root_dir_fid */
6843 smb_fname, /* fname */
6844 FILE_GENERIC_READ, /* access_mask */
6845 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
6846 FILE_OPEN, /* create_disposition */
6847 0, /* create_options */
6848 0, /* file_attributes */
6849 INTERNAL_OPEN_ONLY, /* oplock_request */
6851 0, /* allocation_size */
6852 0, /* private_flags */
6857 NULL, NULL); /* create context */
6858 if (!NT_STATUS_IS_OK(status)) {
6859 DBG_INFO("Opening [%s] failed [%s]\n",
6860 smb_fname_str_dbg(smb_fname), nt_errstr(status));
6865 file_data = talloc_array(talloc_tos(), uint8_t, plist_file_size);
6866 if (file_data == NULL) {
6871 nread = SMB_VFS_NEXT_PREAD(handle, fsp, file_data, plist_file_size, 0);
6872 if (nread != plist_file_size) {
6873 DBG_ERR("Short read on [%s]: %zu/%zd\n",
6874 fsp_str_dbg(fsp), nread, plist_file_size);
6880 status = close_file(NULL, fsp, NORMAL_CLOSE);
6882 if (!NT_STATUS_IS_OK(status)) {
6883 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
6888 lines = file_lines_parse((char *)file_data,
6892 if (lines == NULL) {
6897 band_size_line = fruit_get_bandsize_line(lines, numlines);
6898 if (band_size_line == NULL) {
6899 DBG_ERR("Didn't find band-size key in [%s]\n",
6900 smb_fname_str_dbg(smb_fname));
6905 ok = fruit_get_bandsize_from_line(band_size_line, band_size);
6907 DBG_ERR("fruit_get_bandsize_from_line failed\n");
6911 DBG_DEBUG("Parsed band-size [%zu] for [%s]\n", *band_size, plist);
6915 status = close_file(NULL, fsp, NORMAL_CLOSE);
6916 if (!NT_STATUS_IS_OK(status)) {
6917 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
6922 TALLOC_FREE(smb_fname);
6923 TALLOC_FREE(file_data);
6928 struct fruit_disk_free_state {
6932 static bool fruit_get_num_bands(vfs_handle_struct *handle,
6937 struct smb_filename *bands_dir = NULL;
6939 struct dirent *e = NULL;
6943 path = talloc_asprintf(talloc_tos(),
6945 handle->conn->connectpath,
6951 bands_dir = synthetic_smb_fname(talloc_tos(),
6957 if (bands_dir == NULL) {
6961 d = SMB_VFS_NEXT_OPENDIR(handle, bands_dir, NULL, 0);
6963 TALLOC_FREE(bands_dir);
6969 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
6971 e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
6973 if (ISDOT(e->d_name) || ISDOTDOT(e->d_name)) {
6979 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
6981 TALLOC_FREE(bands_dir);
6985 DBG_DEBUG("%zu bands in [%s]\n", nbands, smb_fname_str_dbg(bands_dir));
6987 TALLOC_FREE(bands_dir);
6993 static bool fruit_tmsize_do_dirent(vfs_handle_struct *handle,
6994 struct fruit_disk_free_state *state,
6999 size_t sparsebundle_strlen = strlen("sparsebundle");
7000 size_t bandsize = 0;
7004 p = strstr(e->d_name, "sparsebundle");
7009 if (p[sparsebundle_strlen] != '\0') {
7013 DBG_DEBUG("Processing sparsebundle [%s]\n", e->d_name);
7015 ok = fruit_get_bandsize(handle, e->d_name, &bandsize);
7018 * Beware of race conditions: this may be an uninitialized
7019 * Info.plist that a client is just creating. We don't want let
7020 * this to trigger complete failure.
7022 DBG_ERR("Processing sparsebundle [%s] failed\n", e->d_name);
7026 ok = fruit_get_num_bands(handle, e->d_name, &nbands);
7029 * Beware of race conditions: this may be a backup sparsebundle
7030 * in an early stage lacking a bands subdirectory. We don't want
7031 * let this to trigger complete failure.
7033 DBG_ERR("Processing sparsebundle [%s] failed\n", e->d_name);
7037 if (bandsize > SIZE_MAX/nbands) {
7038 DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
7042 tm_size = bandsize * nbands;
7044 if (state->total_size + tm_size < state->total_size) {
7045 DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
7050 state->total_size += tm_size;
7052 DBG_DEBUG("[%s] tm_size [%jd] total_size [%jd]\n",
7053 e->d_name, (intmax_t)tm_size, (intmax_t)state->total_size);
7059 * Calculate used size of a TimeMachine volume
7061 * This assumes that the volume is used only for TimeMachine.
7063 * - readdir(basedir of share), then
7064 * - for every element that matches regex "^\(.*\)\.sparsebundle$" :
7065 * - parse "\1.sparsebundle/Info.plist" and read the band-size XML key
7066 * - count band files in "\1.sparsebundle/bands/"
7067 * - calculate used size of all bands: band_count * band_size
7069 static uint64_t fruit_disk_free(vfs_handle_struct *handle,
7070 const struct smb_filename *smb_fname,
7075 struct fruit_config_data *config = NULL;
7076 struct fruit_disk_free_state state = {0};
7078 struct dirent *e = NULL;
7084 SMB_VFS_HANDLE_GET_DATA(handle, config,
7085 struct fruit_config_data,
7088 if (!config->time_machine ||
7089 config->time_machine_max_size == 0)
7091 return SMB_VFS_NEXT_DISK_FREE(handle,
7098 d = SMB_VFS_NEXT_OPENDIR(handle, smb_fname, NULL, 0);
7103 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
7105 e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
7107 ok = fruit_tmsize_do_dirent(handle, &state, e);
7109 SMB_VFS_NEXT_CLOSEDIR(handle, d);
7114 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
7119 dsize = config->time_machine_max_size / 512;
7120 dfree = dsize - (state.total_size / 512);
7121 if (dfree > dsize) {
7131 static struct vfs_fn_pointers vfs_fruit_fns = {
7132 .connect_fn = fruit_connect,
7133 .disk_free_fn = fruit_disk_free,
7135 /* File operations */
7136 .chmod_fn = fruit_chmod,
7137 .chown_fn = fruit_chown,
7138 .unlink_fn = fruit_unlink,
7139 .rename_fn = fruit_rename,
7140 .rmdir_fn = fruit_rmdir,
7141 .open_fn = fruit_open,
7142 .close_fn = fruit_close,
7143 .pread_fn = fruit_pread,
7144 .pwrite_fn = fruit_pwrite,
7145 .pread_send_fn = fruit_pread_send,
7146 .pread_recv_fn = fruit_pread_recv,
7147 .pwrite_send_fn = fruit_pwrite_send,
7148 .pwrite_recv_fn = fruit_pwrite_recv,
7149 .stat_fn = fruit_stat,
7150 .lstat_fn = fruit_lstat,
7151 .fstat_fn = fruit_fstat,
7152 .streaminfo_fn = fruit_streaminfo,
7153 .ntimes_fn = fruit_ntimes,
7154 .ftruncate_fn = fruit_ftruncate,
7155 .fallocate_fn = fruit_fallocate,
7156 .create_file_fn = fruit_create_file,
7157 .readdir_attr_fn = fruit_readdir_attr,
7158 .offload_read_send_fn = fruit_offload_read_send,
7159 .offload_read_recv_fn = fruit_offload_read_recv,
7160 .offload_write_send_fn = fruit_offload_write_send,
7161 .offload_write_recv_fn = fruit_offload_write_recv,
7163 /* NT ACL operations */
7164 .fget_nt_acl_fn = fruit_fget_nt_acl,
7165 .fset_nt_acl_fn = fruit_fset_nt_acl,
7169 NTSTATUS vfs_fruit_init(TALLOC_CTX *ctx)
7171 NTSTATUS ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "fruit",
7173 if (!NT_STATUS_IS_OK(ret)) {
7177 vfs_fruit_debug_level = debug_add_class("fruit");
7178 if (vfs_fruit_debug_level == -1) {
7179 vfs_fruit_debug_level = DBGC_VFS;
7180 DEBUG(0, ("%s: Couldn't register custom debugging class!\n",
7183 DEBUG(10, ("%s: Debug class number of '%s': %d\n",
7184 "vfs_fruit_init","fruit",vfs_fruit_debug_level));