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(struct adouble *ad,
1313 const struct smb_filename *smb_fname)
1318 * FIXME: direct ftruncate(), but we don't have a fsp for the
1321 rc = ftruncate(ad->ad_fsp->fh->fd, ADEDOFF_RFORK_DOT_UND +
1322 ad_getentrylen(ad, ADEID_RFORK));
1330 static bool ad_convert_blank_rfork(vfs_handle_struct *handle,
1334 struct fruit_config_data *config = NULL;
1335 uint8_t *map = MAP_FAILED;
1344 SMB_VFS_HANDLE_GET_DATA(handle, config,
1345 struct fruit_config_data, return false);
1347 if (!config->wipe_intentionally_left_blank_rfork) {
1351 if (ad_getentrylen(ad, ADEID_RFORK) != sizeof(empty_resourcefork)) {
1355 maplen = ad_getentryoff(ad, ADEID_RFORK) +
1356 ad_getentrylen(ad, ADEID_RFORK);
1358 /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
1359 map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
1360 ad->ad_fsp->fh->fd, 0);
1361 if (map == MAP_FAILED) {
1362 DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
1366 cmp = memcmp(map + ADEDOFF_RFORK_DOT_UND,
1368 sizeof(empty_resourcefork));
1369 rc = munmap(map, maplen);
1371 DBG_ERR("munmap failed: %s\n", strerror(errno));
1379 ad_setentrylen(ad, ADEID_RFORK, 0);
1386 len = sys_pwrite(ad->ad_fsp->fh->fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
1387 if (len != AD_DATASZ_DOT_UND) {
1395 static bool ad_convert_delete_adfile(vfs_handle_struct *handle,
1397 const struct smb_filename *smb_fname)
1399 struct fruit_config_data *config = NULL;
1400 struct smb_filename *ad_name = NULL;
1403 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
1407 SMB_VFS_HANDLE_GET_DATA(handle, config,
1408 struct fruit_config_data, return false);
1410 if (!config->delete_empty_adfiles) {
1414 rc = adouble_path(talloc_tos(), smb_fname, &ad_name);
1419 rc = SMB_VFS_NEXT_UNLINK(handle, ad_name);
1421 DBG_ERR("Unlinking [%s] failed: %s\n",
1422 smb_fname_str_dbg(ad_name), strerror(errno));
1423 TALLOC_FREE(ad_name);
1427 DBG_WARNING("Unlinked [%s] after conversion\n", smb_fname_str_dbg(ad_name));
1428 TALLOC_FREE(ad_name);
1434 * Convert from Apple's ._ file to Netatalk
1436 * Apple's AppleDouble may contain a FinderInfo entry longer then 32
1437 * bytes containing packed xattrs.
1439 * @return -1 in case an error occurred, 0 if no conversion was done, 1
1442 static int ad_convert(struct vfs_handle_struct *handle,
1443 const struct smb_filename *smb_fname)
1445 struct adouble *ad = NULL;
1447 bool converted_xattr = false;
1451 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
1456 ok = ad_convert_xattr(handle, ad, smb_fname, &converted_xattr);
1462 ok = ad_convert_blank_rfork(handle, ad, &blank);
1468 if (converted_xattr || blank) {
1469 ok = ad_convert_truncate(ad, smb_fname);
1476 ok = ad_convert_finderinfo(handle, ad, smb_fname);
1478 DBG_ERR("Failed to convert [%s]\n",
1479 smb_fname_str_dbg(smb_fname));
1484 ok = ad_convert_delete_adfile(handle, ad, smb_fname);
1497 * Read and parse Netatalk AppleDouble metadata xattr
1499 static ssize_t ad_read_meta(vfs_handle_struct *handle,
1501 const struct smb_filename *smb_fname)
1507 DEBUG(10, ("reading meta xattr for %s\n", smb_fname->base_name));
1509 ealen = SMB_VFS_GETXATTR(handle->conn, smb_fname,
1510 AFPINFO_EA_NETATALK, ad->ad_data,
1516 if (errno == ENOATTR) {
1522 DEBUG(2, ("error reading meta xattr: %s\n",
1528 if (ealen != AD_DATASZ_XATTR) {
1529 DEBUG(2, ("bad size %zd\n", ealen));
1535 /* Now parse entries */
1536 ok = ad_unpack(ad, ADEID_NUM_XATTR, AD_DATASZ_XATTR);
1538 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1544 if (!ad_getentryoff(ad, ADEID_FINDERI)
1545 || !ad_getentryoff(ad, ADEID_COMMENT)
1546 || !ad_getentryoff(ad, ADEID_FILEDATESI)
1547 || !ad_getentryoff(ad, ADEID_AFPFILEI)
1548 || !ad_getentryoff(ad, ADEID_PRIVDEV)
1549 || !ad_getentryoff(ad, ADEID_PRIVINO)
1550 || !ad_getentryoff(ad, ADEID_PRIVSYN)
1551 || !ad_getentryoff(ad, ADEID_PRIVID)) {
1552 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1559 DEBUG(10, ("reading meta xattr for %s, rc: %d\n",
1560 smb_fname->base_name, rc));
1564 if (errno == EINVAL) {
1566 (void)SMB_VFS_REMOVEXATTR(handle->conn,
1568 AFPINFO_EA_NETATALK);
1576 static int ad_open_rsrc(vfs_handle_struct *handle,
1577 const struct smb_filename *smb_fname,
1580 files_struct **_fsp)
1583 struct smb_filename *adp_smb_fname = NULL;
1584 files_struct *fsp = NULL;
1585 uint32_t access_mask;
1586 uint32_t share_access;
1587 uint32_t create_disposition;
1590 ret = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
1595 ret = SMB_VFS_STAT(handle->conn, adp_smb_fname);
1597 TALLOC_FREE(adp_smb_fname);
1601 access_mask = FILE_GENERIC_READ;
1602 share_access = FILE_SHARE_READ | FILE_SHARE_WRITE;
1603 create_disposition = FILE_OPEN;
1605 if (flags & O_RDWR) {
1606 access_mask |= FILE_GENERIC_WRITE;
1607 share_access &= ~FILE_SHARE_WRITE;
1610 status = SMB_VFS_CREATE_FILE(
1611 handle->conn, /* conn */
1613 0, /* root_dir_fid */
1618 0, /* create_options */
1619 0, /* file_attributes */
1620 INTERNAL_OPEN_ONLY, /* oplock_request */
1622 0, /* allocation_size */
1623 0, /* private_flags */
1628 NULL, NULL); /* create context */
1629 TALLOC_FREE(adp_smb_fname);
1630 if (!NT_STATUS_IS_OK(status)) {
1631 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1640 * Here's the deal: for ADOUBLE_META we can do without an fd as we can issue
1641 * path based xattr calls. For ADOUBLE_RSRC however we need a full-fledged fd
1642 * for file IO on the ._ file.
1644 static int ad_open(vfs_handle_struct *handle,
1647 const struct smb_filename *smb_fname,
1653 DBG_DEBUG("Path [%s] type [%s]\n", smb_fname->base_name,
1654 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
1656 if (ad->ad_type == ADOUBLE_META) {
1662 ad->ad_opened = false;
1666 ret = ad_open_rsrc(handle, smb_fname, flags, mode, &ad->ad_fsp);
1670 ad->ad_opened = true;
1672 DBG_DEBUG("Path [%s] type [%s]\n",
1673 smb_fname->base_name,
1674 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
1679 static ssize_t ad_read_rsrc_adouble(vfs_handle_struct *handle,
1681 const struct smb_filename *smb_fname)
1683 SMB_STRUCT_STAT sbuf;
1690 ret = sys_fstat(ad->ad_fsp->fh->fd, &sbuf, lp_fake_directory_create_times(
1691 SNUM(handle->conn)));
1697 * AppleDouble file header content and size, two cases:
1699 * - without xattrs it is exactly AD_DATASZ_DOT_UND (82) bytes large
1700 * - with embedded xattrs it can be larger, up to AD_XATTR_MAX_HDR_SIZE
1702 * Read as much as we can up to AD_XATTR_MAX_HDR_SIZE.
1704 size = sbuf.st_ex_size;
1705 if (size > talloc_array_length(ad->ad_data)) {
1706 if (size > AD_XATTR_MAX_HDR_SIZE) {
1707 size = AD_XATTR_MAX_HDR_SIZE;
1709 p_ad = talloc_realloc(ad, ad->ad_data, char, size);
1716 len = sys_pread(ad->ad_fsp->fh->fd, ad->ad_data,
1717 talloc_array_length(ad->ad_data), 0);
1718 if (len != talloc_array_length(ad->ad_data)) {
1719 DBG_NOTICE("%s %s: bad size: %zd\n",
1720 smb_fname->base_name, strerror(errno), len);
1724 /* Now parse entries */
1725 ok = ad_unpack(ad, ADEID_NUM_DOT_UND, sbuf.st_ex_size);
1727 DBG_ERR("invalid AppleDouble resource %s\n",
1728 smb_fname->base_name);
1733 if ((ad_getentryoff(ad, ADEID_FINDERI) != ADEDOFF_FINDERI_DOT_UND)
1734 || (ad_getentrylen(ad, ADEID_FINDERI) < ADEDLEN_FINDERI)
1735 || (ad_getentryoff(ad, ADEID_RFORK) < ADEDOFF_RFORK_DOT_UND)) {
1736 DBG_ERR("invalid AppleDouble resource %s\n",
1737 smb_fname->base_name);
1746 * Read and parse resource fork, either ._ AppleDouble file or xattr
1748 static ssize_t ad_read_rsrc(vfs_handle_struct *handle,
1750 const struct smb_filename *smb_fname)
1752 return ad_read_rsrc_adouble(handle, ad, smb_fname);
1756 * Read and unpack an AppleDouble metadata xattr or resource
1758 static ssize_t ad_read(vfs_handle_struct *handle,
1760 const struct smb_filename *smb_fname)
1762 switch (ad->ad_type) {
1764 return ad_read_meta(handle, ad, smb_fname);
1766 return ad_read_rsrc(handle, ad, smb_fname);
1772 static int adouble_destructor(struct adouble *ad)
1776 if (!ad->ad_opened) {
1780 SMB_ASSERT(ad->ad_fsp != NULL);
1782 status = close_file(NULL, ad->ad_fsp, NORMAL_CLOSE);
1783 if (!NT_STATUS_IS_OK(status)) {
1784 DBG_ERR("Closing [%s] failed: %s\n",
1785 fsp_str_dbg(ad->ad_fsp), nt_errstr(status));
1792 * Allocate a struct adouble without initialiing it
1794 * The struct is either hang of the fsp extension context or if fsp is
1797 * @param[in] ctx talloc context
1798 * @param[in] handle vfs handle
1799 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1801 * @return adouble handle
1803 static struct adouble *ad_alloc(TALLOC_CTX *ctx,
1804 adouble_type_t type)
1812 adsize = AD_DATASZ_XATTR;
1815 adsize = AD_DATASZ_DOT_UND;
1821 ad = talloc_zero(ctx, struct adouble);
1828 ad->ad_data = talloc_zero_array(ad, char, adsize);
1829 if (ad->ad_data == NULL) {
1836 ad->ad_magic = AD_MAGIC;
1837 ad->ad_version = AD_VERSION;
1839 talloc_set_destructor(ad, adouble_destructor);
1849 * Allocate and initialize a new struct adouble
1851 * @param[in] ctx talloc context
1852 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1854 * @return adouble handle, initialized
1856 static struct adouble *ad_init(TALLOC_CTX *ctx,
1857 adouble_type_t type)
1860 const struct ad_entry_order *eid;
1861 struct adouble *ad = NULL;
1862 time_t t = time(NULL);
1866 eid = entry_order_meta_xattr;
1869 eid = entry_order_dot_und;
1875 ad = ad_alloc(ctx, type);
1881 ad->ad_eid[eid->id].ade_off = eid->offset;
1882 ad->ad_eid[eid->id].ade_len = eid->len;
1886 /* put something sane in the date fields */
1887 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, t);
1888 ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, t);
1889 ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, t);
1890 ad_setdate(ad, AD_DATE_BACKUP, htonl(AD_DATE_START));
1898 static struct adouble *ad_get_internal(TALLOC_CTX *ctx,
1899 vfs_handle_struct *handle,
1901 const struct smb_filename *smb_fname,
1902 adouble_type_t type)
1906 struct adouble *ad = NULL;
1910 smb_fname = fsp->base_fsp->fsp_name;
1913 DEBUG(10, ("ad_get(%s) called for %s\n",
1914 type == ADOUBLE_META ? "meta" : "rsrc",
1915 smb_fname->base_name));
1917 ad = ad_alloc(ctx, type);
1923 /* Try rw first so we can use the fd in ad_convert() */
1926 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
1927 if (rc == -1 && ((errno == EROFS) || (errno == EACCES))) {
1929 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
1932 DBG_DEBUG("ad_open [%s] error [%s]\n",
1933 smb_fname->base_name, strerror(errno));
1938 len = ad_read(handle, ad, smb_fname);
1940 DEBUG(10, ("error reading AppleDouble for %s\n",
1941 smb_fname->base_name));
1947 DEBUG(10, ("ad_get(%s) for %s returning %d\n",
1948 type == ADOUBLE_META ? "meta" : "rsrc",
1949 smb_fname->base_name, rc));
1958 * Return AppleDouble data for a file
1960 * @param[in] ctx talloc context
1961 * @param[in] handle vfs handle
1962 * @param[in] smb_fname pathname to file or directory
1963 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1965 * @return talloced struct adouble or NULL on error
1967 static struct adouble *ad_get(TALLOC_CTX *ctx,
1968 vfs_handle_struct *handle,
1969 const struct smb_filename *smb_fname,
1970 adouble_type_t type)
1972 return ad_get_internal(ctx, handle, NULL, smb_fname, type);
1976 * Return AppleDouble data for a file
1978 * @param[in] ctx talloc context
1979 * @param[in] handle vfs handle
1980 * @param[in] fsp fsp to use for IO
1981 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1983 * @return talloced struct adouble or NULL on error
1985 static struct adouble *ad_fget(TALLOC_CTX *ctx, vfs_handle_struct *handle,
1986 files_struct *fsp, adouble_type_t type)
1988 return ad_get_internal(ctx, handle, fsp, NULL, type);
1992 * Set AppleDouble metadata on a file or directory
1994 * @param[in] ad adouble handle
1996 * @param[in] smb_fname pathname to file or directory
1998 * @return status code, 0 means success
2000 static int ad_set(vfs_handle_struct *handle,
2002 const struct smb_filename *smb_fname)
2007 DBG_DEBUG("Path [%s]\n", smb_fname->base_name);
2009 if (ad->ad_type != ADOUBLE_META) {
2010 DBG_ERR("ad_set on [%s] used with ADOUBLE_RSRC\n",
2011 smb_fname->base_name);
2020 ret = SMB_VFS_SETXATTR(handle->conn,
2022 AFPINFO_EA_NETATALK,
2024 AD_DATASZ_XATTR, 0);
2026 DBG_DEBUG("Path [%s] ret [%d]\n", smb_fname->base_name, ret);
2032 * Set AppleDouble metadata on a file or directory
2034 * @param[in] ad adouble handle
2035 * @param[in] fsp file handle
2037 * @return status code, 0 means success
2039 static int ad_fset(struct vfs_handle_struct *handle,
2047 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
2050 || (fsp->fh == NULL)
2051 || (fsp->fh->fd == -1))
2053 smb_panic("bad fsp");
2061 switch (ad->ad_type) {
2063 rc = SMB_VFS_NEXT_SETXATTR(handle,
2065 AFPINFO_EA_NETATALK,
2067 AD_DATASZ_XATTR, 0);
2071 len = SMB_VFS_NEXT_PWRITE(handle,
2076 if (len != AD_DATASZ_DOT_UND) {
2077 DBG_ERR("short write on %s: %zd", fsp_str_dbg(fsp), len);
2087 DBG_DEBUG("Path [%s] rc [%d]\n", fsp_str_dbg(fsp), rc);
2092 /*****************************************************************************
2094 *****************************************************************************/
2096 static bool is_afpinfo_stream(const struct smb_filename *smb_fname)
2098 if (strncasecmp_m(smb_fname->stream_name,
2099 AFPINFO_STREAM_NAME,
2100 strlen(AFPINFO_STREAM_NAME)) == 0) {
2106 static bool is_afpresource_stream(const struct smb_filename *smb_fname)
2108 if (strncasecmp_m(smb_fname->stream_name,
2109 AFPRESOURCE_STREAM_NAME,
2110 strlen(AFPRESOURCE_STREAM_NAME)) == 0) {
2117 * Test whether stream is an Apple stream.
2119 static bool is_apple_stream(const struct smb_filename *smb_fname)
2121 if (is_afpinfo_stream(smb_fname)) {
2124 if (is_afpresource_stream(smb_fname)) {
2130 static bool is_adouble_file(const char *path)
2132 const char *p = NULL;
2135 p = strrchr(path, '/');
2143 ADOUBLE_NAME_PREFIX,
2144 strlen(ADOUBLE_NAME_PREFIX));
2152 * Initialize config struct from our smb.conf config parameters
2154 static int init_fruit_config(vfs_handle_struct *handle)
2156 struct fruit_config_data *config;
2158 const char *tm_size_str = NULL;
2160 config = talloc_zero(handle->conn, struct fruit_config_data);
2162 DEBUG(1, ("talloc_zero() failed\n"));
2168 * Versions up to Samba 4.5.x had a spelling bug in the
2169 * fruit:resource option calling lp_parm_enum with
2170 * "res*s*ource" (ie two s).
2172 * In Samba 4.6 we accept both the wrong and the correct
2173 * spelling, in Samba 4.7 the bad spelling will be removed.
2175 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2176 "ressource", fruit_rsrc, FRUIT_RSRC_ADFILE);
2177 if (enumval == -1) {
2178 DEBUG(1, ("value for %s: resource type unknown\n",
2179 FRUIT_PARAM_TYPE_NAME));
2182 config->rsrc = (enum fruit_rsrc)enumval;
2184 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2185 "resource", fruit_rsrc, enumval);
2186 if (enumval == -1) {
2187 DEBUG(1, ("value for %s: resource type unknown\n",
2188 FRUIT_PARAM_TYPE_NAME));
2191 config->rsrc = (enum fruit_rsrc)enumval;
2193 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2194 "metadata", fruit_meta, FRUIT_META_NETATALK);
2195 if (enumval == -1) {
2196 DEBUG(1, ("value for %s: metadata type unknown\n",
2197 FRUIT_PARAM_TYPE_NAME));
2200 config->meta = (enum fruit_meta)enumval;
2202 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2203 "locking", fruit_locking, FRUIT_LOCKING_NONE);
2204 if (enumval == -1) {
2205 DEBUG(1, ("value for %s: locking type unknown\n",
2206 FRUIT_PARAM_TYPE_NAME));
2209 config->locking = (enum fruit_locking)enumval;
2211 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2212 "encoding", fruit_encoding, FRUIT_ENC_PRIVATE);
2213 if (enumval == -1) {
2214 DEBUG(1, ("value for %s: encoding type unknown\n",
2215 FRUIT_PARAM_TYPE_NAME));
2218 config->encoding = (enum fruit_encoding)enumval;
2220 if (config->rsrc == FRUIT_RSRC_ADFILE) {
2221 config->veto_appledouble = lp_parm_bool(SNUM(handle->conn),
2222 FRUIT_PARAM_TYPE_NAME,
2227 config->use_aapl = lp_parm_bool(
2228 -1, FRUIT_PARAM_TYPE_NAME, "aapl", true);
2230 config->time_machine = lp_parm_bool(
2231 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "time machine", false);
2233 config->unix_info_enabled = lp_parm_bool(
2234 -1, FRUIT_PARAM_TYPE_NAME, "nfs_aces", true);
2236 config->use_copyfile = lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME,
2239 config->posix_rename = lp_parm_bool(
2240 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "posix_rename", true);
2242 config->aapl_zero_file_id =
2243 lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME, "zero_file_id", true);
2245 config->readdir_attr_rsize = lp_parm_bool(
2246 SNUM(handle->conn), "readdir_attr", "aapl_rsize", true);
2248 config->readdir_attr_finder_info = lp_parm_bool(
2249 SNUM(handle->conn), "readdir_attr", "aapl_finder_info", true);
2251 config->readdir_attr_max_access = lp_parm_bool(
2252 SNUM(handle->conn), "readdir_attr", "aapl_max_access", true);
2254 config->model = lp_parm_const_string(
2255 -1, FRUIT_PARAM_TYPE_NAME, "model", "MacSamba");
2257 tm_size_str = lp_parm_const_string(
2258 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2259 "time machine max size", NULL);
2260 if (tm_size_str != NULL) {
2261 config->time_machine_max_size = conv_str_size(tm_size_str);
2264 config->wipe_intentionally_left_blank_rfork = lp_parm_bool(
2265 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2266 "wipe_intentionally_left_blank_rfork", false);
2268 config->delete_empty_adfiles = lp_parm_bool(
2269 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2270 "delete_empty_adfiles", false);
2272 SMB_VFS_HANDLE_SET_DATA(handle, config,
2273 NULL, struct fruit_config_data,
2280 * Prepend "._" to a basename
2281 * Return a new struct smb_filename with stream_name == NULL.
2283 static int adouble_path(TALLOC_CTX *ctx,
2284 const struct smb_filename *smb_fname_in,
2285 struct smb_filename **pp_smb_fname_out)
2289 struct smb_filename *smb_fname = cp_smb_filename(ctx,
2292 if (smb_fname == NULL) {
2296 /* We need streamname to be NULL */
2297 TALLOC_FREE(smb_fname->stream_name);
2299 /* And we're replacing base_name. */
2300 TALLOC_FREE(smb_fname->base_name);
2302 if (!parent_dirname(smb_fname, smb_fname_in->base_name,
2304 TALLOC_FREE(smb_fname);
2308 smb_fname->base_name = talloc_asprintf(smb_fname,
2309 "%s/._%s", parent, base);
2310 if (smb_fname->base_name == NULL) {
2311 TALLOC_FREE(smb_fname);
2315 *pp_smb_fname_out = smb_fname;
2321 * Allocate and initialize an AfpInfo struct
2323 static AfpInfo *afpinfo_new(TALLOC_CTX *ctx)
2325 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2329 ai->afpi_Signature = AFP_Signature;
2330 ai->afpi_Version = AFP_Version;
2331 ai->afpi_BackupTime = AD_DATE_START;
2336 * Pack an AfpInfo struct into a buffer
2338 * Buffer size must be at least AFP_INFO_SIZE
2339 * Returns size of packed buffer
2341 static ssize_t afpinfo_pack(const AfpInfo *ai, char *buf)
2343 memset(buf, 0, AFP_INFO_SIZE);
2345 RSIVAL(buf, 0, ai->afpi_Signature);
2346 RSIVAL(buf, 4, ai->afpi_Version);
2347 RSIVAL(buf, 12, ai->afpi_BackupTime);
2348 memcpy(buf + 16, ai->afpi_FinderInfo, sizeof(ai->afpi_FinderInfo));
2350 return AFP_INFO_SIZE;
2354 * Unpack a buffer into a AfpInfo structure
2356 * Buffer size must be at least AFP_INFO_SIZE
2357 * Returns allocated AfpInfo struct
2359 static AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data)
2361 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2366 ai->afpi_Signature = RIVAL(data, 0);
2367 ai->afpi_Version = RIVAL(data, 4);
2368 ai->afpi_BackupTime = RIVAL(data, 12);
2369 memcpy(ai->afpi_FinderInfo, (const char *)data + 16,
2370 sizeof(ai->afpi_FinderInfo));
2372 if (ai->afpi_Signature != AFP_Signature
2373 || ai->afpi_Version != AFP_Version) {
2374 DEBUG(1, ("Bad AfpInfo signature or version\n"));
2382 * Fake an inode number from the md5 hash of the (xattr) name
2384 static SMB_INO_T fruit_inode(const SMB_STRUCT_STAT *sbuf, const char *sname)
2386 gnutls_hash_hd_t hash_hnd = NULL;
2387 unsigned char hash[16];
2388 SMB_INO_T result = 0;
2392 DBG_DEBUG("fruit_inode called for %ju/%ju [%s]\n",
2393 (uintmax_t)sbuf->st_ex_dev,
2394 (uintmax_t)sbuf->st_ex_ino, sname);
2396 upper_sname = talloc_strdup_upper(talloc_tos(), sname);
2397 SMB_ASSERT(upper_sname != NULL);
2399 rc = gnutls_hash_init(&hash_hnd, GNUTLS_DIG_MD5);
2404 rc = gnutls_hash(hash_hnd, &(sbuf->st_ex_dev), sizeof(sbuf->st_ex_dev));
2406 gnutls_hash_deinit(hash_hnd, NULL);
2409 rc = gnutls_hash(hash_hnd,
2411 sizeof(sbuf->st_ex_ino));
2413 gnutls_hash_deinit(hash_hnd, NULL);
2416 rc = gnutls_hash(hash_hnd,
2418 talloc_get_size(upper_sname) - 1);
2420 gnutls_hash_deinit(hash_hnd, NULL);
2424 gnutls_hash_deinit(hash_hnd, hash);
2426 /* Hopefully all the variation is in the lower 4 (or 8) bytes! */
2427 memcpy(&result, hash, sizeof(result));
2430 DBG_DEBUG("fruit_inode \"%s\": ino=%ju\n",
2431 sname, (uintmax_t)result);
2434 TALLOC_FREE(upper_sname);
2439 static bool add_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
2440 struct stream_struct **streams,
2441 const char *name, off_t size,
2444 struct stream_struct *tmp;
2446 tmp = talloc_realloc(mem_ctx, *streams, struct stream_struct,
2452 tmp[*num_streams].name = talloc_asprintf(tmp, "%s:$DATA", name);
2453 if (tmp[*num_streams].name == NULL) {
2457 tmp[*num_streams].size = size;
2458 tmp[*num_streams].alloc_size = alloc_size;
2465 static bool filter_empty_rsrc_stream(unsigned int *num_streams,
2466 struct stream_struct **streams)
2468 struct stream_struct *tmp = *streams;
2471 if (*num_streams == 0) {
2475 for (i = 0; i < *num_streams; i++) {
2476 if (strequal_m(tmp[i].name, AFPRESOURCE_STREAM)) {
2481 if (i == *num_streams) {
2485 if (tmp[i].size > 0) {
2489 TALLOC_FREE(tmp[i].name);
2490 if (*num_streams - 1 > i) {
2491 memmove(&tmp[i], &tmp[i+1],
2492 (*num_streams - i - 1) * sizeof(struct stream_struct));
2499 static bool del_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
2500 struct stream_struct **streams,
2503 struct stream_struct *tmp = *streams;
2506 if (*num_streams == 0) {
2510 for (i = 0; i < *num_streams; i++) {
2511 if (strequal_m(tmp[i].name, name)) {
2516 if (i == *num_streams) {
2520 TALLOC_FREE(tmp[i].name);
2521 if (*num_streams - 1 > i) {
2522 memmove(&tmp[i], &tmp[i+1],
2523 (*num_streams - i - 1) * sizeof(struct stream_struct));
2530 static bool ad_empty_finderinfo(const struct adouble *ad)
2533 char emptybuf[ADEDLEN_FINDERI] = {0};
2536 fi = ad_get_entry(ad, ADEID_FINDERI);
2538 DBG_ERR("Missing FinderInfo in struct adouble [%p]\n", ad);
2542 cmp = memcmp(emptybuf, fi, ADEDLEN_FINDERI);
2546 static bool ai_empty_finderinfo(const AfpInfo *ai)
2549 char emptybuf[ADEDLEN_FINDERI] = {0};
2551 cmp = memcmp(emptybuf, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
2556 * Update btime with btime from Netatalk
2558 static void update_btime(vfs_handle_struct *handle,
2559 struct smb_filename *smb_fname)
2562 struct timespec creation_time = {0};
2564 struct fruit_config_data *config = NULL;
2566 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
2569 switch (config->meta) {
2570 case FRUIT_META_STREAM:
2572 case FRUIT_META_NETATALK:
2576 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
2580 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
2584 if (ad_getdate(ad, AD_DATE_UNIX | AD_DATE_CREATE, &t) != 0) {
2590 creation_time.tv_sec = convert_uint32_t_to_time_t(t);
2591 update_stat_ex_create_time(&smb_fname->st, creation_time);
2597 * Map an access mask to a Netatalk single byte byte range lock
2599 static off_t access_to_netatalk_brl(enum apple_fork fork_type,
2600 uint32_t access_mask)
2604 switch (access_mask) {
2605 case FILE_READ_DATA:
2606 offset = AD_FILELOCK_OPEN_RD;
2609 case FILE_WRITE_DATA:
2610 case FILE_APPEND_DATA:
2611 offset = AD_FILELOCK_OPEN_WR;
2615 offset = AD_FILELOCK_OPEN_NONE;
2619 if (fork_type == APPLE_FORK_RSRC) {
2620 if (offset == AD_FILELOCK_OPEN_NONE) {
2621 offset = AD_FILELOCK_RSRC_OPEN_NONE;
2631 * Map a deny mode to a Netatalk brl
2633 static off_t denymode_to_netatalk_brl(enum apple_fork fork_type,
2638 switch (deny_mode) {
2640 offset = AD_FILELOCK_DENY_RD;
2644 offset = AD_FILELOCK_DENY_WR;
2648 smb_panic("denymode_to_netatalk_brl: bad deny mode\n");
2651 if (fork_type == APPLE_FORK_RSRC) {
2659 * Call fcntl() with an exclusive F_GETLK request in order to
2660 * determine if there's an exisiting shared lock
2662 * @return true if the requested lock was found or any error occurred
2663 * false if the lock was not found
2665 static bool test_netatalk_lock(files_struct *fsp, off_t in_offset)
2668 off_t offset = in_offset;
2673 result = SMB_VFS_GETLOCK(fsp, &offset, &len, &type, &pid);
2674 if (result == false) {
2678 if (type != F_UNLCK) {
2685 static NTSTATUS fruit_check_access(vfs_handle_struct *handle,
2687 uint32_t access_mask,
2688 uint32_t share_mode)
2690 NTSTATUS status = NT_STATUS_OK;
2692 bool share_for_read = (share_mode & FILE_SHARE_READ);
2693 bool share_for_write = (share_mode & FILE_SHARE_WRITE);
2694 bool netatalk_already_open_for_reading = false;
2695 bool netatalk_already_open_for_writing = false;
2696 bool netatalk_already_open_with_deny_read = false;
2697 bool netatalk_already_open_with_deny_write = false;
2699 /* FIXME: hardcoded data fork, add resource fork */
2700 enum apple_fork fork_type = APPLE_FORK_DATA;
2702 DBG_DEBUG("fruit_check_access: %s, am: %s/%s, sm: 0x%x\n",
2704 access_mask & FILE_READ_DATA ? "READ" :"-",
2705 access_mask & FILE_WRITE_DATA ? "WRITE" : "-",
2708 if (fsp->fh->fd == -1) {
2709 return NT_STATUS_OK;
2712 /* Read NetATalk opens and deny modes on the file. */
2713 netatalk_already_open_for_reading = test_netatalk_lock(fsp,
2714 access_to_netatalk_brl(fork_type,
2717 netatalk_already_open_with_deny_read = test_netatalk_lock(fsp,
2718 denymode_to_netatalk_brl(fork_type,
2721 netatalk_already_open_for_writing = test_netatalk_lock(fsp,
2722 access_to_netatalk_brl(fork_type,
2725 netatalk_already_open_with_deny_write = test_netatalk_lock(fsp,
2726 denymode_to_netatalk_brl(fork_type,
2729 /* If there are any conflicts - sharing violation. */
2730 if ((access_mask & FILE_READ_DATA) &&
2731 netatalk_already_open_with_deny_read) {
2732 return NT_STATUS_SHARING_VIOLATION;
2735 if (!share_for_read &&
2736 netatalk_already_open_for_reading) {
2737 return NT_STATUS_SHARING_VIOLATION;
2740 if ((access_mask & FILE_WRITE_DATA) &&
2741 netatalk_already_open_with_deny_write) {
2742 return NT_STATUS_SHARING_VIOLATION;
2745 if (!share_for_write &&
2746 netatalk_already_open_for_writing) {
2747 return NT_STATUS_SHARING_VIOLATION;
2750 if (!(access_mask & FILE_READ_DATA)) {
2752 * Nothing we can do here, we need read access
2755 return NT_STATUS_OK;
2758 /* Set NetAtalk locks matching our access */
2759 if (access_mask & FILE_READ_DATA) {
2760 struct byte_range_lock *br_lck = NULL;
2762 off = access_to_netatalk_brl(fork_type, FILE_READ_DATA);
2764 handle->conn->sconn->msg_ctx, fsp,
2765 fsp->op->global->open_persistent_id, 1, off,
2766 READ_LOCK, POSIX_LOCK, false,
2769 TALLOC_FREE(br_lck);
2771 if (!NT_STATUS_IS_OK(status)) {
2776 if (!share_for_read) {
2777 struct byte_range_lock *br_lck = NULL;
2779 off = denymode_to_netatalk_brl(fork_type, DENY_READ);
2781 handle->conn->sconn->msg_ctx, fsp,
2782 fsp->op->global->open_persistent_id, 1, off,
2783 READ_LOCK, POSIX_LOCK, false,
2786 TALLOC_FREE(br_lck);
2788 if (!NT_STATUS_IS_OK(status)) {
2793 if (access_mask & FILE_WRITE_DATA) {
2794 struct byte_range_lock *br_lck = NULL;
2796 off = access_to_netatalk_brl(fork_type, FILE_WRITE_DATA);
2798 handle->conn->sconn->msg_ctx, fsp,
2799 fsp->op->global->open_persistent_id, 1, off,
2800 READ_LOCK, POSIX_LOCK, false,
2803 TALLOC_FREE(br_lck);
2805 if (!NT_STATUS_IS_OK(status)) {
2810 if (!share_for_write) {
2811 struct byte_range_lock *br_lck = NULL;
2813 off = denymode_to_netatalk_brl(fork_type, DENY_WRITE);
2815 handle->conn->sconn->msg_ctx, fsp,
2816 fsp->op->global->open_persistent_id, 1, off,
2817 READ_LOCK, POSIX_LOCK, false,
2820 TALLOC_FREE(br_lck);
2822 if (!NT_STATUS_IS_OK(status)) {
2827 return NT_STATUS_OK;
2830 static NTSTATUS check_aapl(vfs_handle_struct *handle,
2831 struct smb_request *req,
2832 const struct smb2_create_blobs *in_context_blobs,
2833 struct smb2_create_blobs *out_context_blobs)
2835 struct fruit_config_data *config;
2837 struct smb2_create_blob *aapl = NULL;
2841 DATA_BLOB blob = data_blob_talloc(req, NULL, 0);
2842 uint64_t req_bitmap, client_caps;
2843 uint64_t server_caps = SMB2_CRTCTX_AAPL_UNIX_BASED;
2847 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
2848 return NT_STATUS_UNSUCCESSFUL);
2850 if (!config->use_aapl
2851 || in_context_blobs == NULL
2852 || out_context_blobs == NULL) {
2853 return NT_STATUS_OK;
2856 aapl = smb2_create_blob_find(in_context_blobs,
2857 SMB2_CREATE_TAG_AAPL);
2859 return NT_STATUS_OK;
2862 if (aapl->data.length != 24) {
2863 DEBUG(1, ("unexpected AAPL ctxt length: %ju\n",
2864 (uintmax_t)aapl->data.length));
2865 return NT_STATUS_INVALID_PARAMETER;
2868 cmd = IVAL(aapl->data.data, 0);
2869 if (cmd != SMB2_CRTCTX_AAPL_SERVER_QUERY) {
2870 DEBUG(1, ("unsupported AAPL cmd: %d\n", cmd));
2871 return NT_STATUS_INVALID_PARAMETER;
2874 req_bitmap = BVAL(aapl->data.data, 8);
2875 client_caps = BVAL(aapl->data.data, 16);
2877 SIVAL(p, 0, SMB2_CRTCTX_AAPL_SERVER_QUERY);
2879 SBVAL(p, 8, req_bitmap);
2880 ok = data_blob_append(req, &blob, p, 16);
2882 return NT_STATUS_UNSUCCESSFUL;
2885 if (req_bitmap & SMB2_CRTCTX_AAPL_SERVER_CAPS) {
2886 if ((client_caps & SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR) &&
2887 (handle->conn->tcon->compat->fs_capabilities & FILE_NAMED_STREAMS)) {
2888 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR;
2889 config->readdir_attr_enabled = true;
2892 if (config->use_copyfile) {
2893 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_OSX_COPYFILE;
2894 config->copyfile_enabled = true;
2898 * The client doesn't set the flag, so we can't check
2899 * for it and just set it unconditionally
2901 if (config->unix_info_enabled) {
2902 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_NFS_ACE;
2905 SBVAL(p, 0, server_caps);
2906 ok = data_blob_append(req, &blob, p, 8);
2908 return NT_STATUS_UNSUCCESSFUL;
2912 if (req_bitmap & SMB2_CRTCTX_AAPL_VOLUME_CAPS) {
2913 int val = lp_case_sensitive(SNUM(handle->conn->tcon->compat));
2921 caps |= SMB2_CRTCTX_AAPL_CASE_SENSITIVE;
2928 if (config->time_machine) {
2929 caps |= SMB2_CRTCTX_AAPL_FULL_SYNC;
2934 ok = data_blob_append(req, &blob, p, 8);
2936 return NT_STATUS_UNSUCCESSFUL;
2940 if (req_bitmap & SMB2_CRTCTX_AAPL_MODEL_INFO) {
2941 ok = convert_string_talloc(req,
2942 CH_UNIX, CH_UTF16LE,
2943 config->model, strlen(config->model),
2946 return NT_STATUS_UNSUCCESSFUL;
2950 SIVAL(p + 4, 0, modellen);
2951 ok = data_blob_append(req, &blob, p, 8);
2954 return NT_STATUS_UNSUCCESSFUL;
2957 ok = data_blob_append(req, &blob, model, modellen);
2960 return NT_STATUS_UNSUCCESSFUL;
2964 status = smb2_create_blob_add(out_context_blobs,
2966 SMB2_CREATE_TAG_AAPL,
2968 if (NT_STATUS_IS_OK(status)) {
2969 global_fruit_config.nego_aapl = true;
2970 if (config->aapl_zero_file_id) {
2971 aapl_force_zero_file_id(handle->conn->sconn);
2978 static bool readdir_attr_meta_finderi_stream(
2979 struct vfs_handle_struct *handle,
2980 const struct smb_filename *smb_fname,
2983 struct smb_filename *stream_name = NULL;
2984 files_struct *fsp = NULL;
2989 uint8_t buf[AFP_INFO_SIZE];
2991 stream_name = synthetic_smb_fname(talloc_tos(),
2992 smb_fname->base_name,
2993 AFPINFO_STREAM_NAME,
2994 NULL, smb_fname->flags);
2995 if (stream_name == NULL) {
2999 ret = SMB_VFS_STAT(handle->conn, stream_name);
3004 status = SMB_VFS_CREATE_FILE(
3005 handle->conn, /* conn */
3007 0, /* root_dir_fid */
3008 stream_name, /* fname */
3009 FILE_READ_DATA, /* access_mask */
3010 (FILE_SHARE_READ | FILE_SHARE_WRITE | /* share_access */
3012 FILE_OPEN, /* create_disposition*/
3013 0, /* create_options */
3014 0, /* file_attributes */
3015 INTERNAL_OPEN_ONLY, /* oplock_request */
3017 0, /* allocation_size */
3018 0, /* private_flags */
3023 NULL, NULL); /* create context */
3025 TALLOC_FREE(stream_name);
3027 if (!NT_STATUS_IS_OK(status)) {
3031 nread = SMB_VFS_PREAD(fsp, &buf[0], AFP_INFO_SIZE, 0);
3032 if (nread != AFP_INFO_SIZE) {
3033 DBG_ERR("short read [%s] [%zd/%d]\n",
3034 smb_fname_str_dbg(stream_name), nread, AFP_INFO_SIZE);
3039 memcpy(&ai->afpi_FinderInfo[0], &buf[AFP_OFF_FinderInfo],
3046 close_file(NULL, fsp, NORMAL_CLOSE);
3052 static bool readdir_attr_meta_finderi_netatalk(
3053 struct vfs_handle_struct *handle,
3054 const struct smb_filename *smb_fname,
3057 struct adouble *ad = NULL;
3060 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
3065 p = ad_get_entry(ad, ADEID_FINDERI);
3067 DBG_ERR("No ADEID_FINDERI for [%s]\n", smb_fname->base_name);
3072 memcpy(&ai->afpi_FinderInfo[0], p, AFP_FinderSize);
3077 static bool readdir_attr_meta_finderi(struct vfs_handle_struct *handle,
3078 const struct smb_filename *smb_fname,
3079 struct readdir_attr_data *attr_data)
3081 struct fruit_config_data *config = NULL;
3082 uint32_t date_added;
3086 SMB_VFS_HANDLE_GET_DATA(handle, config,
3087 struct fruit_config_data,
3090 switch (config->meta) {
3091 case FRUIT_META_NETATALK:
3092 ok = readdir_attr_meta_finderi_netatalk(
3093 handle, smb_fname, &ai);
3096 case FRUIT_META_STREAM:
3097 ok = readdir_attr_meta_finderi_stream(
3098 handle, smb_fname, &ai);
3102 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3107 /* Don't bother with errors, it's likely ENOENT */
3111 if (S_ISREG(smb_fname->st.st_ex_mode)) {
3113 memcpy(&attr_data->attr_data.aapl.finder_info[0],
3114 &ai.afpi_FinderInfo[0], 4);
3116 /* finder_creator */
3117 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 4,
3118 &ai.afpi_FinderInfo[4], 4);
3122 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 8,
3123 &ai.afpi_FinderInfo[8], 2);
3125 /* finder_ext_flags */
3126 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 10,
3127 &ai.afpi_FinderInfo[24], 2);
3130 date_added = convert_time_t_to_uint32_t(
3131 smb_fname->st.st_ex_btime.tv_sec - AD_DATE_DELTA);
3133 RSIVAL(&attr_data->attr_data.aapl.finder_info[0], 12, date_added);
3138 static uint64_t readdir_attr_rfork_size_adouble(
3139 struct vfs_handle_struct *handle,
3140 const struct smb_filename *smb_fname)
3142 struct adouble *ad = NULL;
3143 uint64_t rfork_size;
3145 ad = ad_get(talloc_tos(), handle, smb_fname,
3151 rfork_size = ad_getentrylen(ad, ADEID_RFORK);
3157 static uint64_t readdir_attr_rfork_size_stream(
3158 struct vfs_handle_struct *handle,
3159 const struct smb_filename *smb_fname)
3161 struct smb_filename *stream_name = NULL;
3163 uint64_t rfork_size;
3165 stream_name = synthetic_smb_fname(talloc_tos(),
3166 smb_fname->base_name,
3167 AFPRESOURCE_STREAM_NAME,
3169 if (stream_name == NULL) {
3173 ret = SMB_VFS_STAT(handle->conn, stream_name);
3175 TALLOC_FREE(stream_name);
3179 rfork_size = stream_name->st.st_ex_size;
3180 TALLOC_FREE(stream_name);
3185 static uint64_t readdir_attr_rfork_size(struct vfs_handle_struct *handle,
3186 const struct smb_filename *smb_fname)
3188 struct fruit_config_data *config = NULL;
3189 uint64_t rfork_size;
3191 SMB_VFS_HANDLE_GET_DATA(handle, config,
3192 struct fruit_config_data,
3195 switch (config->rsrc) {
3196 case FRUIT_RSRC_ADFILE:
3197 rfork_size = readdir_attr_rfork_size_adouble(handle,
3201 case FRUIT_RSRC_XATTR:
3202 case FRUIT_RSRC_STREAM:
3203 rfork_size = readdir_attr_rfork_size_stream(handle,
3208 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3216 static NTSTATUS readdir_attr_macmeta(struct vfs_handle_struct *handle,
3217 const struct smb_filename *smb_fname,
3218 struct readdir_attr_data *attr_data)
3220 NTSTATUS status = NT_STATUS_OK;
3221 struct fruit_config_data *config = NULL;
3224 SMB_VFS_HANDLE_GET_DATA(handle, config,
3225 struct fruit_config_data,
3226 return NT_STATUS_UNSUCCESSFUL);
3229 /* Ensure we return a default value in the creation_date field */
3230 RSIVAL(&attr_data->attr_data.aapl.finder_info, 12, AD_DATE_START);
3233 * Resource fork length
3236 if (config->readdir_attr_rsize) {
3237 uint64_t rfork_size;
3239 rfork_size = readdir_attr_rfork_size(handle, smb_fname);
3240 attr_data->attr_data.aapl.rfork_size = rfork_size;
3247 if (config->readdir_attr_finder_info) {
3248 ok = readdir_attr_meta_finderi(handle, smb_fname, attr_data);
3250 status = NT_STATUS_INTERNAL_ERROR;
3257 static NTSTATUS remove_virtual_nfs_aces(struct security_descriptor *psd)
3262 if (psd->dacl == NULL) {
3263 return NT_STATUS_OK;
3266 for (i = 0; i < psd->dacl->num_aces; i++) {
3267 /* MS NFS style mode/uid/gid */
3268 int cmp = dom_sid_compare_domain(
3269 &global_sid_Unix_NFS,
3270 &psd->dacl->aces[i].trustee);
3272 /* Normal ACE entry. */
3277 * security_descriptor_dacl_del()
3278 * *must* return NT_STATUS_OK as we know
3279 * we have something to remove.
3282 status = security_descriptor_dacl_del(psd,
3283 &psd->dacl->aces[i].trustee);
3284 if (!NT_STATUS_IS_OK(status)) {
3285 DBG_WARNING("failed to remove MS NFS style ACE: %s\n",
3291 * security_descriptor_dacl_del() may delete more
3292 * then one entry subsequent to this one if the
3293 * SID matches, but we only need to ensure that
3294 * we stay looking at the same element in the array.
3298 return NT_STATUS_OK;
3301 /* Search MS NFS style ACE with UNIX mode */
3302 static NTSTATUS check_ms_nfs(vfs_handle_struct *handle,
3304 struct security_descriptor *psd,
3309 struct fruit_config_data *config = NULL;
3313 SMB_VFS_HANDLE_GET_DATA(handle, config,
3314 struct fruit_config_data,
3315 return NT_STATUS_UNSUCCESSFUL);
3317 if (!global_fruit_config.nego_aapl) {
3318 return NT_STATUS_OK;
3320 if (psd->dacl == NULL || !config->unix_info_enabled) {
3321 return NT_STATUS_OK;
3324 for (i = 0; i < psd->dacl->num_aces; i++) {
3325 if (dom_sid_compare_domain(
3326 &global_sid_Unix_NFS_Mode,
3327 &psd->dacl->aces[i].trustee) == 0) {
3328 *pmode = (mode_t)psd->dacl->aces[i].trustee.sub_auths[2];
3329 *pmode &= (S_IRWXU | S_IRWXG | S_IRWXO);
3332 DEBUG(10, ("MS NFS chmod request %s, %04o\n",
3333 fsp_str_dbg(fsp), (unsigned)(*pmode)));
3339 * Remove any incoming virtual ACE entries generated by
3340 * fruit_fget_nt_acl().
3343 return remove_virtual_nfs_aces(psd);
3346 /****************************************************************************
3348 ****************************************************************************/
3350 static int fruit_connect(vfs_handle_struct *handle,
3351 const char *service,
3355 char *list = NULL, *newlist = NULL;
3356 struct fruit_config_data *config;
3358 DEBUG(10, ("fruit_connect\n"));
3360 rc = SMB_VFS_NEXT_CONNECT(handle, service, user);
3365 rc = init_fruit_config(handle);
3370 SMB_VFS_HANDLE_GET_DATA(handle, config,
3371 struct fruit_config_data, return -1);
3373 if (config->veto_appledouble) {
3374 list = lp_veto_files(talloc_tos(), SNUM(handle->conn));
3377 if (strstr(list, "/" ADOUBLE_NAME_PREFIX "*/") == NULL) {
3378 newlist = talloc_asprintf(
3380 "%s/" ADOUBLE_NAME_PREFIX "*/",
3382 lp_do_parameter(SNUM(handle->conn),
3387 lp_do_parameter(SNUM(handle->conn),
3389 "/" ADOUBLE_NAME_PREFIX "*/");
3395 if (config->encoding == FRUIT_ENC_NATIVE) {
3396 lp_do_parameter(SNUM(handle->conn),
3401 if (config->time_machine) {
3402 DBG_NOTICE("Enabling durable handles for Time Machine "
3403 "support on [%s]\n", service);
3404 lp_do_parameter(SNUM(handle->conn), "durable handles", "yes");
3405 lp_do_parameter(SNUM(handle->conn), "kernel oplocks", "no");
3406 lp_do_parameter(SNUM(handle->conn), "kernel share modes", "no");
3407 if (!lp_strict_sync(SNUM(handle->conn))) {
3408 DBG_WARNING("Time Machine without strict sync is not "
3411 lp_do_parameter(SNUM(handle->conn), "posix locking", "no");
3417 static int fruit_fake_fd(void)
3424 * Return a valid fd, but ensure any attempt to use it returns
3425 * an error (EPIPE). Once we get a write on the handle, we open
3428 ret = pipe(pipe_fds);
3438 static int fruit_open_meta_stream(vfs_handle_struct *handle,
3439 struct smb_filename *smb_fname,
3444 struct fruit_config_data *config = NULL;
3445 struct fio *fio = NULL;
3446 int open_flags = flags & ~O_CREAT;
3449 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3451 SMB_VFS_HANDLE_GET_DATA(handle, config,
3452 struct fruit_config_data, return -1);
3454 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3455 fio->type = ADOUBLE_META;
3456 fio->config = config;
3458 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, open_flags, mode);
3463 if (!(flags & O_CREAT)) {
3464 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
3468 fd = fruit_fake_fd();
3470 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
3474 fio->fake_fd = true;
3481 static int fruit_open_meta_netatalk(vfs_handle_struct *handle,
3482 struct smb_filename *smb_fname,
3487 struct fruit_config_data *config = NULL;
3488 struct fio *fio = NULL;
3489 struct adouble *ad = NULL;
3490 bool meta_exists = false;
3493 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3495 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
3502 if (!meta_exists && !(flags & O_CREAT)) {
3507 fd = fruit_fake_fd();
3512 SMB_VFS_HANDLE_GET_DATA(handle, config,
3513 struct fruit_config_data, return -1);
3515 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3516 fio->type = ADOUBLE_META;
3517 fio->config = config;
3518 fio->fake_fd = true;
3525 static int fruit_open_meta(vfs_handle_struct *handle,
3526 struct smb_filename *smb_fname,
3527 files_struct *fsp, int flags, mode_t mode)
3530 struct fruit_config_data *config = NULL;
3532 DBG_DEBUG("path [%s]\n", smb_fname_str_dbg(smb_fname));
3534 SMB_VFS_HANDLE_GET_DATA(handle, config,
3535 struct fruit_config_data, return -1);
3537 switch (config->meta) {
3538 case FRUIT_META_STREAM:
3539 fd = fruit_open_meta_stream(handle, smb_fname,
3543 case FRUIT_META_NETATALK:
3544 fd = fruit_open_meta_netatalk(handle, smb_fname,
3549 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3553 DBG_DEBUG("path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3558 static int fruit_open_rsrc_adouble(vfs_handle_struct *handle,
3559 struct smb_filename *smb_fname,
3565 struct adouble *ad = NULL;
3566 struct smb_filename *smb_fname_base = NULL;
3567 struct fruit_config_data *config = NULL;
3570 SMB_VFS_HANDLE_GET_DATA(handle, config,
3571 struct fruit_config_data, return -1);
3573 if ((!(flags & O_CREAT)) &&
3574 S_ISDIR(fsp->base_fsp->fsp_name->st.st_ex_mode))
3576 /* sorry, but directories don't habe a resource fork */
3581 rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_base);
3586 /* We always need read/write access for the metadata header too */
3587 flags &= ~(O_RDONLY | O_WRONLY);
3590 hostfd = SMB_VFS_NEXT_OPEN(handle, smb_fname_base, fsp,
3597 if (flags & (O_CREAT | O_TRUNC)) {
3598 ad = ad_init(fsp, ADOUBLE_RSRC);
3604 fsp->fh->fd = hostfd;
3606 rc = ad_fset(handle, ad, fsp);
3617 TALLOC_FREE(smb_fname_base);
3619 DEBUG(10, ("fruit_open resource fork: rc=%d, fd=%d\n", rc, hostfd));
3621 int saved_errno = errno;
3624 * BUGBUGBUG -- we would need to call
3625 * fd_close_posix here, but we don't have a
3628 fsp->fh->fd = hostfd;
3632 errno = saved_errno;
3637 static int fruit_open_rsrc_xattr(vfs_handle_struct *handle,
3638 struct smb_filename *smb_fname,
3643 #ifdef HAVE_ATTROPEN
3646 fd = attropen(smb_fname->base_name,
3647 AFPRESOURCE_EA_NETATALK,
3662 static int fruit_open_rsrc(vfs_handle_struct *handle,
3663 struct smb_filename *smb_fname,
3664 files_struct *fsp, int flags, mode_t mode)
3667 struct fruit_config_data *config = NULL;
3668 struct fio *fio = NULL;
3670 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3672 SMB_VFS_HANDLE_GET_DATA(handle, config,
3673 struct fruit_config_data, return -1);
3675 switch (config->rsrc) {
3676 case FRUIT_RSRC_STREAM:
3677 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3680 case FRUIT_RSRC_ADFILE:
3681 fd = fruit_open_rsrc_adouble(handle, smb_fname,
3685 case FRUIT_RSRC_XATTR:
3686 fd = fruit_open_rsrc_xattr(handle, smb_fname,
3691 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3695 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3701 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3702 fio->type = ADOUBLE_RSRC;
3703 fio->config = config;
3708 static int fruit_open(vfs_handle_struct *handle,
3709 struct smb_filename *smb_fname,
3710 files_struct *fsp, int flags, mode_t mode)
3714 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3716 if (!is_ntfs_stream_smb_fname(smb_fname)) {
3717 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3720 if (is_afpinfo_stream(smb_fname)) {
3721 fd = fruit_open_meta(handle, smb_fname, fsp, flags, mode);
3722 } else if (is_afpresource_stream(smb_fname)) {
3723 fd = fruit_open_rsrc(handle, smb_fname, fsp, flags, mode);
3725 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3728 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3733 static int fruit_close_meta(vfs_handle_struct *handle,
3737 struct fruit_config_data *config = NULL;
3739 SMB_VFS_HANDLE_GET_DATA(handle, config,
3740 struct fruit_config_data, return -1);
3742 switch (config->meta) {
3743 case FRUIT_META_STREAM:
3744 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3747 case FRUIT_META_NETATALK:
3748 ret = close(fsp->fh->fd);
3753 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3761 static int fruit_close_rsrc(vfs_handle_struct *handle,
3765 struct fruit_config_data *config = NULL;
3767 SMB_VFS_HANDLE_GET_DATA(handle, config,
3768 struct fruit_config_data, return -1);
3770 switch (config->rsrc) {
3771 case FRUIT_RSRC_STREAM:
3772 case FRUIT_RSRC_ADFILE:
3773 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3776 case FRUIT_RSRC_XATTR:
3777 ret = close(fsp->fh->fd);
3782 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3789 static int fruit_close(vfs_handle_struct *handle,
3797 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(fsp->fsp_name), fd);
3799 if (!is_ntfs_stream_smb_fname(fsp->fsp_name)) {
3800 return SMB_VFS_NEXT_CLOSE(handle, fsp);
3803 if (is_afpinfo_stream(fsp->fsp_name)) {
3804 ret = fruit_close_meta(handle, fsp);
3805 } else if (is_afpresource_stream(fsp->fsp_name)) {
3806 ret = fruit_close_rsrc(handle, fsp);
3808 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3814 static int fruit_rename(struct vfs_handle_struct *handle,
3815 const struct smb_filename *smb_fname_src,
3816 const struct smb_filename *smb_fname_dst)
3819 struct fruit_config_data *config = NULL;
3820 struct smb_filename *src_adp_smb_fname = NULL;
3821 struct smb_filename *dst_adp_smb_fname = NULL;
3823 SMB_VFS_HANDLE_GET_DATA(handle, config,
3824 struct fruit_config_data, return -1);
3826 if (!VALID_STAT(smb_fname_src->st)) {
3827 DBG_ERR("Need valid stat for [%s]\n",
3828 smb_fname_str_dbg(smb_fname_src));
3832 rc = SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
3837 if ((config->rsrc != FRUIT_RSRC_ADFILE) ||
3838 (!S_ISREG(smb_fname_src->st.st_ex_mode)))
3843 rc = adouble_path(talloc_tos(), smb_fname_src, &src_adp_smb_fname);
3848 rc = adouble_path(talloc_tos(), smb_fname_dst, &dst_adp_smb_fname);
3853 DBG_DEBUG("%s -> %s\n",
3854 smb_fname_str_dbg(src_adp_smb_fname),
3855 smb_fname_str_dbg(dst_adp_smb_fname));
3857 rc = SMB_VFS_NEXT_RENAME(handle, src_adp_smb_fname, dst_adp_smb_fname);
3858 if (errno == ENOENT) {
3863 TALLOC_FREE(src_adp_smb_fname);
3864 TALLOC_FREE(dst_adp_smb_fname);
3868 static int fruit_unlink_meta_stream(vfs_handle_struct *handle,
3869 const struct smb_filename *smb_fname)
3871 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3874 static int fruit_unlink_meta_netatalk(vfs_handle_struct *handle,
3875 const struct smb_filename *smb_fname)
3877 return SMB_VFS_REMOVEXATTR(handle->conn,
3879 AFPINFO_EA_NETATALK);
3882 static int fruit_unlink_meta(vfs_handle_struct *handle,
3883 const struct smb_filename *smb_fname)
3885 struct fruit_config_data *config = NULL;
3888 SMB_VFS_HANDLE_GET_DATA(handle, config,
3889 struct fruit_config_data, return -1);
3891 switch (config->meta) {
3892 case FRUIT_META_STREAM:
3893 rc = fruit_unlink_meta_stream(handle, smb_fname);
3896 case FRUIT_META_NETATALK:
3897 rc = fruit_unlink_meta_netatalk(handle, smb_fname);
3901 DBG_ERR("Unsupported meta config [%d]\n", config->meta);
3908 static int fruit_unlink_rsrc_stream(vfs_handle_struct *handle,
3909 const struct smb_filename *smb_fname,
3914 if (!force_unlink) {
3915 struct smb_filename *smb_fname_cp = NULL;
3918 smb_fname_cp = cp_smb_filename(talloc_tos(), smb_fname);
3919 if (smb_fname_cp == NULL) {
3924 * 0 byte resource fork streams are not listed by
3925 * vfs_streaminfo, as a result stream cleanup/deletion of file
3926 * deletion doesn't remove the resourcefork stream.
3929 ret = SMB_VFS_NEXT_STAT(handle, smb_fname_cp);
3931 TALLOC_FREE(smb_fname_cp);
3932 DBG_ERR("stat [%s] failed [%s]\n",
3933 smb_fname_str_dbg(smb_fname_cp), strerror(errno));
3937 size = smb_fname_cp->st.st_ex_size;
3938 TALLOC_FREE(smb_fname_cp);
3941 /* OS X ignores resource fork stream delete requests */
3946 ret = SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3947 if ((ret != 0) && (errno == ENOENT) && force_unlink) {
3954 static int fruit_unlink_rsrc_adouble(vfs_handle_struct *handle,
3955 const struct smb_filename *smb_fname,
3959 struct adouble *ad = NULL;
3960 struct smb_filename *adp_smb_fname = NULL;
3962 if (!force_unlink) {
3963 ad = ad_get(talloc_tos(), handle, smb_fname,
3972 * 0 byte resource fork streams are not listed by
3973 * vfs_streaminfo, as a result stream cleanup/deletion of file
3974 * deletion doesn't remove the resourcefork stream.
3977 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
3978 /* OS X ignores resource fork stream delete requests */
3986 rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
3991 rc = SMB_VFS_NEXT_UNLINK(handle, adp_smb_fname);
3992 TALLOC_FREE(adp_smb_fname);
3993 if ((rc != 0) && (errno == ENOENT) && force_unlink) {
4000 static int fruit_unlink_rsrc_xattr(vfs_handle_struct *handle,
4001 const struct smb_filename *smb_fname,
4005 * OS X ignores resource fork stream delete requests, so nothing to do
4006 * here. Removing the file will remove the xattr anyway, so we don't
4007 * have to take care of removing 0 byte resource forks that could be
4013 static int fruit_unlink_rsrc(vfs_handle_struct *handle,
4014 const struct smb_filename *smb_fname,
4017 struct fruit_config_data *config = NULL;
4020 SMB_VFS_HANDLE_GET_DATA(handle, config,
4021 struct fruit_config_data, return -1);
4023 switch (config->rsrc) {
4024 case FRUIT_RSRC_STREAM:
4025 rc = fruit_unlink_rsrc_stream(handle, smb_fname, force_unlink);
4028 case FRUIT_RSRC_ADFILE:
4029 rc = fruit_unlink_rsrc_adouble(handle, smb_fname, force_unlink);
4032 case FRUIT_RSRC_XATTR:
4033 rc = fruit_unlink_rsrc_xattr(handle, smb_fname, force_unlink);
4037 DBG_ERR("Unsupported rsrc config [%d]\n", config->rsrc);
4044 static int fruit_unlink(vfs_handle_struct *handle,
4045 const struct smb_filename *smb_fname)
4048 struct fruit_config_data *config = NULL;
4049 struct smb_filename *rsrc_smb_fname = NULL;
4051 SMB_VFS_HANDLE_GET_DATA(handle, config,
4052 struct fruit_config_data, return -1);
4054 if (is_afpinfo_stream(smb_fname)) {
4055 return fruit_unlink_meta(handle, smb_fname);
4056 } else if (is_afpresource_stream(smb_fname)) {
4057 return fruit_unlink_rsrc(handle, smb_fname, false);
4058 } else if (is_ntfs_stream_smb_fname(smb_fname)) {
4059 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4060 } else if (is_adouble_file(smb_fname->base_name)) {
4061 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4065 * A request to delete the base file. Because 0 byte resource
4066 * fork streams are not listed by fruit_streaminfo,
4067 * delete_all_streams() can't remove 0 byte resource fork
4068 * streams, so we have to cleanup this here.
4070 rsrc_smb_fname = synthetic_smb_fname(talloc_tos(),
4071 smb_fname->base_name,
4072 AFPRESOURCE_STREAM_NAME,
4075 if (rsrc_smb_fname == NULL) {
4079 rc = fruit_unlink_rsrc(handle, rsrc_smb_fname, true);
4080 if ((rc != 0) && (errno != ENOENT)) {
4081 DBG_ERR("Forced unlink of [%s] failed [%s]\n",
4082 smb_fname_str_dbg(rsrc_smb_fname), strerror(errno));
4083 TALLOC_FREE(rsrc_smb_fname);
4086 TALLOC_FREE(rsrc_smb_fname);
4088 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4091 static int fruit_chmod(vfs_handle_struct *handle,
4092 const struct smb_filename *smb_fname,
4096 struct fruit_config_data *config = NULL;
4097 struct smb_filename *smb_fname_adp = NULL;
4099 rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname, mode);
4104 SMB_VFS_HANDLE_GET_DATA(handle, config,
4105 struct fruit_config_data, return -1);
4107 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4111 if (!VALID_STAT(smb_fname->st)) {
4115 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
4119 rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_adp);
4124 DEBUG(10, ("fruit_chmod: %s\n", smb_fname_adp->base_name));
4126 rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname_adp, mode);
4127 if (errno == ENOENT) {
4131 TALLOC_FREE(smb_fname_adp);
4135 static int fruit_chown(vfs_handle_struct *handle,
4136 const struct smb_filename *smb_fname,
4141 struct fruit_config_data *config = NULL;
4142 struct smb_filename *adp_smb_fname = NULL;
4144 rc = SMB_VFS_NEXT_CHOWN(handle, smb_fname, uid, gid);
4149 SMB_VFS_HANDLE_GET_DATA(handle, config,
4150 struct fruit_config_data, return -1);
4152 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4156 if (!VALID_STAT(smb_fname->st)) {
4160 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
4164 rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
4169 DEBUG(10, ("fruit_chown: %s\n", adp_smb_fname->base_name));
4171 rc = SMB_VFS_NEXT_CHOWN(handle, adp_smb_fname, uid, gid);
4172 if (errno == ENOENT) {
4177 TALLOC_FREE(adp_smb_fname);
4181 static int fruit_rmdir(struct vfs_handle_struct *handle,
4182 const struct smb_filename *smb_fname)
4186 struct fruit_config_data *config;
4188 SMB_VFS_HANDLE_GET_DATA(handle, config,
4189 struct fruit_config_data, return -1);
4191 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4196 * Due to there is no way to change bDeleteVetoFiles variable
4197 * from this module, need to clean up ourselves
4200 dh = SMB_VFS_OPENDIR(handle->conn, smb_fname, NULL, 0);
4205 while ((de = SMB_VFS_READDIR(handle->conn, dh, NULL)) != NULL) {
4206 struct adouble *ad = NULL;
4208 struct smb_filename *ad_smb_fname = NULL;
4211 if (!is_adouble_file(de->d_name)) {
4215 p = talloc_asprintf(talloc_tos(), "%s/%s",
4216 smb_fname->base_name, de->d_name);
4218 DBG_ERR("talloc_asprintf failed\n");
4222 ad_smb_fname = synthetic_smb_fname(talloc_tos(), p,
4226 if (ad_smb_fname == NULL) {
4227 DBG_ERR("synthetic_smb_fname failed\n");
4232 * Check whether it's a valid AppleDouble file, if
4233 * yes, delete it, ignore it otherwise.
4235 ad = ad_get(talloc_tos(), handle, ad_smb_fname, ADOUBLE_RSRC);
4237 TALLOC_FREE(ad_smb_fname);
4243 ret = SMB_VFS_NEXT_UNLINK(handle, ad_smb_fname);
4245 DBG_ERR("Deleting [%s] failed\n",
4246 smb_fname_str_dbg(ad_smb_fname));
4248 TALLOC_FREE(ad_smb_fname);
4253 SMB_VFS_CLOSEDIR(handle->conn, dh);
4255 return SMB_VFS_NEXT_RMDIR(handle, smb_fname);
4258 static ssize_t fruit_pread_meta_stream(vfs_handle_struct *handle,
4259 files_struct *fsp, void *data,
4260 size_t n, off_t offset)
4265 nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4266 if (nread == -1 || nread == n) {
4270 DBG_ERR("Removing [%s] after short read [%zd]\n",
4271 fsp_str_dbg(fsp), nread);
4273 ret = SMB_VFS_NEXT_UNLINK(handle, fsp->fsp_name);
4275 DBG_ERR("Removing [%s] failed\n", fsp_str_dbg(fsp));
4283 static ssize_t fruit_pread_meta_adouble(vfs_handle_struct *handle,
4284 files_struct *fsp, void *data,
4285 size_t n, off_t offset)
4288 struct adouble *ad = NULL;
4289 char afpinfo_buf[AFP_INFO_SIZE];
4293 ai = afpinfo_new(talloc_tos());
4298 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
4304 p = ad_get_entry(ad, ADEID_FINDERI);
4306 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
4311 memcpy(&ai->afpi_FinderInfo[0], p, ADEDLEN_FINDERI);
4313 nread = afpinfo_pack(ai, afpinfo_buf);
4314 if (nread != AFP_INFO_SIZE) {
4319 memcpy(data, afpinfo_buf, n);
4327 static ssize_t fruit_pread_meta(vfs_handle_struct *handle,
4328 files_struct *fsp, void *data,
4329 size_t n, off_t offset)
4331 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4336 * OS X has a off-by-1 error in the offset calculation, so we're
4337 * bug compatible here. It won't hurt, as any relevant real
4338 * world read requests from the AFP_AfpInfo stream will be
4339 * offset=0 n=60. offset is ignored anyway, see below.
4341 if ((offset < 0) || (offset >= AFP_INFO_SIZE + 1)) {
4346 DBG_ERR("Failed to fetch fsp extension");
4350 /* Yes, macOS always reads from offset 0 */
4352 to_return = MIN(n, AFP_INFO_SIZE);
4354 switch (fio->config->meta) {
4355 case FRUIT_META_STREAM:
4356 nread = fruit_pread_meta_stream(handle, fsp, data,
4360 case FRUIT_META_NETATALK:
4361 nread = fruit_pread_meta_adouble(handle, fsp, data,
4366 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
4370 if (nread == -1 && fio->created) {
4372 char afpinfo_buf[AFP_INFO_SIZE];
4374 ai = afpinfo_new(talloc_tos());
4379 nread = afpinfo_pack(ai, afpinfo_buf);
4381 if (nread != AFP_INFO_SIZE) {
4385 memcpy(data, afpinfo_buf, to_return);
4392 static ssize_t fruit_pread_rsrc_stream(vfs_handle_struct *handle,
4393 files_struct *fsp, void *data,
4394 size_t n, off_t offset)
4396 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4399 static ssize_t fruit_pread_rsrc_xattr(vfs_handle_struct *handle,
4400 files_struct *fsp, void *data,
4401 size_t n, off_t offset)
4403 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4406 static ssize_t fruit_pread_rsrc_adouble(vfs_handle_struct *handle,
4407 files_struct *fsp, void *data,
4408 size_t n, off_t offset)
4410 struct adouble *ad = NULL;
4413 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
4418 nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n,
4419 offset + ad_getentryoff(ad, ADEID_RFORK));
4425 static ssize_t fruit_pread_rsrc(vfs_handle_struct *handle,
4426 files_struct *fsp, void *data,
4427 size_t n, off_t offset)
4429 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4437 switch (fio->config->rsrc) {
4438 case FRUIT_RSRC_STREAM:
4439 nread = fruit_pread_rsrc_stream(handle, fsp, data, n, offset);
4442 case FRUIT_RSRC_ADFILE:
4443 nread = fruit_pread_rsrc_adouble(handle, fsp, data, n, offset);
4446 case FRUIT_RSRC_XATTR:
4447 nread = fruit_pread_rsrc_xattr(handle, fsp, data, n, offset);
4451 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
4458 static ssize_t fruit_pread(vfs_handle_struct *handle,
4459 files_struct *fsp, void *data,
4460 size_t n, off_t offset)
4462 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4465 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4466 fsp_str_dbg(fsp), (intmax_t)offset, n);
4469 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4472 if (fio->type == ADOUBLE_META) {
4473 nread = fruit_pread_meta(handle, fsp, data, n, offset);
4475 nread = fruit_pread_rsrc(handle, fsp, data, n, offset);
4478 DBG_DEBUG("Path [%s] nread [%zd]\n", fsp_str_dbg(fsp), nread);
4482 static bool fruit_must_handle_aio_stream(struct fio *fio)
4488 if (fio->type == ADOUBLE_META) {
4492 if ((fio->type == ADOUBLE_RSRC) &&
4493 (fio->config->rsrc == FRUIT_RSRC_ADFILE))
4501 struct fruit_pread_state {
4503 struct vfs_aio_state vfs_aio_state;
4506 static void fruit_pread_done(struct tevent_req *subreq);
4508 static struct tevent_req *fruit_pread_send(
4509 struct vfs_handle_struct *handle,
4510 TALLOC_CTX *mem_ctx,
4511 struct tevent_context *ev,
4512 struct files_struct *fsp,
4514 size_t n, off_t offset)
4516 struct tevent_req *req = NULL;
4517 struct tevent_req *subreq = NULL;
4518 struct fruit_pread_state *state = NULL;
4519 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4521 req = tevent_req_create(mem_ctx, &state,
4522 struct fruit_pread_state);
4527 if (fruit_must_handle_aio_stream(fio)) {
4528 state->nread = SMB_VFS_PREAD(fsp, data, n, offset);
4529 if (state->nread != n) {
4530 if (state->nread != -1) {
4533 tevent_req_error(req, errno);
4534 return tevent_req_post(req, ev);
4536 tevent_req_done(req);
4537 return tevent_req_post(req, ev);
4540 subreq = SMB_VFS_NEXT_PREAD_SEND(state, ev, handle, fsp,
4542 if (tevent_req_nomem(req, subreq)) {
4543 return tevent_req_post(req, ev);
4545 tevent_req_set_callback(subreq, fruit_pread_done, req);
4549 static void fruit_pread_done(struct tevent_req *subreq)
4551 struct tevent_req *req = tevent_req_callback_data(
4552 subreq, struct tevent_req);
4553 struct fruit_pread_state *state = tevent_req_data(
4554 req, struct fruit_pread_state);
4556 state->nread = SMB_VFS_PREAD_RECV(subreq, &state->vfs_aio_state);
4557 TALLOC_FREE(subreq);
4559 if (tevent_req_error(req, state->vfs_aio_state.error)) {
4562 tevent_req_done(req);
4565 static ssize_t fruit_pread_recv(struct tevent_req *req,
4566 struct vfs_aio_state *vfs_aio_state)
4568 struct fruit_pread_state *state = tevent_req_data(
4569 req, struct fruit_pread_state);
4571 if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
4575 *vfs_aio_state = state->vfs_aio_state;
4576 return state->nread;
4579 static ssize_t fruit_pwrite_meta_stream(vfs_handle_struct *handle,
4580 files_struct *fsp, const void *data,
4581 size_t n, off_t offset)
4583 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4589 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4590 fsp_str_dbg(fsp), (intmax_t)offset, n);
4599 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
4601 DBG_ERR("Close [%s] failed: %s\n",
4602 fsp_str_dbg(fsp), strerror(errno));
4607 fd = SMB_VFS_NEXT_OPEN(handle,
4613 DBG_ERR("On-demand create [%s] in write failed: %s\n",
4614 fsp_str_dbg(fsp), strerror(errno));
4618 fio->fake_fd = false;
4621 ai = afpinfo_unpack(talloc_tos(), data);
4626 if (ai_empty_finderinfo(ai)) {
4628 * Writing an all 0 blob to the metadata stream results in the
4629 * stream being removed on a macOS server. This ensures we
4630 * behave the same and it verified by the "delete AFP_AfpInfo by
4631 * writing all 0" test.
4633 ret = SMB_VFS_NEXT_FTRUNCATE(handle, fsp, 0);
4635 DBG_ERR("SMB_VFS_NEXT_FTRUNCATE on [%s] failed\n",
4640 ok = set_delete_on_close(
4643 handle->conn->session_info->security_token,
4644 handle->conn->session_info->unix_token);
4646 DBG_ERR("set_delete_on_close on [%s] failed\n",
4653 nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4654 if (nwritten != n) {
4661 static ssize_t fruit_pwrite_meta_netatalk(vfs_handle_struct *handle,
4662 files_struct *fsp, const void *data,
4663 size_t n, off_t offset)
4665 struct adouble *ad = NULL;
4671 ai = afpinfo_unpack(talloc_tos(), data);
4676 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
4678 ad = ad_init(talloc_tos(), ADOUBLE_META);
4683 p = ad_get_entry(ad, ADEID_FINDERI);
4685 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
4690 memcpy(p, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
4692 ret = ad_fset(handle, ad, fsp);
4694 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
4701 if (!ai_empty_finderinfo(ai)) {
4706 * Writing an all 0 blob to the metadata stream results in the stream
4707 * being removed on a macOS server. This ensures we behave the same and
4708 * it verified by the "delete AFP_AfpInfo by writing all 0" test.
4711 ok = set_delete_on_close(
4714 handle->conn->session_info->security_token,
4715 handle->conn->session_info->unix_token);
4717 DBG_ERR("set_delete_on_close on [%s] failed\n",
4725 static ssize_t fruit_pwrite_meta(vfs_handle_struct *handle,
4726 files_struct *fsp, const void *data,
4727 size_t n, off_t offset)
4729 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4731 uint8_t buf[AFP_INFO_SIZE];
4737 DBG_ERR("Failed to fetch fsp extension");
4746 if (offset != 0 && n < 60) {
4751 cmp = memcmp(data, "AFP", 3);
4757 if (n <= AFP_OFF_FinderInfo) {
4759 * Nothing to do here really, just return
4767 if (to_copy > AFP_INFO_SIZE) {
4768 to_copy = AFP_INFO_SIZE;
4770 memcpy(buf, data, to_copy);
4773 if (to_write != AFP_INFO_SIZE) {
4774 to_write = AFP_INFO_SIZE;
4777 switch (fio->config->meta) {
4778 case FRUIT_META_STREAM:
4779 nwritten = fruit_pwrite_meta_stream(handle,
4786 case FRUIT_META_NETATALK:
4787 nwritten = fruit_pwrite_meta_netatalk(handle,
4795 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
4799 if (nwritten != to_write) {
4804 * Return the requested amount, verified against macOS SMB server
4809 static ssize_t fruit_pwrite_rsrc_stream(vfs_handle_struct *handle,
4810 files_struct *fsp, const void *data,
4811 size_t n, off_t offset)
4813 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4816 static ssize_t fruit_pwrite_rsrc_xattr(vfs_handle_struct *handle,
4817 files_struct *fsp, const void *data,
4818 size_t n, off_t offset)
4820 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4823 static ssize_t fruit_pwrite_rsrc_adouble(vfs_handle_struct *handle,
4824 files_struct *fsp, const void *data,
4825 size_t n, off_t offset)
4827 struct adouble *ad = NULL;
4831 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
4833 DBG_ERR("ad_get [%s] failed\n", fsp_str_dbg(fsp));
4837 nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n,
4838 offset + ad_getentryoff(ad, ADEID_RFORK));
4839 if (nwritten != n) {
4840 DBG_ERR("Short write on [%s] [%zd/%zd]\n",
4841 fsp_str_dbg(fsp), nwritten, n);
4846 if ((n + offset) > ad_getentrylen(ad, ADEID_RFORK)) {
4847 ad_setentrylen(ad, ADEID_RFORK, n + offset);
4848 ret = ad_fset(handle, ad, fsp);
4850 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
4860 static ssize_t fruit_pwrite_rsrc(vfs_handle_struct *handle,
4861 files_struct *fsp, const void *data,
4862 size_t n, off_t offset)
4864 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4868 DBG_ERR("Failed to fetch fsp extension");
4872 switch (fio->config->rsrc) {
4873 case FRUIT_RSRC_STREAM:
4874 nwritten = fruit_pwrite_rsrc_stream(handle, fsp, data, n, offset);
4877 case FRUIT_RSRC_ADFILE:
4878 nwritten = fruit_pwrite_rsrc_adouble(handle, fsp, data, n, offset);
4881 case FRUIT_RSRC_XATTR:
4882 nwritten = fruit_pwrite_rsrc_xattr(handle, fsp, data, n, offset);
4886 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
4893 static ssize_t fruit_pwrite(vfs_handle_struct *handle,
4894 files_struct *fsp, const void *data,
4895 size_t n, off_t offset)
4897 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4900 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4901 fsp_str_dbg(fsp), (intmax_t)offset, n);
4904 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4907 if (fio->type == ADOUBLE_META) {
4908 nwritten = fruit_pwrite_meta(handle, fsp, data, n, offset);
4910 nwritten = fruit_pwrite_rsrc(handle, fsp, data, n, offset);
4913 DBG_DEBUG("Path [%s] nwritten=%zd\n", fsp_str_dbg(fsp), nwritten);
4917 struct fruit_pwrite_state {
4919 struct vfs_aio_state vfs_aio_state;
4922 static void fruit_pwrite_done(struct tevent_req *subreq);
4924 static struct tevent_req *fruit_pwrite_send(
4925 struct vfs_handle_struct *handle,
4926 TALLOC_CTX *mem_ctx,
4927 struct tevent_context *ev,
4928 struct files_struct *fsp,
4930 size_t n, off_t offset)
4932 struct tevent_req *req = NULL;
4933 struct tevent_req *subreq = NULL;
4934 struct fruit_pwrite_state *state = NULL;
4935 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4937 req = tevent_req_create(mem_ctx, &state,
4938 struct fruit_pwrite_state);
4943 if (fruit_must_handle_aio_stream(fio)) {
4944 state->nwritten = SMB_VFS_PWRITE(fsp, data, n, offset);
4945 if (state->nwritten != n) {
4946 if (state->nwritten != -1) {
4949 tevent_req_error(req, errno);
4950 return tevent_req_post(req, ev);
4952 tevent_req_done(req);
4953 return tevent_req_post(req, ev);
4956 subreq = SMB_VFS_NEXT_PWRITE_SEND(state, ev, handle, fsp,
4958 if (tevent_req_nomem(req, subreq)) {
4959 return tevent_req_post(req, ev);
4961 tevent_req_set_callback(subreq, fruit_pwrite_done, req);
4965 static void fruit_pwrite_done(struct tevent_req *subreq)
4967 struct tevent_req *req = tevent_req_callback_data(
4968 subreq, struct tevent_req);
4969 struct fruit_pwrite_state *state = tevent_req_data(
4970 req, struct fruit_pwrite_state);
4972 state->nwritten = SMB_VFS_PWRITE_RECV(subreq, &state->vfs_aio_state);
4973 TALLOC_FREE(subreq);
4975 if (tevent_req_error(req, state->vfs_aio_state.error)) {
4978 tevent_req_done(req);
4981 static ssize_t fruit_pwrite_recv(struct tevent_req *req,
4982 struct vfs_aio_state *vfs_aio_state)
4984 struct fruit_pwrite_state *state = tevent_req_data(
4985 req, struct fruit_pwrite_state);
4987 if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
4991 *vfs_aio_state = state->vfs_aio_state;
4992 return state->nwritten;
4996 * Helper to stat/lstat the base file of an smb_fname.
4998 static int fruit_stat_base(vfs_handle_struct *handle,
4999 struct smb_filename *smb_fname,
5002 char *tmp_stream_name;
5005 tmp_stream_name = smb_fname->stream_name;
5006 smb_fname->stream_name = NULL;
5008 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
5010 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5012 smb_fname->stream_name = tmp_stream_name;
5014 DBG_DEBUG("fruit_stat_base [%s] dev [%ju] ino [%ju]\n",
5015 smb_fname->base_name,
5016 (uintmax_t)smb_fname->st.st_ex_dev,
5017 (uintmax_t)smb_fname->st.st_ex_ino);
5021 static int fruit_stat_meta_stream(vfs_handle_struct *handle,
5022 struct smb_filename *smb_fname,
5028 ret = fruit_stat_base(handle, smb_fname, false);
5033 ino = fruit_inode(&smb_fname->st, smb_fname->stream_name);
5036 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
5038 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5041 smb_fname->st.st_ex_ino = ino;
5046 static int fruit_stat_meta_netatalk(vfs_handle_struct *handle,
5047 struct smb_filename *smb_fname,
5050 struct adouble *ad = NULL;
5052 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5054 DBG_INFO("fruit_stat_meta %s: %s\n",
5055 smb_fname_str_dbg(smb_fname), strerror(errno));
5061 /* Populate the stat struct with info from the base file. */
5062 if (fruit_stat_base(handle, smb_fname, follow_links) == -1) {
5065 smb_fname->st.st_ex_size = AFP_INFO_SIZE;
5066 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5067 smb_fname->stream_name);
5071 static int fruit_stat_meta(vfs_handle_struct *handle,
5072 struct smb_filename *smb_fname,
5075 struct fruit_config_data *config = NULL;
5078 SMB_VFS_HANDLE_GET_DATA(handle, config,
5079 struct fruit_config_data, return -1);
5081 switch (config->meta) {
5082 case FRUIT_META_STREAM:
5083 ret = fruit_stat_meta_stream(handle, smb_fname, follow_links);
5086 case FRUIT_META_NETATALK:
5087 ret = fruit_stat_meta_netatalk(handle, smb_fname, follow_links);
5091 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
5098 static int fruit_stat_rsrc_netatalk(vfs_handle_struct *handle,
5099 struct smb_filename *smb_fname,
5102 struct adouble *ad = NULL;
5105 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
5111 /* Populate the stat struct with info from the base file. */
5112 ret = fruit_stat_base(handle, smb_fname, follow_links);
5118 smb_fname->st.st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
5119 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5120 smb_fname->stream_name);
5125 static int fruit_stat_rsrc_stream(vfs_handle_struct *handle,
5126 struct smb_filename *smb_fname,
5132 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
5134 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5140 static int fruit_stat_rsrc_xattr(vfs_handle_struct *handle,
5141 struct smb_filename *smb_fname,
5144 #ifdef HAVE_ATTROPEN
5148 /* Populate the stat struct with info from the base file. */
5149 ret = fruit_stat_base(handle, smb_fname, follow_links);
5154 fd = attropen(smb_fname->base_name,
5155 AFPRESOURCE_EA_NETATALK,
5161 ret = sys_fstat(fd, &smb_fname->st, false);
5164 DBG_ERR("fstat [%s:%s] failed\n", smb_fname->base_name,
5165 AFPRESOURCE_EA_NETATALK);
5171 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5172 smb_fname->stream_name);
5182 static int fruit_stat_rsrc(vfs_handle_struct *handle,
5183 struct smb_filename *smb_fname,
5186 struct fruit_config_data *config = NULL;
5189 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
5191 SMB_VFS_HANDLE_GET_DATA(handle, config,
5192 struct fruit_config_data, return -1);
5194 switch (config->rsrc) {
5195 case FRUIT_RSRC_STREAM:
5196 ret = fruit_stat_rsrc_stream(handle, smb_fname, follow_links);
5199 case FRUIT_RSRC_XATTR:
5200 ret = fruit_stat_rsrc_xattr(handle, smb_fname, follow_links);
5203 case FRUIT_RSRC_ADFILE:
5204 ret = fruit_stat_rsrc_netatalk(handle, smb_fname, follow_links);
5208 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
5215 static int fruit_stat(vfs_handle_struct *handle,
5216 struct smb_filename *smb_fname)
5220 DEBUG(10, ("fruit_stat called for %s\n",
5221 smb_fname_str_dbg(smb_fname)));
5223 if (!is_ntfs_stream_smb_fname(smb_fname)
5224 || is_ntfs_default_stream_smb_fname(smb_fname)) {
5225 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
5227 update_btime(handle, smb_fname);
5233 * Note if lp_posix_paths() is true, we can never
5234 * get here as is_ntfs_stream_smb_fname() is
5235 * always false. So we never need worry about
5236 * not following links here.
5239 if (is_afpinfo_stream(smb_fname)) {
5240 rc = fruit_stat_meta(handle, smb_fname, true);
5241 } else if (is_afpresource_stream(smb_fname)) {
5242 rc = fruit_stat_rsrc(handle, smb_fname, true);
5244 return SMB_VFS_NEXT_STAT(handle, smb_fname);
5248 update_btime(handle, smb_fname);
5249 smb_fname->st.st_ex_mode &= ~S_IFMT;
5250 smb_fname->st.st_ex_mode |= S_IFREG;
5251 smb_fname->st.st_ex_blocks =
5252 smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
5257 static int fruit_lstat(vfs_handle_struct *handle,
5258 struct smb_filename *smb_fname)
5262 DEBUG(10, ("fruit_lstat called for %s\n",
5263 smb_fname_str_dbg(smb_fname)));
5265 if (!is_ntfs_stream_smb_fname(smb_fname)
5266 || is_ntfs_default_stream_smb_fname(smb_fname)) {
5267 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5269 update_btime(handle, smb_fname);
5274 if (is_afpinfo_stream(smb_fname)) {
5275 rc = fruit_stat_meta(handle, smb_fname, false);
5276 } else if (is_afpresource_stream(smb_fname)) {
5277 rc = fruit_stat_rsrc(handle, smb_fname, false);
5279 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5283 update_btime(handle, smb_fname);
5284 smb_fname->st.st_ex_mode &= ~S_IFMT;
5285 smb_fname->st.st_ex_mode |= S_IFREG;
5286 smb_fname->st.st_ex_blocks =
5287 smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
5292 static int fruit_fstat_meta_stream(vfs_handle_struct *handle,
5294 SMB_STRUCT_STAT *sbuf)
5296 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5297 struct smb_filename smb_fname;
5306 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5311 *sbuf = fsp->base_fsp->fsp_name->st;
5312 sbuf->st_ex_size = AFP_INFO_SIZE;
5313 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5317 smb_fname = (struct smb_filename) {
5318 .base_name = fsp->fsp_name->base_name,
5321 ret = fruit_stat_base(handle, &smb_fname, false);
5325 *sbuf = smb_fname.st;
5327 ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5329 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5334 sbuf->st_ex_ino = ino;
5338 static int fruit_fstat_meta_netatalk(vfs_handle_struct *handle,
5340 SMB_STRUCT_STAT *sbuf)
5344 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5349 *sbuf = fsp->base_fsp->fsp_name->st;
5350 sbuf->st_ex_size = AFP_INFO_SIZE;
5351 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5356 static int fruit_fstat_meta(vfs_handle_struct *handle,
5358 SMB_STRUCT_STAT *sbuf,
5363 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
5365 switch (fio->config->meta) {
5366 case FRUIT_META_STREAM:
5367 ret = fruit_fstat_meta_stream(handle, fsp, sbuf);
5370 case FRUIT_META_NETATALK:
5371 ret = fruit_fstat_meta_netatalk(handle, fsp, sbuf);
5375 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
5379 DBG_DEBUG("Path [%s] ret [%d]\n", fsp_str_dbg(fsp), ret);
5383 static int fruit_fstat_rsrc_xattr(vfs_handle_struct *handle,
5385 SMB_STRUCT_STAT *sbuf)
5387 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5390 static int fruit_fstat_rsrc_stream(vfs_handle_struct *handle,
5392 SMB_STRUCT_STAT *sbuf)
5394 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5397 static int fruit_fstat_rsrc_adouble(vfs_handle_struct *handle,
5399 SMB_STRUCT_STAT *sbuf)
5401 struct adouble *ad = NULL;
5404 /* Populate the stat struct with info from the base file. */
5405 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5410 ad = ad_get(talloc_tos(), handle,
5411 fsp->base_fsp->fsp_name,
5414 DBG_ERR("ad_get [%s] failed [%s]\n",
5415 fsp_str_dbg(fsp), strerror(errno));
5419 *sbuf = fsp->base_fsp->fsp_name->st;
5420 sbuf->st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
5421 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5427 static int fruit_fstat_rsrc(vfs_handle_struct *handle, files_struct *fsp,
5428 SMB_STRUCT_STAT *sbuf, struct fio *fio)
5432 switch (fio->config->rsrc) {
5433 case FRUIT_RSRC_STREAM:
5434 ret = fruit_fstat_rsrc_stream(handle, fsp, sbuf);
5437 case FRUIT_RSRC_ADFILE:
5438 ret = fruit_fstat_rsrc_adouble(handle, fsp, sbuf);
5441 case FRUIT_RSRC_XATTR:
5442 ret = fruit_fstat_rsrc_xattr(handle, fsp, sbuf);
5446 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
5453 static int fruit_fstat(vfs_handle_struct *handle, files_struct *fsp,
5454 SMB_STRUCT_STAT *sbuf)
5456 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5460 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5463 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
5465 if (fio->type == ADOUBLE_META) {
5466 rc = fruit_fstat_meta(handle, fsp, sbuf, fio);
5468 rc = fruit_fstat_rsrc(handle, fsp, sbuf, fio);
5472 sbuf->st_ex_mode &= ~S_IFMT;
5473 sbuf->st_ex_mode |= S_IFREG;
5474 sbuf->st_ex_blocks = sbuf->st_ex_size / STAT_ST_BLOCKSIZE + 1;
5477 DBG_DEBUG("Path [%s] rc [%d] size [%"PRIdMAX"]\n",
5478 fsp_str_dbg(fsp), rc, (intmax_t)sbuf->st_ex_size);
5482 static NTSTATUS delete_invalid_meta_stream(
5483 vfs_handle_struct *handle,
5484 const struct smb_filename *smb_fname,
5485 TALLOC_CTX *mem_ctx,
5486 unsigned int *pnum_streams,
5487 struct stream_struct **pstreams,
5490 struct smb_filename *sname = NULL;
5494 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams, AFPINFO_STREAM);
5496 return NT_STATUS_INTERNAL_ERROR;
5500 return NT_STATUS_OK;
5503 sname = synthetic_smb_fname(talloc_tos(),
5504 smb_fname->base_name,
5505 AFPINFO_STREAM_NAME,
5507 if (sname == NULL) {
5508 return NT_STATUS_NO_MEMORY;
5511 ret = SMB_VFS_NEXT_UNLINK(handle, sname);
5514 DBG_ERR("Removing [%s] failed\n", smb_fname_str_dbg(sname));
5515 return map_nt_error_from_unix(errno);
5518 return NT_STATUS_OK;
5521 static NTSTATUS fruit_streaminfo_meta_stream(
5522 vfs_handle_struct *handle,
5523 struct files_struct *fsp,
5524 const struct smb_filename *smb_fname,
5525 TALLOC_CTX *mem_ctx,
5526 unsigned int *pnum_streams,
5527 struct stream_struct **pstreams)
5529 struct stream_struct *stream = *pstreams;
5530 unsigned int num_streams = *pnum_streams;
5533 for (i = 0; i < num_streams; i++) {
5534 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
5539 if (i == num_streams) {
5540 return NT_STATUS_OK;
5543 if (stream[i].size != AFP_INFO_SIZE) {
5544 DBG_ERR("Removing invalid AFPINFO_STREAM size [%jd] from [%s]\n",
5545 (intmax_t)stream[i].size, smb_fname_str_dbg(smb_fname));
5547 return delete_invalid_meta_stream(handle,
5556 return NT_STATUS_OK;
5559 static NTSTATUS fruit_streaminfo_meta_netatalk(
5560 vfs_handle_struct *handle,
5561 struct files_struct *fsp,
5562 const struct smb_filename *smb_fname,
5563 TALLOC_CTX *mem_ctx,
5564 unsigned int *pnum_streams,
5565 struct stream_struct **pstreams)
5567 struct stream_struct *stream = *pstreams;
5568 unsigned int num_streams = *pnum_streams;
5569 struct adouble *ad = NULL;
5574 /* Remove the Netatalk xattr from the list */
5575 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5576 ":" NETATALK_META_XATTR ":$DATA");
5578 return NT_STATUS_NO_MEMORY;
5582 * Check if there's a AFPINFO_STREAM from the VFS streams
5583 * backend and if yes, remove it from the list
5585 for (i = 0; i < num_streams; i++) {
5586 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
5591 if (i < num_streams) {
5592 DBG_WARNING("Unexpected AFPINFO_STREAM on [%s]\n",
5593 smb_fname_str_dbg(smb_fname));
5595 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5598 return NT_STATUS_INTERNAL_ERROR;
5602 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5604 return NT_STATUS_OK;
5607 is_fi_empty = ad_empty_finderinfo(ad);
5611 return NT_STATUS_OK;
5614 ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
5615 AFPINFO_STREAM_NAME, AFP_INFO_SIZE,
5616 smb_roundup(handle->conn, AFP_INFO_SIZE));
5618 return NT_STATUS_NO_MEMORY;
5621 return NT_STATUS_OK;
5624 static NTSTATUS fruit_streaminfo_meta(vfs_handle_struct *handle,
5625 struct files_struct *fsp,
5626 const struct smb_filename *smb_fname,
5627 TALLOC_CTX *mem_ctx,
5628 unsigned int *pnum_streams,
5629 struct stream_struct **pstreams)
5631 struct fruit_config_data *config = NULL;
5634 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5635 return NT_STATUS_INTERNAL_ERROR);
5637 switch (config->meta) {
5638 case FRUIT_META_NETATALK:
5639 status = fruit_streaminfo_meta_netatalk(handle, fsp, smb_fname,
5640 mem_ctx, pnum_streams,
5644 case FRUIT_META_STREAM:
5645 status = fruit_streaminfo_meta_stream(handle, fsp, smb_fname,
5646 mem_ctx, pnum_streams,
5651 return NT_STATUS_INTERNAL_ERROR;
5657 static NTSTATUS fruit_streaminfo_rsrc_stream(
5658 vfs_handle_struct *handle,
5659 struct files_struct *fsp,
5660 const struct smb_filename *smb_fname,
5661 TALLOC_CTX *mem_ctx,
5662 unsigned int *pnum_streams,
5663 struct stream_struct **pstreams)
5667 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
5669 DBG_ERR("Filtering resource stream failed\n");
5670 return NT_STATUS_INTERNAL_ERROR;
5672 return NT_STATUS_OK;
5675 static NTSTATUS fruit_streaminfo_rsrc_xattr(
5676 vfs_handle_struct *handle,
5677 struct files_struct *fsp,
5678 const struct smb_filename *smb_fname,
5679 TALLOC_CTX *mem_ctx,
5680 unsigned int *pnum_streams,
5681 struct stream_struct **pstreams)
5685 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
5687 DBG_ERR("Filtering resource stream failed\n");
5688 return NT_STATUS_INTERNAL_ERROR;
5690 return NT_STATUS_OK;
5693 static NTSTATUS fruit_streaminfo_rsrc_adouble(
5694 vfs_handle_struct *handle,
5695 struct files_struct *fsp,
5696 const struct smb_filename *smb_fname,
5697 TALLOC_CTX *mem_ctx,
5698 unsigned int *pnum_streams,
5699 struct stream_struct **pstreams)
5701 struct stream_struct *stream = *pstreams;
5702 unsigned int num_streams = *pnum_streams;
5703 struct adouble *ad = NULL;
5709 * Check if there's a AFPRESOURCE_STREAM from the VFS streams backend
5710 * and if yes, remove it from the list
5712 for (i = 0; i < num_streams; i++) {
5713 if (strequal_m(stream[i].name, AFPRESOURCE_STREAM)) {
5718 if (i < num_streams) {
5719 DBG_WARNING("Unexpected AFPRESOURCE_STREAM on [%s]\n",
5720 smb_fname_str_dbg(smb_fname));
5722 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5723 AFPRESOURCE_STREAM);
5725 return NT_STATUS_INTERNAL_ERROR;
5729 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
5731 return NT_STATUS_OK;
5734 rlen = ad_getentrylen(ad, ADEID_RFORK);
5738 return NT_STATUS_OK;
5741 ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
5742 AFPRESOURCE_STREAM_NAME, rlen,
5743 smb_roundup(handle->conn, rlen));
5745 return NT_STATUS_NO_MEMORY;
5748 return NT_STATUS_OK;
5751 static NTSTATUS fruit_streaminfo_rsrc(vfs_handle_struct *handle,
5752 struct files_struct *fsp,
5753 const struct smb_filename *smb_fname,
5754 TALLOC_CTX *mem_ctx,
5755 unsigned int *pnum_streams,
5756 struct stream_struct **pstreams)
5758 struct fruit_config_data *config = NULL;
5761 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5762 return NT_STATUS_INTERNAL_ERROR);
5764 switch (config->rsrc) {
5765 case FRUIT_RSRC_STREAM:
5766 status = fruit_streaminfo_rsrc_stream(handle, fsp, smb_fname,
5767 mem_ctx, pnum_streams,
5771 case FRUIT_RSRC_XATTR:
5772 status = fruit_streaminfo_rsrc_xattr(handle, fsp, smb_fname,
5773 mem_ctx, pnum_streams,
5777 case FRUIT_RSRC_ADFILE:
5778 status = fruit_streaminfo_rsrc_adouble(handle, fsp, smb_fname,
5779 mem_ctx, pnum_streams,
5784 return NT_STATUS_INTERNAL_ERROR;
5790 static void fruit_filter_empty_streams(unsigned int *pnum_streams,
5791 struct stream_struct **pstreams)
5793 unsigned num_streams = *pnum_streams;
5794 struct stream_struct *streams = *pstreams;
5797 if (!global_fruit_config.nego_aapl) {
5801 while (i < num_streams) {
5802 struct smb_filename smb_fname = (struct smb_filename) {
5803 .stream_name = streams[i].name,
5806 if (is_ntfs_default_stream_smb_fname(&smb_fname)
5807 || streams[i].size > 0)
5813 streams[i] = streams[num_streams - 1];
5817 *pnum_streams = num_streams;
5820 static NTSTATUS fruit_streaminfo(vfs_handle_struct *handle,
5821 struct files_struct *fsp,
5822 const struct smb_filename *smb_fname,
5823 TALLOC_CTX *mem_ctx,
5824 unsigned int *pnum_streams,
5825 struct stream_struct **pstreams)
5827 struct fruit_config_data *config = NULL;
5830 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5831 return NT_STATUS_UNSUCCESSFUL);
5833 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
5835 status = SMB_VFS_NEXT_STREAMINFO(handle, fsp, smb_fname, mem_ctx,
5836 pnum_streams, pstreams);
5837 if (!NT_STATUS_IS_OK(status)) {
5841 fruit_filter_empty_streams(pnum_streams, pstreams);
5843 status = fruit_streaminfo_meta(handle, fsp, smb_fname,
5844 mem_ctx, pnum_streams, pstreams);
5845 if (!NT_STATUS_IS_OK(status)) {
5849 status = fruit_streaminfo_rsrc(handle, fsp, smb_fname,
5850 mem_ctx, pnum_streams, pstreams);
5851 if (!NT_STATUS_IS_OK(status)) {
5855 return NT_STATUS_OK;
5858 static int fruit_ntimes(vfs_handle_struct *handle,
5859 const struct smb_filename *smb_fname,
5860 struct smb_file_time *ft)
5863 struct adouble *ad = NULL;
5864 struct fruit_config_data *config = NULL;
5866 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5869 if ((config->meta != FRUIT_META_NETATALK) ||
5870 null_timespec(ft->create_time))
5872 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
5875 DEBUG(10,("set btime for %s to %s\n", smb_fname_str_dbg(smb_fname),
5876 time_to_asc(convert_timespec_to_time_t(ft->create_time))));
5878 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5883 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX,
5884 convert_time_t_to_uint32_t(ft->create_time.tv_sec));
5886 rc = ad_set(handle, ad, smb_fname);
5892 DEBUG(1, ("fruit_ntimes: %s\n", smb_fname_str_dbg(smb_fname)));
5895 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
5898 static int fruit_fallocate(struct vfs_handle_struct *handle,
5899 struct files_struct *fsp,
5904 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5907 return SMB_VFS_NEXT_FALLOCATE(handle, fsp, mode, offset, len);
5910 /* Let the pwrite code path handle it. */
5915 static int fruit_ftruncate_rsrc_xattr(struct vfs_handle_struct *handle,
5916 struct files_struct *fsp,
5919 #ifdef HAVE_ATTROPEN
5920 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
5925 static int fruit_ftruncate_rsrc_adouble(struct vfs_handle_struct *handle,
5926 struct files_struct *fsp,
5930 struct adouble *ad = NULL;
5933 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
5935 DBG_DEBUG("ad_get [%s] failed [%s]\n",
5936 fsp_str_dbg(fsp), strerror(errno));
5940 ad_off = ad_getentryoff(ad, ADEID_RFORK);
5942 rc = ftruncate(fsp->fh->fd, offset + ad_off);
5948 ad_setentrylen(ad, ADEID_RFORK, offset);
5950 rc = ad_fset(handle, ad, fsp);
5952 DBG_ERR("ad_fset [%s] failed [%s]\n",
5953 fsp_str_dbg(fsp), strerror(errno));
5962 static int fruit_ftruncate_rsrc_stream(struct vfs_handle_struct *handle,
5963 struct files_struct *fsp,
5966 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
5969 static int fruit_ftruncate_rsrc(struct vfs_handle_struct *handle,
5970 struct files_struct *fsp,
5973 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5977 DBG_ERR("Failed to fetch fsp extension");
5981 switch (fio->config->rsrc) {
5982 case FRUIT_RSRC_XATTR:
5983 ret = fruit_ftruncate_rsrc_xattr(handle, fsp, offset);
5986 case FRUIT_RSRC_ADFILE:
5987 ret = fruit_ftruncate_rsrc_adouble(handle, fsp, offset);
5990 case FRUIT_RSRC_STREAM:
5991 ret = fruit_ftruncate_rsrc_stream(handle, fsp, offset);
5995 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
6003 static int fruit_ftruncate_meta(struct vfs_handle_struct *handle,
6004 struct files_struct *fsp,
6008 DBG_WARNING("ftruncate %s to %jd",
6009 fsp_str_dbg(fsp), (intmax_t)offset);
6010 /* OS X returns NT_STATUS_ALLOTTED_SPACE_EXCEEDED */
6015 /* OS X returns success but does nothing */
6016 DBG_INFO("ignoring ftruncate %s to %jd\n",
6017 fsp_str_dbg(fsp), (intmax_t)offset);
6021 static int fruit_ftruncate(struct vfs_handle_struct *handle,
6022 struct files_struct *fsp,
6025 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
6028 DBG_DEBUG("Path [%s] offset [%"PRIdMAX"]\n", fsp_str_dbg(fsp),
6032 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
6035 if (fio->type == ADOUBLE_META) {
6036 ret = fruit_ftruncate_meta(handle, fsp, offset);
6038 ret = fruit_ftruncate_rsrc(handle, fsp, offset);
6041 DBG_DEBUG("Path [%s] result [%d]\n", fsp_str_dbg(fsp), ret);
6045 static NTSTATUS fruit_create_file(vfs_handle_struct *handle,
6046 struct smb_request *req,
6047 uint16_t root_dir_fid,
6048 struct smb_filename *smb_fname,
6049 uint32_t access_mask,
6050 uint32_t share_access,
6051 uint32_t create_disposition,
6052 uint32_t create_options,
6053 uint32_t file_attributes,
6054 uint32_t oplock_request,
6055 struct smb2_lease *lease,
6056 uint64_t allocation_size,
6057 uint32_t private_flags,
6058 struct security_descriptor *sd,
6059 struct ea_list *ea_list,
6060 files_struct **result,
6062 const struct smb2_create_blobs *in_context_blobs,
6063 struct smb2_create_blobs *out_context_blobs)
6066 struct fruit_config_data *config = NULL;
6067 files_struct *fsp = NULL;
6068 struct fio *fio = NULL;
6069 bool internal_open = (oplock_request & INTERNAL_OPEN_ONLY);
6072 status = check_aapl(handle, req, in_context_blobs, out_context_blobs);
6073 if (!NT_STATUS_IS_OK(status)) {
6077 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
6078 return NT_STATUS_UNSUCCESSFUL);
6080 if (is_apple_stream(smb_fname) && !internal_open) {
6081 ret = ad_convert(handle, smb_fname);
6083 DBG_ERR("ad_convert() failed\n");
6084 return NT_STATUS_UNSUCCESSFUL;
6088 status = SMB_VFS_NEXT_CREATE_FILE(
6089 handle, req, root_dir_fid, smb_fname,
6090 access_mask, share_access,
6091 create_disposition, create_options,
6092 file_attributes, oplock_request,
6094 allocation_size, private_flags,
6095 sd, ea_list, result,
6096 pinfo, in_context_blobs, out_context_blobs);
6097 if (!NT_STATUS_IS_OK(status)) {
6103 if (global_fruit_config.nego_aapl) {
6104 if (config->posix_rename && fsp->is_directory) {
6106 * Enable POSIX directory rename behaviour
6108 fsp->posix_flags |= FSP_POSIX_FLAGS_RENAME;
6113 * If this is a plain open for existing files, opening an 0
6114 * byte size resource fork MUST fail with
6115 * NT_STATUS_OBJECT_NAME_NOT_FOUND.
6117 * Cf the vfs_fruit torture tests in test_rfork_create().
6119 if (global_fruit_config.nego_aapl &&
6120 create_disposition == FILE_OPEN &&
6121 smb_fname->st.st_ex_size == 0 &&
6122 is_ntfs_stream_smb_fname(smb_fname) &&
6123 !(is_ntfs_default_stream_smb_fname(smb_fname)))
6125 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
6129 fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
6130 if (fio != NULL && pinfo != NULL && *pinfo == FILE_WAS_CREATED) {
6131 fio->created = true;
6134 if (is_ntfs_stream_smb_fname(smb_fname)
6135 || fsp->is_directory) {
6139 if ((config->locking == FRUIT_LOCKING_NETATALK) &&
6142 status = fruit_check_access(
6146 if (!NT_STATUS_IS_OK(status)) {
6154 DEBUG(10, ("fruit_create_file: %s\n", nt_errstr(status)));
6157 close_file(req, fsp, ERROR_CLOSE);
6158 *result = fsp = NULL;
6164 static NTSTATUS fruit_readdir_attr(struct vfs_handle_struct *handle,
6165 const struct smb_filename *fname,
6166 TALLOC_CTX *mem_ctx,
6167 struct readdir_attr_data **pattr_data)
6169 struct fruit_config_data *config = NULL;
6170 struct readdir_attr_data *attr_data;
6174 SMB_VFS_HANDLE_GET_DATA(handle, config,
6175 struct fruit_config_data,
6176 return NT_STATUS_UNSUCCESSFUL);
6178 if (!global_fruit_config.nego_aapl) {
6179 return SMB_VFS_NEXT_READDIR_ATTR(handle, fname, mem_ctx, pattr_data);
6182 DEBUG(10, ("fruit_readdir_attr %s\n", fname->base_name));
6184 ret = ad_convert(handle, fname);
6186 DBG_ERR("ad_convert() failed\n");
6187 return NT_STATUS_UNSUCCESSFUL;
6190 *pattr_data = talloc_zero(mem_ctx, struct readdir_attr_data);
6191 if (*pattr_data == NULL) {
6192 return NT_STATUS_UNSUCCESSFUL;
6194 attr_data = *pattr_data;
6195 attr_data->type = RDATTR_AAPL;
6198 * Mac metadata: compressed FinderInfo, resource fork length
6201 status = readdir_attr_macmeta(handle, fname, attr_data);
6202 if (!NT_STATUS_IS_OK(status)) {
6204 * Error handling is tricky: if we return failure from
6205 * this function, the corresponding directory entry
6206 * will to be passed to the client, so we really just
6207 * want to error out on fatal errors.
6209 if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
6217 if (config->unix_info_enabled) {
6218 attr_data->attr_data.aapl.unix_mode = fname->st.st_ex_mode;
6224 if (!config->readdir_attr_max_access) {
6225 attr_data->attr_data.aapl.max_access = FILE_GENERIC_ALL;
6227 status = smbd_calculate_access_mask(
6231 SEC_FLAG_MAXIMUM_ALLOWED,
6232 &attr_data->attr_data.aapl.max_access);
6233 if (!NT_STATUS_IS_OK(status)) {
6238 return NT_STATUS_OK;
6241 DEBUG(1, ("fruit_readdir_attr %s, error: %s\n",
6242 fname->base_name, nt_errstr(status)));
6243 TALLOC_FREE(*pattr_data);
6247 static NTSTATUS fruit_fget_nt_acl(vfs_handle_struct *handle,
6249 uint32_t security_info,
6250 TALLOC_CTX *mem_ctx,
6251 struct security_descriptor **ppdesc)
6254 struct security_ace ace;
6256 struct fruit_config_data *config;
6258 SMB_VFS_HANDLE_GET_DATA(handle, config,
6259 struct fruit_config_data,
6260 return NT_STATUS_UNSUCCESSFUL);
6262 status = SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
6264 if (!NT_STATUS_IS_OK(status)) {
6269 * Add MS NFS style ACEs with uid, gid and mode
6271 if (!global_fruit_config.nego_aapl) {
6272 return NT_STATUS_OK;
6274 if (!config->unix_info_enabled) {
6275 return NT_STATUS_OK;
6278 /* First remove any existing ACE's with NFS style mode/uid/gid SIDs. */
6279 status = remove_virtual_nfs_aces(*ppdesc);
6280 if (!NT_STATUS_IS_OK(status)) {
6281 DBG_WARNING("failed to remove MS NFS style ACEs\n");
6285 /* MS NFS style mode */
6286 sid_compose(&sid, &global_sid_Unix_NFS_Mode, fsp->fsp_name->st.st_ex_mode);
6287 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6288 status = security_descriptor_dacl_add(*ppdesc, &ace);
6289 if (!NT_STATUS_IS_OK(status)) {
6290 DEBUG(1,("failed to add MS NFS style ACE\n"));
6294 /* MS NFS style uid */
6295 sid_compose(&sid, &global_sid_Unix_NFS_Users, fsp->fsp_name->st.st_ex_uid);
6296 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6297 status = security_descriptor_dacl_add(*ppdesc, &ace);
6298 if (!NT_STATUS_IS_OK(status)) {
6299 DEBUG(1,("failed to add MS NFS style ACE\n"));
6303 /* MS NFS style gid */
6304 sid_compose(&sid, &global_sid_Unix_NFS_Groups, fsp->fsp_name->st.st_ex_gid);
6305 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6306 status = security_descriptor_dacl_add(*ppdesc, &ace);
6307 if (!NT_STATUS_IS_OK(status)) {
6308 DEBUG(1,("failed to add MS NFS style ACE\n"));
6312 return NT_STATUS_OK;
6315 static NTSTATUS fruit_fset_nt_acl(vfs_handle_struct *handle,
6317 uint32_t security_info_sent,
6318 const struct security_descriptor *orig_psd)
6322 mode_t ms_nfs_mode = 0;
6324 struct security_descriptor *psd = NULL;
6325 uint32_t orig_num_aces = 0;
6327 if (orig_psd->dacl != NULL) {
6328 orig_num_aces = orig_psd->dacl->num_aces;
6331 psd = security_descriptor_copy(talloc_tos(), orig_psd);
6333 return NT_STATUS_NO_MEMORY;
6336 DBG_DEBUG("fruit_fset_nt_acl: %s\n", fsp_str_dbg(fsp));
6338 status = check_ms_nfs(handle, fsp, psd, &ms_nfs_mode, &do_chmod);
6339 if (!NT_STATUS_IS_OK(status)) {
6340 DEBUG(1, ("fruit_fset_nt_acl: check_ms_nfs failed%s\n", fsp_str_dbg(fsp)));
6346 * If only ms_nfs ACE entries were sent, ensure we set the DACL
6347 * sent/present flags correctly now we've removed them.
6350 if (orig_num_aces != 0) {
6352 * Are there any ACE's left ?
6354 if (psd->dacl->num_aces == 0) {
6355 /* No - clear the DACL sent/present flags. */
6356 security_info_sent &= ~SECINFO_DACL;
6357 psd->type &= ~SEC_DESC_DACL_PRESENT;
6361 status = SMB_VFS_NEXT_FSET_NT_ACL(handle, fsp, security_info_sent, psd);
6362 if (!NT_STATUS_IS_OK(status)) {
6363 DEBUG(1, ("fruit_fset_nt_acl: SMB_VFS_NEXT_FSET_NT_ACL failed%s\n", fsp_str_dbg(fsp)));
6369 if (fsp->fh->fd != -1) {
6370 result = SMB_VFS_FCHMOD(fsp, ms_nfs_mode);
6372 result = SMB_VFS_CHMOD(fsp->conn,
6378 DEBUG(1, ("chmod: %s, result: %d, %04o error %s\n", fsp_str_dbg(fsp),
6379 result, (unsigned)ms_nfs_mode,
6381 status = map_nt_error_from_unix(errno);
6388 return NT_STATUS_OK;
6391 static struct vfs_offload_ctx *fruit_offload_ctx;
6393 struct fruit_offload_read_state {
6394 struct vfs_handle_struct *handle;
6395 struct tevent_context *ev;
6401 static void fruit_offload_read_done(struct tevent_req *subreq);
6403 static struct tevent_req *fruit_offload_read_send(
6404 TALLOC_CTX *mem_ctx,
6405 struct tevent_context *ev,
6406 struct vfs_handle_struct *handle,
6413 struct tevent_req *req = NULL;
6414 struct tevent_req *subreq = NULL;
6415 struct fruit_offload_read_state *state = NULL;
6417 req = tevent_req_create(mem_ctx, &state,
6418 struct fruit_offload_read_state);
6422 *state = (struct fruit_offload_read_state) {
6429 subreq = SMB_VFS_NEXT_OFFLOAD_READ_SEND(mem_ctx, ev, handle, fsp,
6430 fsctl, ttl, offset, to_copy);
6431 if (tevent_req_nomem(subreq, req)) {
6432 return tevent_req_post(req, ev);
6434 tevent_req_set_callback(subreq, fruit_offload_read_done, req);
6438 static void fruit_offload_read_done(struct tevent_req *subreq)
6440 struct tevent_req *req = tevent_req_callback_data(
6441 subreq, struct tevent_req);
6442 struct fruit_offload_read_state *state = tevent_req_data(
6443 req, struct fruit_offload_read_state);
6446 status = SMB_VFS_NEXT_OFFLOAD_READ_RECV(subreq,
6450 TALLOC_FREE(subreq);
6451 if (tevent_req_nterror(req, status)) {
6455 if (state->fsctl != FSCTL_SRV_REQUEST_RESUME_KEY) {
6456 tevent_req_done(req);
6460 status = vfs_offload_token_ctx_init(state->fsp->conn->sconn->client,
6461 &fruit_offload_ctx);
6462 if (tevent_req_nterror(req, status)) {
6466 status = vfs_offload_token_db_store_fsp(fruit_offload_ctx,
6469 if (tevent_req_nterror(req, status)) {
6473 tevent_req_done(req);
6477 static NTSTATUS fruit_offload_read_recv(struct tevent_req *req,
6478 struct vfs_handle_struct *handle,
6479 TALLOC_CTX *mem_ctx,
6482 struct fruit_offload_read_state *state = tevent_req_data(
6483 req, struct fruit_offload_read_state);
6486 if (tevent_req_is_nterror(req, &status)) {
6487 tevent_req_received(req);
6491 token->length = state->token.length;
6492 token->data = talloc_move(mem_ctx, &state->token.data);
6494 tevent_req_received(req);
6495 return NT_STATUS_OK;
6498 struct fruit_offload_write_state {
6499 struct vfs_handle_struct *handle;
6501 struct files_struct *src_fsp;
6502 struct files_struct *dst_fsp;
6506 static void fruit_offload_write_done(struct tevent_req *subreq);
6507 static struct tevent_req *fruit_offload_write_send(struct vfs_handle_struct *handle,
6508 TALLOC_CTX *mem_ctx,
6509 struct tevent_context *ev,
6512 off_t transfer_offset,
6513 struct files_struct *dest_fsp,
6517 struct tevent_req *req, *subreq;
6518 struct fruit_offload_write_state *state;
6520 struct fruit_config_data *config;
6521 off_t src_off = transfer_offset;
6522 files_struct *src_fsp = NULL;
6523 off_t to_copy = num;
6524 bool copyfile_enabled = false;
6526 DEBUG(10,("soff: %ju, doff: %ju, len: %ju\n",
6527 (uintmax_t)src_off, (uintmax_t)dest_off, (uintmax_t)num));
6529 SMB_VFS_HANDLE_GET_DATA(handle, config,
6530 struct fruit_config_data,
6533 req = tevent_req_create(mem_ctx, &state,
6534 struct fruit_offload_write_state);
6538 state->handle = handle;
6539 state->dst_fsp = dest_fsp;
6542 case FSCTL_SRV_COPYCHUNK:
6543 case FSCTL_SRV_COPYCHUNK_WRITE:
6544 copyfile_enabled = config->copyfile_enabled;
6551 * Check if this a OS X copyfile style copychunk request with
6552 * a requested chunk count of 0 that was translated to a
6553 * offload_write_send VFS call overloading the parameters src_off
6554 * = dest_off = num = 0.
6556 if (copyfile_enabled && num == 0 && src_off == 0 && dest_off == 0) {
6557 status = vfs_offload_token_db_fetch_fsp(
6558 fruit_offload_ctx, token, &src_fsp);
6559 if (tevent_req_nterror(req, status)) {
6560 return tevent_req_post(req, ev);
6562 state->src_fsp = src_fsp;
6564 status = vfs_stat_fsp(src_fsp);
6565 if (tevent_req_nterror(req, status)) {
6566 return tevent_req_post(req, ev);
6569 to_copy = src_fsp->fsp_name->st.st_ex_size;
6570 state->is_copyfile = true;
6573 subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle,
6582 if (tevent_req_nomem(subreq, req)) {
6583 return tevent_req_post(req, ev);
6586 tevent_req_set_callback(subreq, fruit_offload_write_done, req);
6590 static void fruit_offload_write_done(struct tevent_req *subreq)
6592 struct tevent_req *req = tevent_req_callback_data(
6593 subreq, struct tevent_req);
6594 struct fruit_offload_write_state *state = tevent_req_data(
6595 req, struct fruit_offload_write_state);
6597 unsigned int num_streams = 0;
6598 struct stream_struct *streams = NULL;
6600 struct smb_filename *src_fname_tmp = NULL;
6601 struct smb_filename *dst_fname_tmp = NULL;
6603 status = SMB_VFS_NEXT_OFFLOAD_WRITE_RECV(state->handle,
6606 TALLOC_FREE(subreq);
6607 if (tevent_req_nterror(req, status)) {
6611 if (!state->is_copyfile) {
6612 tevent_req_done(req);
6617 * Now copy all remaining streams. We know the share supports
6618 * streams, because we're in vfs_fruit. We don't do this async
6619 * because streams are few and small.
6621 status = vfs_streaminfo(state->handle->conn, state->src_fsp,
6622 state->src_fsp->fsp_name,
6623 req, &num_streams, &streams);
6624 if (tevent_req_nterror(req, status)) {
6628 if (num_streams == 1) {
6629 /* There is always one stream, ::$DATA. */
6630 tevent_req_done(req);
6634 for (i = 0; i < num_streams; i++) {
6635 DEBUG(10, ("%s: stream: '%s'/%zu\n",
6636 __func__, streams[i].name, (size_t)streams[i].size));
6638 src_fname_tmp = synthetic_smb_fname(
6640 state->src_fsp->fsp_name->base_name,
6643 state->src_fsp->fsp_name->flags);
6644 if (tevent_req_nomem(src_fname_tmp, req)) {
6648 if (is_ntfs_default_stream_smb_fname(src_fname_tmp)) {
6649 TALLOC_FREE(src_fname_tmp);
6653 dst_fname_tmp = synthetic_smb_fname(
6655 state->dst_fsp->fsp_name->base_name,
6658 state->dst_fsp->fsp_name->flags);
6659 if (tevent_req_nomem(dst_fname_tmp, req)) {
6660 TALLOC_FREE(src_fname_tmp);
6664 status = copy_file(req,
6665 state->handle->conn,
6668 OPENX_FILE_CREATE_IF_NOT_EXIST,
6670 if (!NT_STATUS_IS_OK(status)) {
6671 DEBUG(1, ("%s: copy %s to %s failed: %s\n", __func__,
6672 smb_fname_str_dbg(src_fname_tmp),
6673 smb_fname_str_dbg(dst_fname_tmp),
6674 nt_errstr(status)));
6675 TALLOC_FREE(src_fname_tmp);
6676 TALLOC_FREE(dst_fname_tmp);
6677 tevent_req_nterror(req, status);
6681 TALLOC_FREE(src_fname_tmp);
6682 TALLOC_FREE(dst_fname_tmp);
6685 TALLOC_FREE(streams);
6686 TALLOC_FREE(src_fname_tmp);
6687 TALLOC_FREE(dst_fname_tmp);
6688 tevent_req_done(req);
6691 static NTSTATUS fruit_offload_write_recv(struct vfs_handle_struct *handle,
6692 struct tevent_req *req,
6695 struct fruit_offload_write_state *state = tevent_req_data(
6696 req, struct fruit_offload_write_state);
6699 if (tevent_req_is_nterror(req, &status)) {
6700 DEBUG(1, ("server side copy chunk failed: %s\n",
6701 nt_errstr(status)));
6703 tevent_req_received(req);
6707 *copied = state->copied;
6708 tevent_req_received(req);
6710 return NT_STATUS_OK;
6713 static char *fruit_get_bandsize_line(char **lines, int numlines)
6716 static bool re_initialized = false;
6720 if (!re_initialized) {
6721 ret = regcomp(&re, "^[[:blank:]]*<key>band-size</key>$", 0);
6725 re_initialized = true;
6728 for (i = 0; i < numlines; i++) {
6729 regmatch_t matches[1];
6731 ret = regexec(&re, lines[i], 1, matches, 0);
6734 * Check if the match was on the last line, sa we want
6735 * the subsequent line.
6737 if (i + 1 == numlines) {
6740 return lines[i + 1];
6742 if (ret != REG_NOMATCH) {
6750 static bool fruit_get_bandsize_from_line(char *line, size_t *_band_size)
6753 static bool re_initialized = false;
6754 regmatch_t matches[2];
6759 if (!re_initialized) {
6762 "<integer>\\([[:digit:]]*\\)</integer>$",
6767 re_initialized = true;
6770 ret = regexec(&re, line, 2, matches, 0);
6772 DBG_ERR("regex failed [%s]\n", line);
6776 line[matches[1].rm_eo] = '\0';
6778 ok = conv_str_u64(&line[matches[1].rm_so], &band_size);
6782 *_band_size = (size_t)band_size;
6787 * This reads and parses an Info.plist from a TM sparsebundle looking for the
6788 * "band-size" key and value.
6790 static bool fruit_get_bandsize(vfs_handle_struct *handle,
6794 #define INFO_PLIST_MAX_SIZE 64*1024
6796 struct smb_filename *smb_fname = NULL;
6797 files_struct *fsp = NULL;
6798 uint8_t *file_data = NULL;
6799 char **lines = NULL;
6800 char *band_size_line = NULL;
6801 size_t plist_file_size;
6808 plist = talloc_asprintf(talloc_tos(),
6810 handle->conn->connectpath,
6812 if (plist == NULL) {
6817 smb_fname = synthetic_smb_fname(talloc_tos(), plist, NULL, NULL, 0);
6818 if (smb_fname == NULL) {
6823 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
6825 DBG_INFO("Ignoring Sparsebundle without Info.plist [%s]\n", dir);
6830 plist_file_size = smb_fname->st.st_ex_size;
6832 if (plist_file_size > INFO_PLIST_MAX_SIZE) {
6833 DBG_INFO("%s is too large, ignoring\n", plist);
6838 status = SMB_VFS_NEXT_CREATE_FILE(
6841 0, /* root_dir_fid */
6842 smb_fname, /* fname */
6843 FILE_GENERIC_READ, /* access_mask */
6844 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
6845 FILE_OPEN, /* create_disposition */
6846 0, /* create_options */
6847 0, /* file_attributes */
6848 INTERNAL_OPEN_ONLY, /* oplock_request */
6850 0, /* allocation_size */
6851 0, /* private_flags */
6856 NULL, NULL); /* create context */
6857 if (!NT_STATUS_IS_OK(status)) {
6858 DBG_INFO("Opening [%s] failed [%s]\n",
6859 smb_fname_str_dbg(smb_fname), nt_errstr(status));
6864 file_data = talloc_array(talloc_tos(), uint8_t, plist_file_size);
6865 if (file_data == NULL) {
6870 nread = SMB_VFS_NEXT_PREAD(handle, fsp, file_data, plist_file_size, 0);
6871 if (nread != plist_file_size) {
6872 DBG_ERR("Short read on [%s]: %zu/%zd\n",
6873 fsp_str_dbg(fsp), nread, plist_file_size);
6879 status = close_file(NULL, fsp, NORMAL_CLOSE);
6881 if (!NT_STATUS_IS_OK(status)) {
6882 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
6887 lines = file_lines_parse((char *)file_data,
6891 if (lines == NULL) {
6896 band_size_line = fruit_get_bandsize_line(lines, numlines);
6897 if (band_size_line == NULL) {
6898 DBG_ERR("Didn't find band-size key in [%s]\n",
6899 smb_fname_str_dbg(smb_fname));
6904 ok = fruit_get_bandsize_from_line(band_size_line, band_size);
6906 DBG_ERR("fruit_get_bandsize_from_line failed\n");
6910 DBG_DEBUG("Parsed band-size [%zu] for [%s]\n", *band_size, plist);
6914 status = close_file(NULL, fsp, NORMAL_CLOSE);
6915 if (!NT_STATUS_IS_OK(status)) {
6916 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
6921 TALLOC_FREE(smb_fname);
6922 TALLOC_FREE(file_data);
6927 struct fruit_disk_free_state {
6931 static bool fruit_get_num_bands(vfs_handle_struct *handle,
6936 struct smb_filename *bands_dir = NULL;
6938 struct dirent *e = NULL;
6942 path = talloc_asprintf(talloc_tos(),
6944 handle->conn->connectpath,
6950 bands_dir = synthetic_smb_fname(talloc_tos(),
6956 if (bands_dir == NULL) {
6960 d = SMB_VFS_NEXT_OPENDIR(handle, bands_dir, NULL, 0);
6962 TALLOC_FREE(bands_dir);
6968 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
6970 e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
6972 if (ISDOT(e->d_name) || ISDOTDOT(e->d_name)) {
6978 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
6980 TALLOC_FREE(bands_dir);
6984 DBG_DEBUG("%zu bands in [%s]\n", nbands, smb_fname_str_dbg(bands_dir));
6986 TALLOC_FREE(bands_dir);
6992 static bool fruit_tmsize_do_dirent(vfs_handle_struct *handle,
6993 struct fruit_disk_free_state *state,
6998 size_t sparsebundle_strlen = strlen("sparsebundle");
6999 size_t bandsize = 0;
7003 p = strstr(e->d_name, "sparsebundle");
7008 if (p[sparsebundle_strlen] != '\0') {
7012 DBG_DEBUG("Processing sparsebundle [%s]\n", e->d_name);
7014 ok = fruit_get_bandsize(handle, e->d_name, &bandsize);
7017 * Beware of race conditions: this may be an uninitialized
7018 * Info.plist that a client is just creating. We don't want let
7019 * this to trigger complete failure.
7021 DBG_ERR("Processing sparsebundle [%s] failed\n", e->d_name);
7025 ok = fruit_get_num_bands(handle, e->d_name, &nbands);
7028 * Beware of race conditions: this may be a backup sparsebundle
7029 * in an early stage lacking a bands subdirectory. We don't want
7030 * let this to trigger complete failure.
7032 DBG_ERR("Processing sparsebundle [%s] failed\n", e->d_name);
7036 if (bandsize > SIZE_MAX/nbands) {
7037 DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
7041 tm_size = bandsize * nbands;
7043 if (state->total_size + tm_size < state->total_size) {
7044 DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
7049 state->total_size += tm_size;
7051 DBG_DEBUG("[%s] tm_size [%jd] total_size [%jd]\n",
7052 e->d_name, (intmax_t)tm_size, (intmax_t)state->total_size);
7058 * Calculate used size of a TimeMachine volume
7060 * This assumes that the volume is used only for TimeMachine.
7062 * - readdir(basedir of share), then
7063 * - for every element that matches regex "^\(.*\)\.sparsebundle$" :
7064 * - parse "\1.sparsebundle/Info.plist" and read the band-size XML key
7065 * - count band files in "\1.sparsebundle/bands/"
7066 * - calculate used size of all bands: band_count * band_size
7068 static uint64_t fruit_disk_free(vfs_handle_struct *handle,
7069 const struct smb_filename *smb_fname,
7074 struct fruit_config_data *config = NULL;
7075 struct fruit_disk_free_state state = {0};
7077 struct dirent *e = NULL;
7083 SMB_VFS_HANDLE_GET_DATA(handle, config,
7084 struct fruit_config_data,
7087 if (!config->time_machine ||
7088 config->time_machine_max_size == 0)
7090 return SMB_VFS_NEXT_DISK_FREE(handle,
7097 d = SMB_VFS_NEXT_OPENDIR(handle, smb_fname, NULL, 0);
7102 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
7104 e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
7106 ok = fruit_tmsize_do_dirent(handle, &state, e);
7108 SMB_VFS_NEXT_CLOSEDIR(handle, d);
7113 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
7118 dsize = config->time_machine_max_size / 512;
7119 dfree = dsize - (state.total_size / 512);
7120 if (dfree > dsize) {
7130 static struct vfs_fn_pointers vfs_fruit_fns = {
7131 .connect_fn = fruit_connect,
7132 .disk_free_fn = fruit_disk_free,
7134 /* File operations */
7135 .chmod_fn = fruit_chmod,
7136 .chown_fn = fruit_chown,
7137 .unlink_fn = fruit_unlink,
7138 .rename_fn = fruit_rename,
7139 .rmdir_fn = fruit_rmdir,
7140 .open_fn = fruit_open,
7141 .close_fn = fruit_close,
7142 .pread_fn = fruit_pread,
7143 .pwrite_fn = fruit_pwrite,
7144 .pread_send_fn = fruit_pread_send,
7145 .pread_recv_fn = fruit_pread_recv,
7146 .pwrite_send_fn = fruit_pwrite_send,
7147 .pwrite_recv_fn = fruit_pwrite_recv,
7148 .stat_fn = fruit_stat,
7149 .lstat_fn = fruit_lstat,
7150 .fstat_fn = fruit_fstat,
7151 .streaminfo_fn = fruit_streaminfo,
7152 .ntimes_fn = fruit_ntimes,
7153 .ftruncate_fn = fruit_ftruncate,
7154 .fallocate_fn = fruit_fallocate,
7155 .create_file_fn = fruit_create_file,
7156 .readdir_attr_fn = fruit_readdir_attr,
7157 .offload_read_send_fn = fruit_offload_read_send,
7158 .offload_read_recv_fn = fruit_offload_read_recv,
7159 .offload_write_send_fn = fruit_offload_write_send,
7160 .offload_write_recv_fn = fruit_offload_write_recv,
7162 /* NT ACL operations */
7163 .fget_nt_acl_fn = fruit_fget_nt_acl,
7164 .fset_nt_acl_fn = fruit_fset_nt_acl,
7168 NTSTATUS vfs_fruit_init(TALLOC_CTX *ctx)
7170 NTSTATUS ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "fruit",
7172 if (!NT_STATUS_IS_OK(ret)) {
7176 vfs_fruit_debug_level = debug_add_class("fruit");
7177 if (vfs_fruit_debug_level == -1) {
7178 vfs_fruit_debug_level = DBGC_VFS;
7179 DEBUG(0, ("%s: Couldn't register custom debugging class!\n",
7182 DEBUG(10, ("%s: Debug class number of '%s': %d\n",
7183 "vfs_fruit_init","fruit",vfs_fruit_debug_level));