s3:vfs: Initialize pid to 0 in test_netatalk_lock()
[Samba.git] / source3 / modules / vfs_fruit.c
blobf54038f53d482ef9e667f3d35e2655684bff2012
1 /*
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/>.
20 #include "includes.h"
21 #include "MacExtensions.h"
22 #include "smbd/smbd.h"
23 #include "system/filesys.h"
24 #include "lib/util/time.h"
25 #include "../lib/crypto/md5.h"
26 #include "system/shmem.h"
27 #include "locking/proto.h"
28 #include "smbd/globals.h"
29 #include "messages.h"
30 #include "libcli/security/security.h"
31 #include "../libcli/smb/smb2_create_ctx.h"
32 #include "lib/util/sys_rw.h"
33 #include "lib/util/tevent_ntstatus.h"
34 #include "lib/util/tevent_unix.h"
35 #include "offload_token.h"
36 #include "string_replace.h"
39 * Enhanced OS X and Netatalk compatibility
40 * ========================================
42 * This modules takes advantage of vfs_streams_xattr and
43 * vfs_catia. VFS modules vfs_fruit and vfs_streams_xattr must be
44 * loaded in the correct order:
46 * vfs modules = catia fruit streams_xattr
48 * The module intercepts the OS X special streams "AFP_AfpInfo" and
49 * "AFP_Resource" and handles them in a special way. All other named
50 * streams are deferred to vfs_streams_xattr.
52 * The OS X client maps all NTFS illegal characters to the Unicode
53 * private range. This module optionally stores the charcters using
54 * their native ASCII encoding using vfs_catia. If you're not enabling
55 * this feature, you can skip catia from vfs modules.
57 * Finally, open modes are optionally checked against Netatalk AFP
58 * share modes.
60 * The "AFP_AfpInfo" named stream is a binary blob containing OS X
61 * extended metadata for files and directories. This module optionally
62 * reads and stores this metadata in a way compatible with Netatalk 3
63 * which stores the metadata in an EA "org.netatalk.metadata". Cf
64 * source3/include/MacExtensions.h for a description of the binary
65 * blobs content.
67 * The "AFP_Resource" named stream may be arbitrarily large, thus it
68 * can't be stored in an xattr on most filesystem. ZFS on Solaris is
69 * the only available filesystem where xattrs can be of any size and
70 * the OS supports using the file APIs for xattrs.
72 * The AFP_Resource stream is stored in an AppleDouble file prepending
73 * "._" to the filename. On Solaris with ZFS the stream is optionally
74 * stored in an EA "org.netatalk.resource".
77 * Extended Attributes
78 * ===================
80 * The OS X SMB client sends xattrs as ADS too. For xattr interop with
81 * other protocols you may want to adjust the xattr names the VFS
82 * module vfs_streams_xattr uses for storing ADS's. This defaults to
83 * user.DosStream.ADS_NAME:$DATA and can be changed by specifying
84 * these module parameters:
86 * streams_xattr:prefix = user.
87 * streams_xattr:store_stream_type = false
90 * TODO
91 * ====
93 * - log diagnostic if any needed VFS module is not loaded
94 * (eg with lp_vfs_objects())
95 * - add tests
98 static int vfs_fruit_debug_level = DBGC_VFS;
100 static struct global_fruit_config {
101 bool nego_aapl; /* client negotiated AAPL */
103 } global_fruit_config;
105 #undef DBGC_CLASS
106 #define DBGC_CLASS vfs_fruit_debug_level
108 #define FRUIT_PARAM_TYPE_NAME "fruit"
109 #define ADOUBLE_NAME_PREFIX "._"
111 #define NETATALK_META_XATTR "org.netatalk.Metadata"
112 #define NETATALK_RSRC_XATTR "org.netatalk.ResourceFork"
114 #if defined(HAVE_ATTROPEN)
115 #define AFPINFO_EA_NETATALK NETATALK_META_XATTR
116 #define AFPRESOURCE_EA_NETATALK NETATALK_RSRC_XATTR
117 #else
118 #define AFPINFO_EA_NETATALK "user." NETATALK_META_XATTR
119 #define AFPRESOURCE_EA_NETATALK "user." NETATALK_RSRC_XATTR
120 #endif
122 enum apple_fork {APPLE_FORK_DATA, APPLE_FORK_RSRC};
124 enum fruit_rsrc {FRUIT_RSRC_STREAM, FRUIT_RSRC_ADFILE, FRUIT_RSRC_XATTR};
125 enum fruit_meta {FRUIT_META_STREAM, FRUIT_META_NETATALK};
126 enum fruit_locking {FRUIT_LOCKING_NETATALK, FRUIT_LOCKING_NONE};
127 enum fruit_encoding {FRUIT_ENC_NATIVE, FRUIT_ENC_PRIVATE};
129 struct fruit_config_data {
130 enum fruit_rsrc rsrc;
131 enum fruit_meta meta;
132 enum fruit_locking locking;
133 enum fruit_encoding encoding;
134 bool use_aapl; /* config from smb.conf */
135 bool use_copyfile;
136 bool readdir_attr_enabled;
137 bool unix_info_enabled;
138 bool copyfile_enabled;
139 bool veto_appledouble;
140 bool posix_rename;
141 bool aapl_zero_file_id;
142 const char *model;
143 bool time_machine;
144 off_t time_machine_max_size;
145 bool wipe_intentionally_left_blank_rfork;
146 bool delete_empty_adfiles;
149 * Additional options, all enabled by default,
150 * possibly useful for analyzing performance. The associated
151 * operations with each of them may be expensive, so having
152 * the chance to disable them individually gives a chance
153 * tweaking the setup for the particular usecase.
155 bool readdir_attr_rsize;
156 bool readdir_attr_finder_info;
157 bool readdir_attr_max_access;
160 static const struct enum_list fruit_rsrc[] = {
161 {FRUIT_RSRC_STREAM, "stream"}, /* pass on to vfs_streams_xattr */
162 {FRUIT_RSRC_ADFILE, "file"}, /* ._ AppleDouble file */
163 {FRUIT_RSRC_XATTR, "xattr"}, /* Netatalk compatible xattr (ZFS only) */
164 { -1, NULL}
167 static const struct enum_list fruit_meta[] = {
168 {FRUIT_META_STREAM, "stream"}, /* pass on to vfs_streams_xattr */
169 {FRUIT_META_NETATALK, "netatalk"}, /* Netatalk compatible xattr */
170 { -1, NULL}
173 static const struct enum_list fruit_locking[] = {
174 {FRUIT_LOCKING_NETATALK, "netatalk"}, /* synchronize locks with Netatalk */
175 {FRUIT_LOCKING_NONE, "none"},
176 { -1, NULL}
179 static const struct enum_list fruit_encoding[] = {
180 {FRUIT_ENC_NATIVE, "native"}, /* map unicode private chars to ASCII */
181 {FRUIT_ENC_PRIVATE, "private"}, /* keep unicode private chars */
182 { -1, NULL}
185 static const char *fruit_catia_maps =
186 "0x01:0xf001,0x02:0xf002,0x03:0xf003,0x04:0xf004,"
187 "0x05:0xf005,0x06:0xf006,0x07:0xf007,0x08:0xf008,"
188 "0x09:0xf009,0x0a:0xf00a,0x0b:0xf00b,0x0c:0xf00c,"
189 "0x0d:0xf00d,0x0e:0xf00e,0x0f:0xf00f,0x10:0xf010,"
190 "0x11:0xf011,0x12:0xf012,0x13:0xf013,0x14:0xf014,"
191 "0x15:0xf015,0x16:0xf016,0x17:0xf017,0x18:0xf018,"
192 "0x19:0xf019,0x1a:0xf01a,0x1b:0xf01b,0x1c:0xf01c,"
193 "0x1d:0xf01d,0x1e:0xf01e,0x1f:0xf01f,"
194 "0x22:0xf020,0x2a:0xf021,0x3a:0xf022,0x3c:0xf023,"
195 "0x3e:0xf024,0x3f:0xf025,0x5c:0xf026,0x7c:0xf027,"
196 "0x0d:0xf00d";
198 /*****************************************************************************
199 * Defines, functions and data structures that deal with AppleDouble
200 *****************************************************************************/
203 * There are two AppleDouble blobs we deal with:
205 * - ADOUBLE_META - AppleDouble blob used by Netatalk for storing
206 * metadata in an xattr
208 * - ADOUBLE_RSRC - AppleDouble blob used by OS X and Netatalk in
209 * ._ files
211 typedef enum {ADOUBLE_META, ADOUBLE_RSRC} adouble_type_t;
213 /* Version info */
214 #define AD_VERSION2 0x00020000
215 #define AD_VERSION AD_VERSION2
218 * AppleDouble entry IDs.
220 #define ADEID_DFORK 1
221 #define ADEID_RFORK 2
222 #define ADEID_NAME 3
223 #define ADEID_COMMENT 4
224 #define ADEID_ICONBW 5
225 #define ADEID_ICONCOL 6
226 #define ADEID_FILEI 7
227 #define ADEID_FILEDATESI 8
228 #define ADEID_FINDERI 9
229 #define ADEID_MACFILEI 10
230 #define ADEID_PRODOSFILEI 11
231 #define ADEID_MSDOSFILEI 12
232 #define ADEID_SHORTNAME 13
233 #define ADEID_AFPFILEI 14
234 #define ADEID_DID 15
236 /* Private Netatalk entries */
237 #define ADEID_PRIVDEV 16
238 #define ADEID_PRIVINO 17
239 #define ADEID_PRIVSYN 18
240 #define ADEID_PRIVID 19
241 #define ADEID_MAX (ADEID_PRIVID + 1)
244 * These are the real ids for the private entries,
245 * as stored in the adouble file
247 #define AD_DEV 0x80444556
248 #define AD_INO 0x80494E4F
249 #define AD_SYN 0x8053594E
250 #define AD_ID 0x8053567E
252 /* Number of actually used entries */
253 #define ADEID_NUM_XATTR 8
254 #define ADEID_NUM_DOT_UND 2
255 #define ADEID_NUM_RSRC_XATTR 1
257 /* AppleDouble magic */
258 #define AD_APPLESINGLE_MAGIC 0x00051600
259 #define AD_APPLEDOUBLE_MAGIC 0x00051607
260 #define AD_MAGIC AD_APPLEDOUBLE_MAGIC
262 /* Sizes of relevant entry bits */
263 #define ADEDLEN_MAGIC 4
264 #define ADEDLEN_VERSION 4
265 #define ADEDLEN_FILLER 16
266 #define AD_FILLER_TAG "Netatalk " /* should be 16 bytes */
267 #define AD_FILLER_TAG_OSX "Mac OS X " /* should be 16 bytes */
268 #define ADEDLEN_NENTRIES 2
269 #define AD_HEADER_LEN (ADEDLEN_MAGIC + ADEDLEN_VERSION + \
270 ADEDLEN_FILLER + ADEDLEN_NENTRIES) /* 26 */
271 #define AD_ENTRY_LEN_EID 4
272 #define AD_ENTRY_LEN_OFF 4
273 #define AD_ENTRY_LEN_LEN 4
274 #define AD_ENTRY_LEN (AD_ENTRY_LEN_EID + AD_ENTRY_LEN_OFF + AD_ENTRY_LEN_LEN)
276 /* Field widths */
277 #define ADEDLEN_NAME 255
278 #define ADEDLEN_COMMENT 200
279 #define ADEDLEN_FILEI 16
280 #define ADEDLEN_FINDERI 32
281 #define ADEDLEN_FILEDATESI 16
282 #define ADEDLEN_SHORTNAME 12 /* length up to 8.3 */
283 #define ADEDLEN_AFPFILEI 4
284 #define ADEDLEN_MACFILEI 4
285 #define ADEDLEN_PRODOSFILEI 8
286 #define ADEDLEN_MSDOSFILEI 2
287 #define ADEDLEN_DID 4
288 #define ADEDLEN_PRIVDEV 8
289 #define ADEDLEN_PRIVINO 8
290 #define ADEDLEN_PRIVSYN 8
291 #define ADEDLEN_PRIVID 4
293 /* Offsets */
294 #define ADEDOFF_MAGIC 0
295 #define ADEDOFF_VERSION (ADEDOFF_MAGIC + ADEDLEN_MAGIC)
296 #define ADEDOFF_FILLER (ADEDOFF_VERSION + ADEDLEN_VERSION)
297 #define ADEDOFF_NENTRIES (ADEDOFF_FILLER + ADEDLEN_FILLER)
299 #define ADEDOFF_FINDERI_XATTR (AD_HEADER_LEN + \
300 (ADEID_NUM_XATTR * AD_ENTRY_LEN))
301 #define ADEDOFF_COMMENT_XATTR (ADEDOFF_FINDERI_XATTR + ADEDLEN_FINDERI)
302 #define ADEDOFF_FILEDATESI_XATTR (ADEDOFF_COMMENT_XATTR + ADEDLEN_COMMENT)
303 #define ADEDOFF_AFPFILEI_XATTR (ADEDOFF_FILEDATESI_XATTR + \
304 ADEDLEN_FILEDATESI)
305 #define ADEDOFF_PRIVDEV_XATTR (ADEDOFF_AFPFILEI_XATTR + ADEDLEN_AFPFILEI)
306 #define ADEDOFF_PRIVINO_XATTR (ADEDOFF_PRIVDEV_XATTR + ADEDLEN_PRIVDEV)
307 #define ADEDOFF_PRIVSYN_XATTR (ADEDOFF_PRIVINO_XATTR + ADEDLEN_PRIVINO)
308 #define ADEDOFF_PRIVID_XATTR (ADEDOFF_PRIVSYN_XATTR + ADEDLEN_PRIVSYN)
310 #define ADEDOFF_FINDERI_DOT_UND (AD_HEADER_LEN + \
311 (ADEID_NUM_DOT_UND * AD_ENTRY_LEN))
312 #define ADEDOFF_RFORK_DOT_UND (ADEDOFF_FINDERI_DOT_UND + ADEDLEN_FINDERI)
314 #define AD_DATASZ_XATTR (AD_HEADER_LEN + \
315 (ADEID_NUM_XATTR * AD_ENTRY_LEN) + \
316 ADEDLEN_FINDERI + ADEDLEN_COMMENT + \
317 ADEDLEN_FILEDATESI + ADEDLEN_AFPFILEI + \
318 ADEDLEN_PRIVDEV + ADEDLEN_PRIVINO + \
319 ADEDLEN_PRIVSYN + ADEDLEN_PRIVID)
321 #if AD_DATASZ_XATTR != 402
322 #error bad size for AD_DATASZ_XATTR
323 #endif
325 #define AD_DATASZ_DOT_UND (AD_HEADER_LEN + \
326 (ADEID_NUM_DOT_UND * AD_ENTRY_LEN) + \
327 ADEDLEN_FINDERI)
328 #if AD_DATASZ_DOT_UND != 82
329 #error bad size for AD_DATASZ_DOT_UND
330 #endif
333 * Sharemode locks fcntl() offsets
335 #if _FILE_OFFSET_BITS == 64 || defined(HAVE_LARGEFILE)
336 #define AD_FILELOCK_BASE (UINT64_C(0x7FFFFFFFFFFFFFFF) - 9)
337 #else
338 #define AD_FILELOCK_BASE (UINT32_C(0x7FFFFFFF) - 9)
339 #endif
340 #define BYTELOCK_MAX (AD_FILELOCK_BASE - 1)
342 #define AD_FILELOCK_OPEN_WR (AD_FILELOCK_BASE + 0)
343 #define AD_FILELOCK_OPEN_RD (AD_FILELOCK_BASE + 1)
344 #define AD_FILELOCK_RSRC_OPEN_WR (AD_FILELOCK_BASE + 2)
345 #define AD_FILELOCK_RSRC_OPEN_RD (AD_FILELOCK_BASE + 3)
346 #define AD_FILELOCK_DENY_WR (AD_FILELOCK_BASE + 4)
347 #define AD_FILELOCK_DENY_RD (AD_FILELOCK_BASE + 5)
348 #define AD_FILELOCK_RSRC_DENY_WR (AD_FILELOCK_BASE + 6)
349 #define AD_FILELOCK_RSRC_DENY_RD (AD_FILELOCK_BASE + 7)
350 #define AD_FILELOCK_OPEN_NONE (AD_FILELOCK_BASE + 8)
351 #define AD_FILELOCK_RSRC_OPEN_NONE (AD_FILELOCK_BASE + 9)
353 /* Time stuff we overload the bits a little */
354 #define AD_DATE_CREATE 0
355 #define AD_DATE_MODIFY 4
356 #define AD_DATE_BACKUP 8
357 #define AD_DATE_ACCESS 12
358 #define AD_DATE_MASK (AD_DATE_CREATE | AD_DATE_MODIFY | \
359 AD_DATE_BACKUP | AD_DATE_ACCESS)
360 #define AD_DATE_UNIX (1 << 10)
361 #define AD_DATE_START 0x80000000
362 #define AD_DATE_DELTA 946684800
363 #define AD_DATE_FROM_UNIX(x) (htonl((x) - AD_DATE_DELTA))
364 #define AD_DATE_TO_UNIX(x) (ntohl(x) + AD_DATE_DELTA)
366 #define AD_XATTR_HDR_MAGIC 0x41545452 /* 'ATTR' */
367 #define AD_XATTR_MAX_ENTRIES 1024 /* Some arbitrarily enforced limit */
368 #define AD_XATTR_HDR_SIZE 36
369 #define AD_XATTR_MAX_HDR_SIZE 65536
371 /* Accessor macros */
372 #define ad_getentrylen(ad,eid) ((ad)->ad_eid[(eid)].ade_len)
373 #define ad_getentryoff(ad,eid) ((ad)->ad_eid[(eid)].ade_off)
374 #define ad_setentrylen(ad,eid,len) ((ad)->ad_eid[(eid)].ade_len = (len))
375 #define ad_setentryoff(ad,eid,off) ((ad)->ad_eid[(eid)].ade_off = (off))
378 * Both struct ad_xattr_header and struct ad_xattr_entry describe the in memory
379 * representation as well as the on-disk format.
381 * The ad_xattr_header follows the FinderInfo data in the FinderInfo entry if
382 * the length of the FinderInfo entry is larger then 32 bytes. It is then
383 * preceeded with 2 bytes padding.
385 * Cf: https://opensource.apple.com/source/xnu/xnu-4570.1.46/bsd/vfs/vfs_xattr.c
388 struct ad_xattr_header {
389 uint32_t adx_magic; /* ATTR_HDR_MAGIC */
390 uint32_t adx_debug_tag; /* for debugging == file id of owning file */
391 uint32_t adx_total_size; /* file offset of end of attribute header + entries + data */
392 uint32_t adx_data_start; /* file offset to attribute data area */
393 uint32_t adx_data_length; /* length of attribute data area */
394 uint32_t adx_reserved[3];
395 uint16_t adx_flags;
396 uint16_t adx_num_attrs;
399 /* On-disk entries are aligned on 4 byte boundaries */
400 struct ad_xattr_entry {
401 uint32_t adx_offset; /* file offset to data */
402 uint32_t adx_length; /* size of attribute data */
403 uint16_t adx_flags;
404 uint8_t adx_namelen; /* included the NULL terminator */
405 char *adx_name; /* NULL-terminated UTF-8 name */
408 struct ad_entry {
409 size_t ade_off;
410 size_t ade_len;
413 struct adouble {
414 vfs_handle_struct *ad_handle;
415 int ad_fd;
416 bool ad_opened;
417 adouble_type_t ad_type;
418 uint32_t ad_magic;
419 uint32_t ad_version;
420 uint8_t ad_filler[ADEDLEN_FILLER];
421 struct ad_entry ad_eid[ADEID_MAX];
422 char *ad_data;
423 struct ad_xattr_header adx_header;
424 struct ad_xattr_entry *adx_entries;
427 struct ad_entry_order {
428 uint32_t id, offset, len;
431 /* Netatalk AppleDouble metadata xattr */
432 static const
433 struct ad_entry_order entry_order_meta_xattr[ADEID_NUM_XATTR + 1] = {
434 {ADEID_FINDERI, ADEDOFF_FINDERI_XATTR, ADEDLEN_FINDERI},
435 {ADEID_COMMENT, ADEDOFF_COMMENT_XATTR, 0},
436 {ADEID_FILEDATESI, ADEDOFF_FILEDATESI_XATTR, ADEDLEN_FILEDATESI},
437 {ADEID_AFPFILEI, ADEDOFF_AFPFILEI_XATTR, ADEDLEN_AFPFILEI},
438 {ADEID_PRIVDEV, ADEDOFF_PRIVDEV_XATTR, 0},
439 {ADEID_PRIVINO, ADEDOFF_PRIVINO_XATTR, 0},
440 {ADEID_PRIVSYN, ADEDOFF_PRIVSYN_XATTR, 0},
441 {ADEID_PRIVID, ADEDOFF_PRIVID_XATTR, 0},
442 {0, 0, 0}
445 /* AppleDouble resource fork file (the ones prefixed by "._") */
446 static const
447 struct ad_entry_order entry_order_dot_und[ADEID_NUM_DOT_UND + 1] = {
448 {ADEID_FINDERI, ADEDOFF_FINDERI_DOT_UND, ADEDLEN_FINDERI},
449 {ADEID_RFORK, ADEDOFF_RFORK_DOT_UND, 0},
450 {0, 0, 0}
454 * Fake AppleDouble entry oder for resource fork xattr. The xattr
455 * isn't an AppleDouble file, it simply contains the resource data,
456 * but in order to be able to use some API calls like ad_getentryoff()
457 * we build a fake/helper struct adouble with this entry order struct.
459 static const
460 struct ad_entry_order entry_order_rsrc_xattr[ADEID_NUM_RSRC_XATTR + 1] = {
461 {ADEID_RFORK, 0, 0},
462 {0, 0, 0}
465 /* Conversion from enumerated id to on-disk AppleDouble id */
466 #define AD_EID_DISK(a) (set_eid[a])
467 static const uint32_t set_eid[] = {
468 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
469 AD_DEV, AD_INO, AD_SYN, AD_ID
472 static char empty_resourcefork[] = {
473 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
474 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
475 0x54, 0x68, 0x69, 0x73, 0x20, 0x72, 0x65, 0x73,
476 0x6F, 0x75, 0x72, 0x63, 0x65, 0x20, 0x66, 0x6F,
477 0x72, 0x6B, 0x20, 0x69, 0x6E, 0x74, 0x65, 0x6E,
478 0x74, 0x69, 0x6F, 0x6E, 0x61, 0x6C, 0x6C, 0x79,
479 0x20, 0x6C, 0x65, 0x66, 0x74, 0x20, 0x62, 0x6C,
480 0x61, 0x6E, 0x6B, 0x20, 0x20, 0x20, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
495 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
496 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
497 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
498 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
499 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
500 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
501 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
502 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
503 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
504 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
505 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
506 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
507 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
508 0x00, 0x1C, 0x00, 0x1E, 0xFF, 0xFF
511 struct fio {
512 /* tcon config handle */
513 struct fruit_config_data *config;
515 /* Denote stream type, meta or rsrc */
516 adouble_type_t type;
518 /* Whether the create created the stream */
519 bool created;
522 * AFP_AfpInfo stream created, but not written yet, thus still a fake
523 * pipe fd. This is set to true in fruit_open_meta if there was no
524 * exisiting stream but the caller requested O_CREAT. It is later set to
525 * false when we get a write on the stream that then does open and
526 * create the stream.
528 bool fake_fd;
529 int flags;
530 int mode;
534 * Forward declarations
536 static struct adouble *ad_init(TALLOC_CTX *ctx, vfs_handle_struct *handle,
537 adouble_type_t type);
538 static int ad_set(struct adouble *ad, const struct smb_filename *smb_fname);
539 static int ad_fset(struct adouble *ad, files_struct *fsp);
540 static int adouble_path(TALLOC_CTX *ctx,
541 const struct smb_filename *smb_fname__in,
542 struct smb_filename **ppsmb_fname_out);
543 static AfpInfo *afpinfo_new(TALLOC_CTX *ctx);
544 static ssize_t afpinfo_pack(const AfpInfo *ai, char *buf);
545 static AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data);
549 * Return a pointer to an AppleDouble entry
551 * Returns NULL if the entry is not present
553 static char *ad_get_entry(const struct adouble *ad, int eid)
555 off_t off = ad_getentryoff(ad, eid);
556 size_t len = ad_getentrylen(ad, eid);
558 if (off == 0 || len == 0) {
559 return NULL;
562 return ad->ad_data + off;
566 * Get a date
568 static int ad_getdate(const struct adouble *ad,
569 unsigned int dateoff,
570 uint32_t *date)
572 bool xlate = (dateoff & AD_DATE_UNIX);
573 char *p = NULL;
575 dateoff &= AD_DATE_MASK;
576 p = ad_get_entry(ad, ADEID_FILEDATESI);
577 if (p == NULL) {
578 return -1;
581 if (dateoff > AD_DATE_ACCESS) {
582 return -1;
585 memcpy(date, p + dateoff, sizeof(uint32_t));
587 if (xlate) {
588 *date = AD_DATE_TO_UNIX(*date);
590 return 0;
594 * Set a date
596 static int ad_setdate(struct adouble *ad, unsigned int dateoff, uint32_t date)
598 bool xlate = (dateoff & AD_DATE_UNIX);
599 char *p = NULL;
601 p = ad_get_entry(ad, ADEID_FILEDATESI);
602 if (p == NULL) {
603 return -1;
606 dateoff &= AD_DATE_MASK;
607 if (xlate) {
608 date = AD_DATE_FROM_UNIX(date);
611 if (dateoff > AD_DATE_ACCESS) {
612 return -1;
615 memcpy(p + dateoff, &date, sizeof(date));
617 return 0;
622 * Map on-disk AppleDouble id to enumerated id
624 static uint32_t get_eid(uint32_t eid)
626 if (eid <= 15) {
627 return eid;
630 switch (eid) {
631 case AD_DEV:
632 return ADEID_PRIVDEV;
633 case AD_INO:
634 return ADEID_PRIVINO;
635 case AD_SYN:
636 return ADEID_PRIVSYN;
637 case AD_ID:
638 return ADEID_PRIVID;
639 default:
640 break;
643 return 0;
647 * Pack AppleDouble structure into data buffer
649 static bool ad_pack(struct adouble *ad)
651 uint32_t eid;
652 uint16_t nent;
653 uint32_t bufsize;
654 uint32_t offset = 0;
656 bufsize = talloc_get_size(ad->ad_data);
657 if (bufsize < AD_DATASZ_DOT_UND) {
658 DBG_ERR("bad buffer size [0x%" PRIx32 "]\n", bufsize);
659 return false;
662 if (offset + ADEDLEN_MAGIC < offset ||
663 offset + ADEDLEN_MAGIC >= bufsize) {
664 return false;
666 RSIVAL(ad->ad_data, offset, ad->ad_magic);
667 offset += ADEDLEN_MAGIC;
669 if (offset + ADEDLEN_VERSION < offset ||
670 offset + ADEDLEN_VERSION >= bufsize) {
671 return false;
673 RSIVAL(ad->ad_data, offset, ad->ad_version);
674 offset += ADEDLEN_VERSION;
676 if (offset + ADEDLEN_FILLER < offset ||
677 offset + ADEDLEN_FILLER >= bufsize) {
678 return false;
680 if (ad->ad_type == ADOUBLE_RSRC) {
681 memcpy(ad->ad_data + offset, AD_FILLER_TAG, ADEDLEN_FILLER);
683 offset += ADEDLEN_FILLER;
685 if (offset + ADEDLEN_NENTRIES < offset ||
686 offset + ADEDLEN_NENTRIES >= bufsize) {
687 return false;
689 offset += ADEDLEN_NENTRIES;
691 for (eid = 0, nent = 0; eid < ADEID_MAX; eid++) {
692 if (ad->ad_eid[eid].ade_off == 0) {
694 * ade_off is also used as indicator whether a
695 * specific entry is used or not
697 continue;
700 if (offset + AD_ENTRY_LEN_EID < offset ||
701 offset + AD_ENTRY_LEN_EID >= bufsize) {
702 return false;
704 RSIVAL(ad->ad_data, offset, AD_EID_DISK(eid));
705 offset += AD_ENTRY_LEN_EID;
707 if (offset + AD_ENTRY_LEN_OFF < offset ||
708 offset + AD_ENTRY_LEN_OFF >= bufsize) {
709 return false;
711 RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_off);
712 offset += AD_ENTRY_LEN_OFF;
714 if (offset + AD_ENTRY_LEN_LEN < offset ||
715 offset + AD_ENTRY_LEN_LEN >= bufsize) {
716 return false;
718 RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_len);
719 offset += AD_ENTRY_LEN_LEN;
721 nent++;
724 if (ADEDOFF_NENTRIES + 2 >= bufsize) {
725 return false;
727 RSSVAL(ad->ad_data, ADEDOFF_NENTRIES, nent);
729 return true;
732 static bool ad_unpack_xattrs(struct adouble *ad)
734 struct ad_xattr_header *h = &ad->adx_header;
735 const char *p = ad->ad_data;
736 uint32_t hoff;
737 uint32_t i;
739 if (ad_getentrylen(ad, ADEID_FINDERI) <= ADEDLEN_FINDERI) {
740 return true;
743 /* 2 bytes padding */
744 hoff = ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI + 2;
746 h->adx_magic = RIVAL(p, hoff + 0);
747 h->adx_debug_tag = RIVAL(p, hoff + 4); /* Not used -> not checked */
748 h->adx_total_size = RIVAL(p, hoff + 8);
749 h->adx_data_start = RIVAL(p, hoff + 12);
750 h->adx_data_length = RIVAL(p, hoff + 16);
751 h->adx_flags = RSVAL(p, hoff + 32); /* Not used -> not checked */
752 h->adx_num_attrs = RSVAL(p, hoff + 34);
754 if (h->adx_magic != AD_XATTR_HDR_MAGIC) {
755 DBG_ERR("Bad magic: 0x%" PRIx32 "\n", h->adx_magic);
756 return false;
759 if (h->adx_total_size > ad_getentryoff(ad, ADEID_RFORK)) {
760 DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size);
761 return false;
763 if (h->adx_total_size > AD_XATTR_MAX_HDR_SIZE) {
764 DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size);
765 return false;
768 if (h->adx_data_start < (hoff + AD_XATTR_HDR_SIZE)) {
769 DBG_ERR("Bad start: 0x%" PRIx32 "\n", h->adx_data_start);
770 return false;
773 if ((h->adx_data_start + h->adx_data_length) < h->adx_data_start) {
774 DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length);
775 return false;
777 if ((h->adx_data_start + h->adx_data_length) >
778 ad->adx_header.adx_total_size)
780 DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length);
781 return false;
784 if (h->adx_num_attrs > AD_XATTR_MAX_ENTRIES) {
785 DBG_ERR("Bad num xattrs: %" PRIu16 "\n", h->adx_num_attrs);
786 return false;
789 if (h->adx_num_attrs == 0) {
790 return true;
793 ad->adx_entries = talloc_zero_array(
794 ad, struct ad_xattr_entry, h->adx_num_attrs);
795 if (ad->adx_entries == NULL) {
796 return false;
799 hoff += AD_XATTR_HDR_SIZE;
801 for (i = 0; i < h->adx_num_attrs; i++) {
802 struct ad_xattr_entry *e = &ad->adx_entries[i];
804 hoff = (hoff + 3) & ~3;
806 e->adx_offset = RIVAL(p, hoff + 0);
807 e->adx_length = RIVAL(p, hoff + 4);
808 e->adx_flags = RSVAL(p, hoff + 8);
809 e->adx_namelen = *(p + hoff + 10);
811 if (e->adx_offset >= ad->adx_header.adx_total_size) {
812 DBG_ERR("Bad adx_offset: %" PRIx32 "\n",
813 e->adx_offset);
814 return false;
817 if ((e->adx_offset + e->adx_length) < e->adx_offset) {
818 DBG_ERR("Bad adx_length: %" PRIx32 "\n",
819 e->adx_length);
820 return false;
823 if ((e->adx_offset + e->adx_length) >
824 ad->adx_header.adx_total_size)
826 DBG_ERR("Bad adx_length: %" PRIx32 "\n",
827 e->adx_length);
828 return false;
831 if (e->adx_namelen == 0) {
832 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
833 e->adx_namelen);
834 return false;
836 if ((hoff + 11 + e->adx_namelen) < hoff + 11) {
837 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
838 e->adx_namelen);
839 return false;
841 if ((hoff + 11 + e->adx_namelen) >
842 ad->adx_header.adx_data_start)
844 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
845 e->adx_namelen);
846 return false;
849 e->adx_name = talloc_strndup(ad->adx_entries,
850 p + hoff + 11,
851 e->adx_namelen);
852 if (e->adx_name == NULL) {
853 return false;
856 DBG_DEBUG("xattr [%s] offset [0x%x] size [0x%x]\n",
857 e->adx_name, e->adx_offset, e->adx_length);
858 dump_data(10, (uint8_t *)(ad->ad_data + e->adx_offset),
859 e->adx_length);
861 hoff += 11 + e->adx_namelen;
864 return true;
868 * Unpack an AppleDouble blob into a struct adoble
870 static bool ad_unpack(struct adouble *ad, const size_t nentries,
871 size_t filesize)
873 size_t bufsize = talloc_get_size(ad->ad_data);
874 size_t adentries, i;
875 uint32_t eid, len, off;
876 bool ok;
879 * The size of the buffer ad->ad_data is checked when read, so
880 * we wouldn't have to check our own offsets, a few extra
881 * checks won't hurt though. We have to check the offsets we
882 * read from the buffer anyway.
885 if (bufsize < (AD_HEADER_LEN + (AD_ENTRY_LEN * nentries))) {
886 DEBUG(1, ("bad size\n"));
887 return false;
890 ad->ad_magic = RIVAL(ad->ad_data, 0);
891 ad->ad_version = RIVAL(ad->ad_data, ADEDOFF_VERSION);
892 if ((ad->ad_magic != AD_MAGIC) || (ad->ad_version != AD_VERSION)) {
893 DEBUG(1, ("wrong magic or version\n"));
894 return false;
897 memcpy(ad->ad_filler, ad->ad_data + ADEDOFF_FILLER, ADEDLEN_FILLER);
899 adentries = RSVAL(ad->ad_data, ADEDOFF_NENTRIES);
900 if (adentries != nentries) {
901 DEBUG(1, ("invalid number of entries: %zu\n",
902 adentries));
903 return false;
906 /* now, read in the entry bits */
907 for (i = 0; i < adentries; i++) {
908 eid = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN));
909 eid = get_eid(eid);
910 off = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 4);
911 len = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 8);
913 if (!eid || eid >= ADEID_MAX) {
914 DEBUG(1, ("bogus eid %d\n", eid));
915 return false;
919 * All entries other than the resource fork are
920 * expected to be read into the ad_data buffer, so
921 * ensure the specified offset is within that bound
923 if ((off > bufsize) && (eid != ADEID_RFORK)) {
924 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
925 eid, off, len));
926 return false;
930 * All entries besides FinderInfo and resource fork
931 * must fit into the buffer. FinderInfo is special as
932 * it may be larger then the default 32 bytes (if it
933 * contains marshalled xattrs), but we will fixup that
934 * in ad_convert(). And the resource fork is never
935 * accessed directly by the ad_data buf (also see
936 * comment above) anyway.
938 if ((eid != ADEID_RFORK) &&
939 (eid != ADEID_FINDERI) &&
940 ((off + len) > bufsize)) {
941 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
942 eid, off, len));
943 return false;
947 * That would be obviously broken
949 if (off > filesize) {
950 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
951 eid, off, len));
952 return false;
956 * Check for any entry that has its end beyond the
957 * filesize.
959 if (off + len < off) {
960 DEBUG(1, ("offset wrap in eid %d: off: %" PRIu32
961 ", len: %" PRIu32 "\n",
962 eid, off, len));
963 return false;
966 if (off + len > filesize) {
968 * If this is the resource fork entry, we fix
969 * up the length, for any other entry we bail
970 * out.
972 if (eid != ADEID_RFORK) {
973 DEBUG(1, ("bogus eid %d: off: %" PRIu32
974 ", len: %" PRIu32 "\n",
975 eid, off, len));
976 return false;
980 * Fixup the resource fork entry by limiting
981 * the size to entryoffset - filesize.
983 len = filesize - off;
984 DEBUG(1, ("Limiting ADEID_RFORK: off: %" PRIu32
985 ", len: %" PRIu32 "\n", off, len));
988 ad->ad_eid[eid].ade_off = off;
989 ad->ad_eid[eid].ade_len = len;
992 ok = ad_unpack_xattrs(ad);
993 if (!ok) {
994 return false;
997 return true;
1000 static bool ad_convert_move_reso(struct adouble *ad,
1001 const struct smb_filename *smb_fname)
1003 char *map = MAP_FAILED;
1004 size_t maplen;
1005 ssize_t len;
1006 int rc;
1007 bool ok;
1009 if (ad_getentrylen(ad, ADEID_RFORK) == 0) {
1010 return true;
1013 maplen = ad_getentryoff(ad, ADEID_RFORK) +
1014 ad_getentrylen(ad, ADEID_RFORK);
1016 /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
1017 map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
1018 ad->ad_fd, 0);
1019 if (map == MAP_FAILED) {
1020 DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
1021 return false;
1025 memmove(map + ADEDOFF_RFORK_DOT_UND,
1026 map + ad_getentryoff(ad, ADEID_RFORK),
1027 ad_getentrylen(ad, ADEID_RFORK));
1029 rc = munmap(map, maplen);
1030 if (rc != 0) {
1031 DBG_ERR("munmap failed: %s\n", strerror(errno));
1032 return false;
1035 ad_setentryoff(ad, ADEID_RFORK, ADEDOFF_RFORK_DOT_UND);
1037 ok = ad_pack(ad);
1038 if (!ok) {
1039 DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name);
1040 return false;
1043 len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
1044 if (len != AD_DATASZ_DOT_UND) {
1045 DBG_ERR("%s: bad size: %zd\n", smb_fname->base_name, len);
1046 return false;
1049 return true;
1052 static bool ad_convert_xattr(struct adouble *ad,
1053 const struct smb_filename *smb_fname,
1054 bool *converted_xattr)
1056 static struct char_mappings **string_replace_cmaps = NULL;
1057 char *map = MAP_FAILED;
1058 size_t maplen;
1059 uint16_t i;
1060 ssize_t len;
1061 int saved_errno = 0;
1062 NTSTATUS status;
1063 int rc;
1064 bool ok;
1066 *converted_xattr = false;
1068 if (ad_getentrylen(ad, ADEID_FINDERI) == ADEDLEN_FINDERI) {
1069 return true;
1072 if (string_replace_cmaps == NULL) {
1073 const char **mappings = NULL;
1075 mappings = str_list_make_v3_const(
1076 talloc_tos(), fruit_catia_maps, NULL);
1077 if (mappings == NULL) {
1078 return false;
1080 string_replace_cmaps = string_replace_init_map(mappings);
1081 TALLOC_FREE(mappings);
1084 maplen = ad_getentryoff(ad, ADEID_RFORK) +
1085 ad_getentrylen(ad, ADEID_RFORK);
1087 /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
1088 map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
1089 ad->ad_fd, 0);
1090 if (map == MAP_FAILED) {
1091 DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
1092 return false;
1095 for (i = 0; i < ad->adx_header.adx_num_attrs; i++) {
1096 struct ad_xattr_entry *e = &ad->adx_entries[i];
1097 char *mapped_name = NULL;
1098 char *tmp = NULL;
1099 struct smb_filename *stream_name = NULL;
1100 files_struct *fsp = NULL;
1101 ssize_t nwritten;
1103 status = string_replace_allocate(ad->ad_handle->conn,
1104 e->adx_name,
1105 string_replace_cmaps,
1106 talloc_tos(),
1107 &mapped_name,
1108 vfs_translate_to_windows);
1109 if (!NT_STATUS_IS_OK(status) &&
1110 !NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))
1112 DBG_ERR("string_replace_allocate failed\n");
1113 ok = false;
1114 goto fail;
1117 tmp = mapped_name;
1118 mapped_name = talloc_asprintf(talloc_tos(), ":%s", tmp);
1119 TALLOC_FREE(tmp);
1120 if (mapped_name == NULL) {
1121 ok = false;
1122 goto fail;
1125 stream_name = synthetic_smb_fname(talloc_tos(),
1126 smb_fname->base_name,
1127 mapped_name,
1128 NULL,
1129 smb_fname->flags);
1130 TALLOC_FREE(mapped_name);
1131 if (stream_name == NULL) {
1132 DBG_ERR("synthetic_smb_fname failed\n");
1133 ok = false;
1134 goto fail;
1137 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1139 status = SMB_VFS_CREATE_FILE(
1140 ad->ad_handle->conn, /* conn */
1141 NULL, /* req */
1142 0, /* root_dir_fid */
1143 stream_name, /* fname */
1144 FILE_GENERIC_WRITE, /* access_mask */
1145 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
1146 FILE_OPEN_IF, /* create_disposition */
1147 0, /* create_options */
1148 0, /* file_attributes */
1149 INTERNAL_OPEN_ONLY, /* oplock_request */
1150 NULL, /* lease */
1151 0, /* allocation_size */
1152 0, /* private_flags */
1153 NULL, /* sd */
1154 NULL, /* ea_list */
1155 &fsp, /* result */
1156 NULL, /* psbuf */
1157 NULL, NULL); /* create context */
1158 TALLOC_FREE(stream_name);
1159 if (!NT_STATUS_IS_OK(status)) {
1160 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1161 ok = false;
1162 goto fail;
1165 nwritten = SMB_VFS_PWRITE(fsp,
1166 map + e->adx_offset,
1167 e->adx_length,
1169 if (nwritten == -1) {
1170 DBG_ERR("SMB_VFS_PWRITE failed\n");
1171 saved_errno = errno;
1172 close_file(NULL, fsp, ERROR_CLOSE);
1173 errno = saved_errno;
1174 ok = false;
1175 goto fail;
1178 status = close_file(NULL, fsp, NORMAL_CLOSE);
1179 if (!NT_STATUS_IS_OK(status)) {
1180 ok = false;
1181 goto fail;
1183 fsp = NULL;
1186 ad_setentrylen(ad, ADEID_FINDERI, ADEDLEN_FINDERI);
1188 ok = ad_pack(ad);
1189 if (!ok) {
1190 DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name);
1191 goto fail;
1194 len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
1195 if (len != AD_DATASZ_DOT_UND) {
1196 DBG_ERR("%s: bad size: %zd\n", smb_fname->base_name, len);
1197 ok = false;
1198 goto fail;
1201 ok = ad_convert_move_reso(ad, smb_fname);
1202 if (!ok) {
1203 goto fail;
1206 *converted_xattr = true;
1207 ok = true;
1209 fail:
1210 rc = munmap(map, maplen);
1211 if (rc != 0) {
1212 DBG_ERR("munmap failed: %s\n", strerror(errno));
1213 return false;
1216 return ok;
1219 static bool ad_convert_finderinfo(struct adouble *ad,
1220 const struct smb_filename *smb_fname)
1222 char *p_ad = NULL;
1223 AfpInfo *ai = NULL;
1224 DATA_BLOB aiblob;
1225 struct smb_filename *stream_name = NULL;
1226 files_struct *fsp = NULL;
1227 size_t size;
1228 ssize_t nwritten;
1229 NTSTATUS status;
1230 int saved_errno = 0;
1231 int cmp;
1233 cmp = memcmp(ad->ad_filler, AD_FILLER_TAG_OSX, ADEDLEN_FILLER);
1234 if (cmp != 0) {
1235 return true;
1238 p_ad = ad_get_entry(ad, ADEID_FINDERI);
1239 if (p_ad == NULL) {
1240 return false;
1243 ai = afpinfo_new(talloc_tos());
1244 if (ai == NULL) {
1245 return false;
1248 memcpy(ai->afpi_FinderInfo, p_ad, ADEDLEN_FINDERI);
1250 aiblob = data_blob_talloc(talloc_tos(), NULL, AFP_INFO_SIZE);
1251 if (aiblob.data == NULL) {
1252 TALLOC_FREE(ai);
1253 return false;
1256 size = afpinfo_pack(ai, (char *)aiblob.data);
1257 TALLOC_FREE(ai);
1258 if (size != AFP_INFO_SIZE) {
1259 return false;
1262 stream_name = synthetic_smb_fname(talloc_tos(),
1263 smb_fname->base_name,
1264 AFPINFO_STREAM,
1265 NULL,
1266 smb_fname->flags);
1267 if (stream_name == NULL) {
1268 data_blob_free(&aiblob);
1269 DBG_ERR("synthetic_smb_fname failed\n");
1270 return false;
1273 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1275 status = SMB_VFS_CREATE_FILE(
1276 ad->ad_handle->conn, /* conn */
1277 NULL, /* req */
1278 0, /* root_dir_fid */
1279 stream_name, /* fname */
1280 FILE_GENERIC_WRITE, /* access_mask */
1281 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
1282 FILE_OPEN_IF, /* create_disposition */
1283 0, /* create_options */
1284 0, /* file_attributes */
1285 INTERNAL_OPEN_ONLY, /* oplock_request */
1286 NULL, /* lease */
1287 0, /* allocation_size */
1288 0, /* private_flags */
1289 NULL, /* sd */
1290 NULL, /* ea_list */
1291 &fsp, /* result */
1292 NULL, /* psbuf */
1293 NULL, NULL); /* create context */
1294 TALLOC_FREE(stream_name);
1295 if (!NT_STATUS_IS_OK(status)) {
1296 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1297 return false;
1300 nwritten = SMB_VFS_PWRITE(fsp,
1301 aiblob.data,
1302 aiblob.length,
1304 if (nwritten == -1) {
1305 DBG_ERR("SMB_VFS_PWRITE failed\n");
1306 saved_errno = errno;
1307 close_file(NULL, fsp, ERROR_CLOSE);
1308 errno = saved_errno;
1309 return false;
1312 status = close_file(NULL, fsp, NORMAL_CLOSE);
1313 if (!NT_STATUS_IS_OK(status)) {
1314 return false;
1316 fsp = NULL;
1318 return true;
1321 static bool ad_convert_truncate(struct adouble *ad,
1322 const struct smb_filename *smb_fname)
1324 int rc;
1327 * FIXME: direct ftruncate(), but we don't have a fsp for the
1328 * VFS call
1330 rc = ftruncate(ad->ad_fd, ADEDOFF_RFORK_DOT_UND +
1331 ad_getentrylen(ad, ADEID_RFORK));
1332 if (rc != 0) {
1333 return false;
1336 return true;
1339 static bool ad_convert_blank_rfork(struct adouble *ad,
1340 bool *blank)
1342 struct fruit_config_data *config = NULL;
1343 uint8_t *map = MAP_FAILED;
1344 size_t maplen;
1345 int cmp;
1346 ssize_t len;
1347 int rc;
1348 bool ok;
1350 *blank = false;
1352 SMB_VFS_HANDLE_GET_DATA(ad->ad_handle, config,
1353 struct fruit_config_data, return false);
1355 if (!config->wipe_intentionally_left_blank_rfork) {
1356 return true;
1359 if (ad_getentrylen(ad, ADEID_RFORK) != sizeof(empty_resourcefork)) {
1360 return true;
1363 maplen = ad_getentryoff(ad, ADEID_RFORK) +
1364 ad_getentrylen(ad, ADEID_RFORK);
1366 /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
1367 map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
1368 ad->ad_fd, 0);
1369 if (map == MAP_FAILED) {
1370 DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
1371 return false;
1374 cmp = memcmp(map + ADEDOFF_RFORK_DOT_UND,
1375 empty_resourcefork,
1376 sizeof(empty_resourcefork));
1377 rc = munmap(map, maplen);
1378 if (rc != 0) {
1379 DBG_ERR("munmap failed: %s\n", strerror(errno));
1380 return false;
1383 if (cmp != 0) {
1384 return true;
1387 ad_setentrylen(ad, ADEID_RFORK, 0);
1389 ok = ad_pack(ad);
1390 if (!ok) {
1391 return false;
1394 len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
1395 if (len != AD_DATASZ_DOT_UND) {
1396 return false;
1399 *blank = true;
1400 return true;
1403 static bool ad_convert_delete_adfile(struct adouble *ad,
1404 const struct smb_filename *smb_fname)
1406 struct fruit_config_data *config = NULL;
1407 struct smb_filename *ad_name = NULL;
1408 int rc;
1410 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
1411 return true;
1414 SMB_VFS_HANDLE_GET_DATA(ad->ad_handle, config,
1415 struct fruit_config_data, return false);
1417 if (!config->delete_empty_adfiles) {
1418 return true;
1421 rc = adouble_path(talloc_tos(), smb_fname, &ad_name);
1422 if (rc != 0) {
1423 return false;
1426 rc = SMB_VFS_NEXT_UNLINK(ad->ad_handle, ad_name);
1427 if (rc != 0) {
1428 DBG_ERR("Unlinking [%s] failed: %s\n",
1429 smb_fname_str_dbg(ad_name), strerror(errno));
1430 TALLOC_FREE(ad_name);
1431 return false;
1434 DBG_WARNING("Unlinked [%s] after conversion\n", smb_fname_str_dbg(ad_name));
1435 TALLOC_FREE(ad_name);
1437 return true;
1441 * Convert from Apple's ._ file to Netatalk
1443 * Apple's AppleDouble may contain a FinderInfo entry longer then 32
1444 * bytes containing packed xattrs.
1446 * @return -1 in case an error occurred, 0 if no conversion was done, 1
1447 * otherwise
1449 static int ad_convert(struct adouble *ad,
1450 const struct smb_filename *smb_fname)
1452 bool ok;
1453 bool converted_xattr = false;
1454 bool blank;
1456 ok = ad_convert_xattr(ad, smb_fname, &converted_xattr);
1457 if (!ok) {
1458 return -1;
1461 ok = ad_convert_blank_rfork(ad, &blank);
1462 if (!ok) {
1463 return -1;
1466 if (converted_xattr || blank) {
1467 ok = ad_convert_truncate(ad, smb_fname);
1468 if (!ok) {
1469 return -1;
1473 ok = ad_convert_finderinfo(ad, smb_fname);
1474 if (!ok) {
1475 DBG_ERR("Failed to convert [%s]\n",
1476 smb_fname_str_dbg(smb_fname));
1477 return -1;
1480 ok = ad_convert_delete_adfile(ad, smb_fname);
1481 if (!ok) {
1482 return -1;
1485 return 0;
1489 * Read and parse Netatalk AppleDouble metadata xattr
1491 static ssize_t ad_read_meta(struct adouble *ad,
1492 const struct smb_filename *smb_fname)
1494 int rc = 0;
1495 ssize_t ealen;
1496 bool ok;
1498 DEBUG(10, ("reading meta xattr for %s\n", smb_fname->base_name));
1500 ealen = SMB_VFS_GETXATTR(ad->ad_handle->conn, smb_fname,
1501 AFPINFO_EA_NETATALK, ad->ad_data,
1502 AD_DATASZ_XATTR);
1503 if (ealen == -1) {
1504 switch (errno) {
1505 case ENOATTR:
1506 case ENOENT:
1507 if (errno == ENOATTR) {
1508 errno = ENOENT;
1510 rc = -1;
1511 goto exit;
1512 default:
1513 DEBUG(2, ("error reading meta xattr: %s\n",
1514 strerror(errno)));
1515 rc = -1;
1516 goto exit;
1519 if (ealen != AD_DATASZ_XATTR) {
1520 DEBUG(2, ("bad size %zd\n", ealen));
1521 errno = EINVAL;
1522 rc = -1;
1523 goto exit;
1526 /* Now parse entries */
1527 ok = ad_unpack(ad, ADEID_NUM_XATTR, AD_DATASZ_XATTR);
1528 if (!ok) {
1529 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1530 errno = EINVAL;
1531 rc = -1;
1532 goto exit;
1535 if (!ad_getentryoff(ad, ADEID_FINDERI)
1536 || !ad_getentryoff(ad, ADEID_COMMENT)
1537 || !ad_getentryoff(ad, ADEID_FILEDATESI)
1538 || !ad_getentryoff(ad, ADEID_AFPFILEI)
1539 || !ad_getentryoff(ad, ADEID_PRIVDEV)
1540 || !ad_getentryoff(ad, ADEID_PRIVINO)
1541 || !ad_getentryoff(ad, ADEID_PRIVSYN)
1542 || !ad_getentryoff(ad, ADEID_PRIVID)) {
1543 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1544 errno = EINVAL;
1545 rc = -1;
1546 goto exit;
1549 exit:
1550 DEBUG(10, ("reading meta xattr for %s, rc: %d\n",
1551 smb_fname->base_name, rc));
1553 if (rc != 0) {
1554 ealen = -1;
1555 if (errno == EINVAL) {
1556 become_root();
1557 removexattr(smb_fname->base_name, AFPINFO_EA_NETATALK);
1558 unbecome_root();
1559 errno = ENOENT;
1562 return ealen;
1565 static int ad_open_rsrc_xattr(const struct smb_filename *smb_fname,
1566 int flags,
1567 mode_t mode)
1569 #ifdef HAVE_ATTROPEN
1570 /* FIXME: direct Solaris xattr syscall */
1571 return attropen(smb_fname->base_name,
1572 AFPRESOURCE_EA_NETATALK, flags, mode);
1573 #else
1574 errno = ENOSYS;
1575 return -1;
1576 #endif
1579 static int ad_open_rsrc_adouble(const struct smb_filename *smb_fname,
1580 int flags,
1581 mode_t mode)
1583 int ret;
1584 int fd;
1585 struct smb_filename *adp_smb_fname = NULL;
1587 ret = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
1588 if (ret != 0) {
1589 return -1;
1592 fd = open(adp_smb_fname->base_name, flags, mode);
1593 TALLOC_FREE(adp_smb_fname);
1595 return fd;
1598 static int ad_open_rsrc(vfs_handle_struct *handle,
1599 const struct smb_filename *smb_fname,
1600 int flags,
1601 mode_t mode)
1603 struct fruit_config_data *config = NULL;
1604 int fd;
1606 SMB_VFS_HANDLE_GET_DATA(handle, config,
1607 struct fruit_config_data, return -1);
1609 if (config->rsrc == FRUIT_RSRC_XATTR) {
1610 fd = ad_open_rsrc_xattr(smb_fname, flags, mode);
1611 } else {
1612 fd = ad_open_rsrc_adouble(smb_fname, flags, mode);
1615 return fd;
1619 * Here's the deal: for ADOUBLE_META we can do without an fd as we can issue
1620 * path based xattr calls. For ADOUBLE_RSRC however we need a full-fledged fd
1621 * for file IO on the ._ file.
1623 static int ad_open(vfs_handle_struct *handle,
1624 struct adouble *ad,
1625 files_struct *fsp,
1626 const struct smb_filename *smb_fname,
1627 int flags,
1628 mode_t mode)
1630 int fd;
1632 DBG_DEBUG("Path [%s] type [%s]\n", smb_fname->base_name,
1633 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
1635 if (ad->ad_type == ADOUBLE_META) {
1636 return 0;
1639 if ((fsp != NULL) && (fsp->fh != NULL) && (fsp->fh->fd != -1)) {
1640 ad->ad_fd = fsp->fh->fd;
1641 ad->ad_opened = false;
1642 return 0;
1645 fd = ad_open_rsrc(handle, smb_fname, flags, mode);
1646 if (fd == -1) {
1647 return -1;
1649 ad->ad_opened = true;
1650 ad->ad_fd = fd;
1652 DBG_DEBUG("Path [%s] type [%s] fd [%d]\n",
1653 smb_fname->base_name,
1654 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc", fd);
1656 return 0;
1659 static ssize_t ad_read_rsrc_xattr(struct adouble *ad)
1661 int ret;
1662 SMB_STRUCT_STAT st;
1664 /* FIXME: direct sys_fstat(), don't have an fsp */
1665 ret = sys_fstat(ad->ad_fd, &st,
1666 lp_fake_directory_create_times(
1667 SNUM(ad->ad_handle->conn)));
1668 if (ret != 0) {
1669 return -1;
1672 ad_setentrylen(ad, ADEID_RFORK, st.st_ex_size);
1673 return st.st_ex_size;
1676 static ssize_t ad_read_rsrc_adouble(struct adouble *ad,
1677 const struct smb_filename *smb_fname)
1679 SMB_STRUCT_STAT sbuf;
1680 char *p_ad = NULL;
1681 size_t size;
1682 ssize_t len;
1683 int ret;
1684 bool ok;
1686 ret = sys_fstat(ad->ad_fd, &sbuf, lp_fake_directory_create_times(
1687 SNUM(ad->ad_handle->conn)));
1688 if (ret != 0) {
1689 return -1;
1693 * AppleDouble file header content and size, two cases:
1695 * - without xattrs it is exactly AD_DATASZ_DOT_UND (82) bytes large
1696 * - with embedded xattrs it can be larger, up to AD_XATTR_MAX_HDR_SIZE
1698 * Read as much as we can up to AD_XATTR_MAX_HDR_SIZE.
1700 size = sbuf.st_ex_size;
1701 if (size > talloc_array_length(ad->ad_data)) {
1702 if (size > AD_XATTR_MAX_HDR_SIZE) {
1703 size = AD_XATTR_MAX_HDR_SIZE;
1705 p_ad = talloc_realloc(ad, ad->ad_data, char, size);
1706 if (p_ad == NULL) {
1707 return -1;
1709 ad->ad_data = p_ad;
1712 len = sys_pread(ad->ad_fd, ad->ad_data,
1713 talloc_array_length(ad->ad_data), 0);
1714 if (len != talloc_array_length(ad->ad_data)) {
1715 DBG_NOTICE("%s %s: bad size: %zd\n",
1716 smb_fname->base_name, strerror(errno), len);
1717 return -1;
1720 /* Now parse entries */
1721 ok = ad_unpack(ad, ADEID_NUM_DOT_UND, sbuf.st_ex_size);
1722 if (!ok) {
1723 DBG_ERR("invalid AppleDouble resource %s\n",
1724 smb_fname->base_name);
1725 errno = EINVAL;
1726 return -1;
1729 if ((ad_getentryoff(ad, ADEID_FINDERI) != ADEDOFF_FINDERI_DOT_UND)
1730 || (ad_getentrylen(ad, ADEID_FINDERI) < ADEDLEN_FINDERI)
1731 || (ad_getentryoff(ad, ADEID_RFORK) < ADEDOFF_RFORK_DOT_UND)) {
1732 DBG_ERR("invalid AppleDouble resource %s\n",
1733 smb_fname->base_name);
1734 errno = EINVAL;
1735 return -1;
1739 * Try to fixup AppleDouble files created by OS X with xattrs
1740 * appended to the ADEID_FINDERI entry.
1743 ret = ad_convert(ad, smb_fname);
1744 if (ret != 0) {
1745 DBG_WARNING("Failed to convert [%s]\n", smb_fname->base_name);
1746 return len;
1749 return len;
1753 * Read and parse resource fork, either ._ AppleDouble file or xattr
1755 static ssize_t ad_read_rsrc(struct adouble *ad,
1756 const struct smb_filename *smb_fname)
1758 struct fruit_config_data *config = NULL;
1759 ssize_t len;
1761 SMB_VFS_HANDLE_GET_DATA(ad->ad_handle, config,
1762 struct fruit_config_data, return -1);
1764 if (config->rsrc == FRUIT_RSRC_XATTR) {
1765 len = ad_read_rsrc_xattr(ad);
1766 } else {
1767 len = ad_read_rsrc_adouble(ad, smb_fname);
1770 return len;
1774 * Read and unpack an AppleDouble metadata xattr or resource
1776 static ssize_t ad_read(struct adouble *ad, const struct smb_filename *smb_fname)
1778 switch (ad->ad_type) {
1779 case ADOUBLE_META:
1780 return ad_read_meta(ad, smb_fname);
1781 case ADOUBLE_RSRC:
1782 return ad_read_rsrc(ad, smb_fname);
1783 default:
1784 return -1;
1788 static int adouble_destructor(struct adouble *ad)
1790 if ((ad->ad_fd != -1) && ad->ad_opened) {
1791 close(ad->ad_fd);
1792 ad->ad_fd = -1;
1794 return 0;
1798 * Allocate a struct adouble without initialiing it
1800 * The struct is either hang of the fsp extension context or if fsp is
1801 * NULL from ctx.
1803 * @param[in] ctx talloc context
1804 * @param[in] handle vfs handle
1805 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1807 * @return adouble handle
1809 static struct adouble *ad_alloc(TALLOC_CTX *ctx, vfs_handle_struct *handle,
1810 adouble_type_t type)
1812 int rc = 0;
1813 size_t adsize = 0;
1814 struct adouble *ad;
1815 struct fruit_config_data *config;
1817 SMB_VFS_HANDLE_GET_DATA(handle, config,
1818 struct fruit_config_data, return NULL);
1820 switch (type) {
1821 case ADOUBLE_META:
1822 adsize = AD_DATASZ_XATTR;
1823 break;
1824 case ADOUBLE_RSRC:
1825 if (config->rsrc == FRUIT_RSRC_ADFILE) {
1826 adsize = AD_DATASZ_DOT_UND;
1828 break;
1829 default:
1830 return NULL;
1833 ad = talloc_zero(ctx, struct adouble);
1834 if (ad == NULL) {
1835 rc = -1;
1836 goto exit;
1839 if (adsize) {
1840 ad->ad_data = talloc_zero_array(ad, char, adsize);
1841 if (ad->ad_data == NULL) {
1842 rc = -1;
1843 goto exit;
1847 ad->ad_handle = handle;
1848 ad->ad_type = type;
1849 ad->ad_magic = AD_MAGIC;
1850 ad->ad_version = AD_VERSION;
1851 ad->ad_fd = -1;
1853 talloc_set_destructor(ad, adouble_destructor);
1855 exit:
1856 if (rc != 0) {
1857 TALLOC_FREE(ad);
1859 return ad;
1863 * Allocate and initialize a new struct adouble
1865 * @param[in] ctx talloc context
1866 * @param[in] handle vfs handle
1867 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1869 * @return adouble handle, initialized
1871 static struct adouble *ad_init(TALLOC_CTX *ctx, vfs_handle_struct *handle,
1872 adouble_type_t type)
1874 int rc = 0;
1875 const struct ad_entry_order *eid;
1876 struct adouble *ad = NULL;
1877 struct fruit_config_data *config;
1878 time_t t = time(NULL);
1880 SMB_VFS_HANDLE_GET_DATA(handle, config,
1881 struct fruit_config_data, return NULL);
1883 switch (type) {
1884 case ADOUBLE_META:
1885 eid = entry_order_meta_xattr;
1886 break;
1887 case ADOUBLE_RSRC:
1888 if (config->rsrc == FRUIT_RSRC_ADFILE) {
1889 eid = entry_order_dot_und;
1890 } else {
1891 eid = entry_order_rsrc_xattr;
1893 break;
1894 default:
1895 return NULL;
1898 ad = ad_alloc(ctx, handle, type);
1899 if (ad == NULL) {
1900 return NULL;
1903 while (eid->id) {
1904 ad->ad_eid[eid->id].ade_off = eid->offset;
1905 ad->ad_eid[eid->id].ade_len = eid->len;
1906 eid++;
1909 /* put something sane in the date fields */
1910 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, t);
1911 ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, t);
1912 ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, t);
1913 ad_setdate(ad, AD_DATE_BACKUP, htonl(AD_DATE_START));
1915 if (rc != 0) {
1916 TALLOC_FREE(ad);
1918 return ad;
1921 static struct adouble *ad_get_internal(TALLOC_CTX *ctx,
1922 vfs_handle_struct *handle,
1923 files_struct *fsp,
1924 const struct smb_filename *smb_fname,
1925 adouble_type_t type)
1927 int rc = 0;
1928 ssize_t len;
1929 struct adouble *ad = NULL;
1930 int mode;
1932 if (fsp != NULL) {
1933 smb_fname = fsp->base_fsp->fsp_name;
1936 DEBUG(10, ("ad_get(%s) called for %s\n",
1937 type == ADOUBLE_META ? "meta" : "rsrc",
1938 smb_fname->base_name));
1940 ad = ad_alloc(ctx, handle, type);
1941 if (ad == NULL) {
1942 rc = -1;
1943 goto exit;
1946 /* Try rw first so we can use the fd in ad_convert() */
1947 mode = O_RDWR;
1949 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
1950 if (rc == -1 && ((errno == EROFS) || (errno == EACCES))) {
1951 mode = O_RDONLY;
1952 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
1954 if (rc == -1) {
1955 DBG_DEBUG("ad_open [%s] error [%s]\n",
1956 smb_fname->base_name, strerror(errno));
1957 goto exit;
1961 len = ad_read(ad, smb_fname);
1962 if (len == -1) {
1963 DEBUG(10, ("error reading AppleDouble for %s\n",
1964 smb_fname->base_name));
1965 rc = -1;
1966 goto exit;
1969 exit:
1970 DEBUG(10, ("ad_get(%s) for %s returning %d\n",
1971 type == ADOUBLE_META ? "meta" : "rsrc",
1972 smb_fname->base_name, rc));
1974 if (rc != 0) {
1975 TALLOC_FREE(ad);
1977 return ad;
1981 * Return AppleDouble data for a file
1983 * @param[in] ctx talloc context
1984 * @param[in] handle vfs handle
1985 * @param[in] smb_fname pathname to file or directory
1986 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1988 * @return talloced struct adouble or NULL on error
1990 static struct adouble *ad_get(TALLOC_CTX *ctx,
1991 vfs_handle_struct *handle,
1992 const struct smb_filename *smb_fname,
1993 adouble_type_t type)
1995 return ad_get_internal(ctx, handle, NULL, smb_fname, type);
1999 * Return AppleDouble data for a file
2001 * @param[in] ctx talloc context
2002 * @param[in] handle vfs handle
2003 * @param[in] fsp fsp to use for IO
2004 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2006 * @return talloced struct adouble or NULL on error
2008 static struct adouble *ad_fget(TALLOC_CTX *ctx, vfs_handle_struct *handle,
2009 files_struct *fsp, adouble_type_t type)
2011 return ad_get_internal(ctx, handle, fsp, NULL, type);
2015 * Set AppleDouble metadata on a file or directory
2017 * @param[in] ad adouble handle
2019 * @param[in] smb_fname pathname to file or directory
2021 * @return status code, 0 means success
2023 static int ad_set(struct adouble *ad, const struct smb_filename *smb_fname)
2025 bool ok;
2026 int ret;
2028 DBG_DEBUG("Path [%s]\n", smb_fname->base_name);
2030 if (ad->ad_type != ADOUBLE_META) {
2031 DBG_ERR("ad_set on [%s] used with ADOUBLE_RSRC\n",
2032 smb_fname->base_name);
2033 return -1;
2036 ok = ad_pack(ad);
2037 if (!ok) {
2038 return -1;
2041 ret = SMB_VFS_SETXATTR(ad->ad_handle->conn,
2042 smb_fname,
2043 AFPINFO_EA_NETATALK,
2044 ad->ad_data,
2045 AD_DATASZ_XATTR, 0);
2047 DBG_DEBUG("Path [%s] ret [%d]\n", smb_fname->base_name, ret);
2049 return ret;
2053 * Set AppleDouble metadata on a file or directory
2055 * @param[in] ad adouble handle
2056 * @param[in] fsp file handle
2058 * @return status code, 0 means success
2060 static int ad_fset(struct adouble *ad, files_struct *fsp)
2062 int rc = -1;
2063 ssize_t len;
2064 bool ok;
2066 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
2068 if ((fsp == NULL)
2069 || (fsp->fh == NULL)
2070 || (fsp->fh->fd == -1))
2072 smb_panic("bad fsp");
2075 ok = ad_pack(ad);
2076 if (!ok) {
2077 return -1;
2080 switch (ad->ad_type) {
2081 case ADOUBLE_META:
2082 rc = SMB_VFS_NEXT_SETXATTR(ad->ad_handle,
2083 fsp->fsp_name,
2084 AFPINFO_EA_NETATALK,
2085 ad->ad_data,
2086 AD_DATASZ_XATTR, 0);
2087 break;
2089 case ADOUBLE_RSRC:
2090 len = SMB_VFS_NEXT_PWRITE(ad->ad_handle,
2091 fsp,
2092 ad->ad_data,
2093 AD_DATASZ_DOT_UND,
2095 if (len != AD_DATASZ_DOT_UND) {
2096 DBG_ERR("short write on %s: %zd", fsp_str_dbg(fsp), len);
2097 return -1;
2099 rc = 0;
2100 break;
2102 default:
2103 return -1;
2106 DBG_DEBUG("Path [%s] rc [%d]\n", fsp_str_dbg(fsp), rc);
2108 return rc;
2111 /*****************************************************************************
2112 * Helper functions
2113 *****************************************************************************/
2115 static bool is_afpinfo_stream(const struct smb_filename *smb_fname)
2117 if (strncasecmp_m(smb_fname->stream_name,
2118 AFPINFO_STREAM_NAME,
2119 strlen(AFPINFO_STREAM_NAME)) == 0) {
2120 return true;
2122 return false;
2125 static bool is_afpresource_stream(const struct smb_filename *smb_fname)
2127 if (strncasecmp_m(smb_fname->stream_name,
2128 AFPRESOURCE_STREAM_NAME,
2129 strlen(AFPRESOURCE_STREAM_NAME)) == 0) {
2130 return true;
2132 return false;
2136 * Test whether stream is an Apple stream, not used atm
2138 #if 0
2139 static bool is_apple_stream(const struct smb_filename *smb_fname)
2141 if (is_afpinfo_stream(smb_fname)) {
2142 return true;
2144 if (is_afpresource_stream(smb_fname)) {
2145 return true;
2147 return false;
2149 #endif
2152 * Initialize config struct from our smb.conf config parameters
2154 static int init_fruit_config(vfs_handle_struct *handle)
2156 struct fruit_config_data *config;
2157 int enumval;
2158 const char *tm_size_str = NULL;
2160 config = talloc_zero(handle->conn, struct fruit_config_data);
2161 if (!config) {
2162 DEBUG(1, ("talloc_zero() failed\n"));
2163 errno = ENOMEM;
2164 return -1;
2168 * Versions up to Samba 4.5.x had a spelling bug in the
2169 * fruit:resource option calling lp_parm_enum with
2170 * "res*s*ource" (ie two s).
2172 * In Samba 4.6 we accept both the wrong and the correct
2173 * spelling, in Samba 4.7 the bad spelling will be removed.
2175 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2176 "ressource", fruit_rsrc, FRUIT_RSRC_ADFILE);
2177 if (enumval == -1) {
2178 DEBUG(1, ("value for %s: resource type unknown\n",
2179 FRUIT_PARAM_TYPE_NAME));
2180 return -1;
2182 config->rsrc = (enum fruit_rsrc)enumval;
2184 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2185 "resource", fruit_rsrc, enumval);
2186 if (enumval == -1) {
2187 DEBUG(1, ("value for %s: resource type unknown\n",
2188 FRUIT_PARAM_TYPE_NAME));
2189 return -1;
2191 config->rsrc = (enum fruit_rsrc)enumval;
2193 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2194 "metadata", fruit_meta, FRUIT_META_NETATALK);
2195 if (enumval == -1) {
2196 DEBUG(1, ("value for %s: metadata type unknown\n",
2197 FRUIT_PARAM_TYPE_NAME));
2198 return -1;
2200 config->meta = (enum fruit_meta)enumval;
2202 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2203 "locking", fruit_locking, FRUIT_LOCKING_NONE);
2204 if (enumval == -1) {
2205 DEBUG(1, ("value for %s: locking type unknown\n",
2206 FRUIT_PARAM_TYPE_NAME));
2207 return -1;
2209 config->locking = (enum fruit_locking)enumval;
2211 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2212 "encoding", fruit_encoding, FRUIT_ENC_PRIVATE);
2213 if (enumval == -1) {
2214 DEBUG(1, ("value for %s: encoding type unknown\n",
2215 FRUIT_PARAM_TYPE_NAME));
2216 return -1;
2218 config->encoding = (enum fruit_encoding)enumval;
2220 if (config->rsrc == FRUIT_RSRC_ADFILE) {
2221 config->veto_appledouble = lp_parm_bool(SNUM(handle->conn),
2222 FRUIT_PARAM_TYPE_NAME,
2223 "veto_appledouble",
2224 true);
2227 config->use_aapl = lp_parm_bool(
2228 -1, FRUIT_PARAM_TYPE_NAME, "aapl", true);
2230 config->time_machine = lp_parm_bool(
2231 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "time machine", false);
2233 config->unix_info_enabled = lp_parm_bool(
2234 -1, FRUIT_PARAM_TYPE_NAME, "nfs_aces", true);
2236 config->use_copyfile = lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME,
2237 "copyfile", false);
2239 config->posix_rename = lp_parm_bool(
2240 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "posix_rename", true);
2242 config->aapl_zero_file_id =
2243 lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME, "zero_file_id", true);
2245 config->readdir_attr_rsize = lp_parm_bool(
2246 SNUM(handle->conn), "readdir_attr", "aapl_rsize", true);
2248 config->readdir_attr_finder_info = lp_parm_bool(
2249 SNUM(handle->conn), "readdir_attr", "aapl_finder_info", true);
2251 config->readdir_attr_max_access = lp_parm_bool(
2252 SNUM(handle->conn), "readdir_attr", "aapl_max_access", true);
2254 config->model = lp_parm_const_string(
2255 -1, FRUIT_PARAM_TYPE_NAME, "model", "MacSamba");
2257 tm_size_str = lp_parm_const_string(
2258 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2259 "time machine max size", NULL);
2260 if (tm_size_str != NULL) {
2261 config->time_machine_max_size = conv_str_size(tm_size_str);
2264 config->wipe_intentionally_left_blank_rfork = lp_parm_bool(
2265 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2266 "wipe_intentionally_left_blank_rfork", false);
2268 config->delete_empty_adfiles = lp_parm_bool(
2269 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2270 "delete_empty_adfiles", false);
2272 SMB_VFS_HANDLE_SET_DATA(handle, config,
2273 NULL, struct fruit_config_data,
2274 return -1);
2276 return 0;
2280 * Prepend "._" to a basename
2281 * Return a new struct smb_filename with stream_name == NULL.
2283 static int adouble_path(TALLOC_CTX *ctx,
2284 const struct smb_filename *smb_fname_in,
2285 struct smb_filename **pp_smb_fname_out)
2287 char *parent;
2288 const char *base;
2289 struct smb_filename *smb_fname = cp_smb_filename(ctx,
2290 smb_fname_in);
2292 if (smb_fname == NULL) {
2293 return -1;
2296 /* We need streamname to be NULL */
2297 TALLOC_FREE(smb_fname->stream_name);
2299 /* And we're replacing base_name. */
2300 TALLOC_FREE(smb_fname->base_name);
2302 if (!parent_dirname(smb_fname, smb_fname_in->base_name,
2303 &parent, &base)) {
2304 TALLOC_FREE(smb_fname);
2305 return -1;
2308 smb_fname->base_name = talloc_asprintf(smb_fname,
2309 "%s/._%s", parent, base);
2310 if (smb_fname->base_name == NULL) {
2311 TALLOC_FREE(smb_fname);
2312 return -1;
2315 *pp_smb_fname_out = smb_fname;
2317 return 0;
2321 * Allocate and initialize an AfpInfo struct
2323 static AfpInfo *afpinfo_new(TALLOC_CTX *ctx)
2325 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2326 if (ai == NULL) {
2327 return NULL;
2329 ai->afpi_Signature = AFP_Signature;
2330 ai->afpi_Version = AFP_Version;
2331 ai->afpi_BackupTime = AD_DATE_START;
2332 return ai;
2336 * Pack an AfpInfo struct into a buffer
2338 * Buffer size must be at least AFP_INFO_SIZE
2339 * Returns size of packed buffer
2341 static ssize_t afpinfo_pack(const AfpInfo *ai, char *buf)
2343 memset(buf, 0, AFP_INFO_SIZE);
2345 RSIVAL(buf, 0, ai->afpi_Signature);
2346 RSIVAL(buf, 4, ai->afpi_Version);
2347 RSIVAL(buf, 12, ai->afpi_BackupTime);
2348 memcpy(buf + 16, ai->afpi_FinderInfo, sizeof(ai->afpi_FinderInfo));
2350 return AFP_INFO_SIZE;
2354 * Unpack a buffer into a AfpInfo structure
2356 * Buffer size must be at least AFP_INFO_SIZE
2357 * Returns allocated AfpInfo struct
2359 static AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data)
2361 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2362 if (ai == NULL) {
2363 return NULL;
2366 ai->afpi_Signature = RIVAL(data, 0);
2367 ai->afpi_Version = RIVAL(data, 4);
2368 ai->afpi_BackupTime = RIVAL(data, 12);
2369 memcpy(ai->afpi_FinderInfo, (const char *)data + 16,
2370 sizeof(ai->afpi_FinderInfo));
2372 if (ai->afpi_Signature != AFP_Signature
2373 || ai->afpi_Version != AFP_Version) {
2374 DEBUG(1, ("Bad AfpInfo signature or version\n"));
2375 TALLOC_FREE(ai);
2378 return ai;
2382 * Fake an inode number from the md5 hash of the (xattr) name
2384 static SMB_INO_T fruit_inode(const SMB_STRUCT_STAT *sbuf, const char *sname)
2386 MD5_CTX ctx;
2387 unsigned char hash[16];
2388 SMB_INO_T result;
2389 char *upper_sname;
2391 DBG_DEBUG("fruit_inode called for %ju/%ju [%s]\n",
2392 (uintmax_t)sbuf->st_ex_dev,
2393 (uintmax_t)sbuf->st_ex_ino, sname);
2395 upper_sname = talloc_strdup_upper(talloc_tos(), sname);
2396 SMB_ASSERT(upper_sname != NULL);
2398 MD5Init(&ctx);
2399 MD5Update(&ctx, (const unsigned char *)&(sbuf->st_ex_dev),
2400 sizeof(sbuf->st_ex_dev));
2401 MD5Update(&ctx, (const unsigned char *)&(sbuf->st_ex_ino),
2402 sizeof(sbuf->st_ex_ino));
2403 MD5Update(&ctx, (unsigned char *)upper_sname,
2404 talloc_get_size(upper_sname)-1);
2405 MD5Final(hash, &ctx);
2407 TALLOC_FREE(upper_sname);
2409 /* Hopefully all the variation is in the lower 4 (or 8) bytes! */
2410 memcpy(&result, hash, sizeof(result));
2412 DBG_DEBUG("fruit_inode \"%s\": ino=%ju\n",
2413 sname, (uintmax_t)result);
2415 return result;
2418 static bool add_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
2419 struct stream_struct **streams,
2420 const char *name, off_t size,
2421 off_t alloc_size)
2423 struct stream_struct *tmp;
2425 tmp = talloc_realloc(mem_ctx, *streams, struct stream_struct,
2426 (*num_streams)+1);
2427 if (tmp == NULL) {
2428 return false;
2431 tmp[*num_streams].name = talloc_asprintf(tmp, "%s:$DATA", name);
2432 if (tmp[*num_streams].name == NULL) {
2433 return false;
2436 tmp[*num_streams].size = size;
2437 tmp[*num_streams].alloc_size = alloc_size;
2439 *streams = tmp;
2440 *num_streams += 1;
2441 return true;
2444 static bool filter_empty_rsrc_stream(unsigned int *num_streams,
2445 struct stream_struct **streams)
2447 struct stream_struct *tmp = *streams;
2448 unsigned int i;
2450 if (*num_streams == 0) {
2451 return true;
2454 for (i = 0; i < *num_streams; i++) {
2455 if (strequal_m(tmp[i].name, AFPRESOURCE_STREAM)) {
2456 break;
2460 if (i == *num_streams) {
2461 return true;
2464 if (tmp[i].size > 0) {
2465 return true;
2468 TALLOC_FREE(tmp[i].name);
2469 if (*num_streams - 1 > i) {
2470 memmove(&tmp[i], &tmp[i+1],
2471 (*num_streams - i - 1) * sizeof(struct stream_struct));
2474 *num_streams -= 1;
2475 return true;
2478 static bool del_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
2479 struct stream_struct **streams,
2480 const char *name)
2482 struct stream_struct *tmp = *streams;
2483 unsigned int i;
2485 if (*num_streams == 0) {
2486 return true;
2489 for (i = 0; i < *num_streams; i++) {
2490 if (strequal_m(tmp[i].name, name)) {
2491 break;
2495 if (i == *num_streams) {
2496 return true;
2499 TALLOC_FREE(tmp[i].name);
2500 if (*num_streams - 1 > i) {
2501 memmove(&tmp[i], &tmp[i+1],
2502 (*num_streams - i - 1) * sizeof(struct stream_struct));
2505 *num_streams -= 1;
2506 return true;
2509 static bool ad_empty_finderinfo(const struct adouble *ad)
2511 int cmp;
2512 char emptybuf[ADEDLEN_FINDERI] = {0};
2513 char *fi = NULL;
2515 fi = ad_get_entry(ad, ADEID_FINDERI);
2516 if (fi == NULL) {
2517 DBG_ERR("Missing FinderInfo in struct adouble [%p]\n", ad);
2518 return false;
2521 cmp = memcmp(emptybuf, fi, ADEDLEN_FINDERI);
2522 return (cmp == 0);
2525 static bool ai_empty_finderinfo(const AfpInfo *ai)
2527 int cmp;
2528 char emptybuf[ADEDLEN_FINDERI] = {0};
2530 cmp = memcmp(emptybuf, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
2531 return (cmp == 0);
2535 * Update btime with btime from Netatalk
2537 static void update_btime(vfs_handle_struct *handle,
2538 struct smb_filename *smb_fname)
2540 uint32_t t;
2541 struct timespec creation_time = {0};
2542 struct adouble *ad;
2543 struct fruit_config_data *config = NULL;
2545 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
2546 return);
2548 switch (config->meta) {
2549 case FRUIT_META_STREAM:
2550 return;
2551 case FRUIT_META_NETATALK:
2552 /* Handled below */
2553 break;
2554 default:
2555 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
2556 return;
2559 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
2560 if (ad == NULL) {
2561 return;
2563 if (ad_getdate(ad, AD_DATE_UNIX | AD_DATE_CREATE, &t) != 0) {
2564 TALLOC_FREE(ad);
2565 return;
2567 TALLOC_FREE(ad);
2569 creation_time.tv_sec = convert_uint32_t_to_time_t(t);
2570 update_stat_ex_create_time(&smb_fname->st, creation_time);
2572 return;
2576 * Map an access mask to a Netatalk single byte byte range lock
2578 static off_t access_to_netatalk_brl(enum apple_fork fork_type,
2579 uint32_t access_mask)
2581 off_t offset;
2583 switch (access_mask) {
2584 case FILE_READ_DATA:
2585 offset = AD_FILELOCK_OPEN_RD;
2586 break;
2588 case FILE_WRITE_DATA:
2589 case FILE_APPEND_DATA:
2590 offset = AD_FILELOCK_OPEN_WR;
2591 break;
2593 default:
2594 offset = AD_FILELOCK_OPEN_NONE;
2595 break;
2598 if (fork_type == APPLE_FORK_RSRC) {
2599 if (offset == AD_FILELOCK_OPEN_NONE) {
2600 offset = AD_FILELOCK_RSRC_OPEN_NONE;
2601 } else {
2602 offset += 2;
2606 return offset;
2610 * Map a deny mode to a Netatalk brl
2612 static off_t denymode_to_netatalk_brl(enum apple_fork fork_type,
2613 uint32_t deny_mode)
2615 off_t offset = 0;
2617 switch (deny_mode) {
2618 case DENY_READ:
2619 offset = AD_FILELOCK_DENY_RD;
2620 break;
2622 case DENY_WRITE:
2623 offset = AD_FILELOCK_DENY_WR;
2624 break;
2626 default:
2627 smb_panic("denymode_to_netatalk_brl: bad deny mode\n");
2630 if (fork_type == APPLE_FORK_RSRC) {
2631 offset += 2;
2634 return offset;
2638 * Call fcntl() with an exclusive F_GETLK request in order to
2639 * determine if there's an exisiting shared lock
2641 * @return true if the requested lock was found or any error occurred
2642 * false if the lock was not found
2644 static bool test_netatalk_lock(files_struct *fsp, off_t in_offset)
2646 bool result;
2647 off_t offset = in_offset;
2648 off_t len = 1;
2649 int type = F_WRLCK;
2650 pid_t pid = 0;
2652 result = SMB_VFS_GETLOCK(fsp, &offset, &len, &type, &pid);
2653 if (result == false) {
2654 return true;
2657 if (type != F_UNLCK) {
2658 return true;
2661 return false;
2664 static NTSTATUS fruit_check_access(vfs_handle_struct *handle,
2665 files_struct *fsp,
2666 uint32_t access_mask,
2667 uint32_t share_mode)
2669 NTSTATUS status = NT_STATUS_OK;
2670 off_t off;
2671 bool share_for_read = (share_mode & FILE_SHARE_READ);
2672 bool share_for_write = (share_mode & FILE_SHARE_WRITE);
2673 bool netatalk_already_open_for_reading = false;
2674 bool netatalk_already_open_for_writing = false;
2675 bool netatalk_already_open_with_deny_read = false;
2676 bool netatalk_already_open_with_deny_write = false;
2678 /* FIXME: hardcoded data fork, add resource fork */
2679 enum apple_fork fork_type = APPLE_FORK_DATA;
2681 DBG_DEBUG("fruit_check_access: %s, am: %s/%s, sm: 0x%x\n",
2682 fsp_str_dbg(fsp),
2683 access_mask & FILE_READ_DATA ? "READ" :"-",
2684 access_mask & FILE_WRITE_DATA ? "WRITE" : "-",
2685 share_mode);
2687 if (fsp->fh->fd == -1) {
2688 return NT_STATUS_OK;
2691 /* Read NetATalk opens and deny modes on the file. */
2692 netatalk_already_open_for_reading = test_netatalk_lock(fsp,
2693 access_to_netatalk_brl(fork_type,
2694 FILE_READ_DATA));
2696 netatalk_already_open_with_deny_read = test_netatalk_lock(fsp,
2697 denymode_to_netatalk_brl(fork_type,
2698 DENY_READ));
2700 netatalk_already_open_for_writing = test_netatalk_lock(fsp,
2701 access_to_netatalk_brl(fork_type,
2702 FILE_WRITE_DATA));
2704 netatalk_already_open_with_deny_write = test_netatalk_lock(fsp,
2705 denymode_to_netatalk_brl(fork_type,
2706 DENY_WRITE));
2708 /* If there are any conflicts - sharing violation. */
2709 if ((access_mask & FILE_READ_DATA) &&
2710 netatalk_already_open_with_deny_read) {
2711 return NT_STATUS_SHARING_VIOLATION;
2714 if (!share_for_read &&
2715 netatalk_already_open_for_reading) {
2716 return NT_STATUS_SHARING_VIOLATION;
2719 if ((access_mask & FILE_WRITE_DATA) &&
2720 netatalk_already_open_with_deny_write) {
2721 return NT_STATUS_SHARING_VIOLATION;
2724 if (!share_for_write &&
2725 netatalk_already_open_for_writing) {
2726 return NT_STATUS_SHARING_VIOLATION;
2729 if (!(access_mask & FILE_READ_DATA)) {
2731 * Nothing we can do here, we need read access
2732 * to set locks.
2734 return NT_STATUS_OK;
2737 /* Set NetAtalk locks matching our access */
2738 if (access_mask & FILE_READ_DATA) {
2739 struct byte_range_lock *br_lck = NULL;
2741 off = access_to_netatalk_brl(fork_type, FILE_READ_DATA);
2742 br_lck = do_lock(
2743 handle->conn->sconn->msg_ctx, fsp,
2744 fsp->op->global->open_persistent_id, 1, off,
2745 READ_LOCK, POSIX_LOCK, false,
2746 &status, NULL);
2748 TALLOC_FREE(br_lck);
2750 if (!NT_STATUS_IS_OK(status)) {
2751 return status;
2755 if (!share_for_read) {
2756 struct byte_range_lock *br_lck = NULL;
2758 off = denymode_to_netatalk_brl(fork_type, DENY_READ);
2759 br_lck = do_lock(
2760 handle->conn->sconn->msg_ctx, fsp,
2761 fsp->op->global->open_persistent_id, 1, off,
2762 READ_LOCK, POSIX_LOCK, false,
2763 &status, NULL);
2765 TALLOC_FREE(br_lck);
2767 if (!NT_STATUS_IS_OK(status)) {
2768 return status;
2772 if (access_mask & FILE_WRITE_DATA) {
2773 struct byte_range_lock *br_lck = NULL;
2775 off = access_to_netatalk_brl(fork_type, FILE_WRITE_DATA);
2776 br_lck = do_lock(
2777 handle->conn->sconn->msg_ctx, fsp,
2778 fsp->op->global->open_persistent_id, 1, off,
2779 READ_LOCK, POSIX_LOCK, false,
2780 &status, NULL);
2782 TALLOC_FREE(br_lck);
2784 if (!NT_STATUS_IS_OK(status)) {
2785 return status;
2789 if (!share_for_write) {
2790 struct byte_range_lock *br_lck = NULL;
2792 off = denymode_to_netatalk_brl(fork_type, DENY_WRITE);
2793 br_lck = do_lock(
2794 handle->conn->sconn->msg_ctx, fsp,
2795 fsp->op->global->open_persistent_id, 1, off,
2796 READ_LOCK, POSIX_LOCK, false,
2797 &status, NULL);
2799 TALLOC_FREE(br_lck);
2801 if (!NT_STATUS_IS_OK(status)) {
2802 return status;
2806 return NT_STATUS_OK;
2809 static NTSTATUS check_aapl(vfs_handle_struct *handle,
2810 struct smb_request *req,
2811 const struct smb2_create_blobs *in_context_blobs,
2812 struct smb2_create_blobs *out_context_blobs)
2814 struct fruit_config_data *config;
2815 NTSTATUS status;
2816 struct smb2_create_blob *aapl = NULL;
2817 uint32_t cmd;
2818 bool ok;
2819 uint8_t p[16];
2820 DATA_BLOB blob = data_blob_talloc(req, NULL, 0);
2821 uint64_t req_bitmap, client_caps;
2822 uint64_t server_caps = SMB2_CRTCTX_AAPL_UNIX_BASED;
2823 smb_ucs2_t *model;
2824 size_t modellen;
2826 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
2827 return NT_STATUS_UNSUCCESSFUL);
2829 if (!config->use_aapl
2830 || in_context_blobs == NULL
2831 || out_context_blobs == NULL) {
2832 return NT_STATUS_OK;
2835 aapl = smb2_create_blob_find(in_context_blobs,
2836 SMB2_CREATE_TAG_AAPL);
2837 if (aapl == NULL) {
2838 return NT_STATUS_OK;
2841 if (aapl->data.length != 24) {
2842 DEBUG(1, ("unexpected AAPL ctxt length: %ju\n",
2843 (uintmax_t)aapl->data.length));
2844 return NT_STATUS_INVALID_PARAMETER;
2847 cmd = IVAL(aapl->data.data, 0);
2848 if (cmd != SMB2_CRTCTX_AAPL_SERVER_QUERY) {
2849 DEBUG(1, ("unsupported AAPL cmd: %d\n", cmd));
2850 return NT_STATUS_INVALID_PARAMETER;
2853 req_bitmap = BVAL(aapl->data.data, 8);
2854 client_caps = BVAL(aapl->data.data, 16);
2856 SIVAL(p, 0, SMB2_CRTCTX_AAPL_SERVER_QUERY);
2857 SIVAL(p, 4, 0);
2858 SBVAL(p, 8, req_bitmap);
2859 ok = data_blob_append(req, &blob, p, 16);
2860 if (!ok) {
2861 return NT_STATUS_UNSUCCESSFUL;
2864 if (req_bitmap & SMB2_CRTCTX_AAPL_SERVER_CAPS) {
2865 if ((client_caps & SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR) &&
2866 (handle->conn->tcon->compat->fs_capabilities & FILE_NAMED_STREAMS)) {
2867 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR;
2868 config->readdir_attr_enabled = true;
2871 if (config->use_copyfile) {
2872 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_OSX_COPYFILE;
2873 config->copyfile_enabled = true;
2877 * The client doesn't set the flag, so we can't check
2878 * for it and just set it unconditionally
2880 if (config->unix_info_enabled) {
2881 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_NFS_ACE;
2884 SBVAL(p, 0, server_caps);
2885 ok = data_blob_append(req, &blob, p, 8);
2886 if (!ok) {
2887 return NT_STATUS_UNSUCCESSFUL;
2891 if (req_bitmap & SMB2_CRTCTX_AAPL_VOLUME_CAPS) {
2892 int val = lp_case_sensitive(SNUM(handle->conn->tcon->compat));
2893 uint64_t caps = 0;
2895 switch (val) {
2896 case Auto:
2897 break;
2899 case True:
2900 caps |= SMB2_CRTCTX_AAPL_CASE_SENSITIVE;
2901 break;
2903 default:
2904 break;
2907 if (config->time_machine) {
2908 caps |= SMB2_CRTCTX_AAPL_FULL_SYNC;
2911 SBVAL(p, 0, caps);
2913 ok = data_blob_append(req, &blob, p, 8);
2914 if (!ok) {
2915 return NT_STATUS_UNSUCCESSFUL;
2919 if (req_bitmap & SMB2_CRTCTX_AAPL_MODEL_INFO) {
2920 ok = convert_string_talloc(req,
2921 CH_UNIX, CH_UTF16LE,
2922 config->model, strlen(config->model),
2923 &model, &modellen);
2924 if (!ok) {
2925 return NT_STATUS_UNSUCCESSFUL;
2928 SIVAL(p, 0, 0);
2929 SIVAL(p + 4, 0, modellen);
2930 ok = data_blob_append(req, &blob, p, 8);
2931 if (!ok) {
2932 talloc_free(model);
2933 return NT_STATUS_UNSUCCESSFUL;
2936 ok = data_blob_append(req, &blob, model, modellen);
2937 talloc_free(model);
2938 if (!ok) {
2939 return NT_STATUS_UNSUCCESSFUL;
2943 status = smb2_create_blob_add(out_context_blobs,
2944 out_context_blobs,
2945 SMB2_CREATE_TAG_AAPL,
2946 blob);
2947 if (NT_STATUS_IS_OK(status)) {
2948 global_fruit_config.nego_aapl = true;
2949 if (config->aapl_zero_file_id) {
2950 aapl_force_zero_file_id(handle->conn->sconn);
2954 return status;
2957 static bool readdir_attr_meta_finderi_stream(
2958 struct vfs_handle_struct *handle,
2959 const struct smb_filename *smb_fname,
2960 AfpInfo *ai)
2962 struct smb_filename *stream_name = NULL;
2963 files_struct *fsp = NULL;
2964 ssize_t nread;
2965 NTSTATUS status;
2966 int ret;
2967 bool ok;
2968 uint8_t buf[AFP_INFO_SIZE];
2970 stream_name = synthetic_smb_fname(talloc_tos(),
2971 smb_fname->base_name,
2972 AFPINFO_STREAM_NAME,
2973 NULL, smb_fname->flags);
2974 if (stream_name == NULL) {
2975 return false;
2978 ret = SMB_VFS_STAT(handle->conn, stream_name);
2979 if (ret != 0) {
2980 return false;
2983 status = SMB_VFS_CREATE_FILE(
2984 handle->conn, /* conn */
2985 NULL, /* req */
2986 0, /* root_dir_fid */
2987 stream_name, /* fname */
2988 FILE_READ_DATA, /* access_mask */
2989 (FILE_SHARE_READ | FILE_SHARE_WRITE | /* share_access */
2990 FILE_SHARE_DELETE),
2991 FILE_OPEN, /* create_disposition*/
2992 0, /* create_options */
2993 0, /* file_attributes */
2994 INTERNAL_OPEN_ONLY, /* oplock_request */
2995 NULL, /* lease */
2996 0, /* allocation_size */
2997 0, /* private_flags */
2998 NULL, /* sd */
2999 NULL, /* ea_list */
3000 &fsp, /* result */
3001 NULL, /* pinfo */
3002 NULL, NULL); /* create context */
3004 TALLOC_FREE(stream_name);
3006 if (!NT_STATUS_IS_OK(status)) {
3007 return false;
3010 nread = SMB_VFS_PREAD(fsp, &buf[0], AFP_INFO_SIZE, 0);
3011 if (nread != AFP_INFO_SIZE) {
3012 DBG_ERR("short read [%s] [%zd/%d]\n",
3013 smb_fname_str_dbg(stream_name), nread, AFP_INFO_SIZE);
3014 ok = false;
3015 goto fail;
3018 memcpy(&ai->afpi_FinderInfo[0], &buf[AFP_OFF_FinderInfo],
3019 AFP_FinderSize);
3021 ok = true;
3023 fail:
3024 if (fsp != NULL) {
3025 close_file(NULL, fsp, NORMAL_CLOSE);
3028 return ok;
3031 static bool readdir_attr_meta_finderi_netatalk(
3032 struct vfs_handle_struct *handle,
3033 const struct smb_filename *smb_fname,
3034 AfpInfo *ai)
3036 struct adouble *ad = NULL;
3037 char *p = NULL;
3039 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
3040 if (ad == NULL) {
3041 return false;
3044 p = ad_get_entry(ad, ADEID_FINDERI);
3045 if (p == NULL) {
3046 DBG_ERR("No ADEID_FINDERI for [%s]\n", smb_fname->base_name);
3047 TALLOC_FREE(ad);
3048 return false;
3051 memcpy(&ai->afpi_FinderInfo[0], p, AFP_FinderSize);
3052 TALLOC_FREE(ad);
3053 return true;
3056 static bool readdir_attr_meta_finderi(struct vfs_handle_struct *handle,
3057 const struct smb_filename *smb_fname,
3058 struct readdir_attr_data *attr_data)
3060 struct fruit_config_data *config = NULL;
3061 uint32_t date_added;
3062 AfpInfo ai = {0};
3063 bool ok;
3065 SMB_VFS_HANDLE_GET_DATA(handle, config,
3066 struct fruit_config_data,
3067 return false);
3069 switch (config->meta) {
3070 case FRUIT_META_NETATALK:
3071 ok = readdir_attr_meta_finderi_netatalk(
3072 handle, smb_fname, &ai);
3073 break;
3075 case FRUIT_META_STREAM:
3076 ok = readdir_attr_meta_finderi_stream(
3077 handle, smb_fname, &ai);
3078 break;
3080 default:
3081 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3082 return false;
3085 if (!ok) {
3086 /* Don't bother with errors, it's likely ENOENT */
3087 return true;
3090 if (S_ISREG(smb_fname->st.st_ex_mode)) {
3091 /* finder_type */
3092 memcpy(&attr_data->attr_data.aapl.finder_info[0],
3093 &ai.afpi_FinderInfo[0], 4);
3095 /* finder_creator */
3096 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 4,
3097 &ai.afpi_FinderInfo[4], 4);
3100 /* finder_flags */
3101 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 8,
3102 &ai.afpi_FinderInfo[8], 2);
3104 /* finder_ext_flags */
3105 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 10,
3106 &ai.afpi_FinderInfo[24], 2);
3108 /* creation date */
3109 date_added = convert_time_t_to_uint32_t(
3110 smb_fname->st.st_ex_btime.tv_sec - AD_DATE_DELTA);
3112 RSIVAL(&attr_data->attr_data.aapl.finder_info[0], 12, date_added);
3114 return true;
3117 static uint64_t readdir_attr_rfork_size_adouble(
3118 struct vfs_handle_struct *handle,
3119 const struct smb_filename *smb_fname)
3121 struct adouble *ad = NULL;
3122 uint64_t rfork_size;
3124 ad = ad_get(talloc_tos(), handle, smb_fname,
3125 ADOUBLE_RSRC);
3126 if (ad == NULL) {
3127 return 0;
3130 rfork_size = ad_getentrylen(ad, ADEID_RFORK);
3131 TALLOC_FREE(ad);
3133 return rfork_size;
3136 static uint64_t readdir_attr_rfork_size_stream(
3137 struct vfs_handle_struct *handle,
3138 const struct smb_filename *smb_fname)
3140 struct smb_filename *stream_name = NULL;
3141 int ret;
3142 uint64_t rfork_size;
3144 stream_name = synthetic_smb_fname(talloc_tos(),
3145 smb_fname->base_name,
3146 AFPRESOURCE_STREAM_NAME,
3147 NULL, 0);
3148 if (stream_name == NULL) {
3149 return 0;
3152 ret = SMB_VFS_STAT(handle->conn, stream_name);
3153 if (ret != 0) {
3154 TALLOC_FREE(stream_name);
3155 return 0;
3158 rfork_size = stream_name->st.st_ex_size;
3159 TALLOC_FREE(stream_name);
3161 return rfork_size;
3164 static uint64_t readdir_attr_rfork_size(struct vfs_handle_struct *handle,
3165 const struct smb_filename *smb_fname)
3167 struct fruit_config_data *config = NULL;
3168 uint64_t rfork_size;
3170 SMB_VFS_HANDLE_GET_DATA(handle, config,
3171 struct fruit_config_data,
3172 return 0);
3174 switch (config->rsrc) {
3175 case FRUIT_RSRC_ADFILE:
3176 case FRUIT_RSRC_XATTR:
3177 rfork_size = readdir_attr_rfork_size_adouble(handle,
3178 smb_fname);
3179 break;
3181 case FRUIT_META_STREAM:
3182 rfork_size = readdir_attr_rfork_size_stream(handle,
3183 smb_fname);
3184 break;
3186 default:
3187 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3188 rfork_size = 0;
3189 break;
3192 return rfork_size;
3195 static NTSTATUS readdir_attr_macmeta(struct vfs_handle_struct *handle,
3196 const struct smb_filename *smb_fname,
3197 struct readdir_attr_data *attr_data)
3199 NTSTATUS status = NT_STATUS_OK;
3200 struct fruit_config_data *config = NULL;
3201 bool ok;
3203 SMB_VFS_HANDLE_GET_DATA(handle, config,
3204 struct fruit_config_data,
3205 return NT_STATUS_UNSUCCESSFUL);
3208 /* Ensure we return a default value in the creation_date field */
3209 RSIVAL(&attr_data->attr_data.aapl.finder_info, 12, AD_DATE_START);
3212 * Resource fork length
3215 if (config->readdir_attr_rsize) {
3216 uint64_t rfork_size;
3218 rfork_size = readdir_attr_rfork_size(handle, smb_fname);
3219 attr_data->attr_data.aapl.rfork_size = rfork_size;
3223 * FinderInfo
3226 if (config->readdir_attr_finder_info) {
3227 ok = readdir_attr_meta_finderi(handle, smb_fname, attr_data);
3228 if (!ok) {
3229 status = NT_STATUS_INTERNAL_ERROR;
3233 return status;
3236 static NTSTATUS remove_virtual_nfs_aces(struct security_descriptor *psd)
3238 NTSTATUS status;
3239 uint32_t i;
3241 if (psd->dacl == NULL) {
3242 return NT_STATUS_OK;
3245 for (i = 0; i < psd->dacl->num_aces; i++) {
3246 /* MS NFS style mode/uid/gid */
3247 int cmp = dom_sid_compare_domain(
3248 &global_sid_Unix_NFS,
3249 &psd->dacl->aces[i].trustee);
3250 if (cmp != 0) {
3251 /* Normal ACE entry. */
3252 continue;
3256 * security_descriptor_dacl_del()
3257 * *must* return NT_STATUS_OK as we know
3258 * we have something to remove.
3261 status = security_descriptor_dacl_del(psd,
3262 &psd->dacl->aces[i].trustee);
3263 if (!NT_STATUS_IS_OK(status)) {
3264 DBG_WARNING("failed to remove MS NFS style ACE: %s\n",
3265 nt_errstr(status));
3266 return status;
3270 * security_descriptor_dacl_del() may delete more
3271 * then one entry subsequent to this one if the
3272 * SID matches, but we only need to ensure that
3273 * we stay looking at the same element in the array.
3275 i--;
3277 return NT_STATUS_OK;
3280 /* Search MS NFS style ACE with UNIX mode */
3281 static NTSTATUS check_ms_nfs(vfs_handle_struct *handle,
3282 files_struct *fsp,
3283 struct security_descriptor *psd,
3284 mode_t *pmode,
3285 bool *pdo_chmod)
3287 uint32_t i;
3288 struct fruit_config_data *config = NULL;
3290 *pdo_chmod = false;
3292 SMB_VFS_HANDLE_GET_DATA(handle, config,
3293 struct fruit_config_data,
3294 return NT_STATUS_UNSUCCESSFUL);
3296 if (!global_fruit_config.nego_aapl) {
3297 return NT_STATUS_OK;
3299 if (psd->dacl == NULL || !config->unix_info_enabled) {
3300 return NT_STATUS_OK;
3303 for (i = 0; i < psd->dacl->num_aces; i++) {
3304 if (dom_sid_compare_domain(
3305 &global_sid_Unix_NFS_Mode,
3306 &psd->dacl->aces[i].trustee) == 0) {
3307 *pmode = (mode_t)psd->dacl->aces[i].trustee.sub_auths[2];
3308 *pmode &= (S_IRWXU | S_IRWXG | S_IRWXO);
3309 *pdo_chmod = true;
3311 DEBUG(10, ("MS NFS chmod request %s, %04o\n",
3312 fsp_str_dbg(fsp), (unsigned)(*pmode)));
3313 break;
3318 * Remove any incoming virtual ACE entries generated by
3319 * fruit_fget_nt_acl().
3322 return remove_virtual_nfs_aces(psd);
3325 /****************************************************************************
3326 * VFS ops
3327 ****************************************************************************/
3329 static int fruit_connect(vfs_handle_struct *handle,
3330 const char *service,
3331 const char *user)
3333 int rc;
3334 char *list = NULL, *newlist = NULL;
3335 struct fruit_config_data *config;
3337 DEBUG(10, ("fruit_connect\n"));
3339 rc = SMB_VFS_NEXT_CONNECT(handle, service, user);
3340 if (rc < 0) {
3341 return rc;
3344 rc = init_fruit_config(handle);
3345 if (rc != 0) {
3346 return rc;
3349 SMB_VFS_HANDLE_GET_DATA(handle, config,
3350 struct fruit_config_data, return -1);
3352 if (config->veto_appledouble) {
3353 list = lp_veto_files(talloc_tos(), SNUM(handle->conn));
3355 if (list) {
3356 if (strstr(list, "/" ADOUBLE_NAME_PREFIX "*/") == NULL) {
3357 newlist = talloc_asprintf(
3358 list,
3359 "%s/" ADOUBLE_NAME_PREFIX "*/",
3360 list);
3361 lp_do_parameter(SNUM(handle->conn),
3362 "veto files",
3363 newlist);
3365 } else {
3366 lp_do_parameter(SNUM(handle->conn),
3367 "veto files",
3368 "/" ADOUBLE_NAME_PREFIX "*/");
3371 TALLOC_FREE(list);
3374 if (config->encoding == FRUIT_ENC_NATIVE) {
3375 lp_do_parameter(SNUM(handle->conn),
3376 "catia:mappings",
3377 fruit_catia_maps);
3380 if (config->time_machine) {
3381 DBG_NOTICE("Enabling durable handles for Time Machine "
3382 "support on [%s]\n", service);
3383 lp_do_parameter(SNUM(handle->conn), "durable handles", "yes");
3384 lp_do_parameter(SNUM(handle->conn), "kernel oplocks", "no");
3385 lp_do_parameter(SNUM(handle->conn), "kernel share modes", "no");
3386 if (!lp_strict_sync(SNUM(handle->conn))) {
3387 DBG_WARNING("Time Machine without strict sync is not "
3388 "recommended!\n");
3390 lp_do_parameter(SNUM(handle->conn), "posix locking", "no");
3393 return rc;
3396 static int fruit_fake_fd(void)
3398 int pipe_fds[2];
3399 int fd;
3400 int ret;
3403 * Return a valid fd, but ensure any attempt to use it returns
3404 * an error (EPIPE). Once we get a write on the handle, we open
3405 * the real fd.
3407 ret = pipe(pipe_fds);
3408 if (ret != 0) {
3409 return -1;
3411 fd = pipe_fds[0];
3412 close(pipe_fds[1]);
3414 return fd;
3417 static int fruit_open_meta_stream(vfs_handle_struct *handle,
3418 struct smb_filename *smb_fname,
3419 files_struct *fsp,
3420 int flags,
3421 mode_t mode)
3423 struct fruit_config_data *config = NULL;
3424 struct fio *fio = NULL;
3425 int open_flags = flags & ~O_CREAT;
3426 int fd;
3428 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3430 SMB_VFS_HANDLE_GET_DATA(handle, config,
3431 struct fruit_config_data, return -1);
3433 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3434 fio->type = ADOUBLE_META;
3435 fio->config = config;
3437 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, open_flags, mode);
3438 if (fd != -1) {
3439 return fd;
3442 if (!(flags & O_CREAT)) {
3443 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
3444 return -1;
3447 fd = fruit_fake_fd();
3448 if (fd == -1) {
3449 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
3450 return -1;
3453 fio->fake_fd = true;
3454 fio->flags = flags;
3455 fio->mode = mode;
3457 return fd;
3460 static int fruit_open_meta_netatalk(vfs_handle_struct *handle,
3461 struct smb_filename *smb_fname,
3462 files_struct *fsp,
3463 int flags,
3464 mode_t mode)
3466 struct fruit_config_data *config = NULL;
3467 struct fio *fio = NULL;
3468 struct adouble *ad = NULL;
3469 bool meta_exists = false;
3470 int fd;
3472 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3474 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
3475 if (ad != NULL) {
3476 meta_exists = true;
3479 TALLOC_FREE(ad);
3481 if (!meta_exists && !(flags & O_CREAT)) {
3482 errno = ENOENT;
3483 return -1;
3486 fd = fruit_fake_fd();
3487 if (fd == -1) {
3488 return -1;
3491 SMB_VFS_HANDLE_GET_DATA(handle, config,
3492 struct fruit_config_data, return -1);
3494 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3495 fio->type = ADOUBLE_META;
3496 fio->config = config;
3497 fio->fake_fd = true;
3498 fio->flags = flags;
3499 fio->mode = mode;
3501 return fd;
3504 static int fruit_open_meta(vfs_handle_struct *handle,
3505 struct smb_filename *smb_fname,
3506 files_struct *fsp, int flags, mode_t mode)
3508 int fd;
3509 struct fruit_config_data *config = NULL;
3511 DBG_DEBUG("path [%s]\n", smb_fname_str_dbg(smb_fname));
3513 SMB_VFS_HANDLE_GET_DATA(handle, config,
3514 struct fruit_config_data, return -1);
3516 switch (config->meta) {
3517 case FRUIT_META_STREAM:
3518 fd = fruit_open_meta_stream(handle, smb_fname,
3519 fsp, flags, mode);
3520 break;
3522 case FRUIT_META_NETATALK:
3523 fd = fruit_open_meta_netatalk(handle, smb_fname,
3524 fsp, flags, mode);
3525 break;
3527 default:
3528 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3529 return -1;
3532 DBG_DEBUG("path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3534 return fd;
3537 static int fruit_open_rsrc_adouble(vfs_handle_struct *handle,
3538 struct smb_filename *smb_fname,
3539 files_struct *fsp,
3540 int flags,
3541 mode_t mode)
3543 int rc = 0;
3544 struct adouble *ad = NULL;
3545 struct smb_filename *smb_fname_base = NULL;
3546 struct fruit_config_data *config = NULL;
3547 int hostfd = -1;
3549 SMB_VFS_HANDLE_GET_DATA(handle, config,
3550 struct fruit_config_data, return -1);
3552 if ((!(flags & O_CREAT)) &&
3553 S_ISDIR(fsp->base_fsp->fsp_name->st.st_ex_mode))
3555 /* sorry, but directories don't habe a resource fork */
3556 rc = -1;
3557 goto exit;
3560 rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_base);
3561 if (rc != 0) {
3562 goto exit;
3565 /* We always need read/write access for the metadata header too */
3566 flags &= ~(O_RDONLY | O_WRONLY);
3567 flags |= O_RDWR;
3569 hostfd = SMB_VFS_NEXT_OPEN(handle, smb_fname_base, fsp,
3570 flags, mode);
3571 if (hostfd == -1) {
3572 rc = -1;
3573 goto exit;
3576 if (flags & (O_CREAT | O_TRUNC)) {
3577 ad = ad_init(fsp, handle, ADOUBLE_RSRC);
3578 if (ad == NULL) {
3579 rc = -1;
3580 goto exit;
3583 fsp->fh->fd = hostfd;
3585 rc = ad_fset(ad, fsp);
3586 fsp->fh->fd = -1;
3587 if (rc != 0) {
3588 rc = -1;
3589 goto exit;
3591 TALLOC_FREE(ad);
3594 exit:
3596 TALLOC_FREE(smb_fname_base);
3598 DEBUG(10, ("fruit_open resource fork: rc=%d, fd=%d\n", rc, hostfd));
3599 if (rc != 0) {
3600 int saved_errno = errno;
3601 if (hostfd >= 0) {
3603 * BUGBUGBUG -- we would need to call
3604 * fd_close_posix here, but we don't have a
3605 * full fsp yet
3607 fsp->fh->fd = hostfd;
3608 SMB_VFS_CLOSE(fsp);
3610 hostfd = -1;
3611 errno = saved_errno;
3613 return hostfd;
3616 static int fruit_open_rsrc_xattr(vfs_handle_struct *handle,
3617 struct smb_filename *smb_fname,
3618 files_struct *fsp,
3619 int flags,
3620 mode_t mode)
3622 #ifdef HAVE_ATTROPEN
3623 int fd = -1;
3625 fd = attropen(smb_fname->base_name,
3626 AFPRESOURCE_EA_NETATALK,
3627 flags,
3628 mode);
3629 if (fd == -1) {
3630 return -1;
3633 return fd;
3635 #else
3636 errno = ENOSYS;
3637 return -1;
3638 #endif
3641 static int fruit_open_rsrc(vfs_handle_struct *handle,
3642 struct smb_filename *smb_fname,
3643 files_struct *fsp, int flags, mode_t mode)
3645 int fd;
3646 struct fruit_config_data *config = NULL;
3647 struct fio *fio = NULL;
3649 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3651 SMB_VFS_HANDLE_GET_DATA(handle, config,
3652 struct fruit_config_data, return -1);
3654 switch (config->rsrc) {
3655 case FRUIT_RSRC_STREAM:
3656 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3657 break;
3659 case FRUIT_RSRC_ADFILE:
3660 fd = fruit_open_rsrc_adouble(handle, smb_fname,
3661 fsp, flags, mode);
3662 break;
3664 case FRUIT_RSRC_XATTR:
3665 fd = fruit_open_rsrc_xattr(handle, smb_fname,
3666 fsp, flags, mode);
3667 break;
3669 default:
3670 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3671 return -1;
3674 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3676 if (fd == -1) {
3677 return -1;
3680 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3681 fio->type = ADOUBLE_RSRC;
3682 fio->config = config;
3684 return fd;
3687 static int fruit_open(vfs_handle_struct *handle,
3688 struct smb_filename *smb_fname,
3689 files_struct *fsp, int flags, mode_t mode)
3691 int fd;
3693 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3695 if (!is_ntfs_stream_smb_fname(smb_fname)) {
3696 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3699 if (is_afpinfo_stream(smb_fname)) {
3700 fd = fruit_open_meta(handle, smb_fname, fsp, flags, mode);
3701 } else if (is_afpresource_stream(smb_fname)) {
3702 fd = fruit_open_rsrc(handle, smb_fname, fsp, flags, mode);
3703 } else {
3704 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3707 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3709 return fd;
3712 static int fruit_close_meta(vfs_handle_struct *handle,
3713 files_struct *fsp)
3715 int ret;
3716 struct fruit_config_data *config = NULL;
3718 SMB_VFS_HANDLE_GET_DATA(handle, config,
3719 struct fruit_config_data, return -1);
3721 switch (config->meta) {
3722 case FRUIT_META_STREAM:
3723 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3724 break;
3726 case FRUIT_META_NETATALK:
3727 ret = close(fsp->fh->fd);
3728 fsp->fh->fd = -1;
3729 break;
3731 default:
3732 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3733 return -1;
3736 return ret;
3740 static int fruit_close_rsrc(vfs_handle_struct *handle,
3741 files_struct *fsp)
3743 int ret;
3744 struct fruit_config_data *config = NULL;
3746 SMB_VFS_HANDLE_GET_DATA(handle, config,
3747 struct fruit_config_data, return -1);
3749 switch (config->rsrc) {
3750 case FRUIT_RSRC_STREAM:
3751 case FRUIT_RSRC_ADFILE:
3752 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3753 break;
3755 case FRUIT_RSRC_XATTR:
3756 ret = close(fsp->fh->fd);
3757 fsp->fh->fd = -1;
3758 break;
3760 default:
3761 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3762 return -1;
3765 return ret;
3768 static int fruit_close(vfs_handle_struct *handle,
3769 files_struct *fsp)
3771 int ret;
3772 int fd;
3774 fd = fsp->fh->fd;
3776 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(fsp->fsp_name), fd);
3778 if (!is_ntfs_stream_smb_fname(fsp->fsp_name)) {
3779 return SMB_VFS_NEXT_CLOSE(handle, fsp);
3782 if (is_afpinfo_stream(fsp->fsp_name)) {
3783 ret = fruit_close_meta(handle, fsp);
3784 } else if (is_afpresource_stream(fsp->fsp_name)) {
3785 ret = fruit_close_rsrc(handle, fsp);
3786 } else {
3787 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3790 return ret;
3793 static int fruit_rename(struct vfs_handle_struct *handle,
3794 const struct smb_filename *smb_fname_src,
3795 const struct smb_filename *smb_fname_dst)
3797 int rc = -1;
3798 struct fruit_config_data *config = NULL;
3799 struct smb_filename *src_adp_smb_fname = NULL;
3800 struct smb_filename *dst_adp_smb_fname = NULL;
3802 SMB_VFS_HANDLE_GET_DATA(handle, config,
3803 struct fruit_config_data, return -1);
3805 if (!VALID_STAT(smb_fname_src->st)) {
3806 DBG_ERR("Need valid stat for [%s]\n",
3807 smb_fname_str_dbg(smb_fname_src));
3808 return -1;
3811 rc = SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
3812 if (rc != 0) {
3813 return -1;
3816 if ((config->rsrc != FRUIT_RSRC_ADFILE) ||
3817 (!S_ISREG(smb_fname_src->st.st_ex_mode)))
3819 return 0;
3822 rc = adouble_path(talloc_tos(), smb_fname_src, &src_adp_smb_fname);
3823 if (rc != 0) {
3824 goto done;
3827 rc = adouble_path(talloc_tos(), smb_fname_dst, &dst_adp_smb_fname);
3828 if (rc != 0) {
3829 goto done;
3832 DBG_DEBUG("%s -> %s\n",
3833 smb_fname_str_dbg(src_adp_smb_fname),
3834 smb_fname_str_dbg(dst_adp_smb_fname));
3836 rc = SMB_VFS_NEXT_RENAME(handle, src_adp_smb_fname, dst_adp_smb_fname);
3837 if (errno == ENOENT) {
3838 rc = 0;
3841 done:
3842 TALLOC_FREE(src_adp_smb_fname);
3843 TALLOC_FREE(dst_adp_smb_fname);
3844 return rc;
3847 static int fruit_unlink_meta_stream(vfs_handle_struct *handle,
3848 const struct smb_filename *smb_fname)
3850 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3853 static int fruit_unlink_meta_netatalk(vfs_handle_struct *handle,
3854 const struct smb_filename *smb_fname)
3856 return SMB_VFS_REMOVEXATTR(handle->conn,
3857 smb_fname,
3858 AFPINFO_EA_NETATALK);
3861 static int fruit_unlink_meta(vfs_handle_struct *handle,
3862 const struct smb_filename *smb_fname)
3864 struct fruit_config_data *config = NULL;
3865 int rc;
3867 SMB_VFS_HANDLE_GET_DATA(handle, config,
3868 struct fruit_config_data, return -1);
3870 switch (config->meta) {
3871 case FRUIT_META_STREAM:
3872 rc = fruit_unlink_meta_stream(handle, smb_fname);
3873 break;
3875 case FRUIT_META_NETATALK:
3876 rc = fruit_unlink_meta_netatalk(handle, smb_fname);
3877 break;
3879 default:
3880 DBG_ERR("Unsupported meta config [%d]\n", config->meta);
3881 return -1;
3884 return rc;
3887 static int fruit_unlink_rsrc_stream(vfs_handle_struct *handle,
3888 const struct smb_filename *smb_fname,
3889 bool force_unlink)
3891 int ret;
3893 if (!force_unlink) {
3894 struct smb_filename *smb_fname_cp = NULL;
3895 off_t size;
3897 smb_fname_cp = cp_smb_filename(talloc_tos(), smb_fname);
3898 if (smb_fname_cp == NULL) {
3899 return -1;
3903 * 0 byte resource fork streams are not listed by
3904 * vfs_streaminfo, as a result stream cleanup/deletion of file
3905 * deletion doesn't remove the resourcefork stream.
3908 ret = SMB_VFS_NEXT_STAT(handle, smb_fname_cp);
3909 if (ret != 0) {
3910 TALLOC_FREE(smb_fname_cp);
3911 DBG_ERR("stat [%s] failed [%s]\n",
3912 smb_fname_str_dbg(smb_fname_cp), strerror(errno));
3913 return -1;
3916 size = smb_fname_cp->st.st_ex_size;
3917 TALLOC_FREE(smb_fname_cp);
3919 if (size > 0) {
3920 /* OS X ignores resource fork stream delete requests */
3921 return 0;
3925 ret = SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3926 if ((ret != 0) && (errno == ENOENT) && force_unlink) {
3927 ret = 0;
3930 return ret;
3933 static int fruit_unlink_rsrc_adouble(vfs_handle_struct *handle,
3934 const struct smb_filename *smb_fname,
3935 bool force_unlink)
3937 int rc;
3938 struct adouble *ad = NULL;
3939 struct smb_filename *adp_smb_fname = NULL;
3941 if (!force_unlink) {
3942 ad = ad_get(talloc_tos(), handle, smb_fname,
3943 ADOUBLE_RSRC);
3944 if (ad == NULL) {
3945 errno = ENOENT;
3946 return -1;
3951 * 0 byte resource fork streams are not listed by
3952 * vfs_streaminfo, as a result stream cleanup/deletion of file
3953 * deletion doesn't remove the resourcefork stream.
3956 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
3957 /* OS X ignores resource fork stream delete requests */
3958 TALLOC_FREE(ad);
3959 return 0;
3962 TALLOC_FREE(ad);
3965 rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
3966 if (rc != 0) {
3967 return -1;
3970 rc = SMB_VFS_NEXT_UNLINK(handle, adp_smb_fname);
3971 TALLOC_FREE(adp_smb_fname);
3972 if ((rc != 0) && (errno == ENOENT) && force_unlink) {
3973 rc = 0;
3976 return rc;
3979 static int fruit_unlink_rsrc_xattr(vfs_handle_struct *handle,
3980 const struct smb_filename *smb_fname,
3981 bool force_unlink)
3984 * OS X ignores resource fork stream delete requests, so nothing to do
3985 * here. Removing the file will remove the xattr anyway, so we don't
3986 * have to take care of removing 0 byte resource forks that could be
3987 * left behind.
3989 return 0;
3992 static int fruit_unlink_rsrc(vfs_handle_struct *handle,
3993 const struct smb_filename *smb_fname,
3994 bool force_unlink)
3996 struct fruit_config_data *config = NULL;
3997 int rc;
3999 SMB_VFS_HANDLE_GET_DATA(handle, config,
4000 struct fruit_config_data, return -1);
4002 switch (config->rsrc) {
4003 case FRUIT_RSRC_STREAM:
4004 rc = fruit_unlink_rsrc_stream(handle, smb_fname, force_unlink);
4005 break;
4007 case FRUIT_RSRC_ADFILE:
4008 rc = fruit_unlink_rsrc_adouble(handle, smb_fname, force_unlink);
4009 break;
4011 case FRUIT_RSRC_XATTR:
4012 rc = fruit_unlink_rsrc_xattr(handle, smb_fname, force_unlink);
4013 break;
4015 default:
4016 DBG_ERR("Unsupported rsrc config [%d]\n", config->rsrc);
4017 return -1;
4020 return rc;
4023 static int fruit_unlink(vfs_handle_struct *handle,
4024 const struct smb_filename *smb_fname)
4026 int rc;
4027 struct fruit_config_data *config = NULL;
4028 struct smb_filename *rsrc_smb_fname = NULL;
4030 SMB_VFS_HANDLE_GET_DATA(handle, config,
4031 struct fruit_config_data, return -1);
4033 if (is_afpinfo_stream(smb_fname)) {
4034 return fruit_unlink_meta(handle, smb_fname);
4035 } else if (is_afpresource_stream(smb_fname)) {
4036 return fruit_unlink_rsrc(handle, smb_fname, false);
4037 } if (is_ntfs_stream_smb_fname(smb_fname)) {
4038 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4042 * A request to delete the base file. Because 0 byte resource
4043 * fork streams are not listed by fruit_streaminfo,
4044 * delete_all_streams() can't remove 0 byte resource fork
4045 * streams, so we have to cleanup this here.
4047 rsrc_smb_fname = synthetic_smb_fname(talloc_tos(),
4048 smb_fname->base_name,
4049 AFPRESOURCE_STREAM_NAME,
4050 NULL,
4051 smb_fname->flags);
4052 if (rsrc_smb_fname == NULL) {
4053 return -1;
4056 rc = fruit_unlink_rsrc(handle, rsrc_smb_fname, true);
4057 if ((rc != 0) && (errno != ENOENT)) {
4058 DBG_ERR("Forced unlink of [%s] failed [%s]\n",
4059 smb_fname_str_dbg(rsrc_smb_fname), strerror(errno));
4060 TALLOC_FREE(rsrc_smb_fname);
4061 return -1;
4063 TALLOC_FREE(rsrc_smb_fname);
4065 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4068 static int fruit_chmod(vfs_handle_struct *handle,
4069 const struct smb_filename *smb_fname,
4070 mode_t mode)
4072 int rc = -1;
4073 struct fruit_config_data *config = NULL;
4074 struct smb_filename *smb_fname_adp = NULL;
4076 rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname, mode);
4077 if (rc != 0) {
4078 return rc;
4081 SMB_VFS_HANDLE_GET_DATA(handle, config,
4082 struct fruit_config_data, return -1);
4084 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4085 return 0;
4088 if (!VALID_STAT(smb_fname->st)) {
4089 return 0;
4092 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
4093 return 0;
4096 rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_adp);
4097 if (rc != 0) {
4098 return -1;
4101 DEBUG(10, ("fruit_chmod: %s\n", smb_fname_adp->base_name));
4103 rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname_adp, mode);
4104 if (errno == ENOENT) {
4105 rc = 0;
4108 TALLOC_FREE(smb_fname_adp);
4109 return rc;
4112 static int fruit_chown(vfs_handle_struct *handle,
4113 const struct smb_filename *smb_fname,
4114 uid_t uid,
4115 gid_t gid)
4117 int rc = -1;
4118 struct fruit_config_data *config = NULL;
4119 struct smb_filename *adp_smb_fname = NULL;
4121 rc = SMB_VFS_NEXT_CHOWN(handle, smb_fname, uid, gid);
4122 if (rc != 0) {
4123 return rc;
4126 SMB_VFS_HANDLE_GET_DATA(handle, config,
4127 struct fruit_config_data, return -1);
4129 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4130 return 0;
4133 if (!VALID_STAT(smb_fname->st)) {
4134 return 0;
4137 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
4138 return 0;
4141 rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
4142 if (rc != 0) {
4143 goto done;
4146 DEBUG(10, ("fruit_chown: %s\n", adp_smb_fname->base_name));
4148 rc = SMB_VFS_NEXT_CHOWN(handle, adp_smb_fname, uid, gid);
4149 if (errno == ENOENT) {
4150 rc = 0;
4153 done:
4154 TALLOC_FREE(adp_smb_fname);
4155 return rc;
4158 static int fruit_rmdir(struct vfs_handle_struct *handle,
4159 const struct smb_filename *smb_fname)
4161 DIR *dh = NULL;
4162 struct dirent *de;
4163 struct fruit_config_data *config;
4165 SMB_VFS_HANDLE_GET_DATA(handle, config,
4166 struct fruit_config_data, return -1);
4168 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4169 goto exit_rmdir;
4173 * Due to there is no way to change bDeleteVetoFiles variable
4174 * from this module, need to clean up ourselves
4177 dh = SMB_VFS_OPENDIR(handle->conn, smb_fname, NULL, 0);
4178 if (dh == NULL) {
4179 goto exit_rmdir;
4182 while ((de = SMB_VFS_READDIR(handle->conn, dh, NULL)) != NULL) {
4183 int match;
4184 struct adouble *ad = NULL;
4185 char *p = NULL;
4186 struct smb_filename *ad_smb_fname = NULL;
4187 int ret;
4189 match = strncmp(de->d_name,
4190 ADOUBLE_NAME_PREFIX,
4191 strlen(ADOUBLE_NAME_PREFIX));
4192 if (match != 0) {
4193 continue;
4196 p = talloc_asprintf(talloc_tos(), "%s/%s",
4197 smb_fname->base_name, de->d_name);
4198 if (p == NULL) {
4199 DBG_ERR("talloc_asprintf failed\n");
4200 return -1;
4203 ad_smb_fname = synthetic_smb_fname(talloc_tos(), p,
4204 NULL, NULL,
4205 smb_fname->flags);
4206 TALLOC_FREE(p);
4207 if (ad_smb_fname == NULL) {
4208 DBG_ERR("synthetic_smb_fname failed\n");
4209 return -1;
4213 * Check whether it's a valid AppleDouble file, if
4214 * yes, delete it, ignore it otherwise.
4216 ad = ad_get(talloc_tos(), handle, ad_smb_fname, ADOUBLE_RSRC);
4217 if (ad == NULL) {
4218 TALLOC_FREE(ad_smb_fname);
4219 TALLOC_FREE(p);
4220 continue;
4222 TALLOC_FREE(ad);
4224 ret = SMB_VFS_NEXT_UNLINK(handle, ad_smb_fname);
4225 if (ret != 0) {
4226 DBG_ERR("Deleting [%s] failed\n",
4227 smb_fname_str_dbg(ad_smb_fname));
4229 TALLOC_FREE(ad_smb_fname);
4232 exit_rmdir:
4233 if (dh) {
4234 SMB_VFS_CLOSEDIR(handle->conn, dh);
4236 return SMB_VFS_NEXT_RMDIR(handle, smb_fname);
4239 static ssize_t fruit_pread_meta_stream(vfs_handle_struct *handle,
4240 files_struct *fsp, void *data,
4241 size_t n, off_t offset)
4243 ssize_t nread;
4244 int ret;
4246 nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4247 if (nread == -1 || nread == n) {
4248 return nread;
4251 DBG_ERR("Removing [%s] after short read [%zd]\n",
4252 fsp_str_dbg(fsp), nread);
4254 ret = SMB_VFS_NEXT_UNLINK(handle, fsp->fsp_name);
4255 if (ret != 0) {
4256 DBG_ERR("Removing [%s] failed\n", fsp_str_dbg(fsp));
4257 return -1;
4260 errno = EINVAL;
4261 return -1;
4264 static ssize_t fruit_pread_meta_adouble(vfs_handle_struct *handle,
4265 files_struct *fsp, void *data,
4266 size_t n, off_t offset)
4268 AfpInfo *ai = NULL;
4269 struct adouble *ad = NULL;
4270 char afpinfo_buf[AFP_INFO_SIZE];
4271 char *p = NULL;
4272 ssize_t nread;
4274 ai = afpinfo_new(talloc_tos());
4275 if (ai == NULL) {
4276 return -1;
4279 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
4280 if (ad == NULL) {
4281 nread = -1;
4282 goto fail;
4285 p = ad_get_entry(ad, ADEID_FINDERI);
4286 if (p == NULL) {
4287 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
4288 nread = -1;
4289 goto fail;
4292 memcpy(&ai->afpi_FinderInfo[0], p, ADEDLEN_FINDERI);
4294 nread = afpinfo_pack(ai, afpinfo_buf);
4295 if (nread != AFP_INFO_SIZE) {
4296 nread = -1;
4297 goto fail;
4300 memcpy(data, afpinfo_buf, n);
4301 nread = n;
4303 fail:
4304 TALLOC_FREE(ai);
4305 return nread;
4308 static ssize_t fruit_pread_meta(vfs_handle_struct *handle,
4309 files_struct *fsp, void *data,
4310 size_t n, off_t offset)
4312 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4313 ssize_t nread;
4314 ssize_t to_return;
4317 * OS X has a off-by-1 error in the offset calculation, so we're
4318 * bug compatible here. It won't hurt, as any relevant real
4319 * world read requests from the AFP_AfpInfo stream will be
4320 * offset=0 n=60. offset is ignored anyway, see below.
4322 if ((offset < 0) || (offset >= AFP_INFO_SIZE + 1)) {
4323 return 0;
4326 if (fio == NULL) {
4327 DBG_ERR("Failed to fetch fsp extension");
4328 return -1;
4331 /* Yes, macOS always reads from offset 0 */
4332 offset = 0;
4333 to_return = MIN(n, AFP_INFO_SIZE);
4335 switch (fio->config->meta) {
4336 case FRUIT_META_STREAM:
4337 nread = fruit_pread_meta_stream(handle, fsp, data,
4338 to_return, offset);
4339 break;
4341 case FRUIT_META_NETATALK:
4342 nread = fruit_pread_meta_adouble(handle, fsp, data,
4343 to_return, offset);
4344 break;
4346 default:
4347 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
4348 return -1;
4351 if (nread == -1 && fio->created) {
4352 AfpInfo *ai = NULL;
4353 char afpinfo_buf[AFP_INFO_SIZE];
4355 ai = afpinfo_new(talloc_tos());
4356 if (ai == NULL) {
4357 return -1;
4360 nread = afpinfo_pack(ai, afpinfo_buf);
4361 TALLOC_FREE(ai);
4362 if (nread != AFP_INFO_SIZE) {
4363 return -1;
4366 memcpy(data, afpinfo_buf, to_return);
4367 return to_return;
4370 return nread;
4373 static ssize_t fruit_pread_rsrc_stream(vfs_handle_struct *handle,
4374 files_struct *fsp, void *data,
4375 size_t n, off_t offset)
4377 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4380 static ssize_t fruit_pread_rsrc_xattr(vfs_handle_struct *handle,
4381 files_struct *fsp, void *data,
4382 size_t n, off_t offset)
4384 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4387 static ssize_t fruit_pread_rsrc_adouble(vfs_handle_struct *handle,
4388 files_struct *fsp, void *data,
4389 size_t n, off_t offset)
4391 struct adouble *ad = NULL;
4392 ssize_t nread;
4394 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
4395 if (ad == NULL) {
4396 return -1;
4399 nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n,
4400 offset + ad_getentryoff(ad, ADEID_RFORK));
4402 TALLOC_FREE(ad);
4403 return nread;
4406 static ssize_t fruit_pread_rsrc(vfs_handle_struct *handle,
4407 files_struct *fsp, void *data,
4408 size_t n, off_t offset)
4410 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4411 ssize_t nread;
4413 if (fio == NULL) {
4414 errno = EINVAL;
4415 return -1;
4418 switch (fio->config->rsrc) {
4419 case FRUIT_RSRC_STREAM:
4420 nread = fruit_pread_rsrc_stream(handle, fsp, data, n, offset);
4421 break;
4423 case FRUIT_RSRC_ADFILE:
4424 nread = fruit_pread_rsrc_adouble(handle, fsp, data, n, offset);
4425 break;
4427 case FRUIT_RSRC_XATTR:
4428 nread = fruit_pread_rsrc_xattr(handle, fsp, data, n, offset);
4429 break;
4431 default:
4432 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
4433 return -1;
4436 return nread;
4439 static ssize_t fruit_pread(vfs_handle_struct *handle,
4440 files_struct *fsp, void *data,
4441 size_t n, off_t offset)
4443 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4444 ssize_t nread;
4446 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4447 fsp_str_dbg(fsp), (intmax_t)offset, n);
4449 if (fio == NULL) {
4450 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4453 if (fio->type == ADOUBLE_META) {
4454 nread = fruit_pread_meta(handle, fsp, data, n, offset);
4455 } else {
4456 nread = fruit_pread_rsrc(handle, fsp, data, n, offset);
4459 DBG_DEBUG("Path [%s] nread [%zd]\n", fsp_str_dbg(fsp), nread);
4460 return nread;
4463 static bool fruit_must_handle_aio_stream(struct fio *fio)
4465 if (fio == NULL) {
4466 return false;
4469 if (fio->type == ADOUBLE_META) {
4470 return true;
4473 if ((fio->type == ADOUBLE_RSRC) &&
4474 (fio->config->rsrc == FRUIT_RSRC_ADFILE))
4476 return true;
4479 return false;
4482 struct fruit_pread_state {
4483 ssize_t nread;
4484 struct vfs_aio_state vfs_aio_state;
4487 static void fruit_pread_done(struct tevent_req *subreq);
4489 static struct tevent_req *fruit_pread_send(
4490 struct vfs_handle_struct *handle,
4491 TALLOC_CTX *mem_ctx,
4492 struct tevent_context *ev,
4493 struct files_struct *fsp,
4494 void *data,
4495 size_t n, off_t offset)
4497 struct tevent_req *req = NULL;
4498 struct tevent_req *subreq = NULL;
4499 struct fruit_pread_state *state = NULL;
4500 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4502 req = tevent_req_create(mem_ctx, &state,
4503 struct fruit_pread_state);
4504 if (req == NULL) {
4505 return NULL;
4508 if (fruit_must_handle_aio_stream(fio)) {
4509 state->nread = SMB_VFS_PREAD(fsp, data, n, offset);
4510 if (state->nread != n) {
4511 if (state->nread != -1) {
4512 errno = EIO;
4514 tevent_req_error(req, errno);
4515 return tevent_req_post(req, ev);
4517 tevent_req_done(req);
4518 return tevent_req_post(req, ev);
4521 subreq = SMB_VFS_NEXT_PREAD_SEND(state, ev, handle, fsp,
4522 data, n, offset);
4523 if (tevent_req_nomem(req, subreq)) {
4524 return tevent_req_post(req, ev);
4526 tevent_req_set_callback(subreq, fruit_pread_done, req);
4527 return req;
4530 static void fruit_pread_done(struct tevent_req *subreq)
4532 struct tevent_req *req = tevent_req_callback_data(
4533 subreq, struct tevent_req);
4534 struct fruit_pread_state *state = tevent_req_data(
4535 req, struct fruit_pread_state);
4537 state->nread = SMB_VFS_PREAD_RECV(subreq, &state->vfs_aio_state);
4538 TALLOC_FREE(subreq);
4540 if (tevent_req_error(req, state->vfs_aio_state.error)) {
4541 return;
4543 tevent_req_done(req);
4546 static ssize_t fruit_pread_recv(struct tevent_req *req,
4547 struct vfs_aio_state *vfs_aio_state)
4549 struct fruit_pread_state *state = tevent_req_data(
4550 req, struct fruit_pread_state);
4552 if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
4553 return -1;
4556 *vfs_aio_state = state->vfs_aio_state;
4557 return state->nread;
4560 static ssize_t fruit_pwrite_meta_stream(vfs_handle_struct *handle,
4561 files_struct *fsp, const void *data,
4562 size_t n, off_t offset)
4564 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4565 AfpInfo *ai = NULL;
4566 size_t nwritten;
4567 int ret;
4568 bool ok;
4570 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4571 fsp_str_dbg(fsp), (intmax_t)offset, n);
4573 if (fio == NULL) {
4574 return -1;
4577 if (fio->fake_fd) {
4578 int fd;
4580 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
4581 if (ret != 0) {
4582 DBG_ERR("Close [%s] failed: %s\n",
4583 fsp_str_dbg(fsp), strerror(errno));
4584 fsp->fh->fd = -1;
4585 return -1;
4588 fd = SMB_VFS_NEXT_OPEN(handle,
4589 fsp->fsp_name,
4590 fsp,
4591 fio->flags,
4592 fio->mode);
4593 if (fd == -1) {
4594 DBG_ERR("On-demand create [%s] in write failed: %s\n",
4595 fsp_str_dbg(fsp), strerror(errno));
4596 return -1;
4598 fsp->fh->fd = fd;
4599 fio->fake_fd = false;
4602 ai = afpinfo_unpack(talloc_tos(), data);
4603 if (ai == NULL) {
4604 return -1;
4607 if (ai_empty_finderinfo(ai)) {
4609 * Writing an all 0 blob to the metadata stream results in the
4610 * stream being removed on a macOS server. This ensures we
4611 * behave the same and it verified by the "delete AFP_AfpInfo by
4612 * writing all 0" test.
4614 ret = SMB_VFS_NEXT_FTRUNCATE(handle, fsp, 0);
4615 if (ret != 0) {
4616 DBG_ERR("SMB_VFS_NEXT_FTRUNCATE on [%s] failed\n",
4617 fsp_str_dbg(fsp));
4618 return -1;
4621 ok = set_delete_on_close(
4622 fsp,
4623 true,
4624 handle->conn->session_info->security_token,
4625 handle->conn->session_info->unix_token);
4626 if (!ok) {
4627 DBG_ERR("set_delete_on_close on [%s] failed\n",
4628 fsp_str_dbg(fsp));
4629 return -1;
4631 return n;
4634 nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4635 if (nwritten != n) {
4636 return -1;
4639 return n;
4642 static ssize_t fruit_pwrite_meta_netatalk(vfs_handle_struct *handle,
4643 files_struct *fsp, const void *data,
4644 size_t n, off_t offset)
4646 struct adouble *ad = NULL;
4647 AfpInfo *ai = NULL;
4648 char *p = NULL;
4649 int ret;
4650 bool ok;
4652 ai = afpinfo_unpack(talloc_tos(), data);
4653 if (ai == NULL) {
4654 return -1;
4657 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
4658 if (ad == NULL) {
4659 ad = ad_init(talloc_tos(), handle, ADOUBLE_META);
4660 if (ad == NULL) {
4661 return -1;
4664 p = ad_get_entry(ad, ADEID_FINDERI);
4665 if (p == NULL) {
4666 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
4667 TALLOC_FREE(ad);
4668 return -1;
4671 memcpy(p, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
4673 ret = ad_fset(ad, fsp);
4674 if (ret != 0) {
4675 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
4676 TALLOC_FREE(ad);
4677 return -1;
4680 TALLOC_FREE(ad);
4682 if (!ai_empty_finderinfo(ai)) {
4683 return n;
4687 * Writing an all 0 blob to the metadata stream results in the stream
4688 * being removed on a macOS server. This ensures we behave the same and
4689 * it verified by the "delete AFP_AfpInfo by writing all 0" test.
4692 ok = set_delete_on_close(
4693 fsp,
4694 true,
4695 handle->conn->session_info->security_token,
4696 handle->conn->session_info->unix_token);
4697 if (!ok) {
4698 DBG_ERR("set_delete_on_close on [%s] failed\n",
4699 fsp_str_dbg(fsp));
4700 return -1;
4703 return n;
4706 static ssize_t fruit_pwrite_meta(vfs_handle_struct *handle,
4707 files_struct *fsp, const void *data,
4708 size_t n, off_t offset)
4710 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4711 ssize_t nwritten;
4712 uint8_t buf[AFP_INFO_SIZE];
4713 size_t to_write;
4714 size_t to_copy;
4715 int cmp;
4717 if (fio == NULL) {
4718 DBG_ERR("Failed to fetch fsp extension");
4719 return -1;
4722 if (n < 3) {
4723 errno = EINVAL;
4724 return -1;
4727 if (offset != 0 && n < 60) {
4728 errno = EINVAL;
4729 return -1;
4732 cmp = memcmp(data, "AFP", 3);
4733 if (cmp != 0) {
4734 errno = EINVAL;
4735 return -1;
4738 if (n <= AFP_OFF_FinderInfo) {
4740 * Nothing to do here really, just return
4742 return n;
4745 offset = 0;
4747 to_copy = n;
4748 if (to_copy > AFP_INFO_SIZE) {
4749 to_copy = AFP_INFO_SIZE;
4751 memcpy(buf, data, to_copy);
4753 to_write = n;
4754 if (to_write != AFP_INFO_SIZE) {
4755 to_write = AFP_INFO_SIZE;
4758 switch (fio->config->meta) {
4759 case FRUIT_META_STREAM:
4760 nwritten = fruit_pwrite_meta_stream(handle,
4761 fsp,
4762 buf,
4763 to_write,
4764 offset);
4765 break;
4767 case FRUIT_META_NETATALK:
4768 nwritten = fruit_pwrite_meta_netatalk(handle,
4769 fsp,
4770 buf,
4771 to_write,
4772 offset);
4773 break;
4775 default:
4776 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
4777 return -1;
4780 if (nwritten != to_write) {
4781 return -1;
4785 * Return the requested amount, verified against macOS SMB server
4787 return n;
4790 static ssize_t fruit_pwrite_rsrc_stream(vfs_handle_struct *handle,
4791 files_struct *fsp, const void *data,
4792 size_t n, off_t offset)
4794 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4797 static ssize_t fruit_pwrite_rsrc_xattr(vfs_handle_struct *handle,
4798 files_struct *fsp, const void *data,
4799 size_t n, off_t offset)
4801 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4804 static ssize_t fruit_pwrite_rsrc_adouble(vfs_handle_struct *handle,
4805 files_struct *fsp, const void *data,
4806 size_t n, off_t offset)
4808 struct adouble *ad = NULL;
4809 ssize_t nwritten;
4810 int ret;
4812 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
4813 if (ad == NULL) {
4814 DBG_ERR("ad_get [%s] failed\n", fsp_str_dbg(fsp));
4815 return -1;
4818 nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n,
4819 offset + ad_getentryoff(ad, ADEID_RFORK));
4820 if (nwritten != n) {
4821 DBG_ERR("Short write on [%s] [%zd/%zd]\n",
4822 fsp_str_dbg(fsp), nwritten, n);
4823 TALLOC_FREE(ad);
4824 return -1;
4827 if ((n + offset) > ad_getentrylen(ad, ADEID_RFORK)) {
4828 ad_setentrylen(ad, ADEID_RFORK, n + offset);
4829 ret = ad_fset(ad, fsp);
4830 if (ret != 0) {
4831 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
4832 TALLOC_FREE(ad);
4833 return -1;
4837 TALLOC_FREE(ad);
4838 return n;
4841 static ssize_t fruit_pwrite_rsrc(vfs_handle_struct *handle,
4842 files_struct *fsp, const void *data,
4843 size_t n, off_t offset)
4845 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4846 ssize_t nwritten;
4848 if (fio == NULL) {
4849 DBG_ERR("Failed to fetch fsp extension");
4850 return -1;
4853 switch (fio->config->rsrc) {
4854 case FRUIT_RSRC_STREAM:
4855 nwritten = fruit_pwrite_rsrc_stream(handle, fsp, data, n, offset);
4856 break;
4858 case FRUIT_RSRC_ADFILE:
4859 nwritten = fruit_pwrite_rsrc_adouble(handle, fsp, data, n, offset);
4860 break;
4862 case FRUIT_RSRC_XATTR:
4863 nwritten = fruit_pwrite_rsrc_xattr(handle, fsp, data, n, offset);
4864 break;
4866 default:
4867 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
4868 return -1;
4871 return nwritten;
4874 static ssize_t fruit_pwrite(vfs_handle_struct *handle,
4875 files_struct *fsp, const void *data,
4876 size_t n, off_t offset)
4878 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4879 ssize_t nwritten;
4881 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4882 fsp_str_dbg(fsp), (intmax_t)offset, n);
4884 if (fio == NULL) {
4885 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4888 if (fio->type == ADOUBLE_META) {
4889 nwritten = fruit_pwrite_meta(handle, fsp, data, n, offset);
4890 } else {
4891 nwritten = fruit_pwrite_rsrc(handle, fsp, data, n, offset);
4894 DBG_DEBUG("Path [%s] nwritten=%zd\n", fsp_str_dbg(fsp), nwritten);
4895 return nwritten;
4898 struct fruit_pwrite_state {
4899 ssize_t nwritten;
4900 struct vfs_aio_state vfs_aio_state;
4903 static void fruit_pwrite_done(struct tevent_req *subreq);
4905 static struct tevent_req *fruit_pwrite_send(
4906 struct vfs_handle_struct *handle,
4907 TALLOC_CTX *mem_ctx,
4908 struct tevent_context *ev,
4909 struct files_struct *fsp,
4910 const void *data,
4911 size_t n, off_t offset)
4913 struct tevent_req *req = NULL;
4914 struct tevent_req *subreq = NULL;
4915 struct fruit_pwrite_state *state = NULL;
4916 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4918 req = tevent_req_create(mem_ctx, &state,
4919 struct fruit_pwrite_state);
4920 if (req == NULL) {
4921 return NULL;
4924 if (fruit_must_handle_aio_stream(fio)) {
4925 state->nwritten = SMB_VFS_PWRITE(fsp, data, n, offset);
4926 if (state->nwritten != n) {
4927 if (state->nwritten != -1) {
4928 errno = EIO;
4930 tevent_req_error(req, errno);
4931 return tevent_req_post(req, ev);
4933 tevent_req_done(req);
4934 return tevent_req_post(req, ev);
4937 subreq = SMB_VFS_NEXT_PWRITE_SEND(state, ev, handle, fsp,
4938 data, n, offset);
4939 if (tevent_req_nomem(req, subreq)) {
4940 return tevent_req_post(req, ev);
4942 tevent_req_set_callback(subreq, fruit_pwrite_done, req);
4943 return req;
4946 static void fruit_pwrite_done(struct tevent_req *subreq)
4948 struct tevent_req *req = tevent_req_callback_data(
4949 subreq, struct tevent_req);
4950 struct fruit_pwrite_state *state = tevent_req_data(
4951 req, struct fruit_pwrite_state);
4953 state->nwritten = SMB_VFS_PWRITE_RECV(subreq, &state->vfs_aio_state);
4954 TALLOC_FREE(subreq);
4956 if (tevent_req_error(req, state->vfs_aio_state.error)) {
4957 return;
4959 tevent_req_done(req);
4962 static ssize_t fruit_pwrite_recv(struct tevent_req *req,
4963 struct vfs_aio_state *vfs_aio_state)
4965 struct fruit_pwrite_state *state = tevent_req_data(
4966 req, struct fruit_pwrite_state);
4968 if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
4969 return -1;
4972 *vfs_aio_state = state->vfs_aio_state;
4973 return state->nwritten;
4977 * Helper to stat/lstat the base file of an smb_fname.
4979 static int fruit_stat_base(vfs_handle_struct *handle,
4980 struct smb_filename *smb_fname,
4981 bool follow_links)
4983 char *tmp_stream_name;
4984 int rc;
4986 tmp_stream_name = smb_fname->stream_name;
4987 smb_fname->stream_name = NULL;
4988 if (follow_links) {
4989 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
4990 } else {
4991 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
4993 smb_fname->stream_name = tmp_stream_name;
4995 DBG_DEBUG("fruit_stat_base [%s] dev [%ju] ino [%ju]\n",
4996 smb_fname->base_name,
4997 (uintmax_t)smb_fname->st.st_ex_dev,
4998 (uintmax_t)smb_fname->st.st_ex_ino);
4999 return rc;
5002 static int fruit_stat_meta_stream(vfs_handle_struct *handle,
5003 struct smb_filename *smb_fname,
5004 bool follow_links)
5006 int ret;
5007 ino_t ino;
5009 ret = fruit_stat_base(handle, smb_fname, false);
5010 if (ret != 0) {
5011 return -1;
5014 ino = fruit_inode(&smb_fname->st, smb_fname->stream_name);
5016 if (follow_links) {
5017 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
5018 } else {
5019 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5022 smb_fname->st.st_ex_ino = ino;
5024 return ret;
5027 static int fruit_stat_meta_netatalk(vfs_handle_struct *handle,
5028 struct smb_filename *smb_fname,
5029 bool follow_links)
5031 struct adouble *ad = NULL;
5033 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5034 if (ad == NULL) {
5035 DBG_INFO("fruit_stat_meta %s: %s\n",
5036 smb_fname_str_dbg(smb_fname), strerror(errno));
5037 errno = ENOENT;
5038 return -1;
5040 TALLOC_FREE(ad);
5042 /* Populate the stat struct with info from the base file. */
5043 if (fruit_stat_base(handle, smb_fname, follow_links) == -1) {
5044 return -1;
5046 smb_fname->st.st_ex_size = AFP_INFO_SIZE;
5047 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5048 smb_fname->stream_name);
5049 return 0;
5052 static int fruit_stat_meta(vfs_handle_struct *handle,
5053 struct smb_filename *smb_fname,
5054 bool follow_links)
5056 struct fruit_config_data *config = NULL;
5057 int ret;
5059 SMB_VFS_HANDLE_GET_DATA(handle, config,
5060 struct fruit_config_data, return -1);
5062 switch (config->meta) {
5063 case FRUIT_META_STREAM:
5064 ret = fruit_stat_meta_stream(handle, smb_fname, follow_links);
5065 break;
5067 case FRUIT_META_NETATALK:
5068 ret = fruit_stat_meta_netatalk(handle, smb_fname, follow_links);
5069 break;
5071 default:
5072 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
5073 return -1;
5076 return ret;
5079 static int fruit_stat_rsrc_netatalk(vfs_handle_struct *handle,
5080 struct smb_filename *smb_fname,
5081 bool follow_links)
5083 struct adouble *ad = NULL;
5084 int ret;
5086 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
5087 if (ad == NULL) {
5088 errno = ENOENT;
5089 return -1;
5092 /* Populate the stat struct with info from the base file. */
5093 ret = fruit_stat_base(handle, smb_fname, follow_links);
5094 if (ret != 0) {
5095 TALLOC_FREE(ad);
5096 return -1;
5099 smb_fname->st.st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
5100 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5101 smb_fname->stream_name);
5102 TALLOC_FREE(ad);
5103 return 0;
5106 static int fruit_stat_rsrc_stream(vfs_handle_struct *handle,
5107 struct smb_filename *smb_fname,
5108 bool follow_links)
5110 int ret;
5112 if (follow_links) {
5113 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
5114 } else {
5115 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5118 return ret;
5121 static int fruit_stat_rsrc_xattr(vfs_handle_struct *handle,
5122 struct smb_filename *smb_fname,
5123 bool follow_links)
5125 #ifdef HAVE_ATTROPEN
5126 int ret;
5127 int fd = -1;
5129 /* Populate the stat struct with info from the base file. */
5130 ret = fruit_stat_base(handle, smb_fname, follow_links);
5131 if (ret != 0) {
5132 return -1;
5135 fd = attropen(smb_fname->base_name,
5136 AFPRESOURCE_EA_NETATALK,
5137 O_RDONLY);
5138 if (fd == -1) {
5139 return 0;
5142 ret = sys_fstat(fd, &smb_fname->st, false);
5143 if (ret != 0) {
5144 close(fd);
5145 DBG_ERR("fstat [%s:%s] failed\n", smb_fname->base_name,
5146 AFPRESOURCE_EA_NETATALK);
5147 return -1;
5149 close(fd);
5150 fd = -1;
5152 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5153 smb_fname->stream_name);
5155 return ret;
5157 #else
5158 errno = ENOSYS;
5159 return -1;
5160 #endif
5163 static int fruit_stat_rsrc(vfs_handle_struct *handle,
5164 struct smb_filename *smb_fname,
5165 bool follow_links)
5167 struct fruit_config_data *config = NULL;
5168 int ret;
5170 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
5172 SMB_VFS_HANDLE_GET_DATA(handle, config,
5173 struct fruit_config_data, return -1);
5175 switch (config->rsrc) {
5176 case FRUIT_RSRC_STREAM:
5177 ret = fruit_stat_rsrc_stream(handle, smb_fname, follow_links);
5178 break;
5180 case FRUIT_RSRC_XATTR:
5181 ret = fruit_stat_rsrc_xattr(handle, smb_fname, follow_links);
5182 break;
5184 case FRUIT_RSRC_ADFILE:
5185 ret = fruit_stat_rsrc_netatalk(handle, smb_fname, follow_links);
5186 break;
5188 default:
5189 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
5190 return -1;
5193 return ret;
5196 static int fruit_stat(vfs_handle_struct *handle,
5197 struct smb_filename *smb_fname)
5199 int rc = -1;
5201 DEBUG(10, ("fruit_stat called for %s\n",
5202 smb_fname_str_dbg(smb_fname)));
5204 if (!is_ntfs_stream_smb_fname(smb_fname)
5205 || is_ntfs_default_stream_smb_fname(smb_fname)) {
5206 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
5207 if (rc == 0) {
5208 update_btime(handle, smb_fname);
5210 return rc;
5214 * Note if lp_posix_paths() is true, we can never
5215 * get here as is_ntfs_stream_smb_fname() is
5216 * always false. So we never need worry about
5217 * not following links here.
5220 if (is_afpinfo_stream(smb_fname)) {
5221 rc = fruit_stat_meta(handle, smb_fname, true);
5222 } else if (is_afpresource_stream(smb_fname)) {
5223 rc = fruit_stat_rsrc(handle, smb_fname, true);
5224 } else {
5225 return SMB_VFS_NEXT_STAT(handle, smb_fname);
5228 if (rc == 0) {
5229 update_btime(handle, smb_fname);
5230 smb_fname->st.st_ex_mode &= ~S_IFMT;
5231 smb_fname->st.st_ex_mode |= S_IFREG;
5232 smb_fname->st.st_ex_blocks =
5233 smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
5235 return rc;
5238 static int fruit_lstat(vfs_handle_struct *handle,
5239 struct smb_filename *smb_fname)
5241 int rc = -1;
5243 DEBUG(10, ("fruit_lstat called for %s\n",
5244 smb_fname_str_dbg(smb_fname)));
5246 if (!is_ntfs_stream_smb_fname(smb_fname)
5247 || is_ntfs_default_stream_smb_fname(smb_fname)) {
5248 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5249 if (rc == 0) {
5250 update_btime(handle, smb_fname);
5252 return rc;
5255 if (is_afpinfo_stream(smb_fname)) {
5256 rc = fruit_stat_meta(handle, smb_fname, false);
5257 } else if (is_afpresource_stream(smb_fname)) {
5258 rc = fruit_stat_rsrc(handle, smb_fname, false);
5259 } else {
5260 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5263 if (rc == 0) {
5264 update_btime(handle, smb_fname);
5265 smb_fname->st.st_ex_mode &= ~S_IFMT;
5266 smb_fname->st.st_ex_mode |= S_IFREG;
5267 smb_fname->st.st_ex_blocks =
5268 smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
5270 return rc;
5273 static int fruit_fstat_meta_stream(vfs_handle_struct *handle,
5274 files_struct *fsp,
5275 SMB_STRUCT_STAT *sbuf)
5277 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5278 struct smb_filename smb_fname;
5279 ino_t ino;
5280 int ret;
5282 if (fio == NULL) {
5283 return -1;
5286 if (fio->fake_fd) {
5287 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5288 if (ret != 0) {
5289 return -1;
5292 *sbuf = fsp->base_fsp->fsp_name->st;
5293 sbuf->st_ex_size = AFP_INFO_SIZE;
5294 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5295 return 0;
5298 smb_fname = (struct smb_filename) {
5299 .base_name = fsp->fsp_name->base_name,
5302 ret = fruit_stat_base(handle, &smb_fname, false);
5303 if (ret != 0) {
5304 return -1;
5306 *sbuf = smb_fname.st;
5308 ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5310 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5311 if (ret != 0) {
5312 return -1;
5315 sbuf->st_ex_ino = ino;
5316 return 0;
5319 static int fruit_fstat_meta_netatalk(vfs_handle_struct *handle,
5320 files_struct *fsp,
5321 SMB_STRUCT_STAT *sbuf)
5323 int ret;
5325 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5326 if (ret != 0) {
5327 return -1;
5330 *sbuf = fsp->base_fsp->fsp_name->st;
5331 sbuf->st_ex_size = AFP_INFO_SIZE;
5332 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5334 return 0;
5337 static int fruit_fstat_meta(vfs_handle_struct *handle,
5338 files_struct *fsp,
5339 SMB_STRUCT_STAT *sbuf,
5340 struct fio *fio)
5342 int ret;
5344 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
5346 switch (fio->config->meta) {
5347 case FRUIT_META_STREAM:
5348 ret = fruit_fstat_meta_stream(handle, fsp, sbuf);
5349 break;
5351 case FRUIT_META_NETATALK:
5352 ret = fruit_fstat_meta_netatalk(handle, fsp, sbuf);
5353 break;
5355 default:
5356 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
5357 return -1;
5360 DBG_DEBUG("Path [%s] ret [%d]\n", fsp_str_dbg(fsp), ret);
5361 return ret;
5364 static int fruit_fstat_rsrc_xattr(vfs_handle_struct *handle,
5365 files_struct *fsp,
5366 SMB_STRUCT_STAT *sbuf)
5368 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5371 static int fruit_fstat_rsrc_stream(vfs_handle_struct *handle,
5372 files_struct *fsp,
5373 SMB_STRUCT_STAT *sbuf)
5375 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5378 static int fruit_fstat_rsrc_adouble(vfs_handle_struct *handle,
5379 files_struct *fsp,
5380 SMB_STRUCT_STAT *sbuf)
5382 struct adouble *ad = NULL;
5383 int ret;
5385 /* Populate the stat struct with info from the base file. */
5386 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5387 if (ret == -1) {
5388 return -1;
5391 ad = ad_get(talloc_tos(), handle,
5392 fsp->base_fsp->fsp_name,
5393 ADOUBLE_RSRC);
5394 if (ad == NULL) {
5395 DBG_ERR("ad_get [%s] failed [%s]\n",
5396 fsp_str_dbg(fsp), strerror(errno));
5397 return -1;
5400 *sbuf = fsp->base_fsp->fsp_name->st;
5401 sbuf->st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
5402 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5404 TALLOC_FREE(ad);
5405 return 0;
5408 static int fruit_fstat_rsrc(vfs_handle_struct *handle, files_struct *fsp,
5409 SMB_STRUCT_STAT *sbuf, struct fio *fio)
5411 int ret;
5413 switch (fio->config->rsrc) {
5414 case FRUIT_RSRC_STREAM:
5415 ret = fruit_fstat_rsrc_stream(handle, fsp, sbuf);
5416 break;
5418 case FRUIT_RSRC_ADFILE:
5419 ret = fruit_fstat_rsrc_adouble(handle, fsp, sbuf);
5420 break;
5422 case FRUIT_RSRC_XATTR:
5423 ret = fruit_fstat_rsrc_xattr(handle, fsp, sbuf);
5424 break;
5426 default:
5427 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
5428 return -1;
5431 return ret;
5434 static int fruit_fstat(vfs_handle_struct *handle, files_struct *fsp,
5435 SMB_STRUCT_STAT *sbuf)
5437 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5438 int rc;
5440 if (fio == NULL) {
5441 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5444 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
5446 if (fio->type == ADOUBLE_META) {
5447 rc = fruit_fstat_meta(handle, fsp, sbuf, fio);
5448 } else {
5449 rc = fruit_fstat_rsrc(handle, fsp, sbuf, fio);
5452 if (rc == 0) {
5453 sbuf->st_ex_mode &= ~S_IFMT;
5454 sbuf->st_ex_mode |= S_IFREG;
5455 sbuf->st_ex_blocks = sbuf->st_ex_size / STAT_ST_BLOCKSIZE + 1;
5458 DBG_DEBUG("Path [%s] rc [%d] size [%"PRIdMAX"]\n",
5459 fsp_str_dbg(fsp), rc, (intmax_t)sbuf->st_ex_size);
5460 return rc;
5463 static NTSTATUS delete_invalid_meta_stream(
5464 vfs_handle_struct *handle,
5465 const struct smb_filename *smb_fname,
5466 TALLOC_CTX *mem_ctx,
5467 unsigned int *pnum_streams,
5468 struct stream_struct **pstreams,
5469 off_t size)
5471 struct smb_filename *sname = NULL;
5472 int ret;
5473 bool ok;
5475 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams, AFPINFO_STREAM);
5476 if (!ok) {
5477 return NT_STATUS_INTERNAL_ERROR;
5480 if (size == 0) {
5481 return NT_STATUS_OK;
5484 sname = synthetic_smb_fname(talloc_tos(),
5485 smb_fname->base_name,
5486 AFPINFO_STREAM_NAME,
5487 NULL, 0);
5488 if (sname == NULL) {
5489 return NT_STATUS_NO_MEMORY;
5492 ret = SMB_VFS_NEXT_UNLINK(handle, sname);
5493 TALLOC_FREE(sname);
5494 if (ret != 0) {
5495 DBG_ERR("Removing [%s] failed\n", smb_fname_str_dbg(sname));
5496 return map_nt_error_from_unix(errno);
5499 return NT_STATUS_OK;
5502 static NTSTATUS fruit_streaminfo_meta_stream(
5503 vfs_handle_struct *handle,
5504 struct files_struct *fsp,
5505 const struct smb_filename *smb_fname,
5506 TALLOC_CTX *mem_ctx,
5507 unsigned int *pnum_streams,
5508 struct stream_struct **pstreams)
5510 struct stream_struct *stream = *pstreams;
5511 unsigned int num_streams = *pnum_streams;
5512 int i;
5514 for (i = 0; i < num_streams; i++) {
5515 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
5516 break;
5520 if (i == num_streams) {
5521 return NT_STATUS_OK;
5524 if (stream[i].size != AFP_INFO_SIZE) {
5525 DBG_ERR("Removing invalid AFPINFO_STREAM size [%jd] from [%s]\n",
5526 (intmax_t)stream[i].size, smb_fname_str_dbg(smb_fname));
5528 return delete_invalid_meta_stream(handle,
5529 smb_fname,
5530 mem_ctx,
5531 pnum_streams,
5532 pstreams,
5533 stream[i].size);
5537 return NT_STATUS_OK;
5540 static NTSTATUS fruit_streaminfo_meta_netatalk(
5541 vfs_handle_struct *handle,
5542 struct files_struct *fsp,
5543 const struct smb_filename *smb_fname,
5544 TALLOC_CTX *mem_ctx,
5545 unsigned int *pnum_streams,
5546 struct stream_struct **pstreams)
5548 struct stream_struct *stream = *pstreams;
5549 unsigned int num_streams = *pnum_streams;
5550 struct adouble *ad = NULL;
5551 bool is_fi_empty;
5552 int i;
5553 bool ok;
5555 /* Remove the Netatalk xattr from the list */
5556 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5557 ":" NETATALK_META_XATTR ":$DATA");
5558 if (!ok) {
5559 return NT_STATUS_NO_MEMORY;
5563 * Check if there's a AFPINFO_STREAM from the VFS streams
5564 * backend and if yes, remove it from the list
5566 for (i = 0; i < num_streams; i++) {
5567 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
5568 break;
5572 if (i < num_streams) {
5573 DBG_WARNING("Unexpected AFPINFO_STREAM on [%s]\n",
5574 smb_fname_str_dbg(smb_fname));
5576 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5577 AFPINFO_STREAM);
5578 if (!ok) {
5579 return NT_STATUS_INTERNAL_ERROR;
5583 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5584 if (ad == NULL) {
5585 return NT_STATUS_OK;
5588 is_fi_empty = ad_empty_finderinfo(ad);
5589 TALLOC_FREE(ad);
5591 if (is_fi_empty) {
5592 return NT_STATUS_OK;
5595 ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
5596 AFPINFO_STREAM_NAME, AFP_INFO_SIZE,
5597 smb_roundup(handle->conn, AFP_INFO_SIZE));
5598 if (!ok) {
5599 return NT_STATUS_NO_MEMORY;
5602 return NT_STATUS_OK;
5605 static NTSTATUS fruit_streaminfo_meta(vfs_handle_struct *handle,
5606 struct files_struct *fsp,
5607 const struct smb_filename *smb_fname,
5608 TALLOC_CTX *mem_ctx,
5609 unsigned int *pnum_streams,
5610 struct stream_struct **pstreams)
5612 struct fruit_config_data *config = NULL;
5613 NTSTATUS status;
5615 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5616 return NT_STATUS_INTERNAL_ERROR);
5618 switch (config->meta) {
5619 case FRUIT_META_NETATALK:
5620 status = fruit_streaminfo_meta_netatalk(handle, fsp, smb_fname,
5621 mem_ctx, pnum_streams,
5622 pstreams);
5623 break;
5625 case FRUIT_META_STREAM:
5626 status = fruit_streaminfo_meta_stream(handle, fsp, smb_fname,
5627 mem_ctx, pnum_streams,
5628 pstreams);
5629 break;
5631 default:
5632 return NT_STATUS_INTERNAL_ERROR;
5635 return status;
5638 static NTSTATUS fruit_streaminfo_rsrc_stream(
5639 vfs_handle_struct *handle,
5640 struct files_struct *fsp,
5641 const struct smb_filename *smb_fname,
5642 TALLOC_CTX *mem_ctx,
5643 unsigned int *pnum_streams,
5644 struct stream_struct **pstreams)
5646 bool ok;
5648 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
5649 if (!ok) {
5650 DBG_ERR("Filtering resource stream failed\n");
5651 return NT_STATUS_INTERNAL_ERROR;
5653 return NT_STATUS_OK;
5656 static NTSTATUS fruit_streaminfo_rsrc_xattr(
5657 vfs_handle_struct *handle,
5658 struct files_struct *fsp,
5659 const struct smb_filename *smb_fname,
5660 TALLOC_CTX *mem_ctx,
5661 unsigned int *pnum_streams,
5662 struct stream_struct **pstreams)
5664 bool ok;
5666 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
5667 if (!ok) {
5668 DBG_ERR("Filtering resource stream failed\n");
5669 return NT_STATUS_INTERNAL_ERROR;
5671 return NT_STATUS_OK;
5674 static NTSTATUS fruit_streaminfo_rsrc_adouble(
5675 vfs_handle_struct *handle,
5676 struct files_struct *fsp,
5677 const struct smb_filename *smb_fname,
5678 TALLOC_CTX *mem_ctx,
5679 unsigned int *pnum_streams,
5680 struct stream_struct **pstreams)
5682 struct stream_struct *stream = *pstreams;
5683 unsigned int num_streams = *pnum_streams;
5684 struct adouble *ad = NULL;
5685 bool ok;
5686 size_t rlen;
5687 int i;
5690 * Check if there's a AFPRESOURCE_STREAM from the VFS streams backend
5691 * and if yes, remove it from the list
5693 for (i = 0; i < num_streams; i++) {
5694 if (strequal_m(stream[i].name, AFPRESOURCE_STREAM)) {
5695 break;
5699 if (i < num_streams) {
5700 DBG_WARNING("Unexpected AFPRESOURCE_STREAM on [%s]\n",
5701 smb_fname_str_dbg(smb_fname));
5703 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5704 AFPRESOURCE_STREAM);
5705 if (!ok) {
5706 return NT_STATUS_INTERNAL_ERROR;
5710 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
5711 if (ad == NULL) {
5712 return NT_STATUS_OK;
5715 rlen = ad_getentrylen(ad, ADEID_RFORK);
5716 TALLOC_FREE(ad);
5718 if (rlen == 0) {
5719 return NT_STATUS_OK;
5722 ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
5723 AFPRESOURCE_STREAM_NAME, rlen,
5724 smb_roundup(handle->conn, rlen));
5725 if (!ok) {
5726 return NT_STATUS_NO_MEMORY;
5729 return NT_STATUS_OK;
5732 static NTSTATUS fruit_streaminfo_rsrc(vfs_handle_struct *handle,
5733 struct files_struct *fsp,
5734 const struct smb_filename *smb_fname,
5735 TALLOC_CTX *mem_ctx,
5736 unsigned int *pnum_streams,
5737 struct stream_struct **pstreams)
5739 struct fruit_config_data *config = NULL;
5740 NTSTATUS status;
5742 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5743 return NT_STATUS_INTERNAL_ERROR);
5745 switch (config->rsrc) {
5746 case FRUIT_RSRC_STREAM:
5747 status = fruit_streaminfo_rsrc_stream(handle, fsp, smb_fname,
5748 mem_ctx, pnum_streams,
5749 pstreams);
5750 break;
5752 case FRUIT_RSRC_XATTR:
5753 status = fruit_streaminfo_rsrc_xattr(handle, fsp, smb_fname,
5754 mem_ctx, pnum_streams,
5755 pstreams);
5756 break;
5758 case FRUIT_RSRC_ADFILE:
5759 status = fruit_streaminfo_rsrc_adouble(handle, fsp, smb_fname,
5760 mem_ctx, pnum_streams,
5761 pstreams);
5762 break;
5764 default:
5765 return NT_STATUS_INTERNAL_ERROR;
5768 return status;
5771 static void fruit_filter_empty_streams(unsigned int *pnum_streams,
5772 struct stream_struct **pstreams)
5774 unsigned num_streams = *pnum_streams;
5775 struct stream_struct *streams = *pstreams;
5776 unsigned i = 0;
5778 if (!global_fruit_config.nego_aapl) {
5779 return;
5782 while (i < num_streams) {
5783 struct smb_filename smb_fname = (struct smb_filename) {
5784 .stream_name = streams[i].name,
5787 if (is_ntfs_default_stream_smb_fname(&smb_fname)
5788 || streams[i].size > 0)
5790 i++;
5791 continue;
5794 streams[i] = streams[num_streams - 1];
5795 num_streams--;
5798 *pnum_streams = num_streams;
5801 static NTSTATUS fruit_streaminfo(vfs_handle_struct *handle,
5802 struct files_struct *fsp,
5803 const struct smb_filename *smb_fname,
5804 TALLOC_CTX *mem_ctx,
5805 unsigned int *pnum_streams,
5806 struct stream_struct **pstreams)
5808 struct fruit_config_data *config = NULL;
5809 NTSTATUS status;
5811 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5812 return NT_STATUS_UNSUCCESSFUL);
5814 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
5816 status = SMB_VFS_NEXT_STREAMINFO(handle, fsp, smb_fname, mem_ctx,
5817 pnum_streams, pstreams);
5818 if (!NT_STATUS_IS_OK(status)) {
5819 return status;
5822 fruit_filter_empty_streams(pnum_streams, pstreams);
5824 status = fruit_streaminfo_meta(handle, fsp, smb_fname,
5825 mem_ctx, pnum_streams, pstreams);
5826 if (!NT_STATUS_IS_OK(status)) {
5827 return status;
5830 status = fruit_streaminfo_rsrc(handle, fsp, smb_fname,
5831 mem_ctx, pnum_streams, pstreams);
5832 if (!NT_STATUS_IS_OK(status)) {
5833 return status;
5836 return NT_STATUS_OK;
5839 static int fruit_ntimes(vfs_handle_struct *handle,
5840 const struct smb_filename *smb_fname,
5841 struct smb_file_time *ft)
5843 int rc = 0;
5844 struct adouble *ad = NULL;
5845 struct fruit_config_data *config = NULL;
5847 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5848 return -1);
5850 if ((config->meta != FRUIT_META_NETATALK) ||
5851 null_timespec(ft->create_time))
5853 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
5856 DEBUG(10,("set btime for %s to %s\n", smb_fname_str_dbg(smb_fname),
5857 time_to_asc(convert_timespec_to_time_t(ft->create_time))));
5859 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5860 if (ad == NULL) {
5861 goto exit;
5864 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX,
5865 convert_time_t_to_uint32_t(ft->create_time.tv_sec));
5867 rc = ad_set(ad, smb_fname);
5869 exit:
5871 TALLOC_FREE(ad);
5872 if (rc != 0) {
5873 DEBUG(1, ("fruit_ntimes: %s\n", smb_fname_str_dbg(smb_fname)));
5874 return -1;
5876 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
5879 static int fruit_fallocate(struct vfs_handle_struct *handle,
5880 struct files_struct *fsp,
5881 uint32_t mode,
5882 off_t offset,
5883 off_t len)
5885 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5887 if (fio == NULL) {
5888 return SMB_VFS_NEXT_FALLOCATE(handle, fsp, mode, offset, len);
5891 /* Let the pwrite code path handle it. */
5892 errno = ENOSYS;
5893 return -1;
5896 static int fruit_ftruncate_rsrc_xattr(struct vfs_handle_struct *handle,
5897 struct files_struct *fsp,
5898 off_t offset)
5900 #ifdef HAVE_ATTROPEN
5901 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
5902 #endif
5903 return 0;
5906 static int fruit_ftruncate_rsrc_adouble(struct vfs_handle_struct *handle,
5907 struct files_struct *fsp,
5908 off_t offset)
5910 int rc;
5911 struct adouble *ad = NULL;
5912 off_t ad_off;
5914 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
5915 if (ad == NULL) {
5916 DBG_DEBUG("ad_get [%s] failed [%s]\n",
5917 fsp_str_dbg(fsp), strerror(errno));
5918 return -1;
5921 ad_off = ad_getentryoff(ad, ADEID_RFORK);
5923 rc = ftruncate(fsp->fh->fd, offset + ad_off);
5924 if (rc != 0) {
5925 TALLOC_FREE(ad);
5926 return -1;
5929 ad_setentrylen(ad, ADEID_RFORK, offset);
5931 rc = ad_fset(ad, fsp);
5932 if (rc != 0) {
5933 DBG_ERR("ad_fset [%s] failed [%s]\n",
5934 fsp_str_dbg(fsp), strerror(errno));
5935 TALLOC_FREE(ad);
5936 return -1;
5939 TALLOC_FREE(ad);
5940 return 0;
5943 static int fruit_ftruncate_rsrc_stream(struct vfs_handle_struct *handle,
5944 struct files_struct *fsp,
5945 off_t offset)
5947 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
5950 static int fruit_ftruncate_rsrc(struct vfs_handle_struct *handle,
5951 struct files_struct *fsp,
5952 off_t offset)
5954 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5955 int ret;
5957 if (fio == NULL) {
5958 DBG_ERR("Failed to fetch fsp extension");
5959 return -1;
5962 switch (fio->config->rsrc) {
5963 case FRUIT_RSRC_XATTR:
5964 ret = fruit_ftruncate_rsrc_xattr(handle, fsp, offset);
5965 break;
5967 case FRUIT_RSRC_ADFILE:
5968 ret = fruit_ftruncate_rsrc_adouble(handle, fsp, offset);
5969 break;
5971 case FRUIT_RSRC_STREAM:
5972 ret = fruit_ftruncate_rsrc_stream(handle, fsp, offset);
5973 break;
5975 default:
5976 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
5977 return -1;
5981 return ret;
5984 static int fruit_ftruncate_meta(struct vfs_handle_struct *handle,
5985 struct files_struct *fsp,
5986 off_t offset)
5988 if (offset > 60) {
5989 DBG_WARNING("ftruncate %s to %jd",
5990 fsp_str_dbg(fsp), (intmax_t)offset);
5991 /* OS X returns NT_STATUS_ALLOTTED_SPACE_EXCEEDED */
5992 errno = EOVERFLOW;
5993 return -1;
5996 /* OS X returns success but does nothing */
5997 DBG_INFO("ignoring ftruncate %s to %jd\n",
5998 fsp_str_dbg(fsp), (intmax_t)offset);
5999 return 0;
6002 static int fruit_ftruncate(struct vfs_handle_struct *handle,
6003 struct files_struct *fsp,
6004 off_t offset)
6006 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
6007 int ret;
6009 DBG_DEBUG("Path [%s] offset [%"PRIdMAX"]\n", fsp_str_dbg(fsp),
6010 (intmax_t)offset);
6012 if (fio == NULL) {
6013 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
6016 if (fio->type == ADOUBLE_META) {
6017 ret = fruit_ftruncate_meta(handle, fsp, offset);
6018 } else {
6019 ret = fruit_ftruncate_rsrc(handle, fsp, offset);
6022 DBG_DEBUG("Path [%s] result [%d]\n", fsp_str_dbg(fsp), ret);
6023 return ret;
6026 static NTSTATUS fruit_create_file(vfs_handle_struct *handle,
6027 struct smb_request *req,
6028 uint16_t root_dir_fid,
6029 struct smb_filename *smb_fname,
6030 uint32_t access_mask,
6031 uint32_t share_access,
6032 uint32_t create_disposition,
6033 uint32_t create_options,
6034 uint32_t file_attributes,
6035 uint32_t oplock_request,
6036 struct smb2_lease *lease,
6037 uint64_t allocation_size,
6038 uint32_t private_flags,
6039 struct security_descriptor *sd,
6040 struct ea_list *ea_list,
6041 files_struct **result,
6042 int *pinfo,
6043 const struct smb2_create_blobs *in_context_blobs,
6044 struct smb2_create_blobs *out_context_blobs)
6046 NTSTATUS status;
6047 struct fruit_config_data *config = NULL;
6048 files_struct *fsp = NULL;
6049 struct fio *fio = NULL;
6051 status = check_aapl(handle, req, in_context_blobs, out_context_blobs);
6052 if (!NT_STATUS_IS_OK(status)) {
6053 goto fail;
6056 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
6057 return NT_STATUS_UNSUCCESSFUL);
6059 status = SMB_VFS_NEXT_CREATE_FILE(
6060 handle, req, root_dir_fid, smb_fname,
6061 access_mask, share_access,
6062 create_disposition, create_options,
6063 file_attributes, oplock_request,
6064 lease,
6065 allocation_size, private_flags,
6066 sd, ea_list, result,
6067 pinfo, in_context_blobs, out_context_blobs);
6068 if (!NT_STATUS_IS_OK(status)) {
6069 return status;
6072 fsp = *result;
6074 if (global_fruit_config.nego_aapl) {
6075 if (config->posix_rename && fsp->is_directory) {
6077 * Enable POSIX directory rename behaviour
6079 fsp->posix_flags |= FSP_POSIX_FLAGS_RENAME;
6084 * If this is a plain open for existing files, opening an 0
6085 * byte size resource fork MUST fail with
6086 * NT_STATUS_OBJECT_NAME_NOT_FOUND.
6088 * Cf the vfs_fruit torture tests in test_rfork_create().
6090 if (global_fruit_config.nego_aapl &&
6091 create_disposition == FILE_OPEN &&
6092 smb_fname->st.st_ex_size == 0 &&
6093 is_ntfs_stream_smb_fname(smb_fname) &&
6094 !(is_ntfs_default_stream_smb_fname(smb_fname)))
6096 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
6097 goto fail;
6100 fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
6101 if (fio != NULL && pinfo != NULL && *pinfo == FILE_WAS_CREATED) {
6102 fio->created = true;
6105 if (is_ntfs_stream_smb_fname(smb_fname)
6106 || fsp->is_directory) {
6107 return status;
6110 if (config->locking == FRUIT_LOCKING_NETATALK) {
6111 status = fruit_check_access(
6112 handle, *result,
6113 access_mask,
6114 share_access);
6115 if (!NT_STATUS_IS_OK(status)) {
6116 goto fail;
6120 return status;
6122 fail:
6123 DEBUG(10, ("fruit_create_file: %s\n", nt_errstr(status)));
6125 if (fsp) {
6126 close_file(req, fsp, ERROR_CLOSE);
6127 *result = fsp = NULL;
6130 return status;
6133 static NTSTATUS fruit_readdir_attr(struct vfs_handle_struct *handle,
6134 const struct smb_filename *fname,
6135 TALLOC_CTX *mem_ctx,
6136 struct readdir_attr_data **pattr_data)
6138 struct fruit_config_data *config = NULL;
6139 struct readdir_attr_data *attr_data;
6140 NTSTATUS status;
6142 SMB_VFS_HANDLE_GET_DATA(handle, config,
6143 struct fruit_config_data,
6144 return NT_STATUS_UNSUCCESSFUL);
6146 if (!global_fruit_config.nego_aapl) {
6147 return SMB_VFS_NEXT_READDIR_ATTR(handle, fname, mem_ctx, pattr_data);
6150 DEBUG(10, ("fruit_readdir_attr %s\n", fname->base_name));
6152 *pattr_data = talloc_zero(mem_ctx, struct readdir_attr_data);
6153 if (*pattr_data == NULL) {
6154 return NT_STATUS_UNSUCCESSFUL;
6156 attr_data = *pattr_data;
6157 attr_data->type = RDATTR_AAPL;
6160 * Mac metadata: compressed FinderInfo, resource fork length
6161 * and creation date
6163 status = readdir_attr_macmeta(handle, fname, attr_data);
6164 if (!NT_STATUS_IS_OK(status)) {
6166 * Error handling is tricky: if we return failure from
6167 * this function, the corresponding directory entry
6168 * will to be passed to the client, so we really just
6169 * want to error out on fatal errors.
6171 if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
6172 goto fail;
6177 * UNIX mode
6179 if (config->unix_info_enabled) {
6180 attr_data->attr_data.aapl.unix_mode = fname->st.st_ex_mode;
6184 * max_access
6186 if (!config->readdir_attr_max_access) {
6187 attr_data->attr_data.aapl.max_access = FILE_GENERIC_ALL;
6188 } else {
6189 status = smbd_calculate_access_mask(
6190 handle->conn,
6191 fname,
6192 false,
6193 SEC_FLAG_MAXIMUM_ALLOWED,
6194 &attr_data->attr_data.aapl.max_access);
6195 if (!NT_STATUS_IS_OK(status)) {
6196 goto fail;
6200 return NT_STATUS_OK;
6202 fail:
6203 DEBUG(1, ("fruit_readdir_attr %s, error: %s\n",
6204 fname->base_name, nt_errstr(status)));
6205 TALLOC_FREE(*pattr_data);
6206 return status;
6209 static NTSTATUS fruit_fget_nt_acl(vfs_handle_struct *handle,
6210 files_struct *fsp,
6211 uint32_t security_info,
6212 TALLOC_CTX *mem_ctx,
6213 struct security_descriptor **ppdesc)
6215 NTSTATUS status;
6216 struct security_ace ace;
6217 struct dom_sid sid;
6218 struct fruit_config_data *config;
6220 SMB_VFS_HANDLE_GET_DATA(handle, config,
6221 struct fruit_config_data,
6222 return NT_STATUS_UNSUCCESSFUL);
6224 status = SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
6225 mem_ctx, ppdesc);
6226 if (!NT_STATUS_IS_OK(status)) {
6227 return status;
6231 * Add MS NFS style ACEs with uid, gid and mode
6233 if (!global_fruit_config.nego_aapl) {
6234 return NT_STATUS_OK;
6236 if (!config->unix_info_enabled) {
6237 return NT_STATUS_OK;
6240 /* First remove any existing ACE's with NFS style mode/uid/gid SIDs. */
6241 status = remove_virtual_nfs_aces(*ppdesc);
6242 if (!NT_STATUS_IS_OK(status)) {
6243 DBG_WARNING("failed to remove MS NFS style ACEs\n");
6244 return status;
6247 /* MS NFS style mode */
6248 sid_compose(&sid, &global_sid_Unix_NFS_Mode, fsp->fsp_name->st.st_ex_mode);
6249 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6250 status = security_descriptor_dacl_add(*ppdesc, &ace);
6251 if (!NT_STATUS_IS_OK(status)) {
6252 DEBUG(1,("failed to add MS NFS style ACE\n"));
6253 return status;
6256 /* MS NFS style uid */
6257 sid_compose(&sid, &global_sid_Unix_NFS_Users, fsp->fsp_name->st.st_ex_uid);
6258 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6259 status = security_descriptor_dacl_add(*ppdesc, &ace);
6260 if (!NT_STATUS_IS_OK(status)) {
6261 DEBUG(1,("failed to add MS NFS style ACE\n"));
6262 return status;
6265 /* MS NFS style gid */
6266 sid_compose(&sid, &global_sid_Unix_NFS_Groups, fsp->fsp_name->st.st_ex_gid);
6267 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6268 status = security_descriptor_dacl_add(*ppdesc, &ace);
6269 if (!NT_STATUS_IS_OK(status)) {
6270 DEBUG(1,("failed to add MS NFS style ACE\n"));
6271 return status;
6274 return NT_STATUS_OK;
6277 static NTSTATUS fruit_fset_nt_acl(vfs_handle_struct *handle,
6278 files_struct *fsp,
6279 uint32_t security_info_sent,
6280 const struct security_descriptor *orig_psd)
6282 NTSTATUS status;
6283 bool do_chmod;
6284 mode_t ms_nfs_mode = 0;
6285 int result;
6286 struct security_descriptor *psd = NULL;
6287 uint32_t orig_num_aces = 0;
6289 if (orig_psd->dacl != NULL) {
6290 orig_num_aces = orig_psd->dacl->num_aces;
6293 psd = security_descriptor_copy(talloc_tos(), orig_psd);
6294 if (psd == NULL) {
6295 return NT_STATUS_NO_MEMORY;
6298 DBG_DEBUG("fruit_fset_nt_acl: %s\n", fsp_str_dbg(fsp));
6300 status = check_ms_nfs(handle, fsp, psd, &ms_nfs_mode, &do_chmod);
6301 if (!NT_STATUS_IS_OK(status)) {
6302 DEBUG(1, ("fruit_fset_nt_acl: check_ms_nfs failed%s\n", fsp_str_dbg(fsp)));
6303 TALLOC_FREE(psd);
6304 return status;
6308 * If only ms_nfs ACE entries were sent, ensure we set the DACL
6309 * sent/present flags correctly now we've removed them.
6312 if (orig_num_aces != 0) {
6314 * Are there any ACE's left ?
6316 if (psd->dacl->num_aces == 0) {
6317 /* No - clear the DACL sent/present flags. */
6318 security_info_sent &= ~SECINFO_DACL;
6319 psd->type &= ~SEC_DESC_DACL_PRESENT;
6323 status = SMB_VFS_NEXT_FSET_NT_ACL(handle, fsp, security_info_sent, psd);
6324 if (!NT_STATUS_IS_OK(status)) {
6325 DEBUG(1, ("fruit_fset_nt_acl: SMB_VFS_NEXT_FSET_NT_ACL failed%s\n", fsp_str_dbg(fsp)));
6326 TALLOC_FREE(psd);
6327 return status;
6330 if (do_chmod) {
6331 if (fsp->fh->fd != -1) {
6332 result = SMB_VFS_FCHMOD(fsp, ms_nfs_mode);
6333 } else {
6334 result = SMB_VFS_CHMOD(fsp->conn,
6335 fsp->fsp_name,
6336 ms_nfs_mode);
6339 if (result != 0) {
6340 DEBUG(1, ("chmod: %s, result: %d, %04o error %s\n", fsp_str_dbg(fsp),
6341 result, (unsigned)ms_nfs_mode,
6342 strerror(errno)));
6343 status = map_nt_error_from_unix(errno);
6344 TALLOC_FREE(psd);
6345 return status;
6349 TALLOC_FREE(psd);
6350 return NT_STATUS_OK;
6353 static struct vfs_offload_ctx *fruit_offload_ctx;
6355 struct fruit_offload_read_state {
6356 struct vfs_handle_struct *handle;
6357 struct tevent_context *ev;
6358 files_struct *fsp;
6359 uint32_t fsctl;
6360 DATA_BLOB token;
6363 static void fruit_offload_read_done(struct tevent_req *subreq);
6365 static struct tevent_req *fruit_offload_read_send(
6366 TALLOC_CTX *mem_ctx,
6367 struct tevent_context *ev,
6368 struct vfs_handle_struct *handle,
6369 files_struct *fsp,
6370 uint32_t fsctl,
6371 uint32_t ttl,
6372 off_t offset,
6373 size_t to_copy)
6375 struct tevent_req *req = NULL;
6376 struct tevent_req *subreq = NULL;
6377 struct fruit_offload_read_state *state = NULL;
6379 req = tevent_req_create(mem_ctx, &state,
6380 struct fruit_offload_read_state);
6381 if (req == NULL) {
6382 return NULL;
6384 *state = (struct fruit_offload_read_state) {
6385 .handle = handle,
6386 .ev = ev,
6387 .fsp = fsp,
6388 .fsctl = fsctl,
6391 subreq = SMB_VFS_NEXT_OFFLOAD_READ_SEND(mem_ctx, ev, handle, fsp,
6392 fsctl, ttl, offset, to_copy);
6393 if (tevent_req_nomem(subreq, req)) {
6394 return tevent_req_post(req, ev);
6396 tevent_req_set_callback(subreq, fruit_offload_read_done, req);
6397 return req;
6400 static void fruit_offload_read_done(struct tevent_req *subreq)
6402 struct tevent_req *req = tevent_req_callback_data(
6403 subreq, struct tevent_req);
6404 struct fruit_offload_read_state *state = tevent_req_data(
6405 req, struct fruit_offload_read_state);
6406 NTSTATUS status;
6408 status = SMB_VFS_NEXT_OFFLOAD_READ_RECV(subreq,
6409 state->handle,
6410 state,
6411 &state->token);
6412 TALLOC_FREE(subreq);
6413 if (tevent_req_nterror(req, status)) {
6414 return;
6417 if (state->fsctl != FSCTL_SRV_REQUEST_RESUME_KEY) {
6418 tevent_req_done(req);
6419 return;
6422 status = vfs_offload_token_ctx_init(state->fsp->conn->sconn->client,
6423 &fruit_offload_ctx);
6424 if (tevent_req_nterror(req, status)) {
6425 return;
6428 status = vfs_offload_token_db_store_fsp(fruit_offload_ctx,
6429 state->fsp,
6430 &state->token);
6431 if (tevent_req_nterror(req, status)) {
6432 return;
6435 tevent_req_done(req);
6436 return;
6439 static NTSTATUS fruit_offload_read_recv(struct tevent_req *req,
6440 struct vfs_handle_struct *handle,
6441 TALLOC_CTX *mem_ctx,
6442 DATA_BLOB *token)
6444 struct fruit_offload_read_state *state = tevent_req_data(
6445 req, struct fruit_offload_read_state);
6446 NTSTATUS status;
6448 if (tevent_req_is_nterror(req, &status)) {
6449 tevent_req_received(req);
6450 return status;
6453 token->length = state->token.length;
6454 token->data = talloc_move(mem_ctx, &state->token.data);
6456 tevent_req_received(req);
6457 return NT_STATUS_OK;
6460 struct fruit_offload_write_state {
6461 struct vfs_handle_struct *handle;
6462 off_t copied;
6463 struct files_struct *src_fsp;
6464 struct files_struct *dst_fsp;
6465 bool is_copyfile;
6468 static void fruit_offload_write_done(struct tevent_req *subreq);
6469 static struct tevent_req *fruit_offload_write_send(struct vfs_handle_struct *handle,
6470 TALLOC_CTX *mem_ctx,
6471 struct tevent_context *ev,
6472 uint32_t fsctl,
6473 DATA_BLOB *token,
6474 off_t transfer_offset,
6475 struct files_struct *dest_fsp,
6476 off_t dest_off,
6477 off_t num)
6479 struct tevent_req *req, *subreq;
6480 struct fruit_offload_write_state *state;
6481 NTSTATUS status;
6482 struct fruit_config_data *config;
6483 off_t src_off = transfer_offset;
6484 files_struct *src_fsp = NULL;
6485 off_t to_copy = num;
6486 bool copyfile_enabled = false;
6488 DEBUG(10,("soff: %ju, doff: %ju, len: %ju\n",
6489 (uintmax_t)src_off, (uintmax_t)dest_off, (uintmax_t)num));
6491 SMB_VFS_HANDLE_GET_DATA(handle, config,
6492 struct fruit_config_data,
6493 return NULL);
6495 req = tevent_req_create(mem_ctx, &state,
6496 struct fruit_offload_write_state);
6497 if (req == NULL) {
6498 return NULL;
6500 state->handle = handle;
6501 state->dst_fsp = dest_fsp;
6503 switch (fsctl) {
6504 case FSCTL_SRV_COPYCHUNK:
6505 case FSCTL_SRV_COPYCHUNK_WRITE:
6506 copyfile_enabled = config->copyfile_enabled;
6507 break;
6508 default:
6509 break;
6513 * Check if this a OS X copyfile style copychunk request with
6514 * a requested chunk count of 0 that was translated to a
6515 * offload_write_send VFS call overloading the parameters src_off
6516 * = dest_off = num = 0.
6518 if (copyfile_enabled && num == 0 && src_off == 0 && dest_off == 0) {
6519 status = vfs_offload_token_db_fetch_fsp(
6520 fruit_offload_ctx, token, &src_fsp);
6521 if (tevent_req_nterror(req, status)) {
6522 return tevent_req_post(req, ev);
6524 state->src_fsp = src_fsp;
6526 status = vfs_stat_fsp(src_fsp);
6527 if (tevent_req_nterror(req, status)) {
6528 return tevent_req_post(req, ev);
6531 to_copy = src_fsp->fsp_name->st.st_ex_size;
6532 state->is_copyfile = true;
6535 subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle,
6536 mem_ctx,
6538 fsctl,
6539 token,
6540 transfer_offset,
6541 dest_fsp,
6542 dest_off,
6543 to_copy);
6544 if (tevent_req_nomem(subreq, req)) {
6545 return tevent_req_post(req, ev);
6548 tevent_req_set_callback(subreq, fruit_offload_write_done, req);
6549 return req;
6552 static void fruit_offload_write_done(struct tevent_req *subreq)
6554 struct tevent_req *req = tevent_req_callback_data(
6555 subreq, struct tevent_req);
6556 struct fruit_offload_write_state *state = tevent_req_data(
6557 req, struct fruit_offload_write_state);
6558 NTSTATUS status;
6559 unsigned int num_streams = 0;
6560 struct stream_struct *streams = NULL;
6561 unsigned int i;
6562 struct smb_filename *src_fname_tmp = NULL;
6563 struct smb_filename *dst_fname_tmp = NULL;
6565 status = SMB_VFS_NEXT_OFFLOAD_WRITE_RECV(state->handle,
6566 subreq,
6567 &state->copied);
6568 TALLOC_FREE(subreq);
6569 if (tevent_req_nterror(req, status)) {
6570 return;
6573 if (!state->is_copyfile) {
6574 tevent_req_done(req);
6575 return;
6579 * Now copy all remaining streams. We know the share supports
6580 * streams, because we're in vfs_fruit. We don't do this async
6581 * because streams are few and small.
6583 status = vfs_streaminfo(state->handle->conn, state->src_fsp,
6584 state->src_fsp->fsp_name,
6585 req, &num_streams, &streams);
6586 if (tevent_req_nterror(req, status)) {
6587 return;
6590 if (num_streams == 1) {
6591 /* There is always one stream, ::$DATA. */
6592 tevent_req_done(req);
6593 return;
6596 for (i = 0; i < num_streams; i++) {
6597 DEBUG(10, ("%s: stream: '%s'/%zu\n",
6598 __func__, streams[i].name, (size_t)streams[i].size));
6600 src_fname_tmp = synthetic_smb_fname(
6601 req,
6602 state->src_fsp->fsp_name->base_name,
6603 streams[i].name,
6604 NULL,
6605 state->src_fsp->fsp_name->flags);
6606 if (tevent_req_nomem(src_fname_tmp, req)) {
6607 return;
6610 if (is_ntfs_default_stream_smb_fname(src_fname_tmp)) {
6611 TALLOC_FREE(src_fname_tmp);
6612 continue;
6615 dst_fname_tmp = synthetic_smb_fname(
6616 req,
6617 state->dst_fsp->fsp_name->base_name,
6618 streams[i].name,
6619 NULL,
6620 state->dst_fsp->fsp_name->flags);
6621 if (tevent_req_nomem(dst_fname_tmp, req)) {
6622 TALLOC_FREE(src_fname_tmp);
6623 return;
6626 status = copy_file(req,
6627 state->handle->conn,
6628 src_fname_tmp,
6629 dst_fname_tmp,
6630 OPENX_FILE_CREATE_IF_NOT_EXIST,
6631 0, false);
6632 if (!NT_STATUS_IS_OK(status)) {
6633 DEBUG(1, ("%s: copy %s to %s failed: %s\n", __func__,
6634 smb_fname_str_dbg(src_fname_tmp),
6635 smb_fname_str_dbg(dst_fname_tmp),
6636 nt_errstr(status)));
6637 TALLOC_FREE(src_fname_tmp);
6638 TALLOC_FREE(dst_fname_tmp);
6639 tevent_req_nterror(req, status);
6640 return;
6643 TALLOC_FREE(src_fname_tmp);
6644 TALLOC_FREE(dst_fname_tmp);
6647 TALLOC_FREE(streams);
6648 TALLOC_FREE(src_fname_tmp);
6649 TALLOC_FREE(dst_fname_tmp);
6650 tevent_req_done(req);
6653 static NTSTATUS fruit_offload_write_recv(struct vfs_handle_struct *handle,
6654 struct tevent_req *req,
6655 off_t *copied)
6657 struct fruit_offload_write_state *state = tevent_req_data(
6658 req, struct fruit_offload_write_state);
6659 NTSTATUS status;
6661 if (tevent_req_is_nterror(req, &status)) {
6662 DEBUG(1, ("server side copy chunk failed: %s\n",
6663 nt_errstr(status)));
6664 *copied = 0;
6665 tevent_req_received(req);
6666 return status;
6669 *copied = state->copied;
6670 tevent_req_received(req);
6672 return NT_STATUS_OK;
6675 static char *fruit_get_bandsize_line(char **lines, int numlines)
6677 static regex_t re;
6678 static bool re_initialized = false;
6679 int i;
6680 int ret;
6682 if (!re_initialized) {
6683 ret = regcomp(&re, "^[[:blank:]]*<key>band-size</key>$", 0);
6684 if (ret != 0) {
6685 return NULL;
6687 re_initialized = true;
6690 for (i = 0; i < numlines; i++) {
6691 regmatch_t matches[1];
6693 ret = regexec(&re, lines[i], 1, matches, 0);
6694 if (ret == 0) {
6696 * Check if the match was on the last line, sa we want
6697 * the subsequent line.
6699 if (i + 1 == numlines) {
6700 return NULL;
6702 return lines[i + 1];
6704 if (ret != REG_NOMATCH) {
6705 return NULL;
6709 return NULL;
6712 static bool fruit_get_bandsize_from_line(char *line, size_t *_band_size)
6714 static regex_t re;
6715 static bool re_initialized = false;
6716 regmatch_t matches[2];
6717 uint64_t band_size;
6718 int ret;
6719 bool ok;
6721 if (!re_initialized) {
6722 ret = regcomp(&re,
6723 "^[[:blank:]]*"
6724 "<integer>\\([[:digit:]]*\\)</integer>$",
6726 if (ret != 0) {
6727 return false;
6729 re_initialized = true;
6732 ret = regexec(&re, line, 2, matches, 0);
6733 if (ret != 0) {
6734 DBG_ERR("regex failed [%s]\n", line);
6735 return false;
6738 line[matches[1].rm_eo] = '\0';
6740 ok = conv_str_u64(&line[matches[1].rm_so], &band_size);
6741 if (!ok) {
6742 return false;
6744 *_band_size = (size_t)band_size;
6745 return true;
6749 * This reads and parses an Info.plist from a TM sparsebundle looking for the
6750 * "band-size" key and value.
6752 static bool fruit_get_bandsize(vfs_handle_struct *handle,
6753 const char *dir,
6754 size_t *band_size)
6756 #define INFO_PLIST_MAX_SIZE 64*1024
6757 char *plist = NULL;
6758 struct smb_filename *smb_fname = NULL;
6759 files_struct *fsp = NULL;
6760 uint8_t *file_data = NULL;
6761 char **lines = NULL;
6762 char *band_size_line = NULL;
6763 size_t plist_file_size;
6764 ssize_t nread;
6765 int numlines;
6766 int ret;
6767 bool ok = false;
6768 NTSTATUS status;
6770 plist = talloc_asprintf(talloc_tos(),
6771 "%s/%s/Info.plist",
6772 handle->conn->connectpath,
6773 dir);
6774 if (plist == NULL) {
6775 ok = false;
6776 goto out;
6779 smb_fname = synthetic_smb_fname(talloc_tos(), plist, NULL, NULL, 0);
6780 if (smb_fname == NULL) {
6781 ok = false;
6782 goto out;
6785 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
6786 if (ret != 0) {
6787 DBG_INFO("Ignoring Sparsebundle without Info.plist [%s]\n", dir);
6788 ok = true;
6789 goto out;
6792 plist_file_size = smb_fname->st.st_ex_size;
6794 if (plist_file_size > INFO_PLIST_MAX_SIZE) {
6795 DBG_INFO("%s is too large, ignoring\n", plist);
6796 ok = true;
6797 goto out;
6800 status = SMB_VFS_NEXT_CREATE_FILE(
6801 handle, /* conn */
6802 NULL, /* req */
6803 0, /* root_dir_fid */
6804 smb_fname, /* fname */
6805 FILE_GENERIC_READ, /* access_mask */
6806 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
6807 FILE_OPEN, /* create_disposition */
6808 0, /* create_options */
6809 0, /* file_attributes */
6810 INTERNAL_OPEN_ONLY, /* oplock_request */
6811 NULL, /* lease */
6812 0, /* allocation_size */
6813 0, /* private_flags */
6814 NULL, /* sd */
6815 NULL, /* ea_list */
6816 &fsp, /* result */
6817 NULL, /* psbuf */
6818 NULL, NULL); /* create context */
6819 if (!NT_STATUS_IS_OK(status)) {
6820 DBG_INFO("Opening [%s] failed [%s]\n",
6821 smb_fname_str_dbg(smb_fname), nt_errstr(status));
6822 ok = false;
6823 goto out;
6826 file_data = talloc_array(talloc_tos(), uint8_t, plist_file_size);
6827 if (file_data == NULL) {
6828 ok = false;
6829 goto out;
6832 nread = SMB_VFS_NEXT_PREAD(handle, fsp, file_data, plist_file_size, 0);
6833 if (nread != plist_file_size) {
6834 DBG_ERR("Short read on [%s]: %zu/%zd\n",
6835 fsp_str_dbg(fsp), nread, plist_file_size);
6836 ok = false;
6837 goto out;
6841 status = close_file(NULL, fsp, NORMAL_CLOSE);
6842 fsp = NULL;
6843 if (!NT_STATUS_IS_OK(status)) {
6844 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
6845 ok = false;
6846 goto out;
6849 lines = file_lines_parse((char *)file_data,
6850 plist_file_size,
6851 &numlines,
6852 talloc_tos());
6853 if (lines == NULL) {
6854 ok = false;
6855 goto out;
6858 band_size_line = fruit_get_bandsize_line(lines, numlines);
6859 if (band_size_line == NULL) {
6860 DBG_ERR("Didn't find band-size key in [%s]\n",
6861 smb_fname_str_dbg(smb_fname));
6862 ok = false;
6863 goto out;
6866 ok = fruit_get_bandsize_from_line(band_size_line, band_size);
6867 if (!ok) {
6868 DBG_ERR("fruit_get_bandsize_from_line failed\n");
6869 goto out;
6872 DBG_DEBUG("Parsed band-size [%zu] for [%s]\n", *band_size, plist);
6874 out:
6875 if (fsp != NULL) {
6876 status = close_file(NULL, fsp, NORMAL_CLOSE);
6877 if (!NT_STATUS_IS_OK(status)) {
6878 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
6880 fsp = NULL;
6882 TALLOC_FREE(plist);
6883 TALLOC_FREE(smb_fname);
6884 TALLOC_FREE(file_data);
6885 TALLOC_FREE(lines);
6886 return ok;
6889 struct fruit_disk_free_state {
6890 off_t total_size;
6893 static bool fruit_get_num_bands(vfs_handle_struct *handle,
6894 char *bundle,
6895 size_t *_nbands)
6897 char *path = NULL;
6898 struct smb_filename *bands_dir = NULL;
6899 DIR *d = NULL;
6900 struct dirent *e = NULL;
6901 size_t nbands;
6902 int ret;
6904 path = talloc_asprintf(talloc_tos(),
6905 "%s/%s/bands",
6906 handle->conn->connectpath,
6907 bundle);
6908 if (path == NULL) {
6909 return false;
6912 bands_dir = synthetic_smb_fname(talloc_tos(),
6913 path,
6914 NULL,
6915 NULL,
6917 TALLOC_FREE(path);
6918 if (bands_dir == NULL) {
6919 return false;
6922 d = SMB_VFS_NEXT_OPENDIR(handle, bands_dir, NULL, 0);
6923 if (d == NULL) {
6924 TALLOC_FREE(bands_dir);
6925 return false;
6928 nbands = 0;
6930 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
6931 e != NULL;
6932 e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
6934 if (ISDOT(e->d_name) || ISDOTDOT(e->d_name)) {
6935 continue;
6937 nbands++;
6940 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
6941 if (ret != 0) {
6942 TALLOC_FREE(bands_dir);
6943 return false;
6946 DBG_DEBUG("%zu bands in [%s]\n", nbands, smb_fname_str_dbg(bands_dir));
6948 TALLOC_FREE(bands_dir);
6950 *_nbands = nbands;
6951 return true;
6954 static bool fruit_tmsize_do_dirent(vfs_handle_struct *handle,
6955 struct fruit_disk_free_state *state,
6956 struct dirent *e)
6958 bool ok;
6959 char *p = NULL;
6960 size_t sparsebundle_strlen = strlen("sparsebundle");
6961 size_t bandsize = 0;
6962 size_t nbands;
6963 off_t tm_size;
6965 p = strstr(e->d_name, "sparsebundle");
6966 if (p == NULL) {
6967 return true;
6970 if (p[sparsebundle_strlen] != '\0') {
6971 return true;
6974 DBG_DEBUG("Processing sparsebundle [%s]\n", e->d_name);
6976 ok = fruit_get_bandsize(handle, e->d_name, &bandsize);
6977 if (!ok) {
6979 * Beware of race conditions: this may be an uninitialized
6980 * Info.plist that a client is just creating. We don't want let
6981 * this to trigger complete failure.
6983 DBG_ERR("Processing sparsebundle [%s] failed\n", e->d_name);
6984 return true;
6987 ok = fruit_get_num_bands(handle, e->d_name, &nbands);
6988 if (!ok) {
6990 * Beware of race conditions: this may be a backup sparsebundle
6991 * in an early stage lacking a bands subdirectory. We don't want
6992 * let this to trigger complete failure.
6994 DBG_ERR("Processing sparsebundle [%s] failed\n", e->d_name);
6995 return true;
6998 if (bandsize > SIZE_MAX/nbands) {
6999 DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
7000 bandsize, nbands);
7001 return false;
7003 tm_size = bandsize * nbands;
7005 if (state->total_size + tm_size < state->total_size) {
7006 DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
7007 bandsize, nbands);
7008 return false;
7011 state->total_size += tm_size;
7013 DBG_DEBUG("[%s] tm_size [%jd] total_size [%jd]\n",
7014 e->d_name, (intmax_t)tm_size, (intmax_t)state->total_size);
7016 return true;
7020 * Calculate used size of a TimeMachine volume
7022 * This assumes that the volume is used only for TimeMachine.
7024 * - readdir(basedir of share), then
7025 * - for every element that matches regex "^\(.*\)\.sparsebundle$" :
7026 * - parse "\1.sparsebundle/Info.plist" and read the band-size XML key
7027 * - count band files in "\1.sparsebundle/bands/"
7028 * - calculate used size of all bands: band_count * band_size
7030 static uint64_t fruit_disk_free(vfs_handle_struct *handle,
7031 const struct smb_filename *smb_fname,
7032 uint64_t *_bsize,
7033 uint64_t *_dfree,
7034 uint64_t *_dsize)
7036 struct fruit_config_data *config = NULL;
7037 struct fruit_disk_free_state state = {0};
7038 DIR *d = NULL;
7039 struct dirent *e = NULL;
7040 uint64_t dfree;
7041 uint64_t dsize;
7042 int ret;
7043 bool ok;
7045 SMB_VFS_HANDLE_GET_DATA(handle, config,
7046 struct fruit_config_data,
7047 return UINT64_MAX);
7049 if (!config->time_machine ||
7050 config->time_machine_max_size == 0)
7052 return SMB_VFS_NEXT_DISK_FREE(handle,
7053 smb_fname,
7054 _bsize,
7055 _dfree,
7056 _dsize);
7059 d = SMB_VFS_NEXT_OPENDIR(handle, smb_fname, NULL, 0);
7060 if (d == NULL) {
7061 return UINT64_MAX;
7064 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
7065 e != NULL;
7066 e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
7068 ok = fruit_tmsize_do_dirent(handle, &state, e);
7069 if (!ok) {
7070 SMB_VFS_NEXT_CLOSEDIR(handle, d);
7071 return UINT64_MAX;
7075 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
7076 if (ret != 0) {
7077 return UINT64_MAX;
7080 dsize = config->time_machine_max_size / 512;
7081 dfree = dsize - (state.total_size / 512);
7082 if (dfree > dsize) {
7083 dfree = 0;
7086 *_bsize = 512;
7087 *_dsize = dsize;
7088 *_dfree = dfree;
7089 return dfree / 2;
7092 static struct vfs_fn_pointers vfs_fruit_fns = {
7093 .connect_fn = fruit_connect,
7094 .disk_free_fn = fruit_disk_free,
7096 /* File operations */
7097 .chmod_fn = fruit_chmod,
7098 .chown_fn = fruit_chown,
7099 .unlink_fn = fruit_unlink,
7100 .rename_fn = fruit_rename,
7101 .rmdir_fn = fruit_rmdir,
7102 .open_fn = fruit_open,
7103 .close_fn = fruit_close,
7104 .pread_fn = fruit_pread,
7105 .pwrite_fn = fruit_pwrite,
7106 .pread_send_fn = fruit_pread_send,
7107 .pread_recv_fn = fruit_pread_recv,
7108 .pwrite_send_fn = fruit_pwrite_send,
7109 .pwrite_recv_fn = fruit_pwrite_recv,
7110 .stat_fn = fruit_stat,
7111 .lstat_fn = fruit_lstat,
7112 .fstat_fn = fruit_fstat,
7113 .streaminfo_fn = fruit_streaminfo,
7114 .ntimes_fn = fruit_ntimes,
7115 .ftruncate_fn = fruit_ftruncate,
7116 .fallocate_fn = fruit_fallocate,
7117 .create_file_fn = fruit_create_file,
7118 .readdir_attr_fn = fruit_readdir_attr,
7119 .offload_read_send_fn = fruit_offload_read_send,
7120 .offload_read_recv_fn = fruit_offload_read_recv,
7121 .offload_write_send_fn = fruit_offload_write_send,
7122 .offload_write_recv_fn = fruit_offload_write_recv,
7124 /* NT ACL operations */
7125 .fget_nt_acl_fn = fruit_fget_nt_acl,
7126 .fset_nt_acl_fn = fruit_fset_nt_acl,
7129 static_decl_vfs;
7130 NTSTATUS vfs_fruit_init(TALLOC_CTX *ctx)
7132 NTSTATUS ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "fruit",
7133 &vfs_fruit_fns);
7134 if (!NT_STATUS_IS_OK(ret)) {
7135 return ret;
7138 vfs_fruit_debug_level = debug_add_class("fruit");
7139 if (vfs_fruit_debug_level == -1) {
7140 vfs_fruit_debug_level = DBGC_VFS;
7141 DEBUG(0, ("%s: Couldn't register custom debugging class!\n",
7142 "vfs_fruit_init"));
7143 } else {
7144 DEBUG(10, ("%s: Debug class number of '%s': %d\n",
7145 "vfs_fruit_init","fruit",vfs_fruit_debug_level));
7148 return ret;