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/tevent_ntstatus.h"
32 #include "lib/util/tevent_unix.h"
33 #include "offload_token.h"
34 #include "string_replace.h"
36 #include <gnutls/gnutls.h>
37 #include <gnutls/crypto.h>
40 * Enhanced OS X and Netatalk compatibility
41 * ========================================
43 * This modules takes advantage of vfs_streams_xattr and
44 * vfs_catia. VFS modules vfs_fruit and vfs_streams_xattr must be
45 * loaded in the correct order:
47 * vfs modules = catia fruit streams_xattr
49 * The module intercepts the OS X special streams "AFP_AfpInfo" and
50 * "AFP_Resource" and handles them in a special way. All other named
51 * streams are deferred to vfs_streams_xattr.
53 * The OS X client maps all NTFS illegal characters to the Unicode
54 * private range. This module optionally stores the charcters using
55 * their native ASCII encoding using vfs_catia. If you're not enabling
56 * this feature, you can skip catia from vfs modules.
58 * Finally, open modes are optionally checked against Netatalk AFP
61 * The "AFP_AfpInfo" named stream is a binary blob containing OS X
62 * extended metadata for files and directories. This module optionally
63 * reads and stores this metadata in a way compatible with Netatalk 3
64 * which stores the metadata in an EA "org.netatalk.metadata". Cf
65 * source3/include/MacExtensions.h for a description of the binary
68 * The "AFP_Resource" named stream may be arbitrarily large, thus it
69 * can't be stored in an xattr on most filesystem. ZFS on Solaris is
70 * the only available filesystem where xattrs can be of any size and
71 * the OS supports using the file APIs for xattrs.
73 * The AFP_Resource stream is stored in an AppleDouble file prepending
74 * "._" to the filename. On Solaris with ZFS the stream is optionally
75 * stored in an EA "org.netatalk.resource".
81 * The OS X SMB client sends xattrs as ADS too. For xattr interop with
82 * other protocols you may want to adjust the xattr names the VFS
83 * module vfs_streams_xattr uses for storing ADS's. This defaults to
84 * user.DosStream.ADS_NAME:$DATA and can be changed by specifying
85 * these module parameters:
87 * streams_xattr:prefix = user.
88 * streams_xattr:store_stream_type = false
94 * - log diagnostic if any needed VFS module is not loaded
95 * (eg with lp_vfs_objects())
99 static int vfs_fruit_debug_level = DBGC_VFS;
101 static struct global_fruit_config {
102 bool nego_aapl; /* client negotiated AAPL */
104 } global_fruit_config;
107 #define DBGC_CLASS vfs_fruit_debug_level
109 #define FRUIT_PARAM_TYPE_NAME "fruit"
110 #define ADOUBLE_NAME_PREFIX "._"
112 #define NETATALK_META_XATTR "org.netatalk.Metadata"
113 #define NETATALK_RSRC_XATTR "org.netatalk.ResourceFork"
115 #if defined(HAVE_ATTROPEN)
116 #define AFPINFO_EA_NETATALK NETATALK_META_XATTR
117 #define AFPRESOURCE_EA_NETATALK NETATALK_RSRC_XATTR
119 #define AFPINFO_EA_NETATALK "user." NETATALK_META_XATTR
120 #define AFPRESOURCE_EA_NETATALK "user." NETATALK_RSRC_XATTR
123 enum apple_fork {APPLE_FORK_DATA, APPLE_FORK_RSRC};
125 enum fruit_rsrc {FRUIT_RSRC_STREAM, FRUIT_RSRC_ADFILE, FRUIT_RSRC_XATTR};
126 enum fruit_meta {FRUIT_META_STREAM, FRUIT_META_NETATALK};
127 enum fruit_locking {FRUIT_LOCKING_NETATALK, FRUIT_LOCKING_NONE};
128 enum fruit_encoding {FRUIT_ENC_NATIVE, FRUIT_ENC_PRIVATE};
130 struct fruit_config_data {
131 enum fruit_rsrc rsrc;
132 enum fruit_meta meta;
133 enum fruit_locking locking;
134 enum fruit_encoding encoding;
135 bool use_aapl; /* config from smb.conf */
137 bool readdir_attr_enabled;
138 bool unix_info_enabled;
139 bool copyfile_enabled;
140 bool veto_appledouble;
142 bool aapl_zero_file_id;
145 off_t time_machine_max_size;
146 bool wipe_intentionally_left_blank_rfork;
147 bool delete_empty_adfiles;
150 * Additional options, all enabled by default,
151 * possibly useful for analyzing performance. The associated
152 * operations with each of them may be expensive, so having
153 * the chance to disable them individually gives a chance
154 * tweaking the setup for the particular usecase.
156 bool readdir_attr_rsize;
157 bool readdir_attr_finder_info;
158 bool readdir_attr_max_access;
161 static const struct enum_list fruit_rsrc[] = {
162 {FRUIT_RSRC_STREAM, "stream"}, /* pass on to vfs_streams_xattr */
163 {FRUIT_RSRC_ADFILE, "file"}, /* ._ AppleDouble file */
164 {FRUIT_RSRC_XATTR, "xattr"}, /* Netatalk compatible xattr (ZFS only) */
168 static const struct enum_list fruit_meta[] = {
169 {FRUIT_META_STREAM, "stream"}, /* pass on to vfs_streams_xattr */
170 {FRUIT_META_NETATALK, "netatalk"}, /* Netatalk compatible xattr */
174 static const struct enum_list fruit_locking[] = {
175 {FRUIT_LOCKING_NETATALK, "netatalk"}, /* synchronize locks with Netatalk */
176 {FRUIT_LOCKING_NONE, "none"},
180 static const struct enum_list fruit_encoding[] = {
181 {FRUIT_ENC_NATIVE, "native"}, /* map unicode private chars to ASCII */
182 {FRUIT_ENC_PRIVATE, "private"}, /* keep unicode private chars */
186 static const char *fruit_catia_maps =
187 "0x01:0xf001,0x02:0xf002,0x03:0xf003,0x04:0xf004,"
188 "0x05:0xf005,0x06:0xf006,0x07:0xf007,0x08:0xf008,"
189 "0x09:0xf009,0x0a:0xf00a,0x0b:0xf00b,0x0c:0xf00c,"
190 "0x0d:0xf00d,0x0e:0xf00e,0x0f:0xf00f,0x10:0xf010,"
191 "0x11:0xf011,0x12:0xf012,0x13:0xf013,0x14:0xf014,"
192 "0x15:0xf015,0x16:0xf016,0x17:0xf017,0x18:0xf018,"
193 "0x19:0xf019,0x1a:0xf01a,0x1b:0xf01b,0x1c:0xf01c,"
194 "0x1d:0xf01d,0x1e:0xf01e,0x1f:0xf01f,"
195 "0x22:0xf020,0x2a:0xf021,0x3a:0xf022,0x3c:0xf023,"
196 "0x3e:0xf024,0x3f:0xf025,0x5c:0xf026,0x7c:0xf027,"
199 /*****************************************************************************
200 * Defines, functions and data structures that deal with AppleDouble
201 *****************************************************************************/
204 * There are two AppleDouble blobs we deal with:
206 * - ADOUBLE_META - AppleDouble blob used by Netatalk for storing
207 * metadata in an xattr
209 * - ADOUBLE_RSRC - AppleDouble blob used by OS X and Netatalk in
212 typedef enum {ADOUBLE_META, ADOUBLE_RSRC} adouble_type_t;
215 #define AD_VERSION2 0x00020000
216 #define AD_VERSION AD_VERSION2
219 * AppleDouble entry IDs.
221 #define ADEID_DFORK 1
222 #define ADEID_RFORK 2
224 #define ADEID_COMMENT 4
225 #define ADEID_ICONBW 5
226 #define ADEID_ICONCOL 6
227 #define ADEID_FILEI 7
228 #define ADEID_FILEDATESI 8
229 #define ADEID_FINDERI 9
230 #define ADEID_MACFILEI 10
231 #define ADEID_PRODOSFILEI 11
232 #define ADEID_MSDOSFILEI 12
233 #define ADEID_SHORTNAME 13
234 #define ADEID_AFPFILEI 14
237 /* Private Netatalk entries */
238 #define ADEID_PRIVDEV 16
239 #define ADEID_PRIVINO 17
240 #define ADEID_PRIVSYN 18
241 #define ADEID_PRIVID 19
242 #define ADEID_MAX (ADEID_PRIVID + 1)
245 * These are the real ids for the private entries,
246 * as stored in the adouble file
248 #define AD_DEV 0x80444556
249 #define AD_INO 0x80494E4F
250 #define AD_SYN 0x8053594E
251 #define AD_ID 0x8053567E
253 /* Number of actually used entries */
254 #define ADEID_NUM_XATTR 8
255 #define ADEID_NUM_DOT_UND 2
256 #define ADEID_NUM_RSRC_XATTR 1
258 /* AppleDouble magic */
259 #define AD_APPLESINGLE_MAGIC 0x00051600
260 #define AD_APPLEDOUBLE_MAGIC 0x00051607
261 #define AD_MAGIC AD_APPLEDOUBLE_MAGIC
263 /* Sizes of relevant entry bits */
264 #define ADEDLEN_MAGIC 4
265 #define ADEDLEN_VERSION 4
266 #define ADEDLEN_FILLER 16
267 #define AD_FILLER_TAG "Netatalk " /* should be 16 bytes */
268 #define AD_FILLER_TAG_OSX "Mac OS X " /* should be 16 bytes */
269 #define ADEDLEN_NENTRIES 2
270 #define AD_HEADER_LEN (ADEDLEN_MAGIC + ADEDLEN_VERSION + \
271 ADEDLEN_FILLER + ADEDLEN_NENTRIES) /* 26 */
272 #define AD_ENTRY_LEN_EID 4
273 #define AD_ENTRY_LEN_OFF 4
274 #define AD_ENTRY_LEN_LEN 4
275 #define AD_ENTRY_LEN (AD_ENTRY_LEN_EID + AD_ENTRY_LEN_OFF + AD_ENTRY_LEN_LEN)
278 #define ADEDLEN_NAME 255
279 #define ADEDLEN_COMMENT 200
280 #define ADEDLEN_FILEI 16
281 #define ADEDLEN_FINDERI 32
282 #define ADEDLEN_FILEDATESI 16
283 #define ADEDLEN_SHORTNAME 12 /* length up to 8.3 */
284 #define ADEDLEN_AFPFILEI 4
285 #define ADEDLEN_MACFILEI 4
286 #define ADEDLEN_PRODOSFILEI 8
287 #define ADEDLEN_MSDOSFILEI 2
288 #define ADEDLEN_DID 4
289 #define ADEDLEN_PRIVDEV 8
290 #define ADEDLEN_PRIVINO 8
291 #define ADEDLEN_PRIVSYN 8
292 #define ADEDLEN_PRIVID 4
295 #define ADEDOFF_MAGIC 0
296 #define ADEDOFF_VERSION (ADEDOFF_MAGIC + ADEDLEN_MAGIC)
297 #define ADEDOFF_FILLER (ADEDOFF_VERSION + ADEDLEN_VERSION)
298 #define ADEDOFF_NENTRIES (ADEDOFF_FILLER + ADEDLEN_FILLER)
300 #define ADEDOFF_FINDERI_XATTR (AD_HEADER_LEN + \
301 (ADEID_NUM_XATTR * AD_ENTRY_LEN))
302 #define ADEDOFF_COMMENT_XATTR (ADEDOFF_FINDERI_XATTR + ADEDLEN_FINDERI)
303 #define ADEDOFF_FILEDATESI_XATTR (ADEDOFF_COMMENT_XATTR + ADEDLEN_COMMENT)
304 #define ADEDOFF_AFPFILEI_XATTR (ADEDOFF_FILEDATESI_XATTR + \
306 #define ADEDOFF_PRIVDEV_XATTR (ADEDOFF_AFPFILEI_XATTR + ADEDLEN_AFPFILEI)
307 #define ADEDOFF_PRIVINO_XATTR (ADEDOFF_PRIVDEV_XATTR + ADEDLEN_PRIVDEV)
308 #define ADEDOFF_PRIVSYN_XATTR (ADEDOFF_PRIVINO_XATTR + ADEDLEN_PRIVINO)
309 #define ADEDOFF_PRIVID_XATTR (ADEDOFF_PRIVSYN_XATTR + ADEDLEN_PRIVSYN)
311 #define ADEDOFF_FINDERI_DOT_UND (AD_HEADER_LEN + \
312 (ADEID_NUM_DOT_UND * AD_ENTRY_LEN))
313 #define ADEDOFF_RFORK_DOT_UND (ADEDOFF_FINDERI_DOT_UND + ADEDLEN_FINDERI)
315 #define AD_DATASZ_XATTR (AD_HEADER_LEN + \
316 (ADEID_NUM_XATTR * AD_ENTRY_LEN) + \
317 ADEDLEN_FINDERI + ADEDLEN_COMMENT + \
318 ADEDLEN_FILEDATESI + ADEDLEN_AFPFILEI + \
319 ADEDLEN_PRIVDEV + ADEDLEN_PRIVINO + \
320 ADEDLEN_PRIVSYN + ADEDLEN_PRIVID)
322 #if AD_DATASZ_XATTR != 402
323 #error bad size for AD_DATASZ_XATTR
326 #define AD_DATASZ_DOT_UND (AD_HEADER_LEN + \
327 (ADEID_NUM_DOT_UND * AD_ENTRY_LEN) + \
329 #if AD_DATASZ_DOT_UND != 82
330 #error bad size for AD_DATASZ_DOT_UND
334 * Sharemode locks fcntl() offsets
336 #if _FILE_OFFSET_BITS == 64 || defined(HAVE_LARGEFILE)
337 #define AD_FILELOCK_BASE (UINT64_C(0x7FFFFFFFFFFFFFFF) - 9)
339 #define AD_FILELOCK_BASE (UINT32_C(0x7FFFFFFF) - 9)
341 #define BYTELOCK_MAX (AD_FILELOCK_BASE - 1)
343 #define AD_FILELOCK_OPEN_WR (AD_FILELOCK_BASE + 0)
344 #define AD_FILELOCK_OPEN_RD (AD_FILELOCK_BASE + 1)
345 #define AD_FILELOCK_RSRC_OPEN_WR (AD_FILELOCK_BASE + 2)
346 #define AD_FILELOCK_RSRC_OPEN_RD (AD_FILELOCK_BASE + 3)
347 #define AD_FILELOCK_DENY_WR (AD_FILELOCK_BASE + 4)
348 #define AD_FILELOCK_DENY_RD (AD_FILELOCK_BASE + 5)
349 #define AD_FILELOCK_RSRC_DENY_WR (AD_FILELOCK_BASE + 6)
350 #define AD_FILELOCK_RSRC_DENY_RD (AD_FILELOCK_BASE + 7)
351 #define AD_FILELOCK_OPEN_NONE (AD_FILELOCK_BASE + 8)
352 #define AD_FILELOCK_RSRC_OPEN_NONE (AD_FILELOCK_BASE + 9)
354 /* Time stuff we overload the bits a little */
355 #define AD_DATE_CREATE 0
356 #define AD_DATE_MODIFY 4
357 #define AD_DATE_BACKUP 8
358 #define AD_DATE_ACCESS 12
359 #define AD_DATE_MASK (AD_DATE_CREATE | AD_DATE_MODIFY | \
360 AD_DATE_BACKUP | AD_DATE_ACCESS)
361 #define AD_DATE_UNIX (1 << 10)
362 #define AD_DATE_START 0x80000000
363 #define AD_DATE_DELTA 946684800
364 #define AD_DATE_FROM_UNIX(x) (htonl((x) - AD_DATE_DELTA))
365 #define AD_DATE_TO_UNIX(x) (ntohl(x) + AD_DATE_DELTA)
367 #define AD_XATTR_HDR_MAGIC 0x41545452 /* 'ATTR' */
368 #define AD_XATTR_MAX_ENTRIES 1024 /* Some arbitrarily enforced limit */
369 #define AD_XATTR_HDR_SIZE 36
370 #define AD_XATTR_MAX_HDR_SIZE 65536
372 /* Accessor macros */
373 #define ad_getentrylen(ad,eid) ((ad)->ad_eid[(eid)].ade_len)
374 #define ad_getentryoff(ad,eid) ((ad)->ad_eid[(eid)].ade_off)
375 #define ad_setentrylen(ad,eid,len) ((ad)->ad_eid[(eid)].ade_len = (len))
376 #define ad_setentryoff(ad,eid,off) ((ad)->ad_eid[(eid)].ade_off = (off))
379 * Both struct ad_xattr_header and struct ad_xattr_entry describe the in memory
380 * representation as well as the on-disk format.
382 * The ad_xattr_header follows the FinderInfo data in the FinderInfo entry if
383 * the length of the FinderInfo entry is larger then 32 bytes. It is then
384 * preceeded with 2 bytes padding.
386 * Cf: https://opensource.apple.com/source/xnu/xnu-4570.1.46/bsd/vfs/vfs_xattr.c
389 struct ad_xattr_header {
390 uint32_t adx_magic; /* ATTR_HDR_MAGIC */
391 uint32_t adx_debug_tag; /* for debugging == file id of owning file */
392 uint32_t adx_total_size; /* file offset of end of attribute header + entries + data */
393 uint32_t adx_data_start; /* file offset to attribute data area */
394 uint32_t adx_data_length; /* length of attribute data area */
395 uint32_t adx_reserved[3];
397 uint16_t adx_num_attrs;
400 /* On-disk entries are aligned on 4 byte boundaries */
401 struct ad_xattr_entry {
402 uint32_t adx_offset; /* file offset to data */
403 uint32_t adx_length; /* size of attribute data */
405 uint8_t adx_namelen; /* included the NULL terminator */
406 char *adx_name; /* NULL-terminated UTF-8 name */
415 files_struct *ad_fsp;
417 adouble_type_t ad_type;
420 uint8_t ad_filler[ADEDLEN_FILLER];
421 struct ad_entry ad_eid[ADEID_MAX];
423 struct ad_xattr_header adx_header;
424 struct ad_xattr_entry *adx_entries;
427 struct ad_entry_order {
428 uint32_t id, offset, len;
431 /* Netatalk AppleDouble metadata xattr */
433 struct ad_entry_order entry_order_meta_xattr[ADEID_NUM_XATTR + 1] = {
434 {ADEID_FINDERI, ADEDOFF_FINDERI_XATTR, ADEDLEN_FINDERI},
435 {ADEID_COMMENT, ADEDOFF_COMMENT_XATTR, 0},
436 {ADEID_FILEDATESI, ADEDOFF_FILEDATESI_XATTR, ADEDLEN_FILEDATESI},
437 {ADEID_AFPFILEI, ADEDOFF_AFPFILEI_XATTR, ADEDLEN_AFPFILEI},
438 {ADEID_PRIVDEV, ADEDOFF_PRIVDEV_XATTR, 0},
439 {ADEID_PRIVINO, ADEDOFF_PRIVINO_XATTR, 0},
440 {ADEID_PRIVSYN, ADEDOFF_PRIVSYN_XATTR, 0},
441 {ADEID_PRIVID, ADEDOFF_PRIVID_XATTR, 0},
445 /* AppleDouble resource fork file (the ones prefixed by "._") */
447 struct ad_entry_order entry_order_dot_und[ADEID_NUM_DOT_UND + 1] = {
448 {ADEID_FINDERI, ADEDOFF_FINDERI_DOT_UND, ADEDLEN_FINDERI},
449 {ADEID_RFORK, ADEDOFF_RFORK_DOT_UND, 0},
453 /* Conversion from enumerated id to on-disk AppleDouble id */
454 #define AD_EID_DISK(a) (set_eid[a])
455 static const uint32_t set_eid[] = {
456 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
457 AD_DEV, AD_INO, AD_SYN, AD_ID
460 static char empty_resourcefork[] = {
461 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
462 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
463 0x54, 0x68, 0x69, 0x73, 0x20, 0x72, 0x65, 0x73,
464 0x6F, 0x75, 0x72, 0x63, 0x65, 0x20, 0x66, 0x6F,
465 0x72, 0x6B, 0x20, 0x69, 0x6E, 0x74, 0x65, 0x6E,
466 0x74, 0x69, 0x6F, 0x6E, 0x61, 0x6C, 0x6C, 0x79,
467 0x20, 0x6C, 0x65, 0x66, 0x74, 0x20, 0x62, 0x6C,
468 0x61, 0x6E, 0x6B, 0x20, 0x20, 0x20, 0x00, 0x00,
469 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
494 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
495 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
496 0x00, 0x1C, 0x00, 0x1E, 0xFF, 0xFF
500 /* tcon config handle */
501 struct fruit_config_data *config;
503 /* Denote stream type, meta or rsrc */
506 /* Whether the create created the stream */
510 * AFP_AfpInfo stream created, but not written yet, thus still a fake
511 * pipe fd. This is set to true in fruit_open_meta if there was no
512 * exisiting stream but the caller requested O_CREAT. It is later set to
513 * false when we get a write on the stream that then does open and
522 * Forward declarations
524 static struct adouble *ad_init(TALLOC_CTX *ctx,
525 adouble_type_t type);
526 static struct adouble *ad_get(TALLOC_CTX *ctx,
527 vfs_handle_struct *handle,
528 const struct smb_filename *smb_fname,
529 adouble_type_t type);
530 static int ad_set(vfs_handle_struct *handle,
532 const struct smb_filename *smb_fname);
533 static int ad_fset(struct vfs_handle_struct *handle,
536 static int adouble_path(TALLOC_CTX *ctx,
537 const struct smb_filename *smb_fname__in,
538 struct smb_filename **ppsmb_fname_out);
539 static AfpInfo *afpinfo_new(TALLOC_CTX *ctx);
540 static ssize_t afpinfo_pack(const AfpInfo *ai, char *buf);
541 static AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data);
545 * Return a pointer to an AppleDouble entry
547 * Returns NULL if the entry is not present
549 static char *ad_get_entry(const struct adouble *ad, int eid)
551 off_t off = ad_getentryoff(ad, eid);
552 size_t len = ad_getentrylen(ad, eid);
554 if (off == 0 || len == 0) {
558 return ad->ad_data + off;
564 static int ad_getdate(const struct adouble *ad,
565 unsigned int dateoff,
568 bool xlate = (dateoff & AD_DATE_UNIX);
571 dateoff &= AD_DATE_MASK;
572 p = ad_get_entry(ad, ADEID_FILEDATESI);
577 if (dateoff > AD_DATE_ACCESS) {
581 memcpy(date, p + dateoff, sizeof(uint32_t));
584 *date = AD_DATE_TO_UNIX(*date);
592 static int ad_setdate(struct adouble *ad, unsigned int dateoff, uint32_t date)
594 bool xlate = (dateoff & AD_DATE_UNIX);
597 p = ad_get_entry(ad, ADEID_FILEDATESI);
602 dateoff &= AD_DATE_MASK;
604 date = AD_DATE_FROM_UNIX(date);
607 if (dateoff > AD_DATE_ACCESS) {
611 memcpy(p + dateoff, &date, sizeof(date));
618 * Map on-disk AppleDouble id to enumerated id
620 static uint32_t get_eid(uint32_t eid)
628 return ADEID_PRIVDEV;
630 return ADEID_PRIVINO;
632 return ADEID_PRIVSYN;
643 * Pack AppleDouble structure into data buffer
645 static bool ad_pack(struct adouble *ad)
652 bufsize = talloc_get_size(ad->ad_data);
653 if (bufsize < AD_DATASZ_DOT_UND) {
654 DBG_ERR("bad buffer size [0x%" PRIx32 "]\n", bufsize);
658 if (offset + ADEDLEN_MAGIC < offset ||
659 offset + ADEDLEN_MAGIC >= bufsize) {
662 RSIVAL(ad->ad_data, offset, ad->ad_magic);
663 offset += ADEDLEN_MAGIC;
665 if (offset + ADEDLEN_VERSION < offset ||
666 offset + ADEDLEN_VERSION >= bufsize) {
669 RSIVAL(ad->ad_data, offset, ad->ad_version);
670 offset += ADEDLEN_VERSION;
672 if (offset + ADEDLEN_FILLER < offset ||
673 offset + ADEDLEN_FILLER >= bufsize) {
676 if (ad->ad_type == ADOUBLE_RSRC) {
677 memcpy(ad->ad_data + offset, AD_FILLER_TAG, ADEDLEN_FILLER);
679 offset += ADEDLEN_FILLER;
681 if (offset + ADEDLEN_NENTRIES < offset ||
682 offset + ADEDLEN_NENTRIES >= bufsize) {
685 offset += ADEDLEN_NENTRIES;
687 for (eid = 0, nent = 0; eid < ADEID_MAX; eid++) {
688 if (ad->ad_eid[eid].ade_off == 0) {
690 * ade_off is also used as indicator whether a
691 * specific entry is used or not
696 if (offset + AD_ENTRY_LEN_EID < offset ||
697 offset + AD_ENTRY_LEN_EID >= bufsize) {
700 RSIVAL(ad->ad_data, offset, AD_EID_DISK(eid));
701 offset += AD_ENTRY_LEN_EID;
703 if (offset + AD_ENTRY_LEN_OFF < offset ||
704 offset + AD_ENTRY_LEN_OFF >= bufsize) {
707 RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_off);
708 offset += AD_ENTRY_LEN_OFF;
710 if (offset + AD_ENTRY_LEN_LEN < offset ||
711 offset + AD_ENTRY_LEN_LEN >= bufsize) {
714 RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_len);
715 offset += AD_ENTRY_LEN_LEN;
720 if (ADEDOFF_NENTRIES + 2 >= bufsize) {
723 RSSVAL(ad->ad_data, ADEDOFF_NENTRIES, nent);
728 static bool ad_unpack_xattrs(struct adouble *ad)
730 struct ad_xattr_header *h = &ad->adx_header;
731 const char *p = ad->ad_data;
735 if (ad_getentrylen(ad, ADEID_FINDERI) <= ADEDLEN_FINDERI) {
739 /* 2 bytes padding */
740 hoff = ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI + 2;
742 h->adx_magic = RIVAL(p, hoff + 0);
743 h->adx_debug_tag = RIVAL(p, hoff + 4); /* Not used -> not checked */
744 h->adx_total_size = RIVAL(p, hoff + 8);
745 h->adx_data_start = RIVAL(p, hoff + 12);
746 h->adx_data_length = RIVAL(p, hoff + 16);
747 h->adx_flags = RSVAL(p, hoff + 32); /* Not used -> not checked */
748 h->adx_num_attrs = RSVAL(p, hoff + 34);
750 if (h->adx_magic != AD_XATTR_HDR_MAGIC) {
751 DBG_ERR("Bad magic: 0x%" PRIx32 "\n", h->adx_magic);
755 if (h->adx_total_size > ad_getentryoff(ad, ADEID_RFORK)) {
756 DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size);
759 if (h->adx_total_size > AD_XATTR_MAX_HDR_SIZE) {
760 DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size);
764 if (h->adx_data_start < (hoff + AD_XATTR_HDR_SIZE)) {
765 DBG_ERR("Bad start: 0x%" PRIx32 "\n", h->adx_data_start);
769 if ((h->adx_data_start + h->adx_data_length) < h->adx_data_start) {
770 DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length);
773 if ((h->adx_data_start + h->adx_data_length) >
774 ad->adx_header.adx_total_size)
776 DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length);
780 if (h->adx_num_attrs > AD_XATTR_MAX_ENTRIES) {
781 DBG_ERR("Bad num xattrs: %" PRIu16 "\n", h->adx_num_attrs);
785 if (h->adx_num_attrs == 0) {
789 ad->adx_entries = talloc_zero_array(
790 ad, struct ad_xattr_entry, h->adx_num_attrs);
791 if (ad->adx_entries == NULL) {
795 hoff += AD_XATTR_HDR_SIZE;
797 for (i = 0; i < h->adx_num_attrs; i++) {
798 struct ad_xattr_entry *e = &ad->adx_entries[i];
800 hoff = (hoff + 3) & ~3;
802 e->adx_offset = RIVAL(p, hoff + 0);
803 e->adx_length = RIVAL(p, hoff + 4);
804 e->adx_flags = RSVAL(p, hoff + 8);
805 e->adx_namelen = *(p + hoff + 10);
807 if (e->adx_offset >= ad->adx_header.adx_total_size) {
808 DBG_ERR("Bad adx_offset: %" PRIx32 "\n",
813 if ((e->adx_offset + e->adx_length) < e->adx_offset) {
814 DBG_ERR("Bad adx_length: %" PRIx32 "\n",
819 if ((e->adx_offset + e->adx_length) >
820 ad->adx_header.adx_total_size)
822 DBG_ERR("Bad adx_length: %" PRIx32 "\n",
827 if (e->adx_namelen == 0) {
828 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
832 if ((hoff + 11 + e->adx_namelen) < hoff + 11) {
833 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
837 if ((hoff + 11 + e->adx_namelen) >
838 ad->adx_header.adx_data_start)
840 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
845 e->adx_name = talloc_strndup(ad->adx_entries,
848 if (e->adx_name == NULL) {
852 DBG_DEBUG("xattr [%s] offset [0x%x] size [0x%x]\n",
853 e->adx_name, e->adx_offset, e->adx_length);
854 dump_data(10, (uint8_t *)(ad->ad_data + e->adx_offset),
857 hoff += 11 + e->adx_namelen;
864 * Unpack an AppleDouble blob into a struct adoble
866 static bool ad_unpack(struct adouble *ad, const size_t nentries,
869 size_t bufsize = talloc_get_size(ad->ad_data);
871 uint32_t eid, len, off;
875 * The size of the buffer ad->ad_data is checked when read, so
876 * we wouldn't have to check our own offsets, a few extra
877 * checks won't hurt though. We have to check the offsets we
878 * read from the buffer anyway.
881 if (bufsize < (AD_HEADER_LEN + (AD_ENTRY_LEN * nentries))) {
882 DEBUG(1, ("bad size\n"));
886 ad->ad_magic = RIVAL(ad->ad_data, 0);
887 ad->ad_version = RIVAL(ad->ad_data, ADEDOFF_VERSION);
888 if ((ad->ad_magic != AD_MAGIC) || (ad->ad_version != AD_VERSION)) {
889 DEBUG(1, ("wrong magic or version\n"));
893 memcpy(ad->ad_filler, ad->ad_data + ADEDOFF_FILLER, ADEDLEN_FILLER);
895 adentries = RSVAL(ad->ad_data, ADEDOFF_NENTRIES);
896 if (adentries != nentries) {
897 DEBUG(1, ("invalid number of entries: %zu\n",
902 /* now, read in the entry bits */
903 for (i = 0; i < adentries; i++) {
904 eid = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN));
906 off = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 4);
907 len = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 8);
909 if (!eid || eid >= ADEID_MAX) {
910 DEBUG(1, ("bogus eid %d\n", eid));
915 * All entries other than the resource fork are
916 * expected to be read into the ad_data buffer, so
917 * ensure the specified offset is within that bound
919 if ((off > bufsize) && (eid != ADEID_RFORK)) {
920 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
926 * All entries besides FinderInfo and resource fork
927 * must fit into the buffer. FinderInfo is special as
928 * it may be larger then the default 32 bytes (if it
929 * contains marshalled xattrs), but we will fixup that
930 * in ad_convert(). And the resource fork is never
931 * accessed directly by the ad_data buf (also see
932 * comment above) anyway.
934 if ((eid != ADEID_RFORK) &&
935 (eid != ADEID_FINDERI) &&
936 ((off + len) > bufsize)) {
937 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
943 * That would be obviously broken
945 if (off > filesize) {
946 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
952 * Check for any entry that has its end beyond the
955 if (off + len < off) {
956 DEBUG(1, ("offset wrap in eid %d: off: %" PRIu32
957 ", len: %" PRIu32 "\n",
962 if (off + len > filesize) {
964 * If this is the resource fork entry, we fix
965 * up the length, for any other entry we bail
968 if (eid != ADEID_RFORK) {
969 DEBUG(1, ("bogus eid %d: off: %" PRIu32
970 ", len: %" PRIu32 "\n",
976 * Fixup the resource fork entry by limiting
977 * the size to entryoffset - filesize.
979 len = filesize - off;
980 DEBUG(1, ("Limiting ADEID_RFORK: off: %" PRIu32
981 ", len: %" PRIu32 "\n", off, len));
984 ad->ad_eid[eid].ade_off = off;
985 ad->ad_eid[eid].ade_len = len;
988 ok = ad_unpack_xattrs(ad);
996 static bool ad_convert_move_reso(vfs_handle_struct *handle,
998 const struct smb_filename *smb_fname)
1007 rforklen = ad_getentrylen(ad, ADEID_RFORK);
1008 if (rforklen == 0) {
1012 buf = talloc_size(ad, rforklen);
1015 * This allocates a buffer for reading the resource fork data in
1016 * one big swoop. Resource forks won't be larger then, say, 64
1017 * MB, I swear, so just doing the allocation with the talloc
1018 * limit as safeguard seems safe.
1020 DBG_ERR("Failed to allocate %zu bytes for rfork\n",
1025 rforkoff = ad_getentryoff(ad, ADEID_RFORK);
1027 n = SMB_VFS_PREAD(ad->ad_fsp, buf, rforklen, rforkoff);
1028 if (n != rforklen) {
1029 DBG_ERR("Reading %zu bytes from rfork [%s] failed: %s\n",
1030 rforklen, fsp_str_dbg(ad->ad_fsp), strerror(errno));
1034 rforkoff = ADEDOFF_RFORK_DOT_UND;
1036 n = SMB_VFS_PWRITE(ad->ad_fsp, buf, rforklen, rforkoff);
1037 if (n != rforklen) {
1038 DBG_ERR("Writing %zu bytes to rfork [%s] failed: %s\n",
1039 rforklen, fsp_str_dbg(ad->ad_fsp), strerror(errno));
1043 ad_setentryoff(ad, ADEID_RFORK, ADEDOFF_RFORK_DOT_UND);
1046 DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name);
1050 ret = ad_fset(handle, ad, ad->ad_fsp);
1052 DBG_ERR("ad_fset on [%s] failed\n", fsp_str_dbg(ad->ad_fsp));
1059 static bool ad_convert_xattr(vfs_handle_struct *handle,
1061 const struct smb_filename *smb_fname,
1062 bool *converted_xattr)
1064 static struct char_mappings **string_replace_cmaps = NULL;
1066 int saved_errno = 0;
1071 *converted_xattr = false;
1073 if (ad_getentrylen(ad, ADEID_FINDERI) == ADEDLEN_FINDERI) {
1077 if (string_replace_cmaps == NULL) {
1078 const char **mappings = NULL;
1080 mappings = str_list_make_v3_const(
1081 talloc_tos(), fruit_catia_maps, NULL);
1082 if (mappings == NULL) {
1085 string_replace_cmaps = string_replace_init_map(mappings);
1086 TALLOC_FREE(mappings);
1089 for (i = 0; i < ad->adx_header.adx_num_attrs; i++) {
1090 struct ad_xattr_entry *e = &ad->adx_entries[i];
1091 char *mapped_name = NULL;
1093 struct smb_filename *stream_name = NULL;
1094 files_struct *fsp = NULL;
1097 status = string_replace_allocate(handle->conn,
1099 string_replace_cmaps,
1102 vfs_translate_to_windows);
1103 if (!NT_STATUS_IS_OK(status) &&
1104 !NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))
1106 DBG_ERR("string_replace_allocate failed\n");
1112 mapped_name = talloc_asprintf(talloc_tos(), ":%s", tmp);
1114 if (mapped_name == NULL) {
1119 stream_name = synthetic_smb_fname(talloc_tos(),
1120 smb_fname->base_name,
1124 TALLOC_FREE(mapped_name);
1125 if (stream_name == NULL) {
1126 DBG_ERR("synthetic_smb_fname failed\n");
1131 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1133 status = SMB_VFS_CREATE_FILE(
1134 handle->conn, /* conn */
1136 0, /* root_dir_fid */
1137 stream_name, /* fname */
1138 FILE_GENERIC_WRITE, /* access_mask */
1139 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
1140 FILE_OPEN_IF, /* create_disposition */
1141 0, /* create_options */
1142 0, /* file_attributes */
1143 INTERNAL_OPEN_ONLY, /* oplock_request */
1145 0, /* allocation_size */
1146 0, /* private_flags */
1151 NULL, NULL); /* create context */
1152 TALLOC_FREE(stream_name);
1153 if (!NT_STATUS_IS_OK(status)) {
1154 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1159 nwritten = SMB_VFS_PWRITE(fsp,
1160 ad->ad_data + e->adx_offset,
1163 if (nwritten == -1) {
1164 DBG_ERR("SMB_VFS_PWRITE failed\n");
1165 saved_errno = errno;
1166 close_file(NULL, fsp, ERROR_CLOSE);
1167 errno = saved_errno;
1172 status = close_file(NULL, fsp, NORMAL_CLOSE);
1173 if (!NT_STATUS_IS_OK(status)) {
1180 ad_setentrylen(ad, ADEID_FINDERI, ADEDLEN_FINDERI);
1184 DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name);
1188 rc = ad_fset(handle, ad, ad->ad_fsp);
1190 DBG_ERR("ad_fset on [%s] failed: %s\n",
1191 fsp_str_dbg(ad->ad_fsp), strerror(errno));
1196 ok = ad_convert_move_reso(handle, ad, smb_fname);
1201 *converted_xattr = true;
1208 static bool ad_convert_finderinfo(vfs_handle_struct *handle,
1210 const struct smb_filename *smb_fname)
1215 struct smb_filename *stream_name = NULL;
1216 files_struct *fsp = NULL;
1220 int saved_errno = 0;
1223 cmp = memcmp(ad->ad_filler, AD_FILLER_TAG_OSX, ADEDLEN_FILLER);
1228 p_ad = ad_get_entry(ad, ADEID_FINDERI);
1233 ai = afpinfo_new(talloc_tos());
1238 memcpy(ai->afpi_FinderInfo, p_ad, ADEDLEN_FINDERI);
1240 aiblob = data_blob_talloc(talloc_tos(), NULL, AFP_INFO_SIZE);
1241 if (aiblob.data == NULL) {
1246 size = afpinfo_pack(ai, (char *)aiblob.data);
1248 if (size != AFP_INFO_SIZE) {
1252 stream_name = synthetic_smb_fname(talloc_tos(),
1253 smb_fname->base_name,
1257 if (stream_name == NULL) {
1258 data_blob_free(&aiblob);
1259 DBG_ERR("synthetic_smb_fname failed\n");
1263 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1265 status = SMB_VFS_CREATE_FILE(
1266 handle->conn, /* conn */
1268 0, /* root_dir_fid */
1269 stream_name, /* fname */
1270 FILE_GENERIC_WRITE, /* access_mask */
1271 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
1272 FILE_OPEN_IF, /* create_disposition */
1273 0, /* create_options */
1274 0, /* file_attributes */
1275 INTERNAL_OPEN_ONLY, /* oplock_request */
1277 0, /* allocation_size */
1278 0, /* private_flags */
1283 NULL, NULL); /* create context */
1284 TALLOC_FREE(stream_name);
1285 if (!NT_STATUS_IS_OK(status)) {
1286 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1290 nwritten = SMB_VFS_PWRITE(fsp,
1294 if (nwritten == -1) {
1295 DBG_ERR("SMB_VFS_PWRITE failed\n");
1296 saved_errno = errno;
1297 close_file(NULL, fsp, ERROR_CLOSE);
1298 errno = saved_errno;
1302 status = close_file(NULL, fsp, NORMAL_CLOSE);
1303 if (!NT_STATUS_IS_OK(status)) {
1311 static bool ad_convert_truncate(vfs_handle_struct *handle,
1313 const struct smb_filename *smb_fname)
1318 newlen = ADEDOFF_RFORK_DOT_UND + ad_getentrylen(ad, ADEID_RFORK);
1320 rc = SMB_VFS_FTRUNCATE(ad->ad_fsp, newlen);
1328 static bool ad_convert_blank_rfork(vfs_handle_struct *handle,
1332 struct fruit_config_data *config = NULL;
1333 size_t rforklen = sizeof(empty_resourcefork);
1342 SMB_VFS_HANDLE_GET_DATA(handle, config,
1343 struct fruit_config_data, return false);
1345 if (!config->wipe_intentionally_left_blank_rfork) {
1349 if (ad_getentrylen(ad, ADEID_RFORK) != rforklen) {
1353 nread = SMB_VFS_PREAD(ad->ad_fsp, buf, rforklen, ADEDOFF_RFORK_DOT_UND);
1354 if (nread != rforklen) {
1355 DBG_ERR("Reading %zu bytes from rfork [%s] failed: %s\n",
1356 rforklen, fsp_str_dbg(ad->ad_fsp), strerror(errno));
1360 cmp = memcmp(buf, empty_resourcefork, rforklen);
1365 ad_setentrylen(ad, ADEID_RFORK, 0);
1371 rc = ad_fset(handle, ad, ad->ad_fsp);
1373 DBG_ERR("ad_fset on [%s] failed\n", fsp_str_dbg(ad->ad_fsp));
1381 static bool ad_convert_delete_adfile(vfs_handle_struct *handle,
1383 const struct smb_filename *smb_fname)
1385 struct fruit_config_data *config = NULL;
1386 struct smb_filename *ad_name = NULL;
1389 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
1393 SMB_VFS_HANDLE_GET_DATA(handle, config,
1394 struct fruit_config_data, return false);
1396 if (!config->delete_empty_adfiles) {
1400 rc = adouble_path(talloc_tos(), smb_fname, &ad_name);
1405 rc = SMB_VFS_NEXT_UNLINK(handle, ad_name);
1407 DBG_ERR("Unlinking [%s] failed: %s\n",
1408 smb_fname_str_dbg(ad_name), strerror(errno));
1409 TALLOC_FREE(ad_name);
1413 DBG_WARNING("Unlinked [%s] after conversion\n", smb_fname_str_dbg(ad_name));
1414 TALLOC_FREE(ad_name);
1420 * Convert from Apple's ._ file to Netatalk
1422 * Apple's AppleDouble may contain a FinderInfo entry longer then 32
1423 * bytes containing packed xattrs.
1425 * @return -1 in case an error occurred, 0 if no conversion was done, 1
1428 static int ad_convert(struct vfs_handle_struct *handle,
1429 const struct smb_filename *smb_fname)
1431 struct adouble *ad = NULL;
1433 bool converted_xattr = false;
1437 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
1442 ok = ad_convert_xattr(handle, ad, smb_fname, &converted_xattr);
1448 ok = ad_convert_blank_rfork(handle, ad, &blank);
1454 if (converted_xattr || blank) {
1455 ok = ad_convert_truncate(handle, ad, smb_fname);
1462 ok = ad_convert_finderinfo(handle, ad, smb_fname);
1464 DBG_ERR("Failed to convert [%s]\n",
1465 smb_fname_str_dbg(smb_fname));
1470 ok = ad_convert_delete_adfile(handle, ad, smb_fname);
1483 * Read and parse Netatalk AppleDouble metadata xattr
1485 static ssize_t ad_read_meta(vfs_handle_struct *handle,
1487 const struct smb_filename *smb_fname)
1493 DEBUG(10, ("reading meta xattr for %s\n", smb_fname->base_name));
1495 ealen = SMB_VFS_GETXATTR(handle->conn, smb_fname,
1496 AFPINFO_EA_NETATALK, ad->ad_data,
1502 if (errno == ENOATTR) {
1508 DEBUG(2, ("error reading meta xattr: %s\n",
1514 if (ealen != AD_DATASZ_XATTR) {
1515 DEBUG(2, ("bad size %zd\n", ealen));
1521 /* Now parse entries */
1522 ok = ad_unpack(ad, ADEID_NUM_XATTR, AD_DATASZ_XATTR);
1524 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1530 if (!ad_getentryoff(ad, ADEID_FINDERI)
1531 || !ad_getentryoff(ad, ADEID_COMMENT)
1532 || !ad_getentryoff(ad, ADEID_FILEDATESI)
1533 || !ad_getentryoff(ad, ADEID_AFPFILEI)
1534 || !ad_getentryoff(ad, ADEID_PRIVDEV)
1535 || !ad_getentryoff(ad, ADEID_PRIVINO)
1536 || !ad_getentryoff(ad, ADEID_PRIVSYN)
1537 || !ad_getentryoff(ad, ADEID_PRIVID)) {
1538 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1545 DEBUG(10, ("reading meta xattr for %s, rc: %d\n",
1546 smb_fname->base_name, rc));
1550 if (errno == EINVAL) {
1552 (void)SMB_VFS_REMOVEXATTR(handle->conn,
1554 AFPINFO_EA_NETATALK);
1562 static int ad_open_rsrc(vfs_handle_struct *handle,
1563 const struct smb_filename *smb_fname,
1566 files_struct **_fsp)
1569 struct smb_filename *adp_smb_fname = NULL;
1570 files_struct *fsp = NULL;
1571 uint32_t access_mask;
1572 uint32_t share_access;
1573 uint32_t create_disposition;
1576 ret = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
1581 ret = SMB_VFS_STAT(handle->conn, adp_smb_fname);
1583 TALLOC_FREE(adp_smb_fname);
1587 access_mask = FILE_GENERIC_READ;
1588 share_access = FILE_SHARE_READ | FILE_SHARE_WRITE;
1589 create_disposition = FILE_OPEN;
1591 if (flags & O_RDWR) {
1592 access_mask |= FILE_GENERIC_WRITE;
1593 share_access &= ~FILE_SHARE_WRITE;
1596 status = SMB_VFS_CREATE_FILE(
1597 handle->conn, /* conn */
1599 0, /* root_dir_fid */
1604 0, /* create_options */
1605 0, /* file_attributes */
1606 INTERNAL_OPEN_ONLY, /* oplock_request */
1608 0, /* allocation_size */
1609 0, /* private_flags */
1614 NULL, NULL); /* create context */
1615 TALLOC_FREE(adp_smb_fname);
1616 if (!NT_STATUS_IS_OK(status)) {
1617 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1626 * Here's the deal: for ADOUBLE_META we can do without an fd as we can issue
1627 * path based xattr calls. For ADOUBLE_RSRC however we need a full-fledged fd
1628 * for file IO on the ._ file.
1630 static int ad_open(vfs_handle_struct *handle,
1633 const struct smb_filename *smb_fname,
1639 DBG_DEBUG("Path [%s] type [%s]\n", smb_fname->base_name,
1640 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
1642 if (ad->ad_type == ADOUBLE_META) {
1648 ad->ad_opened = false;
1652 ret = ad_open_rsrc(handle, smb_fname, flags, mode, &ad->ad_fsp);
1656 ad->ad_opened = true;
1658 DBG_DEBUG("Path [%s] type [%s]\n",
1659 smb_fname->base_name,
1660 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
1665 static ssize_t ad_read_rsrc_adouble(vfs_handle_struct *handle,
1667 const struct smb_filename *smb_fname)
1675 ret = SMB_VFS_NEXT_FSTAT(handle, ad->ad_fsp, &ad->ad_fsp->fsp_name->st);
1677 DBG_ERR("fstat [%s] failed: %s\n",
1678 fsp_str_dbg(ad->ad_fsp), strerror(errno));
1683 * AppleDouble file header content and size, two cases:
1685 * - without xattrs it is exactly AD_DATASZ_DOT_UND (82) bytes large
1686 * - with embedded xattrs it can be larger, up to AD_XATTR_MAX_HDR_SIZE
1688 * Read as much as we can up to AD_XATTR_MAX_HDR_SIZE.
1690 size = ad->ad_fsp->fsp_name->st.st_ex_size;
1691 if (size > talloc_array_length(ad->ad_data)) {
1692 if (size > AD_XATTR_MAX_HDR_SIZE) {
1693 size = AD_XATTR_MAX_HDR_SIZE;
1695 p_ad = talloc_realloc(ad, ad->ad_data, char, size);
1702 len = SMB_VFS_NEXT_PREAD(handle, ad->ad_fsp, ad->ad_data, talloc_array_length(ad->ad_data), 0);
1703 if (len != talloc_array_length(ad->ad_data)) {
1704 DBG_NOTICE("%s %s: bad size: %zd\n",
1705 smb_fname->base_name, strerror(errno), len);
1709 /* Now parse entries */
1710 ok = ad_unpack(ad, ADEID_NUM_DOT_UND, size);
1712 DBG_ERR("invalid AppleDouble resource %s\n",
1713 smb_fname->base_name);
1718 if ((ad_getentryoff(ad, ADEID_FINDERI) != ADEDOFF_FINDERI_DOT_UND)
1719 || (ad_getentrylen(ad, ADEID_FINDERI) < ADEDLEN_FINDERI)
1720 || (ad_getentryoff(ad, ADEID_RFORK) < ADEDOFF_RFORK_DOT_UND)) {
1721 DBG_ERR("invalid AppleDouble resource %s\n",
1722 smb_fname->base_name);
1731 * Read and parse resource fork, either ._ AppleDouble file or xattr
1733 static ssize_t ad_read_rsrc(vfs_handle_struct *handle,
1735 const struct smb_filename *smb_fname)
1737 return ad_read_rsrc_adouble(handle, ad, smb_fname);
1741 * Read and unpack an AppleDouble metadata xattr or resource
1743 static ssize_t ad_read(vfs_handle_struct *handle,
1745 const struct smb_filename *smb_fname)
1747 switch (ad->ad_type) {
1749 return ad_read_meta(handle, ad, smb_fname);
1751 return ad_read_rsrc(handle, ad, smb_fname);
1757 static int adouble_destructor(struct adouble *ad)
1761 if (!ad->ad_opened) {
1765 SMB_ASSERT(ad->ad_fsp != NULL);
1767 status = close_file(NULL, ad->ad_fsp, NORMAL_CLOSE);
1768 if (!NT_STATUS_IS_OK(status)) {
1769 DBG_ERR("Closing [%s] failed: %s\n",
1770 fsp_str_dbg(ad->ad_fsp), nt_errstr(status));
1777 * Allocate a struct adouble without initialiing it
1779 * The struct is either hang of the fsp extension context or if fsp is
1782 * @param[in] ctx talloc context
1783 * @param[in] handle vfs handle
1784 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1786 * @return adouble handle
1788 static struct adouble *ad_alloc(TALLOC_CTX *ctx,
1789 adouble_type_t type)
1797 adsize = AD_DATASZ_XATTR;
1800 adsize = AD_DATASZ_DOT_UND;
1806 ad = talloc_zero(ctx, struct adouble);
1813 ad->ad_data = talloc_zero_array(ad, char, adsize);
1814 if (ad->ad_data == NULL) {
1821 ad->ad_magic = AD_MAGIC;
1822 ad->ad_version = AD_VERSION;
1824 talloc_set_destructor(ad, adouble_destructor);
1834 * Allocate and initialize a new struct adouble
1836 * @param[in] ctx talloc context
1837 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1839 * @return adouble handle, initialized
1841 static struct adouble *ad_init(TALLOC_CTX *ctx,
1842 adouble_type_t type)
1845 const struct ad_entry_order *eid;
1846 struct adouble *ad = NULL;
1847 time_t t = time(NULL);
1851 eid = entry_order_meta_xattr;
1854 eid = entry_order_dot_und;
1860 ad = ad_alloc(ctx, type);
1866 ad->ad_eid[eid->id].ade_off = eid->offset;
1867 ad->ad_eid[eid->id].ade_len = eid->len;
1871 /* put something sane in the date fields */
1872 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, t);
1873 ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, t);
1874 ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, t);
1875 ad_setdate(ad, AD_DATE_BACKUP, htonl(AD_DATE_START));
1883 static struct adouble *ad_get_internal(TALLOC_CTX *ctx,
1884 vfs_handle_struct *handle,
1886 const struct smb_filename *smb_fname,
1887 adouble_type_t type)
1891 struct adouble *ad = NULL;
1895 smb_fname = fsp->base_fsp->fsp_name;
1898 DEBUG(10, ("ad_get(%s) called for %s\n",
1899 type == ADOUBLE_META ? "meta" : "rsrc",
1900 smb_fname->base_name));
1902 ad = ad_alloc(ctx, type);
1908 /* Try rw first so we can use the fd in ad_convert() */
1911 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
1912 if (rc == -1 && ((errno == EROFS) || (errno == EACCES))) {
1914 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
1917 DBG_DEBUG("ad_open [%s] error [%s]\n",
1918 smb_fname->base_name, strerror(errno));
1923 len = ad_read(handle, ad, smb_fname);
1925 DEBUG(10, ("error reading AppleDouble for %s\n",
1926 smb_fname->base_name));
1932 DEBUG(10, ("ad_get(%s) for %s returning %d\n",
1933 type == ADOUBLE_META ? "meta" : "rsrc",
1934 smb_fname->base_name, rc));
1943 * Return AppleDouble data for a file
1945 * @param[in] ctx talloc context
1946 * @param[in] handle vfs handle
1947 * @param[in] smb_fname pathname to file or directory
1948 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1950 * @return talloced struct adouble or NULL on error
1952 static struct adouble *ad_get(TALLOC_CTX *ctx,
1953 vfs_handle_struct *handle,
1954 const struct smb_filename *smb_fname,
1955 adouble_type_t type)
1957 return ad_get_internal(ctx, handle, NULL, smb_fname, type);
1961 * Return AppleDouble data for a file
1963 * @param[in] ctx talloc context
1964 * @param[in] handle vfs handle
1965 * @param[in] fsp fsp to use for IO
1966 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1968 * @return talloced struct adouble or NULL on error
1970 static struct adouble *ad_fget(TALLOC_CTX *ctx, vfs_handle_struct *handle,
1971 files_struct *fsp, adouble_type_t type)
1973 return ad_get_internal(ctx, handle, fsp, NULL, type);
1977 * Set AppleDouble metadata on a file or directory
1979 * @param[in] ad adouble handle
1981 * @param[in] smb_fname pathname to file or directory
1983 * @return status code, 0 means success
1985 static int ad_set(vfs_handle_struct *handle,
1987 const struct smb_filename *smb_fname)
1992 DBG_DEBUG("Path [%s]\n", smb_fname->base_name);
1994 if (ad->ad_type != ADOUBLE_META) {
1995 DBG_ERR("ad_set on [%s] used with ADOUBLE_RSRC\n",
1996 smb_fname->base_name);
2005 ret = SMB_VFS_SETXATTR(handle->conn,
2007 AFPINFO_EA_NETATALK,
2009 AD_DATASZ_XATTR, 0);
2011 DBG_DEBUG("Path [%s] ret [%d]\n", smb_fname->base_name, ret);
2017 * Set AppleDouble metadata on a file or directory
2019 * @param[in] ad adouble handle
2020 * @param[in] fsp file handle
2022 * @return status code, 0 means success
2024 static int ad_fset(struct vfs_handle_struct *handle,
2032 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
2035 || (fsp->fh == NULL)
2036 || (fsp->fh->fd == -1))
2038 smb_panic("bad fsp");
2046 switch (ad->ad_type) {
2048 rc = SMB_VFS_NEXT_SETXATTR(handle,
2050 AFPINFO_EA_NETATALK,
2052 AD_DATASZ_XATTR, 0);
2056 len = SMB_VFS_NEXT_PWRITE(handle,
2061 if (len != AD_DATASZ_DOT_UND) {
2062 DBG_ERR("short write on %s: %zd", fsp_str_dbg(fsp), len);
2072 DBG_DEBUG("Path [%s] rc [%d]\n", fsp_str_dbg(fsp), rc);
2077 /*****************************************************************************
2079 *****************************************************************************/
2081 static bool is_afpinfo_stream(const struct smb_filename *smb_fname)
2083 if (strncasecmp_m(smb_fname->stream_name,
2084 AFPINFO_STREAM_NAME,
2085 strlen(AFPINFO_STREAM_NAME)) == 0) {
2091 static bool is_afpresource_stream(const struct smb_filename *smb_fname)
2093 if (strncasecmp_m(smb_fname->stream_name,
2094 AFPRESOURCE_STREAM_NAME,
2095 strlen(AFPRESOURCE_STREAM_NAME)) == 0) {
2102 * Test whether stream is an Apple stream.
2104 static bool is_apple_stream(const struct smb_filename *smb_fname)
2106 if (is_afpinfo_stream(smb_fname)) {
2109 if (is_afpresource_stream(smb_fname)) {
2115 static bool is_adouble_file(const char *path)
2117 const char *p = NULL;
2120 p = strrchr(path, '/');
2128 ADOUBLE_NAME_PREFIX,
2129 strlen(ADOUBLE_NAME_PREFIX));
2137 * Initialize config struct from our smb.conf config parameters
2139 static int init_fruit_config(vfs_handle_struct *handle)
2141 struct fruit_config_data *config;
2143 const char *tm_size_str = NULL;
2145 config = talloc_zero(handle->conn, struct fruit_config_data);
2147 DEBUG(1, ("talloc_zero() failed\n"));
2153 * Versions up to Samba 4.5.x had a spelling bug in the
2154 * fruit:resource option calling lp_parm_enum with
2155 * "res*s*ource" (ie two s).
2157 * In Samba 4.6 we accept both the wrong and the correct
2158 * spelling, in Samba 4.7 the bad spelling will be removed.
2160 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2161 "ressource", fruit_rsrc, FRUIT_RSRC_ADFILE);
2162 if (enumval == -1) {
2163 DEBUG(1, ("value for %s: resource type unknown\n",
2164 FRUIT_PARAM_TYPE_NAME));
2167 config->rsrc = (enum fruit_rsrc)enumval;
2169 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2170 "resource", fruit_rsrc, enumval);
2171 if (enumval == -1) {
2172 DEBUG(1, ("value for %s: resource type unknown\n",
2173 FRUIT_PARAM_TYPE_NAME));
2176 config->rsrc = (enum fruit_rsrc)enumval;
2178 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2179 "metadata", fruit_meta, FRUIT_META_NETATALK);
2180 if (enumval == -1) {
2181 DEBUG(1, ("value for %s: metadata type unknown\n",
2182 FRUIT_PARAM_TYPE_NAME));
2185 config->meta = (enum fruit_meta)enumval;
2187 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2188 "locking", fruit_locking, FRUIT_LOCKING_NONE);
2189 if (enumval == -1) {
2190 DEBUG(1, ("value for %s: locking type unknown\n",
2191 FRUIT_PARAM_TYPE_NAME));
2194 config->locking = (enum fruit_locking)enumval;
2196 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2197 "encoding", fruit_encoding, FRUIT_ENC_PRIVATE);
2198 if (enumval == -1) {
2199 DEBUG(1, ("value for %s: encoding type unknown\n",
2200 FRUIT_PARAM_TYPE_NAME));
2203 config->encoding = (enum fruit_encoding)enumval;
2205 if (config->rsrc == FRUIT_RSRC_ADFILE) {
2206 config->veto_appledouble = lp_parm_bool(SNUM(handle->conn),
2207 FRUIT_PARAM_TYPE_NAME,
2212 config->use_aapl = lp_parm_bool(
2213 -1, FRUIT_PARAM_TYPE_NAME, "aapl", true);
2215 config->time_machine = lp_parm_bool(
2216 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "time machine", false);
2218 config->unix_info_enabled = lp_parm_bool(
2219 -1, FRUIT_PARAM_TYPE_NAME, "nfs_aces", true);
2221 config->use_copyfile = lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME,
2224 config->posix_rename = lp_parm_bool(
2225 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "posix_rename", true);
2227 config->aapl_zero_file_id =
2228 lp_parm_bool(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2229 "zero_file_id", false);
2231 config->readdir_attr_rsize = lp_parm_bool(
2232 SNUM(handle->conn), "readdir_attr", "aapl_rsize", true);
2234 config->readdir_attr_finder_info = lp_parm_bool(
2235 SNUM(handle->conn), "readdir_attr", "aapl_finder_info", true);
2237 config->readdir_attr_max_access = lp_parm_bool(
2238 SNUM(handle->conn), "readdir_attr", "aapl_max_access", true);
2240 config->model = lp_parm_const_string(
2241 -1, FRUIT_PARAM_TYPE_NAME, "model", "MacSamba");
2243 tm_size_str = lp_parm_const_string(
2244 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2245 "time machine max size", NULL);
2246 if (tm_size_str != NULL) {
2247 config->time_machine_max_size = conv_str_size(tm_size_str);
2250 config->wipe_intentionally_left_blank_rfork = lp_parm_bool(
2251 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2252 "wipe_intentionally_left_blank_rfork", false);
2254 config->delete_empty_adfiles = lp_parm_bool(
2255 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2256 "delete_empty_adfiles", false);
2258 SMB_VFS_HANDLE_SET_DATA(handle, config,
2259 NULL, struct fruit_config_data,
2266 * Prepend "._" to a basename
2267 * Return a new struct smb_filename with stream_name == NULL.
2269 static int adouble_path(TALLOC_CTX *ctx,
2270 const struct smb_filename *smb_fname_in,
2271 struct smb_filename **pp_smb_fname_out)
2275 struct smb_filename *smb_fname = cp_smb_filename(ctx,
2278 if (smb_fname == NULL) {
2282 /* We need streamname to be NULL */
2283 TALLOC_FREE(smb_fname->stream_name);
2285 /* And we're replacing base_name. */
2286 TALLOC_FREE(smb_fname->base_name);
2288 if (!parent_dirname(smb_fname, smb_fname_in->base_name,
2290 TALLOC_FREE(smb_fname);
2294 smb_fname->base_name = talloc_asprintf(smb_fname,
2295 "%s/._%s", parent, base);
2296 if (smb_fname->base_name == NULL) {
2297 TALLOC_FREE(smb_fname);
2301 *pp_smb_fname_out = smb_fname;
2307 * Allocate and initialize an AfpInfo struct
2309 static AfpInfo *afpinfo_new(TALLOC_CTX *ctx)
2311 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2315 ai->afpi_Signature = AFP_Signature;
2316 ai->afpi_Version = AFP_Version;
2317 ai->afpi_BackupTime = AD_DATE_START;
2322 * Pack an AfpInfo struct into a buffer
2324 * Buffer size must be at least AFP_INFO_SIZE
2325 * Returns size of packed buffer
2327 static ssize_t afpinfo_pack(const AfpInfo *ai, char *buf)
2329 memset(buf, 0, AFP_INFO_SIZE);
2331 RSIVAL(buf, 0, ai->afpi_Signature);
2332 RSIVAL(buf, 4, ai->afpi_Version);
2333 RSIVAL(buf, 12, ai->afpi_BackupTime);
2334 memcpy(buf + 16, ai->afpi_FinderInfo, sizeof(ai->afpi_FinderInfo));
2336 return AFP_INFO_SIZE;
2340 * Unpack a buffer into a AfpInfo structure
2342 * Buffer size must be at least AFP_INFO_SIZE
2343 * Returns allocated AfpInfo struct
2345 static AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data)
2347 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2352 ai->afpi_Signature = RIVAL(data, 0);
2353 ai->afpi_Version = RIVAL(data, 4);
2354 ai->afpi_BackupTime = RIVAL(data, 12);
2355 memcpy(ai->afpi_FinderInfo, (const char *)data + 16,
2356 sizeof(ai->afpi_FinderInfo));
2358 if (ai->afpi_Signature != AFP_Signature
2359 || ai->afpi_Version != AFP_Version) {
2360 DEBUG(1, ("Bad AfpInfo signature or version\n"));
2368 * Fake an inode number from the md5 hash of the (xattr) name
2370 static SMB_INO_T fruit_inode(const SMB_STRUCT_STAT *sbuf, const char *sname)
2372 gnutls_hash_hd_t hash_hnd = NULL;
2373 unsigned char hash[16];
2374 SMB_INO_T result = 0;
2378 DBG_DEBUG("fruit_inode called for %ju/%ju [%s]\n",
2379 (uintmax_t)sbuf->st_ex_dev,
2380 (uintmax_t)sbuf->st_ex_ino, sname);
2382 upper_sname = talloc_strdup_upper(talloc_tos(), sname);
2383 SMB_ASSERT(upper_sname != NULL);
2385 rc = gnutls_hash_init(&hash_hnd, GNUTLS_DIG_MD5);
2390 rc = gnutls_hash(hash_hnd, &(sbuf->st_ex_dev), sizeof(sbuf->st_ex_dev));
2392 gnutls_hash_deinit(hash_hnd, NULL);
2395 rc = gnutls_hash(hash_hnd,
2397 sizeof(sbuf->st_ex_ino));
2399 gnutls_hash_deinit(hash_hnd, NULL);
2402 rc = gnutls_hash(hash_hnd,
2404 talloc_get_size(upper_sname) - 1);
2406 gnutls_hash_deinit(hash_hnd, NULL);
2410 gnutls_hash_deinit(hash_hnd, hash);
2412 /* Hopefully all the variation is in the lower 4 (or 8) bytes! */
2413 memcpy(&result, hash, sizeof(result));
2416 DBG_DEBUG("fruit_inode \"%s\": ino=%ju\n",
2417 sname, (uintmax_t)result);
2420 TALLOC_FREE(upper_sname);
2425 static bool add_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
2426 struct stream_struct **streams,
2427 const char *name, off_t size,
2430 struct stream_struct *tmp;
2432 tmp = talloc_realloc(mem_ctx, *streams, struct stream_struct,
2438 tmp[*num_streams].name = talloc_asprintf(tmp, "%s:$DATA", name);
2439 if (tmp[*num_streams].name == NULL) {
2443 tmp[*num_streams].size = size;
2444 tmp[*num_streams].alloc_size = alloc_size;
2451 static bool filter_empty_rsrc_stream(unsigned int *num_streams,
2452 struct stream_struct **streams)
2454 struct stream_struct *tmp = *streams;
2457 if (*num_streams == 0) {
2461 for (i = 0; i < *num_streams; i++) {
2462 if (strequal_m(tmp[i].name, AFPRESOURCE_STREAM)) {
2467 if (i == *num_streams) {
2471 if (tmp[i].size > 0) {
2475 TALLOC_FREE(tmp[i].name);
2476 if (*num_streams - 1 > i) {
2477 memmove(&tmp[i], &tmp[i+1],
2478 (*num_streams - i - 1) * sizeof(struct stream_struct));
2485 static bool del_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
2486 struct stream_struct **streams,
2489 struct stream_struct *tmp = *streams;
2492 if (*num_streams == 0) {
2496 for (i = 0; i < *num_streams; i++) {
2497 if (strequal_m(tmp[i].name, name)) {
2502 if (i == *num_streams) {
2506 TALLOC_FREE(tmp[i].name);
2507 if (*num_streams - 1 > i) {
2508 memmove(&tmp[i], &tmp[i+1],
2509 (*num_streams - i - 1) * sizeof(struct stream_struct));
2516 static bool ad_empty_finderinfo(const struct adouble *ad)
2519 char emptybuf[ADEDLEN_FINDERI] = {0};
2522 fi = ad_get_entry(ad, ADEID_FINDERI);
2524 DBG_ERR("Missing FinderInfo in struct adouble [%p]\n", ad);
2528 cmp = memcmp(emptybuf, fi, ADEDLEN_FINDERI);
2532 static bool ai_empty_finderinfo(const AfpInfo *ai)
2535 char emptybuf[ADEDLEN_FINDERI] = {0};
2537 cmp = memcmp(emptybuf, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
2542 * Update btime with btime from Netatalk
2544 static void update_btime(vfs_handle_struct *handle,
2545 struct smb_filename *smb_fname)
2548 struct timespec creation_time = {0};
2550 struct fruit_config_data *config = NULL;
2552 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
2555 switch (config->meta) {
2556 case FRUIT_META_STREAM:
2558 case FRUIT_META_NETATALK:
2562 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
2566 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
2570 if (ad_getdate(ad, AD_DATE_UNIX | AD_DATE_CREATE, &t) != 0) {
2576 creation_time.tv_sec = convert_uint32_t_to_time_t(t);
2577 update_stat_ex_create_time(&smb_fname->st, creation_time);
2583 * Map an access mask to a Netatalk single byte byte range lock
2585 static off_t access_to_netatalk_brl(enum apple_fork fork_type,
2586 uint32_t access_mask)
2590 switch (access_mask) {
2591 case FILE_READ_DATA:
2592 offset = AD_FILELOCK_OPEN_RD;
2595 case FILE_WRITE_DATA:
2596 case FILE_APPEND_DATA:
2597 offset = AD_FILELOCK_OPEN_WR;
2601 offset = AD_FILELOCK_OPEN_NONE;
2605 if (fork_type == APPLE_FORK_RSRC) {
2606 if (offset == AD_FILELOCK_OPEN_NONE) {
2607 offset = AD_FILELOCK_RSRC_OPEN_NONE;
2617 * Map a deny mode to a Netatalk brl
2619 static off_t denymode_to_netatalk_brl(enum apple_fork fork_type,
2624 switch (deny_mode) {
2626 offset = AD_FILELOCK_DENY_RD;
2630 offset = AD_FILELOCK_DENY_WR;
2634 smb_panic("denymode_to_netatalk_brl: bad deny mode\n");
2637 if (fork_type == APPLE_FORK_RSRC) {
2645 * Call fcntl() with an exclusive F_GETLK request in order to
2646 * determine if there's an exisiting shared lock
2648 * @return true if the requested lock was found or any error occurred
2649 * false if the lock was not found
2651 static bool test_netatalk_lock(files_struct *fsp, off_t in_offset)
2654 off_t offset = in_offset;
2659 result = SMB_VFS_GETLOCK(fsp, &offset, &len, &type, &pid);
2660 if (result == false) {
2664 if (type != F_UNLCK) {
2671 static NTSTATUS fruit_check_access(vfs_handle_struct *handle,
2673 uint32_t access_mask,
2674 uint32_t share_mode)
2676 NTSTATUS status = NT_STATUS_OK;
2678 bool share_for_read = (share_mode & FILE_SHARE_READ);
2679 bool share_for_write = (share_mode & FILE_SHARE_WRITE);
2680 bool netatalk_already_open_for_reading = false;
2681 bool netatalk_already_open_for_writing = false;
2682 bool netatalk_already_open_with_deny_read = false;
2683 bool netatalk_already_open_with_deny_write = false;
2685 /* FIXME: hardcoded data fork, add resource fork */
2686 enum apple_fork fork_type = APPLE_FORK_DATA;
2688 DBG_DEBUG("fruit_check_access: %s, am: %s/%s, sm: 0x%x\n",
2690 access_mask & FILE_READ_DATA ? "READ" :"-",
2691 access_mask & FILE_WRITE_DATA ? "WRITE" : "-",
2694 if (fsp->fh->fd == -1) {
2695 return NT_STATUS_OK;
2698 /* Read NetATalk opens and deny modes on the file. */
2699 netatalk_already_open_for_reading = test_netatalk_lock(fsp,
2700 access_to_netatalk_brl(fork_type,
2703 netatalk_already_open_with_deny_read = test_netatalk_lock(fsp,
2704 denymode_to_netatalk_brl(fork_type,
2707 netatalk_already_open_for_writing = test_netatalk_lock(fsp,
2708 access_to_netatalk_brl(fork_type,
2711 netatalk_already_open_with_deny_write = test_netatalk_lock(fsp,
2712 denymode_to_netatalk_brl(fork_type,
2715 /* If there are any conflicts - sharing violation. */
2716 if ((access_mask & FILE_READ_DATA) &&
2717 netatalk_already_open_with_deny_read) {
2718 return NT_STATUS_SHARING_VIOLATION;
2721 if (!share_for_read &&
2722 netatalk_already_open_for_reading) {
2723 return NT_STATUS_SHARING_VIOLATION;
2726 if ((access_mask & FILE_WRITE_DATA) &&
2727 netatalk_already_open_with_deny_write) {
2728 return NT_STATUS_SHARING_VIOLATION;
2731 if (!share_for_write &&
2732 netatalk_already_open_for_writing) {
2733 return NT_STATUS_SHARING_VIOLATION;
2736 if (!(access_mask & FILE_READ_DATA)) {
2738 * Nothing we can do here, we need read access
2741 return NT_STATUS_OK;
2744 /* Set NetAtalk locks matching our access */
2745 if (access_mask & FILE_READ_DATA) {
2746 off = access_to_netatalk_brl(fork_type, FILE_READ_DATA);
2749 fsp->op->global->open_persistent_id,
2757 if (!NT_STATUS_IS_OK(status)) {
2762 if (!share_for_read) {
2763 off = denymode_to_netatalk_brl(fork_type, DENY_READ);
2766 fsp->op->global->open_persistent_id,
2774 if (!NT_STATUS_IS_OK(status)) {
2779 if (access_mask & FILE_WRITE_DATA) {
2780 off = access_to_netatalk_brl(fork_type, FILE_WRITE_DATA);
2783 fsp->op->global->open_persistent_id,
2791 if (!NT_STATUS_IS_OK(status)) {
2796 if (!share_for_write) {
2797 off = denymode_to_netatalk_brl(fork_type, DENY_WRITE);
2800 fsp->op->global->open_persistent_id,
2808 if (!NT_STATUS_IS_OK(status)) {
2813 return NT_STATUS_OK;
2816 static NTSTATUS check_aapl(vfs_handle_struct *handle,
2817 struct smb_request *req,
2818 const struct smb2_create_blobs *in_context_blobs,
2819 struct smb2_create_blobs *out_context_blobs)
2821 struct fruit_config_data *config;
2823 struct smb2_create_blob *aapl = NULL;
2827 DATA_BLOB blob = data_blob_talloc(req, NULL, 0);
2828 uint64_t req_bitmap, client_caps;
2829 uint64_t server_caps = SMB2_CRTCTX_AAPL_UNIX_BASED;
2833 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
2834 return NT_STATUS_UNSUCCESSFUL);
2836 if (!config->use_aapl
2837 || in_context_blobs == NULL
2838 || out_context_blobs == NULL) {
2839 return NT_STATUS_OK;
2842 aapl = smb2_create_blob_find(in_context_blobs,
2843 SMB2_CREATE_TAG_AAPL);
2845 return NT_STATUS_OK;
2848 if (aapl->data.length != 24) {
2849 DEBUG(1, ("unexpected AAPL ctxt length: %ju\n",
2850 (uintmax_t)aapl->data.length));
2851 return NT_STATUS_INVALID_PARAMETER;
2854 cmd = IVAL(aapl->data.data, 0);
2855 if (cmd != SMB2_CRTCTX_AAPL_SERVER_QUERY) {
2856 DEBUG(1, ("unsupported AAPL cmd: %d\n", cmd));
2857 return NT_STATUS_INVALID_PARAMETER;
2860 req_bitmap = BVAL(aapl->data.data, 8);
2861 client_caps = BVAL(aapl->data.data, 16);
2863 SIVAL(p, 0, SMB2_CRTCTX_AAPL_SERVER_QUERY);
2865 SBVAL(p, 8, req_bitmap);
2866 ok = data_blob_append(req, &blob, p, 16);
2868 return NT_STATUS_UNSUCCESSFUL;
2871 if (req_bitmap & SMB2_CRTCTX_AAPL_SERVER_CAPS) {
2872 if ((client_caps & SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR) &&
2873 (handle->conn->tcon->compat->fs_capabilities & FILE_NAMED_STREAMS)) {
2874 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR;
2875 config->readdir_attr_enabled = true;
2878 if (config->use_copyfile) {
2879 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_OSX_COPYFILE;
2880 config->copyfile_enabled = true;
2884 * The client doesn't set the flag, so we can't check
2885 * for it and just set it unconditionally
2887 if (config->unix_info_enabled) {
2888 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_NFS_ACE;
2891 SBVAL(p, 0, server_caps);
2892 ok = data_blob_append(req, &blob, p, 8);
2894 return NT_STATUS_UNSUCCESSFUL;
2898 if (req_bitmap & SMB2_CRTCTX_AAPL_VOLUME_CAPS) {
2899 int val = lp_case_sensitive(SNUM(handle->conn->tcon->compat));
2907 caps |= SMB2_CRTCTX_AAPL_CASE_SENSITIVE;
2914 if (config->time_machine) {
2915 caps |= SMB2_CRTCTX_AAPL_FULL_SYNC;
2920 ok = data_blob_append(req, &blob, p, 8);
2922 return NT_STATUS_UNSUCCESSFUL;
2926 if (req_bitmap & SMB2_CRTCTX_AAPL_MODEL_INFO) {
2927 ok = convert_string_talloc(req,
2928 CH_UNIX, CH_UTF16LE,
2929 config->model, strlen(config->model),
2932 return NT_STATUS_UNSUCCESSFUL;
2936 SIVAL(p + 4, 0, modellen);
2937 ok = data_blob_append(req, &blob, p, 8);
2940 return NT_STATUS_UNSUCCESSFUL;
2943 ok = data_blob_append(req, &blob, model, modellen);
2946 return NT_STATUS_UNSUCCESSFUL;
2950 status = smb2_create_blob_add(out_context_blobs,
2952 SMB2_CREATE_TAG_AAPL,
2954 if (NT_STATUS_IS_OK(status)) {
2955 global_fruit_config.nego_aapl = true;
2961 static bool readdir_attr_meta_finderi_stream(
2962 struct vfs_handle_struct *handle,
2963 const struct smb_filename *smb_fname,
2966 struct smb_filename *stream_name = NULL;
2967 files_struct *fsp = NULL;
2972 uint8_t buf[AFP_INFO_SIZE];
2974 stream_name = synthetic_smb_fname(talloc_tos(),
2975 smb_fname->base_name,
2976 AFPINFO_STREAM_NAME,
2977 NULL, smb_fname->flags);
2978 if (stream_name == NULL) {
2982 ret = SMB_VFS_STAT(handle->conn, stream_name);
2987 status = SMB_VFS_CREATE_FILE(
2988 handle->conn, /* conn */
2990 0, /* root_dir_fid */
2991 stream_name, /* fname */
2992 FILE_READ_DATA, /* access_mask */
2993 (FILE_SHARE_READ | FILE_SHARE_WRITE | /* share_access */
2995 FILE_OPEN, /* create_disposition*/
2996 0, /* create_options */
2997 0, /* file_attributes */
2998 INTERNAL_OPEN_ONLY, /* oplock_request */
3000 0, /* allocation_size */
3001 0, /* private_flags */
3006 NULL, NULL); /* create context */
3008 TALLOC_FREE(stream_name);
3010 if (!NT_STATUS_IS_OK(status)) {
3014 nread = SMB_VFS_PREAD(fsp, &buf[0], AFP_INFO_SIZE, 0);
3015 if (nread != AFP_INFO_SIZE) {
3016 DBG_ERR("short read [%s] [%zd/%d]\n",
3017 smb_fname_str_dbg(stream_name), nread, AFP_INFO_SIZE);
3022 memcpy(&ai->afpi_FinderInfo[0], &buf[AFP_OFF_FinderInfo],
3029 close_file(NULL, fsp, NORMAL_CLOSE);
3035 static bool readdir_attr_meta_finderi_netatalk(
3036 struct vfs_handle_struct *handle,
3037 const struct smb_filename *smb_fname,
3040 struct adouble *ad = NULL;
3043 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
3048 p = ad_get_entry(ad, ADEID_FINDERI);
3050 DBG_ERR("No ADEID_FINDERI for [%s]\n", smb_fname->base_name);
3055 memcpy(&ai->afpi_FinderInfo[0], p, AFP_FinderSize);
3060 static bool readdir_attr_meta_finderi(struct vfs_handle_struct *handle,
3061 const struct smb_filename *smb_fname,
3062 struct readdir_attr_data *attr_data)
3064 struct fruit_config_data *config = NULL;
3065 uint32_t date_added;
3069 SMB_VFS_HANDLE_GET_DATA(handle, config,
3070 struct fruit_config_data,
3073 switch (config->meta) {
3074 case FRUIT_META_NETATALK:
3075 ok = readdir_attr_meta_finderi_netatalk(
3076 handle, smb_fname, &ai);
3079 case FRUIT_META_STREAM:
3080 ok = readdir_attr_meta_finderi_stream(
3081 handle, smb_fname, &ai);
3085 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3090 /* Don't bother with errors, it's likely ENOENT */
3094 if (S_ISREG(smb_fname->st.st_ex_mode)) {
3096 memcpy(&attr_data->attr_data.aapl.finder_info[0],
3097 &ai.afpi_FinderInfo[0], 4);
3099 /* finder_creator */
3100 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 4,
3101 &ai.afpi_FinderInfo[4], 4);
3105 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 8,
3106 &ai.afpi_FinderInfo[8], 2);
3108 /* finder_ext_flags */
3109 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 10,
3110 &ai.afpi_FinderInfo[24], 2);
3113 date_added = convert_time_t_to_uint32_t(
3114 smb_fname->st.st_ex_btime.tv_sec - AD_DATE_DELTA);
3116 RSIVAL(&attr_data->attr_data.aapl.finder_info[0], 12, date_added);
3121 static uint64_t readdir_attr_rfork_size_adouble(
3122 struct vfs_handle_struct *handle,
3123 const struct smb_filename *smb_fname)
3125 struct adouble *ad = NULL;
3126 uint64_t rfork_size;
3128 ad = ad_get(talloc_tos(), handle, smb_fname,
3134 rfork_size = ad_getentrylen(ad, ADEID_RFORK);
3140 static uint64_t readdir_attr_rfork_size_stream(
3141 struct vfs_handle_struct *handle,
3142 const struct smb_filename *smb_fname)
3144 struct smb_filename *stream_name = NULL;
3146 uint64_t rfork_size;
3148 stream_name = synthetic_smb_fname(talloc_tos(),
3149 smb_fname->base_name,
3150 AFPRESOURCE_STREAM_NAME,
3152 if (stream_name == NULL) {
3156 ret = SMB_VFS_STAT(handle->conn, stream_name);
3158 TALLOC_FREE(stream_name);
3162 rfork_size = stream_name->st.st_ex_size;
3163 TALLOC_FREE(stream_name);
3168 static uint64_t readdir_attr_rfork_size(struct vfs_handle_struct *handle,
3169 const struct smb_filename *smb_fname)
3171 struct fruit_config_data *config = NULL;
3172 uint64_t rfork_size;
3174 SMB_VFS_HANDLE_GET_DATA(handle, config,
3175 struct fruit_config_data,
3178 switch (config->rsrc) {
3179 case FRUIT_RSRC_ADFILE:
3180 rfork_size = readdir_attr_rfork_size_adouble(handle,
3184 case FRUIT_RSRC_XATTR:
3185 case FRUIT_RSRC_STREAM:
3186 rfork_size = readdir_attr_rfork_size_stream(handle,
3191 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3199 static NTSTATUS readdir_attr_macmeta(struct vfs_handle_struct *handle,
3200 const struct smb_filename *smb_fname,
3201 struct readdir_attr_data *attr_data)
3203 NTSTATUS status = NT_STATUS_OK;
3204 struct fruit_config_data *config = NULL;
3207 SMB_VFS_HANDLE_GET_DATA(handle, config,
3208 struct fruit_config_data,
3209 return NT_STATUS_UNSUCCESSFUL);
3212 /* Ensure we return a default value in the creation_date field */
3213 RSIVAL(&attr_data->attr_data.aapl.finder_info, 12, AD_DATE_START);
3216 * Resource fork length
3219 if (config->readdir_attr_rsize) {
3220 uint64_t rfork_size;
3222 rfork_size = readdir_attr_rfork_size(handle, smb_fname);
3223 attr_data->attr_data.aapl.rfork_size = rfork_size;
3230 if (config->readdir_attr_finder_info) {
3231 ok = readdir_attr_meta_finderi(handle, smb_fname, attr_data);
3233 status = NT_STATUS_INTERNAL_ERROR;
3240 static NTSTATUS remove_virtual_nfs_aces(struct security_descriptor *psd)
3245 if (psd->dacl == NULL) {
3246 return NT_STATUS_OK;
3249 for (i = 0; i < psd->dacl->num_aces; i++) {
3250 /* MS NFS style mode/uid/gid */
3251 int cmp = dom_sid_compare_domain(
3252 &global_sid_Unix_NFS,
3253 &psd->dacl->aces[i].trustee);
3255 /* Normal ACE entry. */
3260 * security_descriptor_dacl_del()
3261 * *must* return NT_STATUS_OK as we know
3262 * we have something to remove.
3265 status = security_descriptor_dacl_del(psd,
3266 &psd->dacl->aces[i].trustee);
3267 if (!NT_STATUS_IS_OK(status)) {
3268 DBG_WARNING("failed to remove MS NFS style ACE: %s\n",
3274 * security_descriptor_dacl_del() may delete more
3275 * then one entry subsequent to this one if the
3276 * SID matches, but we only need to ensure that
3277 * we stay looking at the same element in the array.
3281 return NT_STATUS_OK;
3284 /* Search MS NFS style ACE with UNIX mode */
3285 static NTSTATUS check_ms_nfs(vfs_handle_struct *handle,
3287 struct security_descriptor *psd,
3292 struct fruit_config_data *config = NULL;
3296 SMB_VFS_HANDLE_GET_DATA(handle, config,
3297 struct fruit_config_data,
3298 return NT_STATUS_UNSUCCESSFUL);
3300 if (!global_fruit_config.nego_aapl) {
3301 return NT_STATUS_OK;
3303 if (psd->dacl == NULL || !config->unix_info_enabled) {
3304 return NT_STATUS_OK;
3307 for (i = 0; i < psd->dacl->num_aces; i++) {
3308 if (dom_sid_compare_domain(
3309 &global_sid_Unix_NFS_Mode,
3310 &psd->dacl->aces[i].trustee) == 0) {
3311 *pmode = (mode_t)psd->dacl->aces[i].trustee.sub_auths[2];
3312 *pmode &= (S_IRWXU | S_IRWXG | S_IRWXO);
3315 DEBUG(10, ("MS NFS chmod request %s, %04o\n",
3316 fsp_str_dbg(fsp), (unsigned)(*pmode)));
3322 * Remove any incoming virtual ACE entries generated by
3323 * fruit_fget_nt_acl().
3326 return remove_virtual_nfs_aces(psd);
3329 /****************************************************************************
3331 ****************************************************************************/
3333 static int fruit_connect(vfs_handle_struct *handle,
3334 const char *service,
3338 char *list = NULL, *newlist = NULL;
3339 struct fruit_config_data *config;
3341 DEBUG(10, ("fruit_connect\n"));
3343 rc = SMB_VFS_NEXT_CONNECT(handle, service, user);
3348 rc = init_fruit_config(handle);
3353 SMB_VFS_HANDLE_GET_DATA(handle, config,
3354 struct fruit_config_data, return -1);
3356 if (config->veto_appledouble) {
3357 list = lp_veto_files(talloc_tos(), SNUM(handle->conn));
3360 if (strstr(list, "/" ADOUBLE_NAME_PREFIX "*/") == NULL) {
3361 newlist = talloc_asprintf(
3363 "%s/" ADOUBLE_NAME_PREFIX "*/",
3365 lp_do_parameter(SNUM(handle->conn),
3370 lp_do_parameter(SNUM(handle->conn),
3372 "/" ADOUBLE_NAME_PREFIX "*/");
3378 if (config->encoding == FRUIT_ENC_NATIVE) {
3379 lp_do_parameter(SNUM(handle->conn),
3384 if (config->time_machine) {
3385 DBG_NOTICE("Enabling durable handles for Time Machine "
3386 "support on [%s]\n", service);
3387 lp_do_parameter(SNUM(handle->conn), "durable handles", "yes");
3388 lp_do_parameter(SNUM(handle->conn), "kernel oplocks", "no");
3389 lp_do_parameter(SNUM(handle->conn), "kernel share modes", "no");
3390 if (!lp_strict_sync(SNUM(handle->conn))) {
3391 DBG_WARNING("Time Machine without strict sync is not "
3394 lp_do_parameter(SNUM(handle->conn), "posix locking", "no");
3400 static int fruit_fake_fd(void)
3407 * Return a valid fd, but ensure any attempt to use it returns
3408 * an error (EPIPE). Once we get a write on the handle, we open
3411 ret = pipe(pipe_fds);
3421 static int fruit_open_meta_stream(vfs_handle_struct *handle,
3422 struct smb_filename *smb_fname,
3427 struct fruit_config_data *config = NULL;
3428 struct fio *fio = NULL;
3429 int open_flags = flags & ~O_CREAT;
3432 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3434 SMB_VFS_HANDLE_GET_DATA(handle, config,
3435 struct fruit_config_data, return -1);
3437 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3438 fio->type = ADOUBLE_META;
3439 fio->config = config;
3441 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, open_flags, mode);
3446 if (!(flags & O_CREAT)) {
3447 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
3451 fd = fruit_fake_fd();
3453 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
3457 fio->fake_fd = true;
3464 static int fruit_open_meta_netatalk(vfs_handle_struct *handle,
3465 struct smb_filename *smb_fname,
3470 struct fruit_config_data *config = NULL;
3471 struct fio *fio = NULL;
3472 struct adouble *ad = NULL;
3473 bool meta_exists = false;
3476 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3478 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
3485 if (!meta_exists && !(flags & O_CREAT)) {
3490 fd = fruit_fake_fd();
3495 SMB_VFS_HANDLE_GET_DATA(handle, config,
3496 struct fruit_config_data, return -1);
3498 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3499 fio->type = ADOUBLE_META;
3500 fio->config = config;
3501 fio->fake_fd = true;
3508 static int fruit_open_meta(vfs_handle_struct *handle,
3509 struct smb_filename *smb_fname,
3510 files_struct *fsp, int flags, mode_t mode)
3513 struct fruit_config_data *config = NULL;
3515 DBG_DEBUG("path [%s]\n", smb_fname_str_dbg(smb_fname));
3517 SMB_VFS_HANDLE_GET_DATA(handle, config,
3518 struct fruit_config_data, return -1);
3520 switch (config->meta) {
3521 case FRUIT_META_STREAM:
3522 fd = fruit_open_meta_stream(handle, smb_fname,
3526 case FRUIT_META_NETATALK:
3527 fd = fruit_open_meta_netatalk(handle, smb_fname,
3532 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3536 DBG_DEBUG("path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3541 static int fruit_open_rsrc_adouble(vfs_handle_struct *handle,
3542 struct smb_filename *smb_fname,
3548 struct adouble *ad = NULL;
3549 struct smb_filename *smb_fname_base = NULL;
3550 struct fruit_config_data *config = NULL;
3553 SMB_VFS_HANDLE_GET_DATA(handle, config,
3554 struct fruit_config_data, return -1);
3556 if ((!(flags & O_CREAT)) &&
3557 S_ISDIR(fsp->base_fsp->fsp_name->st.st_ex_mode))
3559 /* sorry, but directories don't habe a resource fork */
3564 rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_base);
3569 /* We always need read/write access for the metadata header too */
3570 flags &= ~(O_RDONLY | O_WRONLY);
3573 hostfd = SMB_VFS_NEXT_OPEN(handle, smb_fname_base, fsp,
3580 if (flags & (O_CREAT | O_TRUNC)) {
3581 ad = ad_init(fsp, ADOUBLE_RSRC);
3587 fsp->fh->fd = hostfd;
3589 rc = ad_fset(handle, ad, fsp);
3600 TALLOC_FREE(smb_fname_base);
3602 DEBUG(10, ("fruit_open resource fork: rc=%d, fd=%d\n", rc, hostfd));
3604 int saved_errno = errno;
3607 * BUGBUGBUG -- we would need to call
3608 * fd_close_posix here, but we don't have a
3611 fsp->fh->fd = hostfd;
3615 errno = saved_errno;
3620 static int fruit_open_rsrc_xattr(vfs_handle_struct *handle,
3621 struct smb_filename *smb_fname,
3626 #ifdef HAVE_ATTROPEN
3629 fd = attropen(smb_fname->base_name,
3630 AFPRESOURCE_EA_NETATALK,
3645 static int fruit_open_rsrc(vfs_handle_struct *handle,
3646 struct smb_filename *smb_fname,
3647 files_struct *fsp, int flags, mode_t mode)
3650 struct fruit_config_data *config = NULL;
3651 struct fio *fio = NULL;
3653 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3655 SMB_VFS_HANDLE_GET_DATA(handle, config,
3656 struct fruit_config_data, return -1);
3658 switch (config->rsrc) {
3659 case FRUIT_RSRC_STREAM:
3660 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3663 case FRUIT_RSRC_ADFILE:
3664 fd = fruit_open_rsrc_adouble(handle, smb_fname,
3668 case FRUIT_RSRC_XATTR:
3669 fd = fruit_open_rsrc_xattr(handle, smb_fname,
3674 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3678 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3684 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3685 fio->type = ADOUBLE_RSRC;
3686 fio->config = config;
3691 static int fruit_open(vfs_handle_struct *handle,
3692 struct smb_filename *smb_fname,
3693 files_struct *fsp, int flags, mode_t mode)
3697 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3699 if (!is_ntfs_stream_smb_fname(smb_fname)) {
3700 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3703 if (is_afpinfo_stream(smb_fname)) {
3704 fd = fruit_open_meta(handle, smb_fname, fsp, flags, mode);
3705 } else if (is_afpresource_stream(smb_fname)) {
3706 fd = fruit_open_rsrc(handle, smb_fname, fsp, flags, mode);
3708 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3711 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3716 static int fruit_close_meta(vfs_handle_struct *handle,
3720 struct fruit_config_data *config = NULL;
3722 SMB_VFS_HANDLE_GET_DATA(handle, config,
3723 struct fruit_config_data, return -1);
3725 switch (config->meta) {
3726 case FRUIT_META_STREAM:
3727 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3730 case FRUIT_META_NETATALK:
3731 ret = close(fsp->fh->fd);
3736 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3744 static int fruit_close_rsrc(vfs_handle_struct *handle,
3748 struct fruit_config_data *config = NULL;
3750 SMB_VFS_HANDLE_GET_DATA(handle, config,
3751 struct fruit_config_data, return -1);
3753 switch (config->rsrc) {
3754 case FRUIT_RSRC_STREAM:
3755 case FRUIT_RSRC_ADFILE:
3756 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3759 case FRUIT_RSRC_XATTR:
3760 ret = close(fsp->fh->fd);
3765 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3772 static int fruit_close(vfs_handle_struct *handle,
3780 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(fsp->fsp_name), fd);
3782 if (!is_ntfs_stream_smb_fname(fsp->fsp_name)) {
3783 return SMB_VFS_NEXT_CLOSE(handle, fsp);
3786 if (is_afpinfo_stream(fsp->fsp_name)) {
3787 ret = fruit_close_meta(handle, fsp);
3788 } else if (is_afpresource_stream(fsp->fsp_name)) {
3789 ret = fruit_close_rsrc(handle, fsp);
3791 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3797 static int fruit_rename(struct vfs_handle_struct *handle,
3798 const struct smb_filename *smb_fname_src,
3799 const struct smb_filename *smb_fname_dst)
3802 struct fruit_config_data *config = NULL;
3803 struct smb_filename *src_adp_smb_fname = NULL;
3804 struct smb_filename *dst_adp_smb_fname = NULL;
3806 SMB_VFS_HANDLE_GET_DATA(handle, config,
3807 struct fruit_config_data, return -1);
3809 if (!VALID_STAT(smb_fname_src->st)) {
3810 DBG_ERR("Need valid stat for [%s]\n",
3811 smb_fname_str_dbg(smb_fname_src));
3815 rc = SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
3820 if ((config->rsrc != FRUIT_RSRC_ADFILE) ||
3821 (!S_ISREG(smb_fname_src->st.st_ex_mode)))
3826 rc = adouble_path(talloc_tos(), smb_fname_src, &src_adp_smb_fname);
3831 rc = adouble_path(talloc_tos(), smb_fname_dst, &dst_adp_smb_fname);
3836 DBG_DEBUG("%s -> %s\n",
3837 smb_fname_str_dbg(src_adp_smb_fname),
3838 smb_fname_str_dbg(dst_adp_smb_fname));
3840 rc = SMB_VFS_NEXT_RENAME(handle, src_adp_smb_fname, dst_adp_smb_fname);
3841 if (errno == ENOENT) {
3846 TALLOC_FREE(src_adp_smb_fname);
3847 TALLOC_FREE(dst_adp_smb_fname);
3851 static int fruit_unlink_meta_stream(vfs_handle_struct *handle,
3852 const struct smb_filename *smb_fname)
3854 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3857 static int fruit_unlink_meta_netatalk(vfs_handle_struct *handle,
3858 const struct smb_filename *smb_fname)
3860 return SMB_VFS_REMOVEXATTR(handle->conn,
3862 AFPINFO_EA_NETATALK);
3865 static int fruit_unlink_meta(vfs_handle_struct *handle,
3866 const struct smb_filename *smb_fname)
3868 struct fruit_config_data *config = NULL;
3871 SMB_VFS_HANDLE_GET_DATA(handle, config,
3872 struct fruit_config_data, return -1);
3874 switch (config->meta) {
3875 case FRUIT_META_STREAM:
3876 rc = fruit_unlink_meta_stream(handle, smb_fname);
3879 case FRUIT_META_NETATALK:
3880 rc = fruit_unlink_meta_netatalk(handle, smb_fname);
3884 DBG_ERR("Unsupported meta config [%d]\n", config->meta);
3891 static int fruit_unlink_rsrc_stream(vfs_handle_struct *handle,
3892 const struct smb_filename *smb_fname,
3897 if (!force_unlink) {
3898 struct smb_filename *smb_fname_cp = NULL;
3901 smb_fname_cp = cp_smb_filename(talloc_tos(), smb_fname);
3902 if (smb_fname_cp == NULL) {
3907 * 0 byte resource fork streams are not listed by
3908 * vfs_streaminfo, as a result stream cleanup/deletion of file
3909 * deletion doesn't remove the resourcefork stream.
3912 ret = SMB_VFS_NEXT_STAT(handle, smb_fname_cp);
3914 TALLOC_FREE(smb_fname_cp);
3915 DBG_ERR("stat [%s] failed [%s]\n",
3916 smb_fname_str_dbg(smb_fname_cp), strerror(errno));
3920 size = smb_fname_cp->st.st_ex_size;
3921 TALLOC_FREE(smb_fname_cp);
3924 /* OS X ignores resource fork stream delete requests */
3929 ret = SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3930 if ((ret != 0) && (errno == ENOENT) && force_unlink) {
3937 static int fruit_unlink_rsrc_adouble(vfs_handle_struct *handle,
3938 const struct smb_filename *smb_fname,
3942 struct adouble *ad = NULL;
3943 struct smb_filename *adp_smb_fname = NULL;
3945 if (!force_unlink) {
3946 ad = ad_get(talloc_tos(), handle, smb_fname,
3955 * 0 byte resource fork streams are not listed by
3956 * vfs_streaminfo, as a result stream cleanup/deletion of file
3957 * deletion doesn't remove the resourcefork stream.
3960 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
3961 /* OS X ignores resource fork stream delete requests */
3969 rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
3974 rc = SMB_VFS_NEXT_UNLINK(handle, adp_smb_fname);
3975 TALLOC_FREE(adp_smb_fname);
3976 if ((rc != 0) && (errno == ENOENT) && force_unlink) {
3983 static int fruit_unlink_rsrc_xattr(vfs_handle_struct *handle,
3984 const struct smb_filename *smb_fname,
3988 * OS X ignores resource fork stream delete requests, so nothing to do
3989 * here. Removing the file will remove the xattr anyway, so we don't
3990 * have to take care of removing 0 byte resource forks that could be
3996 static int fruit_unlink_rsrc(vfs_handle_struct *handle,
3997 const struct smb_filename *smb_fname,
4000 struct fruit_config_data *config = NULL;
4003 SMB_VFS_HANDLE_GET_DATA(handle, config,
4004 struct fruit_config_data, return -1);
4006 switch (config->rsrc) {
4007 case FRUIT_RSRC_STREAM:
4008 rc = fruit_unlink_rsrc_stream(handle, smb_fname, force_unlink);
4011 case FRUIT_RSRC_ADFILE:
4012 rc = fruit_unlink_rsrc_adouble(handle, smb_fname, force_unlink);
4015 case FRUIT_RSRC_XATTR:
4016 rc = fruit_unlink_rsrc_xattr(handle, smb_fname, force_unlink);
4020 DBG_ERR("Unsupported rsrc config [%d]\n", config->rsrc);
4027 static int fruit_unlink(vfs_handle_struct *handle,
4028 const struct smb_filename *smb_fname)
4031 struct fruit_config_data *config = NULL;
4032 struct smb_filename *rsrc_smb_fname = NULL;
4034 SMB_VFS_HANDLE_GET_DATA(handle, config,
4035 struct fruit_config_data, return -1);
4037 if (is_afpinfo_stream(smb_fname)) {
4038 return fruit_unlink_meta(handle, smb_fname);
4039 } else if (is_afpresource_stream(smb_fname)) {
4040 return fruit_unlink_rsrc(handle, smb_fname, false);
4041 } else if (is_ntfs_stream_smb_fname(smb_fname)) {
4042 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4043 } else if (is_adouble_file(smb_fname->base_name)) {
4044 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4048 * A request to delete the base file. Because 0 byte resource
4049 * fork streams are not listed by fruit_streaminfo,
4050 * delete_all_streams() can't remove 0 byte resource fork
4051 * streams, so we have to cleanup this here.
4053 rsrc_smb_fname = synthetic_smb_fname(talloc_tos(),
4054 smb_fname->base_name,
4055 AFPRESOURCE_STREAM_NAME,
4058 if (rsrc_smb_fname == NULL) {
4062 rc = fruit_unlink_rsrc(handle, rsrc_smb_fname, true);
4063 if ((rc != 0) && (errno != ENOENT)) {
4064 DBG_ERR("Forced unlink of [%s] failed [%s]\n",
4065 smb_fname_str_dbg(rsrc_smb_fname), strerror(errno));
4066 TALLOC_FREE(rsrc_smb_fname);
4069 TALLOC_FREE(rsrc_smb_fname);
4071 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4074 static int fruit_chmod(vfs_handle_struct *handle,
4075 const struct smb_filename *smb_fname,
4079 struct fruit_config_data *config = NULL;
4080 struct smb_filename *smb_fname_adp = NULL;
4082 rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname, mode);
4087 SMB_VFS_HANDLE_GET_DATA(handle, config,
4088 struct fruit_config_data, return -1);
4090 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4094 if (!VALID_STAT(smb_fname->st)) {
4098 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
4102 rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_adp);
4107 DEBUG(10, ("fruit_chmod: %s\n", smb_fname_adp->base_name));
4109 rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname_adp, mode);
4110 if (errno == ENOENT) {
4114 TALLOC_FREE(smb_fname_adp);
4118 static int fruit_chown(vfs_handle_struct *handle,
4119 const struct smb_filename *smb_fname,
4124 struct fruit_config_data *config = NULL;
4125 struct smb_filename *adp_smb_fname = NULL;
4127 rc = SMB_VFS_NEXT_CHOWN(handle, smb_fname, uid, gid);
4132 SMB_VFS_HANDLE_GET_DATA(handle, config,
4133 struct fruit_config_data, return -1);
4135 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4139 if (!VALID_STAT(smb_fname->st)) {
4143 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
4147 rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
4152 DEBUG(10, ("fruit_chown: %s\n", adp_smb_fname->base_name));
4154 rc = SMB_VFS_NEXT_CHOWN(handle, adp_smb_fname, uid, gid);
4155 if (errno == ENOENT) {
4160 TALLOC_FREE(adp_smb_fname);
4164 static int fruit_rmdir(struct vfs_handle_struct *handle,
4165 const struct smb_filename *smb_fname)
4169 struct fruit_config_data *config;
4171 SMB_VFS_HANDLE_GET_DATA(handle, config,
4172 struct fruit_config_data, return -1);
4174 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4179 * Due to there is no way to change bDeleteVetoFiles variable
4180 * from this module, need to clean up ourselves
4183 dh = SMB_VFS_OPENDIR(handle->conn, smb_fname, NULL, 0);
4188 while ((de = SMB_VFS_READDIR(handle->conn, dh, NULL)) != NULL) {
4189 struct adouble *ad = NULL;
4191 struct smb_filename *ad_smb_fname = NULL;
4194 if (!is_adouble_file(de->d_name)) {
4198 p = talloc_asprintf(talloc_tos(), "%s/%s",
4199 smb_fname->base_name, de->d_name);
4201 DBG_ERR("talloc_asprintf failed\n");
4205 ad_smb_fname = synthetic_smb_fname(talloc_tos(), p,
4209 if (ad_smb_fname == NULL) {
4210 DBG_ERR("synthetic_smb_fname failed\n");
4215 * Check whether it's a valid AppleDouble file, if
4216 * yes, delete it, ignore it otherwise.
4218 ad = ad_get(talloc_tos(), handle, ad_smb_fname, ADOUBLE_RSRC);
4220 TALLOC_FREE(ad_smb_fname);
4226 ret = SMB_VFS_NEXT_UNLINK(handle, ad_smb_fname);
4228 DBG_ERR("Deleting [%s] failed\n",
4229 smb_fname_str_dbg(ad_smb_fname));
4231 TALLOC_FREE(ad_smb_fname);
4236 SMB_VFS_CLOSEDIR(handle->conn, dh);
4238 return SMB_VFS_NEXT_RMDIR(handle, smb_fname);
4241 static ssize_t fruit_pread_meta_stream(vfs_handle_struct *handle,
4242 files_struct *fsp, void *data,
4243 size_t n, off_t offset)
4248 nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4249 if (nread == -1 || nread == n) {
4253 DBG_ERR("Removing [%s] after short read [%zd]\n",
4254 fsp_str_dbg(fsp), nread);
4256 ret = SMB_VFS_NEXT_UNLINK(handle, fsp->fsp_name);
4258 DBG_ERR("Removing [%s] failed\n", fsp_str_dbg(fsp));
4266 static ssize_t fruit_pread_meta_adouble(vfs_handle_struct *handle,
4267 files_struct *fsp, void *data,
4268 size_t n, off_t offset)
4271 struct adouble *ad = NULL;
4272 char afpinfo_buf[AFP_INFO_SIZE];
4276 ai = afpinfo_new(talloc_tos());
4281 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
4287 p = ad_get_entry(ad, ADEID_FINDERI);
4289 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
4294 memcpy(&ai->afpi_FinderInfo[0], p, ADEDLEN_FINDERI);
4296 nread = afpinfo_pack(ai, afpinfo_buf);
4297 if (nread != AFP_INFO_SIZE) {
4302 memcpy(data, afpinfo_buf, n);
4310 static ssize_t fruit_pread_meta(vfs_handle_struct *handle,
4311 files_struct *fsp, void *data,
4312 size_t n, off_t offset)
4314 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4319 * OS X has a off-by-1 error in the offset calculation, so we're
4320 * bug compatible here. It won't hurt, as any relevant real
4321 * world read requests from the AFP_AfpInfo stream will be
4322 * offset=0 n=60. offset is ignored anyway, see below.
4324 if ((offset < 0) || (offset >= AFP_INFO_SIZE + 1)) {
4329 DBG_ERR("Failed to fetch fsp extension");
4333 /* Yes, macOS always reads from offset 0 */
4335 to_return = MIN(n, AFP_INFO_SIZE);
4337 switch (fio->config->meta) {
4338 case FRUIT_META_STREAM:
4339 nread = fruit_pread_meta_stream(handle, fsp, data,
4343 case FRUIT_META_NETATALK:
4344 nread = fruit_pread_meta_adouble(handle, fsp, data,
4349 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
4353 if (nread == -1 && fio->created) {
4355 char afpinfo_buf[AFP_INFO_SIZE];
4357 ai = afpinfo_new(talloc_tos());
4362 nread = afpinfo_pack(ai, afpinfo_buf);
4364 if (nread != AFP_INFO_SIZE) {
4368 memcpy(data, afpinfo_buf, to_return);
4375 static ssize_t fruit_pread_rsrc_stream(vfs_handle_struct *handle,
4376 files_struct *fsp, void *data,
4377 size_t n, off_t offset)
4379 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4382 static ssize_t fruit_pread_rsrc_xattr(vfs_handle_struct *handle,
4383 files_struct *fsp, void *data,
4384 size_t n, off_t offset)
4386 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4389 static ssize_t fruit_pread_rsrc_adouble(vfs_handle_struct *handle,
4390 files_struct *fsp, void *data,
4391 size_t n, off_t offset)
4393 struct adouble *ad = NULL;
4396 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
4401 nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n,
4402 offset + ad_getentryoff(ad, ADEID_RFORK));
4408 static ssize_t fruit_pread_rsrc(vfs_handle_struct *handle,
4409 files_struct *fsp, void *data,
4410 size_t n, off_t offset)
4412 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4420 switch (fio->config->rsrc) {
4421 case FRUIT_RSRC_STREAM:
4422 nread = fruit_pread_rsrc_stream(handle, fsp, data, n, offset);
4425 case FRUIT_RSRC_ADFILE:
4426 nread = fruit_pread_rsrc_adouble(handle, fsp, data, n, offset);
4429 case FRUIT_RSRC_XATTR:
4430 nread = fruit_pread_rsrc_xattr(handle, fsp, data, n, offset);
4434 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
4441 static ssize_t fruit_pread(vfs_handle_struct *handle,
4442 files_struct *fsp, void *data,
4443 size_t n, off_t offset)
4445 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4448 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4449 fsp_str_dbg(fsp), (intmax_t)offset, n);
4452 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4455 if (fio->type == ADOUBLE_META) {
4456 nread = fruit_pread_meta(handle, fsp, data, n, offset);
4458 nread = fruit_pread_rsrc(handle, fsp, data, n, offset);
4461 DBG_DEBUG("Path [%s] nread [%zd]\n", fsp_str_dbg(fsp), nread);
4465 static bool fruit_must_handle_aio_stream(struct fio *fio)
4471 if (fio->type == ADOUBLE_META) {
4475 if ((fio->type == ADOUBLE_RSRC) &&
4476 (fio->config->rsrc == FRUIT_RSRC_ADFILE))
4484 struct fruit_pread_state {
4486 struct vfs_aio_state vfs_aio_state;
4489 static void fruit_pread_done(struct tevent_req *subreq);
4491 static struct tevent_req *fruit_pread_send(
4492 struct vfs_handle_struct *handle,
4493 TALLOC_CTX *mem_ctx,
4494 struct tevent_context *ev,
4495 struct files_struct *fsp,
4497 size_t n, off_t offset)
4499 struct tevent_req *req = NULL;
4500 struct tevent_req *subreq = NULL;
4501 struct fruit_pread_state *state = NULL;
4502 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4504 req = tevent_req_create(mem_ctx, &state,
4505 struct fruit_pread_state);
4510 if (fruit_must_handle_aio_stream(fio)) {
4511 state->nread = SMB_VFS_PREAD(fsp, data, n, offset);
4512 if (state->nread != n) {
4513 if (state->nread != -1) {
4516 tevent_req_error(req, errno);
4517 return tevent_req_post(req, ev);
4519 tevent_req_done(req);
4520 return tevent_req_post(req, ev);
4523 subreq = SMB_VFS_NEXT_PREAD_SEND(state, ev, handle, fsp,
4525 if (tevent_req_nomem(req, subreq)) {
4526 return tevent_req_post(req, ev);
4528 tevent_req_set_callback(subreq, fruit_pread_done, req);
4532 static void fruit_pread_done(struct tevent_req *subreq)
4534 struct tevent_req *req = tevent_req_callback_data(
4535 subreq, struct tevent_req);
4536 struct fruit_pread_state *state = tevent_req_data(
4537 req, struct fruit_pread_state);
4539 state->nread = SMB_VFS_PREAD_RECV(subreq, &state->vfs_aio_state);
4540 TALLOC_FREE(subreq);
4542 if (tevent_req_error(req, state->vfs_aio_state.error)) {
4545 tevent_req_done(req);
4548 static ssize_t fruit_pread_recv(struct tevent_req *req,
4549 struct vfs_aio_state *vfs_aio_state)
4551 struct fruit_pread_state *state = tevent_req_data(
4552 req, struct fruit_pread_state);
4554 if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
4558 *vfs_aio_state = state->vfs_aio_state;
4559 return state->nread;
4562 static ssize_t fruit_pwrite_meta_stream(vfs_handle_struct *handle,
4563 files_struct *fsp, const void *data,
4564 size_t n, off_t offset)
4566 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4572 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4573 fsp_str_dbg(fsp), (intmax_t)offset, n);
4582 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
4584 DBG_ERR("Close [%s] failed: %s\n",
4585 fsp_str_dbg(fsp), strerror(errno));
4590 fd = SMB_VFS_NEXT_OPEN(handle,
4596 DBG_ERR("On-demand create [%s] in write failed: %s\n",
4597 fsp_str_dbg(fsp), strerror(errno));
4601 fio->fake_fd = false;
4604 ai = afpinfo_unpack(talloc_tos(), data);
4609 if (ai_empty_finderinfo(ai)) {
4611 * Writing an all 0 blob to the metadata stream results in the
4612 * stream being removed on a macOS server. This ensures we
4613 * behave the same and it verified by the "delete AFP_AfpInfo by
4614 * writing all 0" test.
4616 ret = SMB_VFS_NEXT_FTRUNCATE(handle, fsp, 0);
4618 DBG_ERR("SMB_VFS_NEXT_FTRUNCATE on [%s] failed\n",
4623 ok = set_delete_on_close(
4626 handle->conn->session_info->security_token,
4627 handle->conn->session_info->unix_token);
4629 DBG_ERR("set_delete_on_close on [%s] failed\n",
4636 nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4637 if (nwritten != n) {
4644 static ssize_t fruit_pwrite_meta_netatalk(vfs_handle_struct *handle,
4645 files_struct *fsp, const void *data,
4646 size_t n, off_t offset)
4648 struct adouble *ad = NULL;
4654 ai = afpinfo_unpack(talloc_tos(), data);
4659 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
4661 ad = ad_init(talloc_tos(), ADOUBLE_META);
4666 p = ad_get_entry(ad, ADEID_FINDERI);
4668 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
4673 memcpy(p, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
4675 ret = ad_fset(handle, ad, fsp);
4677 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
4684 if (!ai_empty_finderinfo(ai)) {
4689 * Writing an all 0 blob to the metadata stream results in the stream
4690 * being removed on a macOS server. This ensures we behave the same and
4691 * it verified by the "delete AFP_AfpInfo by writing all 0" test.
4694 ok = set_delete_on_close(
4697 handle->conn->session_info->security_token,
4698 handle->conn->session_info->unix_token);
4700 DBG_ERR("set_delete_on_close on [%s] failed\n",
4708 static ssize_t fruit_pwrite_meta(vfs_handle_struct *handle,
4709 files_struct *fsp, const void *data,
4710 size_t n, off_t offset)
4712 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4714 uint8_t buf[AFP_INFO_SIZE];
4720 DBG_ERR("Failed to fetch fsp extension");
4729 if (offset != 0 && n < 60) {
4734 cmp = memcmp(data, "AFP", 3);
4740 if (n <= AFP_OFF_FinderInfo) {
4742 * Nothing to do here really, just return
4750 if (to_copy > AFP_INFO_SIZE) {
4751 to_copy = AFP_INFO_SIZE;
4753 memcpy(buf, data, to_copy);
4756 if (to_write != AFP_INFO_SIZE) {
4757 to_write = AFP_INFO_SIZE;
4760 switch (fio->config->meta) {
4761 case FRUIT_META_STREAM:
4762 nwritten = fruit_pwrite_meta_stream(handle,
4769 case FRUIT_META_NETATALK:
4770 nwritten = fruit_pwrite_meta_netatalk(handle,
4778 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
4782 if (nwritten != to_write) {
4787 * Return the requested amount, verified against macOS SMB server
4792 static ssize_t fruit_pwrite_rsrc_stream(vfs_handle_struct *handle,
4793 files_struct *fsp, const void *data,
4794 size_t n, off_t offset)
4796 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4799 static ssize_t fruit_pwrite_rsrc_xattr(vfs_handle_struct *handle,
4800 files_struct *fsp, const void *data,
4801 size_t n, off_t offset)
4803 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4806 static ssize_t fruit_pwrite_rsrc_adouble(vfs_handle_struct *handle,
4807 files_struct *fsp, const void *data,
4808 size_t n, off_t offset)
4810 struct adouble *ad = NULL;
4814 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
4816 DBG_ERR("ad_get [%s] failed\n", fsp_str_dbg(fsp));
4820 nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n,
4821 offset + ad_getentryoff(ad, ADEID_RFORK));
4822 if (nwritten != n) {
4823 DBG_ERR("Short write on [%s] [%zd/%zd]\n",
4824 fsp_str_dbg(fsp), nwritten, n);
4829 if ((n + offset) > ad_getentrylen(ad, ADEID_RFORK)) {
4830 ad_setentrylen(ad, ADEID_RFORK, n + offset);
4831 ret = ad_fset(handle, ad, fsp);
4833 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
4843 static ssize_t fruit_pwrite_rsrc(vfs_handle_struct *handle,
4844 files_struct *fsp, const void *data,
4845 size_t n, off_t offset)
4847 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4851 DBG_ERR("Failed to fetch fsp extension");
4855 switch (fio->config->rsrc) {
4856 case FRUIT_RSRC_STREAM:
4857 nwritten = fruit_pwrite_rsrc_stream(handle, fsp, data, n, offset);
4860 case FRUIT_RSRC_ADFILE:
4861 nwritten = fruit_pwrite_rsrc_adouble(handle, fsp, data, n, offset);
4864 case FRUIT_RSRC_XATTR:
4865 nwritten = fruit_pwrite_rsrc_xattr(handle, fsp, data, n, offset);
4869 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
4876 static ssize_t fruit_pwrite(vfs_handle_struct *handle,
4877 files_struct *fsp, const void *data,
4878 size_t n, off_t offset)
4880 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4883 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4884 fsp_str_dbg(fsp), (intmax_t)offset, n);
4887 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4890 if (fio->type == ADOUBLE_META) {
4891 nwritten = fruit_pwrite_meta(handle, fsp, data, n, offset);
4893 nwritten = fruit_pwrite_rsrc(handle, fsp, data, n, offset);
4896 DBG_DEBUG("Path [%s] nwritten=%zd\n", fsp_str_dbg(fsp), nwritten);
4900 struct fruit_pwrite_state {
4902 struct vfs_aio_state vfs_aio_state;
4905 static void fruit_pwrite_done(struct tevent_req *subreq);
4907 static struct tevent_req *fruit_pwrite_send(
4908 struct vfs_handle_struct *handle,
4909 TALLOC_CTX *mem_ctx,
4910 struct tevent_context *ev,
4911 struct files_struct *fsp,
4913 size_t n, off_t offset)
4915 struct tevent_req *req = NULL;
4916 struct tevent_req *subreq = NULL;
4917 struct fruit_pwrite_state *state = NULL;
4918 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4920 req = tevent_req_create(mem_ctx, &state,
4921 struct fruit_pwrite_state);
4926 if (fruit_must_handle_aio_stream(fio)) {
4927 state->nwritten = SMB_VFS_PWRITE(fsp, data, n, offset);
4928 if (state->nwritten != n) {
4929 if (state->nwritten != -1) {
4932 tevent_req_error(req, errno);
4933 return tevent_req_post(req, ev);
4935 tevent_req_done(req);
4936 return tevent_req_post(req, ev);
4939 subreq = SMB_VFS_NEXT_PWRITE_SEND(state, ev, handle, fsp,
4941 if (tevent_req_nomem(req, subreq)) {
4942 return tevent_req_post(req, ev);
4944 tevent_req_set_callback(subreq, fruit_pwrite_done, req);
4948 static void fruit_pwrite_done(struct tevent_req *subreq)
4950 struct tevent_req *req = tevent_req_callback_data(
4951 subreq, struct tevent_req);
4952 struct fruit_pwrite_state *state = tevent_req_data(
4953 req, struct fruit_pwrite_state);
4955 state->nwritten = SMB_VFS_PWRITE_RECV(subreq, &state->vfs_aio_state);
4956 TALLOC_FREE(subreq);
4958 if (tevent_req_error(req, state->vfs_aio_state.error)) {
4961 tevent_req_done(req);
4964 static ssize_t fruit_pwrite_recv(struct tevent_req *req,
4965 struct vfs_aio_state *vfs_aio_state)
4967 struct fruit_pwrite_state *state = tevent_req_data(
4968 req, struct fruit_pwrite_state);
4970 if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
4974 *vfs_aio_state = state->vfs_aio_state;
4975 return state->nwritten;
4979 * Helper to stat/lstat the base file of an smb_fname.
4981 static int fruit_stat_base(vfs_handle_struct *handle,
4982 struct smb_filename *smb_fname,
4985 char *tmp_stream_name;
4988 tmp_stream_name = smb_fname->stream_name;
4989 smb_fname->stream_name = NULL;
4991 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
4993 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
4995 smb_fname->stream_name = tmp_stream_name;
4997 DBG_DEBUG("fruit_stat_base [%s] dev [%ju] ino [%ju]\n",
4998 smb_fname->base_name,
4999 (uintmax_t)smb_fname->st.st_ex_dev,
5000 (uintmax_t)smb_fname->st.st_ex_ino);
5004 static int fruit_stat_meta_stream(vfs_handle_struct *handle,
5005 struct smb_filename *smb_fname,
5011 ret = fruit_stat_base(handle, smb_fname, false);
5016 ino = fruit_inode(&smb_fname->st, smb_fname->stream_name);
5019 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
5021 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5024 smb_fname->st.st_ex_ino = ino;
5029 static int fruit_stat_meta_netatalk(vfs_handle_struct *handle,
5030 struct smb_filename *smb_fname,
5033 struct adouble *ad = NULL;
5035 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5037 DBG_INFO("fruit_stat_meta %s: %s\n",
5038 smb_fname_str_dbg(smb_fname), strerror(errno));
5044 /* Populate the stat struct with info from the base file. */
5045 if (fruit_stat_base(handle, smb_fname, follow_links) == -1) {
5048 smb_fname->st.st_ex_size = AFP_INFO_SIZE;
5049 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5050 smb_fname->stream_name);
5054 static int fruit_stat_meta(vfs_handle_struct *handle,
5055 struct smb_filename *smb_fname,
5058 struct fruit_config_data *config = NULL;
5061 SMB_VFS_HANDLE_GET_DATA(handle, config,
5062 struct fruit_config_data, return -1);
5064 switch (config->meta) {
5065 case FRUIT_META_STREAM:
5066 ret = fruit_stat_meta_stream(handle, smb_fname, follow_links);
5069 case FRUIT_META_NETATALK:
5070 ret = fruit_stat_meta_netatalk(handle, smb_fname, follow_links);
5074 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
5081 static int fruit_stat_rsrc_netatalk(vfs_handle_struct *handle,
5082 struct smb_filename *smb_fname,
5085 struct adouble *ad = NULL;
5088 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
5094 /* Populate the stat struct with info from the base file. */
5095 ret = fruit_stat_base(handle, smb_fname, follow_links);
5101 smb_fname->st.st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
5102 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5103 smb_fname->stream_name);
5108 static int fruit_stat_rsrc_stream(vfs_handle_struct *handle,
5109 struct smb_filename *smb_fname,
5115 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
5117 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5123 static int fruit_stat_rsrc_xattr(vfs_handle_struct *handle,
5124 struct smb_filename *smb_fname,
5127 #ifdef HAVE_ATTROPEN
5131 /* Populate the stat struct with info from the base file. */
5132 ret = fruit_stat_base(handle, smb_fname, follow_links);
5137 fd = attropen(smb_fname->base_name,
5138 AFPRESOURCE_EA_NETATALK,
5144 ret = sys_fstat(fd, &smb_fname->st, false);
5147 DBG_ERR("fstat [%s:%s] failed\n", smb_fname->base_name,
5148 AFPRESOURCE_EA_NETATALK);
5154 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5155 smb_fname->stream_name);
5165 static int fruit_stat_rsrc(vfs_handle_struct *handle,
5166 struct smb_filename *smb_fname,
5169 struct fruit_config_data *config = NULL;
5172 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
5174 SMB_VFS_HANDLE_GET_DATA(handle, config,
5175 struct fruit_config_data, return -1);
5177 switch (config->rsrc) {
5178 case FRUIT_RSRC_STREAM:
5179 ret = fruit_stat_rsrc_stream(handle, smb_fname, follow_links);
5182 case FRUIT_RSRC_XATTR:
5183 ret = fruit_stat_rsrc_xattr(handle, smb_fname, follow_links);
5186 case FRUIT_RSRC_ADFILE:
5187 ret = fruit_stat_rsrc_netatalk(handle, smb_fname, follow_links);
5191 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
5198 static int fruit_stat(vfs_handle_struct *handle,
5199 struct smb_filename *smb_fname)
5203 DEBUG(10, ("fruit_stat called for %s\n",
5204 smb_fname_str_dbg(smb_fname)));
5206 if (!is_ntfs_stream_smb_fname(smb_fname)
5207 || is_ntfs_default_stream_smb_fname(smb_fname)) {
5208 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
5210 update_btime(handle, smb_fname);
5216 * Note if lp_posix_paths() is true, we can never
5217 * get here as is_ntfs_stream_smb_fname() is
5218 * always false. So we never need worry about
5219 * not following links here.
5222 if (is_afpinfo_stream(smb_fname)) {
5223 rc = fruit_stat_meta(handle, smb_fname, true);
5224 } else if (is_afpresource_stream(smb_fname)) {
5225 rc = fruit_stat_rsrc(handle, smb_fname, true);
5227 return SMB_VFS_NEXT_STAT(handle, smb_fname);
5231 update_btime(handle, smb_fname);
5232 smb_fname->st.st_ex_mode &= ~S_IFMT;
5233 smb_fname->st.st_ex_mode |= S_IFREG;
5234 smb_fname->st.st_ex_blocks =
5235 smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
5240 static int fruit_lstat(vfs_handle_struct *handle,
5241 struct smb_filename *smb_fname)
5245 DEBUG(10, ("fruit_lstat called for %s\n",
5246 smb_fname_str_dbg(smb_fname)));
5248 if (!is_ntfs_stream_smb_fname(smb_fname)
5249 || is_ntfs_default_stream_smb_fname(smb_fname)) {
5250 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5252 update_btime(handle, smb_fname);
5257 if (is_afpinfo_stream(smb_fname)) {
5258 rc = fruit_stat_meta(handle, smb_fname, false);
5259 } else if (is_afpresource_stream(smb_fname)) {
5260 rc = fruit_stat_rsrc(handle, smb_fname, false);
5262 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5266 update_btime(handle, smb_fname);
5267 smb_fname->st.st_ex_mode &= ~S_IFMT;
5268 smb_fname->st.st_ex_mode |= S_IFREG;
5269 smb_fname->st.st_ex_blocks =
5270 smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
5275 static int fruit_fstat_meta_stream(vfs_handle_struct *handle,
5277 SMB_STRUCT_STAT *sbuf)
5279 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5280 struct smb_filename smb_fname;
5289 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5294 *sbuf = fsp->base_fsp->fsp_name->st;
5295 sbuf->st_ex_size = AFP_INFO_SIZE;
5296 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5300 smb_fname = (struct smb_filename) {
5301 .base_name = fsp->fsp_name->base_name,
5304 ret = fruit_stat_base(handle, &smb_fname, false);
5308 *sbuf = smb_fname.st;
5310 ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5312 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5317 sbuf->st_ex_ino = ino;
5321 static int fruit_fstat_meta_netatalk(vfs_handle_struct *handle,
5323 SMB_STRUCT_STAT *sbuf)
5327 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5332 *sbuf = fsp->base_fsp->fsp_name->st;
5333 sbuf->st_ex_size = AFP_INFO_SIZE;
5334 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5339 static int fruit_fstat_meta(vfs_handle_struct *handle,
5341 SMB_STRUCT_STAT *sbuf,
5346 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
5348 switch (fio->config->meta) {
5349 case FRUIT_META_STREAM:
5350 ret = fruit_fstat_meta_stream(handle, fsp, sbuf);
5353 case FRUIT_META_NETATALK:
5354 ret = fruit_fstat_meta_netatalk(handle, fsp, sbuf);
5358 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
5362 DBG_DEBUG("Path [%s] ret [%d]\n", fsp_str_dbg(fsp), ret);
5366 static int fruit_fstat_rsrc_xattr(vfs_handle_struct *handle,
5368 SMB_STRUCT_STAT *sbuf)
5370 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5373 static int fruit_fstat_rsrc_stream(vfs_handle_struct *handle,
5375 SMB_STRUCT_STAT *sbuf)
5377 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5380 static int fruit_fstat_rsrc_adouble(vfs_handle_struct *handle,
5382 SMB_STRUCT_STAT *sbuf)
5384 struct adouble *ad = NULL;
5387 /* Populate the stat struct with info from the base file. */
5388 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5393 ad = ad_get(talloc_tos(), handle,
5394 fsp->base_fsp->fsp_name,
5397 DBG_ERR("ad_get [%s] failed [%s]\n",
5398 fsp_str_dbg(fsp), strerror(errno));
5402 *sbuf = fsp->base_fsp->fsp_name->st;
5403 sbuf->st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
5404 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5410 static int fruit_fstat_rsrc(vfs_handle_struct *handle, files_struct *fsp,
5411 SMB_STRUCT_STAT *sbuf, struct fio *fio)
5415 switch (fio->config->rsrc) {
5416 case FRUIT_RSRC_STREAM:
5417 ret = fruit_fstat_rsrc_stream(handle, fsp, sbuf);
5420 case FRUIT_RSRC_ADFILE:
5421 ret = fruit_fstat_rsrc_adouble(handle, fsp, sbuf);
5424 case FRUIT_RSRC_XATTR:
5425 ret = fruit_fstat_rsrc_xattr(handle, fsp, sbuf);
5429 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
5436 static int fruit_fstat(vfs_handle_struct *handle, files_struct *fsp,
5437 SMB_STRUCT_STAT *sbuf)
5439 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5443 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5446 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
5448 if (fio->type == ADOUBLE_META) {
5449 rc = fruit_fstat_meta(handle, fsp, sbuf, fio);
5451 rc = fruit_fstat_rsrc(handle, fsp, sbuf, fio);
5455 sbuf->st_ex_mode &= ~S_IFMT;
5456 sbuf->st_ex_mode |= S_IFREG;
5457 sbuf->st_ex_blocks = sbuf->st_ex_size / STAT_ST_BLOCKSIZE + 1;
5460 DBG_DEBUG("Path [%s] rc [%d] size [%"PRIdMAX"]\n",
5461 fsp_str_dbg(fsp), rc, (intmax_t)sbuf->st_ex_size);
5465 static NTSTATUS delete_invalid_meta_stream(
5466 vfs_handle_struct *handle,
5467 const struct smb_filename *smb_fname,
5468 TALLOC_CTX *mem_ctx,
5469 unsigned int *pnum_streams,
5470 struct stream_struct **pstreams,
5473 struct smb_filename *sname = NULL;
5477 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams, AFPINFO_STREAM);
5479 return NT_STATUS_INTERNAL_ERROR;
5483 return NT_STATUS_OK;
5486 sname = synthetic_smb_fname(talloc_tos(),
5487 smb_fname->base_name,
5488 AFPINFO_STREAM_NAME,
5490 if (sname == NULL) {
5491 return NT_STATUS_NO_MEMORY;
5494 ret = SMB_VFS_NEXT_UNLINK(handle, sname);
5497 DBG_ERR("Removing [%s] failed\n", smb_fname_str_dbg(sname));
5498 return map_nt_error_from_unix(errno);
5501 return NT_STATUS_OK;
5504 static NTSTATUS fruit_streaminfo_meta_stream(
5505 vfs_handle_struct *handle,
5506 struct files_struct *fsp,
5507 const struct smb_filename *smb_fname,
5508 TALLOC_CTX *mem_ctx,
5509 unsigned int *pnum_streams,
5510 struct stream_struct **pstreams)
5512 struct stream_struct *stream = *pstreams;
5513 unsigned int num_streams = *pnum_streams;
5516 for (i = 0; i < num_streams; i++) {
5517 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
5522 if (i == num_streams) {
5523 return NT_STATUS_OK;
5526 if (stream[i].size != AFP_INFO_SIZE) {
5527 DBG_ERR("Removing invalid AFPINFO_STREAM size [%jd] from [%s]\n",
5528 (intmax_t)stream[i].size, smb_fname_str_dbg(smb_fname));
5530 return delete_invalid_meta_stream(handle,
5539 return NT_STATUS_OK;
5542 static NTSTATUS fruit_streaminfo_meta_netatalk(
5543 vfs_handle_struct *handle,
5544 struct files_struct *fsp,
5545 const struct smb_filename *smb_fname,
5546 TALLOC_CTX *mem_ctx,
5547 unsigned int *pnum_streams,
5548 struct stream_struct **pstreams)
5550 struct stream_struct *stream = *pstreams;
5551 unsigned int num_streams = *pnum_streams;
5552 struct adouble *ad = NULL;
5557 /* Remove the Netatalk xattr from the list */
5558 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5559 ":" NETATALK_META_XATTR ":$DATA");
5561 return NT_STATUS_NO_MEMORY;
5565 * Check if there's a AFPINFO_STREAM from the VFS streams
5566 * backend and if yes, remove it from the list
5568 for (i = 0; i < num_streams; i++) {
5569 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
5574 if (i < num_streams) {
5575 DBG_WARNING("Unexpected AFPINFO_STREAM on [%s]\n",
5576 smb_fname_str_dbg(smb_fname));
5578 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5581 return NT_STATUS_INTERNAL_ERROR;
5585 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5587 return NT_STATUS_OK;
5590 is_fi_empty = ad_empty_finderinfo(ad);
5594 return NT_STATUS_OK;
5597 ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
5598 AFPINFO_STREAM_NAME, AFP_INFO_SIZE,
5599 smb_roundup(handle->conn, AFP_INFO_SIZE));
5601 return NT_STATUS_NO_MEMORY;
5604 return NT_STATUS_OK;
5607 static NTSTATUS fruit_streaminfo_meta(vfs_handle_struct *handle,
5608 struct files_struct *fsp,
5609 const struct smb_filename *smb_fname,
5610 TALLOC_CTX *mem_ctx,
5611 unsigned int *pnum_streams,
5612 struct stream_struct **pstreams)
5614 struct fruit_config_data *config = NULL;
5617 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5618 return NT_STATUS_INTERNAL_ERROR);
5620 switch (config->meta) {
5621 case FRUIT_META_NETATALK:
5622 status = fruit_streaminfo_meta_netatalk(handle, fsp, smb_fname,
5623 mem_ctx, pnum_streams,
5627 case FRUIT_META_STREAM:
5628 status = fruit_streaminfo_meta_stream(handle, fsp, smb_fname,
5629 mem_ctx, pnum_streams,
5634 return NT_STATUS_INTERNAL_ERROR;
5640 static NTSTATUS fruit_streaminfo_rsrc_stream(
5641 vfs_handle_struct *handle,
5642 struct files_struct *fsp,
5643 const struct smb_filename *smb_fname,
5644 TALLOC_CTX *mem_ctx,
5645 unsigned int *pnum_streams,
5646 struct stream_struct **pstreams)
5650 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
5652 DBG_ERR("Filtering resource stream failed\n");
5653 return NT_STATUS_INTERNAL_ERROR;
5655 return NT_STATUS_OK;
5658 static NTSTATUS fruit_streaminfo_rsrc_xattr(
5659 vfs_handle_struct *handle,
5660 struct files_struct *fsp,
5661 const struct smb_filename *smb_fname,
5662 TALLOC_CTX *mem_ctx,
5663 unsigned int *pnum_streams,
5664 struct stream_struct **pstreams)
5668 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
5670 DBG_ERR("Filtering resource stream failed\n");
5671 return NT_STATUS_INTERNAL_ERROR;
5673 return NT_STATUS_OK;
5676 static NTSTATUS fruit_streaminfo_rsrc_adouble(
5677 vfs_handle_struct *handle,
5678 struct files_struct *fsp,
5679 const struct smb_filename *smb_fname,
5680 TALLOC_CTX *mem_ctx,
5681 unsigned int *pnum_streams,
5682 struct stream_struct **pstreams)
5684 struct stream_struct *stream = *pstreams;
5685 unsigned int num_streams = *pnum_streams;
5686 struct adouble *ad = NULL;
5692 * Check if there's a AFPRESOURCE_STREAM from the VFS streams backend
5693 * and if yes, remove it from the list
5695 for (i = 0; i < num_streams; i++) {
5696 if (strequal_m(stream[i].name, AFPRESOURCE_STREAM)) {
5701 if (i < num_streams) {
5702 DBG_WARNING("Unexpected AFPRESOURCE_STREAM on [%s]\n",
5703 smb_fname_str_dbg(smb_fname));
5705 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5706 AFPRESOURCE_STREAM);
5708 return NT_STATUS_INTERNAL_ERROR;
5712 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
5714 return NT_STATUS_OK;
5717 rlen = ad_getentrylen(ad, ADEID_RFORK);
5721 return NT_STATUS_OK;
5724 ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
5725 AFPRESOURCE_STREAM_NAME, rlen,
5726 smb_roundup(handle->conn, rlen));
5728 return NT_STATUS_NO_MEMORY;
5731 return NT_STATUS_OK;
5734 static NTSTATUS fruit_streaminfo_rsrc(vfs_handle_struct *handle,
5735 struct files_struct *fsp,
5736 const struct smb_filename *smb_fname,
5737 TALLOC_CTX *mem_ctx,
5738 unsigned int *pnum_streams,
5739 struct stream_struct **pstreams)
5741 struct fruit_config_data *config = NULL;
5744 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5745 return NT_STATUS_INTERNAL_ERROR);
5747 switch (config->rsrc) {
5748 case FRUIT_RSRC_STREAM:
5749 status = fruit_streaminfo_rsrc_stream(handle, fsp, smb_fname,
5750 mem_ctx, pnum_streams,
5754 case FRUIT_RSRC_XATTR:
5755 status = fruit_streaminfo_rsrc_xattr(handle, fsp, smb_fname,
5756 mem_ctx, pnum_streams,
5760 case FRUIT_RSRC_ADFILE:
5761 status = fruit_streaminfo_rsrc_adouble(handle, fsp, smb_fname,
5762 mem_ctx, pnum_streams,
5767 return NT_STATUS_INTERNAL_ERROR;
5773 static void fruit_filter_empty_streams(unsigned int *pnum_streams,
5774 struct stream_struct **pstreams)
5776 unsigned num_streams = *pnum_streams;
5777 struct stream_struct *streams = *pstreams;
5780 if (!global_fruit_config.nego_aapl) {
5784 while (i < num_streams) {
5785 struct smb_filename smb_fname = (struct smb_filename) {
5786 .stream_name = streams[i].name,
5789 if (is_ntfs_default_stream_smb_fname(&smb_fname)
5790 || streams[i].size > 0)
5796 streams[i] = streams[num_streams - 1];
5800 *pnum_streams = num_streams;
5803 static NTSTATUS fruit_streaminfo(vfs_handle_struct *handle,
5804 struct files_struct *fsp,
5805 const struct smb_filename *smb_fname,
5806 TALLOC_CTX *mem_ctx,
5807 unsigned int *pnum_streams,
5808 struct stream_struct **pstreams)
5810 struct fruit_config_data *config = NULL;
5813 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5814 return NT_STATUS_UNSUCCESSFUL);
5816 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
5818 status = SMB_VFS_NEXT_STREAMINFO(handle, fsp, smb_fname, mem_ctx,
5819 pnum_streams, pstreams);
5820 if (!NT_STATUS_IS_OK(status)) {
5824 fruit_filter_empty_streams(pnum_streams, pstreams);
5826 status = fruit_streaminfo_meta(handle, fsp, smb_fname,
5827 mem_ctx, pnum_streams, pstreams);
5828 if (!NT_STATUS_IS_OK(status)) {
5832 status = fruit_streaminfo_rsrc(handle, fsp, smb_fname,
5833 mem_ctx, pnum_streams, pstreams);
5834 if (!NT_STATUS_IS_OK(status)) {
5838 return NT_STATUS_OK;
5841 static int fruit_ntimes(vfs_handle_struct *handle,
5842 const struct smb_filename *smb_fname,
5843 struct smb_file_time *ft)
5846 struct adouble *ad = NULL;
5847 struct fruit_config_data *config = NULL;
5849 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5852 if ((config->meta != FRUIT_META_NETATALK) ||
5853 null_timespec(ft->create_time))
5855 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
5858 DEBUG(10,("set btime for %s to %s\n", smb_fname_str_dbg(smb_fname),
5859 time_to_asc(convert_timespec_to_time_t(ft->create_time))));
5861 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5866 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX,
5867 convert_time_t_to_uint32_t(ft->create_time.tv_sec));
5869 rc = ad_set(handle, ad, smb_fname);
5875 DEBUG(1, ("fruit_ntimes: %s\n", smb_fname_str_dbg(smb_fname)));
5878 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
5881 static int fruit_fallocate(struct vfs_handle_struct *handle,
5882 struct files_struct *fsp,
5887 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5890 return SMB_VFS_NEXT_FALLOCATE(handle, fsp, mode, offset, len);
5893 /* Let the pwrite code path handle it. */
5898 static int fruit_ftruncate_rsrc_xattr(struct vfs_handle_struct *handle,
5899 struct files_struct *fsp,
5902 #ifdef HAVE_ATTROPEN
5903 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
5908 static int fruit_ftruncate_rsrc_adouble(struct vfs_handle_struct *handle,
5909 struct files_struct *fsp,
5913 struct adouble *ad = NULL;
5916 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
5918 DBG_DEBUG("ad_get [%s] failed [%s]\n",
5919 fsp_str_dbg(fsp), strerror(errno));
5923 ad_off = ad_getentryoff(ad, ADEID_RFORK);
5925 rc = ftruncate(fsp->fh->fd, offset + ad_off);
5931 ad_setentrylen(ad, ADEID_RFORK, offset);
5933 rc = ad_fset(handle, ad, fsp);
5935 DBG_ERR("ad_fset [%s] failed [%s]\n",
5936 fsp_str_dbg(fsp), strerror(errno));
5945 static int fruit_ftruncate_rsrc_stream(struct vfs_handle_struct *handle,
5946 struct files_struct *fsp,
5949 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
5952 static int fruit_ftruncate_rsrc(struct vfs_handle_struct *handle,
5953 struct files_struct *fsp,
5956 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5960 DBG_ERR("Failed to fetch fsp extension");
5964 switch (fio->config->rsrc) {
5965 case FRUIT_RSRC_XATTR:
5966 ret = fruit_ftruncate_rsrc_xattr(handle, fsp, offset);
5969 case FRUIT_RSRC_ADFILE:
5970 ret = fruit_ftruncate_rsrc_adouble(handle, fsp, offset);
5973 case FRUIT_RSRC_STREAM:
5974 ret = fruit_ftruncate_rsrc_stream(handle, fsp, offset);
5978 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
5986 static int fruit_ftruncate_meta(struct vfs_handle_struct *handle,
5987 struct files_struct *fsp,
5991 DBG_WARNING("ftruncate %s to %jd",
5992 fsp_str_dbg(fsp), (intmax_t)offset);
5993 /* OS X returns NT_STATUS_ALLOTTED_SPACE_EXCEEDED */
5998 /* OS X returns success but does nothing */
5999 DBG_INFO("ignoring ftruncate %s to %jd\n",
6000 fsp_str_dbg(fsp), (intmax_t)offset);
6004 static int fruit_ftruncate(struct vfs_handle_struct *handle,
6005 struct files_struct *fsp,
6008 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
6011 DBG_DEBUG("Path [%s] offset [%"PRIdMAX"]\n", fsp_str_dbg(fsp),
6015 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
6018 if (fio->type == ADOUBLE_META) {
6019 ret = fruit_ftruncate_meta(handle, fsp, offset);
6021 ret = fruit_ftruncate_rsrc(handle, fsp, offset);
6024 DBG_DEBUG("Path [%s] result [%d]\n", fsp_str_dbg(fsp), ret);
6028 static NTSTATUS fruit_create_file(vfs_handle_struct *handle,
6029 struct smb_request *req,
6030 uint16_t root_dir_fid,
6031 struct smb_filename *smb_fname,
6032 uint32_t access_mask,
6033 uint32_t share_access,
6034 uint32_t create_disposition,
6035 uint32_t create_options,
6036 uint32_t file_attributes,
6037 uint32_t oplock_request,
6038 struct smb2_lease *lease,
6039 uint64_t allocation_size,
6040 uint32_t private_flags,
6041 struct security_descriptor *sd,
6042 struct ea_list *ea_list,
6043 files_struct **result,
6045 const struct smb2_create_blobs *in_context_blobs,
6046 struct smb2_create_blobs *out_context_blobs)
6049 struct fruit_config_data *config = NULL;
6050 files_struct *fsp = NULL;
6051 struct fio *fio = NULL;
6052 bool internal_open = (oplock_request & INTERNAL_OPEN_ONLY);
6055 status = check_aapl(handle, req, in_context_blobs, out_context_blobs);
6056 if (!NT_STATUS_IS_OK(status)) {
6060 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
6061 return NT_STATUS_UNSUCCESSFUL);
6063 if (is_apple_stream(smb_fname) && !internal_open) {
6064 ret = ad_convert(handle, smb_fname);
6066 DBG_ERR("ad_convert() failed\n");
6067 return NT_STATUS_UNSUCCESSFUL;
6071 status = SMB_VFS_NEXT_CREATE_FILE(
6072 handle, req, root_dir_fid, smb_fname,
6073 access_mask, share_access,
6074 create_disposition, create_options,
6075 file_attributes, oplock_request,
6077 allocation_size, private_flags,
6078 sd, ea_list, result,
6079 pinfo, in_context_blobs, out_context_blobs);
6080 if (!NT_STATUS_IS_OK(status)) {
6086 if (global_fruit_config.nego_aapl) {
6087 if (config->posix_rename && fsp->is_directory) {
6089 * Enable POSIX directory rename behaviour
6091 fsp->posix_flags |= FSP_POSIX_FLAGS_RENAME;
6096 * If this is a plain open for existing files, opening an 0
6097 * byte size resource fork MUST fail with
6098 * NT_STATUS_OBJECT_NAME_NOT_FOUND.
6100 * Cf the vfs_fruit torture tests in test_rfork_create().
6102 if (global_fruit_config.nego_aapl &&
6103 create_disposition == FILE_OPEN &&
6104 smb_fname->st.st_ex_size == 0 &&
6105 is_ntfs_stream_smb_fname(smb_fname) &&
6106 !(is_ntfs_default_stream_smb_fname(smb_fname)))
6108 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
6112 fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
6113 if (fio != NULL && pinfo != NULL && *pinfo == FILE_WAS_CREATED) {
6114 fio->created = true;
6117 if (is_ntfs_stream_smb_fname(smb_fname)
6118 || fsp->is_directory) {
6122 if ((config->locking == FRUIT_LOCKING_NETATALK) &&
6125 status = fruit_check_access(
6129 if (!NT_STATUS_IS_OK(status)) {
6137 DEBUG(10, ("fruit_create_file: %s\n", nt_errstr(status)));
6140 close_file(req, fsp, ERROR_CLOSE);
6141 *result = fsp = NULL;
6147 static NTSTATUS fruit_readdir_attr(struct vfs_handle_struct *handle,
6148 const struct smb_filename *fname,
6149 TALLOC_CTX *mem_ctx,
6150 struct readdir_attr_data **pattr_data)
6152 struct fruit_config_data *config = NULL;
6153 struct readdir_attr_data *attr_data;
6157 SMB_VFS_HANDLE_GET_DATA(handle, config,
6158 struct fruit_config_data,
6159 return NT_STATUS_UNSUCCESSFUL);
6161 if (!global_fruit_config.nego_aapl) {
6162 return SMB_VFS_NEXT_READDIR_ATTR(handle, fname, mem_ctx, pattr_data);
6165 DEBUG(10, ("fruit_readdir_attr %s\n", fname->base_name));
6167 ret = ad_convert(handle, fname);
6169 DBG_ERR("ad_convert() failed\n");
6170 return NT_STATUS_UNSUCCESSFUL;
6173 *pattr_data = talloc_zero(mem_ctx, struct readdir_attr_data);
6174 if (*pattr_data == NULL) {
6175 return NT_STATUS_UNSUCCESSFUL;
6177 attr_data = *pattr_data;
6178 attr_data->type = RDATTR_AAPL;
6181 * Mac metadata: compressed FinderInfo, resource fork length
6184 status = readdir_attr_macmeta(handle, fname, attr_data);
6185 if (!NT_STATUS_IS_OK(status)) {
6187 * Error handling is tricky: if we return failure from
6188 * this function, the corresponding directory entry
6189 * will to be passed to the client, so we really just
6190 * want to error out on fatal errors.
6192 if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
6200 if (config->unix_info_enabled) {
6201 attr_data->attr_data.aapl.unix_mode = fname->st.st_ex_mode;
6207 if (!config->readdir_attr_max_access) {
6208 attr_data->attr_data.aapl.max_access = FILE_GENERIC_ALL;
6210 status = smbd_calculate_access_mask(
6214 SEC_FLAG_MAXIMUM_ALLOWED,
6215 &attr_data->attr_data.aapl.max_access);
6216 if (!NT_STATUS_IS_OK(status)) {
6221 return NT_STATUS_OK;
6224 DEBUG(1, ("fruit_readdir_attr %s, error: %s\n",
6225 fname->base_name, nt_errstr(status)));
6226 TALLOC_FREE(*pattr_data);
6230 static NTSTATUS fruit_fget_nt_acl(vfs_handle_struct *handle,
6232 uint32_t security_info,
6233 TALLOC_CTX *mem_ctx,
6234 struct security_descriptor **ppdesc)
6237 struct security_ace ace;
6239 struct fruit_config_data *config;
6241 SMB_VFS_HANDLE_GET_DATA(handle, config,
6242 struct fruit_config_data,
6243 return NT_STATUS_UNSUCCESSFUL);
6245 status = SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
6247 if (!NT_STATUS_IS_OK(status)) {
6252 * Add MS NFS style ACEs with uid, gid and mode
6254 if (!global_fruit_config.nego_aapl) {
6255 return NT_STATUS_OK;
6257 if (!config->unix_info_enabled) {
6258 return NT_STATUS_OK;
6261 /* First remove any existing ACE's with NFS style mode/uid/gid SIDs. */
6262 status = remove_virtual_nfs_aces(*ppdesc);
6263 if (!NT_STATUS_IS_OK(status)) {
6264 DBG_WARNING("failed to remove MS NFS style ACEs\n");
6268 /* MS NFS style mode */
6269 sid_compose(&sid, &global_sid_Unix_NFS_Mode, fsp->fsp_name->st.st_ex_mode);
6270 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6271 status = security_descriptor_dacl_add(*ppdesc, &ace);
6272 if (!NT_STATUS_IS_OK(status)) {
6273 DEBUG(1,("failed to add MS NFS style ACE\n"));
6277 /* MS NFS style uid */
6278 sid_compose(&sid, &global_sid_Unix_NFS_Users, fsp->fsp_name->st.st_ex_uid);
6279 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6280 status = security_descriptor_dacl_add(*ppdesc, &ace);
6281 if (!NT_STATUS_IS_OK(status)) {
6282 DEBUG(1,("failed to add MS NFS style ACE\n"));
6286 /* MS NFS style gid */
6287 sid_compose(&sid, &global_sid_Unix_NFS_Groups, fsp->fsp_name->st.st_ex_gid);
6288 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6289 status = security_descriptor_dacl_add(*ppdesc, &ace);
6290 if (!NT_STATUS_IS_OK(status)) {
6291 DEBUG(1,("failed to add MS NFS style ACE\n"));
6295 return NT_STATUS_OK;
6298 static NTSTATUS fruit_fset_nt_acl(vfs_handle_struct *handle,
6300 uint32_t security_info_sent,
6301 const struct security_descriptor *orig_psd)
6305 mode_t ms_nfs_mode = 0;
6307 struct security_descriptor *psd = NULL;
6308 uint32_t orig_num_aces = 0;
6310 if (orig_psd->dacl != NULL) {
6311 orig_num_aces = orig_psd->dacl->num_aces;
6314 psd = security_descriptor_copy(talloc_tos(), orig_psd);
6316 return NT_STATUS_NO_MEMORY;
6319 DBG_DEBUG("fruit_fset_nt_acl: %s\n", fsp_str_dbg(fsp));
6321 status = check_ms_nfs(handle, fsp, psd, &ms_nfs_mode, &do_chmod);
6322 if (!NT_STATUS_IS_OK(status)) {
6323 DEBUG(1, ("fruit_fset_nt_acl: check_ms_nfs failed%s\n", fsp_str_dbg(fsp)));
6329 * If only ms_nfs ACE entries were sent, ensure we set the DACL
6330 * sent/present flags correctly now we've removed them.
6333 if (orig_num_aces != 0) {
6335 * Are there any ACE's left ?
6337 if (psd->dacl->num_aces == 0) {
6338 /* No - clear the DACL sent/present flags. */
6339 security_info_sent &= ~SECINFO_DACL;
6340 psd->type &= ~SEC_DESC_DACL_PRESENT;
6344 status = SMB_VFS_NEXT_FSET_NT_ACL(handle, fsp, security_info_sent, psd);
6345 if (!NT_STATUS_IS_OK(status)) {
6346 DEBUG(1, ("fruit_fset_nt_acl: SMB_VFS_NEXT_FSET_NT_ACL failed%s\n", fsp_str_dbg(fsp)));
6352 result = SMB_VFS_FCHMOD(fsp, ms_nfs_mode);
6354 DBG_WARNING("%s, result: %d, %04o error %s\n",
6357 (unsigned)ms_nfs_mode,
6359 status = map_nt_error_from_unix(errno);
6366 return NT_STATUS_OK;
6369 static struct vfs_offload_ctx *fruit_offload_ctx;
6371 struct fruit_offload_read_state {
6372 struct vfs_handle_struct *handle;
6373 struct tevent_context *ev;
6379 static void fruit_offload_read_done(struct tevent_req *subreq);
6381 static struct tevent_req *fruit_offload_read_send(
6382 TALLOC_CTX *mem_ctx,
6383 struct tevent_context *ev,
6384 struct vfs_handle_struct *handle,
6391 struct tevent_req *req = NULL;
6392 struct tevent_req *subreq = NULL;
6393 struct fruit_offload_read_state *state = NULL;
6395 req = tevent_req_create(mem_ctx, &state,
6396 struct fruit_offload_read_state);
6400 *state = (struct fruit_offload_read_state) {
6407 subreq = SMB_VFS_NEXT_OFFLOAD_READ_SEND(mem_ctx, ev, handle, fsp,
6408 fsctl, ttl, offset, to_copy);
6409 if (tevent_req_nomem(subreq, req)) {
6410 return tevent_req_post(req, ev);
6412 tevent_req_set_callback(subreq, fruit_offload_read_done, req);
6416 static void fruit_offload_read_done(struct tevent_req *subreq)
6418 struct tevent_req *req = tevent_req_callback_data(
6419 subreq, struct tevent_req);
6420 struct fruit_offload_read_state *state = tevent_req_data(
6421 req, struct fruit_offload_read_state);
6424 status = SMB_VFS_NEXT_OFFLOAD_READ_RECV(subreq,
6428 TALLOC_FREE(subreq);
6429 if (tevent_req_nterror(req, status)) {
6433 if (state->fsctl != FSCTL_SRV_REQUEST_RESUME_KEY) {
6434 tevent_req_done(req);
6438 status = vfs_offload_token_ctx_init(state->fsp->conn->sconn->client,
6439 &fruit_offload_ctx);
6440 if (tevent_req_nterror(req, status)) {
6444 status = vfs_offload_token_db_store_fsp(fruit_offload_ctx,
6447 if (tevent_req_nterror(req, status)) {
6451 tevent_req_done(req);
6455 static NTSTATUS fruit_offload_read_recv(struct tevent_req *req,
6456 struct vfs_handle_struct *handle,
6457 TALLOC_CTX *mem_ctx,
6460 struct fruit_offload_read_state *state = tevent_req_data(
6461 req, struct fruit_offload_read_state);
6464 if (tevent_req_is_nterror(req, &status)) {
6465 tevent_req_received(req);
6469 token->length = state->token.length;
6470 token->data = talloc_move(mem_ctx, &state->token.data);
6472 tevent_req_received(req);
6473 return NT_STATUS_OK;
6476 struct fruit_offload_write_state {
6477 struct vfs_handle_struct *handle;
6479 struct files_struct *src_fsp;
6480 struct files_struct *dst_fsp;
6484 static void fruit_offload_write_done(struct tevent_req *subreq);
6485 static struct tevent_req *fruit_offload_write_send(struct vfs_handle_struct *handle,
6486 TALLOC_CTX *mem_ctx,
6487 struct tevent_context *ev,
6490 off_t transfer_offset,
6491 struct files_struct *dest_fsp,
6495 struct tevent_req *req, *subreq;
6496 struct fruit_offload_write_state *state;
6498 struct fruit_config_data *config;
6499 off_t src_off = transfer_offset;
6500 files_struct *src_fsp = NULL;
6501 off_t to_copy = num;
6502 bool copyfile_enabled = false;
6504 DEBUG(10,("soff: %ju, doff: %ju, len: %ju\n",
6505 (uintmax_t)src_off, (uintmax_t)dest_off, (uintmax_t)num));
6507 SMB_VFS_HANDLE_GET_DATA(handle, config,
6508 struct fruit_config_data,
6511 req = tevent_req_create(mem_ctx, &state,
6512 struct fruit_offload_write_state);
6516 state->handle = handle;
6517 state->dst_fsp = dest_fsp;
6520 case FSCTL_SRV_COPYCHUNK:
6521 case FSCTL_SRV_COPYCHUNK_WRITE:
6522 copyfile_enabled = config->copyfile_enabled;
6529 * Check if this a OS X copyfile style copychunk request with
6530 * a requested chunk count of 0 that was translated to a
6531 * offload_write_send VFS call overloading the parameters src_off
6532 * = dest_off = num = 0.
6534 if (copyfile_enabled && num == 0 && src_off == 0 && dest_off == 0) {
6535 status = vfs_offload_token_db_fetch_fsp(
6536 fruit_offload_ctx, token, &src_fsp);
6537 if (tevent_req_nterror(req, status)) {
6538 return tevent_req_post(req, ev);
6540 state->src_fsp = src_fsp;
6542 status = vfs_stat_fsp(src_fsp);
6543 if (tevent_req_nterror(req, status)) {
6544 return tevent_req_post(req, ev);
6547 to_copy = src_fsp->fsp_name->st.st_ex_size;
6548 state->is_copyfile = true;
6551 subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle,
6560 if (tevent_req_nomem(subreq, req)) {
6561 return tevent_req_post(req, ev);
6564 tevent_req_set_callback(subreq, fruit_offload_write_done, req);
6568 static void fruit_offload_write_done(struct tevent_req *subreq)
6570 struct tevent_req *req = tevent_req_callback_data(
6571 subreq, struct tevent_req);
6572 struct fruit_offload_write_state *state = tevent_req_data(
6573 req, struct fruit_offload_write_state);
6575 unsigned int num_streams = 0;
6576 struct stream_struct *streams = NULL;
6578 struct smb_filename *src_fname_tmp = NULL;
6579 struct smb_filename *dst_fname_tmp = NULL;
6581 status = SMB_VFS_NEXT_OFFLOAD_WRITE_RECV(state->handle,
6584 TALLOC_FREE(subreq);
6585 if (tevent_req_nterror(req, status)) {
6589 if (!state->is_copyfile) {
6590 tevent_req_done(req);
6595 * Now copy all remaining streams. We know the share supports
6596 * streams, because we're in vfs_fruit. We don't do this async
6597 * because streams are few and small.
6599 status = vfs_streaminfo(state->handle->conn, state->src_fsp,
6600 state->src_fsp->fsp_name,
6601 req, &num_streams, &streams);
6602 if (tevent_req_nterror(req, status)) {
6606 if (num_streams == 1) {
6607 /* There is always one stream, ::$DATA. */
6608 tevent_req_done(req);
6612 for (i = 0; i < num_streams; i++) {
6613 DEBUG(10, ("%s: stream: '%s'/%zu\n",
6614 __func__, streams[i].name, (size_t)streams[i].size));
6616 src_fname_tmp = synthetic_smb_fname(
6618 state->src_fsp->fsp_name->base_name,
6621 state->src_fsp->fsp_name->flags);
6622 if (tevent_req_nomem(src_fname_tmp, req)) {
6626 if (is_ntfs_default_stream_smb_fname(src_fname_tmp)) {
6627 TALLOC_FREE(src_fname_tmp);
6631 dst_fname_tmp = synthetic_smb_fname(
6633 state->dst_fsp->fsp_name->base_name,
6636 state->dst_fsp->fsp_name->flags);
6637 if (tevent_req_nomem(dst_fname_tmp, req)) {
6638 TALLOC_FREE(src_fname_tmp);
6642 status = copy_file(req,
6643 state->handle->conn,
6646 OPENX_FILE_CREATE_IF_NOT_EXIST,
6648 if (!NT_STATUS_IS_OK(status)) {
6649 DEBUG(1, ("%s: copy %s to %s failed: %s\n", __func__,
6650 smb_fname_str_dbg(src_fname_tmp),
6651 smb_fname_str_dbg(dst_fname_tmp),
6652 nt_errstr(status)));
6653 TALLOC_FREE(src_fname_tmp);
6654 TALLOC_FREE(dst_fname_tmp);
6655 tevent_req_nterror(req, status);
6659 TALLOC_FREE(src_fname_tmp);
6660 TALLOC_FREE(dst_fname_tmp);
6663 TALLOC_FREE(streams);
6664 TALLOC_FREE(src_fname_tmp);
6665 TALLOC_FREE(dst_fname_tmp);
6666 tevent_req_done(req);
6669 static NTSTATUS fruit_offload_write_recv(struct vfs_handle_struct *handle,
6670 struct tevent_req *req,
6673 struct fruit_offload_write_state *state = tevent_req_data(
6674 req, struct fruit_offload_write_state);
6677 if (tevent_req_is_nterror(req, &status)) {
6678 DEBUG(1, ("server side copy chunk failed: %s\n",
6679 nt_errstr(status)));
6681 tevent_req_received(req);
6685 *copied = state->copied;
6686 tevent_req_received(req);
6688 return NT_STATUS_OK;
6691 static char *fruit_get_bandsize_line(char **lines, int numlines)
6694 static bool re_initialized = false;
6698 if (!re_initialized) {
6699 ret = regcomp(&re, "^[[:blank:]]*<key>band-size</key>$", 0);
6703 re_initialized = true;
6706 for (i = 0; i < numlines; i++) {
6707 regmatch_t matches[1];
6709 ret = regexec(&re, lines[i], 1, matches, 0);
6712 * Check if the match was on the last line, sa we want
6713 * the subsequent line.
6715 if (i + 1 == numlines) {
6718 return lines[i + 1];
6720 if (ret != REG_NOMATCH) {
6728 static bool fruit_get_bandsize_from_line(char *line, size_t *_band_size)
6731 static bool re_initialized = false;
6732 regmatch_t matches[2];
6737 if (!re_initialized) {
6740 "<integer>\\([[:digit:]]*\\)</integer>$",
6745 re_initialized = true;
6748 ret = regexec(&re, line, 2, matches, 0);
6750 DBG_ERR("regex failed [%s]\n", line);
6754 line[matches[1].rm_eo] = '\0';
6756 ok = conv_str_u64(&line[matches[1].rm_so], &band_size);
6760 *_band_size = (size_t)band_size;
6765 * This reads and parses an Info.plist from a TM sparsebundle looking for the
6766 * "band-size" key and value.
6768 static bool fruit_get_bandsize(vfs_handle_struct *handle,
6772 #define INFO_PLIST_MAX_SIZE 64*1024
6774 struct smb_filename *smb_fname = NULL;
6775 files_struct *fsp = NULL;
6776 uint8_t *file_data = NULL;
6777 char **lines = NULL;
6778 char *band_size_line = NULL;
6779 size_t plist_file_size;
6786 plist = talloc_asprintf(talloc_tos(),
6788 handle->conn->connectpath,
6790 if (plist == NULL) {
6795 smb_fname = synthetic_smb_fname(talloc_tos(), plist, NULL, NULL, 0);
6796 if (smb_fname == NULL) {
6801 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
6803 DBG_INFO("Ignoring Sparsebundle without Info.plist [%s]\n", dir);
6808 plist_file_size = smb_fname->st.st_ex_size;
6810 if (plist_file_size > INFO_PLIST_MAX_SIZE) {
6811 DBG_INFO("%s is too large, ignoring\n", plist);
6816 status = SMB_VFS_NEXT_CREATE_FILE(
6819 0, /* root_dir_fid */
6820 smb_fname, /* fname */
6821 FILE_GENERIC_READ, /* access_mask */
6822 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
6823 FILE_OPEN, /* create_disposition */
6824 0, /* create_options */
6825 0, /* file_attributes */
6826 INTERNAL_OPEN_ONLY, /* oplock_request */
6828 0, /* allocation_size */
6829 0, /* private_flags */
6834 NULL, NULL); /* create context */
6835 if (!NT_STATUS_IS_OK(status)) {
6836 DBG_INFO("Opening [%s] failed [%s]\n",
6837 smb_fname_str_dbg(smb_fname), nt_errstr(status));
6842 file_data = talloc_array(talloc_tos(), uint8_t, plist_file_size);
6843 if (file_data == NULL) {
6848 nread = SMB_VFS_NEXT_PREAD(handle, fsp, file_data, plist_file_size, 0);
6849 if (nread != plist_file_size) {
6850 DBG_ERR("Short read on [%s]: %zu/%zd\n",
6851 fsp_str_dbg(fsp), nread, plist_file_size);
6857 status = close_file(NULL, fsp, NORMAL_CLOSE);
6859 if (!NT_STATUS_IS_OK(status)) {
6860 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
6865 lines = file_lines_parse((char *)file_data,
6869 if (lines == NULL) {
6874 band_size_line = fruit_get_bandsize_line(lines, numlines);
6875 if (band_size_line == NULL) {
6876 DBG_ERR("Didn't find band-size key in [%s]\n",
6877 smb_fname_str_dbg(smb_fname));
6882 ok = fruit_get_bandsize_from_line(band_size_line, band_size);
6884 DBG_ERR("fruit_get_bandsize_from_line failed\n");
6888 DBG_DEBUG("Parsed band-size [%zu] for [%s]\n", *band_size, plist);
6892 status = close_file(NULL, fsp, NORMAL_CLOSE);
6893 if (!NT_STATUS_IS_OK(status)) {
6894 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
6899 TALLOC_FREE(smb_fname);
6900 TALLOC_FREE(file_data);
6905 struct fruit_disk_free_state {
6909 static bool fruit_get_num_bands(vfs_handle_struct *handle,
6914 struct smb_filename *bands_dir = NULL;
6916 struct dirent *e = NULL;
6920 path = talloc_asprintf(talloc_tos(),
6922 handle->conn->connectpath,
6928 bands_dir = synthetic_smb_fname(talloc_tos(),
6934 if (bands_dir == NULL) {
6938 d = SMB_VFS_NEXT_OPENDIR(handle, bands_dir, NULL, 0);
6940 TALLOC_FREE(bands_dir);
6946 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
6948 e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
6950 if (ISDOT(e->d_name) || ISDOTDOT(e->d_name)) {
6956 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
6958 TALLOC_FREE(bands_dir);
6962 DBG_DEBUG("%zu bands in [%s]\n", nbands, smb_fname_str_dbg(bands_dir));
6964 TALLOC_FREE(bands_dir);
6970 static bool fruit_tmsize_do_dirent(vfs_handle_struct *handle,
6971 struct fruit_disk_free_state *state,
6976 size_t sparsebundle_strlen = strlen("sparsebundle");
6977 size_t bandsize = 0;
6981 p = strstr(e->d_name, "sparsebundle");
6986 if (p[sparsebundle_strlen] != '\0') {
6990 DBG_DEBUG("Processing sparsebundle [%s]\n", e->d_name);
6992 ok = fruit_get_bandsize(handle, e->d_name, &bandsize);
6995 * Beware of race conditions: this may be an uninitialized
6996 * Info.plist that a client is just creating. We don't want let
6997 * this to trigger complete failure.
6999 DBG_ERR("Processing sparsebundle [%s] failed\n", e->d_name);
7003 ok = fruit_get_num_bands(handle, e->d_name, &nbands);
7006 * Beware of race conditions: this may be a backup sparsebundle
7007 * in an early stage lacking a bands subdirectory. We don't want
7008 * let this to trigger complete failure.
7010 DBG_ERR("Processing sparsebundle [%s] failed\n", e->d_name);
7014 if (bandsize > SIZE_MAX/nbands) {
7015 DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
7019 tm_size = bandsize * nbands;
7021 if (state->total_size + tm_size < state->total_size) {
7022 DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
7027 state->total_size += tm_size;
7029 DBG_DEBUG("[%s] tm_size [%jd] total_size [%jd]\n",
7030 e->d_name, (intmax_t)tm_size, (intmax_t)state->total_size);
7036 * Calculate used size of a TimeMachine volume
7038 * This assumes that the volume is used only for TimeMachine.
7040 * - readdir(basedir of share), then
7041 * - for every element that matches regex "^\(.*\)\.sparsebundle$" :
7042 * - parse "\1.sparsebundle/Info.plist" and read the band-size XML key
7043 * - count band files in "\1.sparsebundle/bands/"
7044 * - calculate used size of all bands: band_count * band_size
7046 static uint64_t fruit_disk_free(vfs_handle_struct *handle,
7047 const struct smb_filename *smb_fname,
7052 struct fruit_config_data *config = NULL;
7053 struct fruit_disk_free_state state = {0};
7055 struct dirent *e = NULL;
7061 SMB_VFS_HANDLE_GET_DATA(handle, config,
7062 struct fruit_config_data,
7065 if (!config->time_machine ||
7066 config->time_machine_max_size == 0)
7068 return SMB_VFS_NEXT_DISK_FREE(handle,
7075 d = SMB_VFS_NEXT_OPENDIR(handle, smb_fname, NULL, 0);
7080 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
7082 e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
7084 ok = fruit_tmsize_do_dirent(handle, &state, e);
7086 SMB_VFS_NEXT_CLOSEDIR(handle, d);
7091 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
7096 dsize = config->time_machine_max_size / 512;
7097 dfree = dsize - (state.total_size / 512);
7098 if (dfree > dsize) {
7108 static uint64_t fruit_fs_file_id(struct vfs_handle_struct *handle,
7109 const SMB_STRUCT_STAT *psbuf)
7111 struct fruit_config_data *config = NULL;
7113 SMB_VFS_HANDLE_GET_DATA(handle, config,
7114 struct fruit_config_data,
7117 if (global_fruit_config.nego_aapl &&
7118 config->aapl_zero_file_id)
7123 return SMB_VFS_NEXT_FS_FILE_ID(handle, psbuf);
7126 static struct vfs_fn_pointers vfs_fruit_fns = {
7127 .connect_fn = fruit_connect,
7128 .disk_free_fn = fruit_disk_free,
7130 /* File operations */
7131 .chmod_fn = fruit_chmod,
7132 .chown_fn = fruit_chown,
7133 .unlink_fn = fruit_unlink,
7134 .rename_fn = fruit_rename,
7135 .rmdir_fn = fruit_rmdir,
7136 .open_fn = fruit_open,
7137 .close_fn = fruit_close,
7138 .pread_fn = fruit_pread,
7139 .pwrite_fn = fruit_pwrite,
7140 .pread_send_fn = fruit_pread_send,
7141 .pread_recv_fn = fruit_pread_recv,
7142 .pwrite_send_fn = fruit_pwrite_send,
7143 .pwrite_recv_fn = fruit_pwrite_recv,
7144 .stat_fn = fruit_stat,
7145 .lstat_fn = fruit_lstat,
7146 .fstat_fn = fruit_fstat,
7147 .streaminfo_fn = fruit_streaminfo,
7148 .ntimes_fn = fruit_ntimes,
7149 .ftruncate_fn = fruit_ftruncate,
7150 .fallocate_fn = fruit_fallocate,
7151 .create_file_fn = fruit_create_file,
7152 .readdir_attr_fn = fruit_readdir_attr,
7153 .offload_read_send_fn = fruit_offload_read_send,
7154 .offload_read_recv_fn = fruit_offload_read_recv,
7155 .offload_write_send_fn = fruit_offload_write_send,
7156 .offload_write_recv_fn = fruit_offload_write_recv,
7157 .fs_file_id_fn = fruit_fs_file_id,
7159 /* NT ACL operations */
7160 .fget_nt_acl_fn = fruit_fget_nt_acl,
7161 .fset_nt_acl_fn = fruit_fset_nt_acl,
7165 NTSTATUS vfs_fruit_init(TALLOC_CTX *ctx)
7167 NTSTATUS ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "fruit",
7169 if (!NT_STATUS_IS_OK(ret)) {
7173 vfs_fruit_debug_level = debug_add_class("fruit");
7174 if (vfs_fruit_debug_level == -1) {
7175 vfs_fruit_debug_level = DBGC_VFS;
7176 DEBUG(0, ("%s: Couldn't register custom debugging class!\n",
7179 DEBUG(10, ("%s: Debug class number of '%s': %d\n",
7180 "vfs_fruit_init","fruit",vfs_fruit_debug_level));