2 * OS X and Netatalk interoperability VFS module for Samba-3.x
4 * Copyright (C) Ralph Boehme, 2013, 2014
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
21 #include "MacExtensions.h"
22 #include "smbd/smbd.h"
23 #include "system/filesys.h"
24 #include "lib/util/time.h"
25 #include "system/shmem.h"
26 #include "locking/proto.h"
27 #include "smbd/globals.h"
29 #include "libcli/security/security.h"
30 #include "../libcli/smb/smb2_create_ctx.h"
31 #include "lib/util/sys_rw.h"
32 #include "lib/util/tevent_ntstatus.h"
33 #include "lib/util/tevent_unix.h"
34 #include "offload_token.h"
35 #include "string_replace.h"
37 #include <gnutls/gnutls.h>
38 #include <gnutls/crypto.h>
41 * Enhanced OS X and Netatalk compatibility
42 * ========================================
44 * This modules takes advantage of vfs_streams_xattr and
45 * vfs_catia. VFS modules vfs_fruit and vfs_streams_xattr must be
46 * loaded in the correct order:
48 * vfs modules = catia fruit streams_xattr
50 * The module intercepts the OS X special streams "AFP_AfpInfo" and
51 * "AFP_Resource" and handles them in a special way. All other named
52 * streams are deferred to vfs_streams_xattr.
54 * The OS X client maps all NTFS illegal characters to the Unicode
55 * private range. This module optionally stores the charcters using
56 * their native ASCII encoding using vfs_catia. If you're not enabling
57 * this feature, you can skip catia from vfs modules.
59 * Finally, open modes are optionally checked against Netatalk AFP
62 * The "AFP_AfpInfo" named stream is a binary blob containing OS X
63 * extended metadata for files and directories. This module optionally
64 * reads and stores this metadata in a way compatible with Netatalk 3
65 * which stores the metadata in an EA "org.netatalk.metadata". Cf
66 * source3/include/MacExtensions.h for a description of the binary
69 * The "AFP_Resource" named stream may be arbitrarily large, thus it
70 * can't be stored in an xattr on most filesystem. ZFS on Solaris is
71 * the only available filesystem where xattrs can be of any size and
72 * the OS supports using the file APIs for xattrs.
74 * The AFP_Resource stream is stored in an AppleDouble file prepending
75 * "._" to the filename. On Solaris with ZFS the stream is optionally
76 * stored in an EA "org.netatalk.resource".
82 * The OS X SMB client sends xattrs as ADS too. For xattr interop with
83 * other protocols you may want to adjust the xattr names the VFS
84 * module vfs_streams_xattr uses for storing ADS's. This defaults to
85 * user.DosStream.ADS_NAME:$DATA and can be changed by specifying
86 * these module parameters:
88 * streams_xattr:prefix = user.
89 * streams_xattr:store_stream_type = false
95 * - log diagnostic if any needed VFS module is not loaded
96 * (eg with lp_vfs_objects())
100 static int vfs_fruit_debug_level = DBGC_VFS;
102 static struct global_fruit_config {
103 bool nego_aapl; /* client negotiated AAPL */
105 } global_fruit_config;
108 #define DBGC_CLASS vfs_fruit_debug_level
110 #define FRUIT_PARAM_TYPE_NAME "fruit"
111 #define ADOUBLE_NAME_PREFIX "._"
113 #define NETATALK_META_XATTR "org.netatalk.Metadata"
114 #define NETATALK_RSRC_XATTR "org.netatalk.ResourceFork"
116 #if defined(HAVE_ATTROPEN)
117 #define AFPINFO_EA_NETATALK NETATALK_META_XATTR
118 #define AFPRESOURCE_EA_NETATALK NETATALK_RSRC_XATTR
120 #define AFPINFO_EA_NETATALK "user." NETATALK_META_XATTR
121 #define AFPRESOURCE_EA_NETATALK "user." NETATALK_RSRC_XATTR
124 enum apple_fork {APPLE_FORK_DATA, APPLE_FORK_RSRC};
126 enum fruit_rsrc {FRUIT_RSRC_STREAM, FRUIT_RSRC_ADFILE, FRUIT_RSRC_XATTR};
127 enum fruit_meta {FRUIT_META_STREAM, FRUIT_META_NETATALK};
128 enum fruit_locking {FRUIT_LOCKING_NETATALK, FRUIT_LOCKING_NONE};
129 enum fruit_encoding {FRUIT_ENC_NATIVE, FRUIT_ENC_PRIVATE};
131 struct fruit_config_data {
132 enum fruit_rsrc rsrc;
133 enum fruit_meta meta;
134 enum fruit_locking locking;
135 enum fruit_encoding encoding;
136 bool use_aapl; /* config from smb.conf */
138 bool readdir_attr_enabled;
139 bool unix_info_enabled;
140 bool copyfile_enabled;
141 bool veto_appledouble;
143 bool aapl_zero_file_id;
146 off_t time_machine_max_size;
147 bool wipe_intentionally_left_blank_rfork;
148 bool delete_empty_adfiles;
151 * Additional options, all enabled by default,
152 * possibly useful for analyzing performance. The associated
153 * operations with each of them may be expensive, so having
154 * the chance to disable them individually gives a chance
155 * tweaking the setup for the particular usecase.
157 bool readdir_attr_rsize;
158 bool readdir_attr_finder_info;
159 bool readdir_attr_max_access;
162 static const struct enum_list fruit_rsrc[] = {
163 {FRUIT_RSRC_STREAM, "stream"}, /* pass on to vfs_streams_xattr */
164 {FRUIT_RSRC_ADFILE, "file"}, /* ._ AppleDouble file */
165 {FRUIT_RSRC_XATTR, "xattr"}, /* Netatalk compatible xattr (ZFS only) */
169 static const struct enum_list fruit_meta[] = {
170 {FRUIT_META_STREAM, "stream"}, /* pass on to vfs_streams_xattr */
171 {FRUIT_META_NETATALK, "netatalk"}, /* Netatalk compatible xattr */
175 static const struct enum_list fruit_locking[] = {
176 {FRUIT_LOCKING_NETATALK, "netatalk"}, /* synchronize locks with Netatalk */
177 {FRUIT_LOCKING_NONE, "none"},
181 static const struct enum_list fruit_encoding[] = {
182 {FRUIT_ENC_NATIVE, "native"}, /* map unicode private chars to ASCII */
183 {FRUIT_ENC_PRIVATE, "private"}, /* keep unicode private chars */
187 static const char *fruit_catia_maps =
188 "0x01:0xf001,0x02:0xf002,0x03:0xf003,0x04:0xf004,"
189 "0x05:0xf005,0x06:0xf006,0x07:0xf007,0x08:0xf008,"
190 "0x09:0xf009,0x0a:0xf00a,0x0b:0xf00b,0x0c:0xf00c,"
191 "0x0d:0xf00d,0x0e:0xf00e,0x0f:0xf00f,0x10:0xf010,"
192 "0x11:0xf011,0x12:0xf012,0x13:0xf013,0x14:0xf014,"
193 "0x15:0xf015,0x16:0xf016,0x17:0xf017,0x18:0xf018,"
194 "0x19:0xf019,0x1a:0xf01a,0x1b:0xf01b,0x1c:0xf01c,"
195 "0x1d:0xf01d,0x1e:0xf01e,0x1f:0xf01f,"
196 "0x22:0xf020,0x2a:0xf021,0x3a:0xf022,0x3c:0xf023,"
197 "0x3e:0xf024,0x3f:0xf025,0x5c:0xf026,0x7c:0xf027,"
200 /*****************************************************************************
201 * Defines, functions and data structures that deal with AppleDouble
202 *****************************************************************************/
205 * There are two AppleDouble blobs we deal with:
207 * - ADOUBLE_META - AppleDouble blob used by Netatalk for storing
208 * metadata in an xattr
210 * - ADOUBLE_RSRC - AppleDouble blob used by OS X and Netatalk in
213 typedef enum {ADOUBLE_META, ADOUBLE_RSRC} adouble_type_t;
216 #define AD_VERSION2 0x00020000
217 #define AD_VERSION AD_VERSION2
220 * AppleDouble entry IDs.
222 #define ADEID_DFORK 1
223 #define ADEID_RFORK 2
225 #define ADEID_COMMENT 4
226 #define ADEID_ICONBW 5
227 #define ADEID_ICONCOL 6
228 #define ADEID_FILEI 7
229 #define ADEID_FILEDATESI 8
230 #define ADEID_FINDERI 9
231 #define ADEID_MACFILEI 10
232 #define ADEID_PRODOSFILEI 11
233 #define ADEID_MSDOSFILEI 12
234 #define ADEID_SHORTNAME 13
235 #define ADEID_AFPFILEI 14
238 /* Private Netatalk entries */
239 #define ADEID_PRIVDEV 16
240 #define ADEID_PRIVINO 17
241 #define ADEID_PRIVSYN 18
242 #define ADEID_PRIVID 19
243 #define ADEID_MAX (ADEID_PRIVID + 1)
246 * These are the real ids for the private entries,
247 * as stored in the adouble file
249 #define AD_DEV 0x80444556
250 #define AD_INO 0x80494E4F
251 #define AD_SYN 0x8053594E
252 #define AD_ID 0x8053567E
254 /* Number of actually used entries */
255 #define ADEID_NUM_XATTR 8
256 #define ADEID_NUM_DOT_UND 2
257 #define ADEID_NUM_RSRC_XATTR 1
259 /* AppleDouble magic */
260 #define AD_APPLESINGLE_MAGIC 0x00051600
261 #define AD_APPLEDOUBLE_MAGIC 0x00051607
262 #define AD_MAGIC AD_APPLEDOUBLE_MAGIC
264 /* Sizes of relevant entry bits */
265 #define ADEDLEN_MAGIC 4
266 #define ADEDLEN_VERSION 4
267 #define ADEDLEN_FILLER 16
268 #define AD_FILLER_TAG "Netatalk " /* should be 16 bytes */
269 #define AD_FILLER_TAG_OSX "Mac OS X " /* should be 16 bytes */
270 #define ADEDLEN_NENTRIES 2
271 #define AD_HEADER_LEN (ADEDLEN_MAGIC + ADEDLEN_VERSION + \
272 ADEDLEN_FILLER + ADEDLEN_NENTRIES) /* 26 */
273 #define AD_ENTRY_LEN_EID 4
274 #define AD_ENTRY_LEN_OFF 4
275 #define AD_ENTRY_LEN_LEN 4
276 #define AD_ENTRY_LEN (AD_ENTRY_LEN_EID + AD_ENTRY_LEN_OFF + AD_ENTRY_LEN_LEN)
279 #define ADEDLEN_NAME 255
280 #define ADEDLEN_COMMENT 200
281 #define ADEDLEN_FILEI 16
282 #define ADEDLEN_FINDERI 32
283 #define ADEDLEN_FILEDATESI 16
284 #define ADEDLEN_SHORTNAME 12 /* length up to 8.3 */
285 #define ADEDLEN_AFPFILEI 4
286 #define ADEDLEN_MACFILEI 4
287 #define ADEDLEN_PRODOSFILEI 8
288 #define ADEDLEN_MSDOSFILEI 2
289 #define ADEDLEN_DID 4
290 #define ADEDLEN_PRIVDEV 8
291 #define ADEDLEN_PRIVINO 8
292 #define ADEDLEN_PRIVSYN 8
293 #define ADEDLEN_PRIVID 4
296 #define ADEDOFF_MAGIC 0
297 #define ADEDOFF_VERSION (ADEDOFF_MAGIC + ADEDLEN_MAGIC)
298 #define ADEDOFF_FILLER (ADEDOFF_VERSION + ADEDLEN_VERSION)
299 #define ADEDOFF_NENTRIES (ADEDOFF_FILLER + ADEDLEN_FILLER)
301 #define ADEDOFF_FINDERI_XATTR (AD_HEADER_LEN + \
302 (ADEID_NUM_XATTR * AD_ENTRY_LEN))
303 #define ADEDOFF_COMMENT_XATTR (ADEDOFF_FINDERI_XATTR + ADEDLEN_FINDERI)
304 #define ADEDOFF_FILEDATESI_XATTR (ADEDOFF_COMMENT_XATTR + ADEDLEN_COMMENT)
305 #define ADEDOFF_AFPFILEI_XATTR (ADEDOFF_FILEDATESI_XATTR + \
307 #define ADEDOFF_PRIVDEV_XATTR (ADEDOFF_AFPFILEI_XATTR + ADEDLEN_AFPFILEI)
308 #define ADEDOFF_PRIVINO_XATTR (ADEDOFF_PRIVDEV_XATTR + ADEDLEN_PRIVDEV)
309 #define ADEDOFF_PRIVSYN_XATTR (ADEDOFF_PRIVINO_XATTR + ADEDLEN_PRIVINO)
310 #define ADEDOFF_PRIVID_XATTR (ADEDOFF_PRIVSYN_XATTR + ADEDLEN_PRIVSYN)
312 #define ADEDOFF_FINDERI_DOT_UND (AD_HEADER_LEN + \
313 (ADEID_NUM_DOT_UND * AD_ENTRY_LEN))
314 #define ADEDOFF_RFORK_DOT_UND (ADEDOFF_FINDERI_DOT_UND + ADEDLEN_FINDERI)
316 #define AD_DATASZ_XATTR (AD_HEADER_LEN + \
317 (ADEID_NUM_XATTR * AD_ENTRY_LEN) + \
318 ADEDLEN_FINDERI + ADEDLEN_COMMENT + \
319 ADEDLEN_FILEDATESI + ADEDLEN_AFPFILEI + \
320 ADEDLEN_PRIVDEV + ADEDLEN_PRIVINO + \
321 ADEDLEN_PRIVSYN + ADEDLEN_PRIVID)
323 #if AD_DATASZ_XATTR != 402
324 #error bad size for AD_DATASZ_XATTR
327 #define AD_DATASZ_DOT_UND (AD_HEADER_LEN + \
328 (ADEID_NUM_DOT_UND * AD_ENTRY_LEN) + \
330 #if AD_DATASZ_DOT_UND != 82
331 #error bad size for AD_DATASZ_DOT_UND
335 * Sharemode locks fcntl() offsets
337 #if _FILE_OFFSET_BITS == 64 || defined(HAVE_LARGEFILE)
338 #define AD_FILELOCK_BASE (UINT64_C(0x7FFFFFFFFFFFFFFF) - 9)
340 #define AD_FILELOCK_BASE (UINT32_C(0x7FFFFFFF) - 9)
342 #define BYTELOCK_MAX (AD_FILELOCK_BASE - 1)
344 #define AD_FILELOCK_OPEN_WR (AD_FILELOCK_BASE + 0)
345 #define AD_FILELOCK_OPEN_RD (AD_FILELOCK_BASE + 1)
346 #define AD_FILELOCK_RSRC_OPEN_WR (AD_FILELOCK_BASE + 2)
347 #define AD_FILELOCK_RSRC_OPEN_RD (AD_FILELOCK_BASE + 3)
348 #define AD_FILELOCK_DENY_WR (AD_FILELOCK_BASE + 4)
349 #define AD_FILELOCK_DENY_RD (AD_FILELOCK_BASE + 5)
350 #define AD_FILELOCK_RSRC_DENY_WR (AD_FILELOCK_BASE + 6)
351 #define AD_FILELOCK_RSRC_DENY_RD (AD_FILELOCK_BASE + 7)
352 #define AD_FILELOCK_OPEN_NONE (AD_FILELOCK_BASE + 8)
353 #define AD_FILELOCK_RSRC_OPEN_NONE (AD_FILELOCK_BASE + 9)
355 /* Time stuff we overload the bits a little */
356 #define AD_DATE_CREATE 0
357 #define AD_DATE_MODIFY 4
358 #define AD_DATE_BACKUP 8
359 #define AD_DATE_ACCESS 12
360 #define AD_DATE_MASK (AD_DATE_CREATE | AD_DATE_MODIFY | \
361 AD_DATE_BACKUP | AD_DATE_ACCESS)
362 #define AD_DATE_UNIX (1 << 10)
363 #define AD_DATE_START 0x80000000
364 #define AD_DATE_DELTA 946684800
365 #define AD_DATE_FROM_UNIX(x) (htonl((x) - AD_DATE_DELTA))
366 #define AD_DATE_TO_UNIX(x) (ntohl(x) + AD_DATE_DELTA)
368 #define AD_XATTR_HDR_MAGIC 0x41545452 /* 'ATTR' */
369 #define AD_XATTR_MAX_ENTRIES 1024 /* Some arbitrarily enforced limit */
370 #define AD_XATTR_HDR_SIZE 36
371 #define AD_XATTR_MAX_HDR_SIZE 65536
373 /* Accessor macros */
374 #define ad_getentrylen(ad,eid) ((ad)->ad_eid[(eid)].ade_len)
375 #define ad_getentryoff(ad,eid) ((ad)->ad_eid[(eid)].ade_off)
376 #define ad_setentrylen(ad,eid,len) ((ad)->ad_eid[(eid)].ade_len = (len))
377 #define ad_setentryoff(ad,eid,off) ((ad)->ad_eid[(eid)].ade_off = (off))
380 * Both struct ad_xattr_header and struct ad_xattr_entry describe the in memory
381 * representation as well as the on-disk format.
383 * The ad_xattr_header follows the FinderInfo data in the FinderInfo entry if
384 * the length of the FinderInfo entry is larger then 32 bytes. It is then
385 * preceeded with 2 bytes padding.
387 * Cf: https://opensource.apple.com/source/xnu/xnu-4570.1.46/bsd/vfs/vfs_xattr.c
390 struct ad_xattr_header {
391 uint32_t adx_magic; /* ATTR_HDR_MAGIC */
392 uint32_t adx_debug_tag; /* for debugging == file id of owning file */
393 uint32_t adx_total_size; /* file offset of end of attribute header + entries + data */
394 uint32_t adx_data_start; /* file offset to attribute data area */
395 uint32_t adx_data_length; /* length of attribute data area */
396 uint32_t adx_reserved[3];
398 uint16_t adx_num_attrs;
401 /* On-disk entries are aligned on 4 byte boundaries */
402 struct ad_xattr_entry {
403 uint32_t adx_offset; /* file offset to data */
404 uint32_t adx_length; /* size of attribute data */
406 uint8_t adx_namelen; /* included the NULL terminator */
407 char *adx_name; /* NULL-terminated UTF-8 name */
416 files_struct *ad_fsp;
418 adouble_type_t ad_type;
421 uint8_t ad_filler[ADEDLEN_FILLER];
422 struct ad_entry ad_eid[ADEID_MAX];
424 struct ad_xattr_header adx_header;
425 struct ad_xattr_entry *adx_entries;
428 struct ad_entry_order {
429 uint32_t id, offset, len;
432 /* Netatalk AppleDouble metadata xattr */
434 struct ad_entry_order entry_order_meta_xattr[ADEID_NUM_XATTR + 1] = {
435 {ADEID_FINDERI, ADEDOFF_FINDERI_XATTR, ADEDLEN_FINDERI},
436 {ADEID_COMMENT, ADEDOFF_COMMENT_XATTR, 0},
437 {ADEID_FILEDATESI, ADEDOFF_FILEDATESI_XATTR, ADEDLEN_FILEDATESI},
438 {ADEID_AFPFILEI, ADEDOFF_AFPFILEI_XATTR, ADEDLEN_AFPFILEI},
439 {ADEID_PRIVDEV, ADEDOFF_PRIVDEV_XATTR, 0},
440 {ADEID_PRIVINO, ADEDOFF_PRIVINO_XATTR, 0},
441 {ADEID_PRIVSYN, ADEDOFF_PRIVSYN_XATTR, 0},
442 {ADEID_PRIVID, ADEDOFF_PRIVID_XATTR, 0},
446 /* AppleDouble resource fork file (the ones prefixed by "._") */
448 struct ad_entry_order entry_order_dot_und[ADEID_NUM_DOT_UND + 1] = {
449 {ADEID_FINDERI, ADEDOFF_FINDERI_DOT_UND, ADEDLEN_FINDERI},
450 {ADEID_RFORK, ADEDOFF_RFORK_DOT_UND, 0},
454 /* Conversion from enumerated id to on-disk AppleDouble id */
455 #define AD_EID_DISK(a) (set_eid[a])
456 static const uint32_t set_eid[] = {
457 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
458 AD_DEV, AD_INO, AD_SYN, AD_ID
461 static char empty_resourcefork[] = {
462 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
463 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
464 0x54, 0x68, 0x69, 0x73, 0x20, 0x72, 0x65, 0x73,
465 0x6F, 0x75, 0x72, 0x63, 0x65, 0x20, 0x66, 0x6F,
466 0x72, 0x6B, 0x20, 0x69, 0x6E, 0x74, 0x65, 0x6E,
467 0x74, 0x69, 0x6F, 0x6E, 0x61, 0x6C, 0x6C, 0x79,
468 0x20, 0x6C, 0x65, 0x66, 0x74, 0x20, 0x62, 0x6C,
469 0x61, 0x6E, 0x6B, 0x20, 0x20, 0x20, 0x00, 0x00,
470 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
471 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
472 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
473 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
474 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
475 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
476 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
477 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
478 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
479 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
480 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
481 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
482 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
483 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
484 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
485 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
486 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
487 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
488 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
489 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
490 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
491 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
492 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
493 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
494 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
495 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
496 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
497 0x00, 0x1C, 0x00, 0x1E, 0xFF, 0xFF
501 /* tcon config handle */
502 struct fruit_config_data *config;
504 /* Denote stream type, meta or rsrc */
507 /* Whether the create created the stream */
511 * AFP_AfpInfo stream created, but not written yet, thus still a fake
512 * pipe fd. This is set to true in fruit_open_meta if there was no
513 * exisiting stream but the caller requested O_CREAT. It is later set to
514 * false when we get a write on the stream that then does open and
523 * Forward declarations
525 static struct adouble *ad_init(TALLOC_CTX *ctx,
526 adouble_type_t type);
527 static struct adouble *ad_get(TALLOC_CTX *ctx,
528 vfs_handle_struct *handle,
529 const struct smb_filename *smb_fname,
530 adouble_type_t type);
531 static int ad_set(vfs_handle_struct *handle,
533 const struct smb_filename *smb_fname);
534 static int ad_fset(struct vfs_handle_struct *handle,
537 static int adouble_path(TALLOC_CTX *ctx,
538 const struct smb_filename *smb_fname__in,
539 struct smb_filename **ppsmb_fname_out);
540 static AfpInfo *afpinfo_new(TALLOC_CTX *ctx);
541 static ssize_t afpinfo_pack(const AfpInfo *ai, char *buf);
542 static AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data);
546 * Return a pointer to an AppleDouble entry
548 * Returns NULL if the entry is not present
550 static char *ad_get_entry(const struct adouble *ad, int eid)
552 off_t off = ad_getentryoff(ad, eid);
553 size_t len = ad_getentrylen(ad, eid);
555 if (off == 0 || len == 0) {
559 return ad->ad_data + off;
565 static int ad_getdate(const struct adouble *ad,
566 unsigned int dateoff,
569 bool xlate = (dateoff & AD_DATE_UNIX);
572 dateoff &= AD_DATE_MASK;
573 p = ad_get_entry(ad, ADEID_FILEDATESI);
578 if (dateoff > AD_DATE_ACCESS) {
582 memcpy(date, p + dateoff, sizeof(uint32_t));
585 *date = AD_DATE_TO_UNIX(*date);
593 static int ad_setdate(struct adouble *ad, unsigned int dateoff, uint32_t date)
595 bool xlate = (dateoff & AD_DATE_UNIX);
598 p = ad_get_entry(ad, ADEID_FILEDATESI);
603 dateoff &= AD_DATE_MASK;
605 date = AD_DATE_FROM_UNIX(date);
608 if (dateoff > AD_DATE_ACCESS) {
612 memcpy(p + dateoff, &date, sizeof(date));
619 * Map on-disk AppleDouble id to enumerated id
621 static uint32_t get_eid(uint32_t eid)
629 return ADEID_PRIVDEV;
631 return ADEID_PRIVINO;
633 return ADEID_PRIVSYN;
644 * Pack AppleDouble structure into data buffer
646 static bool ad_pack(struct adouble *ad)
653 bufsize = talloc_get_size(ad->ad_data);
654 if (bufsize < AD_DATASZ_DOT_UND) {
655 DBG_ERR("bad buffer size [0x%" PRIx32 "]\n", bufsize);
659 if (offset + ADEDLEN_MAGIC < offset ||
660 offset + ADEDLEN_MAGIC >= bufsize) {
663 RSIVAL(ad->ad_data, offset, ad->ad_magic);
664 offset += ADEDLEN_MAGIC;
666 if (offset + ADEDLEN_VERSION < offset ||
667 offset + ADEDLEN_VERSION >= bufsize) {
670 RSIVAL(ad->ad_data, offset, ad->ad_version);
671 offset += ADEDLEN_VERSION;
673 if (offset + ADEDLEN_FILLER < offset ||
674 offset + ADEDLEN_FILLER >= bufsize) {
677 if (ad->ad_type == ADOUBLE_RSRC) {
678 memcpy(ad->ad_data + offset, AD_FILLER_TAG, ADEDLEN_FILLER);
680 offset += ADEDLEN_FILLER;
682 if (offset + ADEDLEN_NENTRIES < offset ||
683 offset + ADEDLEN_NENTRIES >= bufsize) {
686 offset += ADEDLEN_NENTRIES;
688 for (eid = 0, nent = 0; eid < ADEID_MAX; eid++) {
689 if (ad->ad_eid[eid].ade_off == 0) {
691 * ade_off is also used as indicator whether a
692 * specific entry is used or not
697 if (offset + AD_ENTRY_LEN_EID < offset ||
698 offset + AD_ENTRY_LEN_EID >= bufsize) {
701 RSIVAL(ad->ad_data, offset, AD_EID_DISK(eid));
702 offset += AD_ENTRY_LEN_EID;
704 if (offset + AD_ENTRY_LEN_OFF < offset ||
705 offset + AD_ENTRY_LEN_OFF >= bufsize) {
708 RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_off);
709 offset += AD_ENTRY_LEN_OFF;
711 if (offset + AD_ENTRY_LEN_LEN < offset ||
712 offset + AD_ENTRY_LEN_LEN >= bufsize) {
715 RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_len);
716 offset += AD_ENTRY_LEN_LEN;
721 if (ADEDOFF_NENTRIES + 2 >= bufsize) {
724 RSSVAL(ad->ad_data, ADEDOFF_NENTRIES, nent);
729 static bool ad_unpack_xattrs(struct adouble *ad)
731 struct ad_xattr_header *h = &ad->adx_header;
732 const char *p = ad->ad_data;
736 if (ad_getentrylen(ad, ADEID_FINDERI) <= ADEDLEN_FINDERI) {
740 /* 2 bytes padding */
741 hoff = ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI + 2;
743 h->adx_magic = RIVAL(p, hoff + 0);
744 h->adx_debug_tag = RIVAL(p, hoff + 4); /* Not used -> not checked */
745 h->adx_total_size = RIVAL(p, hoff + 8);
746 h->adx_data_start = RIVAL(p, hoff + 12);
747 h->adx_data_length = RIVAL(p, hoff + 16);
748 h->adx_flags = RSVAL(p, hoff + 32); /* Not used -> not checked */
749 h->adx_num_attrs = RSVAL(p, hoff + 34);
751 if (h->adx_magic != AD_XATTR_HDR_MAGIC) {
752 DBG_ERR("Bad magic: 0x%" PRIx32 "\n", h->adx_magic);
756 if (h->adx_total_size > ad_getentryoff(ad, ADEID_RFORK)) {
757 DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size);
760 if (h->adx_total_size > AD_XATTR_MAX_HDR_SIZE) {
761 DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size);
765 if (h->adx_data_start < (hoff + AD_XATTR_HDR_SIZE)) {
766 DBG_ERR("Bad start: 0x%" PRIx32 "\n", h->adx_data_start);
770 if ((h->adx_data_start + h->adx_data_length) < h->adx_data_start) {
771 DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length);
774 if ((h->adx_data_start + h->adx_data_length) >
775 ad->adx_header.adx_total_size)
777 DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length);
781 if (h->adx_num_attrs > AD_XATTR_MAX_ENTRIES) {
782 DBG_ERR("Bad num xattrs: %" PRIu16 "\n", h->adx_num_attrs);
786 if (h->adx_num_attrs == 0) {
790 ad->adx_entries = talloc_zero_array(
791 ad, struct ad_xattr_entry, h->adx_num_attrs);
792 if (ad->adx_entries == NULL) {
796 hoff += AD_XATTR_HDR_SIZE;
798 for (i = 0; i < h->adx_num_attrs; i++) {
799 struct ad_xattr_entry *e = &ad->adx_entries[i];
801 hoff = (hoff + 3) & ~3;
803 e->adx_offset = RIVAL(p, hoff + 0);
804 e->adx_length = RIVAL(p, hoff + 4);
805 e->adx_flags = RSVAL(p, hoff + 8);
806 e->adx_namelen = *(p + hoff + 10);
808 if (e->adx_offset >= ad->adx_header.adx_total_size) {
809 DBG_ERR("Bad adx_offset: %" PRIx32 "\n",
814 if ((e->adx_offset + e->adx_length) < e->adx_offset) {
815 DBG_ERR("Bad adx_length: %" PRIx32 "\n",
820 if ((e->adx_offset + e->adx_length) >
821 ad->adx_header.adx_total_size)
823 DBG_ERR("Bad adx_length: %" PRIx32 "\n",
828 if (e->adx_namelen == 0) {
829 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
833 if ((hoff + 11 + e->adx_namelen) < hoff + 11) {
834 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
838 if ((hoff + 11 + e->adx_namelen) >
839 ad->adx_header.adx_data_start)
841 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
846 e->adx_name = talloc_strndup(ad->adx_entries,
849 if (e->adx_name == NULL) {
853 DBG_DEBUG("xattr [%s] offset [0x%x] size [0x%x]\n",
854 e->adx_name, e->adx_offset, e->adx_length);
855 dump_data(10, (uint8_t *)(ad->ad_data + e->adx_offset),
858 hoff += 11 + e->adx_namelen;
865 * Unpack an AppleDouble blob into a struct adoble
867 static bool ad_unpack(struct adouble *ad, const size_t nentries,
870 size_t bufsize = talloc_get_size(ad->ad_data);
872 uint32_t eid, len, off;
876 * The size of the buffer ad->ad_data is checked when read, so
877 * we wouldn't have to check our own offsets, a few extra
878 * checks won't hurt though. We have to check the offsets we
879 * read from the buffer anyway.
882 if (bufsize < (AD_HEADER_LEN + (AD_ENTRY_LEN * nentries))) {
883 DEBUG(1, ("bad size\n"));
887 ad->ad_magic = RIVAL(ad->ad_data, 0);
888 ad->ad_version = RIVAL(ad->ad_data, ADEDOFF_VERSION);
889 if ((ad->ad_magic != AD_MAGIC) || (ad->ad_version != AD_VERSION)) {
890 DEBUG(1, ("wrong magic or version\n"));
894 memcpy(ad->ad_filler, ad->ad_data + ADEDOFF_FILLER, ADEDLEN_FILLER);
896 adentries = RSVAL(ad->ad_data, ADEDOFF_NENTRIES);
897 if (adentries != nentries) {
898 DEBUG(1, ("invalid number of entries: %zu\n",
903 /* now, read in the entry bits */
904 for (i = 0; i < adentries; i++) {
905 eid = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN));
907 off = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 4);
908 len = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 8);
910 if (!eid || eid >= ADEID_MAX) {
911 DEBUG(1, ("bogus eid %d\n", eid));
916 * All entries other than the resource fork are
917 * expected to be read into the ad_data buffer, so
918 * ensure the specified offset is within that bound
920 if ((off > bufsize) && (eid != ADEID_RFORK)) {
921 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
927 * All entries besides FinderInfo and resource fork
928 * must fit into the buffer. FinderInfo is special as
929 * it may be larger then the default 32 bytes (if it
930 * contains marshalled xattrs), but we will fixup that
931 * in ad_convert(). And the resource fork is never
932 * accessed directly by the ad_data buf (also see
933 * comment above) anyway.
935 if ((eid != ADEID_RFORK) &&
936 (eid != ADEID_FINDERI) &&
937 ((off + len) > bufsize)) {
938 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
944 * That would be obviously broken
946 if (off > filesize) {
947 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
953 * Check for any entry that has its end beyond the
956 if (off + len < off) {
957 DEBUG(1, ("offset wrap in eid %d: off: %" PRIu32
958 ", len: %" PRIu32 "\n",
963 if (off + len > filesize) {
965 * If this is the resource fork entry, we fix
966 * up the length, for any other entry we bail
969 if (eid != ADEID_RFORK) {
970 DEBUG(1, ("bogus eid %d: off: %" PRIu32
971 ", len: %" PRIu32 "\n",
977 * Fixup the resource fork entry by limiting
978 * the size to entryoffset - filesize.
980 len = filesize - off;
981 DEBUG(1, ("Limiting ADEID_RFORK: off: %" PRIu32
982 ", len: %" PRIu32 "\n", off, len));
985 ad->ad_eid[eid].ade_off = off;
986 ad->ad_eid[eid].ade_len = len;
989 ok = ad_unpack_xattrs(ad);
997 static bool ad_convert_move_reso(vfs_handle_struct *handle,
999 const struct smb_filename *smb_fname)
1008 rforklen = ad_getentrylen(ad, ADEID_RFORK);
1009 if (rforklen == 0) {
1013 buf = talloc_size(ad, rforklen);
1016 * This allocates a buffer for reading the resource fork data in
1017 * one big swoop. Resource forks won't be larger then, say, 64
1018 * MB, I swear, so just doing the allocation with the talloc
1019 * limit as safeguard seems safe.
1021 DBG_ERR("Failed to allocate %zu bytes for rfork\n",
1026 rforkoff = ad_getentryoff(ad, ADEID_RFORK);
1028 n = SMB_VFS_PREAD(ad->ad_fsp, buf, rforklen, rforkoff);
1029 if (n != rforklen) {
1030 DBG_ERR("Reading %zu bytes from rfork [%s] failed: %s\n",
1031 rforklen, fsp_str_dbg(ad->ad_fsp), strerror(errno));
1035 rforkoff = ADEDOFF_RFORK_DOT_UND;
1037 n = SMB_VFS_PWRITE(ad->ad_fsp, buf, rforklen, rforkoff);
1038 if (n != rforklen) {
1039 DBG_ERR("Writing %zu bytes to rfork [%s] failed: %s\n",
1040 rforklen, fsp_str_dbg(ad->ad_fsp), strerror(errno));
1044 ad_setentryoff(ad, ADEID_RFORK, ADEDOFF_RFORK_DOT_UND);
1047 DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name);
1051 ret = ad_fset(handle, ad, ad->ad_fsp);
1053 DBG_ERR("ad_fset on [%s] failed\n", fsp_str_dbg(ad->ad_fsp));
1060 static bool ad_convert_xattr(vfs_handle_struct *handle,
1062 const struct smb_filename *smb_fname,
1063 bool *converted_xattr)
1065 static struct char_mappings **string_replace_cmaps = NULL;
1067 int saved_errno = 0;
1072 *converted_xattr = false;
1074 if (ad_getentrylen(ad, ADEID_FINDERI) == ADEDLEN_FINDERI) {
1078 if (string_replace_cmaps == NULL) {
1079 const char **mappings = NULL;
1081 mappings = str_list_make_v3_const(
1082 talloc_tos(), fruit_catia_maps, NULL);
1083 if (mappings == NULL) {
1086 string_replace_cmaps = string_replace_init_map(mappings);
1087 TALLOC_FREE(mappings);
1090 for (i = 0; i < ad->adx_header.adx_num_attrs; i++) {
1091 struct ad_xattr_entry *e = &ad->adx_entries[i];
1092 char *mapped_name = NULL;
1094 struct smb_filename *stream_name = NULL;
1095 files_struct *fsp = NULL;
1098 status = string_replace_allocate(handle->conn,
1100 string_replace_cmaps,
1103 vfs_translate_to_windows);
1104 if (!NT_STATUS_IS_OK(status) &&
1105 !NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))
1107 DBG_ERR("string_replace_allocate failed\n");
1113 mapped_name = talloc_asprintf(talloc_tos(), ":%s", tmp);
1115 if (mapped_name == NULL) {
1120 stream_name = synthetic_smb_fname(talloc_tos(),
1121 smb_fname->base_name,
1125 TALLOC_FREE(mapped_name);
1126 if (stream_name == NULL) {
1127 DBG_ERR("synthetic_smb_fname failed\n");
1132 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1134 status = SMB_VFS_CREATE_FILE(
1135 handle->conn, /* conn */
1137 0, /* root_dir_fid */
1138 stream_name, /* fname */
1139 FILE_GENERIC_WRITE, /* access_mask */
1140 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
1141 FILE_OPEN_IF, /* create_disposition */
1142 0, /* create_options */
1143 0, /* file_attributes */
1144 INTERNAL_OPEN_ONLY, /* oplock_request */
1146 0, /* allocation_size */
1147 0, /* private_flags */
1152 NULL, NULL); /* create context */
1153 TALLOC_FREE(stream_name);
1154 if (!NT_STATUS_IS_OK(status)) {
1155 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1160 nwritten = SMB_VFS_PWRITE(fsp,
1161 ad->ad_data + e->adx_offset,
1164 if (nwritten == -1) {
1165 DBG_ERR("SMB_VFS_PWRITE failed\n");
1166 saved_errno = errno;
1167 close_file(NULL, fsp, ERROR_CLOSE);
1168 errno = saved_errno;
1173 status = close_file(NULL, fsp, NORMAL_CLOSE);
1174 if (!NT_STATUS_IS_OK(status)) {
1181 ad_setentrylen(ad, ADEID_FINDERI, ADEDLEN_FINDERI);
1185 DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name);
1189 rc = ad_fset(handle, ad, ad->ad_fsp);
1191 DBG_ERR("ad_fset on [%s] failed: %s\n",
1192 fsp_str_dbg(ad->ad_fsp), strerror(errno));
1197 ok = ad_convert_move_reso(handle, ad, smb_fname);
1202 *converted_xattr = true;
1209 static bool ad_convert_finderinfo(vfs_handle_struct *handle,
1211 const struct smb_filename *smb_fname)
1216 struct smb_filename *stream_name = NULL;
1217 files_struct *fsp = NULL;
1221 int saved_errno = 0;
1224 cmp = memcmp(ad->ad_filler, AD_FILLER_TAG_OSX, ADEDLEN_FILLER);
1229 p_ad = ad_get_entry(ad, ADEID_FINDERI);
1234 ai = afpinfo_new(talloc_tos());
1239 memcpy(ai->afpi_FinderInfo, p_ad, ADEDLEN_FINDERI);
1241 aiblob = data_blob_talloc(talloc_tos(), NULL, AFP_INFO_SIZE);
1242 if (aiblob.data == NULL) {
1247 size = afpinfo_pack(ai, (char *)aiblob.data);
1249 if (size != AFP_INFO_SIZE) {
1253 stream_name = synthetic_smb_fname(talloc_tos(),
1254 smb_fname->base_name,
1258 if (stream_name == NULL) {
1259 data_blob_free(&aiblob);
1260 DBG_ERR("synthetic_smb_fname failed\n");
1264 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1266 status = SMB_VFS_CREATE_FILE(
1267 handle->conn, /* conn */
1269 0, /* root_dir_fid */
1270 stream_name, /* fname */
1271 FILE_GENERIC_WRITE, /* access_mask */
1272 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
1273 FILE_OPEN_IF, /* create_disposition */
1274 0, /* create_options */
1275 0, /* file_attributes */
1276 INTERNAL_OPEN_ONLY, /* oplock_request */
1278 0, /* allocation_size */
1279 0, /* private_flags */
1284 NULL, NULL); /* create context */
1285 TALLOC_FREE(stream_name);
1286 if (!NT_STATUS_IS_OK(status)) {
1287 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1291 nwritten = SMB_VFS_PWRITE(fsp,
1295 if (nwritten == -1) {
1296 DBG_ERR("SMB_VFS_PWRITE failed\n");
1297 saved_errno = errno;
1298 close_file(NULL, fsp, ERROR_CLOSE);
1299 errno = saved_errno;
1303 status = close_file(NULL, fsp, NORMAL_CLOSE);
1304 if (!NT_STATUS_IS_OK(status)) {
1312 static bool ad_convert_truncate(vfs_handle_struct *handle,
1314 const struct smb_filename *smb_fname)
1319 newlen = ADEDOFF_RFORK_DOT_UND + ad_getentrylen(ad, ADEID_RFORK);
1321 rc = SMB_VFS_FTRUNCATE(ad->ad_fsp, newlen);
1329 static bool ad_convert_blank_rfork(vfs_handle_struct *handle,
1333 struct fruit_config_data *config = NULL;
1334 size_t rforklen = sizeof(empty_resourcefork);
1343 SMB_VFS_HANDLE_GET_DATA(handle, config,
1344 struct fruit_config_data, return false);
1346 if (!config->wipe_intentionally_left_blank_rfork) {
1350 if (ad_getentrylen(ad, ADEID_RFORK) != rforklen) {
1354 nread = SMB_VFS_PREAD(ad->ad_fsp, buf, rforklen, ADEDOFF_RFORK_DOT_UND);
1355 if (nread != rforklen) {
1356 DBG_ERR("Reading %zu bytes from rfork [%s] failed: %s\n",
1357 rforklen, fsp_str_dbg(ad->ad_fsp), strerror(errno));
1361 cmp = memcmp(buf, empty_resourcefork, rforklen);
1366 ad_setentrylen(ad, ADEID_RFORK, 0);
1372 rc = ad_fset(handle, ad, ad->ad_fsp);
1374 DBG_ERR("ad_fset on [%s] failed\n", fsp_str_dbg(ad->ad_fsp));
1382 static bool ad_convert_delete_adfile(vfs_handle_struct *handle,
1384 const struct smb_filename *smb_fname)
1386 struct fruit_config_data *config = NULL;
1387 struct smb_filename *ad_name = NULL;
1390 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
1394 SMB_VFS_HANDLE_GET_DATA(handle, config,
1395 struct fruit_config_data, return false);
1397 if (!config->delete_empty_adfiles) {
1401 rc = adouble_path(talloc_tos(), smb_fname, &ad_name);
1406 rc = SMB_VFS_NEXT_UNLINK(handle, ad_name);
1408 DBG_ERR("Unlinking [%s] failed: %s\n",
1409 smb_fname_str_dbg(ad_name), strerror(errno));
1410 TALLOC_FREE(ad_name);
1414 DBG_WARNING("Unlinked [%s] after conversion\n", smb_fname_str_dbg(ad_name));
1415 TALLOC_FREE(ad_name);
1421 * Convert from Apple's ._ file to Netatalk
1423 * Apple's AppleDouble may contain a FinderInfo entry longer then 32
1424 * bytes containing packed xattrs.
1426 * @return -1 in case an error occurred, 0 if no conversion was done, 1
1429 static int ad_convert(struct vfs_handle_struct *handle,
1430 const struct smb_filename *smb_fname)
1432 struct adouble *ad = NULL;
1434 bool converted_xattr = false;
1438 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
1443 ok = ad_convert_xattr(handle, ad, smb_fname, &converted_xattr);
1449 ok = ad_convert_blank_rfork(handle, ad, &blank);
1455 if (converted_xattr || blank) {
1456 ok = ad_convert_truncate(handle, ad, smb_fname);
1463 ok = ad_convert_finderinfo(handle, ad, smb_fname);
1465 DBG_ERR("Failed to convert [%s]\n",
1466 smb_fname_str_dbg(smb_fname));
1471 ok = ad_convert_delete_adfile(handle, ad, smb_fname);
1484 * Read and parse Netatalk AppleDouble metadata xattr
1486 static ssize_t ad_read_meta(vfs_handle_struct *handle,
1488 const struct smb_filename *smb_fname)
1494 DEBUG(10, ("reading meta xattr for %s\n", smb_fname->base_name));
1496 ealen = SMB_VFS_GETXATTR(handle->conn, smb_fname,
1497 AFPINFO_EA_NETATALK, ad->ad_data,
1503 if (errno == ENOATTR) {
1509 DEBUG(2, ("error reading meta xattr: %s\n",
1515 if (ealen != AD_DATASZ_XATTR) {
1516 DEBUG(2, ("bad size %zd\n", ealen));
1522 /* Now parse entries */
1523 ok = ad_unpack(ad, ADEID_NUM_XATTR, AD_DATASZ_XATTR);
1525 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1531 if (!ad_getentryoff(ad, ADEID_FINDERI)
1532 || !ad_getentryoff(ad, ADEID_COMMENT)
1533 || !ad_getentryoff(ad, ADEID_FILEDATESI)
1534 || !ad_getentryoff(ad, ADEID_AFPFILEI)
1535 || !ad_getentryoff(ad, ADEID_PRIVDEV)
1536 || !ad_getentryoff(ad, ADEID_PRIVINO)
1537 || !ad_getentryoff(ad, ADEID_PRIVSYN)
1538 || !ad_getentryoff(ad, ADEID_PRIVID)) {
1539 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1546 DEBUG(10, ("reading meta xattr for %s, rc: %d\n",
1547 smb_fname->base_name, rc));
1551 if (errno == EINVAL) {
1553 (void)SMB_VFS_REMOVEXATTR(handle->conn,
1555 AFPINFO_EA_NETATALK);
1563 static int ad_open_rsrc(vfs_handle_struct *handle,
1564 const struct smb_filename *smb_fname,
1567 files_struct **_fsp)
1570 struct smb_filename *adp_smb_fname = NULL;
1571 files_struct *fsp = NULL;
1572 uint32_t access_mask;
1573 uint32_t share_access;
1574 uint32_t create_disposition;
1577 ret = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
1582 ret = SMB_VFS_STAT(handle->conn, adp_smb_fname);
1584 TALLOC_FREE(adp_smb_fname);
1588 access_mask = FILE_GENERIC_READ;
1589 share_access = FILE_SHARE_READ | FILE_SHARE_WRITE;
1590 create_disposition = FILE_OPEN;
1592 if (flags & O_RDWR) {
1593 access_mask |= FILE_GENERIC_WRITE;
1594 share_access &= ~FILE_SHARE_WRITE;
1597 status = SMB_VFS_CREATE_FILE(
1598 handle->conn, /* conn */
1600 0, /* root_dir_fid */
1605 0, /* create_options */
1606 0, /* file_attributes */
1607 INTERNAL_OPEN_ONLY, /* oplock_request */
1609 0, /* allocation_size */
1610 0, /* private_flags */
1615 NULL, NULL); /* create context */
1616 TALLOC_FREE(adp_smb_fname);
1617 if (!NT_STATUS_IS_OK(status)) {
1618 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1627 * Here's the deal: for ADOUBLE_META we can do without an fd as we can issue
1628 * path based xattr calls. For ADOUBLE_RSRC however we need a full-fledged fd
1629 * for file IO on the ._ file.
1631 static int ad_open(vfs_handle_struct *handle,
1634 const struct smb_filename *smb_fname,
1640 DBG_DEBUG("Path [%s] type [%s]\n", smb_fname->base_name,
1641 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
1643 if (ad->ad_type == ADOUBLE_META) {
1649 ad->ad_opened = false;
1653 ret = ad_open_rsrc(handle, smb_fname, flags, mode, &ad->ad_fsp);
1657 ad->ad_opened = true;
1659 DBG_DEBUG("Path [%s] type [%s]\n",
1660 smb_fname->base_name,
1661 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
1666 static ssize_t ad_read_rsrc_adouble(vfs_handle_struct *handle,
1668 const struct smb_filename *smb_fname)
1676 ret = SMB_VFS_NEXT_FSTAT(handle, ad->ad_fsp, &ad->ad_fsp->fsp_name->st);
1678 DBG_ERR("fstat [%s] failed: %s\n",
1679 fsp_str_dbg(ad->ad_fsp), strerror(errno));
1684 * AppleDouble file header content and size, two cases:
1686 * - without xattrs it is exactly AD_DATASZ_DOT_UND (82) bytes large
1687 * - with embedded xattrs it can be larger, up to AD_XATTR_MAX_HDR_SIZE
1689 * Read as much as we can up to AD_XATTR_MAX_HDR_SIZE.
1691 size = ad->ad_fsp->fsp_name->st.st_ex_size;
1692 if (size > talloc_array_length(ad->ad_data)) {
1693 if (size > AD_XATTR_MAX_HDR_SIZE) {
1694 size = AD_XATTR_MAX_HDR_SIZE;
1696 p_ad = talloc_realloc(ad, ad->ad_data, char, size);
1703 len = SMB_VFS_NEXT_PREAD(handle, ad->ad_fsp, ad->ad_data, talloc_array_length(ad->ad_data), 0);
1704 if (len != talloc_array_length(ad->ad_data)) {
1705 DBG_NOTICE("%s %s: bad size: %zd\n",
1706 smb_fname->base_name, strerror(errno), len);
1710 /* Now parse entries */
1711 ok = ad_unpack(ad, ADEID_NUM_DOT_UND, size);
1713 DBG_ERR("invalid AppleDouble resource %s\n",
1714 smb_fname->base_name);
1719 if ((ad_getentryoff(ad, ADEID_FINDERI) != ADEDOFF_FINDERI_DOT_UND)
1720 || (ad_getentrylen(ad, ADEID_FINDERI) < ADEDLEN_FINDERI)
1721 || (ad_getentryoff(ad, ADEID_RFORK) < ADEDOFF_RFORK_DOT_UND)) {
1722 DBG_ERR("invalid AppleDouble resource %s\n",
1723 smb_fname->base_name);
1732 * Read and parse resource fork, either ._ AppleDouble file or xattr
1734 static ssize_t ad_read_rsrc(vfs_handle_struct *handle,
1736 const struct smb_filename *smb_fname)
1738 return ad_read_rsrc_adouble(handle, ad, smb_fname);
1742 * Read and unpack an AppleDouble metadata xattr or resource
1744 static ssize_t ad_read(vfs_handle_struct *handle,
1746 const struct smb_filename *smb_fname)
1748 switch (ad->ad_type) {
1750 return ad_read_meta(handle, ad, smb_fname);
1752 return ad_read_rsrc(handle, ad, smb_fname);
1758 static int adouble_destructor(struct adouble *ad)
1762 if (!ad->ad_opened) {
1766 SMB_ASSERT(ad->ad_fsp != NULL);
1768 status = close_file(NULL, ad->ad_fsp, NORMAL_CLOSE);
1769 if (!NT_STATUS_IS_OK(status)) {
1770 DBG_ERR("Closing [%s] failed: %s\n",
1771 fsp_str_dbg(ad->ad_fsp), nt_errstr(status));
1778 * Allocate a struct adouble without initialiing it
1780 * The struct is either hang of the fsp extension context or if fsp is
1783 * @param[in] ctx talloc context
1784 * @param[in] handle vfs handle
1785 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1787 * @return adouble handle
1789 static struct adouble *ad_alloc(TALLOC_CTX *ctx,
1790 adouble_type_t type)
1798 adsize = AD_DATASZ_XATTR;
1801 adsize = AD_DATASZ_DOT_UND;
1807 ad = talloc_zero(ctx, struct adouble);
1814 ad->ad_data = talloc_zero_array(ad, char, adsize);
1815 if (ad->ad_data == NULL) {
1822 ad->ad_magic = AD_MAGIC;
1823 ad->ad_version = AD_VERSION;
1825 talloc_set_destructor(ad, adouble_destructor);
1835 * Allocate and initialize a new struct adouble
1837 * @param[in] ctx talloc context
1838 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1840 * @return adouble handle, initialized
1842 static struct adouble *ad_init(TALLOC_CTX *ctx,
1843 adouble_type_t type)
1846 const struct ad_entry_order *eid;
1847 struct adouble *ad = NULL;
1848 time_t t = time(NULL);
1852 eid = entry_order_meta_xattr;
1855 eid = entry_order_dot_und;
1861 ad = ad_alloc(ctx, type);
1867 ad->ad_eid[eid->id].ade_off = eid->offset;
1868 ad->ad_eid[eid->id].ade_len = eid->len;
1872 /* put something sane in the date fields */
1873 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, t);
1874 ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, t);
1875 ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, t);
1876 ad_setdate(ad, AD_DATE_BACKUP, htonl(AD_DATE_START));
1884 static struct adouble *ad_get_internal(TALLOC_CTX *ctx,
1885 vfs_handle_struct *handle,
1887 const struct smb_filename *smb_fname,
1888 adouble_type_t type)
1892 struct adouble *ad = NULL;
1896 smb_fname = fsp->base_fsp->fsp_name;
1899 DEBUG(10, ("ad_get(%s) called for %s\n",
1900 type == ADOUBLE_META ? "meta" : "rsrc",
1901 smb_fname->base_name));
1903 ad = ad_alloc(ctx, type);
1909 /* Try rw first so we can use the fd in ad_convert() */
1912 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
1913 if (rc == -1 && ((errno == EROFS) || (errno == EACCES))) {
1915 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
1918 DBG_DEBUG("ad_open [%s] error [%s]\n",
1919 smb_fname->base_name, strerror(errno));
1924 len = ad_read(handle, ad, smb_fname);
1926 DEBUG(10, ("error reading AppleDouble for %s\n",
1927 smb_fname->base_name));
1933 DEBUG(10, ("ad_get(%s) for %s returning %d\n",
1934 type == ADOUBLE_META ? "meta" : "rsrc",
1935 smb_fname->base_name, rc));
1944 * Return AppleDouble data for a file
1946 * @param[in] ctx talloc context
1947 * @param[in] handle vfs handle
1948 * @param[in] smb_fname pathname to file or directory
1949 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1951 * @return talloced struct adouble or NULL on error
1953 static struct adouble *ad_get(TALLOC_CTX *ctx,
1954 vfs_handle_struct *handle,
1955 const struct smb_filename *smb_fname,
1956 adouble_type_t type)
1958 return ad_get_internal(ctx, handle, NULL, smb_fname, type);
1962 * Return AppleDouble data for a file
1964 * @param[in] ctx talloc context
1965 * @param[in] handle vfs handle
1966 * @param[in] fsp fsp to use for IO
1967 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1969 * @return talloced struct adouble or NULL on error
1971 static struct adouble *ad_fget(TALLOC_CTX *ctx, vfs_handle_struct *handle,
1972 files_struct *fsp, adouble_type_t type)
1974 return ad_get_internal(ctx, handle, fsp, NULL, type);
1978 * Set AppleDouble metadata on a file or directory
1980 * @param[in] ad adouble handle
1982 * @param[in] smb_fname pathname to file or directory
1984 * @return status code, 0 means success
1986 static int ad_set(vfs_handle_struct *handle,
1988 const struct smb_filename *smb_fname)
1993 DBG_DEBUG("Path [%s]\n", smb_fname->base_name);
1995 if (ad->ad_type != ADOUBLE_META) {
1996 DBG_ERR("ad_set on [%s] used with ADOUBLE_RSRC\n",
1997 smb_fname->base_name);
2006 ret = SMB_VFS_SETXATTR(handle->conn,
2008 AFPINFO_EA_NETATALK,
2010 AD_DATASZ_XATTR, 0);
2012 DBG_DEBUG("Path [%s] ret [%d]\n", smb_fname->base_name, ret);
2018 * Set AppleDouble metadata on a file or directory
2020 * @param[in] ad adouble handle
2021 * @param[in] fsp file handle
2023 * @return status code, 0 means success
2025 static int ad_fset(struct vfs_handle_struct *handle,
2033 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
2036 || (fsp->fh == NULL)
2037 || (fsp->fh->fd == -1))
2039 smb_panic("bad fsp");
2047 switch (ad->ad_type) {
2049 rc = SMB_VFS_NEXT_SETXATTR(handle,
2051 AFPINFO_EA_NETATALK,
2053 AD_DATASZ_XATTR, 0);
2057 len = SMB_VFS_NEXT_PWRITE(handle,
2062 if (len != AD_DATASZ_DOT_UND) {
2063 DBG_ERR("short write on %s: %zd", fsp_str_dbg(fsp), len);
2073 DBG_DEBUG("Path [%s] rc [%d]\n", fsp_str_dbg(fsp), rc);
2078 /*****************************************************************************
2080 *****************************************************************************/
2082 static bool is_afpinfo_stream(const struct smb_filename *smb_fname)
2084 if (strncasecmp_m(smb_fname->stream_name,
2085 AFPINFO_STREAM_NAME,
2086 strlen(AFPINFO_STREAM_NAME)) == 0) {
2092 static bool is_afpresource_stream(const struct smb_filename *smb_fname)
2094 if (strncasecmp_m(smb_fname->stream_name,
2095 AFPRESOURCE_STREAM_NAME,
2096 strlen(AFPRESOURCE_STREAM_NAME)) == 0) {
2103 * Test whether stream is an Apple stream.
2105 static bool is_apple_stream(const struct smb_filename *smb_fname)
2107 if (is_afpinfo_stream(smb_fname)) {
2110 if (is_afpresource_stream(smb_fname)) {
2116 static bool is_adouble_file(const char *path)
2118 const char *p = NULL;
2121 p = strrchr(path, '/');
2129 ADOUBLE_NAME_PREFIX,
2130 strlen(ADOUBLE_NAME_PREFIX));
2138 * Initialize config struct from our smb.conf config parameters
2140 static int init_fruit_config(vfs_handle_struct *handle)
2142 struct fruit_config_data *config;
2144 const char *tm_size_str = NULL;
2146 config = talloc_zero(handle->conn, struct fruit_config_data);
2148 DEBUG(1, ("talloc_zero() failed\n"));
2154 * Versions up to Samba 4.5.x had a spelling bug in the
2155 * fruit:resource option calling lp_parm_enum with
2156 * "res*s*ource" (ie two s).
2158 * In Samba 4.6 we accept both the wrong and the correct
2159 * spelling, in Samba 4.7 the bad spelling will be removed.
2161 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2162 "ressource", fruit_rsrc, FRUIT_RSRC_ADFILE);
2163 if (enumval == -1) {
2164 DEBUG(1, ("value for %s: resource type unknown\n",
2165 FRUIT_PARAM_TYPE_NAME));
2168 config->rsrc = (enum fruit_rsrc)enumval;
2170 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2171 "resource", fruit_rsrc, enumval);
2172 if (enumval == -1) {
2173 DEBUG(1, ("value for %s: resource type unknown\n",
2174 FRUIT_PARAM_TYPE_NAME));
2177 config->rsrc = (enum fruit_rsrc)enumval;
2179 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2180 "metadata", fruit_meta, FRUIT_META_NETATALK);
2181 if (enumval == -1) {
2182 DEBUG(1, ("value for %s: metadata type unknown\n",
2183 FRUIT_PARAM_TYPE_NAME));
2186 config->meta = (enum fruit_meta)enumval;
2188 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2189 "locking", fruit_locking, FRUIT_LOCKING_NONE);
2190 if (enumval == -1) {
2191 DEBUG(1, ("value for %s: locking type unknown\n",
2192 FRUIT_PARAM_TYPE_NAME));
2195 config->locking = (enum fruit_locking)enumval;
2197 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2198 "encoding", fruit_encoding, FRUIT_ENC_PRIVATE);
2199 if (enumval == -1) {
2200 DEBUG(1, ("value for %s: encoding type unknown\n",
2201 FRUIT_PARAM_TYPE_NAME));
2204 config->encoding = (enum fruit_encoding)enumval;
2206 if (config->rsrc == FRUIT_RSRC_ADFILE) {
2207 config->veto_appledouble = lp_parm_bool(SNUM(handle->conn),
2208 FRUIT_PARAM_TYPE_NAME,
2213 config->use_aapl = lp_parm_bool(
2214 -1, FRUIT_PARAM_TYPE_NAME, "aapl", true);
2216 config->time_machine = lp_parm_bool(
2217 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "time machine", false);
2219 config->unix_info_enabled = lp_parm_bool(
2220 -1, FRUIT_PARAM_TYPE_NAME, "nfs_aces", true);
2222 config->use_copyfile = lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME,
2225 config->posix_rename = lp_parm_bool(
2226 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "posix_rename", true);
2228 config->aapl_zero_file_id =
2229 lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME, "zero_file_id", true);
2231 config->readdir_attr_rsize = lp_parm_bool(
2232 SNUM(handle->conn), "readdir_attr", "aapl_rsize", true);
2234 config->readdir_attr_finder_info = lp_parm_bool(
2235 SNUM(handle->conn), "readdir_attr", "aapl_finder_info", true);
2237 config->readdir_attr_max_access = lp_parm_bool(
2238 SNUM(handle->conn), "readdir_attr", "aapl_max_access", true);
2240 config->model = lp_parm_const_string(
2241 -1, FRUIT_PARAM_TYPE_NAME, "model", "MacSamba");
2243 tm_size_str = lp_parm_const_string(
2244 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2245 "time machine max size", NULL);
2246 if (tm_size_str != NULL) {
2247 config->time_machine_max_size = conv_str_size(tm_size_str);
2250 config->wipe_intentionally_left_blank_rfork = lp_parm_bool(
2251 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2252 "wipe_intentionally_left_blank_rfork", false);
2254 config->delete_empty_adfiles = lp_parm_bool(
2255 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2256 "delete_empty_adfiles", false);
2258 SMB_VFS_HANDLE_SET_DATA(handle, config,
2259 NULL, struct fruit_config_data,
2266 * Prepend "._" to a basename
2267 * Return a new struct smb_filename with stream_name == NULL.
2269 static int adouble_path(TALLOC_CTX *ctx,
2270 const struct smb_filename *smb_fname_in,
2271 struct smb_filename **pp_smb_fname_out)
2275 struct smb_filename *smb_fname = cp_smb_filename(ctx,
2278 if (smb_fname == NULL) {
2282 /* We need streamname to be NULL */
2283 TALLOC_FREE(smb_fname->stream_name);
2285 /* And we're replacing base_name. */
2286 TALLOC_FREE(smb_fname->base_name);
2288 if (!parent_dirname(smb_fname, smb_fname_in->base_name,
2290 TALLOC_FREE(smb_fname);
2294 smb_fname->base_name = talloc_asprintf(smb_fname,
2295 "%s/._%s", parent, base);
2296 if (smb_fname->base_name == NULL) {
2297 TALLOC_FREE(smb_fname);
2301 *pp_smb_fname_out = smb_fname;
2307 * Allocate and initialize an AfpInfo struct
2309 static AfpInfo *afpinfo_new(TALLOC_CTX *ctx)
2311 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2315 ai->afpi_Signature = AFP_Signature;
2316 ai->afpi_Version = AFP_Version;
2317 ai->afpi_BackupTime = AD_DATE_START;
2322 * Pack an AfpInfo struct into a buffer
2324 * Buffer size must be at least AFP_INFO_SIZE
2325 * Returns size of packed buffer
2327 static ssize_t afpinfo_pack(const AfpInfo *ai, char *buf)
2329 memset(buf, 0, AFP_INFO_SIZE);
2331 RSIVAL(buf, 0, ai->afpi_Signature);
2332 RSIVAL(buf, 4, ai->afpi_Version);
2333 RSIVAL(buf, 12, ai->afpi_BackupTime);
2334 memcpy(buf + 16, ai->afpi_FinderInfo, sizeof(ai->afpi_FinderInfo));
2336 return AFP_INFO_SIZE;
2340 * Unpack a buffer into a AfpInfo structure
2342 * Buffer size must be at least AFP_INFO_SIZE
2343 * Returns allocated AfpInfo struct
2345 static AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data)
2347 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2352 ai->afpi_Signature = RIVAL(data, 0);
2353 ai->afpi_Version = RIVAL(data, 4);
2354 ai->afpi_BackupTime = RIVAL(data, 12);
2355 memcpy(ai->afpi_FinderInfo, (const char *)data + 16,
2356 sizeof(ai->afpi_FinderInfo));
2358 if (ai->afpi_Signature != AFP_Signature
2359 || ai->afpi_Version != AFP_Version) {
2360 DEBUG(1, ("Bad AfpInfo signature or version\n"));
2368 * Fake an inode number from the md5 hash of the (xattr) name
2370 static SMB_INO_T fruit_inode(const SMB_STRUCT_STAT *sbuf, const char *sname)
2372 gnutls_hash_hd_t hash_hnd = NULL;
2373 unsigned char hash[16];
2374 SMB_INO_T result = 0;
2378 DBG_DEBUG("fruit_inode called for %ju/%ju [%s]\n",
2379 (uintmax_t)sbuf->st_ex_dev,
2380 (uintmax_t)sbuf->st_ex_ino, sname);
2382 upper_sname = talloc_strdup_upper(talloc_tos(), sname);
2383 SMB_ASSERT(upper_sname != NULL);
2385 rc = gnutls_hash_init(&hash_hnd, GNUTLS_DIG_MD5);
2390 rc = gnutls_hash(hash_hnd, &(sbuf->st_ex_dev), sizeof(sbuf->st_ex_dev));
2392 gnutls_hash_deinit(hash_hnd, NULL);
2395 rc = gnutls_hash(hash_hnd,
2397 sizeof(sbuf->st_ex_ino));
2399 gnutls_hash_deinit(hash_hnd, NULL);
2402 rc = gnutls_hash(hash_hnd,
2404 talloc_get_size(upper_sname) - 1);
2406 gnutls_hash_deinit(hash_hnd, NULL);
2410 gnutls_hash_deinit(hash_hnd, hash);
2412 /* Hopefully all the variation is in the lower 4 (or 8) bytes! */
2413 memcpy(&result, hash, sizeof(result));
2416 DBG_DEBUG("fruit_inode \"%s\": ino=%ju\n",
2417 sname, (uintmax_t)result);
2420 TALLOC_FREE(upper_sname);
2425 static bool add_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
2426 struct stream_struct **streams,
2427 const char *name, off_t size,
2430 struct stream_struct *tmp;
2432 tmp = talloc_realloc(mem_ctx, *streams, struct stream_struct,
2438 tmp[*num_streams].name = talloc_asprintf(tmp, "%s:$DATA", name);
2439 if (tmp[*num_streams].name == NULL) {
2443 tmp[*num_streams].size = size;
2444 tmp[*num_streams].alloc_size = alloc_size;
2451 static bool filter_empty_rsrc_stream(unsigned int *num_streams,
2452 struct stream_struct **streams)
2454 struct stream_struct *tmp = *streams;
2457 if (*num_streams == 0) {
2461 for (i = 0; i < *num_streams; i++) {
2462 if (strequal_m(tmp[i].name, AFPRESOURCE_STREAM)) {
2467 if (i == *num_streams) {
2471 if (tmp[i].size > 0) {
2475 TALLOC_FREE(tmp[i].name);
2476 if (*num_streams - 1 > i) {
2477 memmove(&tmp[i], &tmp[i+1],
2478 (*num_streams - i - 1) * sizeof(struct stream_struct));
2485 static bool del_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
2486 struct stream_struct **streams,
2489 struct stream_struct *tmp = *streams;
2492 if (*num_streams == 0) {
2496 for (i = 0; i < *num_streams; i++) {
2497 if (strequal_m(tmp[i].name, name)) {
2502 if (i == *num_streams) {
2506 TALLOC_FREE(tmp[i].name);
2507 if (*num_streams - 1 > i) {
2508 memmove(&tmp[i], &tmp[i+1],
2509 (*num_streams - i - 1) * sizeof(struct stream_struct));
2516 static bool ad_empty_finderinfo(const struct adouble *ad)
2519 char emptybuf[ADEDLEN_FINDERI] = {0};
2522 fi = ad_get_entry(ad, ADEID_FINDERI);
2524 DBG_ERR("Missing FinderInfo in struct adouble [%p]\n", ad);
2528 cmp = memcmp(emptybuf, fi, ADEDLEN_FINDERI);
2532 static bool ai_empty_finderinfo(const AfpInfo *ai)
2535 char emptybuf[ADEDLEN_FINDERI] = {0};
2537 cmp = memcmp(emptybuf, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
2542 * Update btime with btime from Netatalk
2544 static void update_btime(vfs_handle_struct *handle,
2545 struct smb_filename *smb_fname)
2548 struct timespec creation_time = {0};
2550 struct fruit_config_data *config = NULL;
2552 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
2555 switch (config->meta) {
2556 case FRUIT_META_STREAM:
2558 case FRUIT_META_NETATALK:
2562 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
2566 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
2570 if (ad_getdate(ad, AD_DATE_UNIX | AD_DATE_CREATE, &t) != 0) {
2576 creation_time.tv_sec = convert_uint32_t_to_time_t(t);
2577 update_stat_ex_create_time(&smb_fname->st, creation_time);
2583 * Map an access mask to a Netatalk single byte byte range lock
2585 static off_t access_to_netatalk_brl(enum apple_fork fork_type,
2586 uint32_t access_mask)
2590 switch (access_mask) {
2591 case FILE_READ_DATA:
2592 offset = AD_FILELOCK_OPEN_RD;
2595 case FILE_WRITE_DATA:
2596 case FILE_APPEND_DATA:
2597 offset = AD_FILELOCK_OPEN_WR;
2601 offset = AD_FILELOCK_OPEN_NONE;
2605 if (fork_type == APPLE_FORK_RSRC) {
2606 if (offset == AD_FILELOCK_OPEN_NONE) {
2607 offset = AD_FILELOCK_RSRC_OPEN_NONE;
2617 * Map a deny mode to a Netatalk brl
2619 static off_t denymode_to_netatalk_brl(enum apple_fork fork_type,
2624 switch (deny_mode) {
2626 offset = AD_FILELOCK_DENY_RD;
2630 offset = AD_FILELOCK_DENY_WR;
2634 smb_panic("denymode_to_netatalk_brl: bad deny mode\n");
2637 if (fork_type == APPLE_FORK_RSRC) {
2645 * Call fcntl() with an exclusive F_GETLK request in order to
2646 * determine if there's an exisiting shared lock
2648 * @return true if the requested lock was found or any error occurred
2649 * false if the lock was not found
2651 static bool test_netatalk_lock(files_struct *fsp, off_t in_offset)
2654 off_t offset = in_offset;
2659 result = SMB_VFS_GETLOCK(fsp, &offset, &len, &type, &pid);
2660 if (result == false) {
2664 if (type != F_UNLCK) {
2671 static NTSTATUS fruit_check_access(vfs_handle_struct *handle,
2673 uint32_t access_mask,
2674 uint32_t share_mode)
2676 NTSTATUS status = NT_STATUS_OK;
2678 bool share_for_read = (share_mode & FILE_SHARE_READ);
2679 bool share_for_write = (share_mode & FILE_SHARE_WRITE);
2680 bool netatalk_already_open_for_reading = false;
2681 bool netatalk_already_open_for_writing = false;
2682 bool netatalk_already_open_with_deny_read = false;
2683 bool netatalk_already_open_with_deny_write = false;
2685 /* FIXME: hardcoded data fork, add resource fork */
2686 enum apple_fork fork_type = APPLE_FORK_DATA;
2688 DBG_DEBUG("fruit_check_access: %s, am: %s/%s, sm: 0x%x\n",
2690 access_mask & FILE_READ_DATA ? "READ" :"-",
2691 access_mask & FILE_WRITE_DATA ? "WRITE" : "-",
2694 if (fsp->fh->fd == -1) {
2695 return NT_STATUS_OK;
2698 /* Read NetATalk opens and deny modes on the file. */
2699 netatalk_already_open_for_reading = test_netatalk_lock(fsp,
2700 access_to_netatalk_brl(fork_type,
2703 netatalk_already_open_with_deny_read = test_netatalk_lock(fsp,
2704 denymode_to_netatalk_brl(fork_type,
2707 netatalk_already_open_for_writing = test_netatalk_lock(fsp,
2708 access_to_netatalk_brl(fork_type,
2711 netatalk_already_open_with_deny_write = test_netatalk_lock(fsp,
2712 denymode_to_netatalk_brl(fork_type,
2715 /* If there are any conflicts - sharing violation. */
2716 if ((access_mask & FILE_READ_DATA) &&
2717 netatalk_already_open_with_deny_read) {
2718 return NT_STATUS_SHARING_VIOLATION;
2721 if (!share_for_read &&
2722 netatalk_already_open_for_reading) {
2723 return NT_STATUS_SHARING_VIOLATION;
2726 if ((access_mask & FILE_WRITE_DATA) &&
2727 netatalk_already_open_with_deny_write) {
2728 return NT_STATUS_SHARING_VIOLATION;
2731 if (!share_for_write &&
2732 netatalk_already_open_for_writing) {
2733 return NT_STATUS_SHARING_VIOLATION;
2736 if (!(access_mask & FILE_READ_DATA)) {
2738 * Nothing we can do here, we need read access
2741 return NT_STATUS_OK;
2744 /* Set NetAtalk locks matching our access */
2745 if (access_mask & FILE_READ_DATA) {
2746 struct byte_range_lock *br_lck = NULL;
2748 off = access_to_netatalk_brl(fork_type, FILE_READ_DATA);
2750 handle->conn->sconn->msg_ctx, fsp,
2751 fsp->op->global->open_persistent_id, 1, off,
2752 READ_LOCK, POSIX_LOCK, false,
2755 TALLOC_FREE(br_lck);
2757 if (!NT_STATUS_IS_OK(status)) {
2762 if (!share_for_read) {
2763 struct byte_range_lock *br_lck = NULL;
2765 off = denymode_to_netatalk_brl(fork_type, DENY_READ);
2767 handle->conn->sconn->msg_ctx, fsp,
2768 fsp->op->global->open_persistent_id, 1, off,
2769 READ_LOCK, POSIX_LOCK, false,
2772 TALLOC_FREE(br_lck);
2774 if (!NT_STATUS_IS_OK(status)) {
2779 if (access_mask & FILE_WRITE_DATA) {
2780 struct byte_range_lock *br_lck = NULL;
2782 off = access_to_netatalk_brl(fork_type, FILE_WRITE_DATA);
2784 handle->conn->sconn->msg_ctx, fsp,
2785 fsp->op->global->open_persistent_id, 1, off,
2786 READ_LOCK, POSIX_LOCK, false,
2789 TALLOC_FREE(br_lck);
2791 if (!NT_STATUS_IS_OK(status)) {
2796 if (!share_for_write) {
2797 struct byte_range_lock *br_lck = NULL;
2799 off = denymode_to_netatalk_brl(fork_type, DENY_WRITE);
2801 handle->conn->sconn->msg_ctx, fsp,
2802 fsp->op->global->open_persistent_id, 1, off,
2803 READ_LOCK, POSIX_LOCK, false,
2806 TALLOC_FREE(br_lck);
2808 if (!NT_STATUS_IS_OK(status)) {
2813 return NT_STATUS_OK;
2816 static NTSTATUS check_aapl(vfs_handle_struct *handle,
2817 struct smb_request *req,
2818 const struct smb2_create_blobs *in_context_blobs,
2819 struct smb2_create_blobs *out_context_blobs)
2821 struct fruit_config_data *config;
2823 struct smb2_create_blob *aapl = NULL;
2827 DATA_BLOB blob = data_blob_talloc(req, NULL, 0);
2828 uint64_t req_bitmap, client_caps;
2829 uint64_t server_caps = SMB2_CRTCTX_AAPL_UNIX_BASED;
2833 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
2834 return NT_STATUS_UNSUCCESSFUL);
2836 if (!config->use_aapl
2837 || in_context_blobs == NULL
2838 || out_context_blobs == NULL) {
2839 return NT_STATUS_OK;
2842 aapl = smb2_create_blob_find(in_context_blobs,
2843 SMB2_CREATE_TAG_AAPL);
2845 return NT_STATUS_OK;
2848 if (aapl->data.length != 24) {
2849 DEBUG(1, ("unexpected AAPL ctxt length: %ju\n",
2850 (uintmax_t)aapl->data.length));
2851 return NT_STATUS_INVALID_PARAMETER;
2854 cmd = IVAL(aapl->data.data, 0);
2855 if (cmd != SMB2_CRTCTX_AAPL_SERVER_QUERY) {
2856 DEBUG(1, ("unsupported AAPL cmd: %d\n", cmd));
2857 return NT_STATUS_INVALID_PARAMETER;
2860 req_bitmap = BVAL(aapl->data.data, 8);
2861 client_caps = BVAL(aapl->data.data, 16);
2863 SIVAL(p, 0, SMB2_CRTCTX_AAPL_SERVER_QUERY);
2865 SBVAL(p, 8, req_bitmap);
2866 ok = data_blob_append(req, &blob, p, 16);
2868 return NT_STATUS_UNSUCCESSFUL;
2871 if (req_bitmap & SMB2_CRTCTX_AAPL_SERVER_CAPS) {
2872 if ((client_caps & SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR) &&
2873 (handle->conn->tcon->compat->fs_capabilities & FILE_NAMED_STREAMS)) {
2874 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR;
2875 config->readdir_attr_enabled = true;
2878 if (config->use_copyfile) {
2879 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_OSX_COPYFILE;
2880 config->copyfile_enabled = true;
2884 * The client doesn't set the flag, so we can't check
2885 * for it and just set it unconditionally
2887 if (config->unix_info_enabled) {
2888 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_NFS_ACE;
2891 SBVAL(p, 0, server_caps);
2892 ok = data_blob_append(req, &blob, p, 8);
2894 return NT_STATUS_UNSUCCESSFUL;
2898 if (req_bitmap & SMB2_CRTCTX_AAPL_VOLUME_CAPS) {
2899 int val = lp_case_sensitive(SNUM(handle->conn->tcon->compat));
2907 caps |= SMB2_CRTCTX_AAPL_CASE_SENSITIVE;
2914 if (config->time_machine) {
2915 caps |= SMB2_CRTCTX_AAPL_FULL_SYNC;
2920 ok = data_blob_append(req, &blob, p, 8);
2922 return NT_STATUS_UNSUCCESSFUL;
2926 if (req_bitmap & SMB2_CRTCTX_AAPL_MODEL_INFO) {
2927 ok = convert_string_talloc(req,
2928 CH_UNIX, CH_UTF16LE,
2929 config->model, strlen(config->model),
2932 return NT_STATUS_UNSUCCESSFUL;
2936 SIVAL(p + 4, 0, modellen);
2937 ok = data_blob_append(req, &blob, p, 8);
2940 return NT_STATUS_UNSUCCESSFUL;
2943 ok = data_blob_append(req, &blob, model, modellen);
2946 return NT_STATUS_UNSUCCESSFUL;
2950 status = smb2_create_blob_add(out_context_blobs,
2952 SMB2_CREATE_TAG_AAPL,
2954 if (NT_STATUS_IS_OK(status)) {
2955 global_fruit_config.nego_aapl = true;
2956 if (config->aapl_zero_file_id) {
2957 aapl_force_zero_file_id(handle->conn->sconn);
2964 static bool readdir_attr_meta_finderi_stream(
2965 struct vfs_handle_struct *handle,
2966 const struct smb_filename *smb_fname,
2969 struct smb_filename *stream_name = NULL;
2970 files_struct *fsp = NULL;
2975 uint8_t buf[AFP_INFO_SIZE];
2977 stream_name = synthetic_smb_fname(talloc_tos(),
2978 smb_fname->base_name,
2979 AFPINFO_STREAM_NAME,
2980 NULL, smb_fname->flags);
2981 if (stream_name == NULL) {
2985 ret = SMB_VFS_STAT(handle->conn, stream_name);
2990 status = SMB_VFS_CREATE_FILE(
2991 handle->conn, /* conn */
2993 0, /* root_dir_fid */
2994 stream_name, /* fname */
2995 FILE_READ_DATA, /* access_mask */
2996 (FILE_SHARE_READ | FILE_SHARE_WRITE | /* share_access */
2998 FILE_OPEN, /* create_disposition*/
2999 0, /* create_options */
3000 0, /* file_attributes */
3001 INTERNAL_OPEN_ONLY, /* oplock_request */
3003 0, /* allocation_size */
3004 0, /* private_flags */
3009 NULL, NULL); /* create context */
3011 TALLOC_FREE(stream_name);
3013 if (!NT_STATUS_IS_OK(status)) {
3017 nread = SMB_VFS_PREAD(fsp, &buf[0], AFP_INFO_SIZE, 0);
3018 if (nread != AFP_INFO_SIZE) {
3019 DBG_ERR("short read [%s] [%zd/%d]\n",
3020 smb_fname_str_dbg(stream_name), nread, AFP_INFO_SIZE);
3025 memcpy(&ai->afpi_FinderInfo[0], &buf[AFP_OFF_FinderInfo],
3032 close_file(NULL, fsp, NORMAL_CLOSE);
3038 static bool readdir_attr_meta_finderi_netatalk(
3039 struct vfs_handle_struct *handle,
3040 const struct smb_filename *smb_fname,
3043 struct adouble *ad = NULL;
3046 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
3051 p = ad_get_entry(ad, ADEID_FINDERI);
3053 DBG_ERR("No ADEID_FINDERI for [%s]\n", smb_fname->base_name);
3058 memcpy(&ai->afpi_FinderInfo[0], p, AFP_FinderSize);
3063 static bool readdir_attr_meta_finderi(struct vfs_handle_struct *handle,
3064 const struct smb_filename *smb_fname,
3065 struct readdir_attr_data *attr_data)
3067 struct fruit_config_data *config = NULL;
3068 uint32_t date_added;
3072 SMB_VFS_HANDLE_GET_DATA(handle, config,
3073 struct fruit_config_data,
3076 switch (config->meta) {
3077 case FRUIT_META_NETATALK:
3078 ok = readdir_attr_meta_finderi_netatalk(
3079 handle, smb_fname, &ai);
3082 case FRUIT_META_STREAM:
3083 ok = readdir_attr_meta_finderi_stream(
3084 handle, smb_fname, &ai);
3088 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3093 /* Don't bother with errors, it's likely ENOENT */
3097 if (S_ISREG(smb_fname->st.st_ex_mode)) {
3099 memcpy(&attr_data->attr_data.aapl.finder_info[0],
3100 &ai.afpi_FinderInfo[0], 4);
3102 /* finder_creator */
3103 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 4,
3104 &ai.afpi_FinderInfo[4], 4);
3108 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 8,
3109 &ai.afpi_FinderInfo[8], 2);
3111 /* finder_ext_flags */
3112 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 10,
3113 &ai.afpi_FinderInfo[24], 2);
3116 date_added = convert_time_t_to_uint32_t(
3117 smb_fname->st.st_ex_btime.tv_sec - AD_DATE_DELTA);
3119 RSIVAL(&attr_data->attr_data.aapl.finder_info[0], 12, date_added);
3124 static uint64_t readdir_attr_rfork_size_adouble(
3125 struct vfs_handle_struct *handle,
3126 const struct smb_filename *smb_fname)
3128 struct adouble *ad = NULL;
3129 uint64_t rfork_size;
3131 ad = ad_get(talloc_tos(), handle, smb_fname,
3137 rfork_size = ad_getentrylen(ad, ADEID_RFORK);
3143 static uint64_t readdir_attr_rfork_size_stream(
3144 struct vfs_handle_struct *handle,
3145 const struct smb_filename *smb_fname)
3147 struct smb_filename *stream_name = NULL;
3149 uint64_t rfork_size;
3151 stream_name = synthetic_smb_fname(talloc_tos(),
3152 smb_fname->base_name,
3153 AFPRESOURCE_STREAM_NAME,
3155 if (stream_name == NULL) {
3159 ret = SMB_VFS_STAT(handle->conn, stream_name);
3161 TALLOC_FREE(stream_name);
3165 rfork_size = stream_name->st.st_ex_size;
3166 TALLOC_FREE(stream_name);
3171 static uint64_t readdir_attr_rfork_size(struct vfs_handle_struct *handle,
3172 const struct smb_filename *smb_fname)
3174 struct fruit_config_data *config = NULL;
3175 uint64_t rfork_size;
3177 SMB_VFS_HANDLE_GET_DATA(handle, config,
3178 struct fruit_config_data,
3181 switch (config->rsrc) {
3182 case FRUIT_RSRC_ADFILE:
3183 rfork_size = readdir_attr_rfork_size_adouble(handle,
3187 case FRUIT_RSRC_XATTR:
3188 case FRUIT_RSRC_STREAM:
3189 rfork_size = readdir_attr_rfork_size_stream(handle,
3194 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3202 static NTSTATUS readdir_attr_macmeta(struct vfs_handle_struct *handle,
3203 const struct smb_filename *smb_fname,
3204 struct readdir_attr_data *attr_data)
3206 NTSTATUS status = NT_STATUS_OK;
3207 struct fruit_config_data *config = NULL;
3210 SMB_VFS_HANDLE_GET_DATA(handle, config,
3211 struct fruit_config_data,
3212 return NT_STATUS_UNSUCCESSFUL);
3215 /* Ensure we return a default value in the creation_date field */
3216 RSIVAL(&attr_data->attr_data.aapl.finder_info, 12, AD_DATE_START);
3219 * Resource fork length
3222 if (config->readdir_attr_rsize) {
3223 uint64_t rfork_size;
3225 rfork_size = readdir_attr_rfork_size(handle, smb_fname);
3226 attr_data->attr_data.aapl.rfork_size = rfork_size;
3233 if (config->readdir_attr_finder_info) {
3234 ok = readdir_attr_meta_finderi(handle, smb_fname, attr_data);
3236 status = NT_STATUS_INTERNAL_ERROR;
3243 static NTSTATUS remove_virtual_nfs_aces(struct security_descriptor *psd)
3248 if (psd->dacl == NULL) {
3249 return NT_STATUS_OK;
3252 for (i = 0; i < psd->dacl->num_aces; i++) {
3253 /* MS NFS style mode/uid/gid */
3254 int cmp = dom_sid_compare_domain(
3255 &global_sid_Unix_NFS,
3256 &psd->dacl->aces[i].trustee);
3258 /* Normal ACE entry. */
3263 * security_descriptor_dacl_del()
3264 * *must* return NT_STATUS_OK as we know
3265 * we have something to remove.
3268 status = security_descriptor_dacl_del(psd,
3269 &psd->dacl->aces[i].trustee);
3270 if (!NT_STATUS_IS_OK(status)) {
3271 DBG_WARNING("failed to remove MS NFS style ACE: %s\n",
3277 * security_descriptor_dacl_del() may delete more
3278 * then one entry subsequent to this one if the
3279 * SID matches, but we only need to ensure that
3280 * we stay looking at the same element in the array.
3284 return NT_STATUS_OK;
3287 /* Search MS NFS style ACE with UNIX mode */
3288 static NTSTATUS check_ms_nfs(vfs_handle_struct *handle,
3290 struct security_descriptor *psd,
3295 struct fruit_config_data *config = NULL;
3299 SMB_VFS_HANDLE_GET_DATA(handle, config,
3300 struct fruit_config_data,
3301 return NT_STATUS_UNSUCCESSFUL);
3303 if (!global_fruit_config.nego_aapl) {
3304 return NT_STATUS_OK;
3306 if (psd->dacl == NULL || !config->unix_info_enabled) {
3307 return NT_STATUS_OK;
3310 for (i = 0; i < psd->dacl->num_aces; i++) {
3311 if (dom_sid_compare_domain(
3312 &global_sid_Unix_NFS_Mode,
3313 &psd->dacl->aces[i].trustee) == 0) {
3314 *pmode = (mode_t)psd->dacl->aces[i].trustee.sub_auths[2];
3315 *pmode &= (S_IRWXU | S_IRWXG | S_IRWXO);
3318 DEBUG(10, ("MS NFS chmod request %s, %04o\n",
3319 fsp_str_dbg(fsp), (unsigned)(*pmode)));
3325 * Remove any incoming virtual ACE entries generated by
3326 * fruit_fget_nt_acl().
3329 return remove_virtual_nfs_aces(psd);
3332 /****************************************************************************
3334 ****************************************************************************/
3336 static int fruit_connect(vfs_handle_struct *handle,
3337 const char *service,
3341 char *list = NULL, *newlist = NULL;
3342 struct fruit_config_data *config;
3344 DEBUG(10, ("fruit_connect\n"));
3346 rc = SMB_VFS_NEXT_CONNECT(handle, service, user);
3351 rc = init_fruit_config(handle);
3356 SMB_VFS_HANDLE_GET_DATA(handle, config,
3357 struct fruit_config_data, return -1);
3359 if (config->veto_appledouble) {
3360 list = lp_veto_files(talloc_tos(), SNUM(handle->conn));
3363 if (strstr(list, "/" ADOUBLE_NAME_PREFIX "*/") == NULL) {
3364 newlist = talloc_asprintf(
3366 "%s/" ADOUBLE_NAME_PREFIX "*/",
3368 lp_do_parameter(SNUM(handle->conn),
3373 lp_do_parameter(SNUM(handle->conn),
3375 "/" ADOUBLE_NAME_PREFIX "*/");
3381 if (config->encoding == FRUIT_ENC_NATIVE) {
3382 lp_do_parameter(SNUM(handle->conn),
3387 if (config->time_machine) {
3388 DBG_NOTICE("Enabling durable handles for Time Machine "
3389 "support on [%s]\n", service);
3390 lp_do_parameter(SNUM(handle->conn), "durable handles", "yes");
3391 lp_do_parameter(SNUM(handle->conn), "kernel oplocks", "no");
3392 lp_do_parameter(SNUM(handle->conn), "kernel share modes", "no");
3393 if (!lp_strict_sync(SNUM(handle->conn))) {
3394 DBG_WARNING("Time Machine without strict sync is not "
3397 lp_do_parameter(SNUM(handle->conn), "posix locking", "no");
3403 static int fruit_fake_fd(void)
3410 * Return a valid fd, but ensure any attempt to use it returns
3411 * an error (EPIPE). Once we get a write on the handle, we open
3414 ret = pipe(pipe_fds);
3424 static int fruit_open_meta_stream(vfs_handle_struct *handle,
3425 struct smb_filename *smb_fname,
3430 struct fruit_config_data *config = NULL;
3431 struct fio *fio = NULL;
3432 int open_flags = flags & ~O_CREAT;
3435 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3437 SMB_VFS_HANDLE_GET_DATA(handle, config,
3438 struct fruit_config_data, return -1);
3440 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3441 fio->type = ADOUBLE_META;
3442 fio->config = config;
3444 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, open_flags, mode);
3449 if (!(flags & O_CREAT)) {
3450 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
3454 fd = fruit_fake_fd();
3456 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
3460 fio->fake_fd = true;
3467 static int fruit_open_meta_netatalk(vfs_handle_struct *handle,
3468 struct smb_filename *smb_fname,
3473 struct fruit_config_data *config = NULL;
3474 struct fio *fio = NULL;
3475 struct adouble *ad = NULL;
3476 bool meta_exists = false;
3479 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3481 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
3488 if (!meta_exists && !(flags & O_CREAT)) {
3493 fd = fruit_fake_fd();
3498 SMB_VFS_HANDLE_GET_DATA(handle, config,
3499 struct fruit_config_data, return -1);
3501 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3502 fio->type = ADOUBLE_META;
3503 fio->config = config;
3504 fio->fake_fd = true;
3511 static int fruit_open_meta(vfs_handle_struct *handle,
3512 struct smb_filename *smb_fname,
3513 files_struct *fsp, int flags, mode_t mode)
3516 struct fruit_config_data *config = NULL;
3518 DBG_DEBUG("path [%s]\n", smb_fname_str_dbg(smb_fname));
3520 SMB_VFS_HANDLE_GET_DATA(handle, config,
3521 struct fruit_config_data, return -1);
3523 switch (config->meta) {
3524 case FRUIT_META_STREAM:
3525 fd = fruit_open_meta_stream(handle, smb_fname,
3529 case FRUIT_META_NETATALK:
3530 fd = fruit_open_meta_netatalk(handle, smb_fname,
3535 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3539 DBG_DEBUG("path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3544 static int fruit_open_rsrc_adouble(vfs_handle_struct *handle,
3545 struct smb_filename *smb_fname,
3551 struct adouble *ad = NULL;
3552 struct smb_filename *smb_fname_base = NULL;
3553 struct fruit_config_data *config = NULL;
3556 SMB_VFS_HANDLE_GET_DATA(handle, config,
3557 struct fruit_config_data, return -1);
3559 if ((!(flags & O_CREAT)) &&
3560 S_ISDIR(fsp->base_fsp->fsp_name->st.st_ex_mode))
3562 /* sorry, but directories don't habe a resource fork */
3567 rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_base);
3572 /* We always need read/write access for the metadata header too */
3573 flags &= ~(O_RDONLY | O_WRONLY);
3576 hostfd = SMB_VFS_NEXT_OPEN(handle, smb_fname_base, fsp,
3583 if (flags & (O_CREAT | O_TRUNC)) {
3584 ad = ad_init(fsp, ADOUBLE_RSRC);
3590 fsp->fh->fd = hostfd;
3592 rc = ad_fset(handle, ad, fsp);
3603 TALLOC_FREE(smb_fname_base);
3605 DEBUG(10, ("fruit_open resource fork: rc=%d, fd=%d\n", rc, hostfd));
3607 int saved_errno = errno;
3610 * BUGBUGBUG -- we would need to call
3611 * fd_close_posix here, but we don't have a
3614 fsp->fh->fd = hostfd;
3618 errno = saved_errno;
3623 static int fruit_open_rsrc_xattr(vfs_handle_struct *handle,
3624 struct smb_filename *smb_fname,
3629 #ifdef HAVE_ATTROPEN
3632 fd = attropen(smb_fname->base_name,
3633 AFPRESOURCE_EA_NETATALK,
3648 static int fruit_open_rsrc(vfs_handle_struct *handle,
3649 struct smb_filename *smb_fname,
3650 files_struct *fsp, int flags, mode_t mode)
3653 struct fruit_config_data *config = NULL;
3654 struct fio *fio = NULL;
3656 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3658 SMB_VFS_HANDLE_GET_DATA(handle, config,
3659 struct fruit_config_data, return -1);
3661 switch (config->rsrc) {
3662 case FRUIT_RSRC_STREAM:
3663 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3666 case FRUIT_RSRC_ADFILE:
3667 fd = fruit_open_rsrc_adouble(handle, smb_fname,
3671 case FRUIT_RSRC_XATTR:
3672 fd = fruit_open_rsrc_xattr(handle, smb_fname,
3677 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3681 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3687 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3688 fio->type = ADOUBLE_RSRC;
3689 fio->config = config;
3694 static int fruit_open(vfs_handle_struct *handle,
3695 struct smb_filename *smb_fname,
3696 files_struct *fsp, int flags, mode_t mode)
3700 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3702 if (!is_ntfs_stream_smb_fname(smb_fname)) {
3703 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3706 if (is_afpinfo_stream(smb_fname)) {
3707 fd = fruit_open_meta(handle, smb_fname, fsp, flags, mode);
3708 } else if (is_afpresource_stream(smb_fname)) {
3709 fd = fruit_open_rsrc(handle, smb_fname, fsp, flags, mode);
3711 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3714 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3719 static int fruit_close_meta(vfs_handle_struct *handle,
3723 struct fruit_config_data *config = NULL;
3725 SMB_VFS_HANDLE_GET_DATA(handle, config,
3726 struct fruit_config_data, return -1);
3728 switch (config->meta) {
3729 case FRUIT_META_STREAM:
3730 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3733 case FRUIT_META_NETATALK:
3734 ret = close(fsp->fh->fd);
3739 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3747 static int fruit_close_rsrc(vfs_handle_struct *handle,
3751 struct fruit_config_data *config = NULL;
3753 SMB_VFS_HANDLE_GET_DATA(handle, config,
3754 struct fruit_config_data, return -1);
3756 switch (config->rsrc) {
3757 case FRUIT_RSRC_STREAM:
3758 case FRUIT_RSRC_ADFILE:
3759 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3762 case FRUIT_RSRC_XATTR:
3763 ret = close(fsp->fh->fd);
3768 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3775 static int fruit_close(vfs_handle_struct *handle,
3783 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(fsp->fsp_name), fd);
3785 if (!is_ntfs_stream_smb_fname(fsp->fsp_name)) {
3786 return SMB_VFS_NEXT_CLOSE(handle, fsp);
3789 if (is_afpinfo_stream(fsp->fsp_name)) {
3790 ret = fruit_close_meta(handle, fsp);
3791 } else if (is_afpresource_stream(fsp->fsp_name)) {
3792 ret = fruit_close_rsrc(handle, fsp);
3794 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3800 static int fruit_rename(struct vfs_handle_struct *handle,
3801 const struct smb_filename *smb_fname_src,
3802 const struct smb_filename *smb_fname_dst)
3805 struct fruit_config_data *config = NULL;
3806 struct smb_filename *src_adp_smb_fname = NULL;
3807 struct smb_filename *dst_adp_smb_fname = NULL;
3809 SMB_VFS_HANDLE_GET_DATA(handle, config,
3810 struct fruit_config_data, return -1);
3812 if (!VALID_STAT(smb_fname_src->st)) {
3813 DBG_ERR("Need valid stat for [%s]\n",
3814 smb_fname_str_dbg(smb_fname_src));
3818 rc = SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
3823 if ((config->rsrc != FRUIT_RSRC_ADFILE) ||
3824 (!S_ISREG(smb_fname_src->st.st_ex_mode)))
3829 rc = adouble_path(talloc_tos(), smb_fname_src, &src_adp_smb_fname);
3834 rc = adouble_path(talloc_tos(), smb_fname_dst, &dst_adp_smb_fname);
3839 DBG_DEBUG("%s -> %s\n",
3840 smb_fname_str_dbg(src_adp_smb_fname),
3841 smb_fname_str_dbg(dst_adp_smb_fname));
3843 rc = SMB_VFS_NEXT_RENAME(handle, src_adp_smb_fname, dst_adp_smb_fname);
3844 if (errno == ENOENT) {
3849 TALLOC_FREE(src_adp_smb_fname);
3850 TALLOC_FREE(dst_adp_smb_fname);
3854 static int fruit_unlink_meta_stream(vfs_handle_struct *handle,
3855 const struct smb_filename *smb_fname)
3857 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3860 static int fruit_unlink_meta_netatalk(vfs_handle_struct *handle,
3861 const struct smb_filename *smb_fname)
3863 return SMB_VFS_REMOVEXATTR(handle->conn,
3865 AFPINFO_EA_NETATALK);
3868 static int fruit_unlink_meta(vfs_handle_struct *handle,
3869 const struct smb_filename *smb_fname)
3871 struct fruit_config_data *config = NULL;
3874 SMB_VFS_HANDLE_GET_DATA(handle, config,
3875 struct fruit_config_data, return -1);
3877 switch (config->meta) {
3878 case FRUIT_META_STREAM:
3879 rc = fruit_unlink_meta_stream(handle, smb_fname);
3882 case FRUIT_META_NETATALK:
3883 rc = fruit_unlink_meta_netatalk(handle, smb_fname);
3887 DBG_ERR("Unsupported meta config [%d]\n", config->meta);
3894 static int fruit_unlink_rsrc_stream(vfs_handle_struct *handle,
3895 const struct smb_filename *smb_fname,
3900 if (!force_unlink) {
3901 struct smb_filename *smb_fname_cp = NULL;
3904 smb_fname_cp = cp_smb_filename(talloc_tos(), smb_fname);
3905 if (smb_fname_cp == NULL) {
3910 * 0 byte resource fork streams are not listed by
3911 * vfs_streaminfo, as a result stream cleanup/deletion of file
3912 * deletion doesn't remove the resourcefork stream.
3915 ret = SMB_VFS_NEXT_STAT(handle, smb_fname_cp);
3917 TALLOC_FREE(smb_fname_cp);
3918 DBG_ERR("stat [%s] failed [%s]\n",
3919 smb_fname_str_dbg(smb_fname_cp), strerror(errno));
3923 size = smb_fname_cp->st.st_ex_size;
3924 TALLOC_FREE(smb_fname_cp);
3927 /* OS X ignores resource fork stream delete requests */
3932 ret = SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3933 if ((ret != 0) && (errno == ENOENT) && force_unlink) {
3940 static int fruit_unlink_rsrc_adouble(vfs_handle_struct *handle,
3941 const struct smb_filename *smb_fname,
3945 struct adouble *ad = NULL;
3946 struct smb_filename *adp_smb_fname = NULL;
3948 if (!force_unlink) {
3949 ad = ad_get(talloc_tos(), handle, smb_fname,
3958 * 0 byte resource fork streams are not listed by
3959 * vfs_streaminfo, as a result stream cleanup/deletion of file
3960 * deletion doesn't remove the resourcefork stream.
3963 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
3964 /* OS X ignores resource fork stream delete requests */
3972 rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
3977 rc = SMB_VFS_NEXT_UNLINK(handle, adp_smb_fname);
3978 TALLOC_FREE(adp_smb_fname);
3979 if ((rc != 0) && (errno == ENOENT) && force_unlink) {
3986 static int fruit_unlink_rsrc_xattr(vfs_handle_struct *handle,
3987 const struct smb_filename *smb_fname,
3991 * OS X ignores resource fork stream delete requests, so nothing to do
3992 * here. Removing the file will remove the xattr anyway, so we don't
3993 * have to take care of removing 0 byte resource forks that could be
3999 static int fruit_unlink_rsrc(vfs_handle_struct *handle,
4000 const struct smb_filename *smb_fname,
4003 struct fruit_config_data *config = NULL;
4006 SMB_VFS_HANDLE_GET_DATA(handle, config,
4007 struct fruit_config_data, return -1);
4009 switch (config->rsrc) {
4010 case FRUIT_RSRC_STREAM:
4011 rc = fruit_unlink_rsrc_stream(handle, smb_fname, force_unlink);
4014 case FRUIT_RSRC_ADFILE:
4015 rc = fruit_unlink_rsrc_adouble(handle, smb_fname, force_unlink);
4018 case FRUIT_RSRC_XATTR:
4019 rc = fruit_unlink_rsrc_xattr(handle, smb_fname, force_unlink);
4023 DBG_ERR("Unsupported rsrc config [%d]\n", config->rsrc);
4030 static int fruit_unlink(vfs_handle_struct *handle,
4031 const struct smb_filename *smb_fname)
4034 struct fruit_config_data *config = NULL;
4035 struct smb_filename *rsrc_smb_fname = NULL;
4037 SMB_VFS_HANDLE_GET_DATA(handle, config,
4038 struct fruit_config_data, return -1);
4040 if (is_afpinfo_stream(smb_fname)) {
4041 return fruit_unlink_meta(handle, smb_fname);
4042 } else if (is_afpresource_stream(smb_fname)) {
4043 return fruit_unlink_rsrc(handle, smb_fname, false);
4044 } else if (is_ntfs_stream_smb_fname(smb_fname)) {
4045 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4046 } else if (is_adouble_file(smb_fname->base_name)) {
4047 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4051 * A request to delete the base file. Because 0 byte resource
4052 * fork streams are not listed by fruit_streaminfo,
4053 * delete_all_streams() can't remove 0 byte resource fork
4054 * streams, so we have to cleanup this here.
4056 rsrc_smb_fname = synthetic_smb_fname(talloc_tos(),
4057 smb_fname->base_name,
4058 AFPRESOURCE_STREAM_NAME,
4061 if (rsrc_smb_fname == NULL) {
4065 rc = fruit_unlink_rsrc(handle, rsrc_smb_fname, true);
4066 if ((rc != 0) && (errno != ENOENT)) {
4067 DBG_ERR("Forced unlink of [%s] failed [%s]\n",
4068 smb_fname_str_dbg(rsrc_smb_fname), strerror(errno));
4069 TALLOC_FREE(rsrc_smb_fname);
4072 TALLOC_FREE(rsrc_smb_fname);
4074 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4077 static int fruit_chmod(vfs_handle_struct *handle,
4078 const struct smb_filename *smb_fname,
4082 struct fruit_config_data *config = NULL;
4083 struct smb_filename *smb_fname_adp = NULL;
4085 rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname, mode);
4090 SMB_VFS_HANDLE_GET_DATA(handle, config,
4091 struct fruit_config_data, return -1);
4093 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4097 if (!VALID_STAT(smb_fname->st)) {
4101 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
4105 rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_adp);
4110 DEBUG(10, ("fruit_chmod: %s\n", smb_fname_adp->base_name));
4112 rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname_adp, mode);
4113 if (errno == ENOENT) {
4117 TALLOC_FREE(smb_fname_adp);
4121 static int fruit_chown(vfs_handle_struct *handle,
4122 const struct smb_filename *smb_fname,
4127 struct fruit_config_data *config = NULL;
4128 struct smb_filename *adp_smb_fname = NULL;
4130 rc = SMB_VFS_NEXT_CHOWN(handle, smb_fname, uid, gid);
4135 SMB_VFS_HANDLE_GET_DATA(handle, config,
4136 struct fruit_config_data, return -1);
4138 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4142 if (!VALID_STAT(smb_fname->st)) {
4146 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
4150 rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
4155 DEBUG(10, ("fruit_chown: %s\n", adp_smb_fname->base_name));
4157 rc = SMB_VFS_NEXT_CHOWN(handle, adp_smb_fname, uid, gid);
4158 if (errno == ENOENT) {
4163 TALLOC_FREE(adp_smb_fname);
4167 static int fruit_rmdir(struct vfs_handle_struct *handle,
4168 const struct smb_filename *smb_fname)
4172 struct fruit_config_data *config;
4174 SMB_VFS_HANDLE_GET_DATA(handle, config,
4175 struct fruit_config_data, return -1);
4177 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4182 * Due to there is no way to change bDeleteVetoFiles variable
4183 * from this module, need to clean up ourselves
4186 dh = SMB_VFS_OPENDIR(handle->conn, smb_fname, NULL, 0);
4191 while ((de = SMB_VFS_READDIR(handle->conn, dh, NULL)) != NULL) {
4192 struct adouble *ad = NULL;
4194 struct smb_filename *ad_smb_fname = NULL;
4197 if (!is_adouble_file(de->d_name)) {
4201 p = talloc_asprintf(talloc_tos(), "%s/%s",
4202 smb_fname->base_name, de->d_name);
4204 DBG_ERR("talloc_asprintf failed\n");
4208 ad_smb_fname = synthetic_smb_fname(talloc_tos(), p,
4212 if (ad_smb_fname == NULL) {
4213 DBG_ERR("synthetic_smb_fname failed\n");
4218 * Check whether it's a valid AppleDouble file, if
4219 * yes, delete it, ignore it otherwise.
4221 ad = ad_get(talloc_tos(), handle, ad_smb_fname, ADOUBLE_RSRC);
4223 TALLOC_FREE(ad_smb_fname);
4229 ret = SMB_VFS_NEXT_UNLINK(handle, ad_smb_fname);
4231 DBG_ERR("Deleting [%s] failed\n",
4232 smb_fname_str_dbg(ad_smb_fname));
4234 TALLOC_FREE(ad_smb_fname);
4239 SMB_VFS_CLOSEDIR(handle->conn, dh);
4241 return SMB_VFS_NEXT_RMDIR(handle, smb_fname);
4244 static ssize_t fruit_pread_meta_stream(vfs_handle_struct *handle,
4245 files_struct *fsp, void *data,
4246 size_t n, off_t offset)
4251 nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4252 if (nread == -1 || nread == n) {
4256 DBG_ERR("Removing [%s] after short read [%zd]\n",
4257 fsp_str_dbg(fsp), nread);
4259 ret = SMB_VFS_NEXT_UNLINK(handle, fsp->fsp_name);
4261 DBG_ERR("Removing [%s] failed\n", fsp_str_dbg(fsp));
4269 static ssize_t fruit_pread_meta_adouble(vfs_handle_struct *handle,
4270 files_struct *fsp, void *data,
4271 size_t n, off_t offset)
4274 struct adouble *ad = NULL;
4275 char afpinfo_buf[AFP_INFO_SIZE];
4279 ai = afpinfo_new(talloc_tos());
4284 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
4290 p = ad_get_entry(ad, ADEID_FINDERI);
4292 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
4297 memcpy(&ai->afpi_FinderInfo[0], p, ADEDLEN_FINDERI);
4299 nread = afpinfo_pack(ai, afpinfo_buf);
4300 if (nread != AFP_INFO_SIZE) {
4305 memcpy(data, afpinfo_buf, n);
4313 static ssize_t fruit_pread_meta(vfs_handle_struct *handle,
4314 files_struct *fsp, void *data,
4315 size_t n, off_t offset)
4317 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4322 * OS X has a off-by-1 error in the offset calculation, so we're
4323 * bug compatible here. It won't hurt, as any relevant real
4324 * world read requests from the AFP_AfpInfo stream will be
4325 * offset=0 n=60. offset is ignored anyway, see below.
4327 if ((offset < 0) || (offset >= AFP_INFO_SIZE + 1)) {
4332 DBG_ERR("Failed to fetch fsp extension");
4336 /* Yes, macOS always reads from offset 0 */
4338 to_return = MIN(n, AFP_INFO_SIZE);
4340 switch (fio->config->meta) {
4341 case FRUIT_META_STREAM:
4342 nread = fruit_pread_meta_stream(handle, fsp, data,
4346 case FRUIT_META_NETATALK:
4347 nread = fruit_pread_meta_adouble(handle, fsp, data,
4352 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
4356 if (nread == -1 && fio->created) {
4358 char afpinfo_buf[AFP_INFO_SIZE];
4360 ai = afpinfo_new(talloc_tos());
4365 nread = afpinfo_pack(ai, afpinfo_buf);
4367 if (nread != AFP_INFO_SIZE) {
4371 memcpy(data, afpinfo_buf, to_return);
4378 static ssize_t fruit_pread_rsrc_stream(vfs_handle_struct *handle,
4379 files_struct *fsp, void *data,
4380 size_t n, off_t offset)
4382 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4385 static ssize_t fruit_pread_rsrc_xattr(vfs_handle_struct *handle,
4386 files_struct *fsp, void *data,
4387 size_t n, off_t offset)
4389 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4392 static ssize_t fruit_pread_rsrc_adouble(vfs_handle_struct *handle,
4393 files_struct *fsp, void *data,
4394 size_t n, off_t offset)
4396 struct adouble *ad = NULL;
4399 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
4404 nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n,
4405 offset + ad_getentryoff(ad, ADEID_RFORK));
4411 static ssize_t fruit_pread_rsrc(vfs_handle_struct *handle,
4412 files_struct *fsp, void *data,
4413 size_t n, off_t offset)
4415 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4423 switch (fio->config->rsrc) {
4424 case FRUIT_RSRC_STREAM:
4425 nread = fruit_pread_rsrc_stream(handle, fsp, data, n, offset);
4428 case FRUIT_RSRC_ADFILE:
4429 nread = fruit_pread_rsrc_adouble(handle, fsp, data, n, offset);
4432 case FRUIT_RSRC_XATTR:
4433 nread = fruit_pread_rsrc_xattr(handle, fsp, data, n, offset);
4437 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
4444 static ssize_t fruit_pread(vfs_handle_struct *handle,
4445 files_struct *fsp, void *data,
4446 size_t n, off_t offset)
4448 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4451 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4452 fsp_str_dbg(fsp), (intmax_t)offset, n);
4455 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4458 if (fio->type == ADOUBLE_META) {
4459 nread = fruit_pread_meta(handle, fsp, data, n, offset);
4461 nread = fruit_pread_rsrc(handle, fsp, data, n, offset);
4464 DBG_DEBUG("Path [%s] nread [%zd]\n", fsp_str_dbg(fsp), nread);
4468 static bool fruit_must_handle_aio_stream(struct fio *fio)
4474 if (fio->type == ADOUBLE_META) {
4478 if ((fio->type == ADOUBLE_RSRC) &&
4479 (fio->config->rsrc == FRUIT_RSRC_ADFILE))
4487 struct fruit_pread_state {
4489 struct vfs_aio_state vfs_aio_state;
4492 static void fruit_pread_done(struct tevent_req *subreq);
4494 static struct tevent_req *fruit_pread_send(
4495 struct vfs_handle_struct *handle,
4496 TALLOC_CTX *mem_ctx,
4497 struct tevent_context *ev,
4498 struct files_struct *fsp,
4500 size_t n, off_t offset)
4502 struct tevent_req *req = NULL;
4503 struct tevent_req *subreq = NULL;
4504 struct fruit_pread_state *state = NULL;
4505 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4507 req = tevent_req_create(mem_ctx, &state,
4508 struct fruit_pread_state);
4513 if (fruit_must_handle_aio_stream(fio)) {
4514 state->nread = SMB_VFS_PREAD(fsp, data, n, offset);
4515 if (state->nread != n) {
4516 if (state->nread != -1) {
4519 tevent_req_error(req, errno);
4520 return tevent_req_post(req, ev);
4522 tevent_req_done(req);
4523 return tevent_req_post(req, ev);
4526 subreq = SMB_VFS_NEXT_PREAD_SEND(state, ev, handle, fsp,
4528 if (tevent_req_nomem(req, subreq)) {
4529 return tevent_req_post(req, ev);
4531 tevent_req_set_callback(subreq, fruit_pread_done, req);
4535 static void fruit_pread_done(struct tevent_req *subreq)
4537 struct tevent_req *req = tevent_req_callback_data(
4538 subreq, struct tevent_req);
4539 struct fruit_pread_state *state = tevent_req_data(
4540 req, struct fruit_pread_state);
4542 state->nread = SMB_VFS_PREAD_RECV(subreq, &state->vfs_aio_state);
4543 TALLOC_FREE(subreq);
4545 if (tevent_req_error(req, state->vfs_aio_state.error)) {
4548 tevent_req_done(req);
4551 static ssize_t fruit_pread_recv(struct tevent_req *req,
4552 struct vfs_aio_state *vfs_aio_state)
4554 struct fruit_pread_state *state = tevent_req_data(
4555 req, struct fruit_pread_state);
4557 if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
4561 *vfs_aio_state = state->vfs_aio_state;
4562 return state->nread;
4565 static ssize_t fruit_pwrite_meta_stream(vfs_handle_struct *handle,
4566 files_struct *fsp, const void *data,
4567 size_t n, off_t offset)
4569 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4575 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4576 fsp_str_dbg(fsp), (intmax_t)offset, n);
4585 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
4587 DBG_ERR("Close [%s] failed: %s\n",
4588 fsp_str_dbg(fsp), strerror(errno));
4593 fd = SMB_VFS_NEXT_OPEN(handle,
4599 DBG_ERR("On-demand create [%s] in write failed: %s\n",
4600 fsp_str_dbg(fsp), strerror(errno));
4604 fio->fake_fd = false;
4607 ai = afpinfo_unpack(talloc_tos(), data);
4612 if (ai_empty_finderinfo(ai)) {
4614 * Writing an all 0 blob to the metadata stream results in the
4615 * stream being removed on a macOS server. This ensures we
4616 * behave the same and it verified by the "delete AFP_AfpInfo by
4617 * writing all 0" test.
4619 ret = SMB_VFS_NEXT_FTRUNCATE(handle, fsp, 0);
4621 DBG_ERR("SMB_VFS_NEXT_FTRUNCATE on [%s] failed\n",
4626 ok = set_delete_on_close(
4629 handle->conn->session_info->security_token,
4630 handle->conn->session_info->unix_token);
4632 DBG_ERR("set_delete_on_close on [%s] failed\n",
4639 nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4640 if (nwritten != n) {
4647 static ssize_t fruit_pwrite_meta_netatalk(vfs_handle_struct *handle,
4648 files_struct *fsp, const void *data,
4649 size_t n, off_t offset)
4651 struct adouble *ad = NULL;
4657 ai = afpinfo_unpack(talloc_tos(), data);
4662 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
4664 ad = ad_init(talloc_tos(), ADOUBLE_META);
4669 p = ad_get_entry(ad, ADEID_FINDERI);
4671 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
4676 memcpy(p, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
4678 ret = ad_fset(handle, ad, fsp);
4680 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
4687 if (!ai_empty_finderinfo(ai)) {
4692 * Writing an all 0 blob to the metadata stream results in the stream
4693 * being removed on a macOS server. This ensures we behave the same and
4694 * it verified by the "delete AFP_AfpInfo by writing all 0" test.
4697 ok = set_delete_on_close(
4700 handle->conn->session_info->security_token,
4701 handle->conn->session_info->unix_token);
4703 DBG_ERR("set_delete_on_close on [%s] failed\n",
4711 static ssize_t fruit_pwrite_meta(vfs_handle_struct *handle,
4712 files_struct *fsp, const void *data,
4713 size_t n, off_t offset)
4715 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4717 uint8_t buf[AFP_INFO_SIZE];
4723 DBG_ERR("Failed to fetch fsp extension");
4732 if (offset != 0 && n < 60) {
4737 cmp = memcmp(data, "AFP", 3);
4743 if (n <= AFP_OFF_FinderInfo) {
4745 * Nothing to do here really, just return
4753 if (to_copy > AFP_INFO_SIZE) {
4754 to_copy = AFP_INFO_SIZE;
4756 memcpy(buf, data, to_copy);
4759 if (to_write != AFP_INFO_SIZE) {
4760 to_write = AFP_INFO_SIZE;
4763 switch (fio->config->meta) {
4764 case FRUIT_META_STREAM:
4765 nwritten = fruit_pwrite_meta_stream(handle,
4772 case FRUIT_META_NETATALK:
4773 nwritten = fruit_pwrite_meta_netatalk(handle,
4781 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
4785 if (nwritten != to_write) {
4790 * Return the requested amount, verified against macOS SMB server
4795 static ssize_t fruit_pwrite_rsrc_stream(vfs_handle_struct *handle,
4796 files_struct *fsp, const void *data,
4797 size_t n, off_t offset)
4799 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4802 static ssize_t fruit_pwrite_rsrc_xattr(vfs_handle_struct *handle,
4803 files_struct *fsp, const void *data,
4804 size_t n, off_t offset)
4806 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4809 static ssize_t fruit_pwrite_rsrc_adouble(vfs_handle_struct *handle,
4810 files_struct *fsp, const void *data,
4811 size_t n, off_t offset)
4813 struct adouble *ad = NULL;
4817 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
4819 DBG_ERR("ad_get [%s] failed\n", fsp_str_dbg(fsp));
4823 nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n,
4824 offset + ad_getentryoff(ad, ADEID_RFORK));
4825 if (nwritten != n) {
4826 DBG_ERR("Short write on [%s] [%zd/%zd]\n",
4827 fsp_str_dbg(fsp), nwritten, n);
4832 if ((n + offset) > ad_getentrylen(ad, ADEID_RFORK)) {
4833 ad_setentrylen(ad, ADEID_RFORK, n + offset);
4834 ret = ad_fset(handle, ad, fsp);
4836 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
4846 static ssize_t fruit_pwrite_rsrc(vfs_handle_struct *handle,
4847 files_struct *fsp, const void *data,
4848 size_t n, off_t offset)
4850 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4854 DBG_ERR("Failed to fetch fsp extension");
4858 switch (fio->config->rsrc) {
4859 case FRUIT_RSRC_STREAM:
4860 nwritten = fruit_pwrite_rsrc_stream(handle, fsp, data, n, offset);
4863 case FRUIT_RSRC_ADFILE:
4864 nwritten = fruit_pwrite_rsrc_adouble(handle, fsp, data, n, offset);
4867 case FRUIT_RSRC_XATTR:
4868 nwritten = fruit_pwrite_rsrc_xattr(handle, fsp, data, n, offset);
4872 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
4879 static ssize_t fruit_pwrite(vfs_handle_struct *handle,
4880 files_struct *fsp, const void *data,
4881 size_t n, off_t offset)
4883 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4886 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4887 fsp_str_dbg(fsp), (intmax_t)offset, n);
4890 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4893 if (fio->type == ADOUBLE_META) {
4894 nwritten = fruit_pwrite_meta(handle, fsp, data, n, offset);
4896 nwritten = fruit_pwrite_rsrc(handle, fsp, data, n, offset);
4899 DBG_DEBUG("Path [%s] nwritten=%zd\n", fsp_str_dbg(fsp), nwritten);
4903 struct fruit_pwrite_state {
4905 struct vfs_aio_state vfs_aio_state;
4908 static void fruit_pwrite_done(struct tevent_req *subreq);
4910 static struct tevent_req *fruit_pwrite_send(
4911 struct vfs_handle_struct *handle,
4912 TALLOC_CTX *mem_ctx,
4913 struct tevent_context *ev,
4914 struct files_struct *fsp,
4916 size_t n, off_t offset)
4918 struct tevent_req *req = NULL;
4919 struct tevent_req *subreq = NULL;
4920 struct fruit_pwrite_state *state = NULL;
4921 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4923 req = tevent_req_create(mem_ctx, &state,
4924 struct fruit_pwrite_state);
4929 if (fruit_must_handle_aio_stream(fio)) {
4930 state->nwritten = SMB_VFS_PWRITE(fsp, data, n, offset);
4931 if (state->nwritten != n) {
4932 if (state->nwritten != -1) {
4935 tevent_req_error(req, errno);
4936 return tevent_req_post(req, ev);
4938 tevent_req_done(req);
4939 return tevent_req_post(req, ev);
4942 subreq = SMB_VFS_NEXT_PWRITE_SEND(state, ev, handle, fsp,
4944 if (tevent_req_nomem(req, subreq)) {
4945 return tevent_req_post(req, ev);
4947 tevent_req_set_callback(subreq, fruit_pwrite_done, req);
4951 static void fruit_pwrite_done(struct tevent_req *subreq)
4953 struct tevent_req *req = tevent_req_callback_data(
4954 subreq, struct tevent_req);
4955 struct fruit_pwrite_state *state = tevent_req_data(
4956 req, struct fruit_pwrite_state);
4958 state->nwritten = SMB_VFS_PWRITE_RECV(subreq, &state->vfs_aio_state);
4959 TALLOC_FREE(subreq);
4961 if (tevent_req_error(req, state->vfs_aio_state.error)) {
4964 tevent_req_done(req);
4967 static ssize_t fruit_pwrite_recv(struct tevent_req *req,
4968 struct vfs_aio_state *vfs_aio_state)
4970 struct fruit_pwrite_state *state = tevent_req_data(
4971 req, struct fruit_pwrite_state);
4973 if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
4977 *vfs_aio_state = state->vfs_aio_state;
4978 return state->nwritten;
4982 * Helper to stat/lstat the base file of an smb_fname.
4984 static int fruit_stat_base(vfs_handle_struct *handle,
4985 struct smb_filename *smb_fname,
4988 char *tmp_stream_name;
4991 tmp_stream_name = smb_fname->stream_name;
4992 smb_fname->stream_name = NULL;
4994 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
4996 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
4998 smb_fname->stream_name = tmp_stream_name;
5000 DBG_DEBUG("fruit_stat_base [%s] dev [%ju] ino [%ju]\n",
5001 smb_fname->base_name,
5002 (uintmax_t)smb_fname->st.st_ex_dev,
5003 (uintmax_t)smb_fname->st.st_ex_ino);
5007 static int fruit_stat_meta_stream(vfs_handle_struct *handle,
5008 struct smb_filename *smb_fname,
5014 ret = fruit_stat_base(handle, smb_fname, false);
5019 ino = fruit_inode(&smb_fname->st, smb_fname->stream_name);
5022 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
5024 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5027 smb_fname->st.st_ex_ino = ino;
5032 static int fruit_stat_meta_netatalk(vfs_handle_struct *handle,
5033 struct smb_filename *smb_fname,
5036 struct adouble *ad = NULL;
5038 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5040 DBG_INFO("fruit_stat_meta %s: %s\n",
5041 smb_fname_str_dbg(smb_fname), strerror(errno));
5047 /* Populate the stat struct with info from the base file. */
5048 if (fruit_stat_base(handle, smb_fname, follow_links) == -1) {
5051 smb_fname->st.st_ex_size = AFP_INFO_SIZE;
5052 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5053 smb_fname->stream_name);
5057 static int fruit_stat_meta(vfs_handle_struct *handle,
5058 struct smb_filename *smb_fname,
5061 struct fruit_config_data *config = NULL;
5064 SMB_VFS_HANDLE_GET_DATA(handle, config,
5065 struct fruit_config_data, return -1);
5067 switch (config->meta) {
5068 case FRUIT_META_STREAM:
5069 ret = fruit_stat_meta_stream(handle, smb_fname, follow_links);
5072 case FRUIT_META_NETATALK:
5073 ret = fruit_stat_meta_netatalk(handle, smb_fname, follow_links);
5077 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
5084 static int fruit_stat_rsrc_netatalk(vfs_handle_struct *handle,
5085 struct smb_filename *smb_fname,
5088 struct adouble *ad = NULL;
5091 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
5097 /* Populate the stat struct with info from the base file. */
5098 ret = fruit_stat_base(handle, smb_fname, follow_links);
5104 smb_fname->st.st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
5105 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5106 smb_fname->stream_name);
5111 static int fruit_stat_rsrc_stream(vfs_handle_struct *handle,
5112 struct smb_filename *smb_fname,
5118 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
5120 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5126 static int fruit_stat_rsrc_xattr(vfs_handle_struct *handle,
5127 struct smb_filename *smb_fname,
5130 #ifdef HAVE_ATTROPEN
5134 /* Populate the stat struct with info from the base file. */
5135 ret = fruit_stat_base(handle, smb_fname, follow_links);
5140 fd = attropen(smb_fname->base_name,
5141 AFPRESOURCE_EA_NETATALK,
5147 ret = sys_fstat(fd, &smb_fname->st, false);
5150 DBG_ERR("fstat [%s:%s] failed\n", smb_fname->base_name,
5151 AFPRESOURCE_EA_NETATALK);
5157 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5158 smb_fname->stream_name);
5168 static int fruit_stat_rsrc(vfs_handle_struct *handle,
5169 struct smb_filename *smb_fname,
5172 struct fruit_config_data *config = NULL;
5175 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
5177 SMB_VFS_HANDLE_GET_DATA(handle, config,
5178 struct fruit_config_data, return -1);
5180 switch (config->rsrc) {
5181 case FRUIT_RSRC_STREAM:
5182 ret = fruit_stat_rsrc_stream(handle, smb_fname, follow_links);
5185 case FRUIT_RSRC_XATTR:
5186 ret = fruit_stat_rsrc_xattr(handle, smb_fname, follow_links);
5189 case FRUIT_RSRC_ADFILE:
5190 ret = fruit_stat_rsrc_netatalk(handle, smb_fname, follow_links);
5194 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
5201 static int fruit_stat(vfs_handle_struct *handle,
5202 struct smb_filename *smb_fname)
5206 DEBUG(10, ("fruit_stat called for %s\n",
5207 smb_fname_str_dbg(smb_fname)));
5209 if (!is_ntfs_stream_smb_fname(smb_fname)
5210 || is_ntfs_default_stream_smb_fname(smb_fname)) {
5211 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
5213 update_btime(handle, smb_fname);
5219 * Note if lp_posix_paths() is true, we can never
5220 * get here as is_ntfs_stream_smb_fname() is
5221 * always false. So we never need worry about
5222 * not following links here.
5225 if (is_afpinfo_stream(smb_fname)) {
5226 rc = fruit_stat_meta(handle, smb_fname, true);
5227 } else if (is_afpresource_stream(smb_fname)) {
5228 rc = fruit_stat_rsrc(handle, smb_fname, true);
5230 return SMB_VFS_NEXT_STAT(handle, smb_fname);
5234 update_btime(handle, smb_fname);
5235 smb_fname->st.st_ex_mode &= ~S_IFMT;
5236 smb_fname->st.st_ex_mode |= S_IFREG;
5237 smb_fname->st.st_ex_blocks =
5238 smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
5243 static int fruit_lstat(vfs_handle_struct *handle,
5244 struct smb_filename *smb_fname)
5248 DEBUG(10, ("fruit_lstat called for %s\n",
5249 smb_fname_str_dbg(smb_fname)));
5251 if (!is_ntfs_stream_smb_fname(smb_fname)
5252 || is_ntfs_default_stream_smb_fname(smb_fname)) {
5253 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5255 update_btime(handle, smb_fname);
5260 if (is_afpinfo_stream(smb_fname)) {
5261 rc = fruit_stat_meta(handle, smb_fname, false);
5262 } else if (is_afpresource_stream(smb_fname)) {
5263 rc = fruit_stat_rsrc(handle, smb_fname, false);
5265 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5269 update_btime(handle, smb_fname);
5270 smb_fname->st.st_ex_mode &= ~S_IFMT;
5271 smb_fname->st.st_ex_mode |= S_IFREG;
5272 smb_fname->st.st_ex_blocks =
5273 smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
5278 static int fruit_fstat_meta_stream(vfs_handle_struct *handle,
5280 SMB_STRUCT_STAT *sbuf)
5282 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5283 struct smb_filename smb_fname;
5292 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5297 *sbuf = fsp->base_fsp->fsp_name->st;
5298 sbuf->st_ex_size = AFP_INFO_SIZE;
5299 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5303 smb_fname = (struct smb_filename) {
5304 .base_name = fsp->fsp_name->base_name,
5307 ret = fruit_stat_base(handle, &smb_fname, false);
5311 *sbuf = smb_fname.st;
5313 ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5315 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5320 sbuf->st_ex_ino = ino;
5324 static int fruit_fstat_meta_netatalk(vfs_handle_struct *handle,
5326 SMB_STRUCT_STAT *sbuf)
5330 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5335 *sbuf = fsp->base_fsp->fsp_name->st;
5336 sbuf->st_ex_size = AFP_INFO_SIZE;
5337 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5342 static int fruit_fstat_meta(vfs_handle_struct *handle,
5344 SMB_STRUCT_STAT *sbuf,
5349 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
5351 switch (fio->config->meta) {
5352 case FRUIT_META_STREAM:
5353 ret = fruit_fstat_meta_stream(handle, fsp, sbuf);
5356 case FRUIT_META_NETATALK:
5357 ret = fruit_fstat_meta_netatalk(handle, fsp, sbuf);
5361 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
5365 DBG_DEBUG("Path [%s] ret [%d]\n", fsp_str_dbg(fsp), ret);
5369 static int fruit_fstat_rsrc_xattr(vfs_handle_struct *handle,
5371 SMB_STRUCT_STAT *sbuf)
5373 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5376 static int fruit_fstat_rsrc_stream(vfs_handle_struct *handle,
5378 SMB_STRUCT_STAT *sbuf)
5380 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5383 static int fruit_fstat_rsrc_adouble(vfs_handle_struct *handle,
5385 SMB_STRUCT_STAT *sbuf)
5387 struct adouble *ad = NULL;
5390 /* Populate the stat struct with info from the base file. */
5391 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5396 ad = ad_get(talloc_tos(), handle,
5397 fsp->base_fsp->fsp_name,
5400 DBG_ERR("ad_get [%s] failed [%s]\n",
5401 fsp_str_dbg(fsp), strerror(errno));
5405 *sbuf = fsp->base_fsp->fsp_name->st;
5406 sbuf->st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
5407 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5413 static int fruit_fstat_rsrc(vfs_handle_struct *handle, files_struct *fsp,
5414 SMB_STRUCT_STAT *sbuf, struct fio *fio)
5418 switch (fio->config->rsrc) {
5419 case FRUIT_RSRC_STREAM:
5420 ret = fruit_fstat_rsrc_stream(handle, fsp, sbuf);
5423 case FRUIT_RSRC_ADFILE:
5424 ret = fruit_fstat_rsrc_adouble(handle, fsp, sbuf);
5427 case FRUIT_RSRC_XATTR:
5428 ret = fruit_fstat_rsrc_xattr(handle, fsp, sbuf);
5432 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
5439 static int fruit_fstat(vfs_handle_struct *handle, files_struct *fsp,
5440 SMB_STRUCT_STAT *sbuf)
5442 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5446 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5449 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
5451 if (fio->type == ADOUBLE_META) {
5452 rc = fruit_fstat_meta(handle, fsp, sbuf, fio);
5454 rc = fruit_fstat_rsrc(handle, fsp, sbuf, fio);
5458 sbuf->st_ex_mode &= ~S_IFMT;
5459 sbuf->st_ex_mode |= S_IFREG;
5460 sbuf->st_ex_blocks = sbuf->st_ex_size / STAT_ST_BLOCKSIZE + 1;
5463 DBG_DEBUG("Path [%s] rc [%d] size [%"PRIdMAX"]\n",
5464 fsp_str_dbg(fsp), rc, (intmax_t)sbuf->st_ex_size);
5468 static NTSTATUS delete_invalid_meta_stream(
5469 vfs_handle_struct *handle,
5470 const struct smb_filename *smb_fname,
5471 TALLOC_CTX *mem_ctx,
5472 unsigned int *pnum_streams,
5473 struct stream_struct **pstreams,
5476 struct smb_filename *sname = NULL;
5480 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams, AFPINFO_STREAM);
5482 return NT_STATUS_INTERNAL_ERROR;
5486 return NT_STATUS_OK;
5489 sname = synthetic_smb_fname(talloc_tos(),
5490 smb_fname->base_name,
5491 AFPINFO_STREAM_NAME,
5493 if (sname == NULL) {
5494 return NT_STATUS_NO_MEMORY;
5497 ret = SMB_VFS_NEXT_UNLINK(handle, sname);
5500 DBG_ERR("Removing [%s] failed\n", smb_fname_str_dbg(sname));
5501 return map_nt_error_from_unix(errno);
5504 return NT_STATUS_OK;
5507 static NTSTATUS fruit_streaminfo_meta_stream(
5508 vfs_handle_struct *handle,
5509 struct files_struct *fsp,
5510 const struct smb_filename *smb_fname,
5511 TALLOC_CTX *mem_ctx,
5512 unsigned int *pnum_streams,
5513 struct stream_struct **pstreams)
5515 struct stream_struct *stream = *pstreams;
5516 unsigned int num_streams = *pnum_streams;
5519 for (i = 0; i < num_streams; i++) {
5520 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
5525 if (i == num_streams) {
5526 return NT_STATUS_OK;
5529 if (stream[i].size != AFP_INFO_SIZE) {
5530 DBG_ERR("Removing invalid AFPINFO_STREAM size [%jd] from [%s]\n",
5531 (intmax_t)stream[i].size, smb_fname_str_dbg(smb_fname));
5533 return delete_invalid_meta_stream(handle,
5542 return NT_STATUS_OK;
5545 static NTSTATUS fruit_streaminfo_meta_netatalk(
5546 vfs_handle_struct *handle,
5547 struct files_struct *fsp,
5548 const struct smb_filename *smb_fname,
5549 TALLOC_CTX *mem_ctx,
5550 unsigned int *pnum_streams,
5551 struct stream_struct **pstreams)
5553 struct stream_struct *stream = *pstreams;
5554 unsigned int num_streams = *pnum_streams;
5555 struct adouble *ad = NULL;
5560 /* Remove the Netatalk xattr from the list */
5561 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5562 ":" NETATALK_META_XATTR ":$DATA");
5564 return NT_STATUS_NO_MEMORY;
5568 * Check if there's a AFPINFO_STREAM from the VFS streams
5569 * backend and if yes, remove it from the list
5571 for (i = 0; i < num_streams; i++) {
5572 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
5577 if (i < num_streams) {
5578 DBG_WARNING("Unexpected AFPINFO_STREAM on [%s]\n",
5579 smb_fname_str_dbg(smb_fname));
5581 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5584 return NT_STATUS_INTERNAL_ERROR;
5588 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5590 return NT_STATUS_OK;
5593 is_fi_empty = ad_empty_finderinfo(ad);
5597 return NT_STATUS_OK;
5600 ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
5601 AFPINFO_STREAM_NAME, AFP_INFO_SIZE,
5602 smb_roundup(handle->conn, AFP_INFO_SIZE));
5604 return NT_STATUS_NO_MEMORY;
5607 return NT_STATUS_OK;
5610 static NTSTATUS fruit_streaminfo_meta(vfs_handle_struct *handle,
5611 struct files_struct *fsp,
5612 const struct smb_filename *smb_fname,
5613 TALLOC_CTX *mem_ctx,
5614 unsigned int *pnum_streams,
5615 struct stream_struct **pstreams)
5617 struct fruit_config_data *config = NULL;
5620 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5621 return NT_STATUS_INTERNAL_ERROR);
5623 switch (config->meta) {
5624 case FRUIT_META_NETATALK:
5625 status = fruit_streaminfo_meta_netatalk(handle, fsp, smb_fname,
5626 mem_ctx, pnum_streams,
5630 case FRUIT_META_STREAM:
5631 status = fruit_streaminfo_meta_stream(handle, fsp, smb_fname,
5632 mem_ctx, pnum_streams,
5637 return NT_STATUS_INTERNAL_ERROR;
5643 static NTSTATUS fruit_streaminfo_rsrc_stream(
5644 vfs_handle_struct *handle,
5645 struct files_struct *fsp,
5646 const struct smb_filename *smb_fname,
5647 TALLOC_CTX *mem_ctx,
5648 unsigned int *pnum_streams,
5649 struct stream_struct **pstreams)
5653 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
5655 DBG_ERR("Filtering resource stream failed\n");
5656 return NT_STATUS_INTERNAL_ERROR;
5658 return NT_STATUS_OK;
5661 static NTSTATUS fruit_streaminfo_rsrc_xattr(
5662 vfs_handle_struct *handle,
5663 struct files_struct *fsp,
5664 const struct smb_filename *smb_fname,
5665 TALLOC_CTX *mem_ctx,
5666 unsigned int *pnum_streams,
5667 struct stream_struct **pstreams)
5671 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
5673 DBG_ERR("Filtering resource stream failed\n");
5674 return NT_STATUS_INTERNAL_ERROR;
5676 return NT_STATUS_OK;
5679 static NTSTATUS fruit_streaminfo_rsrc_adouble(
5680 vfs_handle_struct *handle,
5681 struct files_struct *fsp,
5682 const struct smb_filename *smb_fname,
5683 TALLOC_CTX *mem_ctx,
5684 unsigned int *pnum_streams,
5685 struct stream_struct **pstreams)
5687 struct stream_struct *stream = *pstreams;
5688 unsigned int num_streams = *pnum_streams;
5689 struct adouble *ad = NULL;
5695 * Check if there's a AFPRESOURCE_STREAM from the VFS streams backend
5696 * and if yes, remove it from the list
5698 for (i = 0; i < num_streams; i++) {
5699 if (strequal_m(stream[i].name, AFPRESOURCE_STREAM)) {
5704 if (i < num_streams) {
5705 DBG_WARNING("Unexpected AFPRESOURCE_STREAM on [%s]\n",
5706 smb_fname_str_dbg(smb_fname));
5708 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5709 AFPRESOURCE_STREAM);
5711 return NT_STATUS_INTERNAL_ERROR;
5715 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
5717 return NT_STATUS_OK;
5720 rlen = ad_getentrylen(ad, ADEID_RFORK);
5724 return NT_STATUS_OK;
5727 ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
5728 AFPRESOURCE_STREAM_NAME, rlen,
5729 smb_roundup(handle->conn, rlen));
5731 return NT_STATUS_NO_MEMORY;
5734 return NT_STATUS_OK;
5737 static NTSTATUS fruit_streaminfo_rsrc(vfs_handle_struct *handle,
5738 struct files_struct *fsp,
5739 const struct smb_filename *smb_fname,
5740 TALLOC_CTX *mem_ctx,
5741 unsigned int *pnum_streams,
5742 struct stream_struct **pstreams)
5744 struct fruit_config_data *config = NULL;
5747 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5748 return NT_STATUS_INTERNAL_ERROR);
5750 switch (config->rsrc) {
5751 case FRUIT_RSRC_STREAM:
5752 status = fruit_streaminfo_rsrc_stream(handle, fsp, smb_fname,
5753 mem_ctx, pnum_streams,
5757 case FRUIT_RSRC_XATTR:
5758 status = fruit_streaminfo_rsrc_xattr(handle, fsp, smb_fname,
5759 mem_ctx, pnum_streams,
5763 case FRUIT_RSRC_ADFILE:
5764 status = fruit_streaminfo_rsrc_adouble(handle, fsp, smb_fname,
5765 mem_ctx, pnum_streams,
5770 return NT_STATUS_INTERNAL_ERROR;
5776 static void fruit_filter_empty_streams(unsigned int *pnum_streams,
5777 struct stream_struct **pstreams)
5779 unsigned num_streams = *pnum_streams;
5780 struct stream_struct *streams = *pstreams;
5783 if (!global_fruit_config.nego_aapl) {
5787 while (i < num_streams) {
5788 struct smb_filename smb_fname = (struct smb_filename) {
5789 .stream_name = streams[i].name,
5792 if (is_ntfs_default_stream_smb_fname(&smb_fname)
5793 || streams[i].size > 0)
5799 streams[i] = streams[num_streams - 1];
5803 *pnum_streams = num_streams;
5806 static NTSTATUS fruit_streaminfo(vfs_handle_struct *handle,
5807 struct files_struct *fsp,
5808 const struct smb_filename *smb_fname,
5809 TALLOC_CTX *mem_ctx,
5810 unsigned int *pnum_streams,
5811 struct stream_struct **pstreams)
5813 struct fruit_config_data *config = NULL;
5816 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5817 return NT_STATUS_UNSUCCESSFUL);
5819 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
5821 status = SMB_VFS_NEXT_STREAMINFO(handle, fsp, smb_fname, mem_ctx,
5822 pnum_streams, pstreams);
5823 if (!NT_STATUS_IS_OK(status)) {
5827 fruit_filter_empty_streams(pnum_streams, pstreams);
5829 status = fruit_streaminfo_meta(handle, fsp, smb_fname,
5830 mem_ctx, pnum_streams, pstreams);
5831 if (!NT_STATUS_IS_OK(status)) {
5835 status = fruit_streaminfo_rsrc(handle, fsp, smb_fname,
5836 mem_ctx, pnum_streams, pstreams);
5837 if (!NT_STATUS_IS_OK(status)) {
5841 return NT_STATUS_OK;
5844 static int fruit_ntimes(vfs_handle_struct *handle,
5845 const struct smb_filename *smb_fname,
5846 struct smb_file_time *ft)
5849 struct adouble *ad = NULL;
5850 struct fruit_config_data *config = NULL;
5852 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5855 if ((config->meta != FRUIT_META_NETATALK) ||
5856 null_timespec(ft->create_time))
5858 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
5861 DEBUG(10,("set btime for %s to %s\n", smb_fname_str_dbg(smb_fname),
5862 time_to_asc(convert_timespec_to_time_t(ft->create_time))));
5864 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5869 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX,
5870 convert_time_t_to_uint32_t(ft->create_time.tv_sec));
5872 rc = ad_set(handle, ad, smb_fname);
5878 DEBUG(1, ("fruit_ntimes: %s\n", smb_fname_str_dbg(smb_fname)));
5881 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
5884 static int fruit_fallocate(struct vfs_handle_struct *handle,
5885 struct files_struct *fsp,
5890 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5893 return SMB_VFS_NEXT_FALLOCATE(handle, fsp, mode, offset, len);
5896 /* Let the pwrite code path handle it. */
5901 static int fruit_ftruncate_rsrc_xattr(struct vfs_handle_struct *handle,
5902 struct files_struct *fsp,
5905 #ifdef HAVE_ATTROPEN
5906 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
5911 static int fruit_ftruncate_rsrc_adouble(struct vfs_handle_struct *handle,
5912 struct files_struct *fsp,
5916 struct adouble *ad = NULL;
5919 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
5921 DBG_DEBUG("ad_get [%s] failed [%s]\n",
5922 fsp_str_dbg(fsp), strerror(errno));
5926 ad_off = ad_getentryoff(ad, ADEID_RFORK);
5928 rc = ftruncate(fsp->fh->fd, offset + ad_off);
5934 ad_setentrylen(ad, ADEID_RFORK, offset);
5936 rc = ad_fset(handle, ad, fsp);
5938 DBG_ERR("ad_fset [%s] failed [%s]\n",
5939 fsp_str_dbg(fsp), strerror(errno));
5948 static int fruit_ftruncate_rsrc_stream(struct vfs_handle_struct *handle,
5949 struct files_struct *fsp,
5952 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
5955 static int fruit_ftruncate_rsrc(struct vfs_handle_struct *handle,
5956 struct files_struct *fsp,
5959 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5963 DBG_ERR("Failed to fetch fsp extension");
5967 switch (fio->config->rsrc) {
5968 case FRUIT_RSRC_XATTR:
5969 ret = fruit_ftruncate_rsrc_xattr(handle, fsp, offset);
5972 case FRUIT_RSRC_ADFILE:
5973 ret = fruit_ftruncate_rsrc_adouble(handle, fsp, offset);
5976 case FRUIT_RSRC_STREAM:
5977 ret = fruit_ftruncate_rsrc_stream(handle, fsp, offset);
5981 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
5989 static int fruit_ftruncate_meta(struct vfs_handle_struct *handle,
5990 struct files_struct *fsp,
5994 DBG_WARNING("ftruncate %s to %jd",
5995 fsp_str_dbg(fsp), (intmax_t)offset);
5996 /* OS X returns NT_STATUS_ALLOTTED_SPACE_EXCEEDED */
6001 /* OS X returns success but does nothing */
6002 DBG_INFO("ignoring ftruncate %s to %jd\n",
6003 fsp_str_dbg(fsp), (intmax_t)offset);
6007 static int fruit_ftruncate(struct vfs_handle_struct *handle,
6008 struct files_struct *fsp,
6011 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
6014 DBG_DEBUG("Path [%s] offset [%"PRIdMAX"]\n", fsp_str_dbg(fsp),
6018 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
6021 if (fio->type == ADOUBLE_META) {
6022 ret = fruit_ftruncate_meta(handle, fsp, offset);
6024 ret = fruit_ftruncate_rsrc(handle, fsp, offset);
6027 DBG_DEBUG("Path [%s] result [%d]\n", fsp_str_dbg(fsp), ret);
6031 static NTSTATUS fruit_create_file(vfs_handle_struct *handle,
6032 struct smb_request *req,
6033 uint16_t root_dir_fid,
6034 struct smb_filename *smb_fname,
6035 uint32_t access_mask,
6036 uint32_t share_access,
6037 uint32_t create_disposition,
6038 uint32_t create_options,
6039 uint32_t file_attributes,
6040 uint32_t oplock_request,
6041 struct smb2_lease *lease,
6042 uint64_t allocation_size,
6043 uint32_t private_flags,
6044 struct security_descriptor *sd,
6045 struct ea_list *ea_list,
6046 files_struct **result,
6048 const struct smb2_create_blobs *in_context_blobs,
6049 struct smb2_create_blobs *out_context_blobs)
6052 struct fruit_config_data *config = NULL;
6053 files_struct *fsp = NULL;
6054 struct fio *fio = NULL;
6055 bool internal_open = (oplock_request & INTERNAL_OPEN_ONLY);
6058 status = check_aapl(handle, req, in_context_blobs, out_context_blobs);
6059 if (!NT_STATUS_IS_OK(status)) {
6063 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
6064 return NT_STATUS_UNSUCCESSFUL);
6066 if (is_apple_stream(smb_fname) && !internal_open) {
6067 ret = ad_convert(handle, smb_fname);
6069 DBG_ERR("ad_convert() failed\n");
6070 return NT_STATUS_UNSUCCESSFUL;
6074 status = SMB_VFS_NEXT_CREATE_FILE(
6075 handle, req, root_dir_fid, smb_fname,
6076 access_mask, share_access,
6077 create_disposition, create_options,
6078 file_attributes, oplock_request,
6080 allocation_size, private_flags,
6081 sd, ea_list, result,
6082 pinfo, in_context_blobs, out_context_blobs);
6083 if (!NT_STATUS_IS_OK(status)) {
6089 if (global_fruit_config.nego_aapl) {
6090 if (config->posix_rename && fsp->is_directory) {
6092 * Enable POSIX directory rename behaviour
6094 fsp->posix_flags |= FSP_POSIX_FLAGS_RENAME;
6099 * If this is a plain open for existing files, opening an 0
6100 * byte size resource fork MUST fail with
6101 * NT_STATUS_OBJECT_NAME_NOT_FOUND.
6103 * Cf the vfs_fruit torture tests in test_rfork_create().
6105 if (global_fruit_config.nego_aapl &&
6106 create_disposition == FILE_OPEN &&
6107 smb_fname->st.st_ex_size == 0 &&
6108 is_ntfs_stream_smb_fname(smb_fname) &&
6109 !(is_ntfs_default_stream_smb_fname(smb_fname)))
6111 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
6115 fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
6116 if (fio != NULL && pinfo != NULL && *pinfo == FILE_WAS_CREATED) {
6117 fio->created = true;
6120 if (is_ntfs_stream_smb_fname(smb_fname)
6121 || fsp->is_directory) {
6125 if ((config->locking == FRUIT_LOCKING_NETATALK) &&
6128 status = fruit_check_access(
6132 if (!NT_STATUS_IS_OK(status)) {
6140 DEBUG(10, ("fruit_create_file: %s\n", nt_errstr(status)));
6143 close_file(req, fsp, ERROR_CLOSE);
6144 *result = fsp = NULL;
6150 static NTSTATUS fruit_readdir_attr(struct vfs_handle_struct *handle,
6151 const struct smb_filename *fname,
6152 TALLOC_CTX *mem_ctx,
6153 struct readdir_attr_data **pattr_data)
6155 struct fruit_config_data *config = NULL;
6156 struct readdir_attr_data *attr_data;
6160 SMB_VFS_HANDLE_GET_DATA(handle, config,
6161 struct fruit_config_data,
6162 return NT_STATUS_UNSUCCESSFUL);
6164 if (!global_fruit_config.nego_aapl) {
6165 return SMB_VFS_NEXT_READDIR_ATTR(handle, fname, mem_ctx, pattr_data);
6168 DEBUG(10, ("fruit_readdir_attr %s\n", fname->base_name));
6170 ret = ad_convert(handle, fname);
6172 DBG_ERR("ad_convert() failed\n");
6173 return NT_STATUS_UNSUCCESSFUL;
6176 *pattr_data = talloc_zero(mem_ctx, struct readdir_attr_data);
6177 if (*pattr_data == NULL) {
6178 return NT_STATUS_UNSUCCESSFUL;
6180 attr_data = *pattr_data;
6181 attr_data->type = RDATTR_AAPL;
6184 * Mac metadata: compressed FinderInfo, resource fork length
6187 status = readdir_attr_macmeta(handle, fname, attr_data);
6188 if (!NT_STATUS_IS_OK(status)) {
6190 * Error handling is tricky: if we return failure from
6191 * this function, the corresponding directory entry
6192 * will to be passed to the client, so we really just
6193 * want to error out on fatal errors.
6195 if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
6203 if (config->unix_info_enabled) {
6204 attr_data->attr_data.aapl.unix_mode = fname->st.st_ex_mode;
6210 if (!config->readdir_attr_max_access) {
6211 attr_data->attr_data.aapl.max_access = FILE_GENERIC_ALL;
6213 status = smbd_calculate_access_mask(
6217 SEC_FLAG_MAXIMUM_ALLOWED,
6218 &attr_data->attr_data.aapl.max_access);
6219 if (!NT_STATUS_IS_OK(status)) {
6224 return NT_STATUS_OK;
6227 DEBUG(1, ("fruit_readdir_attr %s, error: %s\n",
6228 fname->base_name, nt_errstr(status)));
6229 TALLOC_FREE(*pattr_data);
6233 static NTSTATUS fruit_fget_nt_acl(vfs_handle_struct *handle,
6235 uint32_t security_info,
6236 TALLOC_CTX *mem_ctx,
6237 struct security_descriptor **ppdesc)
6240 struct security_ace ace;
6242 struct fruit_config_data *config;
6244 SMB_VFS_HANDLE_GET_DATA(handle, config,
6245 struct fruit_config_data,
6246 return NT_STATUS_UNSUCCESSFUL);
6248 status = SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
6250 if (!NT_STATUS_IS_OK(status)) {
6255 * Add MS NFS style ACEs with uid, gid and mode
6257 if (!global_fruit_config.nego_aapl) {
6258 return NT_STATUS_OK;
6260 if (!config->unix_info_enabled) {
6261 return NT_STATUS_OK;
6264 /* First remove any existing ACE's with NFS style mode/uid/gid SIDs. */
6265 status = remove_virtual_nfs_aces(*ppdesc);
6266 if (!NT_STATUS_IS_OK(status)) {
6267 DBG_WARNING("failed to remove MS NFS style ACEs\n");
6271 /* MS NFS style mode */
6272 sid_compose(&sid, &global_sid_Unix_NFS_Mode, fsp->fsp_name->st.st_ex_mode);
6273 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6274 status = security_descriptor_dacl_add(*ppdesc, &ace);
6275 if (!NT_STATUS_IS_OK(status)) {
6276 DEBUG(1,("failed to add MS NFS style ACE\n"));
6280 /* MS NFS style uid */
6281 sid_compose(&sid, &global_sid_Unix_NFS_Users, fsp->fsp_name->st.st_ex_uid);
6282 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6283 status = security_descriptor_dacl_add(*ppdesc, &ace);
6284 if (!NT_STATUS_IS_OK(status)) {
6285 DEBUG(1,("failed to add MS NFS style ACE\n"));
6289 /* MS NFS style gid */
6290 sid_compose(&sid, &global_sid_Unix_NFS_Groups, fsp->fsp_name->st.st_ex_gid);
6291 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6292 status = security_descriptor_dacl_add(*ppdesc, &ace);
6293 if (!NT_STATUS_IS_OK(status)) {
6294 DEBUG(1,("failed to add MS NFS style ACE\n"));
6298 return NT_STATUS_OK;
6301 static NTSTATUS fruit_fset_nt_acl(vfs_handle_struct *handle,
6303 uint32_t security_info_sent,
6304 const struct security_descriptor *orig_psd)
6308 mode_t ms_nfs_mode = 0;
6310 struct security_descriptor *psd = NULL;
6311 uint32_t orig_num_aces = 0;
6313 if (orig_psd->dacl != NULL) {
6314 orig_num_aces = orig_psd->dacl->num_aces;
6317 psd = security_descriptor_copy(talloc_tos(), orig_psd);
6319 return NT_STATUS_NO_MEMORY;
6322 DBG_DEBUG("fruit_fset_nt_acl: %s\n", fsp_str_dbg(fsp));
6324 status = check_ms_nfs(handle, fsp, psd, &ms_nfs_mode, &do_chmod);
6325 if (!NT_STATUS_IS_OK(status)) {
6326 DEBUG(1, ("fruit_fset_nt_acl: check_ms_nfs failed%s\n", fsp_str_dbg(fsp)));
6332 * If only ms_nfs ACE entries were sent, ensure we set the DACL
6333 * sent/present flags correctly now we've removed them.
6336 if (orig_num_aces != 0) {
6338 * Are there any ACE's left ?
6340 if (psd->dacl->num_aces == 0) {
6341 /* No - clear the DACL sent/present flags. */
6342 security_info_sent &= ~SECINFO_DACL;
6343 psd->type &= ~SEC_DESC_DACL_PRESENT;
6347 status = SMB_VFS_NEXT_FSET_NT_ACL(handle, fsp, security_info_sent, psd);
6348 if (!NT_STATUS_IS_OK(status)) {
6349 DEBUG(1, ("fruit_fset_nt_acl: SMB_VFS_NEXT_FSET_NT_ACL failed%s\n", fsp_str_dbg(fsp)));
6355 if (fsp->fh->fd != -1) {
6356 result = SMB_VFS_FCHMOD(fsp, ms_nfs_mode);
6358 result = SMB_VFS_CHMOD(fsp->conn,
6364 DEBUG(1, ("chmod: %s, result: %d, %04o error %s\n", fsp_str_dbg(fsp),
6365 result, (unsigned)ms_nfs_mode,
6367 status = map_nt_error_from_unix(errno);
6374 return NT_STATUS_OK;
6377 static struct vfs_offload_ctx *fruit_offload_ctx;
6379 struct fruit_offload_read_state {
6380 struct vfs_handle_struct *handle;
6381 struct tevent_context *ev;
6387 static void fruit_offload_read_done(struct tevent_req *subreq);
6389 static struct tevent_req *fruit_offload_read_send(
6390 TALLOC_CTX *mem_ctx,
6391 struct tevent_context *ev,
6392 struct vfs_handle_struct *handle,
6399 struct tevent_req *req = NULL;
6400 struct tevent_req *subreq = NULL;
6401 struct fruit_offload_read_state *state = NULL;
6403 req = tevent_req_create(mem_ctx, &state,
6404 struct fruit_offload_read_state);
6408 *state = (struct fruit_offload_read_state) {
6415 subreq = SMB_VFS_NEXT_OFFLOAD_READ_SEND(mem_ctx, ev, handle, fsp,
6416 fsctl, ttl, offset, to_copy);
6417 if (tevent_req_nomem(subreq, req)) {
6418 return tevent_req_post(req, ev);
6420 tevent_req_set_callback(subreq, fruit_offload_read_done, req);
6424 static void fruit_offload_read_done(struct tevent_req *subreq)
6426 struct tevent_req *req = tevent_req_callback_data(
6427 subreq, struct tevent_req);
6428 struct fruit_offload_read_state *state = tevent_req_data(
6429 req, struct fruit_offload_read_state);
6432 status = SMB_VFS_NEXT_OFFLOAD_READ_RECV(subreq,
6436 TALLOC_FREE(subreq);
6437 if (tevent_req_nterror(req, status)) {
6441 if (state->fsctl != FSCTL_SRV_REQUEST_RESUME_KEY) {
6442 tevent_req_done(req);
6446 status = vfs_offload_token_ctx_init(state->fsp->conn->sconn->client,
6447 &fruit_offload_ctx);
6448 if (tevent_req_nterror(req, status)) {
6452 status = vfs_offload_token_db_store_fsp(fruit_offload_ctx,
6455 if (tevent_req_nterror(req, status)) {
6459 tevent_req_done(req);
6463 static NTSTATUS fruit_offload_read_recv(struct tevent_req *req,
6464 struct vfs_handle_struct *handle,
6465 TALLOC_CTX *mem_ctx,
6468 struct fruit_offload_read_state *state = tevent_req_data(
6469 req, struct fruit_offload_read_state);
6472 if (tevent_req_is_nterror(req, &status)) {
6473 tevent_req_received(req);
6477 token->length = state->token.length;
6478 token->data = talloc_move(mem_ctx, &state->token.data);
6480 tevent_req_received(req);
6481 return NT_STATUS_OK;
6484 struct fruit_offload_write_state {
6485 struct vfs_handle_struct *handle;
6487 struct files_struct *src_fsp;
6488 struct files_struct *dst_fsp;
6492 static void fruit_offload_write_done(struct tevent_req *subreq);
6493 static struct tevent_req *fruit_offload_write_send(struct vfs_handle_struct *handle,
6494 TALLOC_CTX *mem_ctx,
6495 struct tevent_context *ev,
6498 off_t transfer_offset,
6499 struct files_struct *dest_fsp,
6503 struct tevent_req *req, *subreq;
6504 struct fruit_offload_write_state *state;
6506 struct fruit_config_data *config;
6507 off_t src_off = transfer_offset;
6508 files_struct *src_fsp = NULL;
6509 off_t to_copy = num;
6510 bool copyfile_enabled = false;
6512 DEBUG(10,("soff: %ju, doff: %ju, len: %ju\n",
6513 (uintmax_t)src_off, (uintmax_t)dest_off, (uintmax_t)num));
6515 SMB_VFS_HANDLE_GET_DATA(handle, config,
6516 struct fruit_config_data,
6519 req = tevent_req_create(mem_ctx, &state,
6520 struct fruit_offload_write_state);
6524 state->handle = handle;
6525 state->dst_fsp = dest_fsp;
6528 case FSCTL_SRV_COPYCHUNK:
6529 case FSCTL_SRV_COPYCHUNK_WRITE:
6530 copyfile_enabled = config->copyfile_enabled;
6537 * Check if this a OS X copyfile style copychunk request with
6538 * a requested chunk count of 0 that was translated to a
6539 * offload_write_send VFS call overloading the parameters src_off
6540 * = dest_off = num = 0.
6542 if (copyfile_enabled && num == 0 && src_off == 0 && dest_off == 0) {
6543 status = vfs_offload_token_db_fetch_fsp(
6544 fruit_offload_ctx, token, &src_fsp);
6545 if (tevent_req_nterror(req, status)) {
6546 return tevent_req_post(req, ev);
6548 state->src_fsp = src_fsp;
6550 status = vfs_stat_fsp(src_fsp);
6551 if (tevent_req_nterror(req, status)) {
6552 return tevent_req_post(req, ev);
6555 to_copy = src_fsp->fsp_name->st.st_ex_size;
6556 state->is_copyfile = true;
6559 subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle,
6568 if (tevent_req_nomem(subreq, req)) {
6569 return tevent_req_post(req, ev);
6572 tevent_req_set_callback(subreq, fruit_offload_write_done, req);
6576 static void fruit_offload_write_done(struct tevent_req *subreq)
6578 struct tevent_req *req = tevent_req_callback_data(
6579 subreq, struct tevent_req);
6580 struct fruit_offload_write_state *state = tevent_req_data(
6581 req, struct fruit_offload_write_state);
6583 unsigned int num_streams = 0;
6584 struct stream_struct *streams = NULL;
6586 struct smb_filename *src_fname_tmp = NULL;
6587 struct smb_filename *dst_fname_tmp = NULL;
6589 status = SMB_VFS_NEXT_OFFLOAD_WRITE_RECV(state->handle,
6592 TALLOC_FREE(subreq);
6593 if (tevent_req_nterror(req, status)) {
6597 if (!state->is_copyfile) {
6598 tevent_req_done(req);
6603 * Now copy all remaining streams. We know the share supports
6604 * streams, because we're in vfs_fruit. We don't do this async
6605 * because streams are few and small.
6607 status = vfs_streaminfo(state->handle->conn, state->src_fsp,
6608 state->src_fsp->fsp_name,
6609 req, &num_streams, &streams);
6610 if (tevent_req_nterror(req, status)) {
6614 if (num_streams == 1) {
6615 /* There is always one stream, ::$DATA. */
6616 tevent_req_done(req);
6620 for (i = 0; i < num_streams; i++) {
6621 DEBUG(10, ("%s: stream: '%s'/%zu\n",
6622 __func__, streams[i].name, (size_t)streams[i].size));
6624 src_fname_tmp = synthetic_smb_fname(
6626 state->src_fsp->fsp_name->base_name,
6629 state->src_fsp->fsp_name->flags);
6630 if (tevent_req_nomem(src_fname_tmp, req)) {
6634 if (is_ntfs_default_stream_smb_fname(src_fname_tmp)) {
6635 TALLOC_FREE(src_fname_tmp);
6639 dst_fname_tmp = synthetic_smb_fname(
6641 state->dst_fsp->fsp_name->base_name,
6644 state->dst_fsp->fsp_name->flags);
6645 if (tevent_req_nomem(dst_fname_tmp, req)) {
6646 TALLOC_FREE(src_fname_tmp);
6650 status = copy_file(req,
6651 state->handle->conn,
6654 OPENX_FILE_CREATE_IF_NOT_EXIST,
6656 if (!NT_STATUS_IS_OK(status)) {
6657 DEBUG(1, ("%s: copy %s to %s failed: %s\n", __func__,
6658 smb_fname_str_dbg(src_fname_tmp),
6659 smb_fname_str_dbg(dst_fname_tmp),
6660 nt_errstr(status)));
6661 TALLOC_FREE(src_fname_tmp);
6662 TALLOC_FREE(dst_fname_tmp);
6663 tevent_req_nterror(req, status);
6667 TALLOC_FREE(src_fname_tmp);
6668 TALLOC_FREE(dst_fname_tmp);
6671 TALLOC_FREE(streams);
6672 TALLOC_FREE(src_fname_tmp);
6673 TALLOC_FREE(dst_fname_tmp);
6674 tevent_req_done(req);
6677 static NTSTATUS fruit_offload_write_recv(struct vfs_handle_struct *handle,
6678 struct tevent_req *req,
6681 struct fruit_offload_write_state *state = tevent_req_data(
6682 req, struct fruit_offload_write_state);
6685 if (tevent_req_is_nterror(req, &status)) {
6686 DEBUG(1, ("server side copy chunk failed: %s\n",
6687 nt_errstr(status)));
6689 tevent_req_received(req);
6693 *copied = state->copied;
6694 tevent_req_received(req);
6696 return NT_STATUS_OK;
6699 static char *fruit_get_bandsize_line(char **lines, int numlines)
6702 static bool re_initialized = false;
6706 if (!re_initialized) {
6707 ret = regcomp(&re, "^[[:blank:]]*<key>band-size</key>$", 0);
6711 re_initialized = true;
6714 for (i = 0; i < numlines; i++) {
6715 regmatch_t matches[1];
6717 ret = regexec(&re, lines[i], 1, matches, 0);
6720 * Check if the match was on the last line, sa we want
6721 * the subsequent line.
6723 if (i + 1 == numlines) {
6726 return lines[i + 1];
6728 if (ret != REG_NOMATCH) {
6736 static bool fruit_get_bandsize_from_line(char *line, size_t *_band_size)
6739 static bool re_initialized = false;
6740 regmatch_t matches[2];
6745 if (!re_initialized) {
6748 "<integer>\\([[:digit:]]*\\)</integer>$",
6753 re_initialized = true;
6756 ret = regexec(&re, line, 2, matches, 0);
6758 DBG_ERR("regex failed [%s]\n", line);
6762 line[matches[1].rm_eo] = '\0';
6764 ok = conv_str_u64(&line[matches[1].rm_so], &band_size);
6768 *_band_size = (size_t)band_size;
6773 * This reads and parses an Info.plist from a TM sparsebundle looking for the
6774 * "band-size" key and value.
6776 static bool fruit_get_bandsize(vfs_handle_struct *handle,
6780 #define INFO_PLIST_MAX_SIZE 64*1024
6782 struct smb_filename *smb_fname = NULL;
6783 files_struct *fsp = NULL;
6784 uint8_t *file_data = NULL;
6785 char **lines = NULL;
6786 char *band_size_line = NULL;
6787 size_t plist_file_size;
6794 plist = talloc_asprintf(talloc_tos(),
6796 handle->conn->connectpath,
6798 if (plist == NULL) {
6803 smb_fname = synthetic_smb_fname(talloc_tos(), plist, NULL, NULL, 0);
6804 if (smb_fname == NULL) {
6809 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
6811 DBG_INFO("Ignoring Sparsebundle without Info.plist [%s]\n", dir);
6816 plist_file_size = smb_fname->st.st_ex_size;
6818 if (plist_file_size > INFO_PLIST_MAX_SIZE) {
6819 DBG_INFO("%s is too large, ignoring\n", plist);
6824 status = SMB_VFS_NEXT_CREATE_FILE(
6827 0, /* root_dir_fid */
6828 smb_fname, /* fname */
6829 FILE_GENERIC_READ, /* access_mask */
6830 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
6831 FILE_OPEN, /* create_disposition */
6832 0, /* create_options */
6833 0, /* file_attributes */
6834 INTERNAL_OPEN_ONLY, /* oplock_request */
6836 0, /* allocation_size */
6837 0, /* private_flags */
6842 NULL, NULL); /* create context */
6843 if (!NT_STATUS_IS_OK(status)) {
6844 DBG_INFO("Opening [%s] failed [%s]\n",
6845 smb_fname_str_dbg(smb_fname), nt_errstr(status));
6850 file_data = talloc_array(talloc_tos(), uint8_t, plist_file_size);
6851 if (file_data == NULL) {
6856 nread = SMB_VFS_NEXT_PREAD(handle, fsp, file_data, plist_file_size, 0);
6857 if (nread != plist_file_size) {
6858 DBG_ERR("Short read on [%s]: %zu/%zd\n",
6859 fsp_str_dbg(fsp), nread, plist_file_size);
6865 status = close_file(NULL, fsp, NORMAL_CLOSE);
6867 if (!NT_STATUS_IS_OK(status)) {
6868 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
6873 lines = file_lines_parse((char *)file_data,
6877 if (lines == NULL) {
6882 band_size_line = fruit_get_bandsize_line(lines, numlines);
6883 if (band_size_line == NULL) {
6884 DBG_ERR("Didn't find band-size key in [%s]\n",
6885 smb_fname_str_dbg(smb_fname));
6890 ok = fruit_get_bandsize_from_line(band_size_line, band_size);
6892 DBG_ERR("fruit_get_bandsize_from_line failed\n");
6896 DBG_DEBUG("Parsed band-size [%zu] for [%s]\n", *band_size, plist);
6900 status = close_file(NULL, fsp, NORMAL_CLOSE);
6901 if (!NT_STATUS_IS_OK(status)) {
6902 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
6907 TALLOC_FREE(smb_fname);
6908 TALLOC_FREE(file_data);
6913 struct fruit_disk_free_state {
6917 static bool fruit_get_num_bands(vfs_handle_struct *handle,
6922 struct smb_filename *bands_dir = NULL;
6924 struct dirent *e = NULL;
6928 path = talloc_asprintf(talloc_tos(),
6930 handle->conn->connectpath,
6936 bands_dir = synthetic_smb_fname(talloc_tos(),
6942 if (bands_dir == NULL) {
6946 d = SMB_VFS_NEXT_OPENDIR(handle, bands_dir, NULL, 0);
6948 TALLOC_FREE(bands_dir);
6954 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
6956 e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
6958 if (ISDOT(e->d_name) || ISDOTDOT(e->d_name)) {
6964 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
6966 TALLOC_FREE(bands_dir);
6970 DBG_DEBUG("%zu bands in [%s]\n", nbands, smb_fname_str_dbg(bands_dir));
6972 TALLOC_FREE(bands_dir);
6978 static bool fruit_tmsize_do_dirent(vfs_handle_struct *handle,
6979 struct fruit_disk_free_state *state,
6984 size_t sparsebundle_strlen = strlen("sparsebundle");
6985 size_t bandsize = 0;
6989 p = strstr(e->d_name, "sparsebundle");
6994 if (p[sparsebundle_strlen] != '\0') {
6998 DBG_DEBUG("Processing sparsebundle [%s]\n", e->d_name);
7000 ok = fruit_get_bandsize(handle, e->d_name, &bandsize);
7003 * Beware of race conditions: this may be an uninitialized
7004 * Info.plist that a client is just creating. We don't want let
7005 * this to trigger complete failure.
7007 DBG_ERR("Processing sparsebundle [%s] failed\n", e->d_name);
7011 ok = fruit_get_num_bands(handle, e->d_name, &nbands);
7014 * Beware of race conditions: this may be a backup sparsebundle
7015 * in an early stage lacking a bands subdirectory. We don't want
7016 * let this to trigger complete failure.
7018 DBG_ERR("Processing sparsebundle [%s] failed\n", e->d_name);
7022 if (bandsize > SIZE_MAX/nbands) {
7023 DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
7027 tm_size = bandsize * nbands;
7029 if (state->total_size + tm_size < state->total_size) {
7030 DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
7035 state->total_size += tm_size;
7037 DBG_DEBUG("[%s] tm_size [%jd] total_size [%jd]\n",
7038 e->d_name, (intmax_t)tm_size, (intmax_t)state->total_size);
7044 * Calculate used size of a TimeMachine volume
7046 * This assumes that the volume is used only for TimeMachine.
7048 * - readdir(basedir of share), then
7049 * - for every element that matches regex "^\(.*\)\.sparsebundle$" :
7050 * - parse "\1.sparsebundle/Info.plist" and read the band-size XML key
7051 * - count band files in "\1.sparsebundle/bands/"
7052 * - calculate used size of all bands: band_count * band_size
7054 static uint64_t fruit_disk_free(vfs_handle_struct *handle,
7055 const struct smb_filename *smb_fname,
7060 struct fruit_config_data *config = NULL;
7061 struct fruit_disk_free_state state = {0};
7063 struct dirent *e = NULL;
7069 SMB_VFS_HANDLE_GET_DATA(handle, config,
7070 struct fruit_config_data,
7073 if (!config->time_machine ||
7074 config->time_machine_max_size == 0)
7076 return SMB_VFS_NEXT_DISK_FREE(handle,
7083 d = SMB_VFS_NEXT_OPENDIR(handle, smb_fname, NULL, 0);
7088 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
7090 e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
7092 ok = fruit_tmsize_do_dirent(handle, &state, e);
7094 SMB_VFS_NEXT_CLOSEDIR(handle, d);
7099 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
7104 dsize = config->time_machine_max_size / 512;
7105 dfree = dsize - (state.total_size / 512);
7106 if (dfree > dsize) {
7116 static struct vfs_fn_pointers vfs_fruit_fns = {
7117 .connect_fn = fruit_connect,
7118 .disk_free_fn = fruit_disk_free,
7120 /* File operations */
7121 .chmod_fn = fruit_chmod,
7122 .chown_fn = fruit_chown,
7123 .unlink_fn = fruit_unlink,
7124 .rename_fn = fruit_rename,
7125 .rmdir_fn = fruit_rmdir,
7126 .open_fn = fruit_open,
7127 .close_fn = fruit_close,
7128 .pread_fn = fruit_pread,
7129 .pwrite_fn = fruit_pwrite,
7130 .pread_send_fn = fruit_pread_send,
7131 .pread_recv_fn = fruit_pread_recv,
7132 .pwrite_send_fn = fruit_pwrite_send,
7133 .pwrite_recv_fn = fruit_pwrite_recv,
7134 .stat_fn = fruit_stat,
7135 .lstat_fn = fruit_lstat,
7136 .fstat_fn = fruit_fstat,
7137 .streaminfo_fn = fruit_streaminfo,
7138 .ntimes_fn = fruit_ntimes,
7139 .ftruncate_fn = fruit_ftruncate,
7140 .fallocate_fn = fruit_fallocate,
7141 .create_file_fn = fruit_create_file,
7142 .readdir_attr_fn = fruit_readdir_attr,
7143 .offload_read_send_fn = fruit_offload_read_send,
7144 .offload_read_recv_fn = fruit_offload_read_recv,
7145 .offload_write_send_fn = fruit_offload_write_send,
7146 .offload_write_recv_fn = fruit_offload_write_recv,
7148 /* NT ACL operations */
7149 .fget_nt_acl_fn = fruit_fget_nt_acl,
7150 .fset_nt_acl_fn = fruit_fset_nt_acl,
7154 NTSTATUS vfs_fruit_init(TALLOC_CTX *ctx)
7156 NTSTATUS ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "fruit",
7158 if (!NT_STATUS_IS_OK(ret)) {
7162 vfs_fruit_debug_level = debug_add_class("fruit");
7163 if (vfs_fruit_debug_level == -1) {
7164 vfs_fruit_debug_level = DBGC_VFS;
7165 DEBUG(0, ("%s: Couldn't register custom debugging class!\n",
7168 DEBUG(10, ("%s: Debug class number of '%s': %d\n",
7169 "vfs_fruit_init","fruit",vfs_fruit_debug_level));