2 * OS X and Netatalk interoperability VFS module for Samba-3.x
4 * Copyright (C) Ralph Boehme, 2013, 2014
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
21 #include "MacExtensions.h"
22 #include "smbd/smbd.h"
23 #include "system/filesys.h"
24 #include "lib/util/time.h"
25 #include "system/shmem.h"
26 #include "locking/proto.h"
27 #include "smbd/globals.h"
29 #include "libcli/security/security.h"
30 #include "../libcli/smb/smb2_create_ctx.h"
31 #include "lib/util/sys_rw.h"
32 #include "lib/util/tevent_ntstatus.h"
33 #include "lib/util/tevent_unix.h"
34 #include "offload_token.h"
35 #include "string_replace.h"
37 #include <gnutls/gnutls.h>
38 #include <gnutls/crypto.h>
41 * Enhanced OS X and Netatalk compatibility
42 * ========================================
44 * This modules takes advantage of vfs_streams_xattr and
45 * vfs_catia. VFS modules vfs_fruit and vfs_streams_xattr must be
46 * loaded in the correct order:
48 * vfs modules = catia fruit streams_xattr
50 * The module intercepts the OS X special streams "AFP_AfpInfo" and
51 * "AFP_Resource" and handles them in a special way. All other named
52 * streams are deferred to vfs_streams_xattr.
54 * The OS X client maps all NTFS illegal characters to the Unicode
55 * private range. This module optionally stores the charcters using
56 * their native ASCII encoding using vfs_catia. If you're not enabling
57 * this feature, you can skip catia from vfs modules.
59 * Finally, open modes are optionally checked against Netatalk AFP
62 * The "AFP_AfpInfo" named stream is a binary blob containing OS X
63 * extended metadata for files and directories. This module optionally
64 * reads and stores this metadata in a way compatible with Netatalk 3
65 * which stores the metadata in an EA "org.netatalk.metadata". Cf
66 * source3/include/MacExtensions.h for a description of the binary
69 * The "AFP_Resource" named stream may be arbitrarily large, thus it
70 * can't be stored in an xattr on most filesystem. ZFS on Solaris is
71 * the only available filesystem where xattrs can be of any size and
72 * the OS supports using the file APIs for xattrs.
74 * The AFP_Resource stream is stored in an AppleDouble file prepending
75 * "._" to the filename. On Solaris with ZFS the stream is optionally
76 * stored in an EA "org.netatalk.resource".
82 * The OS X SMB client sends xattrs as ADS too. For xattr interop with
83 * other protocols you may want to adjust the xattr names the VFS
84 * module vfs_streams_xattr uses for storing ADS's. This defaults to
85 * user.DosStream.ADS_NAME:$DATA and can be changed by specifying
86 * these module parameters:
88 * streams_xattr:prefix = user.
89 * streams_xattr:store_stream_type = false
95 * - log diagnostic if any needed VFS module is not loaded
96 * (eg with lp_vfs_objects())
100 static int vfs_fruit_debug_level = DBGC_VFS;
102 static struct global_fruit_config {
103 bool nego_aapl; /* client negotiated AAPL */
105 } global_fruit_config;
108 #define DBGC_CLASS vfs_fruit_debug_level
110 #define FRUIT_PARAM_TYPE_NAME "fruit"
111 #define ADOUBLE_NAME_PREFIX "._"
113 #define NETATALK_META_XATTR "org.netatalk.Metadata"
114 #define NETATALK_RSRC_XATTR "org.netatalk.ResourceFork"
116 #if defined(HAVE_ATTROPEN)
117 #define AFPINFO_EA_NETATALK NETATALK_META_XATTR
118 #define AFPRESOURCE_EA_NETATALK NETATALK_RSRC_XATTR
120 #define AFPINFO_EA_NETATALK "user." NETATALK_META_XATTR
121 #define AFPRESOURCE_EA_NETATALK "user." NETATALK_RSRC_XATTR
124 enum apple_fork {APPLE_FORK_DATA, APPLE_FORK_RSRC};
126 enum fruit_rsrc {FRUIT_RSRC_STREAM, FRUIT_RSRC_ADFILE, FRUIT_RSRC_XATTR};
127 enum fruit_meta {FRUIT_META_STREAM, FRUIT_META_NETATALK};
128 enum fruit_locking {FRUIT_LOCKING_NETATALK, FRUIT_LOCKING_NONE};
129 enum fruit_encoding {FRUIT_ENC_NATIVE, FRUIT_ENC_PRIVATE};
131 struct fruit_config_data {
132 enum fruit_rsrc rsrc;
133 enum fruit_meta meta;
134 enum fruit_locking locking;
135 enum fruit_encoding encoding;
136 bool use_aapl; /* config from smb.conf */
138 bool readdir_attr_enabled;
139 bool unix_info_enabled;
140 bool copyfile_enabled;
141 bool veto_appledouble;
143 bool aapl_zero_file_id;
146 off_t time_machine_max_size;
147 bool wipe_intentionally_left_blank_rfork;
148 bool delete_empty_adfiles;
151 * Additional options, all enabled by default,
152 * possibly useful for analyzing performance. The associated
153 * operations with each of them may be expensive, so having
154 * the chance to disable them individually gives a chance
155 * tweaking the setup for the particular usecase.
157 bool readdir_attr_rsize;
158 bool readdir_attr_finder_info;
159 bool readdir_attr_max_access;
162 static const struct enum_list fruit_rsrc[] = {
163 {FRUIT_RSRC_STREAM, "stream"}, /* pass on to vfs_streams_xattr */
164 {FRUIT_RSRC_ADFILE, "file"}, /* ._ AppleDouble file */
165 {FRUIT_RSRC_XATTR, "xattr"}, /* Netatalk compatible xattr (ZFS only) */
169 static const struct enum_list fruit_meta[] = {
170 {FRUIT_META_STREAM, "stream"}, /* pass on to vfs_streams_xattr */
171 {FRUIT_META_NETATALK, "netatalk"}, /* Netatalk compatible xattr */
175 static const struct enum_list fruit_locking[] = {
176 {FRUIT_LOCKING_NETATALK, "netatalk"}, /* synchronize locks with Netatalk */
177 {FRUIT_LOCKING_NONE, "none"},
181 static const struct enum_list fruit_encoding[] = {
182 {FRUIT_ENC_NATIVE, "native"}, /* map unicode private chars to ASCII */
183 {FRUIT_ENC_PRIVATE, "private"}, /* keep unicode private chars */
187 static const char *fruit_catia_maps =
188 "0x01:0xf001,0x02:0xf002,0x03:0xf003,0x04:0xf004,"
189 "0x05:0xf005,0x06:0xf006,0x07:0xf007,0x08:0xf008,"
190 "0x09:0xf009,0x0a:0xf00a,0x0b:0xf00b,0x0c:0xf00c,"
191 "0x0d:0xf00d,0x0e:0xf00e,0x0f:0xf00f,0x10:0xf010,"
192 "0x11:0xf011,0x12:0xf012,0x13:0xf013,0x14:0xf014,"
193 "0x15:0xf015,0x16:0xf016,0x17:0xf017,0x18:0xf018,"
194 "0x19:0xf019,0x1a:0xf01a,0x1b:0xf01b,0x1c:0xf01c,"
195 "0x1d:0xf01d,0x1e:0xf01e,0x1f:0xf01f,"
196 "0x22:0xf020,0x2a:0xf021,0x3a:0xf022,0x3c:0xf023,"
197 "0x3e:0xf024,0x3f:0xf025,0x5c:0xf026,0x7c:0xf027,"
200 /*****************************************************************************
201 * Defines, functions and data structures that deal with AppleDouble
202 *****************************************************************************/
205 * There are two AppleDouble blobs we deal with:
207 * - ADOUBLE_META - AppleDouble blob used by Netatalk for storing
208 * metadata in an xattr
210 * - ADOUBLE_RSRC - AppleDouble blob used by OS X and Netatalk in
213 typedef enum {ADOUBLE_META, ADOUBLE_RSRC} adouble_type_t;
216 #define AD_VERSION2 0x00020000
217 #define AD_VERSION AD_VERSION2
220 * AppleDouble entry IDs.
222 #define ADEID_DFORK 1
223 #define ADEID_RFORK 2
225 #define ADEID_COMMENT 4
226 #define ADEID_ICONBW 5
227 #define ADEID_ICONCOL 6
228 #define ADEID_FILEI 7
229 #define ADEID_FILEDATESI 8
230 #define ADEID_FINDERI 9
231 #define ADEID_MACFILEI 10
232 #define ADEID_PRODOSFILEI 11
233 #define ADEID_MSDOSFILEI 12
234 #define ADEID_SHORTNAME 13
235 #define ADEID_AFPFILEI 14
238 /* Private Netatalk entries */
239 #define ADEID_PRIVDEV 16
240 #define ADEID_PRIVINO 17
241 #define ADEID_PRIVSYN 18
242 #define ADEID_PRIVID 19
243 #define ADEID_MAX (ADEID_PRIVID + 1)
246 * These are the real ids for the private entries,
247 * as stored in the adouble file
249 #define AD_DEV 0x80444556
250 #define AD_INO 0x80494E4F
251 #define AD_SYN 0x8053594E
252 #define AD_ID 0x8053567E
254 /* Number of actually used entries */
255 #define ADEID_NUM_XATTR 8
256 #define ADEID_NUM_DOT_UND 2
257 #define ADEID_NUM_RSRC_XATTR 1
259 /* AppleDouble magic */
260 #define AD_APPLESINGLE_MAGIC 0x00051600
261 #define AD_APPLEDOUBLE_MAGIC 0x00051607
262 #define AD_MAGIC AD_APPLEDOUBLE_MAGIC
264 /* Sizes of relevant entry bits */
265 #define ADEDLEN_MAGIC 4
266 #define ADEDLEN_VERSION 4
267 #define ADEDLEN_FILLER 16
268 #define AD_FILLER_TAG "Netatalk " /* should be 16 bytes */
269 #define AD_FILLER_TAG_OSX "Mac OS X " /* should be 16 bytes */
270 #define ADEDLEN_NENTRIES 2
271 #define AD_HEADER_LEN (ADEDLEN_MAGIC + ADEDLEN_VERSION + \
272 ADEDLEN_FILLER + ADEDLEN_NENTRIES) /* 26 */
273 #define AD_ENTRY_LEN_EID 4
274 #define AD_ENTRY_LEN_OFF 4
275 #define AD_ENTRY_LEN_LEN 4
276 #define AD_ENTRY_LEN (AD_ENTRY_LEN_EID + AD_ENTRY_LEN_OFF + AD_ENTRY_LEN_LEN)
279 #define ADEDLEN_NAME 255
280 #define ADEDLEN_COMMENT 200
281 #define ADEDLEN_FILEI 16
282 #define ADEDLEN_FINDERI 32
283 #define ADEDLEN_FILEDATESI 16
284 #define ADEDLEN_SHORTNAME 12 /* length up to 8.3 */
285 #define ADEDLEN_AFPFILEI 4
286 #define ADEDLEN_MACFILEI 4
287 #define ADEDLEN_PRODOSFILEI 8
288 #define ADEDLEN_MSDOSFILEI 2
289 #define ADEDLEN_DID 4
290 #define ADEDLEN_PRIVDEV 8
291 #define ADEDLEN_PRIVINO 8
292 #define ADEDLEN_PRIVSYN 8
293 #define ADEDLEN_PRIVID 4
296 #define ADEDOFF_MAGIC 0
297 #define ADEDOFF_VERSION (ADEDOFF_MAGIC + ADEDLEN_MAGIC)
298 #define ADEDOFF_FILLER (ADEDOFF_VERSION + ADEDLEN_VERSION)
299 #define ADEDOFF_NENTRIES (ADEDOFF_FILLER + ADEDLEN_FILLER)
301 #define ADEDOFF_FINDERI_XATTR (AD_HEADER_LEN + \
302 (ADEID_NUM_XATTR * AD_ENTRY_LEN))
303 #define ADEDOFF_COMMENT_XATTR (ADEDOFF_FINDERI_XATTR + ADEDLEN_FINDERI)
304 #define ADEDOFF_FILEDATESI_XATTR (ADEDOFF_COMMENT_XATTR + ADEDLEN_COMMENT)
305 #define ADEDOFF_AFPFILEI_XATTR (ADEDOFF_FILEDATESI_XATTR + \
307 #define ADEDOFF_PRIVDEV_XATTR (ADEDOFF_AFPFILEI_XATTR + ADEDLEN_AFPFILEI)
308 #define ADEDOFF_PRIVINO_XATTR (ADEDOFF_PRIVDEV_XATTR + ADEDLEN_PRIVDEV)
309 #define ADEDOFF_PRIVSYN_XATTR (ADEDOFF_PRIVINO_XATTR + ADEDLEN_PRIVINO)
310 #define ADEDOFF_PRIVID_XATTR (ADEDOFF_PRIVSYN_XATTR + ADEDLEN_PRIVSYN)
312 #define ADEDOFF_FINDERI_DOT_UND (AD_HEADER_LEN + \
313 (ADEID_NUM_DOT_UND * AD_ENTRY_LEN))
314 #define ADEDOFF_RFORK_DOT_UND (ADEDOFF_FINDERI_DOT_UND + ADEDLEN_FINDERI)
316 #define AD_DATASZ_XATTR (AD_HEADER_LEN + \
317 (ADEID_NUM_XATTR * AD_ENTRY_LEN) + \
318 ADEDLEN_FINDERI + ADEDLEN_COMMENT + \
319 ADEDLEN_FILEDATESI + ADEDLEN_AFPFILEI + \
320 ADEDLEN_PRIVDEV + ADEDLEN_PRIVINO + \
321 ADEDLEN_PRIVSYN + ADEDLEN_PRIVID)
323 #if AD_DATASZ_XATTR != 402
324 #error bad size for AD_DATASZ_XATTR
327 #define AD_DATASZ_DOT_UND (AD_HEADER_LEN + \
328 (ADEID_NUM_DOT_UND * AD_ENTRY_LEN) + \
330 #if AD_DATASZ_DOT_UND != 82
331 #error bad size for AD_DATASZ_DOT_UND
335 * Sharemode locks fcntl() offsets
337 #if _FILE_OFFSET_BITS == 64 || defined(HAVE_LARGEFILE)
338 #define AD_FILELOCK_BASE (UINT64_C(0x7FFFFFFFFFFFFFFF) - 9)
340 #define AD_FILELOCK_BASE (UINT32_C(0x7FFFFFFF) - 9)
342 #define BYTELOCK_MAX (AD_FILELOCK_BASE - 1)
344 #define AD_FILELOCK_OPEN_WR (AD_FILELOCK_BASE + 0)
345 #define AD_FILELOCK_OPEN_RD (AD_FILELOCK_BASE + 1)
346 #define AD_FILELOCK_RSRC_OPEN_WR (AD_FILELOCK_BASE + 2)
347 #define AD_FILELOCK_RSRC_OPEN_RD (AD_FILELOCK_BASE + 3)
348 #define AD_FILELOCK_DENY_WR (AD_FILELOCK_BASE + 4)
349 #define AD_FILELOCK_DENY_RD (AD_FILELOCK_BASE + 5)
350 #define AD_FILELOCK_RSRC_DENY_WR (AD_FILELOCK_BASE + 6)
351 #define AD_FILELOCK_RSRC_DENY_RD (AD_FILELOCK_BASE + 7)
352 #define AD_FILELOCK_OPEN_NONE (AD_FILELOCK_BASE + 8)
353 #define AD_FILELOCK_RSRC_OPEN_NONE (AD_FILELOCK_BASE + 9)
355 /* Time stuff we overload the bits a little */
356 #define AD_DATE_CREATE 0
357 #define AD_DATE_MODIFY 4
358 #define AD_DATE_BACKUP 8
359 #define AD_DATE_ACCESS 12
360 #define AD_DATE_MASK (AD_DATE_CREATE | AD_DATE_MODIFY | \
361 AD_DATE_BACKUP | AD_DATE_ACCESS)
362 #define AD_DATE_UNIX (1 << 10)
363 #define AD_DATE_START 0x80000000
364 #define AD_DATE_DELTA 946684800
365 #define AD_DATE_FROM_UNIX(x) (htonl((x) - AD_DATE_DELTA))
366 #define AD_DATE_TO_UNIX(x) (ntohl(x) + AD_DATE_DELTA)
368 #define AD_XATTR_HDR_MAGIC 0x41545452 /* 'ATTR' */
369 #define AD_XATTR_MAX_ENTRIES 1024 /* Some arbitrarily enforced limit */
370 #define AD_XATTR_HDR_SIZE 36
371 #define AD_XATTR_MAX_HDR_SIZE 65536
373 /* Accessor macros */
374 #define ad_getentrylen(ad,eid) ((ad)->ad_eid[(eid)].ade_len)
375 #define ad_getentryoff(ad,eid) ((ad)->ad_eid[(eid)].ade_off)
376 #define ad_setentrylen(ad,eid,len) ((ad)->ad_eid[(eid)].ade_len = (len))
377 #define ad_setentryoff(ad,eid,off) ((ad)->ad_eid[(eid)].ade_off = (off))
380 * Both struct ad_xattr_header and struct ad_xattr_entry describe the in memory
381 * representation as well as the on-disk format.
383 * The ad_xattr_header follows the FinderInfo data in the FinderInfo entry if
384 * the length of the FinderInfo entry is larger then 32 bytes. It is then
385 * preceeded with 2 bytes padding.
387 * Cf: https://opensource.apple.com/source/xnu/xnu-4570.1.46/bsd/vfs/vfs_xattr.c
390 struct ad_xattr_header {
391 uint32_t adx_magic; /* ATTR_HDR_MAGIC */
392 uint32_t adx_debug_tag; /* for debugging == file id of owning file */
393 uint32_t adx_total_size; /* file offset of end of attribute header + entries + data */
394 uint32_t adx_data_start; /* file offset to attribute data area */
395 uint32_t adx_data_length; /* length of attribute data area */
396 uint32_t adx_reserved[3];
398 uint16_t adx_num_attrs;
401 /* On-disk entries are aligned on 4 byte boundaries */
402 struct ad_xattr_entry {
403 uint32_t adx_offset; /* file offset to data */
404 uint32_t adx_length; /* size of attribute data */
406 uint8_t adx_namelen; /* included the NULL terminator */
407 char *adx_name; /* NULL-terminated UTF-8 name */
418 adouble_type_t ad_type;
421 uint8_t ad_filler[ADEDLEN_FILLER];
422 struct ad_entry ad_eid[ADEID_MAX];
424 struct ad_xattr_header adx_header;
425 struct ad_xattr_entry *adx_entries;
428 struct ad_entry_order {
429 uint32_t id, offset, len;
432 /* Netatalk AppleDouble metadata xattr */
434 struct ad_entry_order entry_order_meta_xattr[ADEID_NUM_XATTR + 1] = {
435 {ADEID_FINDERI, ADEDOFF_FINDERI_XATTR, ADEDLEN_FINDERI},
436 {ADEID_COMMENT, ADEDOFF_COMMENT_XATTR, 0},
437 {ADEID_FILEDATESI, ADEDOFF_FILEDATESI_XATTR, ADEDLEN_FILEDATESI},
438 {ADEID_AFPFILEI, ADEDOFF_AFPFILEI_XATTR, ADEDLEN_AFPFILEI},
439 {ADEID_PRIVDEV, ADEDOFF_PRIVDEV_XATTR, 0},
440 {ADEID_PRIVINO, ADEDOFF_PRIVINO_XATTR, 0},
441 {ADEID_PRIVSYN, ADEDOFF_PRIVSYN_XATTR, 0},
442 {ADEID_PRIVID, ADEDOFF_PRIVID_XATTR, 0},
446 /* AppleDouble resource fork file (the ones prefixed by "._") */
448 struct ad_entry_order entry_order_dot_und[ADEID_NUM_DOT_UND + 1] = {
449 {ADEID_FINDERI, ADEDOFF_FINDERI_DOT_UND, ADEDLEN_FINDERI},
450 {ADEID_RFORK, ADEDOFF_RFORK_DOT_UND, 0},
454 /* Conversion from enumerated id to on-disk AppleDouble id */
455 #define AD_EID_DISK(a) (set_eid[a])
456 static const uint32_t set_eid[] = {
457 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
458 AD_DEV, AD_INO, AD_SYN, AD_ID
461 static char empty_resourcefork[] = {
462 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
463 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
464 0x54, 0x68, 0x69, 0x73, 0x20, 0x72, 0x65, 0x73,
465 0x6F, 0x75, 0x72, 0x63, 0x65, 0x20, 0x66, 0x6F,
466 0x72, 0x6B, 0x20, 0x69, 0x6E, 0x74, 0x65, 0x6E,
467 0x74, 0x69, 0x6F, 0x6E, 0x61, 0x6C, 0x6C, 0x79,
468 0x20, 0x6C, 0x65, 0x66, 0x74, 0x20, 0x62, 0x6C,
469 0x61, 0x6E, 0x6B, 0x20, 0x20, 0x20, 0x00, 0x00,
470 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
471 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
472 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
473 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
474 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
475 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
476 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
477 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
478 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
479 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
480 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
481 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
482 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
483 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
484 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
485 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
486 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
487 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
488 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
489 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
490 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
491 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
492 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
493 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
494 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
495 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
496 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
497 0x00, 0x1C, 0x00, 0x1E, 0xFF, 0xFF
501 /* tcon config handle */
502 struct fruit_config_data *config;
504 /* Denote stream type, meta or rsrc */
507 /* Whether the create created the stream */
511 * AFP_AfpInfo stream created, but not written yet, thus still a fake
512 * pipe fd. This is set to true in fruit_open_meta if there was no
513 * exisiting stream but the caller requested O_CREAT. It is later set to
514 * false when we get a write on the stream that then does open and
523 * Forward declarations
525 static struct adouble *ad_init(TALLOC_CTX *ctx,
526 adouble_type_t type);
527 static struct adouble *ad_get(TALLOC_CTX *ctx,
528 vfs_handle_struct *handle,
529 const struct smb_filename *smb_fname,
530 adouble_type_t type);
531 static int ad_set(vfs_handle_struct *handle,
533 const struct smb_filename *smb_fname);
534 static int ad_fset(struct vfs_handle_struct *handle,
537 static int adouble_path(TALLOC_CTX *ctx,
538 const struct smb_filename *smb_fname__in,
539 struct smb_filename **ppsmb_fname_out);
540 static AfpInfo *afpinfo_new(TALLOC_CTX *ctx);
541 static ssize_t afpinfo_pack(const AfpInfo *ai, char *buf);
542 static AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data);
546 * Return a pointer to an AppleDouble entry
548 * Returns NULL if the entry is not present
550 static char *ad_get_entry(const struct adouble *ad, int eid)
552 off_t off = ad_getentryoff(ad, eid);
553 size_t len = ad_getentrylen(ad, eid);
555 if (off == 0 || len == 0) {
559 return ad->ad_data + off;
565 static int ad_getdate(const struct adouble *ad,
566 unsigned int dateoff,
569 bool xlate = (dateoff & AD_DATE_UNIX);
572 dateoff &= AD_DATE_MASK;
573 p = ad_get_entry(ad, ADEID_FILEDATESI);
578 if (dateoff > AD_DATE_ACCESS) {
582 memcpy(date, p + dateoff, sizeof(uint32_t));
585 *date = AD_DATE_TO_UNIX(*date);
593 static int ad_setdate(struct adouble *ad, unsigned int dateoff, uint32_t date)
595 bool xlate = (dateoff & AD_DATE_UNIX);
598 p = ad_get_entry(ad, ADEID_FILEDATESI);
603 dateoff &= AD_DATE_MASK;
605 date = AD_DATE_FROM_UNIX(date);
608 if (dateoff > AD_DATE_ACCESS) {
612 memcpy(p + dateoff, &date, sizeof(date));
619 * Map on-disk AppleDouble id to enumerated id
621 static uint32_t get_eid(uint32_t eid)
629 return ADEID_PRIVDEV;
631 return ADEID_PRIVINO;
633 return ADEID_PRIVSYN;
644 * Pack AppleDouble structure into data buffer
646 static bool ad_pack(struct adouble *ad)
653 bufsize = talloc_get_size(ad->ad_data);
654 if (bufsize < AD_DATASZ_DOT_UND) {
655 DBG_ERR("bad buffer size [0x%" PRIx32 "]\n", bufsize);
659 if (offset + ADEDLEN_MAGIC < offset ||
660 offset + ADEDLEN_MAGIC >= bufsize) {
663 RSIVAL(ad->ad_data, offset, ad->ad_magic);
664 offset += ADEDLEN_MAGIC;
666 if (offset + ADEDLEN_VERSION < offset ||
667 offset + ADEDLEN_VERSION >= bufsize) {
670 RSIVAL(ad->ad_data, offset, ad->ad_version);
671 offset += ADEDLEN_VERSION;
673 if (offset + ADEDLEN_FILLER < offset ||
674 offset + ADEDLEN_FILLER >= bufsize) {
677 if (ad->ad_type == ADOUBLE_RSRC) {
678 memcpy(ad->ad_data + offset, AD_FILLER_TAG, ADEDLEN_FILLER);
680 offset += ADEDLEN_FILLER;
682 if (offset + ADEDLEN_NENTRIES < offset ||
683 offset + ADEDLEN_NENTRIES >= bufsize) {
686 offset += ADEDLEN_NENTRIES;
688 for (eid = 0, nent = 0; eid < ADEID_MAX; eid++) {
689 if (ad->ad_eid[eid].ade_off == 0) {
691 * ade_off is also used as indicator whether a
692 * specific entry is used or not
697 if (offset + AD_ENTRY_LEN_EID < offset ||
698 offset + AD_ENTRY_LEN_EID >= bufsize) {
701 RSIVAL(ad->ad_data, offset, AD_EID_DISK(eid));
702 offset += AD_ENTRY_LEN_EID;
704 if (offset + AD_ENTRY_LEN_OFF < offset ||
705 offset + AD_ENTRY_LEN_OFF >= bufsize) {
708 RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_off);
709 offset += AD_ENTRY_LEN_OFF;
711 if (offset + AD_ENTRY_LEN_LEN < offset ||
712 offset + AD_ENTRY_LEN_LEN >= bufsize) {
715 RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_len);
716 offset += AD_ENTRY_LEN_LEN;
721 if (ADEDOFF_NENTRIES + 2 >= bufsize) {
724 RSSVAL(ad->ad_data, ADEDOFF_NENTRIES, nent);
729 static bool ad_unpack_xattrs(struct adouble *ad)
731 struct ad_xattr_header *h = &ad->adx_header;
732 const char *p = ad->ad_data;
736 if (ad_getentrylen(ad, ADEID_FINDERI) <= ADEDLEN_FINDERI) {
740 /* 2 bytes padding */
741 hoff = ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI + 2;
743 h->adx_magic = RIVAL(p, hoff + 0);
744 h->adx_debug_tag = RIVAL(p, hoff + 4); /* Not used -> not checked */
745 h->adx_total_size = RIVAL(p, hoff + 8);
746 h->adx_data_start = RIVAL(p, hoff + 12);
747 h->adx_data_length = RIVAL(p, hoff + 16);
748 h->adx_flags = RSVAL(p, hoff + 32); /* Not used -> not checked */
749 h->adx_num_attrs = RSVAL(p, hoff + 34);
751 if (h->adx_magic != AD_XATTR_HDR_MAGIC) {
752 DBG_ERR("Bad magic: 0x%" PRIx32 "\n", h->adx_magic);
756 if (h->adx_total_size > ad_getentryoff(ad, ADEID_RFORK)) {
757 DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size);
760 if (h->adx_total_size > AD_XATTR_MAX_HDR_SIZE) {
761 DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size);
765 if (h->adx_data_start < (hoff + AD_XATTR_HDR_SIZE)) {
766 DBG_ERR("Bad start: 0x%" PRIx32 "\n", h->adx_data_start);
770 if ((h->adx_data_start + h->adx_data_length) < h->adx_data_start) {
771 DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length);
774 if ((h->adx_data_start + h->adx_data_length) >
775 ad->adx_header.adx_total_size)
777 DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length);
781 if (h->adx_num_attrs > AD_XATTR_MAX_ENTRIES) {
782 DBG_ERR("Bad num xattrs: %" PRIu16 "\n", h->adx_num_attrs);
786 if (h->adx_num_attrs == 0) {
790 ad->adx_entries = talloc_zero_array(
791 ad, struct ad_xattr_entry, h->adx_num_attrs);
792 if (ad->adx_entries == NULL) {
796 hoff += AD_XATTR_HDR_SIZE;
798 for (i = 0; i < h->adx_num_attrs; i++) {
799 struct ad_xattr_entry *e = &ad->adx_entries[i];
801 hoff = (hoff + 3) & ~3;
803 e->adx_offset = RIVAL(p, hoff + 0);
804 e->adx_length = RIVAL(p, hoff + 4);
805 e->adx_flags = RSVAL(p, hoff + 8);
806 e->adx_namelen = *(p + hoff + 10);
808 if (e->adx_offset >= ad->adx_header.adx_total_size) {
809 DBG_ERR("Bad adx_offset: %" PRIx32 "\n",
814 if ((e->adx_offset + e->adx_length) < e->adx_offset) {
815 DBG_ERR("Bad adx_length: %" PRIx32 "\n",
820 if ((e->adx_offset + e->adx_length) >
821 ad->adx_header.adx_total_size)
823 DBG_ERR("Bad adx_length: %" PRIx32 "\n",
828 if (e->adx_namelen == 0) {
829 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
833 if ((hoff + 11 + e->adx_namelen) < hoff + 11) {
834 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
838 if ((hoff + 11 + e->adx_namelen) >
839 ad->adx_header.adx_data_start)
841 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
846 e->adx_name = talloc_strndup(ad->adx_entries,
849 if (e->adx_name == NULL) {
853 DBG_DEBUG("xattr [%s] offset [0x%x] size [0x%x]\n",
854 e->adx_name, e->adx_offset, e->adx_length);
855 dump_data(10, (uint8_t *)(ad->ad_data + e->adx_offset),
858 hoff += 11 + e->adx_namelen;
865 * Unpack an AppleDouble blob into a struct adoble
867 static bool ad_unpack(struct adouble *ad, const size_t nentries,
870 size_t bufsize = talloc_get_size(ad->ad_data);
872 uint32_t eid, len, off;
876 * The size of the buffer ad->ad_data is checked when read, so
877 * we wouldn't have to check our own offsets, a few extra
878 * checks won't hurt though. We have to check the offsets we
879 * read from the buffer anyway.
882 if (bufsize < (AD_HEADER_LEN + (AD_ENTRY_LEN * nentries))) {
883 DEBUG(1, ("bad size\n"));
887 ad->ad_magic = RIVAL(ad->ad_data, 0);
888 ad->ad_version = RIVAL(ad->ad_data, ADEDOFF_VERSION);
889 if ((ad->ad_magic != AD_MAGIC) || (ad->ad_version != AD_VERSION)) {
890 DEBUG(1, ("wrong magic or version\n"));
894 memcpy(ad->ad_filler, ad->ad_data + ADEDOFF_FILLER, ADEDLEN_FILLER);
896 adentries = RSVAL(ad->ad_data, ADEDOFF_NENTRIES);
897 if (adentries != nentries) {
898 DEBUG(1, ("invalid number of entries: %zu\n",
903 /* now, read in the entry bits */
904 for (i = 0; i < adentries; i++) {
905 eid = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN));
907 off = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 4);
908 len = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 8);
910 if (!eid || eid >= ADEID_MAX) {
911 DEBUG(1, ("bogus eid %d\n", eid));
916 * All entries other than the resource fork are
917 * expected to be read into the ad_data buffer, so
918 * ensure the specified offset is within that bound
920 if ((off > bufsize) && (eid != ADEID_RFORK)) {
921 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
927 * All entries besides FinderInfo and resource fork
928 * must fit into the buffer. FinderInfo is special as
929 * it may be larger then the default 32 bytes (if it
930 * contains marshalled xattrs), but we will fixup that
931 * in ad_convert(). And the resource fork is never
932 * accessed directly by the ad_data buf (also see
933 * comment above) anyway.
935 if ((eid != ADEID_RFORK) &&
936 (eid != ADEID_FINDERI) &&
937 ((off + len) > bufsize)) {
938 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
944 * That would be obviously broken
946 if (off > filesize) {
947 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
953 * Check for any entry that has its end beyond the
956 if (off + len < off) {
957 DEBUG(1, ("offset wrap in eid %d: off: %" PRIu32
958 ", len: %" PRIu32 "\n",
963 if (off + len > filesize) {
965 * If this is the resource fork entry, we fix
966 * up the length, for any other entry we bail
969 if (eid != ADEID_RFORK) {
970 DEBUG(1, ("bogus eid %d: off: %" PRIu32
971 ", len: %" PRIu32 "\n",
977 * Fixup the resource fork entry by limiting
978 * the size to entryoffset - filesize.
980 len = filesize - off;
981 DEBUG(1, ("Limiting ADEID_RFORK: off: %" PRIu32
982 ", len: %" PRIu32 "\n", off, len));
985 ad->ad_eid[eid].ade_off = off;
986 ad->ad_eid[eid].ade_len = len;
989 ok = ad_unpack_xattrs(ad);
997 static bool ad_convert_move_reso(vfs_handle_struct *handle,
999 const struct smb_filename *smb_fname)
1001 char *map = MAP_FAILED;
1007 if (ad_getentrylen(ad, ADEID_RFORK) == 0) {
1011 maplen = ad_getentryoff(ad, ADEID_RFORK) +
1012 ad_getentrylen(ad, ADEID_RFORK);
1014 /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
1015 map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
1017 if (map == MAP_FAILED) {
1018 DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
1023 memmove(map + ADEDOFF_RFORK_DOT_UND,
1024 map + ad_getentryoff(ad, ADEID_RFORK),
1025 ad_getentrylen(ad, ADEID_RFORK));
1027 rc = munmap(map, maplen);
1029 DBG_ERR("munmap failed: %s\n", strerror(errno));
1033 ad_setentryoff(ad, ADEID_RFORK, ADEDOFF_RFORK_DOT_UND);
1037 DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name);
1041 len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
1042 if (len != AD_DATASZ_DOT_UND) {
1043 DBG_ERR("%s: bad size: %zd\n", smb_fname->base_name, len);
1050 static bool ad_convert_xattr(vfs_handle_struct *handle,
1052 const struct smb_filename *smb_fname,
1053 bool *converted_xattr)
1055 static struct char_mappings **string_replace_cmaps = NULL;
1056 char *map = MAP_FAILED;
1060 int saved_errno = 0;
1065 *converted_xattr = false;
1067 if (ad_getentrylen(ad, ADEID_FINDERI) == ADEDLEN_FINDERI) {
1071 if (string_replace_cmaps == NULL) {
1072 const char **mappings = NULL;
1074 mappings = str_list_make_v3_const(
1075 talloc_tos(), fruit_catia_maps, NULL);
1076 if (mappings == NULL) {
1079 string_replace_cmaps = string_replace_init_map(mappings);
1080 TALLOC_FREE(mappings);
1083 maplen = ad_getentryoff(ad, ADEID_RFORK) +
1084 ad_getentrylen(ad, ADEID_RFORK);
1086 /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
1087 map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
1089 if (map == MAP_FAILED) {
1090 DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
1094 for (i = 0; i < ad->adx_header.adx_num_attrs; i++) {
1095 struct ad_xattr_entry *e = &ad->adx_entries[i];
1096 char *mapped_name = NULL;
1098 struct smb_filename *stream_name = NULL;
1099 files_struct *fsp = NULL;
1102 status = string_replace_allocate(handle->conn,
1104 string_replace_cmaps,
1107 vfs_translate_to_windows);
1108 if (!NT_STATUS_IS_OK(status) &&
1109 !NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))
1111 DBG_ERR("string_replace_allocate failed\n");
1117 mapped_name = talloc_asprintf(talloc_tos(), ":%s", tmp);
1119 if (mapped_name == NULL) {
1124 stream_name = synthetic_smb_fname(talloc_tos(),
1125 smb_fname->base_name,
1129 TALLOC_FREE(mapped_name);
1130 if (stream_name == NULL) {
1131 DBG_ERR("synthetic_smb_fname failed\n");
1136 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1138 status = SMB_VFS_CREATE_FILE(
1139 handle->conn, /* conn */
1141 0, /* root_dir_fid */
1142 stream_name, /* fname */
1143 FILE_GENERIC_WRITE, /* access_mask */
1144 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
1145 FILE_OPEN_IF, /* create_disposition */
1146 0, /* create_options */
1147 0, /* file_attributes */
1148 INTERNAL_OPEN_ONLY, /* oplock_request */
1150 0, /* allocation_size */
1151 0, /* private_flags */
1156 NULL, NULL); /* create context */
1157 TALLOC_FREE(stream_name);
1158 if (!NT_STATUS_IS_OK(status)) {
1159 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1164 nwritten = SMB_VFS_PWRITE(fsp,
1165 map + e->adx_offset,
1168 if (nwritten == -1) {
1169 DBG_ERR("SMB_VFS_PWRITE failed\n");
1170 saved_errno = errno;
1171 close_file(NULL, fsp, ERROR_CLOSE);
1172 errno = saved_errno;
1177 status = close_file(NULL, fsp, NORMAL_CLOSE);
1178 if (!NT_STATUS_IS_OK(status)) {
1185 ad_setentrylen(ad, ADEID_FINDERI, ADEDLEN_FINDERI);
1189 DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name);
1193 len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
1194 if (len != AD_DATASZ_DOT_UND) {
1195 DBG_ERR("%s: bad size: %zd\n", smb_fname->base_name, len);
1200 ok = ad_convert_move_reso(handle, ad, smb_fname);
1205 *converted_xattr = true;
1209 rc = munmap(map, maplen);
1211 DBG_ERR("munmap failed: %s\n", strerror(errno));
1218 static bool ad_convert_finderinfo(vfs_handle_struct *handle,
1220 const struct smb_filename *smb_fname)
1225 struct smb_filename *stream_name = NULL;
1226 files_struct *fsp = NULL;
1230 int saved_errno = 0;
1233 cmp = memcmp(ad->ad_filler, AD_FILLER_TAG_OSX, ADEDLEN_FILLER);
1238 p_ad = ad_get_entry(ad, ADEID_FINDERI);
1243 ai = afpinfo_new(talloc_tos());
1248 memcpy(ai->afpi_FinderInfo, p_ad, ADEDLEN_FINDERI);
1250 aiblob = data_blob_talloc(talloc_tos(), NULL, AFP_INFO_SIZE);
1251 if (aiblob.data == NULL) {
1256 size = afpinfo_pack(ai, (char *)aiblob.data);
1258 if (size != AFP_INFO_SIZE) {
1262 stream_name = synthetic_smb_fname(talloc_tos(),
1263 smb_fname->base_name,
1267 if (stream_name == NULL) {
1268 data_blob_free(&aiblob);
1269 DBG_ERR("synthetic_smb_fname failed\n");
1273 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1275 status = SMB_VFS_CREATE_FILE(
1276 handle->conn, /* conn */
1278 0, /* root_dir_fid */
1279 stream_name, /* fname */
1280 FILE_GENERIC_WRITE, /* access_mask */
1281 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
1282 FILE_OPEN_IF, /* create_disposition */
1283 0, /* create_options */
1284 0, /* file_attributes */
1285 INTERNAL_OPEN_ONLY, /* oplock_request */
1287 0, /* allocation_size */
1288 0, /* private_flags */
1293 NULL, NULL); /* create context */
1294 TALLOC_FREE(stream_name);
1295 if (!NT_STATUS_IS_OK(status)) {
1296 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1300 nwritten = SMB_VFS_PWRITE(fsp,
1304 if (nwritten == -1) {
1305 DBG_ERR("SMB_VFS_PWRITE failed\n");
1306 saved_errno = errno;
1307 close_file(NULL, fsp, ERROR_CLOSE);
1308 errno = saved_errno;
1312 status = close_file(NULL, fsp, NORMAL_CLOSE);
1313 if (!NT_STATUS_IS_OK(status)) {
1321 static bool ad_convert_truncate(struct adouble *ad,
1322 const struct smb_filename *smb_fname)
1327 * FIXME: direct ftruncate(), but we don't have a fsp for the
1330 rc = ftruncate(ad->ad_fd, ADEDOFF_RFORK_DOT_UND +
1331 ad_getentrylen(ad, ADEID_RFORK));
1339 static bool ad_convert_blank_rfork(vfs_handle_struct *handle,
1343 struct fruit_config_data *config = NULL;
1344 uint8_t *map = MAP_FAILED;
1353 SMB_VFS_HANDLE_GET_DATA(handle, config,
1354 struct fruit_config_data, return false);
1356 if (!config->wipe_intentionally_left_blank_rfork) {
1360 if (ad_getentrylen(ad, ADEID_RFORK) != sizeof(empty_resourcefork)) {
1364 maplen = ad_getentryoff(ad, ADEID_RFORK) +
1365 ad_getentrylen(ad, ADEID_RFORK);
1367 /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
1368 map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
1370 if (map == MAP_FAILED) {
1371 DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
1375 cmp = memcmp(map + ADEDOFF_RFORK_DOT_UND,
1377 sizeof(empty_resourcefork));
1378 rc = munmap(map, maplen);
1380 DBG_ERR("munmap failed: %s\n", strerror(errno));
1388 ad_setentrylen(ad, ADEID_RFORK, 0);
1395 len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
1396 if (len != AD_DATASZ_DOT_UND) {
1404 static bool ad_convert_delete_adfile(vfs_handle_struct *handle,
1406 const struct smb_filename *smb_fname)
1408 struct fruit_config_data *config = NULL;
1409 struct smb_filename *ad_name = NULL;
1412 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
1416 SMB_VFS_HANDLE_GET_DATA(handle, config,
1417 struct fruit_config_data, return false);
1419 if (!config->delete_empty_adfiles) {
1423 rc = adouble_path(talloc_tos(), smb_fname, &ad_name);
1428 rc = SMB_VFS_NEXT_UNLINK(handle, ad_name);
1430 DBG_ERR("Unlinking [%s] failed: %s\n",
1431 smb_fname_str_dbg(ad_name), strerror(errno));
1432 TALLOC_FREE(ad_name);
1436 DBG_WARNING("Unlinked [%s] after conversion\n", smb_fname_str_dbg(ad_name));
1437 TALLOC_FREE(ad_name);
1443 * Convert from Apple's ._ file to Netatalk
1445 * Apple's AppleDouble may contain a FinderInfo entry longer then 32
1446 * bytes containing packed xattrs.
1448 * @return -1 in case an error occurred, 0 if no conversion was done, 1
1451 static int ad_convert(struct vfs_handle_struct *handle,
1452 const struct smb_filename *smb_fname)
1454 struct adouble *ad = NULL;
1456 bool converted_xattr = false;
1460 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
1465 ok = ad_convert_xattr(handle, ad, smb_fname, &converted_xattr);
1471 ok = ad_convert_blank_rfork(handle, ad, &blank);
1477 if (converted_xattr || blank) {
1478 ok = ad_convert_truncate(ad, smb_fname);
1485 ok = ad_convert_finderinfo(handle, ad, smb_fname);
1487 DBG_ERR("Failed to convert [%s]\n",
1488 smb_fname_str_dbg(smb_fname));
1493 ok = ad_convert_delete_adfile(handle, ad, smb_fname);
1506 * Read and parse Netatalk AppleDouble metadata xattr
1508 static ssize_t ad_read_meta(vfs_handle_struct *handle,
1510 const struct smb_filename *smb_fname)
1516 DEBUG(10, ("reading meta xattr for %s\n", smb_fname->base_name));
1518 ealen = SMB_VFS_GETXATTR(handle->conn, smb_fname,
1519 AFPINFO_EA_NETATALK, ad->ad_data,
1525 if (errno == ENOATTR) {
1531 DEBUG(2, ("error reading meta xattr: %s\n",
1537 if (ealen != AD_DATASZ_XATTR) {
1538 DEBUG(2, ("bad size %zd\n", ealen));
1544 /* Now parse entries */
1545 ok = ad_unpack(ad, ADEID_NUM_XATTR, AD_DATASZ_XATTR);
1547 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1553 if (!ad_getentryoff(ad, ADEID_FINDERI)
1554 || !ad_getentryoff(ad, ADEID_COMMENT)
1555 || !ad_getentryoff(ad, ADEID_FILEDATESI)
1556 || !ad_getentryoff(ad, ADEID_AFPFILEI)
1557 || !ad_getentryoff(ad, ADEID_PRIVDEV)
1558 || !ad_getentryoff(ad, ADEID_PRIVINO)
1559 || !ad_getentryoff(ad, ADEID_PRIVSYN)
1560 || !ad_getentryoff(ad, ADEID_PRIVID)) {
1561 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1568 DEBUG(10, ("reading meta xattr for %s, rc: %d\n",
1569 smb_fname->base_name, rc));
1573 if (errno == EINVAL) {
1575 (void)SMB_VFS_REMOVEXATTR(handle->conn,
1577 AFPINFO_EA_NETATALK);
1585 static int ad_open_rsrc(vfs_handle_struct *handle,
1586 const struct smb_filename *smb_fname,
1592 struct smb_filename *adp_smb_fname = NULL;
1594 ret = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
1599 fd = open(adp_smb_fname->base_name, flags, mode);
1600 TALLOC_FREE(adp_smb_fname);
1606 * Here's the deal: for ADOUBLE_META we can do without an fd as we can issue
1607 * path based xattr calls. For ADOUBLE_RSRC however we need a full-fledged fd
1608 * for file IO on the ._ file.
1610 static int ad_open(vfs_handle_struct *handle,
1613 const struct smb_filename *smb_fname,
1619 DBG_DEBUG("Path [%s] type [%s]\n", smb_fname->base_name,
1620 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
1622 if (ad->ad_type == ADOUBLE_META) {
1626 if ((fsp != NULL) && (fsp->fh != NULL) && (fsp->fh->fd != -1)) {
1627 ad->ad_fd = fsp->fh->fd;
1628 ad->ad_opened = false;
1632 fd = ad_open_rsrc(handle, smb_fname, flags, mode);
1636 ad->ad_opened = true;
1639 DBG_DEBUG("Path [%s] type [%s] fd [%d]\n",
1640 smb_fname->base_name,
1641 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc", fd);
1646 static ssize_t ad_read_rsrc_adouble(vfs_handle_struct *handle,
1648 const struct smb_filename *smb_fname)
1650 SMB_STRUCT_STAT sbuf;
1657 ret = sys_fstat(ad->ad_fd, &sbuf, lp_fake_directory_create_times(
1658 SNUM(handle->conn)));
1664 * AppleDouble file header content and size, two cases:
1666 * - without xattrs it is exactly AD_DATASZ_DOT_UND (82) bytes large
1667 * - with embedded xattrs it can be larger, up to AD_XATTR_MAX_HDR_SIZE
1669 * Read as much as we can up to AD_XATTR_MAX_HDR_SIZE.
1671 size = sbuf.st_ex_size;
1672 if (size > talloc_array_length(ad->ad_data)) {
1673 if (size > AD_XATTR_MAX_HDR_SIZE) {
1674 size = AD_XATTR_MAX_HDR_SIZE;
1676 p_ad = talloc_realloc(ad, ad->ad_data, char, size);
1683 len = sys_pread(ad->ad_fd, ad->ad_data,
1684 talloc_array_length(ad->ad_data), 0);
1685 if (len != talloc_array_length(ad->ad_data)) {
1686 DBG_NOTICE("%s %s: bad size: %zd\n",
1687 smb_fname->base_name, strerror(errno), len);
1691 /* Now parse entries */
1692 ok = ad_unpack(ad, ADEID_NUM_DOT_UND, sbuf.st_ex_size);
1694 DBG_ERR("invalid AppleDouble resource %s\n",
1695 smb_fname->base_name);
1700 if ((ad_getentryoff(ad, ADEID_FINDERI) != ADEDOFF_FINDERI_DOT_UND)
1701 || (ad_getentrylen(ad, ADEID_FINDERI) < ADEDLEN_FINDERI)
1702 || (ad_getentryoff(ad, ADEID_RFORK) < ADEDOFF_RFORK_DOT_UND)) {
1703 DBG_ERR("invalid AppleDouble resource %s\n",
1704 smb_fname->base_name);
1713 * Read and parse resource fork, either ._ AppleDouble file or xattr
1715 static ssize_t ad_read_rsrc(vfs_handle_struct *handle,
1717 const struct smb_filename *smb_fname)
1719 return ad_read_rsrc_adouble(handle, ad, smb_fname);
1723 * Read and unpack an AppleDouble metadata xattr or resource
1725 static ssize_t ad_read(vfs_handle_struct *handle,
1727 const struct smb_filename *smb_fname)
1729 switch (ad->ad_type) {
1731 return ad_read_meta(handle, ad, smb_fname);
1733 return ad_read_rsrc(handle, ad, smb_fname);
1739 static int adouble_destructor(struct adouble *ad)
1741 if ((ad->ad_fd != -1) && ad->ad_opened) {
1749 * Allocate a struct adouble without initialiing it
1751 * The struct is either hang of the fsp extension context or if fsp is
1754 * @param[in] ctx talloc context
1755 * @param[in] handle vfs handle
1756 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1758 * @return adouble handle
1760 static struct adouble *ad_alloc(TALLOC_CTX *ctx,
1761 adouble_type_t type)
1769 adsize = AD_DATASZ_XATTR;
1772 adsize = AD_DATASZ_DOT_UND;
1778 ad = talloc_zero(ctx, struct adouble);
1785 ad->ad_data = talloc_zero_array(ad, char, adsize);
1786 if (ad->ad_data == NULL) {
1793 ad->ad_magic = AD_MAGIC;
1794 ad->ad_version = AD_VERSION;
1797 talloc_set_destructor(ad, adouble_destructor);
1807 * Allocate and initialize a new struct adouble
1809 * @param[in] ctx talloc context
1810 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1812 * @return adouble handle, initialized
1814 static struct adouble *ad_init(TALLOC_CTX *ctx,
1815 adouble_type_t type)
1818 const struct ad_entry_order *eid;
1819 struct adouble *ad = NULL;
1820 time_t t = time(NULL);
1824 eid = entry_order_meta_xattr;
1827 eid = entry_order_dot_und;
1833 ad = ad_alloc(ctx, type);
1839 ad->ad_eid[eid->id].ade_off = eid->offset;
1840 ad->ad_eid[eid->id].ade_len = eid->len;
1844 /* put something sane in the date fields */
1845 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, t);
1846 ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, t);
1847 ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, t);
1848 ad_setdate(ad, AD_DATE_BACKUP, htonl(AD_DATE_START));
1856 static struct adouble *ad_get_internal(TALLOC_CTX *ctx,
1857 vfs_handle_struct *handle,
1859 const struct smb_filename *smb_fname,
1860 adouble_type_t type)
1864 struct adouble *ad = NULL;
1868 smb_fname = fsp->base_fsp->fsp_name;
1871 DEBUG(10, ("ad_get(%s) called for %s\n",
1872 type == ADOUBLE_META ? "meta" : "rsrc",
1873 smb_fname->base_name));
1875 ad = ad_alloc(ctx, type);
1881 /* Try rw first so we can use the fd in ad_convert() */
1884 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
1885 if (rc == -1 && ((errno == EROFS) || (errno == EACCES))) {
1887 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
1890 DBG_DEBUG("ad_open [%s] error [%s]\n",
1891 smb_fname->base_name, strerror(errno));
1896 len = ad_read(handle, ad, smb_fname);
1898 DEBUG(10, ("error reading AppleDouble for %s\n",
1899 smb_fname->base_name));
1905 DEBUG(10, ("ad_get(%s) for %s returning %d\n",
1906 type == ADOUBLE_META ? "meta" : "rsrc",
1907 smb_fname->base_name, rc));
1916 * Return AppleDouble data for a file
1918 * @param[in] ctx talloc context
1919 * @param[in] handle vfs handle
1920 * @param[in] smb_fname pathname to file or directory
1921 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1923 * @return talloced struct adouble or NULL on error
1925 static struct adouble *ad_get(TALLOC_CTX *ctx,
1926 vfs_handle_struct *handle,
1927 const struct smb_filename *smb_fname,
1928 adouble_type_t type)
1930 return ad_get_internal(ctx, handle, NULL, smb_fname, type);
1934 * Return AppleDouble data for a file
1936 * @param[in] ctx talloc context
1937 * @param[in] handle vfs handle
1938 * @param[in] fsp fsp to use for IO
1939 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1941 * @return talloced struct adouble or NULL on error
1943 static struct adouble *ad_fget(TALLOC_CTX *ctx, vfs_handle_struct *handle,
1944 files_struct *fsp, adouble_type_t type)
1946 return ad_get_internal(ctx, handle, fsp, NULL, type);
1950 * Set AppleDouble metadata on a file or directory
1952 * @param[in] ad adouble handle
1954 * @param[in] smb_fname pathname to file or directory
1956 * @return status code, 0 means success
1958 static int ad_set(vfs_handle_struct *handle,
1960 const struct smb_filename *smb_fname)
1965 DBG_DEBUG("Path [%s]\n", smb_fname->base_name);
1967 if (ad->ad_type != ADOUBLE_META) {
1968 DBG_ERR("ad_set on [%s] used with ADOUBLE_RSRC\n",
1969 smb_fname->base_name);
1978 ret = SMB_VFS_SETXATTR(handle->conn,
1980 AFPINFO_EA_NETATALK,
1982 AD_DATASZ_XATTR, 0);
1984 DBG_DEBUG("Path [%s] ret [%d]\n", smb_fname->base_name, ret);
1990 * Set AppleDouble metadata on a file or directory
1992 * @param[in] ad adouble handle
1993 * @param[in] fsp file handle
1995 * @return status code, 0 means success
1997 static int ad_fset(struct vfs_handle_struct *handle,
2005 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
2008 || (fsp->fh == NULL)
2009 || (fsp->fh->fd == -1))
2011 smb_panic("bad fsp");
2019 switch (ad->ad_type) {
2021 rc = SMB_VFS_NEXT_SETXATTR(handle,
2023 AFPINFO_EA_NETATALK,
2025 AD_DATASZ_XATTR, 0);
2029 len = SMB_VFS_NEXT_PWRITE(handle,
2034 if (len != AD_DATASZ_DOT_UND) {
2035 DBG_ERR("short write on %s: %zd", fsp_str_dbg(fsp), len);
2045 DBG_DEBUG("Path [%s] rc [%d]\n", fsp_str_dbg(fsp), rc);
2050 /*****************************************************************************
2052 *****************************************************************************/
2054 static bool is_afpinfo_stream(const struct smb_filename *smb_fname)
2056 if (strncasecmp_m(smb_fname->stream_name,
2057 AFPINFO_STREAM_NAME,
2058 strlen(AFPINFO_STREAM_NAME)) == 0) {
2064 static bool is_afpresource_stream(const struct smb_filename *smb_fname)
2066 if (strncasecmp_m(smb_fname->stream_name,
2067 AFPRESOURCE_STREAM_NAME,
2068 strlen(AFPRESOURCE_STREAM_NAME)) == 0) {
2075 * Test whether stream is an Apple stream.
2077 static bool is_apple_stream(const struct smb_filename *smb_fname)
2079 if (is_afpinfo_stream(smb_fname)) {
2082 if (is_afpresource_stream(smb_fname)) {
2088 static bool is_adouble_file(const char *path)
2090 const char *p = NULL;
2093 p = strrchr(path, '/');
2101 ADOUBLE_NAME_PREFIX,
2102 strlen(ADOUBLE_NAME_PREFIX));
2110 * Initialize config struct from our smb.conf config parameters
2112 static int init_fruit_config(vfs_handle_struct *handle)
2114 struct fruit_config_data *config;
2116 const char *tm_size_str = NULL;
2118 config = talloc_zero(handle->conn, struct fruit_config_data);
2120 DEBUG(1, ("talloc_zero() failed\n"));
2126 * Versions up to Samba 4.5.x had a spelling bug in the
2127 * fruit:resource option calling lp_parm_enum with
2128 * "res*s*ource" (ie two s).
2130 * In Samba 4.6 we accept both the wrong and the correct
2131 * spelling, in Samba 4.7 the bad spelling will be removed.
2133 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2134 "ressource", fruit_rsrc, FRUIT_RSRC_ADFILE);
2135 if (enumval == -1) {
2136 DEBUG(1, ("value for %s: resource type unknown\n",
2137 FRUIT_PARAM_TYPE_NAME));
2140 config->rsrc = (enum fruit_rsrc)enumval;
2142 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2143 "resource", fruit_rsrc, enumval);
2144 if (enumval == -1) {
2145 DEBUG(1, ("value for %s: resource type unknown\n",
2146 FRUIT_PARAM_TYPE_NAME));
2149 config->rsrc = (enum fruit_rsrc)enumval;
2151 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2152 "metadata", fruit_meta, FRUIT_META_NETATALK);
2153 if (enumval == -1) {
2154 DEBUG(1, ("value for %s: metadata type unknown\n",
2155 FRUIT_PARAM_TYPE_NAME));
2158 config->meta = (enum fruit_meta)enumval;
2160 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2161 "locking", fruit_locking, FRUIT_LOCKING_NONE);
2162 if (enumval == -1) {
2163 DEBUG(1, ("value for %s: locking type unknown\n",
2164 FRUIT_PARAM_TYPE_NAME));
2167 config->locking = (enum fruit_locking)enumval;
2169 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2170 "encoding", fruit_encoding, FRUIT_ENC_PRIVATE);
2171 if (enumval == -1) {
2172 DEBUG(1, ("value for %s: encoding type unknown\n",
2173 FRUIT_PARAM_TYPE_NAME));
2176 config->encoding = (enum fruit_encoding)enumval;
2178 if (config->rsrc == FRUIT_RSRC_ADFILE) {
2179 config->veto_appledouble = lp_parm_bool(SNUM(handle->conn),
2180 FRUIT_PARAM_TYPE_NAME,
2185 config->use_aapl = lp_parm_bool(
2186 -1, FRUIT_PARAM_TYPE_NAME, "aapl", true);
2188 config->time_machine = lp_parm_bool(
2189 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "time machine", false);
2191 config->unix_info_enabled = lp_parm_bool(
2192 -1, FRUIT_PARAM_TYPE_NAME, "nfs_aces", true);
2194 config->use_copyfile = lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME,
2197 config->posix_rename = lp_parm_bool(
2198 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "posix_rename", true);
2200 config->aapl_zero_file_id =
2201 lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME, "zero_file_id", true);
2203 config->readdir_attr_rsize = lp_parm_bool(
2204 SNUM(handle->conn), "readdir_attr", "aapl_rsize", true);
2206 config->readdir_attr_finder_info = lp_parm_bool(
2207 SNUM(handle->conn), "readdir_attr", "aapl_finder_info", true);
2209 config->readdir_attr_max_access = lp_parm_bool(
2210 SNUM(handle->conn), "readdir_attr", "aapl_max_access", true);
2212 config->model = lp_parm_const_string(
2213 -1, FRUIT_PARAM_TYPE_NAME, "model", "MacSamba");
2215 tm_size_str = lp_parm_const_string(
2216 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2217 "time machine max size", NULL);
2218 if (tm_size_str != NULL) {
2219 config->time_machine_max_size = conv_str_size(tm_size_str);
2222 config->wipe_intentionally_left_blank_rfork = lp_parm_bool(
2223 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2224 "wipe_intentionally_left_blank_rfork", false);
2226 config->delete_empty_adfiles = lp_parm_bool(
2227 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2228 "delete_empty_adfiles", false);
2230 SMB_VFS_HANDLE_SET_DATA(handle, config,
2231 NULL, struct fruit_config_data,
2238 * Prepend "._" to a basename
2239 * Return a new struct smb_filename with stream_name == NULL.
2241 static int adouble_path(TALLOC_CTX *ctx,
2242 const struct smb_filename *smb_fname_in,
2243 struct smb_filename **pp_smb_fname_out)
2247 struct smb_filename *smb_fname = cp_smb_filename(ctx,
2250 if (smb_fname == NULL) {
2254 /* We need streamname to be NULL */
2255 TALLOC_FREE(smb_fname->stream_name);
2257 /* And we're replacing base_name. */
2258 TALLOC_FREE(smb_fname->base_name);
2260 if (!parent_dirname(smb_fname, smb_fname_in->base_name,
2262 TALLOC_FREE(smb_fname);
2266 smb_fname->base_name = talloc_asprintf(smb_fname,
2267 "%s/._%s", parent, base);
2268 if (smb_fname->base_name == NULL) {
2269 TALLOC_FREE(smb_fname);
2273 *pp_smb_fname_out = smb_fname;
2279 * Allocate and initialize an AfpInfo struct
2281 static AfpInfo *afpinfo_new(TALLOC_CTX *ctx)
2283 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2287 ai->afpi_Signature = AFP_Signature;
2288 ai->afpi_Version = AFP_Version;
2289 ai->afpi_BackupTime = AD_DATE_START;
2294 * Pack an AfpInfo struct into a buffer
2296 * Buffer size must be at least AFP_INFO_SIZE
2297 * Returns size of packed buffer
2299 static ssize_t afpinfo_pack(const AfpInfo *ai, char *buf)
2301 memset(buf, 0, AFP_INFO_SIZE);
2303 RSIVAL(buf, 0, ai->afpi_Signature);
2304 RSIVAL(buf, 4, ai->afpi_Version);
2305 RSIVAL(buf, 12, ai->afpi_BackupTime);
2306 memcpy(buf + 16, ai->afpi_FinderInfo, sizeof(ai->afpi_FinderInfo));
2308 return AFP_INFO_SIZE;
2312 * Unpack a buffer into a AfpInfo structure
2314 * Buffer size must be at least AFP_INFO_SIZE
2315 * Returns allocated AfpInfo struct
2317 static AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data)
2319 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2324 ai->afpi_Signature = RIVAL(data, 0);
2325 ai->afpi_Version = RIVAL(data, 4);
2326 ai->afpi_BackupTime = RIVAL(data, 12);
2327 memcpy(ai->afpi_FinderInfo, (const char *)data + 16,
2328 sizeof(ai->afpi_FinderInfo));
2330 if (ai->afpi_Signature != AFP_Signature
2331 || ai->afpi_Version != AFP_Version) {
2332 DEBUG(1, ("Bad AfpInfo signature or version\n"));
2340 * Fake an inode number from the md5 hash of the (xattr) name
2342 static SMB_INO_T fruit_inode(const SMB_STRUCT_STAT *sbuf, const char *sname)
2344 gnutls_hash_hd_t hash_hnd = NULL;
2345 unsigned char hash[16];
2346 SMB_INO_T result = 0;
2350 DBG_DEBUG("fruit_inode called for %ju/%ju [%s]\n",
2351 (uintmax_t)sbuf->st_ex_dev,
2352 (uintmax_t)sbuf->st_ex_ino, sname);
2354 upper_sname = talloc_strdup_upper(talloc_tos(), sname);
2355 SMB_ASSERT(upper_sname != NULL);
2357 rc = gnutls_hash_init(&hash_hnd, GNUTLS_DIG_MD5);
2362 rc = gnutls_hash(hash_hnd, &(sbuf->st_ex_dev), sizeof(sbuf->st_ex_dev));
2364 gnutls_hash_deinit(hash_hnd, NULL);
2367 rc = gnutls_hash(hash_hnd,
2369 sizeof(sbuf->st_ex_ino));
2371 gnutls_hash_deinit(hash_hnd, NULL);
2374 rc = gnutls_hash(hash_hnd,
2376 talloc_get_size(upper_sname) - 1);
2378 gnutls_hash_deinit(hash_hnd, NULL);
2382 gnutls_hash_deinit(hash_hnd, hash);
2384 /* Hopefully all the variation is in the lower 4 (or 8) bytes! */
2385 memcpy(&result, hash, sizeof(result));
2388 DBG_DEBUG("fruit_inode \"%s\": ino=%ju\n",
2389 sname, (uintmax_t)result);
2392 TALLOC_FREE(upper_sname);
2397 static bool add_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
2398 struct stream_struct **streams,
2399 const char *name, off_t size,
2402 struct stream_struct *tmp;
2404 tmp = talloc_realloc(mem_ctx, *streams, struct stream_struct,
2410 tmp[*num_streams].name = talloc_asprintf(tmp, "%s:$DATA", name);
2411 if (tmp[*num_streams].name == NULL) {
2415 tmp[*num_streams].size = size;
2416 tmp[*num_streams].alloc_size = alloc_size;
2423 static bool filter_empty_rsrc_stream(unsigned int *num_streams,
2424 struct stream_struct **streams)
2426 struct stream_struct *tmp = *streams;
2429 if (*num_streams == 0) {
2433 for (i = 0; i < *num_streams; i++) {
2434 if (strequal_m(tmp[i].name, AFPRESOURCE_STREAM)) {
2439 if (i == *num_streams) {
2443 if (tmp[i].size > 0) {
2447 TALLOC_FREE(tmp[i].name);
2448 if (*num_streams - 1 > i) {
2449 memmove(&tmp[i], &tmp[i+1],
2450 (*num_streams - i - 1) * sizeof(struct stream_struct));
2457 static bool del_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
2458 struct stream_struct **streams,
2461 struct stream_struct *tmp = *streams;
2464 if (*num_streams == 0) {
2468 for (i = 0; i < *num_streams; i++) {
2469 if (strequal_m(tmp[i].name, name)) {
2474 if (i == *num_streams) {
2478 TALLOC_FREE(tmp[i].name);
2479 if (*num_streams - 1 > i) {
2480 memmove(&tmp[i], &tmp[i+1],
2481 (*num_streams - i - 1) * sizeof(struct stream_struct));
2488 static bool ad_empty_finderinfo(const struct adouble *ad)
2491 char emptybuf[ADEDLEN_FINDERI] = {0};
2494 fi = ad_get_entry(ad, ADEID_FINDERI);
2496 DBG_ERR("Missing FinderInfo in struct adouble [%p]\n", ad);
2500 cmp = memcmp(emptybuf, fi, ADEDLEN_FINDERI);
2504 static bool ai_empty_finderinfo(const AfpInfo *ai)
2507 char emptybuf[ADEDLEN_FINDERI] = {0};
2509 cmp = memcmp(emptybuf, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
2514 * Update btime with btime from Netatalk
2516 static void update_btime(vfs_handle_struct *handle,
2517 struct smb_filename *smb_fname)
2520 struct timespec creation_time = {0};
2522 struct fruit_config_data *config = NULL;
2524 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
2527 switch (config->meta) {
2528 case FRUIT_META_STREAM:
2530 case FRUIT_META_NETATALK:
2534 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
2538 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
2542 if (ad_getdate(ad, AD_DATE_UNIX | AD_DATE_CREATE, &t) != 0) {
2548 creation_time.tv_sec = convert_uint32_t_to_time_t(t);
2549 update_stat_ex_create_time(&smb_fname->st, creation_time);
2555 * Map an access mask to a Netatalk single byte byte range lock
2557 static off_t access_to_netatalk_brl(enum apple_fork fork_type,
2558 uint32_t access_mask)
2562 switch (access_mask) {
2563 case FILE_READ_DATA:
2564 offset = AD_FILELOCK_OPEN_RD;
2567 case FILE_WRITE_DATA:
2568 case FILE_APPEND_DATA:
2569 offset = AD_FILELOCK_OPEN_WR;
2573 offset = AD_FILELOCK_OPEN_NONE;
2577 if (fork_type == APPLE_FORK_RSRC) {
2578 if (offset == AD_FILELOCK_OPEN_NONE) {
2579 offset = AD_FILELOCK_RSRC_OPEN_NONE;
2589 * Map a deny mode to a Netatalk brl
2591 static off_t denymode_to_netatalk_brl(enum apple_fork fork_type,
2596 switch (deny_mode) {
2598 offset = AD_FILELOCK_DENY_RD;
2602 offset = AD_FILELOCK_DENY_WR;
2606 smb_panic("denymode_to_netatalk_brl: bad deny mode\n");
2609 if (fork_type == APPLE_FORK_RSRC) {
2617 * Call fcntl() with an exclusive F_GETLK request in order to
2618 * determine if there's an exisiting shared lock
2620 * @return true if the requested lock was found or any error occurred
2621 * false if the lock was not found
2623 static bool test_netatalk_lock(files_struct *fsp, off_t in_offset)
2626 off_t offset = in_offset;
2631 result = SMB_VFS_GETLOCK(fsp, &offset, &len, &type, &pid);
2632 if (result == false) {
2636 if (type != F_UNLCK) {
2643 static NTSTATUS fruit_check_access(vfs_handle_struct *handle,
2645 uint32_t access_mask,
2646 uint32_t share_mode)
2648 NTSTATUS status = NT_STATUS_OK;
2650 bool share_for_read = (share_mode & FILE_SHARE_READ);
2651 bool share_for_write = (share_mode & FILE_SHARE_WRITE);
2652 bool netatalk_already_open_for_reading = false;
2653 bool netatalk_already_open_for_writing = false;
2654 bool netatalk_already_open_with_deny_read = false;
2655 bool netatalk_already_open_with_deny_write = false;
2657 /* FIXME: hardcoded data fork, add resource fork */
2658 enum apple_fork fork_type = APPLE_FORK_DATA;
2660 DBG_DEBUG("fruit_check_access: %s, am: %s/%s, sm: 0x%x\n",
2662 access_mask & FILE_READ_DATA ? "READ" :"-",
2663 access_mask & FILE_WRITE_DATA ? "WRITE" : "-",
2666 if (fsp->fh->fd == -1) {
2667 return NT_STATUS_OK;
2670 /* Read NetATalk opens and deny modes on the file. */
2671 netatalk_already_open_for_reading = test_netatalk_lock(fsp,
2672 access_to_netatalk_brl(fork_type,
2675 netatalk_already_open_with_deny_read = test_netatalk_lock(fsp,
2676 denymode_to_netatalk_brl(fork_type,
2679 netatalk_already_open_for_writing = test_netatalk_lock(fsp,
2680 access_to_netatalk_brl(fork_type,
2683 netatalk_already_open_with_deny_write = test_netatalk_lock(fsp,
2684 denymode_to_netatalk_brl(fork_type,
2687 /* If there are any conflicts - sharing violation. */
2688 if ((access_mask & FILE_READ_DATA) &&
2689 netatalk_already_open_with_deny_read) {
2690 return NT_STATUS_SHARING_VIOLATION;
2693 if (!share_for_read &&
2694 netatalk_already_open_for_reading) {
2695 return NT_STATUS_SHARING_VIOLATION;
2698 if ((access_mask & FILE_WRITE_DATA) &&
2699 netatalk_already_open_with_deny_write) {
2700 return NT_STATUS_SHARING_VIOLATION;
2703 if (!share_for_write &&
2704 netatalk_already_open_for_writing) {
2705 return NT_STATUS_SHARING_VIOLATION;
2708 if (!(access_mask & FILE_READ_DATA)) {
2710 * Nothing we can do here, we need read access
2713 return NT_STATUS_OK;
2716 /* Set NetAtalk locks matching our access */
2717 if (access_mask & FILE_READ_DATA) {
2718 struct byte_range_lock *br_lck = NULL;
2720 off = access_to_netatalk_brl(fork_type, FILE_READ_DATA);
2722 handle->conn->sconn->msg_ctx, fsp,
2723 fsp->op->global->open_persistent_id, 1, off,
2724 READ_LOCK, POSIX_LOCK, false,
2727 TALLOC_FREE(br_lck);
2729 if (!NT_STATUS_IS_OK(status)) {
2734 if (!share_for_read) {
2735 struct byte_range_lock *br_lck = NULL;
2737 off = denymode_to_netatalk_brl(fork_type, DENY_READ);
2739 handle->conn->sconn->msg_ctx, fsp,
2740 fsp->op->global->open_persistent_id, 1, off,
2741 READ_LOCK, POSIX_LOCK, false,
2744 TALLOC_FREE(br_lck);
2746 if (!NT_STATUS_IS_OK(status)) {
2751 if (access_mask & FILE_WRITE_DATA) {
2752 struct byte_range_lock *br_lck = NULL;
2754 off = access_to_netatalk_brl(fork_type, FILE_WRITE_DATA);
2756 handle->conn->sconn->msg_ctx, fsp,
2757 fsp->op->global->open_persistent_id, 1, off,
2758 READ_LOCK, POSIX_LOCK, false,
2761 TALLOC_FREE(br_lck);
2763 if (!NT_STATUS_IS_OK(status)) {
2768 if (!share_for_write) {
2769 struct byte_range_lock *br_lck = NULL;
2771 off = denymode_to_netatalk_brl(fork_type, DENY_WRITE);
2773 handle->conn->sconn->msg_ctx, fsp,
2774 fsp->op->global->open_persistent_id, 1, off,
2775 READ_LOCK, POSIX_LOCK, false,
2778 TALLOC_FREE(br_lck);
2780 if (!NT_STATUS_IS_OK(status)) {
2785 return NT_STATUS_OK;
2788 static NTSTATUS check_aapl(vfs_handle_struct *handle,
2789 struct smb_request *req,
2790 const struct smb2_create_blobs *in_context_blobs,
2791 struct smb2_create_blobs *out_context_blobs)
2793 struct fruit_config_data *config;
2795 struct smb2_create_blob *aapl = NULL;
2799 DATA_BLOB blob = data_blob_talloc(req, NULL, 0);
2800 uint64_t req_bitmap, client_caps;
2801 uint64_t server_caps = SMB2_CRTCTX_AAPL_UNIX_BASED;
2805 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
2806 return NT_STATUS_UNSUCCESSFUL);
2808 if (!config->use_aapl
2809 || in_context_blobs == NULL
2810 || out_context_blobs == NULL) {
2811 return NT_STATUS_OK;
2814 aapl = smb2_create_blob_find(in_context_blobs,
2815 SMB2_CREATE_TAG_AAPL);
2817 return NT_STATUS_OK;
2820 if (aapl->data.length != 24) {
2821 DEBUG(1, ("unexpected AAPL ctxt length: %ju\n",
2822 (uintmax_t)aapl->data.length));
2823 return NT_STATUS_INVALID_PARAMETER;
2826 cmd = IVAL(aapl->data.data, 0);
2827 if (cmd != SMB2_CRTCTX_AAPL_SERVER_QUERY) {
2828 DEBUG(1, ("unsupported AAPL cmd: %d\n", cmd));
2829 return NT_STATUS_INVALID_PARAMETER;
2832 req_bitmap = BVAL(aapl->data.data, 8);
2833 client_caps = BVAL(aapl->data.data, 16);
2835 SIVAL(p, 0, SMB2_CRTCTX_AAPL_SERVER_QUERY);
2837 SBVAL(p, 8, req_bitmap);
2838 ok = data_blob_append(req, &blob, p, 16);
2840 return NT_STATUS_UNSUCCESSFUL;
2843 if (req_bitmap & SMB2_CRTCTX_AAPL_SERVER_CAPS) {
2844 if ((client_caps & SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR) &&
2845 (handle->conn->tcon->compat->fs_capabilities & FILE_NAMED_STREAMS)) {
2846 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR;
2847 config->readdir_attr_enabled = true;
2850 if (config->use_copyfile) {
2851 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_OSX_COPYFILE;
2852 config->copyfile_enabled = true;
2856 * The client doesn't set the flag, so we can't check
2857 * for it and just set it unconditionally
2859 if (config->unix_info_enabled) {
2860 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_NFS_ACE;
2863 SBVAL(p, 0, server_caps);
2864 ok = data_blob_append(req, &blob, p, 8);
2866 return NT_STATUS_UNSUCCESSFUL;
2870 if (req_bitmap & SMB2_CRTCTX_AAPL_VOLUME_CAPS) {
2871 int val = lp_case_sensitive(SNUM(handle->conn->tcon->compat));
2879 caps |= SMB2_CRTCTX_AAPL_CASE_SENSITIVE;
2886 if (config->time_machine) {
2887 caps |= SMB2_CRTCTX_AAPL_FULL_SYNC;
2892 ok = data_blob_append(req, &blob, p, 8);
2894 return NT_STATUS_UNSUCCESSFUL;
2898 if (req_bitmap & SMB2_CRTCTX_AAPL_MODEL_INFO) {
2899 ok = convert_string_talloc(req,
2900 CH_UNIX, CH_UTF16LE,
2901 config->model, strlen(config->model),
2904 return NT_STATUS_UNSUCCESSFUL;
2908 SIVAL(p + 4, 0, modellen);
2909 ok = data_blob_append(req, &blob, p, 8);
2912 return NT_STATUS_UNSUCCESSFUL;
2915 ok = data_blob_append(req, &blob, model, modellen);
2918 return NT_STATUS_UNSUCCESSFUL;
2922 status = smb2_create_blob_add(out_context_blobs,
2924 SMB2_CREATE_TAG_AAPL,
2926 if (NT_STATUS_IS_OK(status)) {
2927 global_fruit_config.nego_aapl = true;
2928 if (config->aapl_zero_file_id) {
2929 aapl_force_zero_file_id(handle->conn->sconn);
2936 static bool readdir_attr_meta_finderi_stream(
2937 struct vfs_handle_struct *handle,
2938 const struct smb_filename *smb_fname,
2941 struct smb_filename *stream_name = NULL;
2942 files_struct *fsp = NULL;
2947 uint8_t buf[AFP_INFO_SIZE];
2949 stream_name = synthetic_smb_fname(talloc_tos(),
2950 smb_fname->base_name,
2951 AFPINFO_STREAM_NAME,
2952 NULL, smb_fname->flags);
2953 if (stream_name == NULL) {
2957 ret = SMB_VFS_STAT(handle->conn, stream_name);
2962 status = SMB_VFS_CREATE_FILE(
2963 handle->conn, /* conn */
2965 0, /* root_dir_fid */
2966 stream_name, /* fname */
2967 FILE_READ_DATA, /* access_mask */
2968 (FILE_SHARE_READ | FILE_SHARE_WRITE | /* share_access */
2970 FILE_OPEN, /* create_disposition*/
2971 0, /* create_options */
2972 0, /* file_attributes */
2973 INTERNAL_OPEN_ONLY, /* oplock_request */
2975 0, /* allocation_size */
2976 0, /* private_flags */
2981 NULL, NULL); /* create context */
2983 TALLOC_FREE(stream_name);
2985 if (!NT_STATUS_IS_OK(status)) {
2989 nread = SMB_VFS_PREAD(fsp, &buf[0], AFP_INFO_SIZE, 0);
2990 if (nread != AFP_INFO_SIZE) {
2991 DBG_ERR("short read [%s] [%zd/%d]\n",
2992 smb_fname_str_dbg(stream_name), nread, AFP_INFO_SIZE);
2997 memcpy(&ai->afpi_FinderInfo[0], &buf[AFP_OFF_FinderInfo],
3004 close_file(NULL, fsp, NORMAL_CLOSE);
3010 static bool readdir_attr_meta_finderi_netatalk(
3011 struct vfs_handle_struct *handle,
3012 const struct smb_filename *smb_fname,
3015 struct adouble *ad = NULL;
3018 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
3023 p = ad_get_entry(ad, ADEID_FINDERI);
3025 DBG_ERR("No ADEID_FINDERI for [%s]\n", smb_fname->base_name);
3030 memcpy(&ai->afpi_FinderInfo[0], p, AFP_FinderSize);
3035 static bool readdir_attr_meta_finderi(struct vfs_handle_struct *handle,
3036 const struct smb_filename *smb_fname,
3037 struct readdir_attr_data *attr_data)
3039 struct fruit_config_data *config = NULL;
3040 uint32_t date_added;
3044 SMB_VFS_HANDLE_GET_DATA(handle, config,
3045 struct fruit_config_data,
3048 switch (config->meta) {
3049 case FRUIT_META_NETATALK:
3050 ok = readdir_attr_meta_finderi_netatalk(
3051 handle, smb_fname, &ai);
3054 case FRUIT_META_STREAM:
3055 ok = readdir_attr_meta_finderi_stream(
3056 handle, smb_fname, &ai);
3060 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3065 /* Don't bother with errors, it's likely ENOENT */
3069 if (S_ISREG(smb_fname->st.st_ex_mode)) {
3071 memcpy(&attr_data->attr_data.aapl.finder_info[0],
3072 &ai.afpi_FinderInfo[0], 4);
3074 /* finder_creator */
3075 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 4,
3076 &ai.afpi_FinderInfo[4], 4);
3080 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 8,
3081 &ai.afpi_FinderInfo[8], 2);
3083 /* finder_ext_flags */
3084 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 10,
3085 &ai.afpi_FinderInfo[24], 2);
3088 date_added = convert_time_t_to_uint32_t(
3089 smb_fname->st.st_ex_btime.tv_sec - AD_DATE_DELTA);
3091 RSIVAL(&attr_data->attr_data.aapl.finder_info[0], 12, date_added);
3096 static uint64_t readdir_attr_rfork_size_adouble(
3097 struct vfs_handle_struct *handle,
3098 const struct smb_filename *smb_fname)
3100 struct adouble *ad = NULL;
3101 uint64_t rfork_size;
3103 ad = ad_get(talloc_tos(), handle, smb_fname,
3109 rfork_size = ad_getentrylen(ad, ADEID_RFORK);
3115 static uint64_t readdir_attr_rfork_size_stream(
3116 struct vfs_handle_struct *handle,
3117 const struct smb_filename *smb_fname)
3119 struct smb_filename *stream_name = NULL;
3121 uint64_t rfork_size;
3123 stream_name = synthetic_smb_fname(talloc_tos(),
3124 smb_fname->base_name,
3125 AFPRESOURCE_STREAM_NAME,
3127 if (stream_name == NULL) {
3131 ret = SMB_VFS_STAT(handle->conn, stream_name);
3133 TALLOC_FREE(stream_name);
3137 rfork_size = stream_name->st.st_ex_size;
3138 TALLOC_FREE(stream_name);
3143 static uint64_t readdir_attr_rfork_size(struct vfs_handle_struct *handle,
3144 const struct smb_filename *smb_fname)
3146 struct fruit_config_data *config = NULL;
3147 uint64_t rfork_size;
3149 SMB_VFS_HANDLE_GET_DATA(handle, config,
3150 struct fruit_config_data,
3153 switch (config->rsrc) {
3154 case FRUIT_RSRC_ADFILE:
3155 rfork_size = readdir_attr_rfork_size_adouble(handle,
3159 case FRUIT_RSRC_XATTR:
3160 case FRUIT_RSRC_STREAM:
3161 rfork_size = readdir_attr_rfork_size_stream(handle,
3166 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3174 static NTSTATUS readdir_attr_macmeta(struct vfs_handle_struct *handle,
3175 const struct smb_filename *smb_fname,
3176 struct readdir_attr_data *attr_data)
3178 NTSTATUS status = NT_STATUS_OK;
3179 struct fruit_config_data *config = NULL;
3182 SMB_VFS_HANDLE_GET_DATA(handle, config,
3183 struct fruit_config_data,
3184 return NT_STATUS_UNSUCCESSFUL);
3187 /* Ensure we return a default value in the creation_date field */
3188 RSIVAL(&attr_data->attr_data.aapl.finder_info, 12, AD_DATE_START);
3191 * Resource fork length
3194 if (config->readdir_attr_rsize) {
3195 uint64_t rfork_size;
3197 rfork_size = readdir_attr_rfork_size(handle, smb_fname);
3198 attr_data->attr_data.aapl.rfork_size = rfork_size;
3205 if (config->readdir_attr_finder_info) {
3206 ok = readdir_attr_meta_finderi(handle, smb_fname, attr_data);
3208 status = NT_STATUS_INTERNAL_ERROR;
3215 static NTSTATUS remove_virtual_nfs_aces(struct security_descriptor *psd)
3220 if (psd->dacl == NULL) {
3221 return NT_STATUS_OK;
3224 for (i = 0; i < psd->dacl->num_aces; i++) {
3225 /* MS NFS style mode/uid/gid */
3226 int cmp = dom_sid_compare_domain(
3227 &global_sid_Unix_NFS,
3228 &psd->dacl->aces[i].trustee);
3230 /* Normal ACE entry. */
3235 * security_descriptor_dacl_del()
3236 * *must* return NT_STATUS_OK as we know
3237 * we have something to remove.
3240 status = security_descriptor_dacl_del(psd,
3241 &psd->dacl->aces[i].trustee);
3242 if (!NT_STATUS_IS_OK(status)) {
3243 DBG_WARNING("failed to remove MS NFS style ACE: %s\n",
3249 * security_descriptor_dacl_del() may delete more
3250 * then one entry subsequent to this one if the
3251 * SID matches, but we only need to ensure that
3252 * we stay looking at the same element in the array.
3256 return NT_STATUS_OK;
3259 /* Search MS NFS style ACE with UNIX mode */
3260 static NTSTATUS check_ms_nfs(vfs_handle_struct *handle,
3262 struct security_descriptor *psd,
3267 struct fruit_config_data *config = NULL;
3271 SMB_VFS_HANDLE_GET_DATA(handle, config,
3272 struct fruit_config_data,
3273 return NT_STATUS_UNSUCCESSFUL);
3275 if (!global_fruit_config.nego_aapl) {
3276 return NT_STATUS_OK;
3278 if (psd->dacl == NULL || !config->unix_info_enabled) {
3279 return NT_STATUS_OK;
3282 for (i = 0; i < psd->dacl->num_aces; i++) {
3283 if (dom_sid_compare_domain(
3284 &global_sid_Unix_NFS_Mode,
3285 &psd->dacl->aces[i].trustee) == 0) {
3286 *pmode = (mode_t)psd->dacl->aces[i].trustee.sub_auths[2];
3287 *pmode &= (S_IRWXU | S_IRWXG | S_IRWXO);
3290 DEBUG(10, ("MS NFS chmod request %s, %04o\n",
3291 fsp_str_dbg(fsp), (unsigned)(*pmode)));
3297 * Remove any incoming virtual ACE entries generated by
3298 * fruit_fget_nt_acl().
3301 return remove_virtual_nfs_aces(psd);
3304 /****************************************************************************
3306 ****************************************************************************/
3308 static int fruit_connect(vfs_handle_struct *handle,
3309 const char *service,
3313 char *list = NULL, *newlist = NULL;
3314 struct fruit_config_data *config;
3316 DEBUG(10, ("fruit_connect\n"));
3318 rc = SMB_VFS_NEXT_CONNECT(handle, service, user);
3323 rc = init_fruit_config(handle);
3328 SMB_VFS_HANDLE_GET_DATA(handle, config,
3329 struct fruit_config_data, return -1);
3331 if (config->veto_appledouble) {
3332 list = lp_veto_files(talloc_tos(), SNUM(handle->conn));
3335 if (strstr(list, "/" ADOUBLE_NAME_PREFIX "*/") == NULL) {
3336 newlist = talloc_asprintf(
3338 "%s/" ADOUBLE_NAME_PREFIX "*/",
3340 lp_do_parameter(SNUM(handle->conn),
3345 lp_do_parameter(SNUM(handle->conn),
3347 "/" ADOUBLE_NAME_PREFIX "*/");
3353 if (config->encoding == FRUIT_ENC_NATIVE) {
3354 lp_do_parameter(SNUM(handle->conn),
3359 if (config->time_machine) {
3360 DBG_NOTICE("Enabling durable handles for Time Machine "
3361 "support on [%s]\n", service);
3362 lp_do_parameter(SNUM(handle->conn), "durable handles", "yes");
3363 lp_do_parameter(SNUM(handle->conn), "kernel oplocks", "no");
3364 lp_do_parameter(SNUM(handle->conn), "kernel share modes", "no");
3365 if (!lp_strict_sync(SNUM(handle->conn))) {
3366 DBG_WARNING("Time Machine without strict sync is not "
3369 lp_do_parameter(SNUM(handle->conn), "posix locking", "no");
3375 static int fruit_fake_fd(void)
3382 * Return a valid fd, but ensure any attempt to use it returns
3383 * an error (EPIPE). Once we get a write on the handle, we open
3386 ret = pipe(pipe_fds);
3396 static int fruit_open_meta_stream(vfs_handle_struct *handle,
3397 struct smb_filename *smb_fname,
3402 struct fruit_config_data *config = NULL;
3403 struct fio *fio = NULL;
3404 int open_flags = flags & ~O_CREAT;
3407 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3409 SMB_VFS_HANDLE_GET_DATA(handle, config,
3410 struct fruit_config_data, return -1);
3412 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3413 fio->type = ADOUBLE_META;
3414 fio->config = config;
3416 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, open_flags, mode);
3421 if (!(flags & O_CREAT)) {
3422 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
3426 fd = fruit_fake_fd();
3428 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
3432 fio->fake_fd = true;
3439 static int fruit_open_meta_netatalk(vfs_handle_struct *handle,
3440 struct smb_filename *smb_fname,
3445 struct fruit_config_data *config = NULL;
3446 struct fio *fio = NULL;
3447 struct adouble *ad = NULL;
3448 bool meta_exists = false;
3451 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3453 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
3460 if (!meta_exists && !(flags & O_CREAT)) {
3465 fd = fruit_fake_fd();
3470 SMB_VFS_HANDLE_GET_DATA(handle, config,
3471 struct fruit_config_data, return -1);
3473 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3474 fio->type = ADOUBLE_META;
3475 fio->config = config;
3476 fio->fake_fd = true;
3483 static int fruit_open_meta(vfs_handle_struct *handle,
3484 struct smb_filename *smb_fname,
3485 files_struct *fsp, int flags, mode_t mode)
3488 struct fruit_config_data *config = NULL;
3490 DBG_DEBUG("path [%s]\n", smb_fname_str_dbg(smb_fname));
3492 SMB_VFS_HANDLE_GET_DATA(handle, config,
3493 struct fruit_config_data, return -1);
3495 switch (config->meta) {
3496 case FRUIT_META_STREAM:
3497 fd = fruit_open_meta_stream(handle, smb_fname,
3501 case FRUIT_META_NETATALK:
3502 fd = fruit_open_meta_netatalk(handle, smb_fname,
3507 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3511 DBG_DEBUG("path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3516 static int fruit_open_rsrc_adouble(vfs_handle_struct *handle,
3517 struct smb_filename *smb_fname,
3523 struct adouble *ad = NULL;
3524 struct smb_filename *smb_fname_base = NULL;
3525 struct fruit_config_data *config = NULL;
3528 SMB_VFS_HANDLE_GET_DATA(handle, config,
3529 struct fruit_config_data, return -1);
3531 if ((!(flags & O_CREAT)) &&
3532 S_ISDIR(fsp->base_fsp->fsp_name->st.st_ex_mode))
3534 /* sorry, but directories don't habe a resource fork */
3539 rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_base);
3544 /* We always need read/write access for the metadata header too */
3545 flags &= ~(O_RDONLY | O_WRONLY);
3548 hostfd = SMB_VFS_NEXT_OPEN(handle, smb_fname_base, fsp,
3555 if (flags & (O_CREAT | O_TRUNC)) {
3556 ad = ad_init(fsp, ADOUBLE_RSRC);
3562 fsp->fh->fd = hostfd;
3564 rc = ad_fset(handle, ad, fsp);
3575 TALLOC_FREE(smb_fname_base);
3577 DEBUG(10, ("fruit_open resource fork: rc=%d, fd=%d\n", rc, hostfd));
3579 int saved_errno = errno;
3582 * BUGBUGBUG -- we would need to call
3583 * fd_close_posix here, but we don't have a
3586 fsp->fh->fd = hostfd;
3590 errno = saved_errno;
3595 static int fruit_open_rsrc_xattr(vfs_handle_struct *handle,
3596 struct smb_filename *smb_fname,
3601 #ifdef HAVE_ATTROPEN
3604 fd = attropen(smb_fname->base_name,
3605 AFPRESOURCE_EA_NETATALK,
3620 static int fruit_open_rsrc(vfs_handle_struct *handle,
3621 struct smb_filename *smb_fname,
3622 files_struct *fsp, int flags, mode_t mode)
3625 struct fruit_config_data *config = NULL;
3626 struct fio *fio = NULL;
3628 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3630 SMB_VFS_HANDLE_GET_DATA(handle, config,
3631 struct fruit_config_data, return -1);
3633 switch (config->rsrc) {
3634 case FRUIT_RSRC_STREAM:
3635 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3638 case FRUIT_RSRC_ADFILE:
3639 fd = fruit_open_rsrc_adouble(handle, smb_fname,
3643 case FRUIT_RSRC_XATTR:
3644 fd = fruit_open_rsrc_xattr(handle, smb_fname,
3649 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3653 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3659 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3660 fio->type = ADOUBLE_RSRC;
3661 fio->config = config;
3666 static int fruit_open(vfs_handle_struct *handle,
3667 struct smb_filename *smb_fname,
3668 files_struct *fsp, int flags, mode_t mode)
3672 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3674 if (!is_ntfs_stream_smb_fname(smb_fname)) {
3675 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3678 if (is_afpinfo_stream(smb_fname)) {
3679 fd = fruit_open_meta(handle, smb_fname, fsp, flags, mode);
3680 } else if (is_afpresource_stream(smb_fname)) {
3681 fd = fruit_open_rsrc(handle, smb_fname, fsp, flags, mode);
3683 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3686 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3691 static int fruit_close_meta(vfs_handle_struct *handle,
3695 struct fruit_config_data *config = NULL;
3697 SMB_VFS_HANDLE_GET_DATA(handle, config,
3698 struct fruit_config_data, return -1);
3700 switch (config->meta) {
3701 case FRUIT_META_STREAM:
3702 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3705 case FRUIT_META_NETATALK:
3706 ret = close(fsp->fh->fd);
3711 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3719 static int fruit_close_rsrc(vfs_handle_struct *handle,
3723 struct fruit_config_data *config = NULL;
3725 SMB_VFS_HANDLE_GET_DATA(handle, config,
3726 struct fruit_config_data, return -1);
3728 switch (config->rsrc) {
3729 case FRUIT_RSRC_STREAM:
3730 case FRUIT_RSRC_ADFILE:
3731 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3734 case FRUIT_RSRC_XATTR:
3735 ret = close(fsp->fh->fd);
3740 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3747 static int fruit_close(vfs_handle_struct *handle,
3755 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(fsp->fsp_name), fd);
3757 if (!is_ntfs_stream_smb_fname(fsp->fsp_name)) {
3758 return SMB_VFS_NEXT_CLOSE(handle, fsp);
3761 if (is_afpinfo_stream(fsp->fsp_name)) {
3762 ret = fruit_close_meta(handle, fsp);
3763 } else if (is_afpresource_stream(fsp->fsp_name)) {
3764 ret = fruit_close_rsrc(handle, fsp);
3766 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3772 static int fruit_rename(struct vfs_handle_struct *handle,
3773 const struct smb_filename *smb_fname_src,
3774 const struct smb_filename *smb_fname_dst)
3777 struct fruit_config_data *config = NULL;
3778 struct smb_filename *src_adp_smb_fname = NULL;
3779 struct smb_filename *dst_adp_smb_fname = NULL;
3781 SMB_VFS_HANDLE_GET_DATA(handle, config,
3782 struct fruit_config_data, return -1);
3784 if (!VALID_STAT(smb_fname_src->st)) {
3785 DBG_ERR("Need valid stat for [%s]\n",
3786 smb_fname_str_dbg(smb_fname_src));
3790 rc = SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
3795 if ((config->rsrc != FRUIT_RSRC_ADFILE) ||
3796 (!S_ISREG(smb_fname_src->st.st_ex_mode)))
3801 rc = adouble_path(talloc_tos(), smb_fname_src, &src_adp_smb_fname);
3806 rc = adouble_path(talloc_tos(), smb_fname_dst, &dst_adp_smb_fname);
3811 DBG_DEBUG("%s -> %s\n",
3812 smb_fname_str_dbg(src_adp_smb_fname),
3813 smb_fname_str_dbg(dst_adp_smb_fname));
3815 rc = SMB_VFS_NEXT_RENAME(handle, src_adp_smb_fname, dst_adp_smb_fname);
3816 if (errno == ENOENT) {
3821 TALLOC_FREE(src_adp_smb_fname);
3822 TALLOC_FREE(dst_adp_smb_fname);
3826 static int fruit_unlink_meta_stream(vfs_handle_struct *handle,
3827 const struct smb_filename *smb_fname)
3829 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3832 static int fruit_unlink_meta_netatalk(vfs_handle_struct *handle,
3833 const struct smb_filename *smb_fname)
3835 return SMB_VFS_REMOVEXATTR(handle->conn,
3837 AFPINFO_EA_NETATALK);
3840 static int fruit_unlink_meta(vfs_handle_struct *handle,
3841 const struct smb_filename *smb_fname)
3843 struct fruit_config_data *config = NULL;
3846 SMB_VFS_HANDLE_GET_DATA(handle, config,
3847 struct fruit_config_data, return -1);
3849 switch (config->meta) {
3850 case FRUIT_META_STREAM:
3851 rc = fruit_unlink_meta_stream(handle, smb_fname);
3854 case FRUIT_META_NETATALK:
3855 rc = fruit_unlink_meta_netatalk(handle, smb_fname);
3859 DBG_ERR("Unsupported meta config [%d]\n", config->meta);
3866 static int fruit_unlink_rsrc_stream(vfs_handle_struct *handle,
3867 const struct smb_filename *smb_fname,
3872 if (!force_unlink) {
3873 struct smb_filename *smb_fname_cp = NULL;
3876 smb_fname_cp = cp_smb_filename(talloc_tos(), smb_fname);
3877 if (smb_fname_cp == NULL) {
3882 * 0 byte resource fork streams are not listed by
3883 * vfs_streaminfo, as a result stream cleanup/deletion of file
3884 * deletion doesn't remove the resourcefork stream.
3887 ret = SMB_VFS_NEXT_STAT(handle, smb_fname_cp);
3889 TALLOC_FREE(smb_fname_cp);
3890 DBG_ERR("stat [%s] failed [%s]\n",
3891 smb_fname_str_dbg(smb_fname_cp), strerror(errno));
3895 size = smb_fname_cp->st.st_ex_size;
3896 TALLOC_FREE(smb_fname_cp);
3899 /* OS X ignores resource fork stream delete requests */
3904 ret = SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3905 if ((ret != 0) && (errno == ENOENT) && force_unlink) {
3912 static int fruit_unlink_rsrc_adouble(vfs_handle_struct *handle,
3913 const struct smb_filename *smb_fname,
3917 struct adouble *ad = NULL;
3918 struct smb_filename *adp_smb_fname = NULL;
3920 if (!force_unlink) {
3921 ad = ad_get(talloc_tos(), handle, smb_fname,
3930 * 0 byte resource fork streams are not listed by
3931 * vfs_streaminfo, as a result stream cleanup/deletion of file
3932 * deletion doesn't remove the resourcefork stream.
3935 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
3936 /* OS X ignores resource fork stream delete requests */
3944 rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
3949 rc = SMB_VFS_NEXT_UNLINK(handle, adp_smb_fname);
3950 TALLOC_FREE(adp_smb_fname);
3951 if ((rc != 0) && (errno == ENOENT) && force_unlink) {
3958 static int fruit_unlink_rsrc_xattr(vfs_handle_struct *handle,
3959 const struct smb_filename *smb_fname,
3963 * OS X ignores resource fork stream delete requests, so nothing to do
3964 * here. Removing the file will remove the xattr anyway, so we don't
3965 * have to take care of removing 0 byte resource forks that could be
3971 static int fruit_unlink_rsrc(vfs_handle_struct *handle,
3972 const struct smb_filename *smb_fname,
3975 struct fruit_config_data *config = NULL;
3978 SMB_VFS_HANDLE_GET_DATA(handle, config,
3979 struct fruit_config_data, return -1);
3981 switch (config->rsrc) {
3982 case FRUIT_RSRC_STREAM:
3983 rc = fruit_unlink_rsrc_stream(handle, smb_fname, force_unlink);
3986 case FRUIT_RSRC_ADFILE:
3987 rc = fruit_unlink_rsrc_adouble(handle, smb_fname, force_unlink);
3990 case FRUIT_RSRC_XATTR:
3991 rc = fruit_unlink_rsrc_xattr(handle, smb_fname, force_unlink);
3995 DBG_ERR("Unsupported rsrc config [%d]\n", config->rsrc);
4002 static int fruit_unlink(vfs_handle_struct *handle,
4003 const struct smb_filename *smb_fname)
4006 struct fruit_config_data *config = NULL;
4007 struct smb_filename *rsrc_smb_fname = NULL;
4009 SMB_VFS_HANDLE_GET_DATA(handle, config,
4010 struct fruit_config_data, return -1);
4012 if (is_afpinfo_stream(smb_fname)) {
4013 return fruit_unlink_meta(handle, smb_fname);
4014 } else if (is_afpresource_stream(smb_fname)) {
4015 return fruit_unlink_rsrc(handle, smb_fname, false);
4016 } else if (is_ntfs_stream_smb_fname(smb_fname)) {
4017 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4018 } else if (is_adouble_file(smb_fname->base_name)) {
4019 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4023 * A request to delete the base file. Because 0 byte resource
4024 * fork streams are not listed by fruit_streaminfo,
4025 * delete_all_streams() can't remove 0 byte resource fork
4026 * streams, so we have to cleanup this here.
4028 rsrc_smb_fname = synthetic_smb_fname(talloc_tos(),
4029 smb_fname->base_name,
4030 AFPRESOURCE_STREAM_NAME,
4033 if (rsrc_smb_fname == NULL) {
4037 rc = fruit_unlink_rsrc(handle, rsrc_smb_fname, true);
4038 if ((rc != 0) && (errno != ENOENT)) {
4039 DBG_ERR("Forced unlink of [%s] failed [%s]\n",
4040 smb_fname_str_dbg(rsrc_smb_fname), strerror(errno));
4041 TALLOC_FREE(rsrc_smb_fname);
4044 TALLOC_FREE(rsrc_smb_fname);
4046 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4049 static int fruit_chmod(vfs_handle_struct *handle,
4050 const struct smb_filename *smb_fname,
4054 struct fruit_config_data *config = NULL;
4055 struct smb_filename *smb_fname_adp = NULL;
4057 rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname, mode);
4062 SMB_VFS_HANDLE_GET_DATA(handle, config,
4063 struct fruit_config_data, return -1);
4065 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4069 if (!VALID_STAT(smb_fname->st)) {
4073 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
4077 rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_adp);
4082 DEBUG(10, ("fruit_chmod: %s\n", smb_fname_adp->base_name));
4084 rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname_adp, mode);
4085 if (errno == ENOENT) {
4089 TALLOC_FREE(smb_fname_adp);
4093 static int fruit_chown(vfs_handle_struct *handle,
4094 const struct smb_filename *smb_fname,
4099 struct fruit_config_data *config = NULL;
4100 struct smb_filename *adp_smb_fname = NULL;
4102 rc = SMB_VFS_NEXT_CHOWN(handle, smb_fname, uid, gid);
4107 SMB_VFS_HANDLE_GET_DATA(handle, config,
4108 struct fruit_config_data, return -1);
4110 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4114 if (!VALID_STAT(smb_fname->st)) {
4118 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
4122 rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
4127 DEBUG(10, ("fruit_chown: %s\n", adp_smb_fname->base_name));
4129 rc = SMB_VFS_NEXT_CHOWN(handle, adp_smb_fname, uid, gid);
4130 if (errno == ENOENT) {
4135 TALLOC_FREE(adp_smb_fname);
4139 static int fruit_rmdir(struct vfs_handle_struct *handle,
4140 const struct smb_filename *smb_fname)
4144 struct fruit_config_data *config;
4146 SMB_VFS_HANDLE_GET_DATA(handle, config,
4147 struct fruit_config_data, return -1);
4149 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4154 * Due to there is no way to change bDeleteVetoFiles variable
4155 * from this module, need to clean up ourselves
4158 dh = SMB_VFS_OPENDIR(handle->conn, smb_fname, NULL, 0);
4163 while ((de = SMB_VFS_READDIR(handle->conn, dh, NULL)) != NULL) {
4164 struct adouble *ad = NULL;
4166 struct smb_filename *ad_smb_fname = NULL;
4169 if (!is_adouble_file(de->d_name)) {
4173 p = talloc_asprintf(talloc_tos(), "%s/%s",
4174 smb_fname->base_name, de->d_name);
4176 DBG_ERR("talloc_asprintf failed\n");
4180 ad_smb_fname = synthetic_smb_fname(talloc_tos(), p,
4184 if (ad_smb_fname == NULL) {
4185 DBG_ERR("synthetic_smb_fname failed\n");
4190 * Check whether it's a valid AppleDouble file, if
4191 * yes, delete it, ignore it otherwise.
4193 ad = ad_get(talloc_tos(), handle, ad_smb_fname, ADOUBLE_RSRC);
4195 TALLOC_FREE(ad_smb_fname);
4201 ret = SMB_VFS_NEXT_UNLINK(handle, ad_smb_fname);
4203 DBG_ERR("Deleting [%s] failed\n",
4204 smb_fname_str_dbg(ad_smb_fname));
4206 TALLOC_FREE(ad_smb_fname);
4211 SMB_VFS_CLOSEDIR(handle->conn, dh);
4213 return SMB_VFS_NEXT_RMDIR(handle, smb_fname);
4216 static ssize_t fruit_pread_meta_stream(vfs_handle_struct *handle,
4217 files_struct *fsp, void *data,
4218 size_t n, off_t offset)
4223 nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4224 if (nread == -1 || nread == n) {
4228 DBG_ERR("Removing [%s] after short read [%zd]\n",
4229 fsp_str_dbg(fsp), nread);
4231 ret = SMB_VFS_NEXT_UNLINK(handle, fsp->fsp_name);
4233 DBG_ERR("Removing [%s] failed\n", fsp_str_dbg(fsp));
4241 static ssize_t fruit_pread_meta_adouble(vfs_handle_struct *handle,
4242 files_struct *fsp, void *data,
4243 size_t n, off_t offset)
4246 struct adouble *ad = NULL;
4247 char afpinfo_buf[AFP_INFO_SIZE];
4251 ai = afpinfo_new(talloc_tos());
4256 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
4262 p = ad_get_entry(ad, ADEID_FINDERI);
4264 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
4269 memcpy(&ai->afpi_FinderInfo[0], p, ADEDLEN_FINDERI);
4271 nread = afpinfo_pack(ai, afpinfo_buf);
4272 if (nread != AFP_INFO_SIZE) {
4277 memcpy(data, afpinfo_buf, n);
4285 static ssize_t fruit_pread_meta(vfs_handle_struct *handle,
4286 files_struct *fsp, void *data,
4287 size_t n, off_t offset)
4289 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4294 * OS X has a off-by-1 error in the offset calculation, so we're
4295 * bug compatible here. It won't hurt, as any relevant real
4296 * world read requests from the AFP_AfpInfo stream will be
4297 * offset=0 n=60. offset is ignored anyway, see below.
4299 if ((offset < 0) || (offset >= AFP_INFO_SIZE + 1)) {
4304 DBG_ERR("Failed to fetch fsp extension");
4308 /* Yes, macOS always reads from offset 0 */
4310 to_return = MIN(n, AFP_INFO_SIZE);
4312 switch (fio->config->meta) {
4313 case FRUIT_META_STREAM:
4314 nread = fruit_pread_meta_stream(handle, fsp, data,
4318 case FRUIT_META_NETATALK:
4319 nread = fruit_pread_meta_adouble(handle, fsp, data,
4324 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
4328 if (nread == -1 && fio->created) {
4330 char afpinfo_buf[AFP_INFO_SIZE];
4332 ai = afpinfo_new(talloc_tos());
4337 nread = afpinfo_pack(ai, afpinfo_buf);
4339 if (nread != AFP_INFO_SIZE) {
4343 memcpy(data, afpinfo_buf, to_return);
4350 static ssize_t fruit_pread_rsrc_stream(vfs_handle_struct *handle,
4351 files_struct *fsp, void *data,
4352 size_t n, off_t offset)
4354 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4357 static ssize_t fruit_pread_rsrc_xattr(vfs_handle_struct *handle,
4358 files_struct *fsp, void *data,
4359 size_t n, off_t offset)
4361 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4364 static ssize_t fruit_pread_rsrc_adouble(vfs_handle_struct *handle,
4365 files_struct *fsp, void *data,
4366 size_t n, off_t offset)
4368 struct adouble *ad = NULL;
4371 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
4376 nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n,
4377 offset + ad_getentryoff(ad, ADEID_RFORK));
4383 static ssize_t fruit_pread_rsrc(vfs_handle_struct *handle,
4384 files_struct *fsp, void *data,
4385 size_t n, off_t offset)
4387 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4395 switch (fio->config->rsrc) {
4396 case FRUIT_RSRC_STREAM:
4397 nread = fruit_pread_rsrc_stream(handle, fsp, data, n, offset);
4400 case FRUIT_RSRC_ADFILE:
4401 nread = fruit_pread_rsrc_adouble(handle, fsp, data, n, offset);
4404 case FRUIT_RSRC_XATTR:
4405 nread = fruit_pread_rsrc_xattr(handle, fsp, data, n, offset);
4409 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
4416 static ssize_t fruit_pread(vfs_handle_struct *handle,
4417 files_struct *fsp, void *data,
4418 size_t n, off_t offset)
4420 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4423 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4424 fsp_str_dbg(fsp), (intmax_t)offset, n);
4427 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4430 if (fio->type == ADOUBLE_META) {
4431 nread = fruit_pread_meta(handle, fsp, data, n, offset);
4433 nread = fruit_pread_rsrc(handle, fsp, data, n, offset);
4436 DBG_DEBUG("Path [%s] nread [%zd]\n", fsp_str_dbg(fsp), nread);
4440 static bool fruit_must_handle_aio_stream(struct fio *fio)
4446 if (fio->type == ADOUBLE_META) {
4450 if ((fio->type == ADOUBLE_RSRC) &&
4451 (fio->config->rsrc == FRUIT_RSRC_ADFILE))
4459 struct fruit_pread_state {
4461 struct vfs_aio_state vfs_aio_state;
4464 static void fruit_pread_done(struct tevent_req *subreq);
4466 static struct tevent_req *fruit_pread_send(
4467 struct vfs_handle_struct *handle,
4468 TALLOC_CTX *mem_ctx,
4469 struct tevent_context *ev,
4470 struct files_struct *fsp,
4472 size_t n, off_t offset)
4474 struct tevent_req *req = NULL;
4475 struct tevent_req *subreq = NULL;
4476 struct fruit_pread_state *state = NULL;
4477 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4479 req = tevent_req_create(mem_ctx, &state,
4480 struct fruit_pread_state);
4485 if (fruit_must_handle_aio_stream(fio)) {
4486 state->nread = SMB_VFS_PREAD(fsp, data, n, offset);
4487 if (state->nread != n) {
4488 if (state->nread != -1) {
4491 tevent_req_error(req, errno);
4492 return tevent_req_post(req, ev);
4494 tevent_req_done(req);
4495 return tevent_req_post(req, ev);
4498 subreq = SMB_VFS_NEXT_PREAD_SEND(state, ev, handle, fsp,
4500 if (tevent_req_nomem(req, subreq)) {
4501 return tevent_req_post(req, ev);
4503 tevent_req_set_callback(subreq, fruit_pread_done, req);
4507 static void fruit_pread_done(struct tevent_req *subreq)
4509 struct tevent_req *req = tevent_req_callback_data(
4510 subreq, struct tevent_req);
4511 struct fruit_pread_state *state = tevent_req_data(
4512 req, struct fruit_pread_state);
4514 state->nread = SMB_VFS_PREAD_RECV(subreq, &state->vfs_aio_state);
4515 TALLOC_FREE(subreq);
4517 if (tevent_req_error(req, state->vfs_aio_state.error)) {
4520 tevent_req_done(req);
4523 static ssize_t fruit_pread_recv(struct tevent_req *req,
4524 struct vfs_aio_state *vfs_aio_state)
4526 struct fruit_pread_state *state = tevent_req_data(
4527 req, struct fruit_pread_state);
4529 if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
4533 *vfs_aio_state = state->vfs_aio_state;
4534 return state->nread;
4537 static ssize_t fruit_pwrite_meta_stream(vfs_handle_struct *handle,
4538 files_struct *fsp, const void *data,
4539 size_t n, off_t offset)
4541 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4547 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4548 fsp_str_dbg(fsp), (intmax_t)offset, n);
4557 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
4559 DBG_ERR("Close [%s] failed: %s\n",
4560 fsp_str_dbg(fsp), strerror(errno));
4565 fd = SMB_VFS_NEXT_OPEN(handle,
4571 DBG_ERR("On-demand create [%s] in write failed: %s\n",
4572 fsp_str_dbg(fsp), strerror(errno));
4576 fio->fake_fd = false;
4579 ai = afpinfo_unpack(talloc_tos(), data);
4584 if (ai_empty_finderinfo(ai)) {
4586 * Writing an all 0 blob to the metadata stream results in the
4587 * stream being removed on a macOS server. This ensures we
4588 * behave the same and it verified by the "delete AFP_AfpInfo by
4589 * writing all 0" test.
4591 ret = SMB_VFS_NEXT_FTRUNCATE(handle, fsp, 0);
4593 DBG_ERR("SMB_VFS_NEXT_FTRUNCATE on [%s] failed\n",
4598 ok = set_delete_on_close(
4601 handle->conn->session_info->security_token,
4602 handle->conn->session_info->unix_token);
4604 DBG_ERR("set_delete_on_close on [%s] failed\n",
4611 nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4612 if (nwritten != n) {
4619 static ssize_t fruit_pwrite_meta_netatalk(vfs_handle_struct *handle,
4620 files_struct *fsp, const void *data,
4621 size_t n, off_t offset)
4623 struct adouble *ad = NULL;
4629 ai = afpinfo_unpack(talloc_tos(), data);
4634 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
4636 ad = ad_init(talloc_tos(), ADOUBLE_META);
4641 p = ad_get_entry(ad, ADEID_FINDERI);
4643 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
4648 memcpy(p, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
4650 ret = ad_fset(handle, ad, fsp);
4652 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
4659 if (!ai_empty_finderinfo(ai)) {
4664 * Writing an all 0 blob to the metadata stream results in the stream
4665 * being removed on a macOS server. This ensures we behave the same and
4666 * it verified by the "delete AFP_AfpInfo by writing all 0" test.
4669 ok = set_delete_on_close(
4672 handle->conn->session_info->security_token,
4673 handle->conn->session_info->unix_token);
4675 DBG_ERR("set_delete_on_close on [%s] failed\n",
4683 static ssize_t fruit_pwrite_meta(vfs_handle_struct *handle,
4684 files_struct *fsp, const void *data,
4685 size_t n, off_t offset)
4687 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4689 uint8_t buf[AFP_INFO_SIZE];
4695 DBG_ERR("Failed to fetch fsp extension");
4704 if (offset != 0 && n < 60) {
4709 cmp = memcmp(data, "AFP", 3);
4715 if (n <= AFP_OFF_FinderInfo) {
4717 * Nothing to do here really, just return
4725 if (to_copy > AFP_INFO_SIZE) {
4726 to_copy = AFP_INFO_SIZE;
4728 memcpy(buf, data, to_copy);
4731 if (to_write != AFP_INFO_SIZE) {
4732 to_write = AFP_INFO_SIZE;
4735 switch (fio->config->meta) {
4736 case FRUIT_META_STREAM:
4737 nwritten = fruit_pwrite_meta_stream(handle,
4744 case FRUIT_META_NETATALK:
4745 nwritten = fruit_pwrite_meta_netatalk(handle,
4753 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
4757 if (nwritten != to_write) {
4762 * Return the requested amount, verified against macOS SMB server
4767 static ssize_t fruit_pwrite_rsrc_stream(vfs_handle_struct *handle,
4768 files_struct *fsp, const void *data,
4769 size_t n, off_t offset)
4771 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4774 static ssize_t fruit_pwrite_rsrc_xattr(vfs_handle_struct *handle,
4775 files_struct *fsp, const void *data,
4776 size_t n, off_t offset)
4778 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4781 static ssize_t fruit_pwrite_rsrc_adouble(vfs_handle_struct *handle,
4782 files_struct *fsp, const void *data,
4783 size_t n, off_t offset)
4785 struct adouble *ad = NULL;
4789 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
4791 DBG_ERR("ad_get [%s] failed\n", fsp_str_dbg(fsp));
4795 nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n,
4796 offset + ad_getentryoff(ad, ADEID_RFORK));
4797 if (nwritten != n) {
4798 DBG_ERR("Short write on [%s] [%zd/%zd]\n",
4799 fsp_str_dbg(fsp), nwritten, n);
4804 if ((n + offset) > ad_getentrylen(ad, ADEID_RFORK)) {
4805 ad_setentrylen(ad, ADEID_RFORK, n + offset);
4806 ret = ad_fset(handle, ad, fsp);
4808 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
4818 static ssize_t fruit_pwrite_rsrc(vfs_handle_struct *handle,
4819 files_struct *fsp, const void *data,
4820 size_t n, off_t offset)
4822 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4826 DBG_ERR("Failed to fetch fsp extension");
4830 switch (fio->config->rsrc) {
4831 case FRUIT_RSRC_STREAM:
4832 nwritten = fruit_pwrite_rsrc_stream(handle, fsp, data, n, offset);
4835 case FRUIT_RSRC_ADFILE:
4836 nwritten = fruit_pwrite_rsrc_adouble(handle, fsp, data, n, offset);
4839 case FRUIT_RSRC_XATTR:
4840 nwritten = fruit_pwrite_rsrc_xattr(handle, fsp, data, n, offset);
4844 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
4851 static ssize_t fruit_pwrite(vfs_handle_struct *handle,
4852 files_struct *fsp, const void *data,
4853 size_t n, off_t offset)
4855 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4858 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4859 fsp_str_dbg(fsp), (intmax_t)offset, n);
4862 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4865 if (fio->type == ADOUBLE_META) {
4866 nwritten = fruit_pwrite_meta(handle, fsp, data, n, offset);
4868 nwritten = fruit_pwrite_rsrc(handle, fsp, data, n, offset);
4871 DBG_DEBUG("Path [%s] nwritten=%zd\n", fsp_str_dbg(fsp), nwritten);
4875 struct fruit_pwrite_state {
4877 struct vfs_aio_state vfs_aio_state;
4880 static void fruit_pwrite_done(struct tevent_req *subreq);
4882 static struct tevent_req *fruit_pwrite_send(
4883 struct vfs_handle_struct *handle,
4884 TALLOC_CTX *mem_ctx,
4885 struct tevent_context *ev,
4886 struct files_struct *fsp,
4888 size_t n, off_t offset)
4890 struct tevent_req *req = NULL;
4891 struct tevent_req *subreq = NULL;
4892 struct fruit_pwrite_state *state = NULL;
4893 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4895 req = tevent_req_create(mem_ctx, &state,
4896 struct fruit_pwrite_state);
4901 if (fruit_must_handle_aio_stream(fio)) {
4902 state->nwritten = SMB_VFS_PWRITE(fsp, data, n, offset);
4903 if (state->nwritten != n) {
4904 if (state->nwritten != -1) {
4907 tevent_req_error(req, errno);
4908 return tevent_req_post(req, ev);
4910 tevent_req_done(req);
4911 return tevent_req_post(req, ev);
4914 subreq = SMB_VFS_NEXT_PWRITE_SEND(state, ev, handle, fsp,
4916 if (tevent_req_nomem(req, subreq)) {
4917 return tevent_req_post(req, ev);
4919 tevent_req_set_callback(subreq, fruit_pwrite_done, req);
4923 static void fruit_pwrite_done(struct tevent_req *subreq)
4925 struct tevent_req *req = tevent_req_callback_data(
4926 subreq, struct tevent_req);
4927 struct fruit_pwrite_state *state = tevent_req_data(
4928 req, struct fruit_pwrite_state);
4930 state->nwritten = SMB_VFS_PWRITE_RECV(subreq, &state->vfs_aio_state);
4931 TALLOC_FREE(subreq);
4933 if (tevent_req_error(req, state->vfs_aio_state.error)) {
4936 tevent_req_done(req);
4939 static ssize_t fruit_pwrite_recv(struct tevent_req *req,
4940 struct vfs_aio_state *vfs_aio_state)
4942 struct fruit_pwrite_state *state = tevent_req_data(
4943 req, struct fruit_pwrite_state);
4945 if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
4949 *vfs_aio_state = state->vfs_aio_state;
4950 return state->nwritten;
4954 * Helper to stat/lstat the base file of an smb_fname.
4956 static int fruit_stat_base(vfs_handle_struct *handle,
4957 struct smb_filename *smb_fname,
4960 char *tmp_stream_name;
4963 tmp_stream_name = smb_fname->stream_name;
4964 smb_fname->stream_name = NULL;
4966 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
4968 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
4970 smb_fname->stream_name = tmp_stream_name;
4972 DBG_DEBUG("fruit_stat_base [%s] dev [%ju] ino [%ju]\n",
4973 smb_fname->base_name,
4974 (uintmax_t)smb_fname->st.st_ex_dev,
4975 (uintmax_t)smb_fname->st.st_ex_ino);
4979 static int fruit_stat_meta_stream(vfs_handle_struct *handle,
4980 struct smb_filename *smb_fname,
4986 ret = fruit_stat_base(handle, smb_fname, false);
4991 ino = fruit_inode(&smb_fname->st, smb_fname->stream_name);
4994 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
4996 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
4999 smb_fname->st.st_ex_ino = ino;
5004 static int fruit_stat_meta_netatalk(vfs_handle_struct *handle,
5005 struct smb_filename *smb_fname,
5008 struct adouble *ad = NULL;
5010 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5012 DBG_INFO("fruit_stat_meta %s: %s\n",
5013 smb_fname_str_dbg(smb_fname), strerror(errno));
5019 /* Populate the stat struct with info from the base file. */
5020 if (fruit_stat_base(handle, smb_fname, follow_links) == -1) {
5023 smb_fname->st.st_ex_size = AFP_INFO_SIZE;
5024 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5025 smb_fname->stream_name);
5029 static int fruit_stat_meta(vfs_handle_struct *handle,
5030 struct smb_filename *smb_fname,
5033 struct fruit_config_data *config = NULL;
5036 SMB_VFS_HANDLE_GET_DATA(handle, config,
5037 struct fruit_config_data, return -1);
5039 switch (config->meta) {
5040 case FRUIT_META_STREAM:
5041 ret = fruit_stat_meta_stream(handle, smb_fname, follow_links);
5044 case FRUIT_META_NETATALK:
5045 ret = fruit_stat_meta_netatalk(handle, smb_fname, follow_links);
5049 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
5056 static int fruit_stat_rsrc_netatalk(vfs_handle_struct *handle,
5057 struct smb_filename *smb_fname,
5060 struct adouble *ad = NULL;
5063 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
5069 /* Populate the stat struct with info from the base file. */
5070 ret = fruit_stat_base(handle, smb_fname, follow_links);
5076 smb_fname->st.st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
5077 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5078 smb_fname->stream_name);
5083 static int fruit_stat_rsrc_stream(vfs_handle_struct *handle,
5084 struct smb_filename *smb_fname,
5090 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
5092 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5098 static int fruit_stat_rsrc_xattr(vfs_handle_struct *handle,
5099 struct smb_filename *smb_fname,
5102 #ifdef HAVE_ATTROPEN
5106 /* Populate the stat struct with info from the base file. */
5107 ret = fruit_stat_base(handle, smb_fname, follow_links);
5112 fd = attropen(smb_fname->base_name,
5113 AFPRESOURCE_EA_NETATALK,
5119 ret = sys_fstat(fd, &smb_fname->st, false);
5122 DBG_ERR("fstat [%s:%s] failed\n", smb_fname->base_name,
5123 AFPRESOURCE_EA_NETATALK);
5129 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5130 smb_fname->stream_name);
5140 static int fruit_stat_rsrc(vfs_handle_struct *handle,
5141 struct smb_filename *smb_fname,
5144 struct fruit_config_data *config = NULL;
5147 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
5149 SMB_VFS_HANDLE_GET_DATA(handle, config,
5150 struct fruit_config_data, return -1);
5152 switch (config->rsrc) {
5153 case FRUIT_RSRC_STREAM:
5154 ret = fruit_stat_rsrc_stream(handle, smb_fname, follow_links);
5157 case FRUIT_RSRC_XATTR:
5158 ret = fruit_stat_rsrc_xattr(handle, smb_fname, follow_links);
5161 case FRUIT_RSRC_ADFILE:
5162 ret = fruit_stat_rsrc_netatalk(handle, smb_fname, follow_links);
5166 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
5173 static int fruit_stat(vfs_handle_struct *handle,
5174 struct smb_filename *smb_fname)
5178 DEBUG(10, ("fruit_stat called for %s\n",
5179 smb_fname_str_dbg(smb_fname)));
5181 if (!is_ntfs_stream_smb_fname(smb_fname)
5182 || is_ntfs_default_stream_smb_fname(smb_fname)) {
5183 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
5185 update_btime(handle, smb_fname);
5191 * Note if lp_posix_paths() is true, we can never
5192 * get here as is_ntfs_stream_smb_fname() is
5193 * always false. So we never need worry about
5194 * not following links here.
5197 if (is_afpinfo_stream(smb_fname)) {
5198 rc = fruit_stat_meta(handle, smb_fname, true);
5199 } else if (is_afpresource_stream(smb_fname)) {
5200 rc = fruit_stat_rsrc(handle, smb_fname, true);
5202 return SMB_VFS_NEXT_STAT(handle, smb_fname);
5206 update_btime(handle, smb_fname);
5207 smb_fname->st.st_ex_mode &= ~S_IFMT;
5208 smb_fname->st.st_ex_mode |= S_IFREG;
5209 smb_fname->st.st_ex_blocks =
5210 smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
5215 static int fruit_lstat(vfs_handle_struct *handle,
5216 struct smb_filename *smb_fname)
5220 DEBUG(10, ("fruit_lstat called for %s\n",
5221 smb_fname_str_dbg(smb_fname)));
5223 if (!is_ntfs_stream_smb_fname(smb_fname)
5224 || is_ntfs_default_stream_smb_fname(smb_fname)) {
5225 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5227 update_btime(handle, smb_fname);
5232 if (is_afpinfo_stream(smb_fname)) {
5233 rc = fruit_stat_meta(handle, smb_fname, false);
5234 } else if (is_afpresource_stream(smb_fname)) {
5235 rc = fruit_stat_rsrc(handle, smb_fname, false);
5237 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5241 update_btime(handle, smb_fname);
5242 smb_fname->st.st_ex_mode &= ~S_IFMT;
5243 smb_fname->st.st_ex_mode |= S_IFREG;
5244 smb_fname->st.st_ex_blocks =
5245 smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
5250 static int fruit_fstat_meta_stream(vfs_handle_struct *handle,
5252 SMB_STRUCT_STAT *sbuf)
5254 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5255 struct smb_filename smb_fname;
5264 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5269 *sbuf = fsp->base_fsp->fsp_name->st;
5270 sbuf->st_ex_size = AFP_INFO_SIZE;
5271 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5275 smb_fname = (struct smb_filename) {
5276 .base_name = fsp->fsp_name->base_name,
5279 ret = fruit_stat_base(handle, &smb_fname, false);
5283 *sbuf = smb_fname.st;
5285 ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5287 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5292 sbuf->st_ex_ino = ino;
5296 static int fruit_fstat_meta_netatalk(vfs_handle_struct *handle,
5298 SMB_STRUCT_STAT *sbuf)
5302 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5307 *sbuf = fsp->base_fsp->fsp_name->st;
5308 sbuf->st_ex_size = AFP_INFO_SIZE;
5309 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5314 static int fruit_fstat_meta(vfs_handle_struct *handle,
5316 SMB_STRUCT_STAT *sbuf,
5321 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
5323 switch (fio->config->meta) {
5324 case FRUIT_META_STREAM:
5325 ret = fruit_fstat_meta_stream(handle, fsp, sbuf);
5328 case FRUIT_META_NETATALK:
5329 ret = fruit_fstat_meta_netatalk(handle, fsp, sbuf);
5333 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
5337 DBG_DEBUG("Path [%s] ret [%d]\n", fsp_str_dbg(fsp), ret);
5341 static int fruit_fstat_rsrc_xattr(vfs_handle_struct *handle,
5343 SMB_STRUCT_STAT *sbuf)
5345 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5348 static int fruit_fstat_rsrc_stream(vfs_handle_struct *handle,
5350 SMB_STRUCT_STAT *sbuf)
5352 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5355 static int fruit_fstat_rsrc_adouble(vfs_handle_struct *handle,
5357 SMB_STRUCT_STAT *sbuf)
5359 struct adouble *ad = NULL;
5362 /* Populate the stat struct with info from the base file. */
5363 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5368 ad = ad_get(talloc_tos(), handle,
5369 fsp->base_fsp->fsp_name,
5372 DBG_ERR("ad_get [%s] failed [%s]\n",
5373 fsp_str_dbg(fsp), strerror(errno));
5377 *sbuf = fsp->base_fsp->fsp_name->st;
5378 sbuf->st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
5379 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5385 static int fruit_fstat_rsrc(vfs_handle_struct *handle, files_struct *fsp,
5386 SMB_STRUCT_STAT *sbuf, struct fio *fio)
5390 switch (fio->config->rsrc) {
5391 case FRUIT_RSRC_STREAM:
5392 ret = fruit_fstat_rsrc_stream(handle, fsp, sbuf);
5395 case FRUIT_RSRC_ADFILE:
5396 ret = fruit_fstat_rsrc_adouble(handle, fsp, sbuf);
5399 case FRUIT_RSRC_XATTR:
5400 ret = fruit_fstat_rsrc_xattr(handle, fsp, sbuf);
5404 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
5411 static int fruit_fstat(vfs_handle_struct *handle, files_struct *fsp,
5412 SMB_STRUCT_STAT *sbuf)
5414 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5418 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5421 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
5423 if (fio->type == ADOUBLE_META) {
5424 rc = fruit_fstat_meta(handle, fsp, sbuf, fio);
5426 rc = fruit_fstat_rsrc(handle, fsp, sbuf, fio);
5430 sbuf->st_ex_mode &= ~S_IFMT;
5431 sbuf->st_ex_mode |= S_IFREG;
5432 sbuf->st_ex_blocks = sbuf->st_ex_size / STAT_ST_BLOCKSIZE + 1;
5435 DBG_DEBUG("Path [%s] rc [%d] size [%"PRIdMAX"]\n",
5436 fsp_str_dbg(fsp), rc, (intmax_t)sbuf->st_ex_size);
5440 static NTSTATUS delete_invalid_meta_stream(
5441 vfs_handle_struct *handle,
5442 const struct smb_filename *smb_fname,
5443 TALLOC_CTX *mem_ctx,
5444 unsigned int *pnum_streams,
5445 struct stream_struct **pstreams,
5448 struct smb_filename *sname = NULL;
5452 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams, AFPINFO_STREAM);
5454 return NT_STATUS_INTERNAL_ERROR;
5458 return NT_STATUS_OK;
5461 sname = synthetic_smb_fname(talloc_tos(),
5462 smb_fname->base_name,
5463 AFPINFO_STREAM_NAME,
5465 if (sname == NULL) {
5466 return NT_STATUS_NO_MEMORY;
5469 ret = SMB_VFS_NEXT_UNLINK(handle, sname);
5472 DBG_ERR("Removing [%s] failed\n", smb_fname_str_dbg(sname));
5473 return map_nt_error_from_unix(errno);
5476 return NT_STATUS_OK;
5479 static NTSTATUS fruit_streaminfo_meta_stream(
5480 vfs_handle_struct *handle,
5481 struct files_struct *fsp,
5482 const struct smb_filename *smb_fname,
5483 TALLOC_CTX *mem_ctx,
5484 unsigned int *pnum_streams,
5485 struct stream_struct **pstreams)
5487 struct stream_struct *stream = *pstreams;
5488 unsigned int num_streams = *pnum_streams;
5491 for (i = 0; i < num_streams; i++) {
5492 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
5497 if (i == num_streams) {
5498 return NT_STATUS_OK;
5501 if (stream[i].size != AFP_INFO_SIZE) {
5502 DBG_ERR("Removing invalid AFPINFO_STREAM size [%jd] from [%s]\n",
5503 (intmax_t)stream[i].size, smb_fname_str_dbg(smb_fname));
5505 return delete_invalid_meta_stream(handle,
5514 return NT_STATUS_OK;
5517 static NTSTATUS fruit_streaminfo_meta_netatalk(
5518 vfs_handle_struct *handle,
5519 struct files_struct *fsp,
5520 const struct smb_filename *smb_fname,
5521 TALLOC_CTX *mem_ctx,
5522 unsigned int *pnum_streams,
5523 struct stream_struct **pstreams)
5525 struct stream_struct *stream = *pstreams;
5526 unsigned int num_streams = *pnum_streams;
5527 struct adouble *ad = NULL;
5532 /* Remove the Netatalk xattr from the list */
5533 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5534 ":" NETATALK_META_XATTR ":$DATA");
5536 return NT_STATUS_NO_MEMORY;
5540 * Check if there's a AFPINFO_STREAM from the VFS streams
5541 * backend and if yes, remove it from the list
5543 for (i = 0; i < num_streams; i++) {
5544 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
5549 if (i < num_streams) {
5550 DBG_WARNING("Unexpected AFPINFO_STREAM on [%s]\n",
5551 smb_fname_str_dbg(smb_fname));
5553 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5556 return NT_STATUS_INTERNAL_ERROR;
5560 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5562 return NT_STATUS_OK;
5565 is_fi_empty = ad_empty_finderinfo(ad);
5569 return NT_STATUS_OK;
5572 ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
5573 AFPINFO_STREAM_NAME, AFP_INFO_SIZE,
5574 smb_roundup(handle->conn, AFP_INFO_SIZE));
5576 return NT_STATUS_NO_MEMORY;
5579 return NT_STATUS_OK;
5582 static NTSTATUS fruit_streaminfo_meta(vfs_handle_struct *handle,
5583 struct files_struct *fsp,
5584 const struct smb_filename *smb_fname,
5585 TALLOC_CTX *mem_ctx,
5586 unsigned int *pnum_streams,
5587 struct stream_struct **pstreams)
5589 struct fruit_config_data *config = NULL;
5592 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5593 return NT_STATUS_INTERNAL_ERROR);
5595 switch (config->meta) {
5596 case FRUIT_META_NETATALK:
5597 status = fruit_streaminfo_meta_netatalk(handle, fsp, smb_fname,
5598 mem_ctx, pnum_streams,
5602 case FRUIT_META_STREAM:
5603 status = fruit_streaminfo_meta_stream(handle, fsp, smb_fname,
5604 mem_ctx, pnum_streams,
5609 return NT_STATUS_INTERNAL_ERROR;
5615 static NTSTATUS fruit_streaminfo_rsrc_stream(
5616 vfs_handle_struct *handle,
5617 struct files_struct *fsp,
5618 const struct smb_filename *smb_fname,
5619 TALLOC_CTX *mem_ctx,
5620 unsigned int *pnum_streams,
5621 struct stream_struct **pstreams)
5625 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
5627 DBG_ERR("Filtering resource stream failed\n");
5628 return NT_STATUS_INTERNAL_ERROR;
5630 return NT_STATUS_OK;
5633 static NTSTATUS fruit_streaminfo_rsrc_xattr(
5634 vfs_handle_struct *handle,
5635 struct files_struct *fsp,
5636 const struct smb_filename *smb_fname,
5637 TALLOC_CTX *mem_ctx,
5638 unsigned int *pnum_streams,
5639 struct stream_struct **pstreams)
5643 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
5645 DBG_ERR("Filtering resource stream failed\n");
5646 return NT_STATUS_INTERNAL_ERROR;
5648 return NT_STATUS_OK;
5651 static NTSTATUS fruit_streaminfo_rsrc_adouble(
5652 vfs_handle_struct *handle,
5653 struct files_struct *fsp,
5654 const struct smb_filename *smb_fname,
5655 TALLOC_CTX *mem_ctx,
5656 unsigned int *pnum_streams,
5657 struct stream_struct **pstreams)
5659 struct stream_struct *stream = *pstreams;
5660 unsigned int num_streams = *pnum_streams;
5661 struct adouble *ad = NULL;
5667 * Check if there's a AFPRESOURCE_STREAM from the VFS streams backend
5668 * and if yes, remove it from the list
5670 for (i = 0; i < num_streams; i++) {
5671 if (strequal_m(stream[i].name, AFPRESOURCE_STREAM)) {
5676 if (i < num_streams) {
5677 DBG_WARNING("Unexpected AFPRESOURCE_STREAM on [%s]\n",
5678 smb_fname_str_dbg(smb_fname));
5680 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5681 AFPRESOURCE_STREAM);
5683 return NT_STATUS_INTERNAL_ERROR;
5687 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
5689 return NT_STATUS_OK;
5692 rlen = ad_getentrylen(ad, ADEID_RFORK);
5696 return NT_STATUS_OK;
5699 ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
5700 AFPRESOURCE_STREAM_NAME, rlen,
5701 smb_roundup(handle->conn, rlen));
5703 return NT_STATUS_NO_MEMORY;
5706 return NT_STATUS_OK;
5709 static NTSTATUS fruit_streaminfo_rsrc(vfs_handle_struct *handle,
5710 struct files_struct *fsp,
5711 const struct smb_filename *smb_fname,
5712 TALLOC_CTX *mem_ctx,
5713 unsigned int *pnum_streams,
5714 struct stream_struct **pstreams)
5716 struct fruit_config_data *config = NULL;
5719 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5720 return NT_STATUS_INTERNAL_ERROR);
5722 switch (config->rsrc) {
5723 case FRUIT_RSRC_STREAM:
5724 status = fruit_streaminfo_rsrc_stream(handle, fsp, smb_fname,
5725 mem_ctx, pnum_streams,
5729 case FRUIT_RSRC_XATTR:
5730 status = fruit_streaminfo_rsrc_xattr(handle, fsp, smb_fname,
5731 mem_ctx, pnum_streams,
5735 case FRUIT_RSRC_ADFILE:
5736 status = fruit_streaminfo_rsrc_adouble(handle, fsp, smb_fname,
5737 mem_ctx, pnum_streams,
5742 return NT_STATUS_INTERNAL_ERROR;
5748 static void fruit_filter_empty_streams(unsigned int *pnum_streams,
5749 struct stream_struct **pstreams)
5751 unsigned num_streams = *pnum_streams;
5752 struct stream_struct *streams = *pstreams;
5755 if (!global_fruit_config.nego_aapl) {
5759 while (i < num_streams) {
5760 struct smb_filename smb_fname = (struct smb_filename) {
5761 .stream_name = streams[i].name,
5764 if (is_ntfs_default_stream_smb_fname(&smb_fname)
5765 || streams[i].size > 0)
5771 streams[i] = streams[num_streams - 1];
5775 *pnum_streams = num_streams;
5778 static NTSTATUS fruit_streaminfo(vfs_handle_struct *handle,
5779 struct files_struct *fsp,
5780 const struct smb_filename *smb_fname,
5781 TALLOC_CTX *mem_ctx,
5782 unsigned int *pnum_streams,
5783 struct stream_struct **pstreams)
5785 struct fruit_config_data *config = NULL;
5788 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5789 return NT_STATUS_UNSUCCESSFUL);
5791 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
5793 status = SMB_VFS_NEXT_STREAMINFO(handle, fsp, smb_fname, mem_ctx,
5794 pnum_streams, pstreams);
5795 if (!NT_STATUS_IS_OK(status)) {
5799 fruit_filter_empty_streams(pnum_streams, pstreams);
5801 status = fruit_streaminfo_meta(handle, fsp, smb_fname,
5802 mem_ctx, pnum_streams, pstreams);
5803 if (!NT_STATUS_IS_OK(status)) {
5807 status = fruit_streaminfo_rsrc(handle, fsp, smb_fname,
5808 mem_ctx, pnum_streams, pstreams);
5809 if (!NT_STATUS_IS_OK(status)) {
5813 return NT_STATUS_OK;
5816 static int fruit_ntimes(vfs_handle_struct *handle,
5817 const struct smb_filename *smb_fname,
5818 struct smb_file_time *ft)
5821 struct adouble *ad = NULL;
5822 struct fruit_config_data *config = NULL;
5824 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5827 if ((config->meta != FRUIT_META_NETATALK) ||
5828 null_timespec(ft->create_time))
5830 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
5833 DEBUG(10,("set btime for %s to %s\n", smb_fname_str_dbg(smb_fname),
5834 time_to_asc(convert_timespec_to_time_t(ft->create_time))));
5836 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5841 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX,
5842 convert_time_t_to_uint32_t(ft->create_time.tv_sec));
5844 rc = ad_set(handle, ad, smb_fname);
5850 DEBUG(1, ("fruit_ntimes: %s\n", smb_fname_str_dbg(smb_fname)));
5853 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
5856 static int fruit_fallocate(struct vfs_handle_struct *handle,
5857 struct files_struct *fsp,
5862 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5865 return SMB_VFS_NEXT_FALLOCATE(handle, fsp, mode, offset, len);
5868 /* Let the pwrite code path handle it. */
5873 static int fruit_ftruncate_rsrc_xattr(struct vfs_handle_struct *handle,
5874 struct files_struct *fsp,
5877 #ifdef HAVE_ATTROPEN
5878 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
5883 static int fruit_ftruncate_rsrc_adouble(struct vfs_handle_struct *handle,
5884 struct files_struct *fsp,
5888 struct adouble *ad = NULL;
5891 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
5893 DBG_DEBUG("ad_get [%s] failed [%s]\n",
5894 fsp_str_dbg(fsp), strerror(errno));
5898 ad_off = ad_getentryoff(ad, ADEID_RFORK);
5900 rc = ftruncate(fsp->fh->fd, offset + ad_off);
5906 ad_setentrylen(ad, ADEID_RFORK, offset);
5908 rc = ad_fset(handle, ad, fsp);
5910 DBG_ERR("ad_fset [%s] failed [%s]\n",
5911 fsp_str_dbg(fsp), strerror(errno));
5920 static int fruit_ftruncate_rsrc_stream(struct vfs_handle_struct *handle,
5921 struct files_struct *fsp,
5924 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
5927 static int fruit_ftruncate_rsrc(struct vfs_handle_struct *handle,
5928 struct files_struct *fsp,
5931 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5935 DBG_ERR("Failed to fetch fsp extension");
5939 switch (fio->config->rsrc) {
5940 case FRUIT_RSRC_XATTR:
5941 ret = fruit_ftruncate_rsrc_xattr(handle, fsp, offset);
5944 case FRUIT_RSRC_ADFILE:
5945 ret = fruit_ftruncate_rsrc_adouble(handle, fsp, offset);
5948 case FRUIT_RSRC_STREAM:
5949 ret = fruit_ftruncate_rsrc_stream(handle, fsp, offset);
5953 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
5961 static int fruit_ftruncate_meta(struct vfs_handle_struct *handle,
5962 struct files_struct *fsp,
5966 DBG_WARNING("ftruncate %s to %jd",
5967 fsp_str_dbg(fsp), (intmax_t)offset);
5968 /* OS X returns NT_STATUS_ALLOTTED_SPACE_EXCEEDED */
5973 /* OS X returns success but does nothing */
5974 DBG_INFO("ignoring ftruncate %s to %jd\n",
5975 fsp_str_dbg(fsp), (intmax_t)offset);
5979 static int fruit_ftruncate(struct vfs_handle_struct *handle,
5980 struct files_struct *fsp,
5983 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5986 DBG_DEBUG("Path [%s] offset [%"PRIdMAX"]\n", fsp_str_dbg(fsp),
5990 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
5993 if (fio->type == ADOUBLE_META) {
5994 ret = fruit_ftruncate_meta(handle, fsp, offset);
5996 ret = fruit_ftruncate_rsrc(handle, fsp, offset);
5999 DBG_DEBUG("Path [%s] result [%d]\n", fsp_str_dbg(fsp), ret);
6003 static NTSTATUS fruit_create_file(vfs_handle_struct *handle,
6004 struct smb_request *req,
6005 uint16_t root_dir_fid,
6006 struct smb_filename *smb_fname,
6007 uint32_t access_mask,
6008 uint32_t share_access,
6009 uint32_t create_disposition,
6010 uint32_t create_options,
6011 uint32_t file_attributes,
6012 uint32_t oplock_request,
6013 struct smb2_lease *lease,
6014 uint64_t allocation_size,
6015 uint32_t private_flags,
6016 struct security_descriptor *sd,
6017 struct ea_list *ea_list,
6018 files_struct **result,
6020 const struct smb2_create_blobs *in_context_blobs,
6021 struct smb2_create_blobs *out_context_blobs)
6024 struct fruit_config_data *config = NULL;
6025 files_struct *fsp = NULL;
6026 struct fio *fio = NULL;
6027 bool internal_open = (oplock_request & INTERNAL_OPEN_ONLY);
6030 status = check_aapl(handle, req, in_context_blobs, out_context_blobs);
6031 if (!NT_STATUS_IS_OK(status)) {
6035 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
6036 return NT_STATUS_UNSUCCESSFUL);
6038 if (is_apple_stream(smb_fname) && !internal_open) {
6039 ret = ad_convert(handle, smb_fname);
6041 DBG_ERR("ad_convert() failed\n");
6042 return NT_STATUS_UNSUCCESSFUL;
6046 status = SMB_VFS_NEXT_CREATE_FILE(
6047 handle, req, root_dir_fid, smb_fname,
6048 access_mask, share_access,
6049 create_disposition, create_options,
6050 file_attributes, oplock_request,
6052 allocation_size, private_flags,
6053 sd, ea_list, result,
6054 pinfo, in_context_blobs, out_context_blobs);
6055 if (!NT_STATUS_IS_OK(status)) {
6061 if (global_fruit_config.nego_aapl) {
6062 if (config->posix_rename && fsp->is_directory) {
6064 * Enable POSIX directory rename behaviour
6066 fsp->posix_flags |= FSP_POSIX_FLAGS_RENAME;
6071 * If this is a plain open for existing files, opening an 0
6072 * byte size resource fork MUST fail with
6073 * NT_STATUS_OBJECT_NAME_NOT_FOUND.
6075 * Cf the vfs_fruit torture tests in test_rfork_create().
6077 if (global_fruit_config.nego_aapl &&
6078 create_disposition == FILE_OPEN &&
6079 smb_fname->st.st_ex_size == 0 &&
6080 is_ntfs_stream_smb_fname(smb_fname) &&
6081 !(is_ntfs_default_stream_smb_fname(smb_fname)))
6083 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
6087 fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
6088 if (fio != NULL && pinfo != NULL && *pinfo == FILE_WAS_CREATED) {
6089 fio->created = true;
6092 if (is_ntfs_stream_smb_fname(smb_fname)
6093 || fsp->is_directory) {
6097 if (config->locking == FRUIT_LOCKING_NETATALK) {
6098 status = fruit_check_access(
6102 if (!NT_STATUS_IS_OK(status)) {
6110 DEBUG(10, ("fruit_create_file: %s\n", nt_errstr(status)));
6113 close_file(req, fsp, ERROR_CLOSE);
6114 *result = fsp = NULL;
6120 static NTSTATUS fruit_readdir_attr(struct vfs_handle_struct *handle,
6121 const struct smb_filename *fname,
6122 TALLOC_CTX *mem_ctx,
6123 struct readdir_attr_data **pattr_data)
6125 struct fruit_config_data *config = NULL;
6126 struct readdir_attr_data *attr_data;
6130 SMB_VFS_HANDLE_GET_DATA(handle, config,
6131 struct fruit_config_data,
6132 return NT_STATUS_UNSUCCESSFUL);
6134 if (!global_fruit_config.nego_aapl) {
6135 return SMB_VFS_NEXT_READDIR_ATTR(handle, fname, mem_ctx, pattr_data);
6138 DEBUG(10, ("fruit_readdir_attr %s\n", fname->base_name));
6140 ret = ad_convert(handle, fname);
6142 DBG_ERR("ad_convert() failed\n");
6143 return NT_STATUS_UNSUCCESSFUL;
6146 *pattr_data = talloc_zero(mem_ctx, struct readdir_attr_data);
6147 if (*pattr_data == NULL) {
6148 return NT_STATUS_UNSUCCESSFUL;
6150 attr_data = *pattr_data;
6151 attr_data->type = RDATTR_AAPL;
6154 * Mac metadata: compressed FinderInfo, resource fork length
6157 status = readdir_attr_macmeta(handle, fname, attr_data);
6158 if (!NT_STATUS_IS_OK(status)) {
6160 * Error handling is tricky: if we return failure from
6161 * this function, the corresponding directory entry
6162 * will to be passed to the client, so we really just
6163 * want to error out on fatal errors.
6165 if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
6173 if (config->unix_info_enabled) {
6174 attr_data->attr_data.aapl.unix_mode = fname->st.st_ex_mode;
6180 if (!config->readdir_attr_max_access) {
6181 attr_data->attr_data.aapl.max_access = FILE_GENERIC_ALL;
6183 status = smbd_calculate_access_mask(
6187 SEC_FLAG_MAXIMUM_ALLOWED,
6188 &attr_data->attr_data.aapl.max_access);
6189 if (!NT_STATUS_IS_OK(status)) {
6194 return NT_STATUS_OK;
6197 DEBUG(1, ("fruit_readdir_attr %s, error: %s\n",
6198 fname->base_name, nt_errstr(status)));
6199 TALLOC_FREE(*pattr_data);
6203 static NTSTATUS fruit_fget_nt_acl(vfs_handle_struct *handle,
6205 uint32_t security_info,
6206 TALLOC_CTX *mem_ctx,
6207 struct security_descriptor **ppdesc)
6210 struct security_ace ace;
6212 struct fruit_config_data *config;
6214 SMB_VFS_HANDLE_GET_DATA(handle, config,
6215 struct fruit_config_data,
6216 return NT_STATUS_UNSUCCESSFUL);
6218 status = SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
6220 if (!NT_STATUS_IS_OK(status)) {
6225 * Add MS NFS style ACEs with uid, gid and mode
6227 if (!global_fruit_config.nego_aapl) {
6228 return NT_STATUS_OK;
6230 if (!config->unix_info_enabled) {
6231 return NT_STATUS_OK;
6234 /* First remove any existing ACE's with NFS style mode/uid/gid SIDs. */
6235 status = remove_virtual_nfs_aces(*ppdesc);
6236 if (!NT_STATUS_IS_OK(status)) {
6237 DBG_WARNING("failed to remove MS NFS style ACEs\n");
6241 /* MS NFS style mode */
6242 sid_compose(&sid, &global_sid_Unix_NFS_Mode, fsp->fsp_name->st.st_ex_mode);
6243 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6244 status = security_descriptor_dacl_add(*ppdesc, &ace);
6245 if (!NT_STATUS_IS_OK(status)) {
6246 DEBUG(1,("failed to add MS NFS style ACE\n"));
6250 /* MS NFS style uid */
6251 sid_compose(&sid, &global_sid_Unix_NFS_Users, fsp->fsp_name->st.st_ex_uid);
6252 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6253 status = security_descriptor_dacl_add(*ppdesc, &ace);
6254 if (!NT_STATUS_IS_OK(status)) {
6255 DEBUG(1,("failed to add MS NFS style ACE\n"));
6259 /* MS NFS style gid */
6260 sid_compose(&sid, &global_sid_Unix_NFS_Groups, fsp->fsp_name->st.st_ex_gid);
6261 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6262 status = security_descriptor_dacl_add(*ppdesc, &ace);
6263 if (!NT_STATUS_IS_OK(status)) {
6264 DEBUG(1,("failed to add MS NFS style ACE\n"));
6268 return NT_STATUS_OK;
6271 static NTSTATUS fruit_fset_nt_acl(vfs_handle_struct *handle,
6273 uint32_t security_info_sent,
6274 const struct security_descriptor *orig_psd)
6278 mode_t ms_nfs_mode = 0;
6280 struct security_descriptor *psd = NULL;
6281 uint32_t orig_num_aces = 0;
6283 if (orig_psd->dacl != NULL) {
6284 orig_num_aces = orig_psd->dacl->num_aces;
6287 psd = security_descriptor_copy(talloc_tos(), orig_psd);
6289 return NT_STATUS_NO_MEMORY;
6292 DBG_DEBUG("fruit_fset_nt_acl: %s\n", fsp_str_dbg(fsp));
6294 status = check_ms_nfs(handle, fsp, psd, &ms_nfs_mode, &do_chmod);
6295 if (!NT_STATUS_IS_OK(status)) {
6296 DEBUG(1, ("fruit_fset_nt_acl: check_ms_nfs failed%s\n", fsp_str_dbg(fsp)));
6302 * If only ms_nfs ACE entries were sent, ensure we set the DACL
6303 * sent/present flags correctly now we've removed them.
6306 if (orig_num_aces != 0) {
6308 * Are there any ACE's left ?
6310 if (psd->dacl->num_aces == 0) {
6311 /* No - clear the DACL sent/present flags. */
6312 security_info_sent &= ~SECINFO_DACL;
6313 psd->type &= ~SEC_DESC_DACL_PRESENT;
6317 status = SMB_VFS_NEXT_FSET_NT_ACL(handle, fsp, security_info_sent, psd);
6318 if (!NT_STATUS_IS_OK(status)) {
6319 DEBUG(1, ("fruit_fset_nt_acl: SMB_VFS_NEXT_FSET_NT_ACL failed%s\n", fsp_str_dbg(fsp)));
6325 if (fsp->fh->fd != -1) {
6326 result = SMB_VFS_FCHMOD(fsp, ms_nfs_mode);
6328 result = SMB_VFS_CHMOD(fsp->conn,
6334 DEBUG(1, ("chmod: %s, result: %d, %04o error %s\n", fsp_str_dbg(fsp),
6335 result, (unsigned)ms_nfs_mode,
6337 status = map_nt_error_from_unix(errno);
6344 return NT_STATUS_OK;
6347 static struct vfs_offload_ctx *fruit_offload_ctx;
6349 struct fruit_offload_read_state {
6350 struct vfs_handle_struct *handle;
6351 struct tevent_context *ev;
6357 static void fruit_offload_read_done(struct tevent_req *subreq);
6359 static struct tevent_req *fruit_offload_read_send(
6360 TALLOC_CTX *mem_ctx,
6361 struct tevent_context *ev,
6362 struct vfs_handle_struct *handle,
6369 struct tevent_req *req = NULL;
6370 struct tevent_req *subreq = NULL;
6371 struct fruit_offload_read_state *state = NULL;
6373 req = tevent_req_create(mem_ctx, &state,
6374 struct fruit_offload_read_state);
6378 *state = (struct fruit_offload_read_state) {
6385 subreq = SMB_VFS_NEXT_OFFLOAD_READ_SEND(mem_ctx, ev, handle, fsp,
6386 fsctl, ttl, offset, to_copy);
6387 if (tevent_req_nomem(subreq, req)) {
6388 return tevent_req_post(req, ev);
6390 tevent_req_set_callback(subreq, fruit_offload_read_done, req);
6394 static void fruit_offload_read_done(struct tevent_req *subreq)
6396 struct tevent_req *req = tevent_req_callback_data(
6397 subreq, struct tevent_req);
6398 struct fruit_offload_read_state *state = tevent_req_data(
6399 req, struct fruit_offload_read_state);
6402 status = SMB_VFS_NEXT_OFFLOAD_READ_RECV(subreq,
6406 TALLOC_FREE(subreq);
6407 if (tevent_req_nterror(req, status)) {
6411 if (state->fsctl != FSCTL_SRV_REQUEST_RESUME_KEY) {
6412 tevent_req_done(req);
6416 status = vfs_offload_token_ctx_init(state->fsp->conn->sconn->client,
6417 &fruit_offload_ctx);
6418 if (tevent_req_nterror(req, status)) {
6422 status = vfs_offload_token_db_store_fsp(fruit_offload_ctx,
6425 if (tevent_req_nterror(req, status)) {
6429 tevent_req_done(req);
6433 static NTSTATUS fruit_offload_read_recv(struct tevent_req *req,
6434 struct vfs_handle_struct *handle,
6435 TALLOC_CTX *mem_ctx,
6438 struct fruit_offload_read_state *state = tevent_req_data(
6439 req, struct fruit_offload_read_state);
6442 if (tevent_req_is_nterror(req, &status)) {
6443 tevent_req_received(req);
6447 token->length = state->token.length;
6448 token->data = talloc_move(mem_ctx, &state->token.data);
6450 tevent_req_received(req);
6451 return NT_STATUS_OK;
6454 struct fruit_offload_write_state {
6455 struct vfs_handle_struct *handle;
6457 struct files_struct *src_fsp;
6458 struct files_struct *dst_fsp;
6462 static void fruit_offload_write_done(struct tevent_req *subreq);
6463 static struct tevent_req *fruit_offload_write_send(struct vfs_handle_struct *handle,
6464 TALLOC_CTX *mem_ctx,
6465 struct tevent_context *ev,
6468 off_t transfer_offset,
6469 struct files_struct *dest_fsp,
6473 struct tevent_req *req, *subreq;
6474 struct fruit_offload_write_state *state;
6476 struct fruit_config_data *config;
6477 off_t src_off = transfer_offset;
6478 files_struct *src_fsp = NULL;
6479 off_t to_copy = num;
6480 bool copyfile_enabled = false;
6482 DEBUG(10,("soff: %ju, doff: %ju, len: %ju\n",
6483 (uintmax_t)src_off, (uintmax_t)dest_off, (uintmax_t)num));
6485 SMB_VFS_HANDLE_GET_DATA(handle, config,
6486 struct fruit_config_data,
6489 req = tevent_req_create(mem_ctx, &state,
6490 struct fruit_offload_write_state);
6494 state->handle = handle;
6495 state->dst_fsp = dest_fsp;
6498 case FSCTL_SRV_COPYCHUNK:
6499 case FSCTL_SRV_COPYCHUNK_WRITE:
6500 copyfile_enabled = config->copyfile_enabled;
6507 * Check if this a OS X copyfile style copychunk request with
6508 * a requested chunk count of 0 that was translated to a
6509 * offload_write_send VFS call overloading the parameters src_off
6510 * = dest_off = num = 0.
6512 if (copyfile_enabled && num == 0 && src_off == 0 && dest_off == 0) {
6513 status = vfs_offload_token_db_fetch_fsp(
6514 fruit_offload_ctx, token, &src_fsp);
6515 if (tevent_req_nterror(req, status)) {
6516 return tevent_req_post(req, ev);
6518 state->src_fsp = src_fsp;
6520 status = vfs_stat_fsp(src_fsp);
6521 if (tevent_req_nterror(req, status)) {
6522 return tevent_req_post(req, ev);
6525 to_copy = src_fsp->fsp_name->st.st_ex_size;
6526 state->is_copyfile = true;
6529 subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle,
6538 if (tevent_req_nomem(subreq, req)) {
6539 return tevent_req_post(req, ev);
6542 tevent_req_set_callback(subreq, fruit_offload_write_done, req);
6546 static void fruit_offload_write_done(struct tevent_req *subreq)
6548 struct tevent_req *req = tevent_req_callback_data(
6549 subreq, struct tevent_req);
6550 struct fruit_offload_write_state *state = tevent_req_data(
6551 req, struct fruit_offload_write_state);
6553 unsigned int num_streams = 0;
6554 struct stream_struct *streams = NULL;
6556 struct smb_filename *src_fname_tmp = NULL;
6557 struct smb_filename *dst_fname_tmp = NULL;
6559 status = SMB_VFS_NEXT_OFFLOAD_WRITE_RECV(state->handle,
6562 TALLOC_FREE(subreq);
6563 if (tevent_req_nterror(req, status)) {
6567 if (!state->is_copyfile) {
6568 tevent_req_done(req);
6573 * Now copy all remaining streams. We know the share supports
6574 * streams, because we're in vfs_fruit. We don't do this async
6575 * because streams are few and small.
6577 status = vfs_streaminfo(state->handle->conn, state->src_fsp,
6578 state->src_fsp->fsp_name,
6579 req, &num_streams, &streams);
6580 if (tevent_req_nterror(req, status)) {
6584 if (num_streams == 1) {
6585 /* There is always one stream, ::$DATA. */
6586 tevent_req_done(req);
6590 for (i = 0; i < num_streams; i++) {
6591 DEBUG(10, ("%s: stream: '%s'/%zu\n",
6592 __func__, streams[i].name, (size_t)streams[i].size));
6594 src_fname_tmp = synthetic_smb_fname(
6596 state->src_fsp->fsp_name->base_name,
6599 state->src_fsp->fsp_name->flags);
6600 if (tevent_req_nomem(src_fname_tmp, req)) {
6604 if (is_ntfs_default_stream_smb_fname(src_fname_tmp)) {
6605 TALLOC_FREE(src_fname_tmp);
6609 dst_fname_tmp = synthetic_smb_fname(
6611 state->dst_fsp->fsp_name->base_name,
6614 state->dst_fsp->fsp_name->flags);
6615 if (tevent_req_nomem(dst_fname_tmp, req)) {
6616 TALLOC_FREE(src_fname_tmp);
6620 status = copy_file(req,
6621 state->handle->conn,
6624 OPENX_FILE_CREATE_IF_NOT_EXIST,
6626 if (!NT_STATUS_IS_OK(status)) {
6627 DEBUG(1, ("%s: copy %s to %s failed: %s\n", __func__,
6628 smb_fname_str_dbg(src_fname_tmp),
6629 smb_fname_str_dbg(dst_fname_tmp),
6630 nt_errstr(status)));
6631 TALLOC_FREE(src_fname_tmp);
6632 TALLOC_FREE(dst_fname_tmp);
6633 tevent_req_nterror(req, status);
6637 TALLOC_FREE(src_fname_tmp);
6638 TALLOC_FREE(dst_fname_tmp);
6641 TALLOC_FREE(streams);
6642 TALLOC_FREE(src_fname_tmp);
6643 TALLOC_FREE(dst_fname_tmp);
6644 tevent_req_done(req);
6647 static NTSTATUS fruit_offload_write_recv(struct vfs_handle_struct *handle,
6648 struct tevent_req *req,
6651 struct fruit_offload_write_state *state = tevent_req_data(
6652 req, struct fruit_offload_write_state);
6655 if (tevent_req_is_nterror(req, &status)) {
6656 DEBUG(1, ("server side copy chunk failed: %s\n",
6657 nt_errstr(status)));
6659 tevent_req_received(req);
6663 *copied = state->copied;
6664 tevent_req_received(req);
6666 return NT_STATUS_OK;
6669 static char *fruit_get_bandsize_line(char **lines, int numlines)
6672 static bool re_initialized = false;
6676 if (!re_initialized) {
6677 ret = regcomp(&re, "^[[:blank:]]*<key>band-size</key>$", 0);
6681 re_initialized = true;
6684 for (i = 0; i < numlines; i++) {
6685 regmatch_t matches[1];
6687 ret = regexec(&re, lines[i], 1, matches, 0);
6690 * Check if the match was on the last line, sa we want
6691 * the subsequent line.
6693 if (i + 1 == numlines) {
6696 return lines[i + 1];
6698 if (ret != REG_NOMATCH) {
6706 static bool fruit_get_bandsize_from_line(char *line, size_t *_band_size)
6709 static bool re_initialized = false;
6710 regmatch_t matches[2];
6715 if (!re_initialized) {
6718 "<integer>\\([[:digit:]]*\\)</integer>$",
6723 re_initialized = true;
6726 ret = regexec(&re, line, 2, matches, 0);
6728 DBG_ERR("regex failed [%s]\n", line);
6732 line[matches[1].rm_eo] = '\0';
6734 ok = conv_str_u64(&line[matches[1].rm_so], &band_size);
6738 *_band_size = (size_t)band_size;
6743 * This reads and parses an Info.plist from a TM sparsebundle looking for the
6744 * "band-size" key and value.
6746 static bool fruit_get_bandsize(vfs_handle_struct *handle,
6750 #define INFO_PLIST_MAX_SIZE 64*1024
6752 struct smb_filename *smb_fname = NULL;
6753 files_struct *fsp = NULL;
6754 uint8_t *file_data = NULL;
6755 char **lines = NULL;
6756 char *band_size_line = NULL;
6757 size_t plist_file_size;
6764 plist = talloc_asprintf(talloc_tos(),
6766 handle->conn->connectpath,
6768 if (plist == NULL) {
6773 smb_fname = synthetic_smb_fname(talloc_tos(), plist, NULL, NULL, 0);
6774 if (smb_fname == NULL) {
6779 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
6781 DBG_INFO("Ignoring Sparsebundle without Info.plist [%s]\n", dir);
6786 plist_file_size = smb_fname->st.st_ex_size;
6788 if (plist_file_size > INFO_PLIST_MAX_SIZE) {
6789 DBG_INFO("%s is too large, ignoring\n", plist);
6794 status = SMB_VFS_NEXT_CREATE_FILE(
6797 0, /* root_dir_fid */
6798 smb_fname, /* fname */
6799 FILE_GENERIC_READ, /* access_mask */
6800 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
6801 FILE_OPEN, /* create_disposition */
6802 0, /* create_options */
6803 0, /* file_attributes */
6804 INTERNAL_OPEN_ONLY, /* oplock_request */
6806 0, /* allocation_size */
6807 0, /* private_flags */
6812 NULL, NULL); /* create context */
6813 if (!NT_STATUS_IS_OK(status)) {
6814 DBG_INFO("Opening [%s] failed [%s]\n",
6815 smb_fname_str_dbg(smb_fname), nt_errstr(status));
6820 file_data = talloc_array(talloc_tos(), uint8_t, plist_file_size);
6821 if (file_data == NULL) {
6826 nread = SMB_VFS_NEXT_PREAD(handle, fsp, file_data, plist_file_size, 0);
6827 if (nread != plist_file_size) {
6828 DBG_ERR("Short read on [%s]: %zu/%zd\n",
6829 fsp_str_dbg(fsp), nread, plist_file_size);
6835 status = close_file(NULL, fsp, NORMAL_CLOSE);
6837 if (!NT_STATUS_IS_OK(status)) {
6838 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
6843 lines = file_lines_parse((char *)file_data,
6847 if (lines == NULL) {
6852 band_size_line = fruit_get_bandsize_line(lines, numlines);
6853 if (band_size_line == NULL) {
6854 DBG_ERR("Didn't find band-size key in [%s]\n",
6855 smb_fname_str_dbg(smb_fname));
6860 ok = fruit_get_bandsize_from_line(band_size_line, band_size);
6862 DBG_ERR("fruit_get_bandsize_from_line failed\n");
6866 DBG_DEBUG("Parsed band-size [%zu] for [%s]\n", *band_size, plist);
6870 status = close_file(NULL, fsp, NORMAL_CLOSE);
6871 if (!NT_STATUS_IS_OK(status)) {
6872 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
6877 TALLOC_FREE(smb_fname);
6878 TALLOC_FREE(file_data);
6883 struct fruit_disk_free_state {
6887 static bool fruit_get_num_bands(vfs_handle_struct *handle,
6892 struct smb_filename *bands_dir = NULL;
6894 struct dirent *e = NULL;
6898 path = talloc_asprintf(talloc_tos(),
6900 handle->conn->connectpath,
6906 bands_dir = synthetic_smb_fname(talloc_tos(),
6912 if (bands_dir == NULL) {
6916 d = SMB_VFS_NEXT_OPENDIR(handle, bands_dir, NULL, 0);
6918 TALLOC_FREE(bands_dir);
6924 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
6926 e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
6928 if (ISDOT(e->d_name) || ISDOTDOT(e->d_name)) {
6934 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
6936 TALLOC_FREE(bands_dir);
6940 DBG_DEBUG("%zu bands in [%s]\n", nbands, smb_fname_str_dbg(bands_dir));
6942 TALLOC_FREE(bands_dir);
6948 static bool fruit_tmsize_do_dirent(vfs_handle_struct *handle,
6949 struct fruit_disk_free_state *state,
6954 size_t sparsebundle_strlen = strlen("sparsebundle");
6955 size_t bandsize = 0;
6959 p = strstr(e->d_name, "sparsebundle");
6964 if (p[sparsebundle_strlen] != '\0') {
6968 DBG_DEBUG("Processing sparsebundle [%s]\n", e->d_name);
6970 ok = fruit_get_bandsize(handle, e->d_name, &bandsize);
6973 * Beware of race conditions: this may be an uninitialized
6974 * Info.plist that a client is just creating. We don't want let
6975 * this to trigger complete failure.
6977 DBG_ERR("Processing sparsebundle [%s] failed\n", e->d_name);
6981 ok = fruit_get_num_bands(handle, e->d_name, &nbands);
6984 * Beware of race conditions: this may be a backup sparsebundle
6985 * in an early stage lacking a bands subdirectory. We don't want
6986 * let this to trigger complete failure.
6988 DBG_ERR("Processing sparsebundle [%s] failed\n", e->d_name);
6992 if (bandsize > SIZE_MAX/nbands) {
6993 DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
6997 tm_size = bandsize * nbands;
6999 if (state->total_size + tm_size < state->total_size) {
7000 DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
7005 state->total_size += tm_size;
7007 DBG_DEBUG("[%s] tm_size [%jd] total_size [%jd]\n",
7008 e->d_name, (intmax_t)tm_size, (intmax_t)state->total_size);
7014 * Calculate used size of a TimeMachine volume
7016 * This assumes that the volume is used only for TimeMachine.
7018 * - readdir(basedir of share), then
7019 * - for every element that matches regex "^\(.*\)\.sparsebundle$" :
7020 * - parse "\1.sparsebundle/Info.plist" and read the band-size XML key
7021 * - count band files in "\1.sparsebundle/bands/"
7022 * - calculate used size of all bands: band_count * band_size
7024 static uint64_t fruit_disk_free(vfs_handle_struct *handle,
7025 const struct smb_filename *smb_fname,
7030 struct fruit_config_data *config = NULL;
7031 struct fruit_disk_free_state state = {0};
7033 struct dirent *e = NULL;
7039 SMB_VFS_HANDLE_GET_DATA(handle, config,
7040 struct fruit_config_data,
7043 if (!config->time_machine ||
7044 config->time_machine_max_size == 0)
7046 return SMB_VFS_NEXT_DISK_FREE(handle,
7053 d = SMB_VFS_NEXT_OPENDIR(handle, smb_fname, NULL, 0);
7058 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
7060 e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
7062 ok = fruit_tmsize_do_dirent(handle, &state, e);
7064 SMB_VFS_NEXT_CLOSEDIR(handle, d);
7069 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
7074 dsize = config->time_machine_max_size / 512;
7075 dfree = dsize - (state.total_size / 512);
7076 if (dfree > dsize) {
7086 static struct vfs_fn_pointers vfs_fruit_fns = {
7087 .connect_fn = fruit_connect,
7088 .disk_free_fn = fruit_disk_free,
7090 /* File operations */
7091 .chmod_fn = fruit_chmod,
7092 .chown_fn = fruit_chown,
7093 .unlink_fn = fruit_unlink,
7094 .rename_fn = fruit_rename,
7095 .rmdir_fn = fruit_rmdir,
7096 .open_fn = fruit_open,
7097 .close_fn = fruit_close,
7098 .pread_fn = fruit_pread,
7099 .pwrite_fn = fruit_pwrite,
7100 .pread_send_fn = fruit_pread_send,
7101 .pread_recv_fn = fruit_pread_recv,
7102 .pwrite_send_fn = fruit_pwrite_send,
7103 .pwrite_recv_fn = fruit_pwrite_recv,
7104 .stat_fn = fruit_stat,
7105 .lstat_fn = fruit_lstat,
7106 .fstat_fn = fruit_fstat,
7107 .streaminfo_fn = fruit_streaminfo,
7108 .ntimes_fn = fruit_ntimes,
7109 .ftruncate_fn = fruit_ftruncate,
7110 .fallocate_fn = fruit_fallocate,
7111 .create_file_fn = fruit_create_file,
7112 .readdir_attr_fn = fruit_readdir_attr,
7113 .offload_read_send_fn = fruit_offload_read_send,
7114 .offload_read_recv_fn = fruit_offload_read_recv,
7115 .offload_write_send_fn = fruit_offload_write_send,
7116 .offload_write_recv_fn = fruit_offload_write_recv,
7118 /* NT ACL operations */
7119 .fget_nt_acl_fn = fruit_fget_nt_acl,
7120 .fset_nt_acl_fn = fruit_fset_nt_acl,
7124 NTSTATUS vfs_fruit_init(TALLOC_CTX *ctx)
7126 NTSTATUS ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "fruit",
7128 if (!NT_STATUS_IS_OK(ret)) {
7132 vfs_fruit_debug_level = debug_add_class("fruit");
7133 if (vfs_fruit_debug_level == -1) {
7134 vfs_fruit_debug_level = DBGC_VFS;
7135 DEBUG(0, ("%s: Couldn't register custom debugging class!\n",
7138 DEBUG(10, ("%s: Debug class number of '%s': %d\n",
7139 "vfs_fruit_init","fruit",vfs_fruit_debug_level));