2 * OS X and Netatalk interoperability VFS module for Samba-3.x
4 * Copyright (C) Ralph Boehme, 2013, 2014
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
21 #include "MacExtensions.h"
22 #include "smbd/smbd.h"
23 #include "system/filesys.h"
24 #include "lib/util/time.h"
25 #include "system/shmem.h"
26 #include "locking/proto.h"
27 #include "smbd/globals.h"
29 #include "libcli/security/security.h"
30 #include "../libcli/smb/smb2_create_ctx.h"
31 #include "lib/util/sys_rw.h"
32 #include "lib/util/tevent_ntstatus.h"
33 #include "lib/util/tevent_unix.h"
34 #include "offload_token.h"
35 #include "string_replace.h"
37 #include <gnutls/gnutls.h>
38 #include <gnutls/crypto.h>
41 * Enhanced OS X and Netatalk compatibility
42 * ========================================
44 * This modules takes advantage of vfs_streams_xattr and
45 * vfs_catia. VFS modules vfs_fruit and vfs_streams_xattr must be
46 * loaded in the correct order:
48 * vfs modules = catia fruit streams_xattr
50 * The module intercepts the OS X special streams "AFP_AfpInfo" and
51 * "AFP_Resource" and handles them in a special way. All other named
52 * streams are deferred to vfs_streams_xattr.
54 * The OS X client maps all NTFS illegal characters to the Unicode
55 * private range. This module optionally stores the charcters using
56 * their native ASCII encoding using vfs_catia. If you're not enabling
57 * this feature, you can skip catia from vfs modules.
59 * Finally, open modes are optionally checked against Netatalk AFP
62 * The "AFP_AfpInfo" named stream is a binary blob containing OS X
63 * extended metadata for files and directories. This module optionally
64 * reads and stores this metadata in a way compatible with Netatalk 3
65 * which stores the metadata in an EA "org.netatalk.metadata". Cf
66 * source3/include/MacExtensions.h for a description of the binary
69 * The "AFP_Resource" named stream may be arbitrarily large, thus it
70 * can't be stored in an xattr on most filesystem. ZFS on Solaris is
71 * the only available filesystem where xattrs can be of any size and
72 * the OS supports using the file APIs for xattrs.
74 * The AFP_Resource stream is stored in an AppleDouble file prepending
75 * "._" to the filename. On Solaris with ZFS the stream is optionally
76 * stored in an EA "org.netatalk.resource".
82 * The OS X SMB client sends xattrs as ADS too. For xattr interop with
83 * other protocols you may want to adjust the xattr names the VFS
84 * module vfs_streams_xattr uses for storing ADS's. This defaults to
85 * user.DosStream.ADS_NAME:$DATA and can be changed by specifying
86 * these module parameters:
88 * streams_xattr:prefix = user.
89 * streams_xattr:store_stream_type = false
95 * - log diagnostic if any needed VFS module is not loaded
96 * (eg with lp_vfs_objects())
100 static int vfs_fruit_debug_level = DBGC_VFS;
102 static struct global_fruit_config {
103 bool nego_aapl; /* client negotiated AAPL */
105 } global_fruit_config;
108 #define DBGC_CLASS vfs_fruit_debug_level
110 #define FRUIT_PARAM_TYPE_NAME "fruit"
111 #define ADOUBLE_NAME_PREFIX "._"
113 #define NETATALK_META_XATTR "org.netatalk.Metadata"
114 #define NETATALK_RSRC_XATTR "org.netatalk.ResourceFork"
116 #if defined(HAVE_ATTROPEN)
117 #define AFPINFO_EA_NETATALK NETATALK_META_XATTR
118 #define AFPRESOURCE_EA_NETATALK NETATALK_RSRC_XATTR
120 #define AFPINFO_EA_NETATALK "user." NETATALK_META_XATTR
121 #define AFPRESOURCE_EA_NETATALK "user." NETATALK_RSRC_XATTR
124 enum apple_fork {APPLE_FORK_DATA, APPLE_FORK_RSRC};
126 enum fruit_rsrc {FRUIT_RSRC_STREAM, FRUIT_RSRC_ADFILE, FRUIT_RSRC_XATTR};
127 enum fruit_meta {FRUIT_META_STREAM, FRUIT_META_NETATALK};
128 enum fruit_locking {FRUIT_LOCKING_NETATALK, FRUIT_LOCKING_NONE};
129 enum fruit_encoding {FRUIT_ENC_NATIVE, FRUIT_ENC_PRIVATE};
131 struct fruit_config_data {
132 enum fruit_rsrc rsrc;
133 enum fruit_meta meta;
134 enum fruit_locking locking;
135 enum fruit_encoding encoding;
136 bool use_aapl; /* config from smb.conf */
138 bool readdir_attr_enabled;
139 bool unix_info_enabled;
140 bool copyfile_enabled;
141 bool veto_appledouble;
143 bool aapl_zero_file_id;
146 off_t time_machine_max_size;
147 bool wipe_intentionally_left_blank_rfork;
148 bool delete_empty_adfiles;
151 * Additional options, all enabled by default,
152 * possibly useful for analyzing performance. The associated
153 * operations with each of them may be expensive, so having
154 * the chance to disable them individually gives a chance
155 * tweaking the setup for the particular usecase.
157 bool readdir_attr_rsize;
158 bool readdir_attr_finder_info;
159 bool readdir_attr_max_access;
162 static const struct enum_list fruit_rsrc[] = {
163 {FRUIT_RSRC_STREAM, "stream"}, /* pass on to vfs_streams_xattr */
164 {FRUIT_RSRC_ADFILE, "file"}, /* ._ AppleDouble file */
165 {FRUIT_RSRC_XATTR, "xattr"}, /* Netatalk compatible xattr (ZFS only) */
169 static const struct enum_list fruit_meta[] = {
170 {FRUIT_META_STREAM, "stream"}, /* pass on to vfs_streams_xattr */
171 {FRUIT_META_NETATALK, "netatalk"}, /* Netatalk compatible xattr */
175 static const struct enum_list fruit_locking[] = {
176 {FRUIT_LOCKING_NETATALK, "netatalk"}, /* synchronize locks with Netatalk */
177 {FRUIT_LOCKING_NONE, "none"},
181 static const struct enum_list fruit_encoding[] = {
182 {FRUIT_ENC_NATIVE, "native"}, /* map unicode private chars to ASCII */
183 {FRUIT_ENC_PRIVATE, "private"}, /* keep unicode private chars */
187 static const char *fruit_catia_maps =
188 "0x01:0xf001,0x02:0xf002,0x03:0xf003,0x04:0xf004,"
189 "0x05:0xf005,0x06:0xf006,0x07:0xf007,0x08:0xf008,"
190 "0x09:0xf009,0x0a:0xf00a,0x0b:0xf00b,0x0c:0xf00c,"
191 "0x0d:0xf00d,0x0e:0xf00e,0x0f:0xf00f,0x10:0xf010,"
192 "0x11:0xf011,0x12:0xf012,0x13:0xf013,0x14:0xf014,"
193 "0x15:0xf015,0x16:0xf016,0x17:0xf017,0x18:0xf018,"
194 "0x19:0xf019,0x1a:0xf01a,0x1b:0xf01b,0x1c:0xf01c,"
195 "0x1d:0xf01d,0x1e:0xf01e,0x1f:0xf01f,"
196 "0x22:0xf020,0x2a:0xf021,0x3a:0xf022,0x3c:0xf023,"
197 "0x3e:0xf024,0x3f:0xf025,0x5c:0xf026,0x7c:0xf027,"
200 /*****************************************************************************
201 * Defines, functions and data structures that deal with AppleDouble
202 *****************************************************************************/
205 * There are two AppleDouble blobs we deal with:
207 * - ADOUBLE_META - AppleDouble blob used by Netatalk for storing
208 * metadata in an xattr
210 * - ADOUBLE_RSRC - AppleDouble blob used by OS X and Netatalk in
213 typedef enum {ADOUBLE_META, ADOUBLE_RSRC} adouble_type_t;
216 #define AD_VERSION2 0x00020000
217 #define AD_VERSION AD_VERSION2
220 * AppleDouble entry IDs.
222 #define ADEID_DFORK 1
223 #define ADEID_RFORK 2
225 #define ADEID_COMMENT 4
226 #define ADEID_ICONBW 5
227 #define ADEID_ICONCOL 6
228 #define ADEID_FILEI 7
229 #define ADEID_FILEDATESI 8
230 #define ADEID_FINDERI 9
231 #define ADEID_MACFILEI 10
232 #define ADEID_PRODOSFILEI 11
233 #define ADEID_MSDOSFILEI 12
234 #define ADEID_SHORTNAME 13
235 #define ADEID_AFPFILEI 14
238 /* Private Netatalk entries */
239 #define ADEID_PRIVDEV 16
240 #define ADEID_PRIVINO 17
241 #define ADEID_PRIVSYN 18
242 #define ADEID_PRIVID 19
243 #define ADEID_MAX (ADEID_PRIVID + 1)
246 * These are the real ids for the private entries,
247 * as stored in the adouble file
249 #define AD_DEV 0x80444556
250 #define AD_INO 0x80494E4F
251 #define AD_SYN 0x8053594E
252 #define AD_ID 0x8053567E
254 /* Number of actually used entries */
255 #define ADEID_NUM_XATTR 8
256 #define ADEID_NUM_DOT_UND 2
257 #define ADEID_NUM_RSRC_XATTR 1
259 /* AppleDouble magic */
260 #define AD_APPLESINGLE_MAGIC 0x00051600
261 #define AD_APPLEDOUBLE_MAGIC 0x00051607
262 #define AD_MAGIC AD_APPLEDOUBLE_MAGIC
264 /* Sizes of relevant entry bits */
265 #define ADEDLEN_MAGIC 4
266 #define ADEDLEN_VERSION 4
267 #define ADEDLEN_FILLER 16
268 #define AD_FILLER_TAG "Netatalk " /* should be 16 bytes */
269 #define AD_FILLER_TAG_OSX "Mac OS X " /* should be 16 bytes */
270 #define ADEDLEN_NENTRIES 2
271 #define AD_HEADER_LEN (ADEDLEN_MAGIC + ADEDLEN_VERSION + \
272 ADEDLEN_FILLER + ADEDLEN_NENTRIES) /* 26 */
273 #define AD_ENTRY_LEN_EID 4
274 #define AD_ENTRY_LEN_OFF 4
275 #define AD_ENTRY_LEN_LEN 4
276 #define AD_ENTRY_LEN (AD_ENTRY_LEN_EID + AD_ENTRY_LEN_OFF + AD_ENTRY_LEN_LEN)
279 #define ADEDLEN_NAME 255
280 #define ADEDLEN_COMMENT 200
281 #define ADEDLEN_FILEI 16
282 #define ADEDLEN_FINDERI 32
283 #define ADEDLEN_FILEDATESI 16
284 #define ADEDLEN_SHORTNAME 12 /* length up to 8.3 */
285 #define ADEDLEN_AFPFILEI 4
286 #define ADEDLEN_MACFILEI 4
287 #define ADEDLEN_PRODOSFILEI 8
288 #define ADEDLEN_MSDOSFILEI 2
289 #define ADEDLEN_DID 4
290 #define ADEDLEN_PRIVDEV 8
291 #define ADEDLEN_PRIVINO 8
292 #define ADEDLEN_PRIVSYN 8
293 #define ADEDLEN_PRIVID 4
296 #define ADEDOFF_MAGIC 0
297 #define ADEDOFF_VERSION (ADEDOFF_MAGIC + ADEDLEN_MAGIC)
298 #define ADEDOFF_FILLER (ADEDOFF_VERSION + ADEDLEN_VERSION)
299 #define ADEDOFF_NENTRIES (ADEDOFF_FILLER + ADEDLEN_FILLER)
301 #define ADEDOFF_FINDERI_XATTR (AD_HEADER_LEN + \
302 (ADEID_NUM_XATTR * AD_ENTRY_LEN))
303 #define ADEDOFF_COMMENT_XATTR (ADEDOFF_FINDERI_XATTR + ADEDLEN_FINDERI)
304 #define ADEDOFF_FILEDATESI_XATTR (ADEDOFF_COMMENT_XATTR + ADEDLEN_COMMENT)
305 #define ADEDOFF_AFPFILEI_XATTR (ADEDOFF_FILEDATESI_XATTR + \
307 #define ADEDOFF_PRIVDEV_XATTR (ADEDOFF_AFPFILEI_XATTR + ADEDLEN_AFPFILEI)
308 #define ADEDOFF_PRIVINO_XATTR (ADEDOFF_PRIVDEV_XATTR + ADEDLEN_PRIVDEV)
309 #define ADEDOFF_PRIVSYN_XATTR (ADEDOFF_PRIVINO_XATTR + ADEDLEN_PRIVINO)
310 #define ADEDOFF_PRIVID_XATTR (ADEDOFF_PRIVSYN_XATTR + ADEDLEN_PRIVSYN)
312 #define ADEDOFF_FINDERI_DOT_UND (AD_HEADER_LEN + \
313 (ADEID_NUM_DOT_UND * AD_ENTRY_LEN))
314 #define ADEDOFF_RFORK_DOT_UND (ADEDOFF_FINDERI_DOT_UND + ADEDLEN_FINDERI)
316 #define AD_DATASZ_XATTR (AD_HEADER_LEN + \
317 (ADEID_NUM_XATTR * AD_ENTRY_LEN) + \
318 ADEDLEN_FINDERI + ADEDLEN_COMMENT + \
319 ADEDLEN_FILEDATESI + ADEDLEN_AFPFILEI + \
320 ADEDLEN_PRIVDEV + ADEDLEN_PRIVINO + \
321 ADEDLEN_PRIVSYN + ADEDLEN_PRIVID)
323 #if AD_DATASZ_XATTR != 402
324 #error bad size for AD_DATASZ_XATTR
327 #define AD_DATASZ_DOT_UND (AD_HEADER_LEN + \
328 (ADEID_NUM_DOT_UND * AD_ENTRY_LEN) + \
330 #if AD_DATASZ_DOT_UND != 82
331 #error bad size for AD_DATASZ_DOT_UND
335 * Sharemode locks fcntl() offsets
337 #if _FILE_OFFSET_BITS == 64 || defined(HAVE_LARGEFILE)
338 #define AD_FILELOCK_BASE (UINT64_C(0x7FFFFFFFFFFFFFFF) - 9)
340 #define AD_FILELOCK_BASE (UINT32_C(0x7FFFFFFF) - 9)
342 #define BYTELOCK_MAX (AD_FILELOCK_BASE - 1)
344 #define AD_FILELOCK_OPEN_WR (AD_FILELOCK_BASE + 0)
345 #define AD_FILELOCK_OPEN_RD (AD_FILELOCK_BASE + 1)
346 #define AD_FILELOCK_RSRC_OPEN_WR (AD_FILELOCK_BASE + 2)
347 #define AD_FILELOCK_RSRC_OPEN_RD (AD_FILELOCK_BASE + 3)
348 #define AD_FILELOCK_DENY_WR (AD_FILELOCK_BASE + 4)
349 #define AD_FILELOCK_DENY_RD (AD_FILELOCK_BASE + 5)
350 #define AD_FILELOCK_RSRC_DENY_WR (AD_FILELOCK_BASE + 6)
351 #define AD_FILELOCK_RSRC_DENY_RD (AD_FILELOCK_BASE + 7)
352 #define AD_FILELOCK_OPEN_NONE (AD_FILELOCK_BASE + 8)
353 #define AD_FILELOCK_RSRC_OPEN_NONE (AD_FILELOCK_BASE + 9)
355 /* Time stuff we overload the bits a little */
356 #define AD_DATE_CREATE 0
357 #define AD_DATE_MODIFY 4
358 #define AD_DATE_BACKUP 8
359 #define AD_DATE_ACCESS 12
360 #define AD_DATE_MASK (AD_DATE_CREATE | AD_DATE_MODIFY | \
361 AD_DATE_BACKUP | AD_DATE_ACCESS)
362 #define AD_DATE_UNIX (1 << 10)
363 #define AD_DATE_START 0x80000000
364 #define AD_DATE_DELTA 946684800
365 #define AD_DATE_FROM_UNIX(x) (htonl((x) - AD_DATE_DELTA))
366 #define AD_DATE_TO_UNIX(x) (ntohl(x) + AD_DATE_DELTA)
368 #define AD_XATTR_HDR_MAGIC 0x41545452 /* 'ATTR' */
369 #define AD_XATTR_MAX_ENTRIES 1024 /* Some arbitrarily enforced limit */
370 #define AD_XATTR_HDR_SIZE 36
371 #define AD_XATTR_MAX_HDR_SIZE 65536
373 /* Accessor macros */
374 #define ad_getentrylen(ad,eid) ((ad)->ad_eid[(eid)].ade_len)
375 #define ad_getentryoff(ad,eid) ((ad)->ad_eid[(eid)].ade_off)
376 #define ad_setentrylen(ad,eid,len) ((ad)->ad_eid[(eid)].ade_len = (len))
377 #define ad_setentryoff(ad,eid,off) ((ad)->ad_eid[(eid)].ade_off = (off))
380 * Both struct ad_xattr_header and struct ad_xattr_entry describe the in memory
381 * representation as well as the on-disk format.
383 * The ad_xattr_header follows the FinderInfo data in the FinderInfo entry if
384 * the length of the FinderInfo entry is larger then 32 bytes. It is then
385 * preceeded with 2 bytes padding.
387 * Cf: https://opensource.apple.com/source/xnu/xnu-4570.1.46/bsd/vfs/vfs_xattr.c
390 struct ad_xattr_header {
391 uint32_t adx_magic; /* ATTR_HDR_MAGIC */
392 uint32_t adx_debug_tag; /* for debugging == file id of owning file */
393 uint32_t adx_total_size; /* file offset of end of attribute header + entries + data */
394 uint32_t adx_data_start; /* file offset to attribute data area */
395 uint32_t adx_data_length; /* length of attribute data area */
396 uint32_t adx_reserved[3];
398 uint16_t adx_num_attrs;
401 /* On-disk entries are aligned on 4 byte boundaries */
402 struct ad_xattr_entry {
403 uint32_t adx_offset; /* file offset to data */
404 uint32_t adx_length; /* size of attribute data */
406 uint8_t adx_namelen; /* included the NULL terminator */
407 char *adx_name; /* NULL-terminated UTF-8 name */
418 adouble_type_t ad_type;
421 uint8_t ad_filler[ADEDLEN_FILLER];
422 struct ad_entry ad_eid[ADEID_MAX];
424 struct ad_xattr_header adx_header;
425 struct ad_xattr_entry *adx_entries;
428 struct ad_entry_order {
429 uint32_t id, offset, len;
432 /* Netatalk AppleDouble metadata xattr */
434 struct ad_entry_order entry_order_meta_xattr[ADEID_NUM_XATTR + 1] = {
435 {ADEID_FINDERI, ADEDOFF_FINDERI_XATTR, ADEDLEN_FINDERI},
436 {ADEID_COMMENT, ADEDOFF_COMMENT_XATTR, 0},
437 {ADEID_FILEDATESI, ADEDOFF_FILEDATESI_XATTR, ADEDLEN_FILEDATESI},
438 {ADEID_AFPFILEI, ADEDOFF_AFPFILEI_XATTR, ADEDLEN_AFPFILEI},
439 {ADEID_PRIVDEV, ADEDOFF_PRIVDEV_XATTR, 0},
440 {ADEID_PRIVINO, ADEDOFF_PRIVINO_XATTR, 0},
441 {ADEID_PRIVSYN, ADEDOFF_PRIVSYN_XATTR, 0},
442 {ADEID_PRIVID, ADEDOFF_PRIVID_XATTR, 0},
446 /* AppleDouble resource fork file (the ones prefixed by "._") */
448 struct ad_entry_order entry_order_dot_und[ADEID_NUM_DOT_UND + 1] = {
449 {ADEID_FINDERI, ADEDOFF_FINDERI_DOT_UND, ADEDLEN_FINDERI},
450 {ADEID_RFORK, ADEDOFF_RFORK_DOT_UND, 0},
454 /* Conversion from enumerated id to on-disk AppleDouble id */
455 #define AD_EID_DISK(a) (set_eid[a])
456 static const uint32_t set_eid[] = {
457 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
458 AD_DEV, AD_INO, AD_SYN, AD_ID
461 static char empty_resourcefork[] = {
462 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
463 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
464 0x54, 0x68, 0x69, 0x73, 0x20, 0x72, 0x65, 0x73,
465 0x6F, 0x75, 0x72, 0x63, 0x65, 0x20, 0x66, 0x6F,
466 0x72, 0x6B, 0x20, 0x69, 0x6E, 0x74, 0x65, 0x6E,
467 0x74, 0x69, 0x6F, 0x6E, 0x61, 0x6C, 0x6C, 0x79,
468 0x20, 0x6C, 0x65, 0x66, 0x74, 0x20, 0x62, 0x6C,
469 0x61, 0x6E, 0x6B, 0x20, 0x20, 0x20, 0x00, 0x00,
470 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
471 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
472 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
473 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
474 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
475 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
476 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
477 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
478 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
479 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
480 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
481 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
482 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
483 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
484 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
485 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
486 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
487 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
488 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
489 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
490 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
491 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
492 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
493 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
494 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
495 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
496 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
497 0x00, 0x1C, 0x00, 0x1E, 0xFF, 0xFF
501 /* tcon config handle */
502 struct fruit_config_data *config;
504 /* Denote stream type, meta or rsrc */
507 /* Whether the create created the stream */
511 * AFP_AfpInfo stream created, but not written yet, thus still a fake
512 * pipe fd. This is set to true in fruit_open_meta if there was no
513 * exisiting stream but the caller requested O_CREAT. It is later set to
514 * false when we get a write on the stream that then does open and
523 * Forward declarations
525 static struct adouble *ad_init(TALLOC_CTX *ctx,
526 adouble_type_t type);
527 static struct adouble *ad_get(TALLOC_CTX *ctx,
528 vfs_handle_struct *handle,
529 const struct smb_filename *smb_fname,
530 adouble_type_t type);
531 static int ad_set(vfs_handle_struct *handle,
533 const struct smb_filename *smb_fname);
534 static int ad_fset(struct vfs_handle_struct *handle,
537 static int adouble_path(TALLOC_CTX *ctx,
538 const struct smb_filename *smb_fname__in,
539 struct smb_filename **ppsmb_fname_out);
540 static AfpInfo *afpinfo_new(TALLOC_CTX *ctx);
541 static ssize_t afpinfo_pack(const AfpInfo *ai, char *buf);
542 static AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data);
546 * Return a pointer to an AppleDouble entry
548 * Returns NULL if the entry is not present
550 static char *ad_get_entry(const struct adouble *ad, int eid)
552 off_t off = ad_getentryoff(ad, eid);
553 size_t len = ad_getentrylen(ad, eid);
555 if (off == 0 || len == 0) {
559 return ad->ad_data + off;
565 static int ad_getdate(const struct adouble *ad,
566 unsigned int dateoff,
569 bool xlate = (dateoff & AD_DATE_UNIX);
572 dateoff &= AD_DATE_MASK;
573 p = ad_get_entry(ad, ADEID_FILEDATESI);
578 if (dateoff > AD_DATE_ACCESS) {
582 memcpy(date, p + dateoff, sizeof(uint32_t));
585 *date = AD_DATE_TO_UNIX(*date);
593 static int ad_setdate(struct adouble *ad, unsigned int dateoff, uint32_t date)
595 bool xlate = (dateoff & AD_DATE_UNIX);
598 p = ad_get_entry(ad, ADEID_FILEDATESI);
603 dateoff &= AD_DATE_MASK;
605 date = AD_DATE_FROM_UNIX(date);
608 if (dateoff > AD_DATE_ACCESS) {
612 memcpy(p + dateoff, &date, sizeof(date));
619 * Map on-disk AppleDouble id to enumerated id
621 static uint32_t get_eid(uint32_t eid)
629 return ADEID_PRIVDEV;
631 return ADEID_PRIVINO;
633 return ADEID_PRIVSYN;
644 * Pack AppleDouble structure into data buffer
646 static bool ad_pack(struct adouble *ad)
653 bufsize = talloc_get_size(ad->ad_data);
654 if (bufsize < AD_DATASZ_DOT_UND) {
655 DBG_ERR("bad buffer size [0x%" PRIx32 "]\n", bufsize);
659 if (offset + ADEDLEN_MAGIC < offset ||
660 offset + ADEDLEN_MAGIC >= bufsize) {
663 RSIVAL(ad->ad_data, offset, ad->ad_magic);
664 offset += ADEDLEN_MAGIC;
666 if (offset + ADEDLEN_VERSION < offset ||
667 offset + ADEDLEN_VERSION >= bufsize) {
670 RSIVAL(ad->ad_data, offset, ad->ad_version);
671 offset += ADEDLEN_VERSION;
673 if (offset + ADEDLEN_FILLER < offset ||
674 offset + ADEDLEN_FILLER >= bufsize) {
677 if (ad->ad_type == ADOUBLE_RSRC) {
678 memcpy(ad->ad_data + offset, AD_FILLER_TAG, ADEDLEN_FILLER);
680 offset += ADEDLEN_FILLER;
682 if (offset + ADEDLEN_NENTRIES < offset ||
683 offset + ADEDLEN_NENTRIES >= bufsize) {
686 offset += ADEDLEN_NENTRIES;
688 for (eid = 0, nent = 0; eid < ADEID_MAX; eid++) {
689 if (ad->ad_eid[eid].ade_off == 0) {
691 * ade_off is also used as indicator whether a
692 * specific entry is used or not
697 if (offset + AD_ENTRY_LEN_EID < offset ||
698 offset + AD_ENTRY_LEN_EID >= bufsize) {
701 RSIVAL(ad->ad_data, offset, AD_EID_DISK(eid));
702 offset += AD_ENTRY_LEN_EID;
704 if (offset + AD_ENTRY_LEN_OFF < offset ||
705 offset + AD_ENTRY_LEN_OFF >= bufsize) {
708 RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_off);
709 offset += AD_ENTRY_LEN_OFF;
711 if (offset + AD_ENTRY_LEN_LEN < offset ||
712 offset + AD_ENTRY_LEN_LEN >= bufsize) {
715 RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_len);
716 offset += AD_ENTRY_LEN_LEN;
721 if (ADEDOFF_NENTRIES + 2 >= bufsize) {
724 RSSVAL(ad->ad_data, ADEDOFF_NENTRIES, nent);
729 static bool ad_unpack_xattrs(struct adouble *ad)
731 struct ad_xattr_header *h = &ad->adx_header;
732 const char *p = ad->ad_data;
736 if (ad_getentrylen(ad, ADEID_FINDERI) <= ADEDLEN_FINDERI) {
740 /* 2 bytes padding */
741 hoff = ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI + 2;
743 h->adx_magic = RIVAL(p, hoff + 0);
744 h->adx_debug_tag = RIVAL(p, hoff + 4); /* Not used -> not checked */
745 h->adx_total_size = RIVAL(p, hoff + 8);
746 h->adx_data_start = RIVAL(p, hoff + 12);
747 h->adx_data_length = RIVAL(p, hoff + 16);
748 h->adx_flags = RSVAL(p, hoff + 32); /* Not used -> not checked */
749 h->adx_num_attrs = RSVAL(p, hoff + 34);
751 if (h->adx_magic != AD_XATTR_HDR_MAGIC) {
752 DBG_ERR("Bad magic: 0x%" PRIx32 "\n", h->adx_magic);
756 if (h->adx_total_size > ad_getentryoff(ad, ADEID_RFORK)) {
757 DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size);
760 if (h->adx_total_size > AD_XATTR_MAX_HDR_SIZE) {
761 DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size);
765 if (h->adx_data_start < (hoff + AD_XATTR_HDR_SIZE)) {
766 DBG_ERR("Bad start: 0x%" PRIx32 "\n", h->adx_data_start);
770 if ((h->adx_data_start + h->adx_data_length) < h->adx_data_start) {
771 DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length);
774 if ((h->adx_data_start + h->adx_data_length) >
775 ad->adx_header.adx_total_size)
777 DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length);
781 if (h->adx_num_attrs > AD_XATTR_MAX_ENTRIES) {
782 DBG_ERR("Bad num xattrs: %" PRIu16 "\n", h->adx_num_attrs);
786 if (h->adx_num_attrs == 0) {
790 ad->adx_entries = talloc_zero_array(
791 ad, struct ad_xattr_entry, h->adx_num_attrs);
792 if (ad->adx_entries == NULL) {
796 hoff += AD_XATTR_HDR_SIZE;
798 for (i = 0; i < h->adx_num_attrs; i++) {
799 struct ad_xattr_entry *e = &ad->adx_entries[i];
801 hoff = (hoff + 3) & ~3;
803 e->adx_offset = RIVAL(p, hoff + 0);
804 e->adx_length = RIVAL(p, hoff + 4);
805 e->adx_flags = RSVAL(p, hoff + 8);
806 e->adx_namelen = *(p + hoff + 10);
808 if (e->adx_offset >= ad->adx_header.adx_total_size) {
809 DBG_ERR("Bad adx_offset: %" PRIx32 "\n",
814 if ((e->adx_offset + e->adx_length) < e->adx_offset) {
815 DBG_ERR("Bad adx_length: %" PRIx32 "\n",
820 if ((e->adx_offset + e->adx_length) >
821 ad->adx_header.adx_total_size)
823 DBG_ERR("Bad adx_length: %" PRIx32 "\n",
828 if (e->adx_namelen == 0) {
829 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
833 if ((hoff + 11 + e->adx_namelen) < hoff + 11) {
834 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
838 if ((hoff + 11 + e->adx_namelen) >
839 ad->adx_header.adx_data_start)
841 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
846 e->adx_name = talloc_strndup(ad->adx_entries,
849 if (e->adx_name == NULL) {
853 DBG_DEBUG("xattr [%s] offset [0x%x] size [0x%x]\n",
854 e->adx_name, e->adx_offset, e->adx_length);
855 dump_data(10, (uint8_t *)(ad->ad_data + e->adx_offset),
858 hoff += 11 + e->adx_namelen;
865 * Unpack an AppleDouble blob into a struct adoble
867 static bool ad_unpack(struct adouble *ad, const size_t nentries,
870 size_t bufsize = talloc_get_size(ad->ad_data);
872 uint32_t eid, len, off;
876 * The size of the buffer ad->ad_data is checked when read, so
877 * we wouldn't have to check our own offsets, a few extra
878 * checks won't hurt though. We have to check the offsets we
879 * read from the buffer anyway.
882 if (bufsize < (AD_HEADER_LEN + (AD_ENTRY_LEN * nentries))) {
883 DEBUG(1, ("bad size\n"));
887 ad->ad_magic = RIVAL(ad->ad_data, 0);
888 ad->ad_version = RIVAL(ad->ad_data, ADEDOFF_VERSION);
889 if ((ad->ad_magic != AD_MAGIC) || (ad->ad_version != AD_VERSION)) {
890 DEBUG(1, ("wrong magic or version\n"));
894 memcpy(ad->ad_filler, ad->ad_data + ADEDOFF_FILLER, ADEDLEN_FILLER);
896 adentries = RSVAL(ad->ad_data, ADEDOFF_NENTRIES);
897 if (adentries != nentries) {
898 DEBUG(1, ("invalid number of entries: %zu\n",
903 /* now, read in the entry bits */
904 for (i = 0; i < adentries; i++) {
905 eid = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN));
907 off = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 4);
908 len = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 8);
910 if (!eid || eid >= ADEID_MAX) {
911 DEBUG(1, ("bogus eid %d\n", eid));
916 * All entries other than the resource fork are
917 * expected to be read into the ad_data buffer, so
918 * ensure the specified offset is within that bound
920 if ((off > bufsize) && (eid != ADEID_RFORK)) {
921 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
927 * All entries besides FinderInfo and resource fork
928 * must fit into the buffer. FinderInfo is special as
929 * it may be larger then the default 32 bytes (if it
930 * contains marshalled xattrs), but we will fixup that
931 * in ad_convert(). And the resource fork is never
932 * accessed directly by the ad_data buf (also see
933 * comment above) anyway.
935 if ((eid != ADEID_RFORK) &&
936 (eid != ADEID_FINDERI) &&
937 ((off + len) > bufsize)) {
938 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
944 * That would be obviously broken
946 if (off > filesize) {
947 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
953 * Check for any entry that has its end beyond the
956 if (off + len < off) {
957 DEBUG(1, ("offset wrap in eid %d: off: %" PRIu32
958 ", len: %" PRIu32 "\n",
963 if (off + len > filesize) {
965 * If this is the resource fork entry, we fix
966 * up the length, for any other entry we bail
969 if (eid != ADEID_RFORK) {
970 DEBUG(1, ("bogus eid %d: off: %" PRIu32
971 ", len: %" PRIu32 "\n",
977 * Fixup the resource fork entry by limiting
978 * the size to entryoffset - filesize.
980 len = filesize - off;
981 DEBUG(1, ("Limiting ADEID_RFORK: off: %" PRIu32
982 ", len: %" PRIu32 "\n", off, len));
985 ad->ad_eid[eid].ade_off = off;
986 ad->ad_eid[eid].ade_len = len;
989 ok = ad_unpack_xattrs(ad);
997 static bool ad_convert_move_reso(vfs_handle_struct *handle,
999 const struct smb_filename *smb_fname)
1001 char *map = MAP_FAILED;
1007 if (ad_getentrylen(ad, ADEID_RFORK) == 0) {
1011 maplen = ad_getentryoff(ad, ADEID_RFORK) +
1012 ad_getentrylen(ad, ADEID_RFORK);
1014 /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
1015 map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
1017 if (map == MAP_FAILED) {
1018 DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
1023 memmove(map + ADEDOFF_RFORK_DOT_UND,
1024 map + ad_getentryoff(ad, ADEID_RFORK),
1025 ad_getentrylen(ad, ADEID_RFORK));
1027 rc = munmap(map, maplen);
1029 DBG_ERR("munmap failed: %s\n", strerror(errno));
1033 ad_setentryoff(ad, ADEID_RFORK, ADEDOFF_RFORK_DOT_UND);
1037 DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name);
1041 len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
1042 if (len != AD_DATASZ_DOT_UND) {
1043 DBG_ERR("%s: bad size: %zd\n", smb_fname->base_name, len);
1050 static bool ad_convert_xattr(vfs_handle_struct *handle,
1052 const struct smb_filename *smb_fname,
1053 bool *converted_xattr)
1055 static struct char_mappings **string_replace_cmaps = NULL;
1056 char *map = MAP_FAILED;
1060 int saved_errno = 0;
1065 *converted_xattr = false;
1067 if (ad_getentrylen(ad, ADEID_FINDERI) == ADEDLEN_FINDERI) {
1071 if (string_replace_cmaps == NULL) {
1072 const char **mappings = NULL;
1074 mappings = str_list_make_v3_const(
1075 talloc_tos(), fruit_catia_maps, NULL);
1076 if (mappings == NULL) {
1079 string_replace_cmaps = string_replace_init_map(mappings);
1080 TALLOC_FREE(mappings);
1083 maplen = ad_getentryoff(ad, ADEID_RFORK) +
1084 ad_getentrylen(ad, ADEID_RFORK);
1086 /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
1087 map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
1089 if (map == MAP_FAILED) {
1090 DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
1094 for (i = 0; i < ad->adx_header.adx_num_attrs; i++) {
1095 struct ad_xattr_entry *e = &ad->adx_entries[i];
1096 char *mapped_name = NULL;
1098 struct smb_filename *stream_name = NULL;
1099 files_struct *fsp = NULL;
1102 status = string_replace_allocate(handle->conn,
1104 string_replace_cmaps,
1107 vfs_translate_to_windows);
1108 if (!NT_STATUS_IS_OK(status) &&
1109 !NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))
1111 DBG_ERR("string_replace_allocate failed\n");
1117 mapped_name = talloc_asprintf(talloc_tos(), ":%s", tmp);
1119 if (mapped_name == NULL) {
1124 stream_name = synthetic_smb_fname(talloc_tos(),
1125 smb_fname->base_name,
1129 TALLOC_FREE(mapped_name);
1130 if (stream_name == NULL) {
1131 DBG_ERR("synthetic_smb_fname failed\n");
1136 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1138 status = SMB_VFS_CREATE_FILE(
1139 handle->conn, /* conn */
1141 0, /* root_dir_fid */
1142 stream_name, /* fname */
1143 FILE_GENERIC_WRITE, /* access_mask */
1144 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
1145 FILE_OPEN_IF, /* create_disposition */
1146 0, /* create_options */
1147 0, /* file_attributes */
1148 INTERNAL_OPEN_ONLY, /* oplock_request */
1150 0, /* allocation_size */
1151 0, /* private_flags */
1156 NULL, NULL); /* create context */
1157 TALLOC_FREE(stream_name);
1158 if (!NT_STATUS_IS_OK(status)) {
1159 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1164 nwritten = SMB_VFS_PWRITE(fsp,
1165 map + e->adx_offset,
1168 if (nwritten == -1) {
1169 DBG_ERR("SMB_VFS_PWRITE failed\n");
1170 saved_errno = errno;
1171 close_file(NULL, fsp, ERROR_CLOSE);
1172 errno = saved_errno;
1177 status = close_file(NULL, fsp, NORMAL_CLOSE);
1178 if (!NT_STATUS_IS_OK(status)) {
1185 ad_setentrylen(ad, ADEID_FINDERI, ADEDLEN_FINDERI);
1189 DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name);
1193 len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
1194 if (len != AD_DATASZ_DOT_UND) {
1195 DBG_ERR("%s: bad size: %zd\n", smb_fname->base_name, len);
1200 ok = ad_convert_move_reso(handle, ad, smb_fname);
1205 *converted_xattr = true;
1209 rc = munmap(map, maplen);
1211 DBG_ERR("munmap failed: %s\n", strerror(errno));
1218 static bool ad_convert_finderinfo(vfs_handle_struct *handle,
1220 const struct smb_filename *smb_fname)
1225 struct smb_filename *stream_name = NULL;
1226 files_struct *fsp = NULL;
1230 int saved_errno = 0;
1233 cmp = memcmp(ad->ad_filler, AD_FILLER_TAG_OSX, ADEDLEN_FILLER);
1238 p_ad = ad_get_entry(ad, ADEID_FINDERI);
1243 ai = afpinfo_new(talloc_tos());
1248 memcpy(ai->afpi_FinderInfo, p_ad, ADEDLEN_FINDERI);
1250 aiblob = data_blob_talloc(talloc_tos(), NULL, AFP_INFO_SIZE);
1251 if (aiblob.data == NULL) {
1256 size = afpinfo_pack(ai, (char *)aiblob.data);
1258 if (size != AFP_INFO_SIZE) {
1262 stream_name = synthetic_smb_fname(talloc_tos(),
1263 smb_fname->base_name,
1267 if (stream_name == NULL) {
1268 data_blob_free(&aiblob);
1269 DBG_ERR("synthetic_smb_fname failed\n");
1273 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1275 status = SMB_VFS_CREATE_FILE(
1276 handle->conn, /* conn */
1278 0, /* root_dir_fid */
1279 stream_name, /* fname */
1280 FILE_GENERIC_WRITE, /* access_mask */
1281 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
1282 FILE_OPEN_IF, /* create_disposition */
1283 0, /* create_options */
1284 0, /* file_attributes */
1285 INTERNAL_OPEN_ONLY, /* oplock_request */
1287 0, /* allocation_size */
1288 0, /* private_flags */
1293 NULL, NULL); /* create context */
1294 TALLOC_FREE(stream_name);
1295 if (!NT_STATUS_IS_OK(status)) {
1296 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1300 nwritten = SMB_VFS_PWRITE(fsp,
1304 if (nwritten == -1) {
1305 DBG_ERR("SMB_VFS_PWRITE failed\n");
1306 saved_errno = errno;
1307 close_file(NULL, fsp, ERROR_CLOSE);
1308 errno = saved_errno;
1312 status = close_file(NULL, fsp, NORMAL_CLOSE);
1313 if (!NT_STATUS_IS_OK(status)) {
1321 static bool ad_convert_truncate(struct adouble *ad,
1322 const struct smb_filename *smb_fname)
1327 * FIXME: direct ftruncate(), but we don't have a fsp for the
1330 rc = ftruncate(ad->ad_fd, ADEDOFF_RFORK_DOT_UND +
1331 ad_getentrylen(ad, ADEID_RFORK));
1339 static bool ad_convert_blank_rfork(vfs_handle_struct *handle,
1343 struct fruit_config_data *config = NULL;
1344 uint8_t *map = MAP_FAILED;
1353 SMB_VFS_HANDLE_GET_DATA(handle, config,
1354 struct fruit_config_data, return false);
1356 if (!config->wipe_intentionally_left_blank_rfork) {
1360 if (ad_getentrylen(ad, ADEID_RFORK) != sizeof(empty_resourcefork)) {
1364 maplen = ad_getentryoff(ad, ADEID_RFORK) +
1365 ad_getentrylen(ad, ADEID_RFORK);
1367 /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
1368 map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
1370 if (map == MAP_FAILED) {
1371 DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
1375 cmp = memcmp(map + ADEDOFF_RFORK_DOT_UND,
1377 sizeof(empty_resourcefork));
1378 rc = munmap(map, maplen);
1380 DBG_ERR("munmap failed: %s\n", strerror(errno));
1388 ad_setentrylen(ad, ADEID_RFORK, 0);
1395 len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
1396 if (len != AD_DATASZ_DOT_UND) {
1404 static bool ad_convert_delete_adfile(vfs_handle_struct *handle,
1406 const struct smb_filename *smb_fname)
1408 struct fruit_config_data *config = NULL;
1409 struct smb_filename *ad_name = NULL;
1412 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
1416 SMB_VFS_HANDLE_GET_DATA(handle, config,
1417 struct fruit_config_data, return false);
1419 if (!config->delete_empty_adfiles) {
1423 rc = adouble_path(talloc_tos(), smb_fname, &ad_name);
1428 rc = SMB_VFS_NEXT_UNLINK(handle, ad_name);
1430 DBG_ERR("Unlinking [%s] failed: %s\n",
1431 smb_fname_str_dbg(ad_name), strerror(errno));
1432 TALLOC_FREE(ad_name);
1436 DBG_WARNING("Unlinked [%s] after conversion\n", smb_fname_str_dbg(ad_name));
1437 TALLOC_FREE(ad_name);
1443 * Convert from Apple's ._ file to Netatalk
1445 * Apple's AppleDouble may contain a FinderInfo entry longer then 32
1446 * bytes containing packed xattrs.
1448 * @return -1 in case an error occurred, 0 if no conversion was done, 1
1451 static int ad_convert(struct vfs_handle_struct *handle,
1452 const struct smb_filename *smb_fname)
1454 struct adouble *ad = NULL;
1456 bool converted_xattr = false;
1460 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
1465 ok = ad_convert_xattr(handle, ad, smb_fname, &converted_xattr);
1471 ok = ad_convert_blank_rfork(handle, ad, &blank);
1477 if (converted_xattr || blank) {
1478 ok = ad_convert_truncate(ad, smb_fname);
1485 ok = ad_convert_finderinfo(handle, ad, smb_fname);
1487 DBG_ERR("Failed to convert [%s]\n",
1488 smb_fname_str_dbg(smb_fname));
1493 ok = ad_convert_delete_adfile(handle, ad, smb_fname);
1506 * Read and parse Netatalk AppleDouble metadata xattr
1508 static ssize_t ad_read_meta(vfs_handle_struct *handle,
1510 const struct smb_filename *smb_fname)
1516 DEBUG(10, ("reading meta xattr for %s\n", smb_fname->base_name));
1518 ealen = SMB_VFS_GETXATTR(handle->conn, smb_fname,
1519 AFPINFO_EA_NETATALK, ad->ad_data,
1525 if (errno == ENOATTR) {
1531 DEBUG(2, ("error reading meta xattr: %s\n",
1537 if (ealen != AD_DATASZ_XATTR) {
1538 DEBUG(2, ("bad size %zd\n", ealen));
1544 /* Now parse entries */
1545 ok = ad_unpack(ad, ADEID_NUM_XATTR, AD_DATASZ_XATTR);
1547 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1553 if (!ad_getentryoff(ad, ADEID_FINDERI)
1554 || !ad_getentryoff(ad, ADEID_COMMENT)
1555 || !ad_getentryoff(ad, ADEID_FILEDATESI)
1556 || !ad_getentryoff(ad, ADEID_AFPFILEI)
1557 || !ad_getentryoff(ad, ADEID_PRIVDEV)
1558 || !ad_getentryoff(ad, ADEID_PRIVINO)
1559 || !ad_getentryoff(ad, ADEID_PRIVSYN)
1560 || !ad_getentryoff(ad, ADEID_PRIVID)) {
1561 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1568 DEBUG(10, ("reading meta xattr for %s, rc: %d\n",
1569 smb_fname->base_name, rc));
1573 if (errno == EINVAL) {
1575 (void)SMB_VFS_REMOVEXATTR(handle->conn,
1577 AFPINFO_EA_NETATALK);
1585 static int ad_open_rsrc_adouble(const struct smb_filename *smb_fname,
1591 struct smb_filename *adp_smb_fname = NULL;
1593 ret = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
1598 fd = open(adp_smb_fname->base_name, flags, mode);
1599 TALLOC_FREE(adp_smb_fname);
1604 static int ad_open_rsrc(vfs_handle_struct *handle,
1605 const struct smb_filename *smb_fname,
1609 return ad_open_rsrc_adouble(smb_fname, flags, mode);
1613 * Here's the deal: for ADOUBLE_META we can do without an fd as we can issue
1614 * path based xattr calls. For ADOUBLE_RSRC however we need a full-fledged fd
1615 * for file IO on the ._ file.
1617 static int ad_open(vfs_handle_struct *handle,
1620 const struct smb_filename *smb_fname,
1626 DBG_DEBUG("Path [%s] type [%s]\n", smb_fname->base_name,
1627 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
1629 if (ad->ad_type == ADOUBLE_META) {
1633 if ((fsp != NULL) && (fsp->fh != NULL) && (fsp->fh->fd != -1)) {
1634 ad->ad_fd = fsp->fh->fd;
1635 ad->ad_opened = false;
1639 fd = ad_open_rsrc(handle, smb_fname, flags, mode);
1643 ad->ad_opened = true;
1646 DBG_DEBUG("Path [%s] type [%s] fd [%d]\n",
1647 smb_fname->base_name,
1648 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc", fd);
1653 static ssize_t ad_read_rsrc_adouble(vfs_handle_struct *handle,
1655 const struct smb_filename *smb_fname)
1657 SMB_STRUCT_STAT sbuf;
1664 ret = sys_fstat(ad->ad_fd, &sbuf, lp_fake_directory_create_times(
1665 SNUM(handle->conn)));
1671 * AppleDouble file header content and size, two cases:
1673 * - without xattrs it is exactly AD_DATASZ_DOT_UND (82) bytes large
1674 * - with embedded xattrs it can be larger, up to AD_XATTR_MAX_HDR_SIZE
1676 * Read as much as we can up to AD_XATTR_MAX_HDR_SIZE.
1678 size = sbuf.st_ex_size;
1679 if (size > talloc_array_length(ad->ad_data)) {
1680 if (size > AD_XATTR_MAX_HDR_SIZE) {
1681 size = AD_XATTR_MAX_HDR_SIZE;
1683 p_ad = talloc_realloc(ad, ad->ad_data, char, size);
1690 len = sys_pread(ad->ad_fd, ad->ad_data,
1691 talloc_array_length(ad->ad_data), 0);
1692 if (len != talloc_array_length(ad->ad_data)) {
1693 DBG_NOTICE("%s %s: bad size: %zd\n",
1694 smb_fname->base_name, strerror(errno), len);
1698 /* Now parse entries */
1699 ok = ad_unpack(ad, ADEID_NUM_DOT_UND, sbuf.st_ex_size);
1701 DBG_ERR("invalid AppleDouble resource %s\n",
1702 smb_fname->base_name);
1707 if ((ad_getentryoff(ad, ADEID_FINDERI) != ADEDOFF_FINDERI_DOT_UND)
1708 || (ad_getentrylen(ad, ADEID_FINDERI) < ADEDLEN_FINDERI)
1709 || (ad_getentryoff(ad, ADEID_RFORK) < ADEDOFF_RFORK_DOT_UND)) {
1710 DBG_ERR("invalid AppleDouble resource %s\n",
1711 smb_fname->base_name);
1720 * Read and parse resource fork, either ._ AppleDouble file or xattr
1722 static ssize_t ad_read_rsrc(vfs_handle_struct *handle,
1724 const struct smb_filename *smb_fname)
1726 return ad_read_rsrc_adouble(handle, ad, smb_fname);
1730 * Read and unpack an AppleDouble metadata xattr or resource
1732 static ssize_t ad_read(vfs_handle_struct *handle,
1734 const struct smb_filename *smb_fname)
1736 switch (ad->ad_type) {
1738 return ad_read_meta(handle, ad, smb_fname);
1740 return ad_read_rsrc(handle, ad, smb_fname);
1746 static int adouble_destructor(struct adouble *ad)
1748 if ((ad->ad_fd != -1) && ad->ad_opened) {
1756 * Allocate a struct adouble without initialiing it
1758 * The struct is either hang of the fsp extension context or if fsp is
1761 * @param[in] ctx talloc context
1762 * @param[in] handle vfs handle
1763 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1765 * @return adouble handle
1767 static struct adouble *ad_alloc(TALLOC_CTX *ctx,
1768 adouble_type_t type)
1776 adsize = AD_DATASZ_XATTR;
1779 adsize = AD_DATASZ_DOT_UND;
1785 ad = talloc_zero(ctx, struct adouble);
1792 ad->ad_data = talloc_zero_array(ad, char, adsize);
1793 if (ad->ad_data == NULL) {
1800 ad->ad_magic = AD_MAGIC;
1801 ad->ad_version = AD_VERSION;
1804 talloc_set_destructor(ad, adouble_destructor);
1814 * Allocate and initialize a new struct adouble
1816 * @param[in] ctx talloc context
1817 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1819 * @return adouble handle, initialized
1821 static struct adouble *ad_init(TALLOC_CTX *ctx,
1822 adouble_type_t type)
1825 const struct ad_entry_order *eid;
1826 struct adouble *ad = NULL;
1827 time_t t = time(NULL);
1831 eid = entry_order_meta_xattr;
1834 eid = entry_order_dot_und;
1840 ad = ad_alloc(ctx, type);
1846 ad->ad_eid[eid->id].ade_off = eid->offset;
1847 ad->ad_eid[eid->id].ade_len = eid->len;
1851 /* put something sane in the date fields */
1852 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, t);
1853 ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, t);
1854 ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, t);
1855 ad_setdate(ad, AD_DATE_BACKUP, htonl(AD_DATE_START));
1863 static struct adouble *ad_get_internal(TALLOC_CTX *ctx,
1864 vfs_handle_struct *handle,
1866 const struct smb_filename *smb_fname,
1867 adouble_type_t type)
1871 struct adouble *ad = NULL;
1875 smb_fname = fsp->base_fsp->fsp_name;
1878 DEBUG(10, ("ad_get(%s) called for %s\n",
1879 type == ADOUBLE_META ? "meta" : "rsrc",
1880 smb_fname->base_name));
1882 ad = ad_alloc(ctx, type);
1888 /* Try rw first so we can use the fd in ad_convert() */
1891 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
1892 if (rc == -1 && ((errno == EROFS) || (errno == EACCES))) {
1894 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
1897 DBG_DEBUG("ad_open [%s] error [%s]\n",
1898 smb_fname->base_name, strerror(errno));
1903 len = ad_read(handle, ad, smb_fname);
1905 DEBUG(10, ("error reading AppleDouble for %s\n",
1906 smb_fname->base_name));
1912 DEBUG(10, ("ad_get(%s) for %s returning %d\n",
1913 type == ADOUBLE_META ? "meta" : "rsrc",
1914 smb_fname->base_name, rc));
1923 * Return AppleDouble data for a file
1925 * @param[in] ctx talloc context
1926 * @param[in] handle vfs handle
1927 * @param[in] smb_fname pathname to file or directory
1928 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1930 * @return talloced struct adouble or NULL on error
1932 static struct adouble *ad_get(TALLOC_CTX *ctx,
1933 vfs_handle_struct *handle,
1934 const struct smb_filename *smb_fname,
1935 adouble_type_t type)
1937 return ad_get_internal(ctx, handle, NULL, smb_fname, type);
1941 * Return AppleDouble data for a file
1943 * @param[in] ctx talloc context
1944 * @param[in] handle vfs handle
1945 * @param[in] fsp fsp to use for IO
1946 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1948 * @return talloced struct adouble or NULL on error
1950 static struct adouble *ad_fget(TALLOC_CTX *ctx, vfs_handle_struct *handle,
1951 files_struct *fsp, adouble_type_t type)
1953 return ad_get_internal(ctx, handle, fsp, NULL, type);
1957 * Set AppleDouble metadata on a file or directory
1959 * @param[in] ad adouble handle
1961 * @param[in] smb_fname pathname to file or directory
1963 * @return status code, 0 means success
1965 static int ad_set(vfs_handle_struct *handle,
1967 const struct smb_filename *smb_fname)
1972 DBG_DEBUG("Path [%s]\n", smb_fname->base_name);
1974 if (ad->ad_type != ADOUBLE_META) {
1975 DBG_ERR("ad_set on [%s] used with ADOUBLE_RSRC\n",
1976 smb_fname->base_name);
1985 ret = SMB_VFS_SETXATTR(handle->conn,
1987 AFPINFO_EA_NETATALK,
1989 AD_DATASZ_XATTR, 0);
1991 DBG_DEBUG("Path [%s] ret [%d]\n", smb_fname->base_name, ret);
1997 * Set AppleDouble metadata on a file or directory
1999 * @param[in] ad adouble handle
2000 * @param[in] fsp file handle
2002 * @return status code, 0 means success
2004 static int ad_fset(struct vfs_handle_struct *handle,
2012 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
2015 || (fsp->fh == NULL)
2016 || (fsp->fh->fd == -1))
2018 smb_panic("bad fsp");
2026 switch (ad->ad_type) {
2028 rc = SMB_VFS_NEXT_SETXATTR(handle,
2030 AFPINFO_EA_NETATALK,
2032 AD_DATASZ_XATTR, 0);
2036 len = SMB_VFS_NEXT_PWRITE(handle,
2041 if (len != AD_DATASZ_DOT_UND) {
2042 DBG_ERR("short write on %s: %zd", fsp_str_dbg(fsp), len);
2052 DBG_DEBUG("Path [%s] rc [%d]\n", fsp_str_dbg(fsp), rc);
2057 /*****************************************************************************
2059 *****************************************************************************/
2061 static bool is_afpinfo_stream(const struct smb_filename *smb_fname)
2063 if (strncasecmp_m(smb_fname->stream_name,
2064 AFPINFO_STREAM_NAME,
2065 strlen(AFPINFO_STREAM_NAME)) == 0) {
2071 static bool is_afpresource_stream(const struct smb_filename *smb_fname)
2073 if (strncasecmp_m(smb_fname->stream_name,
2074 AFPRESOURCE_STREAM_NAME,
2075 strlen(AFPRESOURCE_STREAM_NAME)) == 0) {
2082 * Test whether stream is an Apple stream.
2084 static bool is_apple_stream(const struct smb_filename *smb_fname)
2086 if (is_afpinfo_stream(smb_fname)) {
2089 if (is_afpresource_stream(smb_fname)) {
2095 static bool is_adouble_file(const char *path)
2097 const char *p = NULL;
2100 p = strrchr(path, '/');
2108 ADOUBLE_NAME_PREFIX,
2109 strlen(ADOUBLE_NAME_PREFIX));
2117 * Initialize config struct from our smb.conf config parameters
2119 static int init_fruit_config(vfs_handle_struct *handle)
2121 struct fruit_config_data *config;
2123 const char *tm_size_str = NULL;
2125 config = talloc_zero(handle->conn, struct fruit_config_data);
2127 DEBUG(1, ("talloc_zero() failed\n"));
2133 * Versions up to Samba 4.5.x had a spelling bug in the
2134 * fruit:resource option calling lp_parm_enum with
2135 * "res*s*ource" (ie two s).
2137 * In Samba 4.6 we accept both the wrong and the correct
2138 * spelling, in Samba 4.7 the bad spelling will be removed.
2140 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2141 "ressource", fruit_rsrc, FRUIT_RSRC_ADFILE);
2142 if (enumval == -1) {
2143 DEBUG(1, ("value for %s: resource type unknown\n",
2144 FRUIT_PARAM_TYPE_NAME));
2147 config->rsrc = (enum fruit_rsrc)enumval;
2149 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2150 "resource", fruit_rsrc, enumval);
2151 if (enumval == -1) {
2152 DEBUG(1, ("value for %s: resource type unknown\n",
2153 FRUIT_PARAM_TYPE_NAME));
2156 config->rsrc = (enum fruit_rsrc)enumval;
2158 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2159 "metadata", fruit_meta, FRUIT_META_NETATALK);
2160 if (enumval == -1) {
2161 DEBUG(1, ("value for %s: metadata type unknown\n",
2162 FRUIT_PARAM_TYPE_NAME));
2165 config->meta = (enum fruit_meta)enumval;
2167 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2168 "locking", fruit_locking, FRUIT_LOCKING_NONE);
2169 if (enumval == -1) {
2170 DEBUG(1, ("value for %s: locking type unknown\n",
2171 FRUIT_PARAM_TYPE_NAME));
2174 config->locking = (enum fruit_locking)enumval;
2176 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2177 "encoding", fruit_encoding, FRUIT_ENC_PRIVATE);
2178 if (enumval == -1) {
2179 DEBUG(1, ("value for %s: encoding type unknown\n",
2180 FRUIT_PARAM_TYPE_NAME));
2183 config->encoding = (enum fruit_encoding)enumval;
2185 if (config->rsrc == FRUIT_RSRC_ADFILE) {
2186 config->veto_appledouble = lp_parm_bool(SNUM(handle->conn),
2187 FRUIT_PARAM_TYPE_NAME,
2192 config->use_aapl = lp_parm_bool(
2193 -1, FRUIT_PARAM_TYPE_NAME, "aapl", true);
2195 config->time_machine = lp_parm_bool(
2196 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "time machine", false);
2198 config->unix_info_enabled = lp_parm_bool(
2199 -1, FRUIT_PARAM_TYPE_NAME, "nfs_aces", true);
2201 config->use_copyfile = lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME,
2204 config->posix_rename = lp_parm_bool(
2205 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "posix_rename", true);
2207 config->aapl_zero_file_id =
2208 lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME, "zero_file_id", true);
2210 config->readdir_attr_rsize = lp_parm_bool(
2211 SNUM(handle->conn), "readdir_attr", "aapl_rsize", true);
2213 config->readdir_attr_finder_info = lp_parm_bool(
2214 SNUM(handle->conn), "readdir_attr", "aapl_finder_info", true);
2216 config->readdir_attr_max_access = lp_parm_bool(
2217 SNUM(handle->conn), "readdir_attr", "aapl_max_access", true);
2219 config->model = lp_parm_const_string(
2220 -1, FRUIT_PARAM_TYPE_NAME, "model", "MacSamba");
2222 tm_size_str = lp_parm_const_string(
2223 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2224 "time machine max size", NULL);
2225 if (tm_size_str != NULL) {
2226 config->time_machine_max_size = conv_str_size(tm_size_str);
2229 config->wipe_intentionally_left_blank_rfork = lp_parm_bool(
2230 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2231 "wipe_intentionally_left_blank_rfork", false);
2233 config->delete_empty_adfiles = lp_parm_bool(
2234 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2235 "delete_empty_adfiles", false);
2237 SMB_VFS_HANDLE_SET_DATA(handle, config,
2238 NULL, struct fruit_config_data,
2245 * Prepend "._" to a basename
2246 * Return a new struct smb_filename with stream_name == NULL.
2248 static int adouble_path(TALLOC_CTX *ctx,
2249 const struct smb_filename *smb_fname_in,
2250 struct smb_filename **pp_smb_fname_out)
2254 struct smb_filename *smb_fname = cp_smb_filename(ctx,
2257 if (smb_fname == NULL) {
2261 /* We need streamname to be NULL */
2262 TALLOC_FREE(smb_fname->stream_name);
2264 /* And we're replacing base_name. */
2265 TALLOC_FREE(smb_fname->base_name);
2267 if (!parent_dirname(smb_fname, smb_fname_in->base_name,
2269 TALLOC_FREE(smb_fname);
2273 smb_fname->base_name = talloc_asprintf(smb_fname,
2274 "%s/._%s", parent, base);
2275 if (smb_fname->base_name == NULL) {
2276 TALLOC_FREE(smb_fname);
2280 *pp_smb_fname_out = smb_fname;
2286 * Allocate and initialize an AfpInfo struct
2288 static AfpInfo *afpinfo_new(TALLOC_CTX *ctx)
2290 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2294 ai->afpi_Signature = AFP_Signature;
2295 ai->afpi_Version = AFP_Version;
2296 ai->afpi_BackupTime = AD_DATE_START;
2301 * Pack an AfpInfo struct into a buffer
2303 * Buffer size must be at least AFP_INFO_SIZE
2304 * Returns size of packed buffer
2306 static ssize_t afpinfo_pack(const AfpInfo *ai, char *buf)
2308 memset(buf, 0, AFP_INFO_SIZE);
2310 RSIVAL(buf, 0, ai->afpi_Signature);
2311 RSIVAL(buf, 4, ai->afpi_Version);
2312 RSIVAL(buf, 12, ai->afpi_BackupTime);
2313 memcpy(buf + 16, ai->afpi_FinderInfo, sizeof(ai->afpi_FinderInfo));
2315 return AFP_INFO_SIZE;
2319 * Unpack a buffer into a AfpInfo structure
2321 * Buffer size must be at least AFP_INFO_SIZE
2322 * Returns allocated AfpInfo struct
2324 static AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data)
2326 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2331 ai->afpi_Signature = RIVAL(data, 0);
2332 ai->afpi_Version = RIVAL(data, 4);
2333 ai->afpi_BackupTime = RIVAL(data, 12);
2334 memcpy(ai->afpi_FinderInfo, (const char *)data + 16,
2335 sizeof(ai->afpi_FinderInfo));
2337 if (ai->afpi_Signature != AFP_Signature
2338 || ai->afpi_Version != AFP_Version) {
2339 DEBUG(1, ("Bad AfpInfo signature or version\n"));
2347 * Fake an inode number from the md5 hash of the (xattr) name
2349 static SMB_INO_T fruit_inode(const SMB_STRUCT_STAT *sbuf, const char *sname)
2351 gnutls_hash_hd_t hash_hnd = NULL;
2352 unsigned char hash[16];
2353 SMB_INO_T result = 0;
2357 DBG_DEBUG("fruit_inode called for %ju/%ju [%s]\n",
2358 (uintmax_t)sbuf->st_ex_dev,
2359 (uintmax_t)sbuf->st_ex_ino, sname);
2361 upper_sname = talloc_strdup_upper(talloc_tos(), sname);
2362 SMB_ASSERT(upper_sname != NULL);
2364 rc = gnutls_hash_init(&hash_hnd, GNUTLS_DIG_MD5);
2369 rc = gnutls_hash(hash_hnd, &(sbuf->st_ex_dev), sizeof(sbuf->st_ex_dev));
2371 gnutls_hash_deinit(hash_hnd, NULL);
2374 rc = gnutls_hash(hash_hnd,
2376 sizeof(sbuf->st_ex_ino));
2378 gnutls_hash_deinit(hash_hnd, NULL);
2381 rc = gnutls_hash(hash_hnd,
2383 talloc_get_size(upper_sname) - 1);
2385 gnutls_hash_deinit(hash_hnd, NULL);
2389 gnutls_hash_deinit(hash_hnd, hash);
2391 /* Hopefully all the variation is in the lower 4 (or 8) bytes! */
2392 memcpy(&result, hash, sizeof(result));
2395 DBG_DEBUG("fruit_inode \"%s\": ino=%ju\n",
2396 sname, (uintmax_t)result);
2399 TALLOC_FREE(upper_sname);
2404 static bool add_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
2405 struct stream_struct **streams,
2406 const char *name, off_t size,
2409 struct stream_struct *tmp;
2411 tmp = talloc_realloc(mem_ctx, *streams, struct stream_struct,
2417 tmp[*num_streams].name = talloc_asprintf(tmp, "%s:$DATA", name);
2418 if (tmp[*num_streams].name == NULL) {
2422 tmp[*num_streams].size = size;
2423 tmp[*num_streams].alloc_size = alloc_size;
2430 static bool filter_empty_rsrc_stream(unsigned int *num_streams,
2431 struct stream_struct **streams)
2433 struct stream_struct *tmp = *streams;
2436 if (*num_streams == 0) {
2440 for (i = 0; i < *num_streams; i++) {
2441 if (strequal_m(tmp[i].name, AFPRESOURCE_STREAM)) {
2446 if (i == *num_streams) {
2450 if (tmp[i].size > 0) {
2454 TALLOC_FREE(tmp[i].name);
2455 if (*num_streams - 1 > i) {
2456 memmove(&tmp[i], &tmp[i+1],
2457 (*num_streams - i - 1) * sizeof(struct stream_struct));
2464 static bool del_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
2465 struct stream_struct **streams,
2468 struct stream_struct *tmp = *streams;
2471 if (*num_streams == 0) {
2475 for (i = 0; i < *num_streams; i++) {
2476 if (strequal_m(tmp[i].name, name)) {
2481 if (i == *num_streams) {
2485 TALLOC_FREE(tmp[i].name);
2486 if (*num_streams - 1 > i) {
2487 memmove(&tmp[i], &tmp[i+1],
2488 (*num_streams - i - 1) * sizeof(struct stream_struct));
2495 static bool ad_empty_finderinfo(const struct adouble *ad)
2498 char emptybuf[ADEDLEN_FINDERI] = {0};
2501 fi = ad_get_entry(ad, ADEID_FINDERI);
2503 DBG_ERR("Missing FinderInfo in struct adouble [%p]\n", ad);
2507 cmp = memcmp(emptybuf, fi, ADEDLEN_FINDERI);
2511 static bool ai_empty_finderinfo(const AfpInfo *ai)
2514 char emptybuf[ADEDLEN_FINDERI] = {0};
2516 cmp = memcmp(emptybuf, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
2521 * Update btime with btime from Netatalk
2523 static void update_btime(vfs_handle_struct *handle,
2524 struct smb_filename *smb_fname)
2527 struct timespec creation_time = {0};
2529 struct fruit_config_data *config = NULL;
2531 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
2534 switch (config->meta) {
2535 case FRUIT_META_STREAM:
2537 case FRUIT_META_NETATALK:
2541 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
2545 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
2549 if (ad_getdate(ad, AD_DATE_UNIX | AD_DATE_CREATE, &t) != 0) {
2555 creation_time.tv_sec = convert_uint32_t_to_time_t(t);
2556 update_stat_ex_create_time(&smb_fname->st, creation_time);
2562 * Map an access mask to a Netatalk single byte byte range lock
2564 static off_t access_to_netatalk_brl(enum apple_fork fork_type,
2565 uint32_t access_mask)
2569 switch (access_mask) {
2570 case FILE_READ_DATA:
2571 offset = AD_FILELOCK_OPEN_RD;
2574 case FILE_WRITE_DATA:
2575 case FILE_APPEND_DATA:
2576 offset = AD_FILELOCK_OPEN_WR;
2580 offset = AD_FILELOCK_OPEN_NONE;
2584 if (fork_type == APPLE_FORK_RSRC) {
2585 if (offset == AD_FILELOCK_OPEN_NONE) {
2586 offset = AD_FILELOCK_RSRC_OPEN_NONE;
2596 * Map a deny mode to a Netatalk brl
2598 static off_t denymode_to_netatalk_brl(enum apple_fork fork_type,
2603 switch (deny_mode) {
2605 offset = AD_FILELOCK_DENY_RD;
2609 offset = AD_FILELOCK_DENY_WR;
2613 smb_panic("denymode_to_netatalk_brl: bad deny mode\n");
2616 if (fork_type == APPLE_FORK_RSRC) {
2624 * Call fcntl() with an exclusive F_GETLK request in order to
2625 * determine if there's an exisiting shared lock
2627 * @return true if the requested lock was found or any error occurred
2628 * false if the lock was not found
2630 static bool test_netatalk_lock(files_struct *fsp, off_t in_offset)
2633 off_t offset = in_offset;
2638 result = SMB_VFS_GETLOCK(fsp, &offset, &len, &type, &pid);
2639 if (result == false) {
2643 if (type != F_UNLCK) {
2650 static NTSTATUS fruit_check_access(vfs_handle_struct *handle,
2652 uint32_t access_mask,
2653 uint32_t share_mode)
2655 NTSTATUS status = NT_STATUS_OK;
2657 bool share_for_read = (share_mode & FILE_SHARE_READ);
2658 bool share_for_write = (share_mode & FILE_SHARE_WRITE);
2659 bool netatalk_already_open_for_reading = false;
2660 bool netatalk_already_open_for_writing = false;
2661 bool netatalk_already_open_with_deny_read = false;
2662 bool netatalk_already_open_with_deny_write = false;
2664 /* FIXME: hardcoded data fork, add resource fork */
2665 enum apple_fork fork_type = APPLE_FORK_DATA;
2667 DBG_DEBUG("fruit_check_access: %s, am: %s/%s, sm: 0x%x\n",
2669 access_mask & FILE_READ_DATA ? "READ" :"-",
2670 access_mask & FILE_WRITE_DATA ? "WRITE" : "-",
2673 if (fsp->fh->fd == -1) {
2674 return NT_STATUS_OK;
2677 /* Read NetATalk opens and deny modes on the file. */
2678 netatalk_already_open_for_reading = test_netatalk_lock(fsp,
2679 access_to_netatalk_brl(fork_type,
2682 netatalk_already_open_with_deny_read = test_netatalk_lock(fsp,
2683 denymode_to_netatalk_brl(fork_type,
2686 netatalk_already_open_for_writing = test_netatalk_lock(fsp,
2687 access_to_netatalk_brl(fork_type,
2690 netatalk_already_open_with_deny_write = test_netatalk_lock(fsp,
2691 denymode_to_netatalk_brl(fork_type,
2694 /* If there are any conflicts - sharing violation. */
2695 if ((access_mask & FILE_READ_DATA) &&
2696 netatalk_already_open_with_deny_read) {
2697 return NT_STATUS_SHARING_VIOLATION;
2700 if (!share_for_read &&
2701 netatalk_already_open_for_reading) {
2702 return NT_STATUS_SHARING_VIOLATION;
2705 if ((access_mask & FILE_WRITE_DATA) &&
2706 netatalk_already_open_with_deny_write) {
2707 return NT_STATUS_SHARING_VIOLATION;
2710 if (!share_for_write &&
2711 netatalk_already_open_for_writing) {
2712 return NT_STATUS_SHARING_VIOLATION;
2715 if (!(access_mask & FILE_READ_DATA)) {
2717 * Nothing we can do here, we need read access
2720 return NT_STATUS_OK;
2723 /* Set NetAtalk locks matching our access */
2724 if (access_mask & FILE_READ_DATA) {
2725 struct byte_range_lock *br_lck = NULL;
2727 off = access_to_netatalk_brl(fork_type, FILE_READ_DATA);
2729 handle->conn->sconn->msg_ctx, fsp,
2730 fsp->op->global->open_persistent_id, 1, off,
2731 READ_LOCK, POSIX_LOCK, false,
2734 TALLOC_FREE(br_lck);
2736 if (!NT_STATUS_IS_OK(status)) {
2741 if (!share_for_read) {
2742 struct byte_range_lock *br_lck = NULL;
2744 off = denymode_to_netatalk_brl(fork_type, DENY_READ);
2746 handle->conn->sconn->msg_ctx, fsp,
2747 fsp->op->global->open_persistent_id, 1, off,
2748 READ_LOCK, POSIX_LOCK, false,
2751 TALLOC_FREE(br_lck);
2753 if (!NT_STATUS_IS_OK(status)) {
2758 if (access_mask & FILE_WRITE_DATA) {
2759 struct byte_range_lock *br_lck = NULL;
2761 off = access_to_netatalk_brl(fork_type, FILE_WRITE_DATA);
2763 handle->conn->sconn->msg_ctx, fsp,
2764 fsp->op->global->open_persistent_id, 1, off,
2765 READ_LOCK, POSIX_LOCK, false,
2768 TALLOC_FREE(br_lck);
2770 if (!NT_STATUS_IS_OK(status)) {
2775 if (!share_for_write) {
2776 struct byte_range_lock *br_lck = NULL;
2778 off = denymode_to_netatalk_brl(fork_type, DENY_WRITE);
2780 handle->conn->sconn->msg_ctx, fsp,
2781 fsp->op->global->open_persistent_id, 1, off,
2782 READ_LOCK, POSIX_LOCK, false,
2785 TALLOC_FREE(br_lck);
2787 if (!NT_STATUS_IS_OK(status)) {
2792 return NT_STATUS_OK;
2795 static NTSTATUS check_aapl(vfs_handle_struct *handle,
2796 struct smb_request *req,
2797 const struct smb2_create_blobs *in_context_blobs,
2798 struct smb2_create_blobs *out_context_blobs)
2800 struct fruit_config_data *config;
2802 struct smb2_create_blob *aapl = NULL;
2806 DATA_BLOB blob = data_blob_talloc(req, NULL, 0);
2807 uint64_t req_bitmap, client_caps;
2808 uint64_t server_caps = SMB2_CRTCTX_AAPL_UNIX_BASED;
2812 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
2813 return NT_STATUS_UNSUCCESSFUL);
2815 if (!config->use_aapl
2816 || in_context_blobs == NULL
2817 || out_context_blobs == NULL) {
2818 return NT_STATUS_OK;
2821 aapl = smb2_create_blob_find(in_context_blobs,
2822 SMB2_CREATE_TAG_AAPL);
2824 return NT_STATUS_OK;
2827 if (aapl->data.length != 24) {
2828 DEBUG(1, ("unexpected AAPL ctxt length: %ju\n",
2829 (uintmax_t)aapl->data.length));
2830 return NT_STATUS_INVALID_PARAMETER;
2833 cmd = IVAL(aapl->data.data, 0);
2834 if (cmd != SMB2_CRTCTX_AAPL_SERVER_QUERY) {
2835 DEBUG(1, ("unsupported AAPL cmd: %d\n", cmd));
2836 return NT_STATUS_INVALID_PARAMETER;
2839 req_bitmap = BVAL(aapl->data.data, 8);
2840 client_caps = BVAL(aapl->data.data, 16);
2842 SIVAL(p, 0, SMB2_CRTCTX_AAPL_SERVER_QUERY);
2844 SBVAL(p, 8, req_bitmap);
2845 ok = data_blob_append(req, &blob, p, 16);
2847 return NT_STATUS_UNSUCCESSFUL;
2850 if (req_bitmap & SMB2_CRTCTX_AAPL_SERVER_CAPS) {
2851 if ((client_caps & SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR) &&
2852 (handle->conn->tcon->compat->fs_capabilities & FILE_NAMED_STREAMS)) {
2853 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR;
2854 config->readdir_attr_enabled = true;
2857 if (config->use_copyfile) {
2858 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_OSX_COPYFILE;
2859 config->copyfile_enabled = true;
2863 * The client doesn't set the flag, so we can't check
2864 * for it and just set it unconditionally
2866 if (config->unix_info_enabled) {
2867 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_NFS_ACE;
2870 SBVAL(p, 0, server_caps);
2871 ok = data_blob_append(req, &blob, p, 8);
2873 return NT_STATUS_UNSUCCESSFUL;
2877 if (req_bitmap & SMB2_CRTCTX_AAPL_VOLUME_CAPS) {
2878 int val = lp_case_sensitive(SNUM(handle->conn->tcon->compat));
2886 caps |= SMB2_CRTCTX_AAPL_CASE_SENSITIVE;
2893 if (config->time_machine) {
2894 caps |= SMB2_CRTCTX_AAPL_FULL_SYNC;
2899 ok = data_blob_append(req, &blob, p, 8);
2901 return NT_STATUS_UNSUCCESSFUL;
2905 if (req_bitmap & SMB2_CRTCTX_AAPL_MODEL_INFO) {
2906 ok = convert_string_talloc(req,
2907 CH_UNIX, CH_UTF16LE,
2908 config->model, strlen(config->model),
2911 return NT_STATUS_UNSUCCESSFUL;
2915 SIVAL(p + 4, 0, modellen);
2916 ok = data_blob_append(req, &blob, p, 8);
2919 return NT_STATUS_UNSUCCESSFUL;
2922 ok = data_blob_append(req, &blob, model, modellen);
2925 return NT_STATUS_UNSUCCESSFUL;
2929 status = smb2_create_blob_add(out_context_blobs,
2931 SMB2_CREATE_TAG_AAPL,
2933 if (NT_STATUS_IS_OK(status)) {
2934 global_fruit_config.nego_aapl = true;
2935 if (config->aapl_zero_file_id) {
2936 aapl_force_zero_file_id(handle->conn->sconn);
2943 static bool readdir_attr_meta_finderi_stream(
2944 struct vfs_handle_struct *handle,
2945 const struct smb_filename *smb_fname,
2948 struct smb_filename *stream_name = NULL;
2949 files_struct *fsp = NULL;
2954 uint8_t buf[AFP_INFO_SIZE];
2956 stream_name = synthetic_smb_fname(talloc_tos(),
2957 smb_fname->base_name,
2958 AFPINFO_STREAM_NAME,
2959 NULL, smb_fname->flags);
2960 if (stream_name == NULL) {
2964 ret = SMB_VFS_STAT(handle->conn, stream_name);
2969 status = SMB_VFS_CREATE_FILE(
2970 handle->conn, /* conn */
2972 0, /* root_dir_fid */
2973 stream_name, /* fname */
2974 FILE_READ_DATA, /* access_mask */
2975 (FILE_SHARE_READ | FILE_SHARE_WRITE | /* share_access */
2977 FILE_OPEN, /* create_disposition*/
2978 0, /* create_options */
2979 0, /* file_attributes */
2980 INTERNAL_OPEN_ONLY, /* oplock_request */
2982 0, /* allocation_size */
2983 0, /* private_flags */
2988 NULL, NULL); /* create context */
2990 TALLOC_FREE(stream_name);
2992 if (!NT_STATUS_IS_OK(status)) {
2996 nread = SMB_VFS_PREAD(fsp, &buf[0], AFP_INFO_SIZE, 0);
2997 if (nread != AFP_INFO_SIZE) {
2998 DBG_ERR("short read [%s] [%zd/%d]\n",
2999 smb_fname_str_dbg(stream_name), nread, AFP_INFO_SIZE);
3004 memcpy(&ai->afpi_FinderInfo[0], &buf[AFP_OFF_FinderInfo],
3011 close_file(NULL, fsp, NORMAL_CLOSE);
3017 static bool readdir_attr_meta_finderi_netatalk(
3018 struct vfs_handle_struct *handle,
3019 const struct smb_filename *smb_fname,
3022 struct adouble *ad = NULL;
3025 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
3030 p = ad_get_entry(ad, ADEID_FINDERI);
3032 DBG_ERR("No ADEID_FINDERI for [%s]\n", smb_fname->base_name);
3037 memcpy(&ai->afpi_FinderInfo[0], p, AFP_FinderSize);
3042 static bool readdir_attr_meta_finderi(struct vfs_handle_struct *handle,
3043 const struct smb_filename *smb_fname,
3044 struct readdir_attr_data *attr_data)
3046 struct fruit_config_data *config = NULL;
3047 uint32_t date_added;
3051 SMB_VFS_HANDLE_GET_DATA(handle, config,
3052 struct fruit_config_data,
3055 switch (config->meta) {
3056 case FRUIT_META_NETATALK:
3057 ok = readdir_attr_meta_finderi_netatalk(
3058 handle, smb_fname, &ai);
3061 case FRUIT_META_STREAM:
3062 ok = readdir_attr_meta_finderi_stream(
3063 handle, smb_fname, &ai);
3067 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3072 /* Don't bother with errors, it's likely ENOENT */
3076 if (S_ISREG(smb_fname->st.st_ex_mode)) {
3078 memcpy(&attr_data->attr_data.aapl.finder_info[0],
3079 &ai.afpi_FinderInfo[0], 4);
3081 /* finder_creator */
3082 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 4,
3083 &ai.afpi_FinderInfo[4], 4);
3087 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 8,
3088 &ai.afpi_FinderInfo[8], 2);
3090 /* finder_ext_flags */
3091 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 10,
3092 &ai.afpi_FinderInfo[24], 2);
3095 date_added = convert_time_t_to_uint32_t(
3096 smb_fname->st.st_ex_btime.tv_sec - AD_DATE_DELTA);
3098 RSIVAL(&attr_data->attr_data.aapl.finder_info[0], 12, date_added);
3103 static uint64_t readdir_attr_rfork_size_adouble(
3104 struct vfs_handle_struct *handle,
3105 const struct smb_filename *smb_fname)
3107 struct adouble *ad = NULL;
3108 uint64_t rfork_size;
3110 ad = ad_get(talloc_tos(), handle, smb_fname,
3116 rfork_size = ad_getentrylen(ad, ADEID_RFORK);
3122 static uint64_t readdir_attr_rfork_size_stream(
3123 struct vfs_handle_struct *handle,
3124 const struct smb_filename *smb_fname)
3126 struct smb_filename *stream_name = NULL;
3128 uint64_t rfork_size;
3130 stream_name = synthetic_smb_fname(talloc_tos(),
3131 smb_fname->base_name,
3132 AFPRESOURCE_STREAM_NAME,
3134 if (stream_name == NULL) {
3138 ret = SMB_VFS_STAT(handle->conn, stream_name);
3140 TALLOC_FREE(stream_name);
3144 rfork_size = stream_name->st.st_ex_size;
3145 TALLOC_FREE(stream_name);
3150 static uint64_t readdir_attr_rfork_size(struct vfs_handle_struct *handle,
3151 const struct smb_filename *smb_fname)
3153 struct fruit_config_data *config = NULL;
3154 uint64_t rfork_size;
3156 SMB_VFS_HANDLE_GET_DATA(handle, config,
3157 struct fruit_config_data,
3160 switch (config->rsrc) {
3161 case FRUIT_RSRC_ADFILE:
3162 rfork_size = readdir_attr_rfork_size_adouble(handle,
3166 case FRUIT_RSRC_XATTR:
3167 case FRUIT_RSRC_STREAM:
3168 rfork_size = readdir_attr_rfork_size_stream(handle,
3173 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3181 static NTSTATUS readdir_attr_macmeta(struct vfs_handle_struct *handle,
3182 const struct smb_filename *smb_fname,
3183 struct readdir_attr_data *attr_data)
3185 NTSTATUS status = NT_STATUS_OK;
3186 struct fruit_config_data *config = NULL;
3189 SMB_VFS_HANDLE_GET_DATA(handle, config,
3190 struct fruit_config_data,
3191 return NT_STATUS_UNSUCCESSFUL);
3194 /* Ensure we return a default value in the creation_date field */
3195 RSIVAL(&attr_data->attr_data.aapl.finder_info, 12, AD_DATE_START);
3198 * Resource fork length
3201 if (config->readdir_attr_rsize) {
3202 uint64_t rfork_size;
3204 rfork_size = readdir_attr_rfork_size(handle, smb_fname);
3205 attr_data->attr_data.aapl.rfork_size = rfork_size;
3212 if (config->readdir_attr_finder_info) {
3213 ok = readdir_attr_meta_finderi(handle, smb_fname, attr_data);
3215 status = NT_STATUS_INTERNAL_ERROR;
3222 static NTSTATUS remove_virtual_nfs_aces(struct security_descriptor *psd)
3227 if (psd->dacl == NULL) {
3228 return NT_STATUS_OK;
3231 for (i = 0; i < psd->dacl->num_aces; i++) {
3232 /* MS NFS style mode/uid/gid */
3233 int cmp = dom_sid_compare_domain(
3234 &global_sid_Unix_NFS,
3235 &psd->dacl->aces[i].trustee);
3237 /* Normal ACE entry. */
3242 * security_descriptor_dacl_del()
3243 * *must* return NT_STATUS_OK as we know
3244 * we have something to remove.
3247 status = security_descriptor_dacl_del(psd,
3248 &psd->dacl->aces[i].trustee);
3249 if (!NT_STATUS_IS_OK(status)) {
3250 DBG_WARNING("failed to remove MS NFS style ACE: %s\n",
3256 * security_descriptor_dacl_del() may delete more
3257 * then one entry subsequent to this one if the
3258 * SID matches, but we only need to ensure that
3259 * we stay looking at the same element in the array.
3263 return NT_STATUS_OK;
3266 /* Search MS NFS style ACE with UNIX mode */
3267 static NTSTATUS check_ms_nfs(vfs_handle_struct *handle,
3269 struct security_descriptor *psd,
3274 struct fruit_config_data *config = NULL;
3278 SMB_VFS_HANDLE_GET_DATA(handle, config,
3279 struct fruit_config_data,
3280 return NT_STATUS_UNSUCCESSFUL);
3282 if (!global_fruit_config.nego_aapl) {
3283 return NT_STATUS_OK;
3285 if (psd->dacl == NULL || !config->unix_info_enabled) {
3286 return NT_STATUS_OK;
3289 for (i = 0; i < psd->dacl->num_aces; i++) {
3290 if (dom_sid_compare_domain(
3291 &global_sid_Unix_NFS_Mode,
3292 &psd->dacl->aces[i].trustee) == 0) {
3293 *pmode = (mode_t)psd->dacl->aces[i].trustee.sub_auths[2];
3294 *pmode &= (S_IRWXU | S_IRWXG | S_IRWXO);
3297 DEBUG(10, ("MS NFS chmod request %s, %04o\n",
3298 fsp_str_dbg(fsp), (unsigned)(*pmode)));
3304 * Remove any incoming virtual ACE entries generated by
3305 * fruit_fget_nt_acl().
3308 return remove_virtual_nfs_aces(psd);
3311 /****************************************************************************
3313 ****************************************************************************/
3315 static int fruit_connect(vfs_handle_struct *handle,
3316 const char *service,
3320 char *list = NULL, *newlist = NULL;
3321 struct fruit_config_data *config;
3323 DEBUG(10, ("fruit_connect\n"));
3325 rc = SMB_VFS_NEXT_CONNECT(handle, service, user);
3330 rc = init_fruit_config(handle);
3335 SMB_VFS_HANDLE_GET_DATA(handle, config,
3336 struct fruit_config_data, return -1);
3338 if (config->veto_appledouble) {
3339 list = lp_veto_files(talloc_tos(), SNUM(handle->conn));
3342 if (strstr(list, "/" ADOUBLE_NAME_PREFIX "*/") == NULL) {
3343 newlist = talloc_asprintf(
3345 "%s/" ADOUBLE_NAME_PREFIX "*/",
3347 lp_do_parameter(SNUM(handle->conn),
3352 lp_do_parameter(SNUM(handle->conn),
3354 "/" ADOUBLE_NAME_PREFIX "*/");
3360 if (config->encoding == FRUIT_ENC_NATIVE) {
3361 lp_do_parameter(SNUM(handle->conn),
3366 if (config->time_machine) {
3367 DBG_NOTICE("Enabling durable handles for Time Machine "
3368 "support on [%s]\n", service);
3369 lp_do_parameter(SNUM(handle->conn), "durable handles", "yes");
3370 lp_do_parameter(SNUM(handle->conn), "kernel oplocks", "no");
3371 lp_do_parameter(SNUM(handle->conn), "kernel share modes", "no");
3372 if (!lp_strict_sync(SNUM(handle->conn))) {
3373 DBG_WARNING("Time Machine without strict sync is not "
3376 lp_do_parameter(SNUM(handle->conn), "posix locking", "no");
3382 static int fruit_fake_fd(void)
3389 * Return a valid fd, but ensure any attempt to use it returns
3390 * an error (EPIPE). Once we get a write on the handle, we open
3393 ret = pipe(pipe_fds);
3403 static int fruit_open_meta_stream(vfs_handle_struct *handle,
3404 struct smb_filename *smb_fname,
3409 struct fruit_config_data *config = NULL;
3410 struct fio *fio = NULL;
3411 int open_flags = flags & ~O_CREAT;
3414 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3416 SMB_VFS_HANDLE_GET_DATA(handle, config,
3417 struct fruit_config_data, return -1);
3419 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3420 fio->type = ADOUBLE_META;
3421 fio->config = config;
3423 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, open_flags, mode);
3428 if (!(flags & O_CREAT)) {
3429 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
3433 fd = fruit_fake_fd();
3435 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
3439 fio->fake_fd = true;
3446 static int fruit_open_meta_netatalk(vfs_handle_struct *handle,
3447 struct smb_filename *smb_fname,
3452 struct fruit_config_data *config = NULL;
3453 struct fio *fio = NULL;
3454 struct adouble *ad = NULL;
3455 bool meta_exists = false;
3458 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3460 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
3467 if (!meta_exists && !(flags & O_CREAT)) {
3472 fd = fruit_fake_fd();
3477 SMB_VFS_HANDLE_GET_DATA(handle, config,
3478 struct fruit_config_data, return -1);
3480 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3481 fio->type = ADOUBLE_META;
3482 fio->config = config;
3483 fio->fake_fd = true;
3490 static int fruit_open_meta(vfs_handle_struct *handle,
3491 struct smb_filename *smb_fname,
3492 files_struct *fsp, int flags, mode_t mode)
3495 struct fruit_config_data *config = NULL;
3497 DBG_DEBUG("path [%s]\n", smb_fname_str_dbg(smb_fname));
3499 SMB_VFS_HANDLE_GET_DATA(handle, config,
3500 struct fruit_config_data, return -1);
3502 switch (config->meta) {
3503 case FRUIT_META_STREAM:
3504 fd = fruit_open_meta_stream(handle, smb_fname,
3508 case FRUIT_META_NETATALK:
3509 fd = fruit_open_meta_netatalk(handle, smb_fname,
3514 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3518 DBG_DEBUG("path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3523 static int fruit_open_rsrc_adouble(vfs_handle_struct *handle,
3524 struct smb_filename *smb_fname,
3530 struct adouble *ad = NULL;
3531 struct smb_filename *smb_fname_base = NULL;
3532 struct fruit_config_data *config = NULL;
3535 SMB_VFS_HANDLE_GET_DATA(handle, config,
3536 struct fruit_config_data, return -1);
3538 if ((!(flags & O_CREAT)) &&
3539 S_ISDIR(fsp->base_fsp->fsp_name->st.st_ex_mode))
3541 /* sorry, but directories don't habe a resource fork */
3546 rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_base);
3551 /* We always need read/write access for the metadata header too */
3552 flags &= ~(O_RDONLY | O_WRONLY);
3555 hostfd = SMB_VFS_NEXT_OPEN(handle, smb_fname_base, fsp,
3562 if (flags & (O_CREAT | O_TRUNC)) {
3563 ad = ad_init(fsp, ADOUBLE_RSRC);
3569 fsp->fh->fd = hostfd;
3571 rc = ad_fset(handle, ad, fsp);
3582 TALLOC_FREE(smb_fname_base);
3584 DEBUG(10, ("fruit_open resource fork: rc=%d, fd=%d\n", rc, hostfd));
3586 int saved_errno = errno;
3589 * BUGBUGBUG -- we would need to call
3590 * fd_close_posix here, but we don't have a
3593 fsp->fh->fd = hostfd;
3597 errno = saved_errno;
3602 static int fruit_open_rsrc_xattr(vfs_handle_struct *handle,
3603 struct smb_filename *smb_fname,
3608 #ifdef HAVE_ATTROPEN
3611 fd = attropen(smb_fname->base_name,
3612 AFPRESOURCE_EA_NETATALK,
3627 static int fruit_open_rsrc(vfs_handle_struct *handle,
3628 struct smb_filename *smb_fname,
3629 files_struct *fsp, int flags, mode_t mode)
3632 struct fruit_config_data *config = NULL;
3633 struct fio *fio = NULL;
3635 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3637 SMB_VFS_HANDLE_GET_DATA(handle, config,
3638 struct fruit_config_data, return -1);
3640 switch (config->rsrc) {
3641 case FRUIT_RSRC_STREAM:
3642 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3645 case FRUIT_RSRC_ADFILE:
3646 fd = fruit_open_rsrc_adouble(handle, smb_fname,
3650 case FRUIT_RSRC_XATTR:
3651 fd = fruit_open_rsrc_xattr(handle, smb_fname,
3656 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3660 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3666 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3667 fio->type = ADOUBLE_RSRC;
3668 fio->config = config;
3673 static int fruit_open(vfs_handle_struct *handle,
3674 struct smb_filename *smb_fname,
3675 files_struct *fsp, int flags, mode_t mode)
3679 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3681 if (!is_ntfs_stream_smb_fname(smb_fname)) {
3682 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3685 if (is_afpinfo_stream(smb_fname)) {
3686 fd = fruit_open_meta(handle, smb_fname, fsp, flags, mode);
3687 } else if (is_afpresource_stream(smb_fname)) {
3688 fd = fruit_open_rsrc(handle, smb_fname, fsp, flags, mode);
3690 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3693 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3698 static int fruit_close_meta(vfs_handle_struct *handle,
3702 struct fruit_config_data *config = NULL;
3704 SMB_VFS_HANDLE_GET_DATA(handle, config,
3705 struct fruit_config_data, return -1);
3707 switch (config->meta) {
3708 case FRUIT_META_STREAM:
3709 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3712 case FRUIT_META_NETATALK:
3713 ret = close(fsp->fh->fd);
3718 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3726 static int fruit_close_rsrc(vfs_handle_struct *handle,
3730 struct fruit_config_data *config = NULL;
3732 SMB_VFS_HANDLE_GET_DATA(handle, config,
3733 struct fruit_config_data, return -1);
3735 switch (config->rsrc) {
3736 case FRUIT_RSRC_STREAM:
3737 case FRUIT_RSRC_ADFILE:
3738 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3741 case FRUIT_RSRC_XATTR:
3742 ret = close(fsp->fh->fd);
3747 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3754 static int fruit_close(vfs_handle_struct *handle,
3762 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(fsp->fsp_name), fd);
3764 if (!is_ntfs_stream_smb_fname(fsp->fsp_name)) {
3765 return SMB_VFS_NEXT_CLOSE(handle, fsp);
3768 if (is_afpinfo_stream(fsp->fsp_name)) {
3769 ret = fruit_close_meta(handle, fsp);
3770 } else if (is_afpresource_stream(fsp->fsp_name)) {
3771 ret = fruit_close_rsrc(handle, fsp);
3773 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3779 static int fruit_rename(struct vfs_handle_struct *handle,
3780 const struct smb_filename *smb_fname_src,
3781 const struct smb_filename *smb_fname_dst)
3784 struct fruit_config_data *config = NULL;
3785 struct smb_filename *src_adp_smb_fname = NULL;
3786 struct smb_filename *dst_adp_smb_fname = NULL;
3788 SMB_VFS_HANDLE_GET_DATA(handle, config,
3789 struct fruit_config_data, return -1);
3791 if (!VALID_STAT(smb_fname_src->st)) {
3792 DBG_ERR("Need valid stat for [%s]\n",
3793 smb_fname_str_dbg(smb_fname_src));
3797 rc = SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
3802 if ((config->rsrc != FRUIT_RSRC_ADFILE) ||
3803 (!S_ISREG(smb_fname_src->st.st_ex_mode)))
3808 rc = adouble_path(talloc_tos(), smb_fname_src, &src_adp_smb_fname);
3813 rc = adouble_path(talloc_tos(), smb_fname_dst, &dst_adp_smb_fname);
3818 DBG_DEBUG("%s -> %s\n",
3819 smb_fname_str_dbg(src_adp_smb_fname),
3820 smb_fname_str_dbg(dst_adp_smb_fname));
3822 rc = SMB_VFS_NEXT_RENAME(handle, src_adp_smb_fname, dst_adp_smb_fname);
3823 if (errno == ENOENT) {
3828 TALLOC_FREE(src_adp_smb_fname);
3829 TALLOC_FREE(dst_adp_smb_fname);
3833 static int fruit_unlink_meta_stream(vfs_handle_struct *handle,
3834 const struct smb_filename *smb_fname)
3836 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3839 static int fruit_unlink_meta_netatalk(vfs_handle_struct *handle,
3840 const struct smb_filename *smb_fname)
3842 return SMB_VFS_REMOVEXATTR(handle->conn,
3844 AFPINFO_EA_NETATALK);
3847 static int fruit_unlink_meta(vfs_handle_struct *handle,
3848 const struct smb_filename *smb_fname)
3850 struct fruit_config_data *config = NULL;
3853 SMB_VFS_HANDLE_GET_DATA(handle, config,
3854 struct fruit_config_data, return -1);
3856 switch (config->meta) {
3857 case FRUIT_META_STREAM:
3858 rc = fruit_unlink_meta_stream(handle, smb_fname);
3861 case FRUIT_META_NETATALK:
3862 rc = fruit_unlink_meta_netatalk(handle, smb_fname);
3866 DBG_ERR("Unsupported meta config [%d]\n", config->meta);
3873 static int fruit_unlink_rsrc_stream(vfs_handle_struct *handle,
3874 const struct smb_filename *smb_fname,
3879 if (!force_unlink) {
3880 struct smb_filename *smb_fname_cp = NULL;
3883 smb_fname_cp = cp_smb_filename(talloc_tos(), smb_fname);
3884 if (smb_fname_cp == NULL) {
3889 * 0 byte resource fork streams are not listed by
3890 * vfs_streaminfo, as a result stream cleanup/deletion of file
3891 * deletion doesn't remove the resourcefork stream.
3894 ret = SMB_VFS_NEXT_STAT(handle, smb_fname_cp);
3896 TALLOC_FREE(smb_fname_cp);
3897 DBG_ERR("stat [%s] failed [%s]\n",
3898 smb_fname_str_dbg(smb_fname_cp), strerror(errno));
3902 size = smb_fname_cp->st.st_ex_size;
3903 TALLOC_FREE(smb_fname_cp);
3906 /* OS X ignores resource fork stream delete requests */
3911 ret = SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3912 if ((ret != 0) && (errno == ENOENT) && force_unlink) {
3919 static int fruit_unlink_rsrc_adouble(vfs_handle_struct *handle,
3920 const struct smb_filename *smb_fname,
3924 struct adouble *ad = NULL;
3925 struct smb_filename *adp_smb_fname = NULL;
3927 if (!force_unlink) {
3928 ad = ad_get(talloc_tos(), handle, smb_fname,
3937 * 0 byte resource fork streams are not listed by
3938 * vfs_streaminfo, as a result stream cleanup/deletion of file
3939 * deletion doesn't remove the resourcefork stream.
3942 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
3943 /* OS X ignores resource fork stream delete requests */
3951 rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
3956 rc = SMB_VFS_NEXT_UNLINK(handle, adp_smb_fname);
3957 TALLOC_FREE(adp_smb_fname);
3958 if ((rc != 0) && (errno == ENOENT) && force_unlink) {
3965 static int fruit_unlink_rsrc_xattr(vfs_handle_struct *handle,
3966 const struct smb_filename *smb_fname,
3970 * OS X ignores resource fork stream delete requests, so nothing to do
3971 * here. Removing the file will remove the xattr anyway, so we don't
3972 * have to take care of removing 0 byte resource forks that could be
3978 static int fruit_unlink_rsrc(vfs_handle_struct *handle,
3979 const struct smb_filename *smb_fname,
3982 struct fruit_config_data *config = NULL;
3985 SMB_VFS_HANDLE_GET_DATA(handle, config,
3986 struct fruit_config_data, return -1);
3988 switch (config->rsrc) {
3989 case FRUIT_RSRC_STREAM:
3990 rc = fruit_unlink_rsrc_stream(handle, smb_fname, force_unlink);
3993 case FRUIT_RSRC_ADFILE:
3994 rc = fruit_unlink_rsrc_adouble(handle, smb_fname, force_unlink);
3997 case FRUIT_RSRC_XATTR:
3998 rc = fruit_unlink_rsrc_xattr(handle, smb_fname, force_unlink);
4002 DBG_ERR("Unsupported rsrc config [%d]\n", config->rsrc);
4009 static int fruit_unlink(vfs_handle_struct *handle,
4010 const struct smb_filename *smb_fname)
4013 struct fruit_config_data *config = NULL;
4014 struct smb_filename *rsrc_smb_fname = NULL;
4016 SMB_VFS_HANDLE_GET_DATA(handle, config,
4017 struct fruit_config_data, return -1);
4019 if (is_afpinfo_stream(smb_fname)) {
4020 return fruit_unlink_meta(handle, smb_fname);
4021 } else if (is_afpresource_stream(smb_fname)) {
4022 return fruit_unlink_rsrc(handle, smb_fname, false);
4023 } else if (is_ntfs_stream_smb_fname(smb_fname)) {
4024 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4025 } else if (is_adouble_file(smb_fname->base_name)) {
4026 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4030 * A request to delete the base file. Because 0 byte resource
4031 * fork streams are not listed by fruit_streaminfo,
4032 * delete_all_streams() can't remove 0 byte resource fork
4033 * streams, so we have to cleanup this here.
4035 rsrc_smb_fname = synthetic_smb_fname(talloc_tos(),
4036 smb_fname->base_name,
4037 AFPRESOURCE_STREAM_NAME,
4040 if (rsrc_smb_fname == NULL) {
4044 rc = fruit_unlink_rsrc(handle, rsrc_smb_fname, true);
4045 if ((rc != 0) && (errno != ENOENT)) {
4046 DBG_ERR("Forced unlink of [%s] failed [%s]\n",
4047 smb_fname_str_dbg(rsrc_smb_fname), strerror(errno));
4048 TALLOC_FREE(rsrc_smb_fname);
4051 TALLOC_FREE(rsrc_smb_fname);
4053 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4056 static int fruit_chmod(vfs_handle_struct *handle,
4057 const struct smb_filename *smb_fname,
4061 struct fruit_config_data *config = NULL;
4062 struct smb_filename *smb_fname_adp = NULL;
4064 rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname, mode);
4069 SMB_VFS_HANDLE_GET_DATA(handle, config,
4070 struct fruit_config_data, return -1);
4072 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4076 if (!VALID_STAT(smb_fname->st)) {
4080 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
4084 rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_adp);
4089 DEBUG(10, ("fruit_chmod: %s\n", smb_fname_adp->base_name));
4091 rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname_adp, mode);
4092 if (errno == ENOENT) {
4096 TALLOC_FREE(smb_fname_adp);
4100 static int fruit_chown(vfs_handle_struct *handle,
4101 const struct smb_filename *smb_fname,
4106 struct fruit_config_data *config = NULL;
4107 struct smb_filename *adp_smb_fname = NULL;
4109 rc = SMB_VFS_NEXT_CHOWN(handle, smb_fname, uid, gid);
4114 SMB_VFS_HANDLE_GET_DATA(handle, config,
4115 struct fruit_config_data, return -1);
4117 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4121 if (!VALID_STAT(smb_fname->st)) {
4125 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
4129 rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
4134 DEBUG(10, ("fruit_chown: %s\n", adp_smb_fname->base_name));
4136 rc = SMB_VFS_NEXT_CHOWN(handle, adp_smb_fname, uid, gid);
4137 if (errno == ENOENT) {
4142 TALLOC_FREE(adp_smb_fname);
4146 static int fruit_rmdir(struct vfs_handle_struct *handle,
4147 const struct smb_filename *smb_fname)
4151 struct fruit_config_data *config;
4153 SMB_VFS_HANDLE_GET_DATA(handle, config,
4154 struct fruit_config_data, return -1);
4156 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4161 * Due to there is no way to change bDeleteVetoFiles variable
4162 * from this module, need to clean up ourselves
4165 dh = SMB_VFS_OPENDIR(handle->conn, smb_fname, NULL, 0);
4170 while ((de = SMB_VFS_READDIR(handle->conn, dh, NULL)) != NULL) {
4171 struct adouble *ad = NULL;
4173 struct smb_filename *ad_smb_fname = NULL;
4176 if (!is_adouble_file(de->d_name)) {
4180 p = talloc_asprintf(talloc_tos(), "%s/%s",
4181 smb_fname->base_name, de->d_name);
4183 DBG_ERR("talloc_asprintf failed\n");
4187 ad_smb_fname = synthetic_smb_fname(talloc_tos(), p,
4191 if (ad_smb_fname == NULL) {
4192 DBG_ERR("synthetic_smb_fname failed\n");
4197 * Check whether it's a valid AppleDouble file, if
4198 * yes, delete it, ignore it otherwise.
4200 ad = ad_get(talloc_tos(), handle, ad_smb_fname, ADOUBLE_RSRC);
4202 TALLOC_FREE(ad_smb_fname);
4208 ret = SMB_VFS_NEXT_UNLINK(handle, ad_smb_fname);
4210 DBG_ERR("Deleting [%s] failed\n",
4211 smb_fname_str_dbg(ad_smb_fname));
4213 TALLOC_FREE(ad_smb_fname);
4218 SMB_VFS_CLOSEDIR(handle->conn, dh);
4220 return SMB_VFS_NEXT_RMDIR(handle, smb_fname);
4223 static ssize_t fruit_pread_meta_stream(vfs_handle_struct *handle,
4224 files_struct *fsp, void *data,
4225 size_t n, off_t offset)
4230 nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4231 if (nread == -1 || nread == n) {
4235 DBG_ERR("Removing [%s] after short read [%zd]\n",
4236 fsp_str_dbg(fsp), nread);
4238 ret = SMB_VFS_NEXT_UNLINK(handle, fsp->fsp_name);
4240 DBG_ERR("Removing [%s] failed\n", fsp_str_dbg(fsp));
4248 static ssize_t fruit_pread_meta_adouble(vfs_handle_struct *handle,
4249 files_struct *fsp, void *data,
4250 size_t n, off_t offset)
4253 struct adouble *ad = NULL;
4254 char afpinfo_buf[AFP_INFO_SIZE];
4258 ai = afpinfo_new(talloc_tos());
4263 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
4269 p = ad_get_entry(ad, ADEID_FINDERI);
4271 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
4276 memcpy(&ai->afpi_FinderInfo[0], p, ADEDLEN_FINDERI);
4278 nread = afpinfo_pack(ai, afpinfo_buf);
4279 if (nread != AFP_INFO_SIZE) {
4284 memcpy(data, afpinfo_buf, n);
4292 static ssize_t fruit_pread_meta(vfs_handle_struct *handle,
4293 files_struct *fsp, void *data,
4294 size_t n, off_t offset)
4296 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4301 * OS X has a off-by-1 error in the offset calculation, so we're
4302 * bug compatible here. It won't hurt, as any relevant real
4303 * world read requests from the AFP_AfpInfo stream will be
4304 * offset=0 n=60. offset is ignored anyway, see below.
4306 if ((offset < 0) || (offset >= AFP_INFO_SIZE + 1)) {
4311 DBG_ERR("Failed to fetch fsp extension");
4315 /* Yes, macOS always reads from offset 0 */
4317 to_return = MIN(n, AFP_INFO_SIZE);
4319 switch (fio->config->meta) {
4320 case FRUIT_META_STREAM:
4321 nread = fruit_pread_meta_stream(handle, fsp, data,
4325 case FRUIT_META_NETATALK:
4326 nread = fruit_pread_meta_adouble(handle, fsp, data,
4331 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
4335 if (nread == -1 && fio->created) {
4337 char afpinfo_buf[AFP_INFO_SIZE];
4339 ai = afpinfo_new(talloc_tos());
4344 nread = afpinfo_pack(ai, afpinfo_buf);
4346 if (nread != AFP_INFO_SIZE) {
4350 memcpy(data, afpinfo_buf, to_return);
4357 static ssize_t fruit_pread_rsrc_stream(vfs_handle_struct *handle,
4358 files_struct *fsp, void *data,
4359 size_t n, off_t offset)
4361 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4364 static ssize_t fruit_pread_rsrc_xattr(vfs_handle_struct *handle,
4365 files_struct *fsp, void *data,
4366 size_t n, off_t offset)
4368 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4371 static ssize_t fruit_pread_rsrc_adouble(vfs_handle_struct *handle,
4372 files_struct *fsp, void *data,
4373 size_t n, off_t offset)
4375 struct adouble *ad = NULL;
4378 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
4383 nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n,
4384 offset + ad_getentryoff(ad, ADEID_RFORK));
4390 static ssize_t fruit_pread_rsrc(vfs_handle_struct *handle,
4391 files_struct *fsp, void *data,
4392 size_t n, off_t offset)
4394 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4402 switch (fio->config->rsrc) {
4403 case FRUIT_RSRC_STREAM:
4404 nread = fruit_pread_rsrc_stream(handle, fsp, data, n, offset);
4407 case FRUIT_RSRC_ADFILE:
4408 nread = fruit_pread_rsrc_adouble(handle, fsp, data, n, offset);
4411 case FRUIT_RSRC_XATTR:
4412 nread = fruit_pread_rsrc_xattr(handle, fsp, data, n, offset);
4416 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
4423 static ssize_t fruit_pread(vfs_handle_struct *handle,
4424 files_struct *fsp, void *data,
4425 size_t n, off_t offset)
4427 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4430 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4431 fsp_str_dbg(fsp), (intmax_t)offset, n);
4434 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4437 if (fio->type == ADOUBLE_META) {
4438 nread = fruit_pread_meta(handle, fsp, data, n, offset);
4440 nread = fruit_pread_rsrc(handle, fsp, data, n, offset);
4443 DBG_DEBUG("Path [%s] nread [%zd]\n", fsp_str_dbg(fsp), nread);
4447 static bool fruit_must_handle_aio_stream(struct fio *fio)
4453 if (fio->type == ADOUBLE_META) {
4457 if ((fio->type == ADOUBLE_RSRC) &&
4458 (fio->config->rsrc == FRUIT_RSRC_ADFILE))
4466 struct fruit_pread_state {
4468 struct vfs_aio_state vfs_aio_state;
4471 static void fruit_pread_done(struct tevent_req *subreq);
4473 static struct tevent_req *fruit_pread_send(
4474 struct vfs_handle_struct *handle,
4475 TALLOC_CTX *mem_ctx,
4476 struct tevent_context *ev,
4477 struct files_struct *fsp,
4479 size_t n, off_t offset)
4481 struct tevent_req *req = NULL;
4482 struct tevent_req *subreq = NULL;
4483 struct fruit_pread_state *state = NULL;
4484 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4486 req = tevent_req_create(mem_ctx, &state,
4487 struct fruit_pread_state);
4492 if (fruit_must_handle_aio_stream(fio)) {
4493 state->nread = SMB_VFS_PREAD(fsp, data, n, offset);
4494 if (state->nread != n) {
4495 if (state->nread != -1) {
4498 tevent_req_error(req, errno);
4499 return tevent_req_post(req, ev);
4501 tevent_req_done(req);
4502 return tevent_req_post(req, ev);
4505 subreq = SMB_VFS_NEXT_PREAD_SEND(state, ev, handle, fsp,
4507 if (tevent_req_nomem(req, subreq)) {
4508 return tevent_req_post(req, ev);
4510 tevent_req_set_callback(subreq, fruit_pread_done, req);
4514 static void fruit_pread_done(struct tevent_req *subreq)
4516 struct tevent_req *req = tevent_req_callback_data(
4517 subreq, struct tevent_req);
4518 struct fruit_pread_state *state = tevent_req_data(
4519 req, struct fruit_pread_state);
4521 state->nread = SMB_VFS_PREAD_RECV(subreq, &state->vfs_aio_state);
4522 TALLOC_FREE(subreq);
4524 if (tevent_req_error(req, state->vfs_aio_state.error)) {
4527 tevent_req_done(req);
4530 static ssize_t fruit_pread_recv(struct tevent_req *req,
4531 struct vfs_aio_state *vfs_aio_state)
4533 struct fruit_pread_state *state = tevent_req_data(
4534 req, struct fruit_pread_state);
4536 if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
4540 *vfs_aio_state = state->vfs_aio_state;
4541 return state->nread;
4544 static ssize_t fruit_pwrite_meta_stream(vfs_handle_struct *handle,
4545 files_struct *fsp, const void *data,
4546 size_t n, off_t offset)
4548 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4554 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4555 fsp_str_dbg(fsp), (intmax_t)offset, n);
4564 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
4566 DBG_ERR("Close [%s] failed: %s\n",
4567 fsp_str_dbg(fsp), strerror(errno));
4572 fd = SMB_VFS_NEXT_OPEN(handle,
4578 DBG_ERR("On-demand create [%s] in write failed: %s\n",
4579 fsp_str_dbg(fsp), strerror(errno));
4583 fio->fake_fd = false;
4586 ai = afpinfo_unpack(talloc_tos(), data);
4591 if (ai_empty_finderinfo(ai)) {
4593 * Writing an all 0 blob to the metadata stream results in the
4594 * stream being removed on a macOS server. This ensures we
4595 * behave the same and it verified by the "delete AFP_AfpInfo by
4596 * writing all 0" test.
4598 ret = SMB_VFS_NEXT_FTRUNCATE(handle, fsp, 0);
4600 DBG_ERR("SMB_VFS_NEXT_FTRUNCATE on [%s] failed\n",
4605 ok = set_delete_on_close(
4608 handle->conn->session_info->security_token,
4609 handle->conn->session_info->unix_token);
4611 DBG_ERR("set_delete_on_close on [%s] failed\n",
4618 nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4619 if (nwritten != n) {
4626 static ssize_t fruit_pwrite_meta_netatalk(vfs_handle_struct *handle,
4627 files_struct *fsp, const void *data,
4628 size_t n, off_t offset)
4630 struct adouble *ad = NULL;
4636 ai = afpinfo_unpack(talloc_tos(), data);
4641 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
4643 ad = ad_init(talloc_tos(), ADOUBLE_META);
4648 p = ad_get_entry(ad, ADEID_FINDERI);
4650 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
4655 memcpy(p, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
4657 ret = ad_fset(handle, ad, fsp);
4659 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
4666 if (!ai_empty_finderinfo(ai)) {
4671 * Writing an all 0 blob to the metadata stream results in the stream
4672 * being removed on a macOS server. This ensures we behave the same and
4673 * it verified by the "delete AFP_AfpInfo by writing all 0" test.
4676 ok = set_delete_on_close(
4679 handle->conn->session_info->security_token,
4680 handle->conn->session_info->unix_token);
4682 DBG_ERR("set_delete_on_close on [%s] failed\n",
4690 static ssize_t fruit_pwrite_meta(vfs_handle_struct *handle,
4691 files_struct *fsp, const void *data,
4692 size_t n, off_t offset)
4694 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4696 uint8_t buf[AFP_INFO_SIZE];
4702 DBG_ERR("Failed to fetch fsp extension");
4711 if (offset != 0 && n < 60) {
4716 cmp = memcmp(data, "AFP", 3);
4722 if (n <= AFP_OFF_FinderInfo) {
4724 * Nothing to do here really, just return
4732 if (to_copy > AFP_INFO_SIZE) {
4733 to_copy = AFP_INFO_SIZE;
4735 memcpy(buf, data, to_copy);
4738 if (to_write != AFP_INFO_SIZE) {
4739 to_write = AFP_INFO_SIZE;
4742 switch (fio->config->meta) {
4743 case FRUIT_META_STREAM:
4744 nwritten = fruit_pwrite_meta_stream(handle,
4751 case FRUIT_META_NETATALK:
4752 nwritten = fruit_pwrite_meta_netatalk(handle,
4760 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
4764 if (nwritten != to_write) {
4769 * Return the requested amount, verified against macOS SMB server
4774 static ssize_t fruit_pwrite_rsrc_stream(vfs_handle_struct *handle,
4775 files_struct *fsp, const void *data,
4776 size_t n, off_t offset)
4778 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4781 static ssize_t fruit_pwrite_rsrc_xattr(vfs_handle_struct *handle,
4782 files_struct *fsp, const void *data,
4783 size_t n, off_t offset)
4785 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4788 static ssize_t fruit_pwrite_rsrc_adouble(vfs_handle_struct *handle,
4789 files_struct *fsp, const void *data,
4790 size_t n, off_t offset)
4792 struct adouble *ad = NULL;
4796 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
4798 DBG_ERR("ad_get [%s] failed\n", fsp_str_dbg(fsp));
4802 nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n,
4803 offset + ad_getentryoff(ad, ADEID_RFORK));
4804 if (nwritten != n) {
4805 DBG_ERR("Short write on [%s] [%zd/%zd]\n",
4806 fsp_str_dbg(fsp), nwritten, n);
4811 if ((n + offset) > ad_getentrylen(ad, ADEID_RFORK)) {
4812 ad_setentrylen(ad, ADEID_RFORK, n + offset);
4813 ret = ad_fset(handle, ad, fsp);
4815 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
4825 static ssize_t fruit_pwrite_rsrc(vfs_handle_struct *handle,
4826 files_struct *fsp, const void *data,
4827 size_t n, off_t offset)
4829 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4833 DBG_ERR("Failed to fetch fsp extension");
4837 switch (fio->config->rsrc) {
4838 case FRUIT_RSRC_STREAM:
4839 nwritten = fruit_pwrite_rsrc_stream(handle, fsp, data, n, offset);
4842 case FRUIT_RSRC_ADFILE:
4843 nwritten = fruit_pwrite_rsrc_adouble(handle, fsp, data, n, offset);
4846 case FRUIT_RSRC_XATTR:
4847 nwritten = fruit_pwrite_rsrc_xattr(handle, fsp, data, n, offset);
4851 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
4858 static ssize_t fruit_pwrite(vfs_handle_struct *handle,
4859 files_struct *fsp, const void *data,
4860 size_t n, off_t offset)
4862 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4865 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4866 fsp_str_dbg(fsp), (intmax_t)offset, n);
4869 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4872 if (fio->type == ADOUBLE_META) {
4873 nwritten = fruit_pwrite_meta(handle, fsp, data, n, offset);
4875 nwritten = fruit_pwrite_rsrc(handle, fsp, data, n, offset);
4878 DBG_DEBUG("Path [%s] nwritten=%zd\n", fsp_str_dbg(fsp), nwritten);
4882 struct fruit_pwrite_state {
4884 struct vfs_aio_state vfs_aio_state;
4887 static void fruit_pwrite_done(struct tevent_req *subreq);
4889 static struct tevent_req *fruit_pwrite_send(
4890 struct vfs_handle_struct *handle,
4891 TALLOC_CTX *mem_ctx,
4892 struct tevent_context *ev,
4893 struct files_struct *fsp,
4895 size_t n, off_t offset)
4897 struct tevent_req *req = NULL;
4898 struct tevent_req *subreq = NULL;
4899 struct fruit_pwrite_state *state = NULL;
4900 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4902 req = tevent_req_create(mem_ctx, &state,
4903 struct fruit_pwrite_state);
4908 if (fruit_must_handle_aio_stream(fio)) {
4909 state->nwritten = SMB_VFS_PWRITE(fsp, data, n, offset);
4910 if (state->nwritten != n) {
4911 if (state->nwritten != -1) {
4914 tevent_req_error(req, errno);
4915 return tevent_req_post(req, ev);
4917 tevent_req_done(req);
4918 return tevent_req_post(req, ev);
4921 subreq = SMB_VFS_NEXT_PWRITE_SEND(state, ev, handle, fsp,
4923 if (tevent_req_nomem(req, subreq)) {
4924 return tevent_req_post(req, ev);
4926 tevent_req_set_callback(subreq, fruit_pwrite_done, req);
4930 static void fruit_pwrite_done(struct tevent_req *subreq)
4932 struct tevent_req *req = tevent_req_callback_data(
4933 subreq, struct tevent_req);
4934 struct fruit_pwrite_state *state = tevent_req_data(
4935 req, struct fruit_pwrite_state);
4937 state->nwritten = SMB_VFS_PWRITE_RECV(subreq, &state->vfs_aio_state);
4938 TALLOC_FREE(subreq);
4940 if (tevent_req_error(req, state->vfs_aio_state.error)) {
4943 tevent_req_done(req);
4946 static ssize_t fruit_pwrite_recv(struct tevent_req *req,
4947 struct vfs_aio_state *vfs_aio_state)
4949 struct fruit_pwrite_state *state = tevent_req_data(
4950 req, struct fruit_pwrite_state);
4952 if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
4956 *vfs_aio_state = state->vfs_aio_state;
4957 return state->nwritten;
4961 * Helper to stat/lstat the base file of an smb_fname.
4963 static int fruit_stat_base(vfs_handle_struct *handle,
4964 struct smb_filename *smb_fname,
4967 char *tmp_stream_name;
4970 tmp_stream_name = smb_fname->stream_name;
4971 smb_fname->stream_name = NULL;
4973 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
4975 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
4977 smb_fname->stream_name = tmp_stream_name;
4979 DBG_DEBUG("fruit_stat_base [%s] dev [%ju] ino [%ju]\n",
4980 smb_fname->base_name,
4981 (uintmax_t)smb_fname->st.st_ex_dev,
4982 (uintmax_t)smb_fname->st.st_ex_ino);
4986 static int fruit_stat_meta_stream(vfs_handle_struct *handle,
4987 struct smb_filename *smb_fname,
4993 ret = fruit_stat_base(handle, smb_fname, false);
4998 ino = fruit_inode(&smb_fname->st, smb_fname->stream_name);
5001 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
5003 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5006 smb_fname->st.st_ex_ino = ino;
5011 static int fruit_stat_meta_netatalk(vfs_handle_struct *handle,
5012 struct smb_filename *smb_fname,
5015 struct adouble *ad = NULL;
5017 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5019 DBG_INFO("fruit_stat_meta %s: %s\n",
5020 smb_fname_str_dbg(smb_fname), strerror(errno));
5026 /* Populate the stat struct with info from the base file. */
5027 if (fruit_stat_base(handle, smb_fname, follow_links) == -1) {
5030 smb_fname->st.st_ex_size = AFP_INFO_SIZE;
5031 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5032 smb_fname->stream_name);
5036 static int fruit_stat_meta(vfs_handle_struct *handle,
5037 struct smb_filename *smb_fname,
5040 struct fruit_config_data *config = NULL;
5043 SMB_VFS_HANDLE_GET_DATA(handle, config,
5044 struct fruit_config_data, return -1);
5046 switch (config->meta) {
5047 case FRUIT_META_STREAM:
5048 ret = fruit_stat_meta_stream(handle, smb_fname, follow_links);
5051 case FRUIT_META_NETATALK:
5052 ret = fruit_stat_meta_netatalk(handle, smb_fname, follow_links);
5056 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
5063 static int fruit_stat_rsrc_netatalk(vfs_handle_struct *handle,
5064 struct smb_filename *smb_fname,
5067 struct adouble *ad = NULL;
5070 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
5076 /* Populate the stat struct with info from the base file. */
5077 ret = fruit_stat_base(handle, smb_fname, follow_links);
5083 smb_fname->st.st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
5084 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5085 smb_fname->stream_name);
5090 static int fruit_stat_rsrc_stream(vfs_handle_struct *handle,
5091 struct smb_filename *smb_fname,
5097 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
5099 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5105 static int fruit_stat_rsrc_xattr(vfs_handle_struct *handle,
5106 struct smb_filename *smb_fname,
5109 #ifdef HAVE_ATTROPEN
5113 /* Populate the stat struct with info from the base file. */
5114 ret = fruit_stat_base(handle, smb_fname, follow_links);
5119 fd = attropen(smb_fname->base_name,
5120 AFPRESOURCE_EA_NETATALK,
5126 ret = sys_fstat(fd, &smb_fname->st, false);
5129 DBG_ERR("fstat [%s:%s] failed\n", smb_fname->base_name,
5130 AFPRESOURCE_EA_NETATALK);
5136 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5137 smb_fname->stream_name);
5147 static int fruit_stat_rsrc(vfs_handle_struct *handle,
5148 struct smb_filename *smb_fname,
5151 struct fruit_config_data *config = NULL;
5154 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
5156 SMB_VFS_HANDLE_GET_DATA(handle, config,
5157 struct fruit_config_data, return -1);
5159 switch (config->rsrc) {
5160 case FRUIT_RSRC_STREAM:
5161 ret = fruit_stat_rsrc_stream(handle, smb_fname, follow_links);
5164 case FRUIT_RSRC_XATTR:
5165 ret = fruit_stat_rsrc_xattr(handle, smb_fname, follow_links);
5168 case FRUIT_RSRC_ADFILE:
5169 ret = fruit_stat_rsrc_netatalk(handle, smb_fname, follow_links);
5173 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
5180 static int fruit_stat(vfs_handle_struct *handle,
5181 struct smb_filename *smb_fname)
5185 DEBUG(10, ("fruit_stat called for %s\n",
5186 smb_fname_str_dbg(smb_fname)));
5188 if (!is_ntfs_stream_smb_fname(smb_fname)
5189 || is_ntfs_default_stream_smb_fname(smb_fname)) {
5190 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
5192 update_btime(handle, smb_fname);
5198 * Note if lp_posix_paths() is true, we can never
5199 * get here as is_ntfs_stream_smb_fname() is
5200 * always false. So we never need worry about
5201 * not following links here.
5204 if (is_afpinfo_stream(smb_fname)) {
5205 rc = fruit_stat_meta(handle, smb_fname, true);
5206 } else if (is_afpresource_stream(smb_fname)) {
5207 rc = fruit_stat_rsrc(handle, smb_fname, true);
5209 return SMB_VFS_NEXT_STAT(handle, smb_fname);
5213 update_btime(handle, smb_fname);
5214 smb_fname->st.st_ex_mode &= ~S_IFMT;
5215 smb_fname->st.st_ex_mode |= S_IFREG;
5216 smb_fname->st.st_ex_blocks =
5217 smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
5222 static int fruit_lstat(vfs_handle_struct *handle,
5223 struct smb_filename *smb_fname)
5227 DEBUG(10, ("fruit_lstat called for %s\n",
5228 smb_fname_str_dbg(smb_fname)));
5230 if (!is_ntfs_stream_smb_fname(smb_fname)
5231 || is_ntfs_default_stream_smb_fname(smb_fname)) {
5232 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5234 update_btime(handle, smb_fname);
5239 if (is_afpinfo_stream(smb_fname)) {
5240 rc = fruit_stat_meta(handle, smb_fname, false);
5241 } else if (is_afpresource_stream(smb_fname)) {
5242 rc = fruit_stat_rsrc(handle, smb_fname, false);
5244 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5248 update_btime(handle, smb_fname);
5249 smb_fname->st.st_ex_mode &= ~S_IFMT;
5250 smb_fname->st.st_ex_mode |= S_IFREG;
5251 smb_fname->st.st_ex_blocks =
5252 smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
5257 static int fruit_fstat_meta_stream(vfs_handle_struct *handle,
5259 SMB_STRUCT_STAT *sbuf)
5261 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5262 struct smb_filename smb_fname;
5271 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5276 *sbuf = fsp->base_fsp->fsp_name->st;
5277 sbuf->st_ex_size = AFP_INFO_SIZE;
5278 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5282 smb_fname = (struct smb_filename) {
5283 .base_name = fsp->fsp_name->base_name,
5286 ret = fruit_stat_base(handle, &smb_fname, false);
5290 *sbuf = smb_fname.st;
5292 ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5294 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5299 sbuf->st_ex_ino = ino;
5303 static int fruit_fstat_meta_netatalk(vfs_handle_struct *handle,
5305 SMB_STRUCT_STAT *sbuf)
5309 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5314 *sbuf = fsp->base_fsp->fsp_name->st;
5315 sbuf->st_ex_size = AFP_INFO_SIZE;
5316 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5321 static int fruit_fstat_meta(vfs_handle_struct *handle,
5323 SMB_STRUCT_STAT *sbuf,
5328 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
5330 switch (fio->config->meta) {
5331 case FRUIT_META_STREAM:
5332 ret = fruit_fstat_meta_stream(handle, fsp, sbuf);
5335 case FRUIT_META_NETATALK:
5336 ret = fruit_fstat_meta_netatalk(handle, fsp, sbuf);
5340 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
5344 DBG_DEBUG("Path [%s] ret [%d]\n", fsp_str_dbg(fsp), ret);
5348 static int fruit_fstat_rsrc_xattr(vfs_handle_struct *handle,
5350 SMB_STRUCT_STAT *sbuf)
5352 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5355 static int fruit_fstat_rsrc_stream(vfs_handle_struct *handle,
5357 SMB_STRUCT_STAT *sbuf)
5359 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5362 static int fruit_fstat_rsrc_adouble(vfs_handle_struct *handle,
5364 SMB_STRUCT_STAT *sbuf)
5366 struct adouble *ad = NULL;
5369 /* Populate the stat struct with info from the base file. */
5370 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5375 ad = ad_get(talloc_tos(), handle,
5376 fsp->base_fsp->fsp_name,
5379 DBG_ERR("ad_get [%s] failed [%s]\n",
5380 fsp_str_dbg(fsp), strerror(errno));
5384 *sbuf = fsp->base_fsp->fsp_name->st;
5385 sbuf->st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
5386 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5392 static int fruit_fstat_rsrc(vfs_handle_struct *handle, files_struct *fsp,
5393 SMB_STRUCT_STAT *sbuf, struct fio *fio)
5397 switch (fio->config->rsrc) {
5398 case FRUIT_RSRC_STREAM:
5399 ret = fruit_fstat_rsrc_stream(handle, fsp, sbuf);
5402 case FRUIT_RSRC_ADFILE:
5403 ret = fruit_fstat_rsrc_adouble(handle, fsp, sbuf);
5406 case FRUIT_RSRC_XATTR:
5407 ret = fruit_fstat_rsrc_xattr(handle, fsp, sbuf);
5411 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
5418 static int fruit_fstat(vfs_handle_struct *handle, files_struct *fsp,
5419 SMB_STRUCT_STAT *sbuf)
5421 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5425 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5428 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
5430 if (fio->type == ADOUBLE_META) {
5431 rc = fruit_fstat_meta(handle, fsp, sbuf, fio);
5433 rc = fruit_fstat_rsrc(handle, fsp, sbuf, fio);
5437 sbuf->st_ex_mode &= ~S_IFMT;
5438 sbuf->st_ex_mode |= S_IFREG;
5439 sbuf->st_ex_blocks = sbuf->st_ex_size / STAT_ST_BLOCKSIZE + 1;
5442 DBG_DEBUG("Path [%s] rc [%d] size [%"PRIdMAX"]\n",
5443 fsp_str_dbg(fsp), rc, (intmax_t)sbuf->st_ex_size);
5447 static NTSTATUS delete_invalid_meta_stream(
5448 vfs_handle_struct *handle,
5449 const struct smb_filename *smb_fname,
5450 TALLOC_CTX *mem_ctx,
5451 unsigned int *pnum_streams,
5452 struct stream_struct **pstreams,
5455 struct smb_filename *sname = NULL;
5459 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams, AFPINFO_STREAM);
5461 return NT_STATUS_INTERNAL_ERROR;
5465 return NT_STATUS_OK;
5468 sname = synthetic_smb_fname(talloc_tos(),
5469 smb_fname->base_name,
5470 AFPINFO_STREAM_NAME,
5472 if (sname == NULL) {
5473 return NT_STATUS_NO_MEMORY;
5476 ret = SMB_VFS_NEXT_UNLINK(handle, sname);
5479 DBG_ERR("Removing [%s] failed\n", smb_fname_str_dbg(sname));
5480 return map_nt_error_from_unix(errno);
5483 return NT_STATUS_OK;
5486 static NTSTATUS fruit_streaminfo_meta_stream(
5487 vfs_handle_struct *handle,
5488 struct files_struct *fsp,
5489 const struct smb_filename *smb_fname,
5490 TALLOC_CTX *mem_ctx,
5491 unsigned int *pnum_streams,
5492 struct stream_struct **pstreams)
5494 struct stream_struct *stream = *pstreams;
5495 unsigned int num_streams = *pnum_streams;
5498 for (i = 0; i < num_streams; i++) {
5499 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
5504 if (i == num_streams) {
5505 return NT_STATUS_OK;
5508 if (stream[i].size != AFP_INFO_SIZE) {
5509 DBG_ERR("Removing invalid AFPINFO_STREAM size [%jd] from [%s]\n",
5510 (intmax_t)stream[i].size, smb_fname_str_dbg(smb_fname));
5512 return delete_invalid_meta_stream(handle,
5521 return NT_STATUS_OK;
5524 static NTSTATUS fruit_streaminfo_meta_netatalk(
5525 vfs_handle_struct *handle,
5526 struct files_struct *fsp,
5527 const struct smb_filename *smb_fname,
5528 TALLOC_CTX *mem_ctx,
5529 unsigned int *pnum_streams,
5530 struct stream_struct **pstreams)
5532 struct stream_struct *stream = *pstreams;
5533 unsigned int num_streams = *pnum_streams;
5534 struct adouble *ad = NULL;
5539 /* Remove the Netatalk xattr from the list */
5540 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5541 ":" NETATALK_META_XATTR ":$DATA");
5543 return NT_STATUS_NO_MEMORY;
5547 * Check if there's a AFPINFO_STREAM from the VFS streams
5548 * backend and if yes, remove it from the list
5550 for (i = 0; i < num_streams; i++) {
5551 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
5556 if (i < num_streams) {
5557 DBG_WARNING("Unexpected AFPINFO_STREAM on [%s]\n",
5558 smb_fname_str_dbg(smb_fname));
5560 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5563 return NT_STATUS_INTERNAL_ERROR;
5567 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5569 return NT_STATUS_OK;
5572 is_fi_empty = ad_empty_finderinfo(ad);
5576 return NT_STATUS_OK;
5579 ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
5580 AFPINFO_STREAM_NAME, AFP_INFO_SIZE,
5581 smb_roundup(handle->conn, AFP_INFO_SIZE));
5583 return NT_STATUS_NO_MEMORY;
5586 return NT_STATUS_OK;
5589 static NTSTATUS fruit_streaminfo_meta(vfs_handle_struct *handle,
5590 struct files_struct *fsp,
5591 const struct smb_filename *smb_fname,
5592 TALLOC_CTX *mem_ctx,
5593 unsigned int *pnum_streams,
5594 struct stream_struct **pstreams)
5596 struct fruit_config_data *config = NULL;
5599 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5600 return NT_STATUS_INTERNAL_ERROR);
5602 switch (config->meta) {
5603 case FRUIT_META_NETATALK:
5604 status = fruit_streaminfo_meta_netatalk(handle, fsp, smb_fname,
5605 mem_ctx, pnum_streams,
5609 case FRUIT_META_STREAM:
5610 status = fruit_streaminfo_meta_stream(handle, fsp, smb_fname,
5611 mem_ctx, pnum_streams,
5616 return NT_STATUS_INTERNAL_ERROR;
5622 static NTSTATUS fruit_streaminfo_rsrc_stream(
5623 vfs_handle_struct *handle,
5624 struct files_struct *fsp,
5625 const struct smb_filename *smb_fname,
5626 TALLOC_CTX *mem_ctx,
5627 unsigned int *pnum_streams,
5628 struct stream_struct **pstreams)
5632 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
5634 DBG_ERR("Filtering resource stream failed\n");
5635 return NT_STATUS_INTERNAL_ERROR;
5637 return NT_STATUS_OK;
5640 static NTSTATUS fruit_streaminfo_rsrc_xattr(
5641 vfs_handle_struct *handle,
5642 struct files_struct *fsp,
5643 const struct smb_filename *smb_fname,
5644 TALLOC_CTX *mem_ctx,
5645 unsigned int *pnum_streams,
5646 struct stream_struct **pstreams)
5650 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
5652 DBG_ERR("Filtering resource stream failed\n");
5653 return NT_STATUS_INTERNAL_ERROR;
5655 return NT_STATUS_OK;
5658 static NTSTATUS fruit_streaminfo_rsrc_adouble(
5659 vfs_handle_struct *handle,
5660 struct files_struct *fsp,
5661 const struct smb_filename *smb_fname,
5662 TALLOC_CTX *mem_ctx,
5663 unsigned int *pnum_streams,
5664 struct stream_struct **pstreams)
5666 struct stream_struct *stream = *pstreams;
5667 unsigned int num_streams = *pnum_streams;
5668 struct adouble *ad = NULL;
5674 * Check if there's a AFPRESOURCE_STREAM from the VFS streams backend
5675 * and if yes, remove it from the list
5677 for (i = 0; i < num_streams; i++) {
5678 if (strequal_m(stream[i].name, AFPRESOURCE_STREAM)) {
5683 if (i < num_streams) {
5684 DBG_WARNING("Unexpected AFPRESOURCE_STREAM on [%s]\n",
5685 smb_fname_str_dbg(smb_fname));
5687 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5688 AFPRESOURCE_STREAM);
5690 return NT_STATUS_INTERNAL_ERROR;
5694 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
5696 return NT_STATUS_OK;
5699 rlen = ad_getentrylen(ad, ADEID_RFORK);
5703 return NT_STATUS_OK;
5706 ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
5707 AFPRESOURCE_STREAM_NAME, rlen,
5708 smb_roundup(handle->conn, rlen));
5710 return NT_STATUS_NO_MEMORY;
5713 return NT_STATUS_OK;
5716 static NTSTATUS fruit_streaminfo_rsrc(vfs_handle_struct *handle,
5717 struct files_struct *fsp,
5718 const struct smb_filename *smb_fname,
5719 TALLOC_CTX *mem_ctx,
5720 unsigned int *pnum_streams,
5721 struct stream_struct **pstreams)
5723 struct fruit_config_data *config = NULL;
5726 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5727 return NT_STATUS_INTERNAL_ERROR);
5729 switch (config->rsrc) {
5730 case FRUIT_RSRC_STREAM:
5731 status = fruit_streaminfo_rsrc_stream(handle, fsp, smb_fname,
5732 mem_ctx, pnum_streams,
5736 case FRUIT_RSRC_XATTR:
5737 status = fruit_streaminfo_rsrc_xattr(handle, fsp, smb_fname,
5738 mem_ctx, pnum_streams,
5742 case FRUIT_RSRC_ADFILE:
5743 status = fruit_streaminfo_rsrc_adouble(handle, fsp, smb_fname,
5744 mem_ctx, pnum_streams,
5749 return NT_STATUS_INTERNAL_ERROR;
5755 static void fruit_filter_empty_streams(unsigned int *pnum_streams,
5756 struct stream_struct **pstreams)
5758 unsigned num_streams = *pnum_streams;
5759 struct stream_struct *streams = *pstreams;
5762 if (!global_fruit_config.nego_aapl) {
5766 while (i < num_streams) {
5767 struct smb_filename smb_fname = (struct smb_filename) {
5768 .stream_name = streams[i].name,
5771 if (is_ntfs_default_stream_smb_fname(&smb_fname)
5772 || streams[i].size > 0)
5778 streams[i] = streams[num_streams - 1];
5782 *pnum_streams = num_streams;
5785 static NTSTATUS fruit_streaminfo(vfs_handle_struct *handle,
5786 struct files_struct *fsp,
5787 const struct smb_filename *smb_fname,
5788 TALLOC_CTX *mem_ctx,
5789 unsigned int *pnum_streams,
5790 struct stream_struct **pstreams)
5792 struct fruit_config_data *config = NULL;
5795 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5796 return NT_STATUS_UNSUCCESSFUL);
5798 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
5800 status = SMB_VFS_NEXT_STREAMINFO(handle, fsp, smb_fname, mem_ctx,
5801 pnum_streams, pstreams);
5802 if (!NT_STATUS_IS_OK(status)) {
5806 fruit_filter_empty_streams(pnum_streams, pstreams);
5808 status = fruit_streaminfo_meta(handle, fsp, smb_fname,
5809 mem_ctx, pnum_streams, pstreams);
5810 if (!NT_STATUS_IS_OK(status)) {
5814 status = fruit_streaminfo_rsrc(handle, fsp, smb_fname,
5815 mem_ctx, pnum_streams, pstreams);
5816 if (!NT_STATUS_IS_OK(status)) {
5820 return NT_STATUS_OK;
5823 static int fruit_ntimes(vfs_handle_struct *handle,
5824 const struct smb_filename *smb_fname,
5825 struct smb_file_time *ft)
5828 struct adouble *ad = NULL;
5829 struct fruit_config_data *config = NULL;
5831 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5834 if ((config->meta != FRUIT_META_NETATALK) ||
5835 null_timespec(ft->create_time))
5837 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
5840 DEBUG(10,("set btime for %s to %s\n", smb_fname_str_dbg(smb_fname),
5841 time_to_asc(convert_timespec_to_time_t(ft->create_time))));
5843 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5848 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX,
5849 convert_time_t_to_uint32_t(ft->create_time.tv_sec));
5851 rc = ad_set(handle, ad, smb_fname);
5857 DEBUG(1, ("fruit_ntimes: %s\n", smb_fname_str_dbg(smb_fname)));
5860 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
5863 static int fruit_fallocate(struct vfs_handle_struct *handle,
5864 struct files_struct *fsp,
5869 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5872 return SMB_VFS_NEXT_FALLOCATE(handle, fsp, mode, offset, len);
5875 /* Let the pwrite code path handle it. */
5880 static int fruit_ftruncate_rsrc_xattr(struct vfs_handle_struct *handle,
5881 struct files_struct *fsp,
5884 #ifdef HAVE_ATTROPEN
5885 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
5890 static int fruit_ftruncate_rsrc_adouble(struct vfs_handle_struct *handle,
5891 struct files_struct *fsp,
5895 struct adouble *ad = NULL;
5898 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
5900 DBG_DEBUG("ad_get [%s] failed [%s]\n",
5901 fsp_str_dbg(fsp), strerror(errno));
5905 ad_off = ad_getentryoff(ad, ADEID_RFORK);
5907 rc = ftruncate(fsp->fh->fd, offset + ad_off);
5913 ad_setentrylen(ad, ADEID_RFORK, offset);
5915 rc = ad_fset(handle, ad, fsp);
5917 DBG_ERR("ad_fset [%s] failed [%s]\n",
5918 fsp_str_dbg(fsp), strerror(errno));
5927 static int fruit_ftruncate_rsrc_stream(struct vfs_handle_struct *handle,
5928 struct files_struct *fsp,
5931 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
5934 static int fruit_ftruncate_rsrc(struct vfs_handle_struct *handle,
5935 struct files_struct *fsp,
5938 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5942 DBG_ERR("Failed to fetch fsp extension");
5946 switch (fio->config->rsrc) {
5947 case FRUIT_RSRC_XATTR:
5948 ret = fruit_ftruncate_rsrc_xattr(handle, fsp, offset);
5951 case FRUIT_RSRC_ADFILE:
5952 ret = fruit_ftruncate_rsrc_adouble(handle, fsp, offset);
5955 case FRUIT_RSRC_STREAM:
5956 ret = fruit_ftruncate_rsrc_stream(handle, fsp, offset);
5960 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
5968 static int fruit_ftruncate_meta(struct vfs_handle_struct *handle,
5969 struct files_struct *fsp,
5973 DBG_WARNING("ftruncate %s to %jd",
5974 fsp_str_dbg(fsp), (intmax_t)offset);
5975 /* OS X returns NT_STATUS_ALLOTTED_SPACE_EXCEEDED */
5980 /* OS X returns success but does nothing */
5981 DBG_INFO("ignoring ftruncate %s to %jd\n",
5982 fsp_str_dbg(fsp), (intmax_t)offset);
5986 static int fruit_ftruncate(struct vfs_handle_struct *handle,
5987 struct files_struct *fsp,
5990 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5993 DBG_DEBUG("Path [%s] offset [%"PRIdMAX"]\n", fsp_str_dbg(fsp),
5997 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
6000 if (fio->type == ADOUBLE_META) {
6001 ret = fruit_ftruncate_meta(handle, fsp, offset);
6003 ret = fruit_ftruncate_rsrc(handle, fsp, offset);
6006 DBG_DEBUG("Path [%s] result [%d]\n", fsp_str_dbg(fsp), ret);
6010 static NTSTATUS fruit_create_file(vfs_handle_struct *handle,
6011 struct smb_request *req,
6012 uint16_t root_dir_fid,
6013 struct smb_filename *smb_fname,
6014 uint32_t access_mask,
6015 uint32_t share_access,
6016 uint32_t create_disposition,
6017 uint32_t create_options,
6018 uint32_t file_attributes,
6019 uint32_t oplock_request,
6020 struct smb2_lease *lease,
6021 uint64_t allocation_size,
6022 uint32_t private_flags,
6023 struct security_descriptor *sd,
6024 struct ea_list *ea_list,
6025 files_struct **result,
6027 const struct smb2_create_blobs *in_context_blobs,
6028 struct smb2_create_blobs *out_context_blobs)
6031 struct fruit_config_data *config = NULL;
6032 files_struct *fsp = NULL;
6033 struct fio *fio = NULL;
6034 bool internal_open = (oplock_request & INTERNAL_OPEN_ONLY);
6037 status = check_aapl(handle, req, in_context_blobs, out_context_blobs);
6038 if (!NT_STATUS_IS_OK(status)) {
6042 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
6043 return NT_STATUS_UNSUCCESSFUL);
6045 if (is_apple_stream(smb_fname) && !internal_open) {
6046 ret = ad_convert(handle, smb_fname);
6048 DBG_ERR("ad_convert() failed\n");
6049 return NT_STATUS_UNSUCCESSFUL;
6053 status = SMB_VFS_NEXT_CREATE_FILE(
6054 handle, req, root_dir_fid, smb_fname,
6055 access_mask, share_access,
6056 create_disposition, create_options,
6057 file_attributes, oplock_request,
6059 allocation_size, private_flags,
6060 sd, ea_list, result,
6061 pinfo, in_context_blobs, out_context_blobs);
6062 if (!NT_STATUS_IS_OK(status)) {
6068 if (global_fruit_config.nego_aapl) {
6069 if (config->posix_rename && fsp->is_directory) {
6071 * Enable POSIX directory rename behaviour
6073 fsp->posix_flags |= FSP_POSIX_FLAGS_RENAME;
6078 * If this is a plain open for existing files, opening an 0
6079 * byte size resource fork MUST fail with
6080 * NT_STATUS_OBJECT_NAME_NOT_FOUND.
6082 * Cf the vfs_fruit torture tests in test_rfork_create().
6084 if (global_fruit_config.nego_aapl &&
6085 create_disposition == FILE_OPEN &&
6086 smb_fname->st.st_ex_size == 0 &&
6087 is_ntfs_stream_smb_fname(smb_fname) &&
6088 !(is_ntfs_default_stream_smb_fname(smb_fname)))
6090 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
6094 fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
6095 if (fio != NULL && pinfo != NULL && *pinfo == FILE_WAS_CREATED) {
6096 fio->created = true;
6099 if (is_ntfs_stream_smb_fname(smb_fname)
6100 || fsp->is_directory) {
6104 if (config->locking == FRUIT_LOCKING_NETATALK) {
6105 status = fruit_check_access(
6109 if (!NT_STATUS_IS_OK(status)) {
6117 DEBUG(10, ("fruit_create_file: %s\n", nt_errstr(status)));
6120 close_file(req, fsp, ERROR_CLOSE);
6121 *result = fsp = NULL;
6127 static NTSTATUS fruit_readdir_attr(struct vfs_handle_struct *handle,
6128 const struct smb_filename *fname,
6129 TALLOC_CTX *mem_ctx,
6130 struct readdir_attr_data **pattr_data)
6132 struct fruit_config_data *config = NULL;
6133 struct readdir_attr_data *attr_data;
6137 SMB_VFS_HANDLE_GET_DATA(handle, config,
6138 struct fruit_config_data,
6139 return NT_STATUS_UNSUCCESSFUL);
6141 if (!global_fruit_config.nego_aapl) {
6142 return SMB_VFS_NEXT_READDIR_ATTR(handle, fname, mem_ctx, pattr_data);
6145 DEBUG(10, ("fruit_readdir_attr %s\n", fname->base_name));
6147 ret = ad_convert(handle, fname);
6149 DBG_ERR("ad_convert() failed\n");
6150 return NT_STATUS_UNSUCCESSFUL;
6153 *pattr_data = talloc_zero(mem_ctx, struct readdir_attr_data);
6154 if (*pattr_data == NULL) {
6155 return NT_STATUS_UNSUCCESSFUL;
6157 attr_data = *pattr_data;
6158 attr_data->type = RDATTR_AAPL;
6161 * Mac metadata: compressed FinderInfo, resource fork length
6164 status = readdir_attr_macmeta(handle, fname, attr_data);
6165 if (!NT_STATUS_IS_OK(status)) {
6167 * Error handling is tricky: if we return failure from
6168 * this function, the corresponding directory entry
6169 * will to be passed to the client, so we really just
6170 * want to error out on fatal errors.
6172 if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
6180 if (config->unix_info_enabled) {
6181 attr_data->attr_data.aapl.unix_mode = fname->st.st_ex_mode;
6187 if (!config->readdir_attr_max_access) {
6188 attr_data->attr_data.aapl.max_access = FILE_GENERIC_ALL;
6190 status = smbd_calculate_access_mask(
6194 SEC_FLAG_MAXIMUM_ALLOWED,
6195 &attr_data->attr_data.aapl.max_access);
6196 if (!NT_STATUS_IS_OK(status)) {
6201 return NT_STATUS_OK;
6204 DEBUG(1, ("fruit_readdir_attr %s, error: %s\n",
6205 fname->base_name, nt_errstr(status)));
6206 TALLOC_FREE(*pattr_data);
6210 static NTSTATUS fruit_fget_nt_acl(vfs_handle_struct *handle,
6212 uint32_t security_info,
6213 TALLOC_CTX *mem_ctx,
6214 struct security_descriptor **ppdesc)
6217 struct security_ace ace;
6219 struct fruit_config_data *config;
6221 SMB_VFS_HANDLE_GET_DATA(handle, config,
6222 struct fruit_config_data,
6223 return NT_STATUS_UNSUCCESSFUL);
6225 status = SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
6227 if (!NT_STATUS_IS_OK(status)) {
6232 * Add MS NFS style ACEs with uid, gid and mode
6234 if (!global_fruit_config.nego_aapl) {
6235 return NT_STATUS_OK;
6237 if (!config->unix_info_enabled) {
6238 return NT_STATUS_OK;
6241 /* First remove any existing ACE's with NFS style mode/uid/gid SIDs. */
6242 status = remove_virtual_nfs_aces(*ppdesc);
6243 if (!NT_STATUS_IS_OK(status)) {
6244 DBG_WARNING("failed to remove MS NFS style ACEs\n");
6248 /* MS NFS style mode */
6249 sid_compose(&sid, &global_sid_Unix_NFS_Mode, fsp->fsp_name->st.st_ex_mode);
6250 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6251 status = security_descriptor_dacl_add(*ppdesc, &ace);
6252 if (!NT_STATUS_IS_OK(status)) {
6253 DEBUG(1,("failed to add MS NFS style ACE\n"));
6257 /* MS NFS style uid */
6258 sid_compose(&sid, &global_sid_Unix_NFS_Users, fsp->fsp_name->st.st_ex_uid);
6259 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6260 status = security_descriptor_dacl_add(*ppdesc, &ace);
6261 if (!NT_STATUS_IS_OK(status)) {
6262 DEBUG(1,("failed to add MS NFS style ACE\n"));
6266 /* MS NFS style gid */
6267 sid_compose(&sid, &global_sid_Unix_NFS_Groups, fsp->fsp_name->st.st_ex_gid);
6268 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6269 status = security_descriptor_dacl_add(*ppdesc, &ace);
6270 if (!NT_STATUS_IS_OK(status)) {
6271 DEBUG(1,("failed to add MS NFS style ACE\n"));
6275 return NT_STATUS_OK;
6278 static NTSTATUS fruit_fset_nt_acl(vfs_handle_struct *handle,
6280 uint32_t security_info_sent,
6281 const struct security_descriptor *orig_psd)
6285 mode_t ms_nfs_mode = 0;
6287 struct security_descriptor *psd = NULL;
6288 uint32_t orig_num_aces = 0;
6290 if (orig_psd->dacl != NULL) {
6291 orig_num_aces = orig_psd->dacl->num_aces;
6294 psd = security_descriptor_copy(talloc_tos(), orig_psd);
6296 return NT_STATUS_NO_MEMORY;
6299 DBG_DEBUG("fruit_fset_nt_acl: %s\n", fsp_str_dbg(fsp));
6301 status = check_ms_nfs(handle, fsp, psd, &ms_nfs_mode, &do_chmod);
6302 if (!NT_STATUS_IS_OK(status)) {
6303 DEBUG(1, ("fruit_fset_nt_acl: check_ms_nfs failed%s\n", fsp_str_dbg(fsp)));
6309 * If only ms_nfs ACE entries were sent, ensure we set the DACL
6310 * sent/present flags correctly now we've removed them.
6313 if (orig_num_aces != 0) {
6315 * Are there any ACE's left ?
6317 if (psd->dacl->num_aces == 0) {
6318 /* No - clear the DACL sent/present flags. */
6319 security_info_sent &= ~SECINFO_DACL;
6320 psd->type &= ~SEC_DESC_DACL_PRESENT;
6324 status = SMB_VFS_NEXT_FSET_NT_ACL(handle, fsp, security_info_sent, psd);
6325 if (!NT_STATUS_IS_OK(status)) {
6326 DEBUG(1, ("fruit_fset_nt_acl: SMB_VFS_NEXT_FSET_NT_ACL failed%s\n", fsp_str_dbg(fsp)));
6332 if (fsp->fh->fd != -1) {
6333 result = SMB_VFS_FCHMOD(fsp, ms_nfs_mode);
6335 result = SMB_VFS_CHMOD(fsp->conn,
6341 DEBUG(1, ("chmod: %s, result: %d, %04o error %s\n", fsp_str_dbg(fsp),
6342 result, (unsigned)ms_nfs_mode,
6344 status = map_nt_error_from_unix(errno);
6351 return NT_STATUS_OK;
6354 static struct vfs_offload_ctx *fruit_offload_ctx;
6356 struct fruit_offload_read_state {
6357 struct vfs_handle_struct *handle;
6358 struct tevent_context *ev;
6364 static void fruit_offload_read_done(struct tevent_req *subreq);
6366 static struct tevent_req *fruit_offload_read_send(
6367 TALLOC_CTX *mem_ctx,
6368 struct tevent_context *ev,
6369 struct vfs_handle_struct *handle,
6376 struct tevent_req *req = NULL;
6377 struct tevent_req *subreq = NULL;
6378 struct fruit_offload_read_state *state = NULL;
6380 req = tevent_req_create(mem_ctx, &state,
6381 struct fruit_offload_read_state);
6385 *state = (struct fruit_offload_read_state) {
6392 subreq = SMB_VFS_NEXT_OFFLOAD_READ_SEND(mem_ctx, ev, handle, fsp,
6393 fsctl, ttl, offset, to_copy);
6394 if (tevent_req_nomem(subreq, req)) {
6395 return tevent_req_post(req, ev);
6397 tevent_req_set_callback(subreq, fruit_offload_read_done, req);
6401 static void fruit_offload_read_done(struct tevent_req *subreq)
6403 struct tevent_req *req = tevent_req_callback_data(
6404 subreq, struct tevent_req);
6405 struct fruit_offload_read_state *state = tevent_req_data(
6406 req, struct fruit_offload_read_state);
6409 status = SMB_VFS_NEXT_OFFLOAD_READ_RECV(subreq,
6413 TALLOC_FREE(subreq);
6414 if (tevent_req_nterror(req, status)) {
6418 if (state->fsctl != FSCTL_SRV_REQUEST_RESUME_KEY) {
6419 tevent_req_done(req);
6423 status = vfs_offload_token_ctx_init(state->fsp->conn->sconn->client,
6424 &fruit_offload_ctx);
6425 if (tevent_req_nterror(req, status)) {
6429 status = vfs_offload_token_db_store_fsp(fruit_offload_ctx,
6432 if (tevent_req_nterror(req, status)) {
6436 tevent_req_done(req);
6440 static NTSTATUS fruit_offload_read_recv(struct tevent_req *req,
6441 struct vfs_handle_struct *handle,
6442 TALLOC_CTX *mem_ctx,
6445 struct fruit_offload_read_state *state = tevent_req_data(
6446 req, struct fruit_offload_read_state);
6449 if (tevent_req_is_nterror(req, &status)) {
6450 tevent_req_received(req);
6454 token->length = state->token.length;
6455 token->data = talloc_move(mem_ctx, &state->token.data);
6457 tevent_req_received(req);
6458 return NT_STATUS_OK;
6461 struct fruit_offload_write_state {
6462 struct vfs_handle_struct *handle;
6464 struct files_struct *src_fsp;
6465 struct files_struct *dst_fsp;
6469 static void fruit_offload_write_done(struct tevent_req *subreq);
6470 static struct tevent_req *fruit_offload_write_send(struct vfs_handle_struct *handle,
6471 TALLOC_CTX *mem_ctx,
6472 struct tevent_context *ev,
6475 off_t transfer_offset,
6476 struct files_struct *dest_fsp,
6480 struct tevent_req *req, *subreq;
6481 struct fruit_offload_write_state *state;
6483 struct fruit_config_data *config;
6484 off_t src_off = transfer_offset;
6485 files_struct *src_fsp = NULL;
6486 off_t to_copy = num;
6487 bool copyfile_enabled = false;
6489 DEBUG(10,("soff: %ju, doff: %ju, len: %ju\n",
6490 (uintmax_t)src_off, (uintmax_t)dest_off, (uintmax_t)num));
6492 SMB_VFS_HANDLE_GET_DATA(handle, config,
6493 struct fruit_config_data,
6496 req = tevent_req_create(mem_ctx, &state,
6497 struct fruit_offload_write_state);
6501 state->handle = handle;
6502 state->dst_fsp = dest_fsp;
6505 case FSCTL_SRV_COPYCHUNK:
6506 case FSCTL_SRV_COPYCHUNK_WRITE:
6507 copyfile_enabled = config->copyfile_enabled;
6514 * Check if this a OS X copyfile style copychunk request with
6515 * a requested chunk count of 0 that was translated to a
6516 * offload_write_send VFS call overloading the parameters src_off
6517 * = dest_off = num = 0.
6519 if (copyfile_enabled && num == 0 && src_off == 0 && dest_off == 0) {
6520 status = vfs_offload_token_db_fetch_fsp(
6521 fruit_offload_ctx, token, &src_fsp);
6522 if (tevent_req_nterror(req, status)) {
6523 return tevent_req_post(req, ev);
6525 state->src_fsp = src_fsp;
6527 status = vfs_stat_fsp(src_fsp);
6528 if (tevent_req_nterror(req, status)) {
6529 return tevent_req_post(req, ev);
6532 to_copy = src_fsp->fsp_name->st.st_ex_size;
6533 state->is_copyfile = true;
6536 subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle,
6545 if (tevent_req_nomem(subreq, req)) {
6546 return tevent_req_post(req, ev);
6549 tevent_req_set_callback(subreq, fruit_offload_write_done, req);
6553 static void fruit_offload_write_done(struct tevent_req *subreq)
6555 struct tevent_req *req = tevent_req_callback_data(
6556 subreq, struct tevent_req);
6557 struct fruit_offload_write_state *state = tevent_req_data(
6558 req, struct fruit_offload_write_state);
6560 unsigned int num_streams = 0;
6561 struct stream_struct *streams = NULL;
6563 struct smb_filename *src_fname_tmp = NULL;
6564 struct smb_filename *dst_fname_tmp = NULL;
6566 status = SMB_VFS_NEXT_OFFLOAD_WRITE_RECV(state->handle,
6569 TALLOC_FREE(subreq);
6570 if (tevent_req_nterror(req, status)) {
6574 if (!state->is_copyfile) {
6575 tevent_req_done(req);
6580 * Now copy all remaining streams. We know the share supports
6581 * streams, because we're in vfs_fruit. We don't do this async
6582 * because streams are few and small.
6584 status = vfs_streaminfo(state->handle->conn, state->src_fsp,
6585 state->src_fsp->fsp_name,
6586 req, &num_streams, &streams);
6587 if (tevent_req_nterror(req, status)) {
6591 if (num_streams == 1) {
6592 /* There is always one stream, ::$DATA. */
6593 tevent_req_done(req);
6597 for (i = 0; i < num_streams; i++) {
6598 DEBUG(10, ("%s: stream: '%s'/%zu\n",
6599 __func__, streams[i].name, (size_t)streams[i].size));
6601 src_fname_tmp = synthetic_smb_fname(
6603 state->src_fsp->fsp_name->base_name,
6606 state->src_fsp->fsp_name->flags);
6607 if (tevent_req_nomem(src_fname_tmp, req)) {
6611 if (is_ntfs_default_stream_smb_fname(src_fname_tmp)) {
6612 TALLOC_FREE(src_fname_tmp);
6616 dst_fname_tmp = synthetic_smb_fname(
6618 state->dst_fsp->fsp_name->base_name,
6621 state->dst_fsp->fsp_name->flags);
6622 if (tevent_req_nomem(dst_fname_tmp, req)) {
6623 TALLOC_FREE(src_fname_tmp);
6627 status = copy_file(req,
6628 state->handle->conn,
6631 OPENX_FILE_CREATE_IF_NOT_EXIST,
6633 if (!NT_STATUS_IS_OK(status)) {
6634 DEBUG(1, ("%s: copy %s to %s failed: %s\n", __func__,
6635 smb_fname_str_dbg(src_fname_tmp),
6636 smb_fname_str_dbg(dst_fname_tmp),
6637 nt_errstr(status)));
6638 TALLOC_FREE(src_fname_tmp);
6639 TALLOC_FREE(dst_fname_tmp);
6640 tevent_req_nterror(req, status);
6644 TALLOC_FREE(src_fname_tmp);
6645 TALLOC_FREE(dst_fname_tmp);
6648 TALLOC_FREE(streams);
6649 TALLOC_FREE(src_fname_tmp);
6650 TALLOC_FREE(dst_fname_tmp);
6651 tevent_req_done(req);
6654 static NTSTATUS fruit_offload_write_recv(struct vfs_handle_struct *handle,
6655 struct tevent_req *req,
6658 struct fruit_offload_write_state *state = tevent_req_data(
6659 req, struct fruit_offload_write_state);
6662 if (tevent_req_is_nterror(req, &status)) {
6663 DEBUG(1, ("server side copy chunk failed: %s\n",
6664 nt_errstr(status)));
6666 tevent_req_received(req);
6670 *copied = state->copied;
6671 tevent_req_received(req);
6673 return NT_STATUS_OK;
6676 static char *fruit_get_bandsize_line(char **lines, int numlines)
6679 static bool re_initialized = false;
6683 if (!re_initialized) {
6684 ret = regcomp(&re, "^[[:blank:]]*<key>band-size</key>$", 0);
6688 re_initialized = true;
6691 for (i = 0; i < numlines; i++) {
6692 regmatch_t matches[1];
6694 ret = regexec(&re, lines[i], 1, matches, 0);
6697 * Check if the match was on the last line, sa we want
6698 * the subsequent line.
6700 if (i + 1 == numlines) {
6703 return lines[i + 1];
6705 if (ret != REG_NOMATCH) {
6713 static bool fruit_get_bandsize_from_line(char *line, size_t *_band_size)
6716 static bool re_initialized = false;
6717 regmatch_t matches[2];
6722 if (!re_initialized) {
6725 "<integer>\\([[:digit:]]*\\)</integer>$",
6730 re_initialized = true;
6733 ret = regexec(&re, line, 2, matches, 0);
6735 DBG_ERR("regex failed [%s]\n", line);
6739 line[matches[1].rm_eo] = '\0';
6741 ok = conv_str_u64(&line[matches[1].rm_so], &band_size);
6745 *_band_size = (size_t)band_size;
6750 * This reads and parses an Info.plist from a TM sparsebundle looking for the
6751 * "band-size" key and value.
6753 static bool fruit_get_bandsize(vfs_handle_struct *handle,
6757 #define INFO_PLIST_MAX_SIZE 64*1024
6759 struct smb_filename *smb_fname = NULL;
6760 files_struct *fsp = NULL;
6761 uint8_t *file_data = NULL;
6762 char **lines = NULL;
6763 char *band_size_line = NULL;
6764 size_t plist_file_size;
6771 plist = talloc_asprintf(talloc_tos(),
6773 handle->conn->connectpath,
6775 if (plist == NULL) {
6780 smb_fname = synthetic_smb_fname(talloc_tos(), plist, NULL, NULL, 0);
6781 if (smb_fname == NULL) {
6786 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
6788 DBG_INFO("Ignoring Sparsebundle without Info.plist [%s]\n", dir);
6793 plist_file_size = smb_fname->st.st_ex_size;
6795 if (plist_file_size > INFO_PLIST_MAX_SIZE) {
6796 DBG_INFO("%s is too large, ignoring\n", plist);
6801 status = SMB_VFS_NEXT_CREATE_FILE(
6804 0, /* root_dir_fid */
6805 smb_fname, /* fname */
6806 FILE_GENERIC_READ, /* access_mask */
6807 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
6808 FILE_OPEN, /* create_disposition */
6809 0, /* create_options */
6810 0, /* file_attributes */
6811 INTERNAL_OPEN_ONLY, /* oplock_request */
6813 0, /* allocation_size */
6814 0, /* private_flags */
6819 NULL, NULL); /* create context */
6820 if (!NT_STATUS_IS_OK(status)) {
6821 DBG_INFO("Opening [%s] failed [%s]\n",
6822 smb_fname_str_dbg(smb_fname), nt_errstr(status));
6827 file_data = talloc_array(talloc_tos(), uint8_t, plist_file_size);
6828 if (file_data == NULL) {
6833 nread = SMB_VFS_NEXT_PREAD(handle, fsp, file_data, plist_file_size, 0);
6834 if (nread != plist_file_size) {
6835 DBG_ERR("Short read on [%s]: %zu/%zd\n",
6836 fsp_str_dbg(fsp), nread, plist_file_size);
6842 status = close_file(NULL, fsp, NORMAL_CLOSE);
6844 if (!NT_STATUS_IS_OK(status)) {
6845 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
6850 lines = file_lines_parse((char *)file_data,
6854 if (lines == NULL) {
6859 band_size_line = fruit_get_bandsize_line(lines, numlines);
6860 if (band_size_line == NULL) {
6861 DBG_ERR("Didn't find band-size key in [%s]\n",
6862 smb_fname_str_dbg(smb_fname));
6867 ok = fruit_get_bandsize_from_line(band_size_line, band_size);
6869 DBG_ERR("fruit_get_bandsize_from_line failed\n");
6873 DBG_DEBUG("Parsed band-size [%zu] for [%s]\n", *band_size, plist);
6877 status = close_file(NULL, fsp, NORMAL_CLOSE);
6878 if (!NT_STATUS_IS_OK(status)) {
6879 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
6884 TALLOC_FREE(smb_fname);
6885 TALLOC_FREE(file_data);
6890 struct fruit_disk_free_state {
6894 static bool fruit_get_num_bands(vfs_handle_struct *handle,
6899 struct smb_filename *bands_dir = NULL;
6901 struct dirent *e = NULL;
6905 path = talloc_asprintf(talloc_tos(),
6907 handle->conn->connectpath,
6913 bands_dir = synthetic_smb_fname(talloc_tos(),
6919 if (bands_dir == NULL) {
6923 d = SMB_VFS_NEXT_OPENDIR(handle, bands_dir, NULL, 0);
6925 TALLOC_FREE(bands_dir);
6931 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
6933 e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
6935 if (ISDOT(e->d_name) || ISDOTDOT(e->d_name)) {
6941 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
6943 TALLOC_FREE(bands_dir);
6947 DBG_DEBUG("%zu bands in [%s]\n", nbands, smb_fname_str_dbg(bands_dir));
6949 TALLOC_FREE(bands_dir);
6955 static bool fruit_tmsize_do_dirent(vfs_handle_struct *handle,
6956 struct fruit_disk_free_state *state,
6961 size_t sparsebundle_strlen = strlen("sparsebundle");
6962 size_t bandsize = 0;
6966 p = strstr(e->d_name, "sparsebundle");
6971 if (p[sparsebundle_strlen] != '\0') {
6975 DBG_DEBUG("Processing sparsebundle [%s]\n", e->d_name);
6977 ok = fruit_get_bandsize(handle, e->d_name, &bandsize);
6980 * Beware of race conditions: this may be an uninitialized
6981 * Info.plist that a client is just creating. We don't want let
6982 * this to trigger complete failure.
6984 DBG_ERR("Processing sparsebundle [%s] failed\n", e->d_name);
6988 ok = fruit_get_num_bands(handle, e->d_name, &nbands);
6991 * Beware of race conditions: this may be a backup sparsebundle
6992 * in an early stage lacking a bands subdirectory. We don't want
6993 * let this to trigger complete failure.
6995 DBG_ERR("Processing sparsebundle [%s] failed\n", e->d_name);
6999 if (bandsize > SIZE_MAX/nbands) {
7000 DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
7004 tm_size = bandsize * nbands;
7006 if (state->total_size + tm_size < state->total_size) {
7007 DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
7012 state->total_size += tm_size;
7014 DBG_DEBUG("[%s] tm_size [%jd] total_size [%jd]\n",
7015 e->d_name, (intmax_t)tm_size, (intmax_t)state->total_size);
7021 * Calculate used size of a TimeMachine volume
7023 * This assumes that the volume is used only for TimeMachine.
7025 * - readdir(basedir of share), then
7026 * - for every element that matches regex "^\(.*\)\.sparsebundle$" :
7027 * - parse "\1.sparsebundle/Info.plist" and read the band-size XML key
7028 * - count band files in "\1.sparsebundle/bands/"
7029 * - calculate used size of all bands: band_count * band_size
7031 static uint64_t fruit_disk_free(vfs_handle_struct *handle,
7032 const struct smb_filename *smb_fname,
7037 struct fruit_config_data *config = NULL;
7038 struct fruit_disk_free_state state = {0};
7040 struct dirent *e = NULL;
7046 SMB_VFS_HANDLE_GET_DATA(handle, config,
7047 struct fruit_config_data,
7050 if (!config->time_machine ||
7051 config->time_machine_max_size == 0)
7053 return SMB_VFS_NEXT_DISK_FREE(handle,
7060 d = SMB_VFS_NEXT_OPENDIR(handle, smb_fname, NULL, 0);
7065 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
7067 e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
7069 ok = fruit_tmsize_do_dirent(handle, &state, e);
7071 SMB_VFS_NEXT_CLOSEDIR(handle, d);
7076 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
7081 dsize = config->time_machine_max_size / 512;
7082 dfree = dsize - (state.total_size / 512);
7083 if (dfree > dsize) {
7093 static struct vfs_fn_pointers vfs_fruit_fns = {
7094 .connect_fn = fruit_connect,
7095 .disk_free_fn = fruit_disk_free,
7097 /* File operations */
7098 .chmod_fn = fruit_chmod,
7099 .chown_fn = fruit_chown,
7100 .unlink_fn = fruit_unlink,
7101 .rename_fn = fruit_rename,
7102 .rmdir_fn = fruit_rmdir,
7103 .open_fn = fruit_open,
7104 .close_fn = fruit_close,
7105 .pread_fn = fruit_pread,
7106 .pwrite_fn = fruit_pwrite,
7107 .pread_send_fn = fruit_pread_send,
7108 .pread_recv_fn = fruit_pread_recv,
7109 .pwrite_send_fn = fruit_pwrite_send,
7110 .pwrite_recv_fn = fruit_pwrite_recv,
7111 .stat_fn = fruit_stat,
7112 .lstat_fn = fruit_lstat,
7113 .fstat_fn = fruit_fstat,
7114 .streaminfo_fn = fruit_streaminfo,
7115 .ntimes_fn = fruit_ntimes,
7116 .ftruncate_fn = fruit_ftruncate,
7117 .fallocate_fn = fruit_fallocate,
7118 .create_file_fn = fruit_create_file,
7119 .readdir_attr_fn = fruit_readdir_attr,
7120 .offload_read_send_fn = fruit_offload_read_send,
7121 .offload_read_recv_fn = fruit_offload_read_recv,
7122 .offload_write_send_fn = fruit_offload_write_send,
7123 .offload_write_recv_fn = fruit_offload_write_recv,
7125 /* NT ACL operations */
7126 .fget_nt_acl_fn = fruit_fget_nt_acl,
7127 .fset_nt_acl_fn = fruit_fset_nt_acl,
7131 NTSTATUS vfs_fruit_init(TALLOC_CTX *ctx)
7133 NTSTATUS ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "fruit",
7135 if (!NT_STATUS_IS_OK(ret)) {
7139 vfs_fruit_debug_level = debug_add_class("fruit");
7140 if (vfs_fruit_debug_level == -1) {
7141 vfs_fruit_debug_level = DBGC_VFS;
7142 DEBUG(0, ("%s: Couldn't register custom debugging class!\n",
7145 DEBUG(10, ("%s: Debug class number of '%s': %d\n",
7146 "vfs_fruit_init","fruit",vfs_fruit_debug_level));