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(struct adouble *ad,
998 const struct smb_filename *smb_fname)
1000 char *map = MAP_FAILED;
1006 if (ad_getentrylen(ad, ADEID_RFORK) == 0) {
1010 maplen = ad_getentryoff(ad, ADEID_RFORK) +
1011 ad_getentrylen(ad, ADEID_RFORK);
1013 /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
1014 map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
1016 if (map == MAP_FAILED) {
1017 DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
1022 memmove(map + ADEDOFF_RFORK_DOT_UND,
1023 map + ad_getentryoff(ad, ADEID_RFORK),
1024 ad_getentrylen(ad, ADEID_RFORK));
1026 rc = munmap(map, maplen);
1028 DBG_ERR("munmap failed: %s\n", strerror(errno));
1032 ad_setentryoff(ad, ADEID_RFORK, ADEDOFF_RFORK_DOT_UND);
1036 DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name);
1040 len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
1041 if (len != AD_DATASZ_DOT_UND) {
1042 DBG_ERR("%s: bad size: %zd\n", smb_fname->base_name, len);
1049 static bool ad_convert_xattr(vfs_handle_struct *handle,
1051 const struct smb_filename *smb_fname,
1052 bool *converted_xattr)
1054 static struct char_mappings **string_replace_cmaps = NULL;
1055 char *map = MAP_FAILED;
1059 int saved_errno = 0;
1064 *converted_xattr = false;
1066 if (ad_getentrylen(ad, ADEID_FINDERI) == ADEDLEN_FINDERI) {
1070 if (string_replace_cmaps == NULL) {
1071 const char **mappings = NULL;
1073 mappings = str_list_make_v3_const(
1074 talloc_tos(), fruit_catia_maps, NULL);
1075 if (mappings == NULL) {
1078 string_replace_cmaps = string_replace_init_map(mappings);
1079 TALLOC_FREE(mappings);
1082 maplen = ad_getentryoff(ad, ADEID_RFORK) +
1083 ad_getentrylen(ad, ADEID_RFORK);
1085 /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
1086 map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
1088 if (map == MAP_FAILED) {
1089 DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
1093 for (i = 0; i < ad->adx_header.adx_num_attrs; i++) {
1094 struct ad_xattr_entry *e = &ad->adx_entries[i];
1095 char *mapped_name = NULL;
1097 struct smb_filename *stream_name = NULL;
1098 files_struct *fsp = NULL;
1101 status = string_replace_allocate(handle->conn,
1103 string_replace_cmaps,
1106 vfs_translate_to_windows);
1107 if (!NT_STATUS_IS_OK(status) &&
1108 !NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))
1110 DBG_ERR("string_replace_allocate failed\n");
1116 mapped_name = talloc_asprintf(talloc_tos(), ":%s", tmp);
1118 if (mapped_name == NULL) {
1123 stream_name = synthetic_smb_fname(talloc_tos(),
1124 smb_fname->base_name,
1128 TALLOC_FREE(mapped_name);
1129 if (stream_name == NULL) {
1130 DBG_ERR("synthetic_smb_fname failed\n");
1135 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1137 status = SMB_VFS_CREATE_FILE(
1138 handle->conn, /* conn */
1140 0, /* root_dir_fid */
1141 stream_name, /* fname */
1142 FILE_GENERIC_WRITE, /* access_mask */
1143 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
1144 FILE_OPEN_IF, /* create_disposition */
1145 0, /* create_options */
1146 0, /* file_attributes */
1147 INTERNAL_OPEN_ONLY, /* oplock_request */
1149 0, /* allocation_size */
1150 0, /* private_flags */
1155 NULL, NULL); /* create context */
1156 TALLOC_FREE(stream_name);
1157 if (!NT_STATUS_IS_OK(status)) {
1158 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1163 nwritten = SMB_VFS_PWRITE(fsp,
1164 map + e->adx_offset,
1167 if (nwritten == -1) {
1168 DBG_ERR("SMB_VFS_PWRITE failed\n");
1169 saved_errno = errno;
1170 close_file(NULL, fsp, ERROR_CLOSE);
1171 errno = saved_errno;
1176 status = close_file(NULL, fsp, NORMAL_CLOSE);
1177 if (!NT_STATUS_IS_OK(status)) {
1184 ad_setentrylen(ad, ADEID_FINDERI, ADEDLEN_FINDERI);
1188 DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name);
1192 len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
1193 if (len != AD_DATASZ_DOT_UND) {
1194 DBG_ERR("%s: bad size: %zd\n", smb_fname->base_name, len);
1199 ok = ad_convert_move_reso(ad, smb_fname);
1204 *converted_xattr = true;
1208 rc = munmap(map, maplen);
1210 DBG_ERR("munmap failed: %s\n", strerror(errno));
1217 static bool ad_convert_finderinfo(vfs_handle_struct *handle,
1219 const struct smb_filename *smb_fname)
1224 struct smb_filename *stream_name = NULL;
1225 files_struct *fsp = NULL;
1229 int saved_errno = 0;
1232 cmp = memcmp(ad->ad_filler, AD_FILLER_TAG_OSX, ADEDLEN_FILLER);
1237 p_ad = ad_get_entry(ad, ADEID_FINDERI);
1242 ai = afpinfo_new(talloc_tos());
1247 memcpy(ai->afpi_FinderInfo, p_ad, ADEDLEN_FINDERI);
1249 aiblob = data_blob_talloc(talloc_tos(), NULL, AFP_INFO_SIZE);
1250 if (aiblob.data == NULL) {
1255 size = afpinfo_pack(ai, (char *)aiblob.data);
1257 if (size != AFP_INFO_SIZE) {
1261 stream_name = synthetic_smb_fname(talloc_tos(),
1262 smb_fname->base_name,
1266 if (stream_name == NULL) {
1267 data_blob_free(&aiblob);
1268 DBG_ERR("synthetic_smb_fname failed\n");
1272 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1274 status = SMB_VFS_CREATE_FILE(
1275 handle->conn, /* conn */
1277 0, /* root_dir_fid */
1278 stream_name, /* fname */
1279 FILE_GENERIC_WRITE, /* access_mask */
1280 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
1281 FILE_OPEN_IF, /* create_disposition */
1282 0, /* create_options */
1283 0, /* file_attributes */
1284 INTERNAL_OPEN_ONLY, /* oplock_request */
1286 0, /* allocation_size */
1287 0, /* private_flags */
1292 NULL, NULL); /* create context */
1293 TALLOC_FREE(stream_name);
1294 if (!NT_STATUS_IS_OK(status)) {
1295 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1299 nwritten = SMB_VFS_PWRITE(fsp,
1303 if (nwritten == -1) {
1304 DBG_ERR("SMB_VFS_PWRITE failed\n");
1305 saved_errno = errno;
1306 close_file(NULL, fsp, ERROR_CLOSE);
1307 errno = saved_errno;
1311 status = close_file(NULL, fsp, NORMAL_CLOSE);
1312 if (!NT_STATUS_IS_OK(status)) {
1320 static bool ad_convert_truncate(struct adouble *ad,
1321 const struct smb_filename *smb_fname)
1326 * FIXME: direct ftruncate(), but we don't have a fsp for the
1329 rc = ftruncate(ad->ad_fd, ADEDOFF_RFORK_DOT_UND +
1330 ad_getentrylen(ad, ADEID_RFORK));
1338 static bool ad_convert_blank_rfork(vfs_handle_struct *handle,
1342 struct fruit_config_data *config = NULL;
1343 uint8_t *map = MAP_FAILED;
1352 SMB_VFS_HANDLE_GET_DATA(handle, config,
1353 struct fruit_config_data, return false);
1355 if (!config->wipe_intentionally_left_blank_rfork) {
1359 if (ad_getentrylen(ad, ADEID_RFORK) != sizeof(empty_resourcefork)) {
1363 maplen = ad_getentryoff(ad, ADEID_RFORK) +
1364 ad_getentrylen(ad, ADEID_RFORK);
1366 /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
1367 map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
1369 if (map == MAP_FAILED) {
1370 DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
1374 cmp = memcmp(map + ADEDOFF_RFORK_DOT_UND,
1376 sizeof(empty_resourcefork));
1377 rc = munmap(map, maplen);
1379 DBG_ERR("munmap failed: %s\n", strerror(errno));
1387 ad_setentrylen(ad, ADEID_RFORK, 0);
1394 len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
1395 if (len != AD_DATASZ_DOT_UND) {
1403 static bool ad_convert_delete_adfile(vfs_handle_struct *handle,
1405 const struct smb_filename *smb_fname)
1407 struct fruit_config_data *config = NULL;
1408 struct smb_filename *ad_name = NULL;
1411 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
1415 SMB_VFS_HANDLE_GET_DATA(handle, config,
1416 struct fruit_config_data, return false);
1418 if (!config->delete_empty_adfiles) {
1422 rc = adouble_path(talloc_tos(), smb_fname, &ad_name);
1427 rc = SMB_VFS_NEXT_UNLINK(handle, ad_name);
1429 DBG_ERR("Unlinking [%s] failed: %s\n",
1430 smb_fname_str_dbg(ad_name), strerror(errno));
1431 TALLOC_FREE(ad_name);
1435 DBG_WARNING("Unlinked [%s] after conversion\n", smb_fname_str_dbg(ad_name));
1436 TALLOC_FREE(ad_name);
1442 * Convert from Apple's ._ file to Netatalk
1444 * Apple's AppleDouble may contain a FinderInfo entry longer then 32
1445 * bytes containing packed xattrs.
1447 * @return -1 in case an error occurred, 0 if no conversion was done, 1
1450 static int ad_convert(struct vfs_handle_struct *handle,
1451 const struct smb_filename *smb_fname)
1453 struct adouble *ad = NULL;
1455 bool converted_xattr = false;
1459 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
1464 ok = ad_convert_xattr(handle, ad, smb_fname, &converted_xattr);
1470 ok = ad_convert_blank_rfork(handle, ad, &blank);
1476 if (converted_xattr || blank) {
1477 ok = ad_convert_truncate(ad, smb_fname);
1484 ok = ad_convert_finderinfo(handle, ad, smb_fname);
1486 DBG_ERR("Failed to convert [%s]\n",
1487 smb_fname_str_dbg(smb_fname));
1492 ok = ad_convert_delete_adfile(handle, ad, smb_fname);
1505 * Read and parse Netatalk AppleDouble metadata xattr
1507 static ssize_t ad_read_meta(vfs_handle_struct *handle,
1509 const struct smb_filename *smb_fname)
1515 DEBUG(10, ("reading meta xattr for %s\n", smb_fname->base_name));
1517 ealen = SMB_VFS_GETXATTR(handle->conn, smb_fname,
1518 AFPINFO_EA_NETATALK, ad->ad_data,
1524 if (errno == ENOATTR) {
1530 DEBUG(2, ("error reading meta xattr: %s\n",
1536 if (ealen != AD_DATASZ_XATTR) {
1537 DEBUG(2, ("bad size %zd\n", ealen));
1543 /* Now parse entries */
1544 ok = ad_unpack(ad, ADEID_NUM_XATTR, AD_DATASZ_XATTR);
1546 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1552 if (!ad_getentryoff(ad, ADEID_FINDERI)
1553 || !ad_getentryoff(ad, ADEID_COMMENT)
1554 || !ad_getentryoff(ad, ADEID_FILEDATESI)
1555 || !ad_getentryoff(ad, ADEID_AFPFILEI)
1556 || !ad_getentryoff(ad, ADEID_PRIVDEV)
1557 || !ad_getentryoff(ad, ADEID_PRIVINO)
1558 || !ad_getentryoff(ad, ADEID_PRIVSYN)
1559 || !ad_getentryoff(ad, ADEID_PRIVID)) {
1560 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1567 DEBUG(10, ("reading meta xattr for %s, rc: %d\n",
1568 smb_fname->base_name, rc));
1572 if (errno == EINVAL) {
1574 (void)SMB_VFS_REMOVEXATTR(handle->conn,
1576 AFPINFO_EA_NETATALK);
1584 static int ad_open_rsrc_adouble(const struct smb_filename *smb_fname,
1590 struct smb_filename *adp_smb_fname = NULL;
1592 ret = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
1597 fd = open(adp_smb_fname->base_name, flags, mode);
1598 TALLOC_FREE(adp_smb_fname);
1603 static int ad_open_rsrc(vfs_handle_struct *handle,
1604 const struct smb_filename *smb_fname,
1608 return ad_open_rsrc_adouble(smb_fname, flags, mode);
1612 * Here's the deal: for ADOUBLE_META we can do without an fd as we can issue
1613 * path based xattr calls. For ADOUBLE_RSRC however we need a full-fledged fd
1614 * for file IO on the ._ file.
1616 static int ad_open(vfs_handle_struct *handle,
1619 const struct smb_filename *smb_fname,
1625 DBG_DEBUG("Path [%s] type [%s]\n", smb_fname->base_name,
1626 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
1628 if (ad->ad_type == ADOUBLE_META) {
1632 if ((fsp != NULL) && (fsp->fh != NULL) && (fsp->fh->fd != -1)) {
1633 ad->ad_fd = fsp->fh->fd;
1634 ad->ad_opened = false;
1638 fd = ad_open_rsrc(handle, smb_fname, flags, mode);
1642 ad->ad_opened = true;
1645 DBG_DEBUG("Path [%s] type [%s] fd [%d]\n",
1646 smb_fname->base_name,
1647 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc", fd);
1652 static ssize_t ad_read_rsrc_adouble(vfs_handle_struct *handle,
1654 const struct smb_filename *smb_fname)
1656 SMB_STRUCT_STAT sbuf;
1663 ret = sys_fstat(ad->ad_fd, &sbuf, lp_fake_directory_create_times(
1664 SNUM(handle->conn)));
1670 * AppleDouble file header content and size, two cases:
1672 * - without xattrs it is exactly AD_DATASZ_DOT_UND (82) bytes large
1673 * - with embedded xattrs it can be larger, up to AD_XATTR_MAX_HDR_SIZE
1675 * Read as much as we can up to AD_XATTR_MAX_HDR_SIZE.
1677 size = sbuf.st_ex_size;
1678 if (size > talloc_array_length(ad->ad_data)) {
1679 if (size > AD_XATTR_MAX_HDR_SIZE) {
1680 size = AD_XATTR_MAX_HDR_SIZE;
1682 p_ad = talloc_realloc(ad, ad->ad_data, char, size);
1689 len = sys_pread(ad->ad_fd, ad->ad_data,
1690 talloc_array_length(ad->ad_data), 0);
1691 if (len != talloc_array_length(ad->ad_data)) {
1692 DBG_NOTICE("%s %s: bad size: %zd\n",
1693 smb_fname->base_name, strerror(errno), len);
1697 /* Now parse entries */
1698 ok = ad_unpack(ad, ADEID_NUM_DOT_UND, sbuf.st_ex_size);
1700 DBG_ERR("invalid AppleDouble resource %s\n",
1701 smb_fname->base_name);
1706 if ((ad_getentryoff(ad, ADEID_FINDERI) != ADEDOFF_FINDERI_DOT_UND)
1707 || (ad_getentrylen(ad, ADEID_FINDERI) < ADEDLEN_FINDERI)
1708 || (ad_getentryoff(ad, ADEID_RFORK) < ADEDOFF_RFORK_DOT_UND)) {
1709 DBG_ERR("invalid AppleDouble resource %s\n",
1710 smb_fname->base_name);
1719 * Read and parse resource fork, either ._ AppleDouble file or xattr
1721 static ssize_t ad_read_rsrc(vfs_handle_struct *handle,
1723 const struct smb_filename *smb_fname)
1725 return ad_read_rsrc_adouble(handle, ad, smb_fname);
1729 * Read and unpack an AppleDouble metadata xattr or resource
1731 static ssize_t ad_read(vfs_handle_struct *handle,
1733 const struct smb_filename *smb_fname)
1735 switch (ad->ad_type) {
1737 return ad_read_meta(handle, ad, smb_fname);
1739 return ad_read_rsrc(handle, ad, smb_fname);
1745 static int adouble_destructor(struct adouble *ad)
1747 if ((ad->ad_fd != -1) && ad->ad_opened) {
1755 * Allocate a struct adouble without initialiing it
1757 * The struct is either hang of the fsp extension context or if fsp is
1760 * @param[in] ctx talloc context
1761 * @param[in] handle vfs handle
1762 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1764 * @return adouble handle
1766 static struct adouble *ad_alloc(TALLOC_CTX *ctx,
1767 adouble_type_t type)
1775 adsize = AD_DATASZ_XATTR;
1778 adsize = AD_DATASZ_DOT_UND;
1784 ad = talloc_zero(ctx, struct adouble);
1791 ad->ad_data = talloc_zero_array(ad, char, adsize);
1792 if (ad->ad_data == NULL) {
1799 ad->ad_magic = AD_MAGIC;
1800 ad->ad_version = AD_VERSION;
1803 talloc_set_destructor(ad, adouble_destructor);
1813 * Allocate and initialize a new struct adouble
1815 * @param[in] ctx talloc context
1816 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1818 * @return adouble handle, initialized
1820 static struct adouble *ad_init(TALLOC_CTX *ctx,
1821 adouble_type_t type)
1824 const struct ad_entry_order *eid;
1825 struct adouble *ad = NULL;
1826 time_t t = time(NULL);
1830 eid = entry_order_meta_xattr;
1833 eid = entry_order_dot_und;
1839 ad = ad_alloc(ctx, type);
1845 ad->ad_eid[eid->id].ade_off = eid->offset;
1846 ad->ad_eid[eid->id].ade_len = eid->len;
1850 /* put something sane in the date fields */
1851 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, t);
1852 ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, t);
1853 ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, t);
1854 ad_setdate(ad, AD_DATE_BACKUP, htonl(AD_DATE_START));
1862 static struct adouble *ad_get_internal(TALLOC_CTX *ctx,
1863 vfs_handle_struct *handle,
1865 const struct smb_filename *smb_fname,
1866 adouble_type_t type)
1870 struct adouble *ad = NULL;
1874 smb_fname = fsp->base_fsp->fsp_name;
1877 DEBUG(10, ("ad_get(%s) called for %s\n",
1878 type == ADOUBLE_META ? "meta" : "rsrc",
1879 smb_fname->base_name));
1881 ad = ad_alloc(ctx, type);
1887 /* Try rw first so we can use the fd in ad_convert() */
1890 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
1891 if (rc == -1 && ((errno == EROFS) || (errno == EACCES))) {
1893 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
1896 DBG_DEBUG("ad_open [%s] error [%s]\n",
1897 smb_fname->base_name, strerror(errno));
1902 len = ad_read(handle, ad, smb_fname);
1904 DEBUG(10, ("error reading AppleDouble for %s\n",
1905 smb_fname->base_name));
1911 DEBUG(10, ("ad_get(%s) for %s returning %d\n",
1912 type == ADOUBLE_META ? "meta" : "rsrc",
1913 smb_fname->base_name, rc));
1922 * Return AppleDouble data for a file
1924 * @param[in] ctx talloc context
1925 * @param[in] handle vfs handle
1926 * @param[in] smb_fname pathname to file or directory
1927 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1929 * @return talloced struct adouble or NULL on error
1931 static struct adouble *ad_get(TALLOC_CTX *ctx,
1932 vfs_handle_struct *handle,
1933 const struct smb_filename *smb_fname,
1934 adouble_type_t type)
1936 return ad_get_internal(ctx, handle, NULL, smb_fname, type);
1940 * Return AppleDouble data for a file
1942 * @param[in] ctx talloc context
1943 * @param[in] handle vfs handle
1944 * @param[in] fsp fsp to use for IO
1945 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1947 * @return talloced struct adouble or NULL on error
1949 static struct adouble *ad_fget(TALLOC_CTX *ctx, vfs_handle_struct *handle,
1950 files_struct *fsp, adouble_type_t type)
1952 return ad_get_internal(ctx, handle, fsp, NULL, type);
1956 * Set AppleDouble metadata on a file or directory
1958 * @param[in] ad adouble handle
1960 * @param[in] smb_fname pathname to file or directory
1962 * @return status code, 0 means success
1964 static int ad_set(vfs_handle_struct *handle,
1966 const struct smb_filename *smb_fname)
1971 DBG_DEBUG("Path [%s]\n", smb_fname->base_name);
1973 if (ad->ad_type != ADOUBLE_META) {
1974 DBG_ERR("ad_set on [%s] used with ADOUBLE_RSRC\n",
1975 smb_fname->base_name);
1984 ret = SMB_VFS_SETXATTR(handle->conn,
1986 AFPINFO_EA_NETATALK,
1988 AD_DATASZ_XATTR, 0);
1990 DBG_DEBUG("Path [%s] ret [%d]\n", smb_fname->base_name, ret);
1996 * Set AppleDouble metadata on a file or directory
1998 * @param[in] ad adouble handle
1999 * @param[in] fsp file handle
2001 * @return status code, 0 means success
2003 static int ad_fset(struct vfs_handle_struct *handle,
2011 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
2014 || (fsp->fh == NULL)
2015 || (fsp->fh->fd == -1))
2017 smb_panic("bad fsp");
2025 switch (ad->ad_type) {
2027 rc = SMB_VFS_NEXT_SETXATTR(handle,
2029 AFPINFO_EA_NETATALK,
2031 AD_DATASZ_XATTR, 0);
2035 len = SMB_VFS_NEXT_PWRITE(handle,
2040 if (len != AD_DATASZ_DOT_UND) {
2041 DBG_ERR("short write on %s: %zd", fsp_str_dbg(fsp), len);
2051 DBG_DEBUG("Path [%s] rc [%d]\n", fsp_str_dbg(fsp), rc);
2056 /*****************************************************************************
2058 *****************************************************************************/
2060 static bool is_afpinfo_stream(const struct smb_filename *smb_fname)
2062 if (strncasecmp_m(smb_fname->stream_name,
2063 AFPINFO_STREAM_NAME,
2064 strlen(AFPINFO_STREAM_NAME)) == 0) {
2070 static bool is_afpresource_stream(const struct smb_filename *smb_fname)
2072 if (strncasecmp_m(smb_fname->stream_name,
2073 AFPRESOURCE_STREAM_NAME,
2074 strlen(AFPRESOURCE_STREAM_NAME)) == 0) {
2081 * Test whether stream is an Apple stream.
2083 static bool is_apple_stream(const struct smb_filename *smb_fname)
2085 if (is_afpinfo_stream(smb_fname)) {
2088 if (is_afpresource_stream(smb_fname)) {
2094 static bool is_adouble_file(const char *path)
2096 const char *p = NULL;
2099 p = strrchr(path, '/');
2107 ADOUBLE_NAME_PREFIX,
2108 strlen(ADOUBLE_NAME_PREFIX));
2116 * Initialize config struct from our smb.conf config parameters
2118 static int init_fruit_config(vfs_handle_struct *handle)
2120 struct fruit_config_data *config;
2122 const char *tm_size_str = NULL;
2124 config = talloc_zero(handle->conn, struct fruit_config_data);
2126 DEBUG(1, ("talloc_zero() failed\n"));
2132 * Versions up to Samba 4.5.x had a spelling bug in the
2133 * fruit:resource option calling lp_parm_enum with
2134 * "res*s*ource" (ie two s).
2136 * In Samba 4.6 we accept both the wrong and the correct
2137 * spelling, in Samba 4.7 the bad spelling will be removed.
2139 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2140 "ressource", fruit_rsrc, FRUIT_RSRC_ADFILE);
2141 if (enumval == -1) {
2142 DEBUG(1, ("value for %s: resource type unknown\n",
2143 FRUIT_PARAM_TYPE_NAME));
2146 config->rsrc = (enum fruit_rsrc)enumval;
2148 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2149 "resource", fruit_rsrc, enumval);
2150 if (enumval == -1) {
2151 DEBUG(1, ("value for %s: resource type unknown\n",
2152 FRUIT_PARAM_TYPE_NAME));
2155 config->rsrc = (enum fruit_rsrc)enumval;
2157 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2158 "metadata", fruit_meta, FRUIT_META_NETATALK);
2159 if (enumval == -1) {
2160 DEBUG(1, ("value for %s: metadata type unknown\n",
2161 FRUIT_PARAM_TYPE_NAME));
2164 config->meta = (enum fruit_meta)enumval;
2166 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2167 "locking", fruit_locking, FRUIT_LOCKING_NONE);
2168 if (enumval == -1) {
2169 DEBUG(1, ("value for %s: locking type unknown\n",
2170 FRUIT_PARAM_TYPE_NAME));
2173 config->locking = (enum fruit_locking)enumval;
2175 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2176 "encoding", fruit_encoding, FRUIT_ENC_PRIVATE);
2177 if (enumval == -1) {
2178 DEBUG(1, ("value for %s: encoding type unknown\n",
2179 FRUIT_PARAM_TYPE_NAME));
2182 config->encoding = (enum fruit_encoding)enumval;
2184 if (config->rsrc == FRUIT_RSRC_ADFILE) {
2185 config->veto_appledouble = lp_parm_bool(SNUM(handle->conn),
2186 FRUIT_PARAM_TYPE_NAME,
2191 config->use_aapl = lp_parm_bool(
2192 -1, FRUIT_PARAM_TYPE_NAME, "aapl", true);
2194 config->time_machine = lp_parm_bool(
2195 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "time machine", false);
2197 config->unix_info_enabled = lp_parm_bool(
2198 -1, FRUIT_PARAM_TYPE_NAME, "nfs_aces", true);
2200 config->use_copyfile = lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME,
2203 config->posix_rename = lp_parm_bool(
2204 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "posix_rename", true);
2206 config->aapl_zero_file_id =
2207 lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME, "zero_file_id", true);
2209 config->readdir_attr_rsize = lp_parm_bool(
2210 SNUM(handle->conn), "readdir_attr", "aapl_rsize", true);
2212 config->readdir_attr_finder_info = lp_parm_bool(
2213 SNUM(handle->conn), "readdir_attr", "aapl_finder_info", true);
2215 config->readdir_attr_max_access = lp_parm_bool(
2216 SNUM(handle->conn), "readdir_attr", "aapl_max_access", true);
2218 config->model = lp_parm_const_string(
2219 -1, FRUIT_PARAM_TYPE_NAME, "model", "MacSamba");
2221 tm_size_str = lp_parm_const_string(
2222 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2223 "time machine max size", NULL);
2224 if (tm_size_str != NULL) {
2225 config->time_machine_max_size = conv_str_size(tm_size_str);
2228 config->wipe_intentionally_left_blank_rfork = lp_parm_bool(
2229 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2230 "wipe_intentionally_left_blank_rfork", false);
2232 config->delete_empty_adfiles = lp_parm_bool(
2233 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2234 "delete_empty_adfiles", false);
2236 SMB_VFS_HANDLE_SET_DATA(handle, config,
2237 NULL, struct fruit_config_data,
2244 * Prepend "._" to a basename
2245 * Return a new struct smb_filename with stream_name == NULL.
2247 static int adouble_path(TALLOC_CTX *ctx,
2248 const struct smb_filename *smb_fname_in,
2249 struct smb_filename **pp_smb_fname_out)
2253 struct smb_filename *smb_fname = cp_smb_filename(ctx,
2256 if (smb_fname == NULL) {
2260 /* We need streamname to be NULL */
2261 TALLOC_FREE(smb_fname->stream_name);
2263 /* And we're replacing base_name. */
2264 TALLOC_FREE(smb_fname->base_name);
2266 if (!parent_dirname(smb_fname, smb_fname_in->base_name,
2268 TALLOC_FREE(smb_fname);
2272 smb_fname->base_name = talloc_asprintf(smb_fname,
2273 "%s/._%s", parent, base);
2274 if (smb_fname->base_name == NULL) {
2275 TALLOC_FREE(smb_fname);
2279 *pp_smb_fname_out = smb_fname;
2285 * Allocate and initialize an AfpInfo struct
2287 static AfpInfo *afpinfo_new(TALLOC_CTX *ctx)
2289 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2293 ai->afpi_Signature = AFP_Signature;
2294 ai->afpi_Version = AFP_Version;
2295 ai->afpi_BackupTime = AD_DATE_START;
2300 * Pack an AfpInfo struct into a buffer
2302 * Buffer size must be at least AFP_INFO_SIZE
2303 * Returns size of packed buffer
2305 static ssize_t afpinfo_pack(const AfpInfo *ai, char *buf)
2307 memset(buf, 0, AFP_INFO_SIZE);
2309 RSIVAL(buf, 0, ai->afpi_Signature);
2310 RSIVAL(buf, 4, ai->afpi_Version);
2311 RSIVAL(buf, 12, ai->afpi_BackupTime);
2312 memcpy(buf + 16, ai->afpi_FinderInfo, sizeof(ai->afpi_FinderInfo));
2314 return AFP_INFO_SIZE;
2318 * Unpack a buffer into a AfpInfo structure
2320 * Buffer size must be at least AFP_INFO_SIZE
2321 * Returns allocated AfpInfo struct
2323 static AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data)
2325 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2330 ai->afpi_Signature = RIVAL(data, 0);
2331 ai->afpi_Version = RIVAL(data, 4);
2332 ai->afpi_BackupTime = RIVAL(data, 12);
2333 memcpy(ai->afpi_FinderInfo, (const char *)data + 16,
2334 sizeof(ai->afpi_FinderInfo));
2336 if (ai->afpi_Signature != AFP_Signature
2337 || ai->afpi_Version != AFP_Version) {
2338 DEBUG(1, ("Bad AfpInfo signature or version\n"));
2346 * Fake an inode number from the md5 hash of the (xattr) name
2348 static SMB_INO_T fruit_inode(const SMB_STRUCT_STAT *sbuf, const char *sname)
2350 gnutls_hash_hd_t hash_hnd = NULL;
2351 unsigned char hash[16];
2352 SMB_INO_T result = 0;
2356 DBG_DEBUG("fruit_inode called for %ju/%ju [%s]\n",
2357 (uintmax_t)sbuf->st_ex_dev,
2358 (uintmax_t)sbuf->st_ex_ino, sname);
2360 upper_sname = talloc_strdup_upper(talloc_tos(), sname);
2361 SMB_ASSERT(upper_sname != NULL);
2363 rc = gnutls_hash_init(&hash_hnd, GNUTLS_DIG_MD5);
2368 rc = gnutls_hash(hash_hnd, &(sbuf->st_ex_dev), sizeof(sbuf->st_ex_dev));
2370 gnutls_hash_deinit(hash_hnd, NULL);
2373 rc = gnutls_hash(hash_hnd,
2375 sizeof(sbuf->st_ex_ino));
2377 gnutls_hash_deinit(hash_hnd, NULL);
2380 rc = gnutls_hash(hash_hnd,
2382 talloc_get_size(upper_sname) - 1);
2384 gnutls_hash_deinit(hash_hnd, NULL);
2388 gnutls_hash_deinit(hash_hnd, hash);
2390 /* Hopefully all the variation is in the lower 4 (or 8) bytes! */
2391 memcpy(&result, hash, sizeof(result));
2394 DBG_DEBUG("fruit_inode \"%s\": ino=%ju\n",
2395 sname, (uintmax_t)result);
2398 TALLOC_FREE(upper_sname);
2403 static bool add_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
2404 struct stream_struct **streams,
2405 const char *name, off_t size,
2408 struct stream_struct *tmp;
2410 tmp = talloc_realloc(mem_ctx, *streams, struct stream_struct,
2416 tmp[*num_streams].name = talloc_asprintf(tmp, "%s:$DATA", name);
2417 if (tmp[*num_streams].name == NULL) {
2421 tmp[*num_streams].size = size;
2422 tmp[*num_streams].alloc_size = alloc_size;
2429 static bool filter_empty_rsrc_stream(unsigned int *num_streams,
2430 struct stream_struct **streams)
2432 struct stream_struct *tmp = *streams;
2435 if (*num_streams == 0) {
2439 for (i = 0; i < *num_streams; i++) {
2440 if (strequal_m(tmp[i].name, AFPRESOURCE_STREAM)) {
2445 if (i == *num_streams) {
2449 if (tmp[i].size > 0) {
2453 TALLOC_FREE(tmp[i].name);
2454 if (*num_streams - 1 > i) {
2455 memmove(&tmp[i], &tmp[i+1],
2456 (*num_streams - i - 1) * sizeof(struct stream_struct));
2463 static bool del_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
2464 struct stream_struct **streams,
2467 struct stream_struct *tmp = *streams;
2470 if (*num_streams == 0) {
2474 for (i = 0; i < *num_streams; i++) {
2475 if (strequal_m(tmp[i].name, name)) {
2480 if (i == *num_streams) {
2484 TALLOC_FREE(tmp[i].name);
2485 if (*num_streams - 1 > i) {
2486 memmove(&tmp[i], &tmp[i+1],
2487 (*num_streams - i - 1) * sizeof(struct stream_struct));
2494 static bool ad_empty_finderinfo(const struct adouble *ad)
2497 char emptybuf[ADEDLEN_FINDERI] = {0};
2500 fi = ad_get_entry(ad, ADEID_FINDERI);
2502 DBG_ERR("Missing FinderInfo in struct adouble [%p]\n", ad);
2506 cmp = memcmp(emptybuf, fi, ADEDLEN_FINDERI);
2510 static bool ai_empty_finderinfo(const AfpInfo *ai)
2513 char emptybuf[ADEDLEN_FINDERI] = {0};
2515 cmp = memcmp(emptybuf, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
2520 * Update btime with btime from Netatalk
2522 static void update_btime(vfs_handle_struct *handle,
2523 struct smb_filename *smb_fname)
2526 struct timespec creation_time = {0};
2528 struct fruit_config_data *config = NULL;
2530 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
2533 switch (config->meta) {
2534 case FRUIT_META_STREAM:
2536 case FRUIT_META_NETATALK:
2540 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
2544 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
2548 if (ad_getdate(ad, AD_DATE_UNIX | AD_DATE_CREATE, &t) != 0) {
2554 creation_time.tv_sec = convert_uint32_t_to_time_t(t);
2555 update_stat_ex_create_time(&smb_fname->st, creation_time);
2561 * Map an access mask to a Netatalk single byte byte range lock
2563 static off_t access_to_netatalk_brl(enum apple_fork fork_type,
2564 uint32_t access_mask)
2568 switch (access_mask) {
2569 case FILE_READ_DATA:
2570 offset = AD_FILELOCK_OPEN_RD;
2573 case FILE_WRITE_DATA:
2574 case FILE_APPEND_DATA:
2575 offset = AD_FILELOCK_OPEN_WR;
2579 offset = AD_FILELOCK_OPEN_NONE;
2583 if (fork_type == APPLE_FORK_RSRC) {
2584 if (offset == AD_FILELOCK_OPEN_NONE) {
2585 offset = AD_FILELOCK_RSRC_OPEN_NONE;
2595 * Map a deny mode to a Netatalk brl
2597 static off_t denymode_to_netatalk_brl(enum apple_fork fork_type,
2602 switch (deny_mode) {
2604 offset = AD_FILELOCK_DENY_RD;
2608 offset = AD_FILELOCK_DENY_WR;
2612 smb_panic("denymode_to_netatalk_brl: bad deny mode\n");
2615 if (fork_type == APPLE_FORK_RSRC) {
2623 * Call fcntl() with an exclusive F_GETLK request in order to
2624 * determine if there's an exisiting shared lock
2626 * @return true if the requested lock was found or any error occurred
2627 * false if the lock was not found
2629 static bool test_netatalk_lock(files_struct *fsp, off_t in_offset)
2632 off_t offset = in_offset;
2637 result = SMB_VFS_GETLOCK(fsp, &offset, &len, &type, &pid);
2638 if (result == false) {
2642 if (type != F_UNLCK) {
2649 static NTSTATUS fruit_check_access(vfs_handle_struct *handle,
2651 uint32_t access_mask,
2652 uint32_t share_mode)
2654 NTSTATUS status = NT_STATUS_OK;
2656 bool share_for_read = (share_mode & FILE_SHARE_READ);
2657 bool share_for_write = (share_mode & FILE_SHARE_WRITE);
2658 bool netatalk_already_open_for_reading = false;
2659 bool netatalk_already_open_for_writing = false;
2660 bool netatalk_already_open_with_deny_read = false;
2661 bool netatalk_already_open_with_deny_write = false;
2663 /* FIXME: hardcoded data fork, add resource fork */
2664 enum apple_fork fork_type = APPLE_FORK_DATA;
2666 DBG_DEBUG("fruit_check_access: %s, am: %s/%s, sm: 0x%x\n",
2668 access_mask & FILE_READ_DATA ? "READ" :"-",
2669 access_mask & FILE_WRITE_DATA ? "WRITE" : "-",
2672 if (fsp->fh->fd == -1) {
2673 return NT_STATUS_OK;
2676 /* Read NetATalk opens and deny modes on the file. */
2677 netatalk_already_open_for_reading = test_netatalk_lock(fsp,
2678 access_to_netatalk_brl(fork_type,
2681 netatalk_already_open_with_deny_read = test_netatalk_lock(fsp,
2682 denymode_to_netatalk_brl(fork_type,
2685 netatalk_already_open_for_writing = test_netatalk_lock(fsp,
2686 access_to_netatalk_brl(fork_type,
2689 netatalk_already_open_with_deny_write = test_netatalk_lock(fsp,
2690 denymode_to_netatalk_brl(fork_type,
2693 /* If there are any conflicts - sharing violation. */
2694 if ((access_mask & FILE_READ_DATA) &&
2695 netatalk_already_open_with_deny_read) {
2696 return NT_STATUS_SHARING_VIOLATION;
2699 if (!share_for_read &&
2700 netatalk_already_open_for_reading) {
2701 return NT_STATUS_SHARING_VIOLATION;
2704 if ((access_mask & FILE_WRITE_DATA) &&
2705 netatalk_already_open_with_deny_write) {
2706 return NT_STATUS_SHARING_VIOLATION;
2709 if (!share_for_write &&
2710 netatalk_already_open_for_writing) {
2711 return NT_STATUS_SHARING_VIOLATION;
2714 if (!(access_mask & FILE_READ_DATA)) {
2716 * Nothing we can do here, we need read access
2719 return NT_STATUS_OK;
2722 /* Set NetAtalk locks matching our access */
2723 if (access_mask & FILE_READ_DATA) {
2724 struct byte_range_lock *br_lck = NULL;
2726 off = access_to_netatalk_brl(fork_type, FILE_READ_DATA);
2728 handle->conn->sconn->msg_ctx, fsp,
2729 fsp->op->global->open_persistent_id, 1, off,
2730 READ_LOCK, POSIX_LOCK, false,
2733 TALLOC_FREE(br_lck);
2735 if (!NT_STATUS_IS_OK(status)) {
2740 if (!share_for_read) {
2741 struct byte_range_lock *br_lck = NULL;
2743 off = denymode_to_netatalk_brl(fork_type, DENY_READ);
2745 handle->conn->sconn->msg_ctx, fsp,
2746 fsp->op->global->open_persistent_id, 1, off,
2747 READ_LOCK, POSIX_LOCK, false,
2750 TALLOC_FREE(br_lck);
2752 if (!NT_STATUS_IS_OK(status)) {
2757 if (access_mask & FILE_WRITE_DATA) {
2758 struct byte_range_lock *br_lck = NULL;
2760 off = access_to_netatalk_brl(fork_type, FILE_WRITE_DATA);
2762 handle->conn->sconn->msg_ctx, fsp,
2763 fsp->op->global->open_persistent_id, 1, off,
2764 READ_LOCK, POSIX_LOCK, false,
2767 TALLOC_FREE(br_lck);
2769 if (!NT_STATUS_IS_OK(status)) {
2774 if (!share_for_write) {
2775 struct byte_range_lock *br_lck = NULL;
2777 off = denymode_to_netatalk_brl(fork_type, DENY_WRITE);
2779 handle->conn->sconn->msg_ctx, fsp,
2780 fsp->op->global->open_persistent_id, 1, off,
2781 READ_LOCK, POSIX_LOCK, false,
2784 TALLOC_FREE(br_lck);
2786 if (!NT_STATUS_IS_OK(status)) {
2791 return NT_STATUS_OK;
2794 static NTSTATUS check_aapl(vfs_handle_struct *handle,
2795 struct smb_request *req,
2796 const struct smb2_create_blobs *in_context_blobs,
2797 struct smb2_create_blobs *out_context_blobs)
2799 struct fruit_config_data *config;
2801 struct smb2_create_blob *aapl = NULL;
2805 DATA_BLOB blob = data_blob_talloc(req, NULL, 0);
2806 uint64_t req_bitmap, client_caps;
2807 uint64_t server_caps = SMB2_CRTCTX_AAPL_UNIX_BASED;
2811 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
2812 return NT_STATUS_UNSUCCESSFUL);
2814 if (!config->use_aapl
2815 || in_context_blobs == NULL
2816 || out_context_blobs == NULL) {
2817 return NT_STATUS_OK;
2820 aapl = smb2_create_blob_find(in_context_blobs,
2821 SMB2_CREATE_TAG_AAPL);
2823 return NT_STATUS_OK;
2826 if (aapl->data.length != 24) {
2827 DEBUG(1, ("unexpected AAPL ctxt length: %ju\n",
2828 (uintmax_t)aapl->data.length));
2829 return NT_STATUS_INVALID_PARAMETER;
2832 cmd = IVAL(aapl->data.data, 0);
2833 if (cmd != SMB2_CRTCTX_AAPL_SERVER_QUERY) {
2834 DEBUG(1, ("unsupported AAPL cmd: %d\n", cmd));
2835 return NT_STATUS_INVALID_PARAMETER;
2838 req_bitmap = BVAL(aapl->data.data, 8);
2839 client_caps = BVAL(aapl->data.data, 16);
2841 SIVAL(p, 0, SMB2_CRTCTX_AAPL_SERVER_QUERY);
2843 SBVAL(p, 8, req_bitmap);
2844 ok = data_blob_append(req, &blob, p, 16);
2846 return NT_STATUS_UNSUCCESSFUL;
2849 if (req_bitmap & SMB2_CRTCTX_AAPL_SERVER_CAPS) {
2850 if ((client_caps & SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR) &&
2851 (handle->conn->tcon->compat->fs_capabilities & FILE_NAMED_STREAMS)) {
2852 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR;
2853 config->readdir_attr_enabled = true;
2856 if (config->use_copyfile) {
2857 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_OSX_COPYFILE;
2858 config->copyfile_enabled = true;
2862 * The client doesn't set the flag, so we can't check
2863 * for it and just set it unconditionally
2865 if (config->unix_info_enabled) {
2866 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_NFS_ACE;
2869 SBVAL(p, 0, server_caps);
2870 ok = data_blob_append(req, &blob, p, 8);
2872 return NT_STATUS_UNSUCCESSFUL;
2876 if (req_bitmap & SMB2_CRTCTX_AAPL_VOLUME_CAPS) {
2877 int val = lp_case_sensitive(SNUM(handle->conn->tcon->compat));
2885 caps |= SMB2_CRTCTX_AAPL_CASE_SENSITIVE;
2892 if (config->time_machine) {
2893 caps |= SMB2_CRTCTX_AAPL_FULL_SYNC;
2898 ok = data_blob_append(req, &blob, p, 8);
2900 return NT_STATUS_UNSUCCESSFUL;
2904 if (req_bitmap & SMB2_CRTCTX_AAPL_MODEL_INFO) {
2905 ok = convert_string_talloc(req,
2906 CH_UNIX, CH_UTF16LE,
2907 config->model, strlen(config->model),
2910 return NT_STATUS_UNSUCCESSFUL;
2914 SIVAL(p + 4, 0, modellen);
2915 ok = data_blob_append(req, &blob, p, 8);
2918 return NT_STATUS_UNSUCCESSFUL;
2921 ok = data_blob_append(req, &blob, model, modellen);
2924 return NT_STATUS_UNSUCCESSFUL;
2928 status = smb2_create_blob_add(out_context_blobs,
2930 SMB2_CREATE_TAG_AAPL,
2932 if (NT_STATUS_IS_OK(status)) {
2933 global_fruit_config.nego_aapl = true;
2934 if (config->aapl_zero_file_id) {
2935 aapl_force_zero_file_id(handle->conn->sconn);
2942 static bool readdir_attr_meta_finderi_stream(
2943 struct vfs_handle_struct *handle,
2944 const struct smb_filename *smb_fname,
2947 struct smb_filename *stream_name = NULL;
2948 files_struct *fsp = NULL;
2953 uint8_t buf[AFP_INFO_SIZE];
2955 stream_name = synthetic_smb_fname(talloc_tos(),
2956 smb_fname->base_name,
2957 AFPINFO_STREAM_NAME,
2958 NULL, smb_fname->flags);
2959 if (stream_name == NULL) {
2963 ret = SMB_VFS_STAT(handle->conn, stream_name);
2968 status = SMB_VFS_CREATE_FILE(
2969 handle->conn, /* conn */
2971 0, /* root_dir_fid */
2972 stream_name, /* fname */
2973 FILE_READ_DATA, /* access_mask */
2974 (FILE_SHARE_READ | FILE_SHARE_WRITE | /* share_access */
2976 FILE_OPEN, /* create_disposition*/
2977 0, /* create_options */
2978 0, /* file_attributes */
2979 INTERNAL_OPEN_ONLY, /* oplock_request */
2981 0, /* allocation_size */
2982 0, /* private_flags */
2987 NULL, NULL); /* create context */
2989 TALLOC_FREE(stream_name);
2991 if (!NT_STATUS_IS_OK(status)) {
2995 nread = SMB_VFS_PREAD(fsp, &buf[0], AFP_INFO_SIZE, 0);
2996 if (nread != AFP_INFO_SIZE) {
2997 DBG_ERR("short read [%s] [%zd/%d]\n",
2998 smb_fname_str_dbg(stream_name), nread, AFP_INFO_SIZE);
3003 memcpy(&ai->afpi_FinderInfo[0], &buf[AFP_OFF_FinderInfo],
3010 close_file(NULL, fsp, NORMAL_CLOSE);
3016 static bool readdir_attr_meta_finderi_netatalk(
3017 struct vfs_handle_struct *handle,
3018 const struct smb_filename *smb_fname,
3021 struct adouble *ad = NULL;
3024 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
3029 p = ad_get_entry(ad, ADEID_FINDERI);
3031 DBG_ERR("No ADEID_FINDERI for [%s]\n", smb_fname->base_name);
3036 memcpy(&ai->afpi_FinderInfo[0], p, AFP_FinderSize);
3041 static bool readdir_attr_meta_finderi(struct vfs_handle_struct *handle,
3042 const struct smb_filename *smb_fname,
3043 struct readdir_attr_data *attr_data)
3045 struct fruit_config_data *config = NULL;
3046 uint32_t date_added;
3050 SMB_VFS_HANDLE_GET_DATA(handle, config,
3051 struct fruit_config_data,
3054 switch (config->meta) {
3055 case FRUIT_META_NETATALK:
3056 ok = readdir_attr_meta_finderi_netatalk(
3057 handle, smb_fname, &ai);
3060 case FRUIT_META_STREAM:
3061 ok = readdir_attr_meta_finderi_stream(
3062 handle, smb_fname, &ai);
3066 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3071 /* Don't bother with errors, it's likely ENOENT */
3075 if (S_ISREG(smb_fname->st.st_ex_mode)) {
3077 memcpy(&attr_data->attr_data.aapl.finder_info[0],
3078 &ai.afpi_FinderInfo[0], 4);
3080 /* finder_creator */
3081 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 4,
3082 &ai.afpi_FinderInfo[4], 4);
3086 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 8,
3087 &ai.afpi_FinderInfo[8], 2);
3089 /* finder_ext_flags */
3090 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 10,
3091 &ai.afpi_FinderInfo[24], 2);
3094 date_added = convert_time_t_to_uint32_t(
3095 smb_fname->st.st_ex_btime.tv_sec - AD_DATE_DELTA);
3097 RSIVAL(&attr_data->attr_data.aapl.finder_info[0], 12, date_added);
3102 static uint64_t readdir_attr_rfork_size_adouble(
3103 struct vfs_handle_struct *handle,
3104 const struct smb_filename *smb_fname)
3106 struct adouble *ad = NULL;
3107 uint64_t rfork_size;
3109 ad = ad_get(talloc_tos(), handle, smb_fname,
3115 rfork_size = ad_getentrylen(ad, ADEID_RFORK);
3121 static uint64_t readdir_attr_rfork_size_stream(
3122 struct vfs_handle_struct *handle,
3123 const struct smb_filename *smb_fname)
3125 struct smb_filename *stream_name = NULL;
3127 uint64_t rfork_size;
3129 stream_name = synthetic_smb_fname(talloc_tos(),
3130 smb_fname->base_name,
3131 AFPRESOURCE_STREAM_NAME,
3133 if (stream_name == NULL) {
3137 ret = SMB_VFS_STAT(handle->conn, stream_name);
3139 TALLOC_FREE(stream_name);
3143 rfork_size = stream_name->st.st_ex_size;
3144 TALLOC_FREE(stream_name);
3149 static uint64_t readdir_attr_rfork_size(struct vfs_handle_struct *handle,
3150 const struct smb_filename *smb_fname)
3152 struct fruit_config_data *config = NULL;
3153 uint64_t rfork_size;
3155 SMB_VFS_HANDLE_GET_DATA(handle, config,
3156 struct fruit_config_data,
3159 switch (config->rsrc) {
3160 case FRUIT_RSRC_ADFILE:
3161 rfork_size = readdir_attr_rfork_size_adouble(handle,
3165 case FRUIT_RSRC_XATTR:
3166 case FRUIT_RSRC_STREAM:
3167 rfork_size = readdir_attr_rfork_size_stream(handle,
3172 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3180 static NTSTATUS readdir_attr_macmeta(struct vfs_handle_struct *handle,
3181 const struct smb_filename *smb_fname,
3182 struct readdir_attr_data *attr_data)
3184 NTSTATUS status = NT_STATUS_OK;
3185 struct fruit_config_data *config = NULL;
3188 SMB_VFS_HANDLE_GET_DATA(handle, config,
3189 struct fruit_config_data,
3190 return NT_STATUS_UNSUCCESSFUL);
3193 /* Ensure we return a default value in the creation_date field */
3194 RSIVAL(&attr_data->attr_data.aapl.finder_info, 12, AD_DATE_START);
3197 * Resource fork length
3200 if (config->readdir_attr_rsize) {
3201 uint64_t rfork_size;
3203 rfork_size = readdir_attr_rfork_size(handle, smb_fname);
3204 attr_data->attr_data.aapl.rfork_size = rfork_size;
3211 if (config->readdir_attr_finder_info) {
3212 ok = readdir_attr_meta_finderi(handle, smb_fname, attr_data);
3214 status = NT_STATUS_INTERNAL_ERROR;
3221 static NTSTATUS remove_virtual_nfs_aces(struct security_descriptor *psd)
3226 if (psd->dacl == NULL) {
3227 return NT_STATUS_OK;
3230 for (i = 0; i < psd->dacl->num_aces; i++) {
3231 /* MS NFS style mode/uid/gid */
3232 int cmp = dom_sid_compare_domain(
3233 &global_sid_Unix_NFS,
3234 &psd->dacl->aces[i].trustee);
3236 /* Normal ACE entry. */
3241 * security_descriptor_dacl_del()
3242 * *must* return NT_STATUS_OK as we know
3243 * we have something to remove.
3246 status = security_descriptor_dacl_del(psd,
3247 &psd->dacl->aces[i].trustee);
3248 if (!NT_STATUS_IS_OK(status)) {
3249 DBG_WARNING("failed to remove MS NFS style ACE: %s\n",
3255 * security_descriptor_dacl_del() may delete more
3256 * then one entry subsequent to this one if the
3257 * SID matches, but we only need to ensure that
3258 * we stay looking at the same element in the array.
3262 return NT_STATUS_OK;
3265 /* Search MS NFS style ACE with UNIX mode */
3266 static NTSTATUS check_ms_nfs(vfs_handle_struct *handle,
3268 struct security_descriptor *psd,
3273 struct fruit_config_data *config = NULL;
3277 SMB_VFS_HANDLE_GET_DATA(handle, config,
3278 struct fruit_config_data,
3279 return NT_STATUS_UNSUCCESSFUL);
3281 if (!global_fruit_config.nego_aapl) {
3282 return NT_STATUS_OK;
3284 if (psd->dacl == NULL || !config->unix_info_enabled) {
3285 return NT_STATUS_OK;
3288 for (i = 0; i < psd->dacl->num_aces; i++) {
3289 if (dom_sid_compare_domain(
3290 &global_sid_Unix_NFS_Mode,
3291 &psd->dacl->aces[i].trustee) == 0) {
3292 *pmode = (mode_t)psd->dacl->aces[i].trustee.sub_auths[2];
3293 *pmode &= (S_IRWXU | S_IRWXG | S_IRWXO);
3296 DEBUG(10, ("MS NFS chmod request %s, %04o\n",
3297 fsp_str_dbg(fsp), (unsigned)(*pmode)));
3303 * Remove any incoming virtual ACE entries generated by
3304 * fruit_fget_nt_acl().
3307 return remove_virtual_nfs_aces(psd);
3310 /****************************************************************************
3312 ****************************************************************************/
3314 static int fruit_connect(vfs_handle_struct *handle,
3315 const char *service,
3319 char *list = NULL, *newlist = NULL;
3320 struct fruit_config_data *config;
3322 DEBUG(10, ("fruit_connect\n"));
3324 rc = SMB_VFS_NEXT_CONNECT(handle, service, user);
3329 rc = init_fruit_config(handle);
3334 SMB_VFS_HANDLE_GET_DATA(handle, config,
3335 struct fruit_config_data, return -1);
3337 if (config->veto_appledouble) {
3338 list = lp_veto_files(talloc_tos(), SNUM(handle->conn));
3341 if (strstr(list, "/" ADOUBLE_NAME_PREFIX "*/") == NULL) {
3342 newlist = talloc_asprintf(
3344 "%s/" ADOUBLE_NAME_PREFIX "*/",
3346 lp_do_parameter(SNUM(handle->conn),
3351 lp_do_parameter(SNUM(handle->conn),
3353 "/" ADOUBLE_NAME_PREFIX "*/");
3359 if (config->encoding == FRUIT_ENC_NATIVE) {
3360 lp_do_parameter(SNUM(handle->conn),
3365 if (config->time_machine) {
3366 DBG_NOTICE("Enabling durable handles for Time Machine "
3367 "support on [%s]\n", service);
3368 lp_do_parameter(SNUM(handle->conn), "durable handles", "yes");
3369 lp_do_parameter(SNUM(handle->conn), "kernel oplocks", "no");
3370 lp_do_parameter(SNUM(handle->conn), "kernel share modes", "no");
3371 if (!lp_strict_sync(SNUM(handle->conn))) {
3372 DBG_WARNING("Time Machine without strict sync is not "
3375 lp_do_parameter(SNUM(handle->conn), "posix locking", "no");
3381 static int fruit_fake_fd(void)
3388 * Return a valid fd, but ensure any attempt to use it returns
3389 * an error (EPIPE). Once we get a write on the handle, we open
3392 ret = pipe(pipe_fds);
3402 static int fruit_open_meta_stream(vfs_handle_struct *handle,
3403 struct smb_filename *smb_fname,
3408 struct fruit_config_data *config = NULL;
3409 struct fio *fio = NULL;
3410 int open_flags = flags & ~O_CREAT;
3413 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3415 SMB_VFS_HANDLE_GET_DATA(handle, config,
3416 struct fruit_config_data, return -1);
3418 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3419 fio->type = ADOUBLE_META;
3420 fio->config = config;
3422 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, open_flags, mode);
3427 if (!(flags & O_CREAT)) {
3428 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
3432 fd = fruit_fake_fd();
3434 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
3438 fio->fake_fd = true;
3445 static int fruit_open_meta_netatalk(vfs_handle_struct *handle,
3446 struct smb_filename *smb_fname,
3451 struct fruit_config_data *config = NULL;
3452 struct fio *fio = NULL;
3453 struct adouble *ad = NULL;
3454 bool meta_exists = false;
3457 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3459 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
3466 if (!meta_exists && !(flags & O_CREAT)) {
3471 fd = fruit_fake_fd();
3476 SMB_VFS_HANDLE_GET_DATA(handle, config,
3477 struct fruit_config_data, return -1);
3479 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3480 fio->type = ADOUBLE_META;
3481 fio->config = config;
3482 fio->fake_fd = true;
3489 static int fruit_open_meta(vfs_handle_struct *handle,
3490 struct smb_filename *smb_fname,
3491 files_struct *fsp, int flags, mode_t mode)
3494 struct fruit_config_data *config = NULL;
3496 DBG_DEBUG("path [%s]\n", smb_fname_str_dbg(smb_fname));
3498 SMB_VFS_HANDLE_GET_DATA(handle, config,
3499 struct fruit_config_data, return -1);
3501 switch (config->meta) {
3502 case FRUIT_META_STREAM:
3503 fd = fruit_open_meta_stream(handle, smb_fname,
3507 case FRUIT_META_NETATALK:
3508 fd = fruit_open_meta_netatalk(handle, smb_fname,
3513 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3517 DBG_DEBUG("path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3522 static int fruit_open_rsrc_adouble(vfs_handle_struct *handle,
3523 struct smb_filename *smb_fname,
3529 struct adouble *ad = NULL;
3530 struct smb_filename *smb_fname_base = NULL;
3531 struct fruit_config_data *config = NULL;
3534 SMB_VFS_HANDLE_GET_DATA(handle, config,
3535 struct fruit_config_data, return -1);
3537 if ((!(flags & O_CREAT)) &&
3538 S_ISDIR(fsp->base_fsp->fsp_name->st.st_ex_mode))
3540 /* sorry, but directories don't habe a resource fork */
3545 rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_base);
3550 /* We always need read/write access for the metadata header too */
3551 flags &= ~(O_RDONLY | O_WRONLY);
3554 hostfd = SMB_VFS_NEXT_OPEN(handle, smb_fname_base, fsp,
3561 if (flags & (O_CREAT | O_TRUNC)) {
3562 ad = ad_init(fsp, ADOUBLE_RSRC);
3568 fsp->fh->fd = hostfd;
3570 rc = ad_fset(handle, ad, fsp);
3581 TALLOC_FREE(smb_fname_base);
3583 DEBUG(10, ("fruit_open resource fork: rc=%d, fd=%d\n", rc, hostfd));
3585 int saved_errno = errno;
3588 * BUGBUGBUG -- we would need to call
3589 * fd_close_posix here, but we don't have a
3592 fsp->fh->fd = hostfd;
3596 errno = saved_errno;
3601 static int fruit_open_rsrc_xattr(vfs_handle_struct *handle,
3602 struct smb_filename *smb_fname,
3607 #ifdef HAVE_ATTROPEN
3610 fd = attropen(smb_fname->base_name,
3611 AFPRESOURCE_EA_NETATALK,
3626 static int fruit_open_rsrc(vfs_handle_struct *handle,
3627 struct smb_filename *smb_fname,
3628 files_struct *fsp, int flags, mode_t mode)
3631 struct fruit_config_data *config = NULL;
3632 struct fio *fio = NULL;
3634 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3636 SMB_VFS_HANDLE_GET_DATA(handle, config,
3637 struct fruit_config_data, return -1);
3639 switch (config->rsrc) {
3640 case FRUIT_RSRC_STREAM:
3641 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3644 case FRUIT_RSRC_ADFILE:
3645 fd = fruit_open_rsrc_adouble(handle, smb_fname,
3649 case FRUIT_RSRC_XATTR:
3650 fd = fruit_open_rsrc_xattr(handle, smb_fname,
3655 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3659 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3665 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3666 fio->type = ADOUBLE_RSRC;
3667 fio->config = config;
3672 static int fruit_open(vfs_handle_struct *handle,
3673 struct smb_filename *smb_fname,
3674 files_struct *fsp, int flags, mode_t mode)
3678 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3680 if (!is_ntfs_stream_smb_fname(smb_fname)) {
3681 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3684 if (is_afpinfo_stream(smb_fname)) {
3685 fd = fruit_open_meta(handle, smb_fname, fsp, flags, mode);
3686 } else if (is_afpresource_stream(smb_fname)) {
3687 fd = fruit_open_rsrc(handle, smb_fname, fsp, flags, mode);
3689 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3692 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3697 static int fruit_close_meta(vfs_handle_struct *handle,
3701 struct fruit_config_data *config = NULL;
3703 SMB_VFS_HANDLE_GET_DATA(handle, config,
3704 struct fruit_config_data, return -1);
3706 switch (config->meta) {
3707 case FRUIT_META_STREAM:
3708 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3711 case FRUIT_META_NETATALK:
3712 ret = close(fsp->fh->fd);
3717 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3725 static int fruit_close_rsrc(vfs_handle_struct *handle,
3729 struct fruit_config_data *config = NULL;
3731 SMB_VFS_HANDLE_GET_DATA(handle, config,
3732 struct fruit_config_data, return -1);
3734 switch (config->rsrc) {
3735 case FRUIT_RSRC_STREAM:
3736 case FRUIT_RSRC_ADFILE:
3737 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3740 case FRUIT_RSRC_XATTR:
3741 ret = close(fsp->fh->fd);
3746 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3753 static int fruit_close(vfs_handle_struct *handle,
3761 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(fsp->fsp_name), fd);
3763 if (!is_ntfs_stream_smb_fname(fsp->fsp_name)) {
3764 return SMB_VFS_NEXT_CLOSE(handle, fsp);
3767 if (is_afpinfo_stream(fsp->fsp_name)) {
3768 ret = fruit_close_meta(handle, fsp);
3769 } else if (is_afpresource_stream(fsp->fsp_name)) {
3770 ret = fruit_close_rsrc(handle, fsp);
3772 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3778 static int fruit_rename(struct vfs_handle_struct *handle,
3779 const struct smb_filename *smb_fname_src,
3780 const struct smb_filename *smb_fname_dst)
3783 struct fruit_config_data *config = NULL;
3784 struct smb_filename *src_adp_smb_fname = NULL;
3785 struct smb_filename *dst_adp_smb_fname = NULL;
3787 SMB_VFS_HANDLE_GET_DATA(handle, config,
3788 struct fruit_config_data, return -1);
3790 if (!VALID_STAT(smb_fname_src->st)) {
3791 DBG_ERR("Need valid stat for [%s]\n",
3792 smb_fname_str_dbg(smb_fname_src));
3796 rc = SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
3801 if ((config->rsrc != FRUIT_RSRC_ADFILE) ||
3802 (!S_ISREG(smb_fname_src->st.st_ex_mode)))
3807 rc = adouble_path(talloc_tos(), smb_fname_src, &src_adp_smb_fname);
3812 rc = adouble_path(talloc_tos(), smb_fname_dst, &dst_adp_smb_fname);
3817 DBG_DEBUG("%s -> %s\n",
3818 smb_fname_str_dbg(src_adp_smb_fname),
3819 smb_fname_str_dbg(dst_adp_smb_fname));
3821 rc = SMB_VFS_NEXT_RENAME(handle, src_adp_smb_fname, dst_adp_smb_fname);
3822 if (errno == ENOENT) {
3827 TALLOC_FREE(src_adp_smb_fname);
3828 TALLOC_FREE(dst_adp_smb_fname);
3832 static int fruit_unlink_meta_stream(vfs_handle_struct *handle,
3833 const struct smb_filename *smb_fname)
3835 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3838 static int fruit_unlink_meta_netatalk(vfs_handle_struct *handle,
3839 const struct smb_filename *smb_fname)
3841 return SMB_VFS_REMOVEXATTR(handle->conn,
3843 AFPINFO_EA_NETATALK);
3846 static int fruit_unlink_meta(vfs_handle_struct *handle,
3847 const struct smb_filename *smb_fname)
3849 struct fruit_config_data *config = NULL;
3852 SMB_VFS_HANDLE_GET_DATA(handle, config,
3853 struct fruit_config_data, return -1);
3855 switch (config->meta) {
3856 case FRUIT_META_STREAM:
3857 rc = fruit_unlink_meta_stream(handle, smb_fname);
3860 case FRUIT_META_NETATALK:
3861 rc = fruit_unlink_meta_netatalk(handle, smb_fname);
3865 DBG_ERR("Unsupported meta config [%d]\n", config->meta);
3872 static int fruit_unlink_rsrc_stream(vfs_handle_struct *handle,
3873 const struct smb_filename *smb_fname,
3878 if (!force_unlink) {
3879 struct smb_filename *smb_fname_cp = NULL;
3882 smb_fname_cp = cp_smb_filename(talloc_tos(), smb_fname);
3883 if (smb_fname_cp == NULL) {
3888 * 0 byte resource fork streams are not listed by
3889 * vfs_streaminfo, as a result stream cleanup/deletion of file
3890 * deletion doesn't remove the resourcefork stream.
3893 ret = SMB_VFS_NEXT_STAT(handle, smb_fname_cp);
3895 TALLOC_FREE(smb_fname_cp);
3896 DBG_ERR("stat [%s] failed [%s]\n",
3897 smb_fname_str_dbg(smb_fname_cp), strerror(errno));
3901 size = smb_fname_cp->st.st_ex_size;
3902 TALLOC_FREE(smb_fname_cp);
3905 /* OS X ignores resource fork stream delete requests */
3910 ret = SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3911 if ((ret != 0) && (errno == ENOENT) && force_unlink) {
3918 static int fruit_unlink_rsrc_adouble(vfs_handle_struct *handle,
3919 const struct smb_filename *smb_fname,
3923 struct adouble *ad = NULL;
3924 struct smb_filename *adp_smb_fname = NULL;
3926 if (!force_unlink) {
3927 ad = ad_get(talloc_tos(), handle, smb_fname,
3936 * 0 byte resource fork streams are not listed by
3937 * vfs_streaminfo, as a result stream cleanup/deletion of file
3938 * deletion doesn't remove the resourcefork stream.
3941 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
3942 /* OS X ignores resource fork stream delete requests */
3950 rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
3955 rc = SMB_VFS_NEXT_UNLINK(handle, adp_smb_fname);
3956 TALLOC_FREE(adp_smb_fname);
3957 if ((rc != 0) && (errno == ENOENT) && force_unlink) {
3964 static int fruit_unlink_rsrc_xattr(vfs_handle_struct *handle,
3965 const struct smb_filename *smb_fname,
3969 * OS X ignores resource fork stream delete requests, so nothing to do
3970 * here. Removing the file will remove the xattr anyway, so we don't
3971 * have to take care of removing 0 byte resource forks that could be
3977 static int fruit_unlink_rsrc(vfs_handle_struct *handle,
3978 const struct smb_filename *smb_fname,
3981 struct fruit_config_data *config = NULL;
3984 SMB_VFS_HANDLE_GET_DATA(handle, config,
3985 struct fruit_config_data, return -1);
3987 switch (config->rsrc) {
3988 case FRUIT_RSRC_STREAM:
3989 rc = fruit_unlink_rsrc_stream(handle, smb_fname, force_unlink);
3992 case FRUIT_RSRC_ADFILE:
3993 rc = fruit_unlink_rsrc_adouble(handle, smb_fname, force_unlink);
3996 case FRUIT_RSRC_XATTR:
3997 rc = fruit_unlink_rsrc_xattr(handle, smb_fname, force_unlink);
4001 DBG_ERR("Unsupported rsrc config [%d]\n", config->rsrc);
4008 static int fruit_unlink(vfs_handle_struct *handle,
4009 const struct smb_filename *smb_fname)
4012 struct fruit_config_data *config = NULL;
4013 struct smb_filename *rsrc_smb_fname = NULL;
4015 SMB_VFS_HANDLE_GET_DATA(handle, config,
4016 struct fruit_config_data, return -1);
4018 if (is_afpinfo_stream(smb_fname)) {
4019 return fruit_unlink_meta(handle, smb_fname);
4020 } else if (is_afpresource_stream(smb_fname)) {
4021 return fruit_unlink_rsrc(handle, smb_fname, false);
4022 } else if (is_ntfs_stream_smb_fname(smb_fname)) {
4023 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4024 } else if (is_adouble_file(smb_fname->base_name)) {
4025 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4029 * A request to delete the base file. Because 0 byte resource
4030 * fork streams are not listed by fruit_streaminfo,
4031 * delete_all_streams() can't remove 0 byte resource fork
4032 * streams, so we have to cleanup this here.
4034 rsrc_smb_fname = synthetic_smb_fname(talloc_tos(),
4035 smb_fname->base_name,
4036 AFPRESOURCE_STREAM_NAME,
4039 if (rsrc_smb_fname == NULL) {
4043 rc = fruit_unlink_rsrc(handle, rsrc_smb_fname, true);
4044 if ((rc != 0) && (errno != ENOENT)) {
4045 DBG_ERR("Forced unlink of [%s] failed [%s]\n",
4046 smb_fname_str_dbg(rsrc_smb_fname), strerror(errno));
4047 TALLOC_FREE(rsrc_smb_fname);
4050 TALLOC_FREE(rsrc_smb_fname);
4052 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4055 static int fruit_chmod(vfs_handle_struct *handle,
4056 const struct smb_filename *smb_fname,
4060 struct fruit_config_data *config = NULL;
4061 struct smb_filename *smb_fname_adp = NULL;
4063 rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname, mode);
4068 SMB_VFS_HANDLE_GET_DATA(handle, config,
4069 struct fruit_config_data, return -1);
4071 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4075 if (!VALID_STAT(smb_fname->st)) {
4079 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
4083 rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_adp);
4088 DEBUG(10, ("fruit_chmod: %s\n", smb_fname_adp->base_name));
4090 rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname_adp, mode);
4091 if (errno == ENOENT) {
4095 TALLOC_FREE(smb_fname_adp);
4099 static int fruit_chown(vfs_handle_struct *handle,
4100 const struct smb_filename *smb_fname,
4105 struct fruit_config_data *config = NULL;
4106 struct smb_filename *adp_smb_fname = NULL;
4108 rc = SMB_VFS_NEXT_CHOWN(handle, smb_fname, uid, gid);
4113 SMB_VFS_HANDLE_GET_DATA(handle, config,
4114 struct fruit_config_data, return -1);
4116 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4120 if (!VALID_STAT(smb_fname->st)) {
4124 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
4128 rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
4133 DEBUG(10, ("fruit_chown: %s\n", adp_smb_fname->base_name));
4135 rc = SMB_VFS_NEXT_CHOWN(handle, adp_smb_fname, uid, gid);
4136 if (errno == ENOENT) {
4141 TALLOC_FREE(adp_smb_fname);
4145 static int fruit_rmdir(struct vfs_handle_struct *handle,
4146 const struct smb_filename *smb_fname)
4150 struct fruit_config_data *config;
4152 SMB_VFS_HANDLE_GET_DATA(handle, config,
4153 struct fruit_config_data, return -1);
4155 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4160 * Due to there is no way to change bDeleteVetoFiles variable
4161 * from this module, need to clean up ourselves
4164 dh = SMB_VFS_OPENDIR(handle->conn, smb_fname, NULL, 0);
4169 while ((de = SMB_VFS_READDIR(handle->conn, dh, NULL)) != NULL) {
4170 struct adouble *ad = NULL;
4172 struct smb_filename *ad_smb_fname = NULL;
4175 if (!is_adouble_file(de->d_name)) {
4179 p = talloc_asprintf(talloc_tos(), "%s/%s",
4180 smb_fname->base_name, de->d_name);
4182 DBG_ERR("talloc_asprintf failed\n");
4186 ad_smb_fname = synthetic_smb_fname(talloc_tos(), p,
4190 if (ad_smb_fname == NULL) {
4191 DBG_ERR("synthetic_smb_fname failed\n");
4196 * Check whether it's a valid AppleDouble file, if
4197 * yes, delete it, ignore it otherwise.
4199 ad = ad_get(talloc_tos(), handle, ad_smb_fname, ADOUBLE_RSRC);
4201 TALLOC_FREE(ad_smb_fname);
4207 ret = SMB_VFS_NEXT_UNLINK(handle, ad_smb_fname);
4209 DBG_ERR("Deleting [%s] failed\n",
4210 smb_fname_str_dbg(ad_smb_fname));
4212 TALLOC_FREE(ad_smb_fname);
4217 SMB_VFS_CLOSEDIR(handle->conn, dh);
4219 return SMB_VFS_NEXT_RMDIR(handle, smb_fname);
4222 static ssize_t fruit_pread_meta_stream(vfs_handle_struct *handle,
4223 files_struct *fsp, void *data,
4224 size_t n, off_t offset)
4229 nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4230 if (nread == -1 || nread == n) {
4234 DBG_ERR("Removing [%s] after short read [%zd]\n",
4235 fsp_str_dbg(fsp), nread);
4237 ret = SMB_VFS_NEXT_UNLINK(handle, fsp->fsp_name);
4239 DBG_ERR("Removing [%s] failed\n", fsp_str_dbg(fsp));
4247 static ssize_t fruit_pread_meta_adouble(vfs_handle_struct *handle,
4248 files_struct *fsp, void *data,
4249 size_t n, off_t offset)
4252 struct adouble *ad = NULL;
4253 char afpinfo_buf[AFP_INFO_SIZE];
4257 ai = afpinfo_new(talloc_tos());
4262 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
4268 p = ad_get_entry(ad, ADEID_FINDERI);
4270 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
4275 memcpy(&ai->afpi_FinderInfo[0], p, ADEDLEN_FINDERI);
4277 nread = afpinfo_pack(ai, afpinfo_buf);
4278 if (nread != AFP_INFO_SIZE) {
4283 memcpy(data, afpinfo_buf, n);
4291 static ssize_t fruit_pread_meta(vfs_handle_struct *handle,
4292 files_struct *fsp, void *data,
4293 size_t n, off_t offset)
4295 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4300 * OS X has a off-by-1 error in the offset calculation, so we're
4301 * bug compatible here. It won't hurt, as any relevant real
4302 * world read requests from the AFP_AfpInfo stream will be
4303 * offset=0 n=60. offset is ignored anyway, see below.
4305 if ((offset < 0) || (offset >= AFP_INFO_SIZE + 1)) {
4310 DBG_ERR("Failed to fetch fsp extension");
4314 /* Yes, macOS always reads from offset 0 */
4316 to_return = MIN(n, AFP_INFO_SIZE);
4318 switch (fio->config->meta) {
4319 case FRUIT_META_STREAM:
4320 nread = fruit_pread_meta_stream(handle, fsp, data,
4324 case FRUIT_META_NETATALK:
4325 nread = fruit_pread_meta_adouble(handle, fsp, data,
4330 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
4334 if (nread == -1 && fio->created) {
4336 char afpinfo_buf[AFP_INFO_SIZE];
4338 ai = afpinfo_new(talloc_tos());
4343 nread = afpinfo_pack(ai, afpinfo_buf);
4345 if (nread != AFP_INFO_SIZE) {
4349 memcpy(data, afpinfo_buf, to_return);
4356 static ssize_t fruit_pread_rsrc_stream(vfs_handle_struct *handle,
4357 files_struct *fsp, void *data,
4358 size_t n, off_t offset)
4360 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4363 static ssize_t fruit_pread_rsrc_xattr(vfs_handle_struct *handle,
4364 files_struct *fsp, void *data,
4365 size_t n, off_t offset)
4367 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4370 static ssize_t fruit_pread_rsrc_adouble(vfs_handle_struct *handle,
4371 files_struct *fsp, void *data,
4372 size_t n, off_t offset)
4374 struct adouble *ad = NULL;
4377 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
4382 nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n,
4383 offset + ad_getentryoff(ad, ADEID_RFORK));
4389 static ssize_t fruit_pread_rsrc(vfs_handle_struct *handle,
4390 files_struct *fsp, void *data,
4391 size_t n, off_t offset)
4393 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4401 switch (fio->config->rsrc) {
4402 case FRUIT_RSRC_STREAM:
4403 nread = fruit_pread_rsrc_stream(handle, fsp, data, n, offset);
4406 case FRUIT_RSRC_ADFILE:
4407 nread = fruit_pread_rsrc_adouble(handle, fsp, data, n, offset);
4410 case FRUIT_RSRC_XATTR:
4411 nread = fruit_pread_rsrc_xattr(handle, fsp, data, n, offset);
4415 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
4422 static ssize_t fruit_pread(vfs_handle_struct *handle,
4423 files_struct *fsp, void *data,
4424 size_t n, off_t offset)
4426 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4429 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4430 fsp_str_dbg(fsp), (intmax_t)offset, n);
4433 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4436 if (fio->type == ADOUBLE_META) {
4437 nread = fruit_pread_meta(handle, fsp, data, n, offset);
4439 nread = fruit_pread_rsrc(handle, fsp, data, n, offset);
4442 DBG_DEBUG("Path [%s] nread [%zd]\n", fsp_str_dbg(fsp), nread);
4446 static bool fruit_must_handle_aio_stream(struct fio *fio)
4452 if (fio->type == ADOUBLE_META) {
4456 if ((fio->type == ADOUBLE_RSRC) &&
4457 (fio->config->rsrc == FRUIT_RSRC_ADFILE))
4465 struct fruit_pread_state {
4467 struct vfs_aio_state vfs_aio_state;
4470 static void fruit_pread_done(struct tevent_req *subreq);
4472 static struct tevent_req *fruit_pread_send(
4473 struct vfs_handle_struct *handle,
4474 TALLOC_CTX *mem_ctx,
4475 struct tevent_context *ev,
4476 struct files_struct *fsp,
4478 size_t n, off_t offset)
4480 struct tevent_req *req = NULL;
4481 struct tevent_req *subreq = NULL;
4482 struct fruit_pread_state *state = NULL;
4483 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4485 req = tevent_req_create(mem_ctx, &state,
4486 struct fruit_pread_state);
4491 if (fruit_must_handle_aio_stream(fio)) {
4492 state->nread = SMB_VFS_PREAD(fsp, data, n, offset);
4493 if (state->nread != n) {
4494 if (state->nread != -1) {
4497 tevent_req_error(req, errno);
4498 return tevent_req_post(req, ev);
4500 tevent_req_done(req);
4501 return tevent_req_post(req, ev);
4504 subreq = SMB_VFS_NEXT_PREAD_SEND(state, ev, handle, fsp,
4506 if (tevent_req_nomem(req, subreq)) {
4507 return tevent_req_post(req, ev);
4509 tevent_req_set_callback(subreq, fruit_pread_done, req);
4513 static void fruit_pread_done(struct tevent_req *subreq)
4515 struct tevent_req *req = tevent_req_callback_data(
4516 subreq, struct tevent_req);
4517 struct fruit_pread_state *state = tevent_req_data(
4518 req, struct fruit_pread_state);
4520 state->nread = SMB_VFS_PREAD_RECV(subreq, &state->vfs_aio_state);
4521 TALLOC_FREE(subreq);
4523 if (tevent_req_error(req, state->vfs_aio_state.error)) {
4526 tevent_req_done(req);
4529 static ssize_t fruit_pread_recv(struct tevent_req *req,
4530 struct vfs_aio_state *vfs_aio_state)
4532 struct fruit_pread_state *state = tevent_req_data(
4533 req, struct fruit_pread_state);
4535 if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
4539 *vfs_aio_state = state->vfs_aio_state;
4540 return state->nread;
4543 static ssize_t fruit_pwrite_meta_stream(vfs_handle_struct *handle,
4544 files_struct *fsp, const void *data,
4545 size_t n, off_t offset)
4547 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4553 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4554 fsp_str_dbg(fsp), (intmax_t)offset, n);
4563 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
4565 DBG_ERR("Close [%s] failed: %s\n",
4566 fsp_str_dbg(fsp), strerror(errno));
4571 fd = SMB_VFS_NEXT_OPEN(handle,
4577 DBG_ERR("On-demand create [%s] in write failed: %s\n",
4578 fsp_str_dbg(fsp), strerror(errno));
4582 fio->fake_fd = false;
4585 ai = afpinfo_unpack(talloc_tos(), data);
4590 if (ai_empty_finderinfo(ai)) {
4592 * Writing an all 0 blob to the metadata stream results in the
4593 * stream being removed on a macOS server. This ensures we
4594 * behave the same and it verified by the "delete AFP_AfpInfo by
4595 * writing all 0" test.
4597 ret = SMB_VFS_NEXT_FTRUNCATE(handle, fsp, 0);
4599 DBG_ERR("SMB_VFS_NEXT_FTRUNCATE on [%s] failed\n",
4604 ok = set_delete_on_close(
4607 handle->conn->session_info->security_token,
4608 handle->conn->session_info->unix_token);
4610 DBG_ERR("set_delete_on_close on [%s] failed\n",
4617 nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4618 if (nwritten != n) {
4625 static ssize_t fruit_pwrite_meta_netatalk(vfs_handle_struct *handle,
4626 files_struct *fsp, const void *data,
4627 size_t n, off_t offset)
4629 struct adouble *ad = NULL;
4635 ai = afpinfo_unpack(talloc_tos(), data);
4640 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
4642 ad = ad_init(talloc_tos(), ADOUBLE_META);
4647 p = ad_get_entry(ad, ADEID_FINDERI);
4649 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
4654 memcpy(p, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
4656 ret = ad_fset(handle, ad, fsp);
4658 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
4665 if (!ai_empty_finderinfo(ai)) {
4670 * Writing an all 0 blob to the metadata stream results in the stream
4671 * being removed on a macOS server. This ensures we behave the same and
4672 * it verified by the "delete AFP_AfpInfo by writing all 0" test.
4675 ok = set_delete_on_close(
4678 handle->conn->session_info->security_token,
4679 handle->conn->session_info->unix_token);
4681 DBG_ERR("set_delete_on_close on [%s] failed\n",
4689 static ssize_t fruit_pwrite_meta(vfs_handle_struct *handle,
4690 files_struct *fsp, const void *data,
4691 size_t n, off_t offset)
4693 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4695 uint8_t buf[AFP_INFO_SIZE];
4701 DBG_ERR("Failed to fetch fsp extension");
4710 if (offset != 0 && n < 60) {
4715 cmp = memcmp(data, "AFP", 3);
4721 if (n <= AFP_OFF_FinderInfo) {
4723 * Nothing to do here really, just return
4731 if (to_copy > AFP_INFO_SIZE) {
4732 to_copy = AFP_INFO_SIZE;
4734 memcpy(buf, data, to_copy);
4737 if (to_write != AFP_INFO_SIZE) {
4738 to_write = AFP_INFO_SIZE;
4741 switch (fio->config->meta) {
4742 case FRUIT_META_STREAM:
4743 nwritten = fruit_pwrite_meta_stream(handle,
4750 case FRUIT_META_NETATALK:
4751 nwritten = fruit_pwrite_meta_netatalk(handle,
4759 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
4763 if (nwritten != to_write) {
4768 * Return the requested amount, verified against macOS SMB server
4773 static ssize_t fruit_pwrite_rsrc_stream(vfs_handle_struct *handle,
4774 files_struct *fsp, const void *data,
4775 size_t n, off_t offset)
4777 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4780 static ssize_t fruit_pwrite_rsrc_xattr(vfs_handle_struct *handle,
4781 files_struct *fsp, const void *data,
4782 size_t n, off_t offset)
4784 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4787 static ssize_t fruit_pwrite_rsrc_adouble(vfs_handle_struct *handle,
4788 files_struct *fsp, const void *data,
4789 size_t n, off_t offset)
4791 struct adouble *ad = NULL;
4795 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
4797 DBG_ERR("ad_get [%s] failed\n", fsp_str_dbg(fsp));
4801 nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n,
4802 offset + ad_getentryoff(ad, ADEID_RFORK));
4803 if (nwritten != n) {
4804 DBG_ERR("Short write on [%s] [%zd/%zd]\n",
4805 fsp_str_dbg(fsp), nwritten, n);
4810 if ((n + offset) > ad_getentrylen(ad, ADEID_RFORK)) {
4811 ad_setentrylen(ad, ADEID_RFORK, n + offset);
4812 ret = ad_fset(handle, ad, fsp);
4814 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
4824 static ssize_t fruit_pwrite_rsrc(vfs_handle_struct *handle,
4825 files_struct *fsp, const void *data,
4826 size_t n, off_t offset)
4828 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4832 DBG_ERR("Failed to fetch fsp extension");
4836 switch (fio->config->rsrc) {
4837 case FRUIT_RSRC_STREAM:
4838 nwritten = fruit_pwrite_rsrc_stream(handle, fsp, data, n, offset);
4841 case FRUIT_RSRC_ADFILE:
4842 nwritten = fruit_pwrite_rsrc_adouble(handle, fsp, data, n, offset);
4845 case FRUIT_RSRC_XATTR:
4846 nwritten = fruit_pwrite_rsrc_xattr(handle, fsp, data, n, offset);
4850 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
4857 static ssize_t fruit_pwrite(vfs_handle_struct *handle,
4858 files_struct *fsp, const void *data,
4859 size_t n, off_t offset)
4861 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4864 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4865 fsp_str_dbg(fsp), (intmax_t)offset, n);
4868 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4871 if (fio->type == ADOUBLE_META) {
4872 nwritten = fruit_pwrite_meta(handle, fsp, data, n, offset);
4874 nwritten = fruit_pwrite_rsrc(handle, fsp, data, n, offset);
4877 DBG_DEBUG("Path [%s] nwritten=%zd\n", fsp_str_dbg(fsp), nwritten);
4881 struct fruit_pwrite_state {
4883 struct vfs_aio_state vfs_aio_state;
4886 static void fruit_pwrite_done(struct tevent_req *subreq);
4888 static struct tevent_req *fruit_pwrite_send(
4889 struct vfs_handle_struct *handle,
4890 TALLOC_CTX *mem_ctx,
4891 struct tevent_context *ev,
4892 struct files_struct *fsp,
4894 size_t n, off_t offset)
4896 struct tevent_req *req = NULL;
4897 struct tevent_req *subreq = NULL;
4898 struct fruit_pwrite_state *state = NULL;
4899 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4901 req = tevent_req_create(mem_ctx, &state,
4902 struct fruit_pwrite_state);
4907 if (fruit_must_handle_aio_stream(fio)) {
4908 state->nwritten = SMB_VFS_PWRITE(fsp, data, n, offset);
4909 if (state->nwritten != n) {
4910 if (state->nwritten != -1) {
4913 tevent_req_error(req, errno);
4914 return tevent_req_post(req, ev);
4916 tevent_req_done(req);
4917 return tevent_req_post(req, ev);
4920 subreq = SMB_VFS_NEXT_PWRITE_SEND(state, ev, handle, fsp,
4922 if (tevent_req_nomem(req, subreq)) {
4923 return tevent_req_post(req, ev);
4925 tevent_req_set_callback(subreq, fruit_pwrite_done, req);
4929 static void fruit_pwrite_done(struct tevent_req *subreq)
4931 struct tevent_req *req = tevent_req_callback_data(
4932 subreq, struct tevent_req);
4933 struct fruit_pwrite_state *state = tevent_req_data(
4934 req, struct fruit_pwrite_state);
4936 state->nwritten = SMB_VFS_PWRITE_RECV(subreq, &state->vfs_aio_state);
4937 TALLOC_FREE(subreq);
4939 if (tevent_req_error(req, state->vfs_aio_state.error)) {
4942 tevent_req_done(req);
4945 static ssize_t fruit_pwrite_recv(struct tevent_req *req,
4946 struct vfs_aio_state *vfs_aio_state)
4948 struct fruit_pwrite_state *state = tevent_req_data(
4949 req, struct fruit_pwrite_state);
4951 if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
4955 *vfs_aio_state = state->vfs_aio_state;
4956 return state->nwritten;
4960 * Helper to stat/lstat the base file of an smb_fname.
4962 static int fruit_stat_base(vfs_handle_struct *handle,
4963 struct smb_filename *smb_fname,
4966 char *tmp_stream_name;
4969 tmp_stream_name = smb_fname->stream_name;
4970 smb_fname->stream_name = NULL;
4972 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
4974 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
4976 smb_fname->stream_name = tmp_stream_name;
4978 DBG_DEBUG("fruit_stat_base [%s] dev [%ju] ino [%ju]\n",
4979 smb_fname->base_name,
4980 (uintmax_t)smb_fname->st.st_ex_dev,
4981 (uintmax_t)smb_fname->st.st_ex_ino);
4985 static int fruit_stat_meta_stream(vfs_handle_struct *handle,
4986 struct smb_filename *smb_fname,
4992 ret = fruit_stat_base(handle, smb_fname, false);
4997 ino = fruit_inode(&smb_fname->st, smb_fname->stream_name);
5000 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
5002 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5005 smb_fname->st.st_ex_ino = ino;
5010 static int fruit_stat_meta_netatalk(vfs_handle_struct *handle,
5011 struct smb_filename *smb_fname,
5014 struct adouble *ad = NULL;
5016 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5018 DBG_INFO("fruit_stat_meta %s: %s\n",
5019 smb_fname_str_dbg(smb_fname), strerror(errno));
5025 /* Populate the stat struct with info from the base file. */
5026 if (fruit_stat_base(handle, smb_fname, follow_links) == -1) {
5029 smb_fname->st.st_ex_size = AFP_INFO_SIZE;
5030 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5031 smb_fname->stream_name);
5035 static int fruit_stat_meta(vfs_handle_struct *handle,
5036 struct smb_filename *smb_fname,
5039 struct fruit_config_data *config = NULL;
5042 SMB_VFS_HANDLE_GET_DATA(handle, config,
5043 struct fruit_config_data, return -1);
5045 switch (config->meta) {
5046 case FRUIT_META_STREAM:
5047 ret = fruit_stat_meta_stream(handle, smb_fname, follow_links);
5050 case FRUIT_META_NETATALK:
5051 ret = fruit_stat_meta_netatalk(handle, smb_fname, follow_links);
5055 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
5062 static int fruit_stat_rsrc_netatalk(vfs_handle_struct *handle,
5063 struct smb_filename *smb_fname,
5066 struct adouble *ad = NULL;
5069 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
5075 /* Populate the stat struct with info from the base file. */
5076 ret = fruit_stat_base(handle, smb_fname, follow_links);
5082 smb_fname->st.st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
5083 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5084 smb_fname->stream_name);
5089 static int fruit_stat_rsrc_stream(vfs_handle_struct *handle,
5090 struct smb_filename *smb_fname,
5096 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
5098 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5104 static int fruit_stat_rsrc_xattr(vfs_handle_struct *handle,
5105 struct smb_filename *smb_fname,
5108 #ifdef HAVE_ATTROPEN
5112 /* Populate the stat struct with info from the base file. */
5113 ret = fruit_stat_base(handle, smb_fname, follow_links);
5118 fd = attropen(smb_fname->base_name,
5119 AFPRESOURCE_EA_NETATALK,
5125 ret = sys_fstat(fd, &smb_fname->st, false);
5128 DBG_ERR("fstat [%s:%s] failed\n", smb_fname->base_name,
5129 AFPRESOURCE_EA_NETATALK);
5135 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5136 smb_fname->stream_name);
5146 static int fruit_stat_rsrc(vfs_handle_struct *handle,
5147 struct smb_filename *smb_fname,
5150 struct fruit_config_data *config = NULL;
5153 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
5155 SMB_VFS_HANDLE_GET_DATA(handle, config,
5156 struct fruit_config_data, return -1);
5158 switch (config->rsrc) {
5159 case FRUIT_RSRC_STREAM:
5160 ret = fruit_stat_rsrc_stream(handle, smb_fname, follow_links);
5163 case FRUIT_RSRC_XATTR:
5164 ret = fruit_stat_rsrc_xattr(handle, smb_fname, follow_links);
5167 case FRUIT_RSRC_ADFILE:
5168 ret = fruit_stat_rsrc_netatalk(handle, smb_fname, follow_links);
5172 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
5179 static int fruit_stat(vfs_handle_struct *handle,
5180 struct smb_filename *smb_fname)
5184 DEBUG(10, ("fruit_stat called for %s\n",
5185 smb_fname_str_dbg(smb_fname)));
5187 if (!is_ntfs_stream_smb_fname(smb_fname)
5188 || is_ntfs_default_stream_smb_fname(smb_fname)) {
5189 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
5191 update_btime(handle, smb_fname);
5197 * Note if lp_posix_paths() is true, we can never
5198 * get here as is_ntfs_stream_smb_fname() is
5199 * always false. So we never need worry about
5200 * not following links here.
5203 if (is_afpinfo_stream(smb_fname)) {
5204 rc = fruit_stat_meta(handle, smb_fname, true);
5205 } else if (is_afpresource_stream(smb_fname)) {
5206 rc = fruit_stat_rsrc(handle, smb_fname, true);
5208 return SMB_VFS_NEXT_STAT(handle, smb_fname);
5212 update_btime(handle, smb_fname);
5213 smb_fname->st.st_ex_mode &= ~S_IFMT;
5214 smb_fname->st.st_ex_mode |= S_IFREG;
5215 smb_fname->st.st_ex_blocks =
5216 smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
5221 static int fruit_lstat(vfs_handle_struct *handle,
5222 struct smb_filename *smb_fname)
5226 DEBUG(10, ("fruit_lstat called for %s\n",
5227 smb_fname_str_dbg(smb_fname)));
5229 if (!is_ntfs_stream_smb_fname(smb_fname)
5230 || is_ntfs_default_stream_smb_fname(smb_fname)) {
5231 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5233 update_btime(handle, smb_fname);
5238 if (is_afpinfo_stream(smb_fname)) {
5239 rc = fruit_stat_meta(handle, smb_fname, false);
5240 } else if (is_afpresource_stream(smb_fname)) {
5241 rc = fruit_stat_rsrc(handle, smb_fname, false);
5243 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5247 update_btime(handle, smb_fname);
5248 smb_fname->st.st_ex_mode &= ~S_IFMT;
5249 smb_fname->st.st_ex_mode |= S_IFREG;
5250 smb_fname->st.st_ex_blocks =
5251 smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
5256 static int fruit_fstat_meta_stream(vfs_handle_struct *handle,
5258 SMB_STRUCT_STAT *sbuf)
5260 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5261 struct smb_filename smb_fname;
5270 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5275 *sbuf = fsp->base_fsp->fsp_name->st;
5276 sbuf->st_ex_size = AFP_INFO_SIZE;
5277 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5281 smb_fname = (struct smb_filename) {
5282 .base_name = fsp->fsp_name->base_name,
5285 ret = fruit_stat_base(handle, &smb_fname, false);
5289 *sbuf = smb_fname.st;
5291 ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5293 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5298 sbuf->st_ex_ino = ino;
5302 static int fruit_fstat_meta_netatalk(vfs_handle_struct *handle,
5304 SMB_STRUCT_STAT *sbuf)
5308 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5313 *sbuf = fsp->base_fsp->fsp_name->st;
5314 sbuf->st_ex_size = AFP_INFO_SIZE;
5315 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5320 static int fruit_fstat_meta(vfs_handle_struct *handle,
5322 SMB_STRUCT_STAT *sbuf,
5327 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
5329 switch (fio->config->meta) {
5330 case FRUIT_META_STREAM:
5331 ret = fruit_fstat_meta_stream(handle, fsp, sbuf);
5334 case FRUIT_META_NETATALK:
5335 ret = fruit_fstat_meta_netatalk(handle, fsp, sbuf);
5339 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
5343 DBG_DEBUG("Path [%s] ret [%d]\n", fsp_str_dbg(fsp), ret);
5347 static int fruit_fstat_rsrc_xattr(vfs_handle_struct *handle,
5349 SMB_STRUCT_STAT *sbuf)
5351 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5354 static int fruit_fstat_rsrc_stream(vfs_handle_struct *handle,
5356 SMB_STRUCT_STAT *sbuf)
5358 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5361 static int fruit_fstat_rsrc_adouble(vfs_handle_struct *handle,
5363 SMB_STRUCT_STAT *sbuf)
5365 struct adouble *ad = NULL;
5368 /* Populate the stat struct with info from the base file. */
5369 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5374 ad = ad_get(talloc_tos(), handle,
5375 fsp->base_fsp->fsp_name,
5378 DBG_ERR("ad_get [%s] failed [%s]\n",
5379 fsp_str_dbg(fsp), strerror(errno));
5383 *sbuf = fsp->base_fsp->fsp_name->st;
5384 sbuf->st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
5385 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5391 static int fruit_fstat_rsrc(vfs_handle_struct *handle, files_struct *fsp,
5392 SMB_STRUCT_STAT *sbuf, struct fio *fio)
5396 switch (fio->config->rsrc) {
5397 case FRUIT_RSRC_STREAM:
5398 ret = fruit_fstat_rsrc_stream(handle, fsp, sbuf);
5401 case FRUIT_RSRC_ADFILE:
5402 ret = fruit_fstat_rsrc_adouble(handle, fsp, sbuf);
5405 case FRUIT_RSRC_XATTR:
5406 ret = fruit_fstat_rsrc_xattr(handle, fsp, sbuf);
5410 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
5417 static int fruit_fstat(vfs_handle_struct *handle, files_struct *fsp,
5418 SMB_STRUCT_STAT *sbuf)
5420 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5424 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5427 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
5429 if (fio->type == ADOUBLE_META) {
5430 rc = fruit_fstat_meta(handle, fsp, sbuf, fio);
5432 rc = fruit_fstat_rsrc(handle, fsp, sbuf, fio);
5436 sbuf->st_ex_mode &= ~S_IFMT;
5437 sbuf->st_ex_mode |= S_IFREG;
5438 sbuf->st_ex_blocks = sbuf->st_ex_size / STAT_ST_BLOCKSIZE + 1;
5441 DBG_DEBUG("Path [%s] rc [%d] size [%"PRIdMAX"]\n",
5442 fsp_str_dbg(fsp), rc, (intmax_t)sbuf->st_ex_size);
5446 static NTSTATUS delete_invalid_meta_stream(
5447 vfs_handle_struct *handle,
5448 const struct smb_filename *smb_fname,
5449 TALLOC_CTX *mem_ctx,
5450 unsigned int *pnum_streams,
5451 struct stream_struct **pstreams,
5454 struct smb_filename *sname = NULL;
5458 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams, AFPINFO_STREAM);
5460 return NT_STATUS_INTERNAL_ERROR;
5464 return NT_STATUS_OK;
5467 sname = synthetic_smb_fname(talloc_tos(),
5468 smb_fname->base_name,
5469 AFPINFO_STREAM_NAME,
5471 if (sname == NULL) {
5472 return NT_STATUS_NO_MEMORY;
5475 ret = SMB_VFS_NEXT_UNLINK(handle, sname);
5478 DBG_ERR("Removing [%s] failed\n", smb_fname_str_dbg(sname));
5479 return map_nt_error_from_unix(errno);
5482 return NT_STATUS_OK;
5485 static NTSTATUS fruit_streaminfo_meta_stream(
5486 vfs_handle_struct *handle,
5487 struct files_struct *fsp,
5488 const struct smb_filename *smb_fname,
5489 TALLOC_CTX *mem_ctx,
5490 unsigned int *pnum_streams,
5491 struct stream_struct **pstreams)
5493 struct stream_struct *stream = *pstreams;
5494 unsigned int num_streams = *pnum_streams;
5497 for (i = 0; i < num_streams; i++) {
5498 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
5503 if (i == num_streams) {
5504 return NT_STATUS_OK;
5507 if (stream[i].size != AFP_INFO_SIZE) {
5508 DBG_ERR("Removing invalid AFPINFO_STREAM size [%jd] from [%s]\n",
5509 (intmax_t)stream[i].size, smb_fname_str_dbg(smb_fname));
5511 return delete_invalid_meta_stream(handle,
5520 return NT_STATUS_OK;
5523 static NTSTATUS fruit_streaminfo_meta_netatalk(
5524 vfs_handle_struct *handle,
5525 struct files_struct *fsp,
5526 const struct smb_filename *smb_fname,
5527 TALLOC_CTX *mem_ctx,
5528 unsigned int *pnum_streams,
5529 struct stream_struct **pstreams)
5531 struct stream_struct *stream = *pstreams;
5532 unsigned int num_streams = *pnum_streams;
5533 struct adouble *ad = NULL;
5538 /* Remove the Netatalk xattr from the list */
5539 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5540 ":" NETATALK_META_XATTR ":$DATA");
5542 return NT_STATUS_NO_MEMORY;
5546 * Check if there's a AFPINFO_STREAM from the VFS streams
5547 * backend and if yes, remove it from the list
5549 for (i = 0; i < num_streams; i++) {
5550 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
5555 if (i < num_streams) {
5556 DBG_WARNING("Unexpected AFPINFO_STREAM on [%s]\n",
5557 smb_fname_str_dbg(smb_fname));
5559 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5562 return NT_STATUS_INTERNAL_ERROR;
5566 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5568 return NT_STATUS_OK;
5571 is_fi_empty = ad_empty_finderinfo(ad);
5575 return NT_STATUS_OK;
5578 ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
5579 AFPINFO_STREAM_NAME, AFP_INFO_SIZE,
5580 smb_roundup(handle->conn, AFP_INFO_SIZE));
5582 return NT_STATUS_NO_MEMORY;
5585 return NT_STATUS_OK;
5588 static NTSTATUS fruit_streaminfo_meta(vfs_handle_struct *handle,
5589 struct files_struct *fsp,
5590 const struct smb_filename *smb_fname,
5591 TALLOC_CTX *mem_ctx,
5592 unsigned int *pnum_streams,
5593 struct stream_struct **pstreams)
5595 struct fruit_config_data *config = NULL;
5598 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5599 return NT_STATUS_INTERNAL_ERROR);
5601 switch (config->meta) {
5602 case FRUIT_META_NETATALK:
5603 status = fruit_streaminfo_meta_netatalk(handle, fsp, smb_fname,
5604 mem_ctx, pnum_streams,
5608 case FRUIT_META_STREAM:
5609 status = fruit_streaminfo_meta_stream(handle, fsp, smb_fname,
5610 mem_ctx, pnum_streams,
5615 return NT_STATUS_INTERNAL_ERROR;
5621 static NTSTATUS fruit_streaminfo_rsrc_stream(
5622 vfs_handle_struct *handle,
5623 struct files_struct *fsp,
5624 const struct smb_filename *smb_fname,
5625 TALLOC_CTX *mem_ctx,
5626 unsigned int *pnum_streams,
5627 struct stream_struct **pstreams)
5631 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
5633 DBG_ERR("Filtering resource stream failed\n");
5634 return NT_STATUS_INTERNAL_ERROR;
5636 return NT_STATUS_OK;
5639 static NTSTATUS fruit_streaminfo_rsrc_xattr(
5640 vfs_handle_struct *handle,
5641 struct files_struct *fsp,
5642 const struct smb_filename *smb_fname,
5643 TALLOC_CTX *mem_ctx,
5644 unsigned int *pnum_streams,
5645 struct stream_struct **pstreams)
5649 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
5651 DBG_ERR("Filtering resource stream failed\n");
5652 return NT_STATUS_INTERNAL_ERROR;
5654 return NT_STATUS_OK;
5657 static NTSTATUS fruit_streaminfo_rsrc_adouble(
5658 vfs_handle_struct *handle,
5659 struct files_struct *fsp,
5660 const struct smb_filename *smb_fname,
5661 TALLOC_CTX *mem_ctx,
5662 unsigned int *pnum_streams,
5663 struct stream_struct **pstreams)
5665 struct stream_struct *stream = *pstreams;
5666 unsigned int num_streams = *pnum_streams;
5667 struct adouble *ad = NULL;
5673 * Check if there's a AFPRESOURCE_STREAM from the VFS streams backend
5674 * and if yes, remove it from the list
5676 for (i = 0; i < num_streams; i++) {
5677 if (strequal_m(stream[i].name, AFPRESOURCE_STREAM)) {
5682 if (i < num_streams) {
5683 DBG_WARNING("Unexpected AFPRESOURCE_STREAM on [%s]\n",
5684 smb_fname_str_dbg(smb_fname));
5686 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5687 AFPRESOURCE_STREAM);
5689 return NT_STATUS_INTERNAL_ERROR;
5693 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
5695 return NT_STATUS_OK;
5698 rlen = ad_getentrylen(ad, ADEID_RFORK);
5702 return NT_STATUS_OK;
5705 ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
5706 AFPRESOURCE_STREAM_NAME, rlen,
5707 smb_roundup(handle->conn, rlen));
5709 return NT_STATUS_NO_MEMORY;
5712 return NT_STATUS_OK;
5715 static NTSTATUS fruit_streaminfo_rsrc(vfs_handle_struct *handle,
5716 struct files_struct *fsp,
5717 const struct smb_filename *smb_fname,
5718 TALLOC_CTX *mem_ctx,
5719 unsigned int *pnum_streams,
5720 struct stream_struct **pstreams)
5722 struct fruit_config_data *config = NULL;
5725 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5726 return NT_STATUS_INTERNAL_ERROR);
5728 switch (config->rsrc) {
5729 case FRUIT_RSRC_STREAM:
5730 status = fruit_streaminfo_rsrc_stream(handle, fsp, smb_fname,
5731 mem_ctx, pnum_streams,
5735 case FRUIT_RSRC_XATTR:
5736 status = fruit_streaminfo_rsrc_xattr(handle, fsp, smb_fname,
5737 mem_ctx, pnum_streams,
5741 case FRUIT_RSRC_ADFILE:
5742 status = fruit_streaminfo_rsrc_adouble(handle, fsp, smb_fname,
5743 mem_ctx, pnum_streams,
5748 return NT_STATUS_INTERNAL_ERROR;
5754 static void fruit_filter_empty_streams(unsigned int *pnum_streams,
5755 struct stream_struct **pstreams)
5757 unsigned num_streams = *pnum_streams;
5758 struct stream_struct *streams = *pstreams;
5761 if (!global_fruit_config.nego_aapl) {
5765 while (i < num_streams) {
5766 struct smb_filename smb_fname = (struct smb_filename) {
5767 .stream_name = streams[i].name,
5770 if (is_ntfs_default_stream_smb_fname(&smb_fname)
5771 || streams[i].size > 0)
5777 streams[i] = streams[num_streams - 1];
5781 *pnum_streams = num_streams;
5784 static NTSTATUS fruit_streaminfo(vfs_handle_struct *handle,
5785 struct files_struct *fsp,
5786 const struct smb_filename *smb_fname,
5787 TALLOC_CTX *mem_ctx,
5788 unsigned int *pnum_streams,
5789 struct stream_struct **pstreams)
5791 struct fruit_config_data *config = NULL;
5794 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5795 return NT_STATUS_UNSUCCESSFUL);
5797 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
5799 status = SMB_VFS_NEXT_STREAMINFO(handle, fsp, smb_fname, mem_ctx,
5800 pnum_streams, pstreams);
5801 if (!NT_STATUS_IS_OK(status)) {
5805 fruit_filter_empty_streams(pnum_streams, pstreams);
5807 status = fruit_streaminfo_meta(handle, fsp, smb_fname,
5808 mem_ctx, pnum_streams, pstreams);
5809 if (!NT_STATUS_IS_OK(status)) {
5813 status = fruit_streaminfo_rsrc(handle, fsp, smb_fname,
5814 mem_ctx, pnum_streams, pstreams);
5815 if (!NT_STATUS_IS_OK(status)) {
5819 return NT_STATUS_OK;
5822 static int fruit_ntimes(vfs_handle_struct *handle,
5823 const struct smb_filename *smb_fname,
5824 struct smb_file_time *ft)
5827 struct adouble *ad = NULL;
5828 struct fruit_config_data *config = NULL;
5830 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5833 if ((config->meta != FRUIT_META_NETATALK) ||
5834 null_timespec(ft->create_time))
5836 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
5839 DEBUG(10,("set btime for %s to %s\n", smb_fname_str_dbg(smb_fname),
5840 time_to_asc(convert_timespec_to_time_t(ft->create_time))));
5842 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5847 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX,
5848 convert_time_t_to_uint32_t(ft->create_time.tv_sec));
5850 rc = ad_set(handle, ad, smb_fname);
5856 DEBUG(1, ("fruit_ntimes: %s\n", smb_fname_str_dbg(smb_fname)));
5859 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
5862 static int fruit_fallocate(struct vfs_handle_struct *handle,
5863 struct files_struct *fsp,
5868 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5871 return SMB_VFS_NEXT_FALLOCATE(handle, fsp, mode, offset, len);
5874 /* Let the pwrite code path handle it. */
5879 static int fruit_ftruncate_rsrc_xattr(struct vfs_handle_struct *handle,
5880 struct files_struct *fsp,
5883 #ifdef HAVE_ATTROPEN
5884 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
5889 static int fruit_ftruncate_rsrc_adouble(struct vfs_handle_struct *handle,
5890 struct files_struct *fsp,
5894 struct adouble *ad = NULL;
5897 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
5899 DBG_DEBUG("ad_get [%s] failed [%s]\n",
5900 fsp_str_dbg(fsp), strerror(errno));
5904 ad_off = ad_getentryoff(ad, ADEID_RFORK);
5906 rc = ftruncate(fsp->fh->fd, offset + ad_off);
5912 ad_setentrylen(ad, ADEID_RFORK, offset);
5914 rc = ad_fset(handle, ad, fsp);
5916 DBG_ERR("ad_fset [%s] failed [%s]\n",
5917 fsp_str_dbg(fsp), strerror(errno));
5926 static int fruit_ftruncate_rsrc_stream(struct vfs_handle_struct *handle,
5927 struct files_struct *fsp,
5930 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
5933 static int fruit_ftruncate_rsrc(struct vfs_handle_struct *handle,
5934 struct files_struct *fsp,
5937 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5941 DBG_ERR("Failed to fetch fsp extension");
5945 switch (fio->config->rsrc) {
5946 case FRUIT_RSRC_XATTR:
5947 ret = fruit_ftruncate_rsrc_xattr(handle, fsp, offset);
5950 case FRUIT_RSRC_ADFILE:
5951 ret = fruit_ftruncate_rsrc_adouble(handle, fsp, offset);
5954 case FRUIT_RSRC_STREAM:
5955 ret = fruit_ftruncate_rsrc_stream(handle, fsp, offset);
5959 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
5967 static int fruit_ftruncate_meta(struct vfs_handle_struct *handle,
5968 struct files_struct *fsp,
5972 DBG_WARNING("ftruncate %s to %jd",
5973 fsp_str_dbg(fsp), (intmax_t)offset);
5974 /* OS X returns NT_STATUS_ALLOTTED_SPACE_EXCEEDED */
5979 /* OS X returns success but does nothing */
5980 DBG_INFO("ignoring ftruncate %s to %jd\n",
5981 fsp_str_dbg(fsp), (intmax_t)offset);
5985 static int fruit_ftruncate(struct vfs_handle_struct *handle,
5986 struct files_struct *fsp,
5989 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5992 DBG_DEBUG("Path [%s] offset [%"PRIdMAX"]\n", fsp_str_dbg(fsp),
5996 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
5999 if (fio->type == ADOUBLE_META) {
6000 ret = fruit_ftruncate_meta(handle, fsp, offset);
6002 ret = fruit_ftruncate_rsrc(handle, fsp, offset);
6005 DBG_DEBUG("Path [%s] result [%d]\n", fsp_str_dbg(fsp), ret);
6009 static NTSTATUS fruit_create_file(vfs_handle_struct *handle,
6010 struct smb_request *req,
6011 uint16_t root_dir_fid,
6012 struct smb_filename *smb_fname,
6013 uint32_t access_mask,
6014 uint32_t share_access,
6015 uint32_t create_disposition,
6016 uint32_t create_options,
6017 uint32_t file_attributes,
6018 uint32_t oplock_request,
6019 struct smb2_lease *lease,
6020 uint64_t allocation_size,
6021 uint32_t private_flags,
6022 struct security_descriptor *sd,
6023 struct ea_list *ea_list,
6024 files_struct **result,
6026 const struct smb2_create_blobs *in_context_blobs,
6027 struct smb2_create_blobs *out_context_blobs)
6030 struct fruit_config_data *config = NULL;
6031 files_struct *fsp = NULL;
6032 struct fio *fio = NULL;
6033 bool internal_open = (oplock_request & INTERNAL_OPEN_ONLY);
6036 status = check_aapl(handle, req, in_context_blobs, out_context_blobs);
6037 if (!NT_STATUS_IS_OK(status)) {
6041 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
6042 return NT_STATUS_UNSUCCESSFUL);
6044 if (is_apple_stream(smb_fname) && !internal_open) {
6045 ret = ad_convert(handle, smb_fname);
6047 DBG_ERR("ad_convert() failed\n");
6048 return NT_STATUS_UNSUCCESSFUL;
6052 status = SMB_VFS_NEXT_CREATE_FILE(
6053 handle, req, root_dir_fid, smb_fname,
6054 access_mask, share_access,
6055 create_disposition, create_options,
6056 file_attributes, oplock_request,
6058 allocation_size, private_flags,
6059 sd, ea_list, result,
6060 pinfo, in_context_blobs, out_context_blobs);
6061 if (!NT_STATUS_IS_OK(status)) {
6067 if (global_fruit_config.nego_aapl) {
6068 if (config->posix_rename && fsp->is_directory) {
6070 * Enable POSIX directory rename behaviour
6072 fsp->posix_flags |= FSP_POSIX_FLAGS_RENAME;
6077 * If this is a plain open for existing files, opening an 0
6078 * byte size resource fork MUST fail with
6079 * NT_STATUS_OBJECT_NAME_NOT_FOUND.
6081 * Cf the vfs_fruit torture tests in test_rfork_create().
6083 if (global_fruit_config.nego_aapl &&
6084 create_disposition == FILE_OPEN &&
6085 smb_fname->st.st_ex_size == 0 &&
6086 is_ntfs_stream_smb_fname(smb_fname) &&
6087 !(is_ntfs_default_stream_smb_fname(smb_fname)))
6089 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
6093 fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
6094 if (fio != NULL && pinfo != NULL && *pinfo == FILE_WAS_CREATED) {
6095 fio->created = true;
6098 if (is_ntfs_stream_smb_fname(smb_fname)
6099 || fsp->is_directory) {
6103 if (config->locking == FRUIT_LOCKING_NETATALK) {
6104 status = fruit_check_access(
6108 if (!NT_STATUS_IS_OK(status)) {
6116 DEBUG(10, ("fruit_create_file: %s\n", nt_errstr(status)));
6119 close_file(req, fsp, ERROR_CLOSE);
6120 *result = fsp = NULL;
6126 static NTSTATUS fruit_readdir_attr(struct vfs_handle_struct *handle,
6127 const struct smb_filename *fname,
6128 TALLOC_CTX *mem_ctx,
6129 struct readdir_attr_data **pattr_data)
6131 struct fruit_config_data *config = NULL;
6132 struct readdir_attr_data *attr_data;
6136 SMB_VFS_HANDLE_GET_DATA(handle, config,
6137 struct fruit_config_data,
6138 return NT_STATUS_UNSUCCESSFUL);
6140 if (!global_fruit_config.nego_aapl) {
6141 return SMB_VFS_NEXT_READDIR_ATTR(handle, fname, mem_ctx, pattr_data);
6144 DEBUG(10, ("fruit_readdir_attr %s\n", fname->base_name));
6146 ret = ad_convert(handle, fname);
6148 DBG_ERR("ad_convert() failed\n");
6149 return NT_STATUS_UNSUCCESSFUL;
6152 *pattr_data = talloc_zero(mem_ctx, struct readdir_attr_data);
6153 if (*pattr_data == NULL) {
6154 return NT_STATUS_UNSUCCESSFUL;
6156 attr_data = *pattr_data;
6157 attr_data->type = RDATTR_AAPL;
6160 * Mac metadata: compressed FinderInfo, resource fork length
6163 status = readdir_attr_macmeta(handle, fname, attr_data);
6164 if (!NT_STATUS_IS_OK(status)) {
6166 * Error handling is tricky: if we return failure from
6167 * this function, the corresponding directory entry
6168 * will to be passed to the client, so we really just
6169 * want to error out on fatal errors.
6171 if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
6179 if (config->unix_info_enabled) {
6180 attr_data->attr_data.aapl.unix_mode = fname->st.st_ex_mode;
6186 if (!config->readdir_attr_max_access) {
6187 attr_data->attr_data.aapl.max_access = FILE_GENERIC_ALL;
6189 status = smbd_calculate_access_mask(
6193 SEC_FLAG_MAXIMUM_ALLOWED,
6194 &attr_data->attr_data.aapl.max_access);
6195 if (!NT_STATUS_IS_OK(status)) {
6200 return NT_STATUS_OK;
6203 DEBUG(1, ("fruit_readdir_attr %s, error: %s\n",
6204 fname->base_name, nt_errstr(status)));
6205 TALLOC_FREE(*pattr_data);
6209 static NTSTATUS fruit_fget_nt_acl(vfs_handle_struct *handle,
6211 uint32_t security_info,
6212 TALLOC_CTX *mem_ctx,
6213 struct security_descriptor **ppdesc)
6216 struct security_ace ace;
6218 struct fruit_config_data *config;
6220 SMB_VFS_HANDLE_GET_DATA(handle, config,
6221 struct fruit_config_data,
6222 return NT_STATUS_UNSUCCESSFUL);
6224 status = SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
6226 if (!NT_STATUS_IS_OK(status)) {
6231 * Add MS NFS style ACEs with uid, gid and mode
6233 if (!global_fruit_config.nego_aapl) {
6234 return NT_STATUS_OK;
6236 if (!config->unix_info_enabled) {
6237 return NT_STATUS_OK;
6240 /* First remove any existing ACE's with NFS style mode/uid/gid SIDs. */
6241 status = remove_virtual_nfs_aces(*ppdesc);
6242 if (!NT_STATUS_IS_OK(status)) {
6243 DBG_WARNING("failed to remove MS NFS style ACEs\n");
6247 /* MS NFS style mode */
6248 sid_compose(&sid, &global_sid_Unix_NFS_Mode, fsp->fsp_name->st.st_ex_mode);
6249 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6250 status = security_descriptor_dacl_add(*ppdesc, &ace);
6251 if (!NT_STATUS_IS_OK(status)) {
6252 DEBUG(1,("failed to add MS NFS style ACE\n"));
6256 /* MS NFS style uid */
6257 sid_compose(&sid, &global_sid_Unix_NFS_Users, fsp->fsp_name->st.st_ex_uid);
6258 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6259 status = security_descriptor_dacl_add(*ppdesc, &ace);
6260 if (!NT_STATUS_IS_OK(status)) {
6261 DEBUG(1,("failed to add MS NFS style ACE\n"));
6265 /* MS NFS style gid */
6266 sid_compose(&sid, &global_sid_Unix_NFS_Groups, fsp->fsp_name->st.st_ex_gid);
6267 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6268 status = security_descriptor_dacl_add(*ppdesc, &ace);
6269 if (!NT_STATUS_IS_OK(status)) {
6270 DEBUG(1,("failed to add MS NFS style ACE\n"));
6274 return NT_STATUS_OK;
6277 static NTSTATUS fruit_fset_nt_acl(vfs_handle_struct *handle,
6279 uint32_t security_info_sent,
6280 const struct security_descriptor *orig_psd)
6284 mode_t ms_nfs_mode = 0;
6286 struct security_descriptor *psd = NULL;
6287 uint32_t orig_num_aces = 0;
6289 if (orig_psd->dacl != NULL) {
6290 orig_num_aces = orig_psd->dacl->num_aces;
6293 psd = security_descriptor_copy(talloc_tos(), orig_psd);
6295 return NT_STATUS_NO_MEMORY;
6298 DBG_DEBUG("fruit_fset_nt_acl: %s\n", fsp_str_dbg(fsp));
6300 status = check_ms_nfs(handle, fsp, psd, &ms_nfs_mode, &do_chmod);
6301 if (!NT_STATUS_IS_OK(status)) {
6302 DEBUG(1, ("fruit_fset_nt_acl: check_ms_nfs failed%s\n", fsp_str_dbg(fsp)));
6308 * If only ms_nfs ACE entries were sent, ensure we set the DACL
6309 * sent/present flags correctly now we've removed them.
6312 if (orig_num_aces != 0) {
6314 * Are there any ACE's left ?
6316 if (psd->dacl->num_aces == 0) {
6317 /* No - clear the DACL sent/present flags. */
6318 security_info_sent &= ~SECINFO_DACL;
6319 psd->type &= ~SEC_DESC_DACL_PRESENT;
6323 status = SMB_VFS_NEXT_FSET_NT_ACL(handle, fsp, security_info_sent, psd);
6324 if (!NT_STATUS_IS_OK(status)) {
6325 DEBUG(1, ("fruit_fset_nt_acl: SMB_VFS_NEXT_FSET_NT_ACL failed%s\n", fsp_str_dbg(fsp)));
6331 if (fsp->fh->fd != -1) {
6332 result = SMB_VFS_FCHMOD(fsp, ms_nfs_mode);
6334 result = SMB_VFS_CHMOD(fsp->conn,
6340 DEBUG(1, ("chmod: %s, result: %d, %04o error %s\n", fsp_str_dbg(fsp),
6341 result, (unsigned)ms_nfs_mode,
6343 status = map_nt_error_from_unix(errno);
6350 return NT_STATUS_OK;
6353 static struct vfs_offload_ctx *fruit_offload_ctx;
6355 struct fruit_offload_read_state {
6356 struct vfs_handle_struct *handle;
6357 struct tevent_context *ev;
6363 static void fruit_offload_read_done(struct tevent_req *subreq);
6365 static struct tevent_req *fruit_offload_read_send(
6366 TALLOC_CTX *mem_ctx,
6367 struct tevent_context *ev,
6368 struct vfs_handle_struct *handle,
6375 struct tevent_req *req = NULL;
6376 struct tevent_req *subreq = NULL;
6377 struct fruit_offload_read_state *state = NULL;
6379 req = tevent_req_create(mem_ctx, &state,
6380 struct fruit_offload_read_state);
6384 *state = (struct fruit_offload_read_state) {
6391 subreq = SMB_VFS_NEXT_OFFLOAD_READ_SEND(mem_ctx, ev, handle, fsp,
6392 fsctl, ttl, offset, to_copy);
6393 if (tevent_req_nomem(subreq, req)) {
6394 return tevent_req_post(req, ev);
6396 tevent_req_set_callback(subreq, fruit_offload_read_done, req);
6400 static void fruit_offload_read_done(struct tevent_req *subreq)
6402 struct tevent_req *req = tevent_req_callback_data(
6403 subreq, struct tevent_req);
6404 struct fruit_offload_read_state *state = tevent_req_data(
6405 req, struct fruit_offload_read_state);
6408 status = SMB_VFS_NEXT_OFFLOAD_READ_RECV(subreq,
6412 TALLOC_FREE(subreq);
6413 if (tevent_req_nterror(req, status)) {
6417 if (state->fsctl != FSCTL_SRV_REQUEST_RESUME_KEY) {
6418 tevent_req_done(req);
6422 status = vfs_offload_token_ctx_init(state->fsp->conn->sconn->client,
6423 &fruit_offload_ctx);
6424 if (tevent_req_nterror(req, status)) {
6428 status = vfs_offload_token_db_store_fsp(fruit_offload_ctx,
6431 if (tevent_req_nterror(req, status)) {
6435 tevent_req_done(req);
6439 static NTSTATUS fruit_offload_read_recv(struct tevent_req *req,
6440 struct vfs_handle_struct *handle,
6441 TALLOC_CTX *mem_ctx,
6444 struct fruit_offload_read_state *state = tevent_req_data(
6445 req, struct fruit_offload_read_state);
6448 if (tevent_req_is_nterror(req, &status)) {
6449 tevent_req_received(req);
6453 token->length = state->token.length;
6454 token->data = talloc_move(mem_ctx, &state->token.data);
6456 tevent_req_received(req);
6457 return NT_STATUS_OK;
6460 struct fruit_offload_write_state {
6461 struct vfs_handle_struct *handle;
6463 struct files_struct *src_fsp;
6464 struct files_struct *dst_fsp;
6468 static void fruit_offload_write_done(struct tevent_req *subreq);
6469 static struct tevent_req *fruit_offload_write_send(struct vfs_handle_struct *handle,
6470 TALLOC_CTX *mem_ctx,
6471 struct tevent_context *ev,
6474 off_t transfer_offset,
6475 struct files_struct *dest_fsp,
6479 struct tevent_req *req, *subreq;
6480 struct fruit_offload_write_state *state;
6482 struct fruit_config_data *config;
6483 off_t src_off = transfer_offset;
6484 files_struct *src_fsp = NULL;
6485 off_t to_copy = num;
6486 bool copyfile_enabled = false;
6488 DEBUG(10,("soff: %ju, doff: %ju, len: %ju\n",
6489 (uintmax_t)src_off, (uintmax_t)dest_off, (uintmax_t)num));
6491 SMB_VFS_HANDLE_GET_DATA(handle, config,
6492 struct fruit_config_data,
6495 req = tevent_req_create(mem_ctx, &state,
6496 struct fruit_offload_write_state);
6500 state->handle = handle;
6501 state->dst_fsp = dest_fsp;
6504 case FSCTL_SRV_COPYCHUNK:
6505 case FSCTL_SRV_COPYCHUNK_WRITE:
6506 copyfile_enabled = config->copyfile_enabled;
6513 * Check if this a OS X copyfile style copychunk request with
6514 * a requested chunk count of 0 that was translated to a
6515 * offload_write_send VFS call overloading the parameters src_off
6516 * = dest_off = num = 0.
6518 if (copyfile_enabled && num == 0 && src_off == 0 && dest_off == 0) {
6519 status = vfs_offload_token_db_fetch_fsp(
6520 fruit_offload_ctx, token, &src_fsp);
6521 if (tevent_req_nterror(req, status)) {
6522 return tevent_req_post(req, ev);
6524 state->src_fsp = src_fsp;
6526 status = vfs_stat_fsp(src_fsp);
6527 if (tevent_req_nterror(req, status)) {
6528 return tevent_req_post(req, ev);
6531 to_copy = src_fsp->fsp_name->st.st_ex_size;
6532 state->is_copyfile = true;
6535 subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle,
6544 if (tevent_req_nomem(subreq, req)) {
6545 return tevent_req_post(req, ev);
6548 tevent_req_set_callback(subreq, fruit_offload_write_done, req);
6552 static void fruit_offload_write_done(struct tevent_req *subreq)
6554 struct tevent_req *req = tevent_req_callback_data(
6555 subreq, struct tevent_req);
6556 struct fruit_offload_write_state *state = tevent_req_data(
6557 req, struct fruit_offload_write_state);
6559 unsigned int num_streams = 0;
6560 struct stream_struct *streams = NULL;
6562 struct smb_filename *src_fname_tmp = NULL;
6563 struct smb_filename *dst_fname_tmp = NULL;
6565 status = SMB_VFS_NEXT_OFFLOAD_WRITE_RECV(state->handle,
6568 TALLOC_FREE(subreq);
6569 if (tevent_req_nterror(req, status)) {
6573 if (!state->is_copyfile) {
6574 tevent_req_done(req);
6579 * Now copy all remaining streams. We know the share supports
6580 * streams, because we're in vfs_fruit. We don't do this async
6581 * because streams are few and small.
6583 status = vfs_streaminfo(state->handle->conn, state->src_fsp,
6584 state->src_fsp->fsp_name,
6585 req, &num_streams, &streams);
6586 if (tevent_req_nterror(req, status)) {
6590 if (num_streams == 1) {
6591 /* There is always one stream, ::$DATA. */
6592 tevent_req_done(req);
6596 for (i = 0; i < num_streams; i++) {
6597 DEBUG(10, ("%s: stream: '%s'/%zu\n",
6598 __func__, streams[i].name, (size_t)streams[i].size));
6600 src_fname_tmp = synthetic_smb_fname(
6602 state->src_fsp->fsp_name->base_name,
6605 state->src_fsp->fsp_name->flags);
6606 if (tevent_req_nomem(src_fname_tmp, req)) {
6610 if (is_ntfs_default_stream_smb_fname(src_fname_tmp)) {
6611 TALLOC_FREE(src_fname_tmp);
6615 dst_fname_tmp = synthetic_smb_fname(
6617 state->dst_fsp->fsp_name->base_name,
6620 state->dst_fsp->fsp_name->flags);
6621 if (tevent_req_nomem(dst_fname_tmp, req)) {
6622 TALLOC_FREE(src_fname_tmp);
6626 status = copy_file(req,
6627 state->handle->conn,
6630 OPENX_FILE_CREATE_IF_NOT_EXIST,
6632 if (!NT_STATUS_IS_OK(status)) {
6633 DEBUG(1, ("%s: copy %s to %s failed: %s\n", __func__,
6634 smb_fname_str_dbg(src_fname_tmp),
6635 smb_fname_str_dbg(dst_fname_tmp),
6636 nt_errstr(status)));
6637 TALLOC_FREE(src_fname_tmp);
6638 TALLOC_FREE(dst_fname_tmp);
6639 tevent_req_nterror(req, status);
6643 TALLOC_FREE(src_fname_tmp);
6644 TALLOC_FREE(dst_fname_tmp);
6647 TALLOC_FREE(streams);
6648 TALLOC_FREE(src_fname_tmp);
6649 TALLOC_FREE(dst_fname_tmp);
6650 tevent_req_done(req);
6653 static NTSTATUS fruit_offload_write_recv(struct vfs_handle_struct *handle,
6654 struct tevent_req *req,
6657 struct fruit_offload_write_state *state = tevent_req_data(
6658 req, struct fruit_offload_write_state);
6661 if (tevent_req_is_nterror(req, &status)) {
6662 DEBUG(1, ("server side copy chunk failed: %s\n",
6663 nt_errstr(status)));
6665 tevent_req_received(req);
6669 *copied = state->copied;
6670 tevent_req_received(req);
6672 return NT_STATUS_OK;
6675 static char *fruit_get_bandsize_line(char **lines, int numlines)
6678 static bool re_initialized = false;
6682 if (!re_initialized) {
6683 ret = regcomp(&re, "^[[:blank:]]*<key>band-size</key>$", 0);
6687 re_initialized = true;
6690 for (i = 0; i < numlines; i++) {
6691 regmatch_t matches[1];
6693 ret = regexec(&re, lines[i], 1, matches, 0);
6696 * Check if the match was on the last line, sa we want
6697 * the subsequent line.
6699 if (i + 1 == numlines) {
6702 return lines[i + 1];
6704 if (ret != REG_NOMATCH) {
6712 static bool fruit_get_bandsize_from_line(char *line, size_t *_band_size)
6715 static bool re_initialized = false;
6716 regmatch_t matches[2];
6721 if (!re_initialized) {
6724 "<integer>\\([[:digit:]]*\\)</integer>$",
6729 re_initialized = true;
6732 ret = regexec(&re, line, 2, matches, 0);
6734 DBG_ERR("regex failed [%s]\n", line);
6738 line[matches[1].rm_eo] = '\0';
6740 ok = conv_str_u64(&line[matches[1].rm_so], &band_size);
6744 *_band_size = (size_t)band_size;
6749 * This reads and parses an Info.plist from a TM sparsebundle looking for the
6750 * "band-size" key and value.
6752 static bool fruit_get_bandsize(vfs_handle_struct *handle,
6756 #define INFO_PLIST_MAX_SIZE 64*1024
6758 struct smb_filename *smb_fname = NULL;
6759 files_struct *fsp = NULL;
6760 uint8_t *file_data = NULL;
6761 char **lines = NULL;
6762 char *band_size_line = NULL;
6763 size_t plist_file_size;
6770 plist = talloc_asprintf(talloc_tos(),
6772 handle->conn->connectpath,
6774 if (plist == NULL) {
6779 smb_fname = synthetic_smb_fname(talloc_tos(), plist, NULL, NULL, 0);
6780 if (smb_fname == NULL) {
6785 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
6787 DBG_INFO("Ignoring Sparsebundle without Info.plist [%s]\n", dir);
6792 plist_file_size = smb_fname->st.st_ex_size;
6794 if (plist_file_size > INFO_PLIST_MAX_SIZE) {
6795 DBG_INFO("%s is too large, ignoring\n", plist);
6800 status = SMB_VFS_NEXT_CREATE_FILE(
6803 0, /* root_dir_fid */
6804 smb_fname, /* fname */
6805 FILE_GENERIC_READ, /* access_mask */
6806 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
6807 FILE_OPEN, /* create_disposition */
6808 0, /* create_options */
6809 0, /* file_attributes */
6810 INTERNAL_OPEN_ONLY, /* oplock_request */
6812 0, /* allocation_size */
6813 0, /* private_flags */
6818 NULL, NULL); /* create context */
6819 if (!NT_STATUS_IS_OK(status)) {
6820 DBG_INFO("Opening [%s] failed [%s]\n",
6821 smb_fname_str_dbg(smb_fname), nt_errstr(status));
6826 file_data = talloc_array(talloc_tos(), uint8_t, plist_file_size);
6827 if (file_data == NULL) {
6832 nread = SMB_VFS_NEXT_PREAD(handle, fsp, file_data, plist_file_size, 0);
6833 if (nread != plist_file_size) {
6834 DBG_ERR("Short read on [%s]: %zu/%zd\n",
6835 fsp_str_dbg(fsp), nread, plist_file_size);
6841 status = close_file(NULL, fsp, NORMAL_CLOSE);
6843 if (!NT_STATUS_IS_OK(status)) {
6844 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
6849 lines = file_lines_parse((char *)file_data,
6853 if (lines == NULL) {
6858 band_size_line = fruit_get_bandsize_line(lines, numlines);
6859 if (band_size_line == NULL) {
6860 DBG_ERR("Didn't find band-size key in [%s]\n",
6861 smb_fname_str_dbg(smb_fname));
6866 ok = fruit_get_bandsize_from_line(band_size_line, band_size);
6868 DBG_ERR("fruit_get_bandsize_from_line failed\n");
6872 DBG_DEBUG("Parsed band-size [%zu] for [%s]\n", *band_size, plist);
6876 status = close_file(NULL, fsp, NORMAL_CLOSE);
6877 if (!NT_STATUS_IS_OK(status)) {
6878 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
6883 TALLOC_FREE(smb_fname);
6884 TALLOC_FREE(file_data);
6889 struct fruit_disk_free_state {
6893 static bool fruit_get_num_bands(vfs_handle_struct *handle,
6898 struct smb_filename *bands_dir = NULL;
6900 struct dirent *e = NULL;
6904 path = talloc_asprintf(talloc_tos(),
6906 handle->conn->connectpath,
6912 bands_dir = synthetic_smb_fname(talloc_tos(),
6918 if (bands_dir == NULL) {
6922 d = SMB_VFS_NEXT_OPENDIR(handle, bands_dir, NULL, 0);
6924 TALLOC_FREE(bands_dir);
6930 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
6932 e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
6934 if (ISDOT(e->d_name) || ISDOTDOT(e->d_name)) {
6940 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
6942 TALLOC_FREE(bands_dir);
6946 DBG_DEBUG("%zu bands in [%s]\n", nbands, smb_fname_str_dbg(bands_dir));
6948 TALLOC_FREE(bands_dir);
6954 static bool fruit_tmsize_do_dirent(vfs_handle_struct *handle,
6955 struct fruit_disk_free_state *state,
6960 size_t sparsebundle_strlen = strlen("sparsebundle");
6961 size_t bandsize = 0;
6965 p = strstr(e->d_name, "sparsebundle");
6970 if (p[sparsebundle_strlen] != '\0') {
6974 DBG_DEBUG("Processing sparsebundle [%s]\n", e->d_name);
6976 ok = fruit_get_bandsize(handle, e->d_name, &bandsize);
6979 * Beware of race conditions: this may be an uninitialized
6980 * Info.plist that a client is just creating. We don't want let
6981 * this to trigger complete failure.
6983 DBG_ERR("Processing sparsebundle [%s] failed\n", e->d_name);
6987 ok = fruit_get_num_bands(handle, e->d_name, &nbands);
6990 * Beware of race conditions: this may be a backup sparsebundle
6991 * in an early stage lacking a bands subdirectory. We don't want
6992 * let this to trigger complete failure.
6994 DBG_ERR("Processing sparsebundle [%s] failed\n", e->d_name);
6998 if (bandsize > SIZE_MAX/nbands) {
6999 DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
7003 tm_size = bandsize * nbands;
7005 if (state->total_size + tm_size < state->total_size) {
7006 DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
7011 state->total_size += tm_size;
7013 DBG_DEBUG("[%s] tm_size [%jd] total_size [%jd]\n",
7014 e->d_name, (intmax_t)tm_size, (intmax_t)state->total_size);
7020 * Calculate used size of a TimeMachine volume
7022 * This assumes that the volume is used only for TimeMachine.
7024 * - readdir(basedir of share), then
7025 * - for every element that matches regex "^\(.*\)\.sparsebundle$" :
7026 * - parse "\1.sparsebundle/Info.plist" and read the band-size XML key
7027 * - count band files in "\1.sparsebundle/bands/"
7028 * - calculate used size of all bands: band_count * band_size
7030 static uint64_t fruit_disk_free(vfs_handle_struct *handle,
7031 const struct smb_filename *smb_fname,
7036 struct fruit_config_data *config = NULL;
7037 struct fruit_disk_free_state state = {0};
7039 struct dirent *e = NULL;
7045 SMB_VFS_HANDLE_GET_DATA(handle, config,
7046 struct fruit_config_data,
7049 if (!config->time_machine ||
7050 config->time_machine_max_size == 0)
7052 return SMB_VFS_NEXT_DISK_FREE(handle,
7059 d = SMB_VFS_NEXT_OPENDIR(handle, smb_fname, NULL, 0);
7064 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
7066 e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
7068 ok = fruit_tmsize_do_dirent(handle, &state, e);
7070 SMB_VFS_NEXT_CLOSEDIR(handle, d);
7075 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
7080 dsize = config->time_machine_max_size / 512;
7081 dfree = dsize - (state.total_size / 512);
7082 if (dfree > dsize) {
7092 static struct vfs_fn_pointers vfs_fruit_fns = {
7093 .connect_fn = fruit_connect,
7094 .disk_free_fn = fruit_disk_free,
7096 /* File operations */
7097 .chmod_fn = fruit_chmod,
7098 .chown_fn = fruit_chown,
7099 .unlink_fn = fruit_unlink,
7100 .rename_fn = fruit_rename,
7101 .rmdir_fn = fruit_rmdir,
7102 .open_fn = fruit_open,
7103 .close_fn = fruit_close,
7104 .pread_fn = fruit_pread,
7105 .pwrite_fn = fruit_pwrite,
7106 .pread_send_fn = fruit_pread_send,
7107 .pread_recv_fn = fruit_pread_recv,
7108 .pwrite_send_fn = fruit_pwrite_send,
7109 .pwrite_recv_fn = fruit_pwrite_recv,
7110 .stat_fn = fruit_stat,
7111 .lstat_fn = fruit_lstat,
7112 .fstat_fn = fruit_fstat,
7113 .streaminfo_fn = fruit_streaminfo,
7114 .ntimes_fn = fruit_ntimes,
7115 .ftruncate_fn = fruit_ftruncate,
7116 .fallocate_fn = fruit_fallocate,
7117 .create_file_fn = fruit_create_file,
7118 .readdir_attr_fn = fruit_readdir_attr,
7119 .offload_read_send_fn = fruit_offload_read_send,
7120 .offload_read_recv_fn = fruit_offload_read_recv,
7121 .offload_write_send_fn = fruit_offload_write_send,
7122 .offload_write_recv_fn = fruit_offload_write_recv,
7124 /* NT ACL operations */
7125 .fget_nt_acl_fn = fruit_fget_nt_acl,
7126 .fset_nt_acl_fn = fruit_fset_nt_acl,
7130 NTSTATUS vfs_fruit_init(TALLOC_CTX *ctx)
7132 NTSTATUS ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "fruit",
7134 if (!NT_STATUS_IS_OK(ret)) {
7138 vfs_fruit_debug_level = debug_add_class("fruit");
7139 if (vfs_fruit_debug_level == -1) {
7140 vfs_fruit_debug_level = DBGC_VFS;
7141 DEBUG(0, ("%s: Couldn't register custom debugging class!\n",
7144 DEBUG(10, ("%s: Debug class number of '%s': %d\n",
7145 "vfs_fruit_init","fruit",vfs_fruit_debug_level));