vfs_fruit: update handling of read-only creation of resource fork
[Samba.git] / source3 / modules / vfs_fruit.c
blobb51eda1146bd509509b44aee4a7ade268d461d6c
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;
520 * Forward declarations
522 static struct adouble *ad_init(TALLOC_CTX *ctx, vfs_handle_struct *handle,
523 adouble_type_t type);
524 static int ad_set(struct adouble *ad, const struct smb_filename *smb_fname);
525 static int ad_fset(struct adouble *ad, files_struct *fsp);
526 static int adouble_path(TALLOC_CTX *ctx,
527 const struct smb_filename *smb_fname__in,
528 struct smb_filename **ppsmb_fname_out);
529 static AfpInfo *afpinfo_new(TALLOC_CTX *ctx);
530 static ssize_t afpinfo_pack(const AfpInfo *ai, char *buf);
531 static AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data);
535 * Return a pointer to an AppleDouble entry
537 * Returns NULL if the entry is not present
539 static char *ad_get_entry(const struct adouble *ad, int eid)
541 off_t off = ad_getentryoff(ad, eid);
542 size_t len = ad_getentrylen(ad, eid);
544 if (off == 0 || len == 0) {
545 return NULL;
548 return ad->ad_data + off;
552 * Get a date
554 static int ad_getdate(const struct adouble *ad,
555 unsigned int dateoff,
556 uint32_t *date)
558 bool xlate = (dateoff & AD_DATE_UNIX);
559 char *p = NULL;
561 dateoff &= AD_DATE_MASK;
562 p = ad_get_entry(ad, ADEID_FILEDATESI);
563 if (p == NULL) {
564 return -1;
567 if (dateoff > AD_DATE_ACCESS) {
568 return -1;
571 memcpy(date, p + dateoff, sizeof(uint32_t));
573 if (xlate) {
574 *date = AD_DATE_TO_UNIX(*date);
576 return 0;
580 * Set a date
582 static int ad_setdate(struct adouble *ad, unsigned int dateoff, uint32_t date)
584 bool xlate = (dateoff & AD_DATE_UNIX);
585 char *p = NULL;
587 p = ad_get_entry(ad, ADEID_FILEDATESI);
588 if (p == NULL) {
589 return -1;
592 dateoff &= AD_DATE_MASK;
593 if (xlate) {
594 date = AD_DATE_FROM_UNIX(date);
597 if (dateoff > AD_DATE_ACCESS) {
598 return -1;
601 memcpy(p + dateoff, &date, sizeof(date));
603 return 0;
608 * Map on-disk AppleDouble id to enumerated id
610 static uint32_t get_eid(uint32_t eid)
612 if (eid <= 15) {
613 return eid;
616 switch (eid) {
617 case AD_DEV:
618 return ADEID_PRIVDEV;
619 case AD_INO:
620 return ADEID_PRIVINO;
621 case AD_SYN:
622 return ADEID_PRIVSYN;
623 case AD_ID:
624 return ADEID_PRIVID;
625 default:
626 break;
629 return 0;
633 * Pack AppleDouble structure into data buffer
635 static bool ad_pack(struct adouble *ad)
637 uint32_t eid;
638 uint16_t nent;
639 uint32_t bufsize;
640 uint32_t offset = 0;
642 bufsize = talloc_get_size(ad->ad_data);
643 if (bufsize < AD_DATASZ_DOT_UND) {
644 DBG_ERR("bad buffer size [0x%" PRIx32 "]\n", bufsize);
645 return false;
648 if (offset + ADEDLEN_MAGIC < offset ||
649 offset + ADEDLEN_MAGIC >= bufsize) {
650 return false;
652 RSIVAL(ad->ad_data, offset, ad->ad_magic);
653 offset += ADEDLEN_MAGIC;
655 if (offset + ADEDLEN_VERSION < offset ||
656 offset + ADEDLEN_VERSION >= bufsize) {
657 return false;
659 RSIVAL(ad->ad_data, offset, ad->ad_version);
660 offset += ADEDLEN_VERSION;
662 if (offset + ADEDLEN_FILLER < offset ||
663 offset + ADEDLEN_FILLER >= bufsize) {
664 return false;
666 if (ad->ad_type == ADOUBLE_RSRC) {
667 memcpy(ad->ad_data + offset, AD_FILLER_TAG, ADEDLEN_FILLER);
669 offset += ADEDLEN_FILLER;
671 if (offset + ADEDLEN_NENTRIES < offset ||
672 offset + ADEDLEN_NENTRIES >= bufsize) {
673 return false;
675 offset += ADEDLEN_NENTRIES;
677 for (eid = 0, nent = 0; eid < ADEID_MAX; eid++) {
678 if (ad->ad_eid[eid].ade_off == 0) {
680 * ade_off is also used as indicator whether a
681 * specific entry is used or not
683 continue;
686 if (offset + AD_ENTRY_LEN_EID < offset ||
687 offset + AD_ENTRY_LEN_EID >= bufsize) {
688 return false;
690 RSIVAL(ad->ad_data, offset, AD_EID_DISK(eid));
691 offset += AD_ENTRY_LEN_EID;
693 if (offset + AD_ENTRY_LEN_OFF < offset ||
694 offset + AD_ENTRY_LEN_OFF >= bufsize) {
695 return false;
697 RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_off);
698 offset += AD_ENTRY_LEN_OFF;
700 if (offset + AD_ENTRY_LEN_LEN < offset ||
701 offset + AD_ENTRY_LEN_LEN >= bufsize) {
702 return false;
704 RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_len);
705 offset += AD_ENTRY_LEN_LEN;
707 nent++;
710 if (ADEDOFF_NENTRIES + 2 >= bufsize) {
711 return false;
713 RSSVAL(ad->ad_data, ADEDOFF_NENTRIES, nent);
715 return true;
718 static bool ad_unpack_xattrs(struct adouble *ad)
720 struct ad_xattr_header *h = &ad->adx_header;
721 const char *p = ad->ad_data;
722 uint32_t hoff;
723 uint32_t i;
725 if (ad_getentrylen(ad, ADEID_FINDERI) <= ADEDLEN_FINDERI) {
726 return true;
729 /* 2 bytes padding */
730 hoff = ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI + 2;
732 h->adx_magic = RIVAL(p, hoff + 0);
733 h->adx_debug_tag = RIVAL(p, hoff + 4); /* Not used -> not checked */
734 h->adx_total_size = RIVAL(p, hoff + 8);
735 h->adx_data_start = RIVAL(p, hoff + 12);
736 h->adx_data_length = RIVAL(p, hoff + 16);
737 h->adx_flags = RSVAL(p, hoff + 32); /* Not used -> not checked */
738 h->adx_num_attrs = RSVAL(p, hoff + 34);
740 if (h->adx_magic != AD_XATTR_HDR_MAGIC) {
741 DBG_ERR("Bad magic: 0x%" PRIx32 "\n", h->adx_magic);
742 return false;
745 if (h->adx_total_size > ad_getentryoff(ad, ADEID_RFORK)) {
746 DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size);
747 return false;
749 if (h->adx_total_size > AD_XATTR_MAX_HDR_SIZE) {
750 DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size);
751 return false;
754 if (h->adx_data_start < (hoff + AD_XATTR_HDR_SIZE)) {
755 DBG_ERR("Bad start: 0x%" PRIx32 "\n", h->adx_data_start);
756 return false;
759 if ((h->adx_data_start + h->adx_data_length) < h->adx_data_start) {
760 DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length);
761 return false;
763 if ((h->adx_data_start + h->adx_data_length) >
764 ad->adx_header.adx_total_size)
766 DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length);
767 return false;
770 if (h->adx_num_attrs > AD_XATTR_MAX_ENTRIES) {
771 DBG_ERR("Bad num xattrs: %" PRIu16 "\n", h->adx_num_attrs);
772 return false;
775 if (h->adx_num_attrs == 0) {
776 return true;
779 ad->adx_entries = talloc_zero_array(
780 ad, struct ad_xattr_entry, h->adx_num_attrs);
781 if (ad->adx_entries == NULL) {
782 return false;
785 hoff += AD_XATTR_HDR_SIZE;
787 for (i = 0; i < h->adx_num_attrs; i++) {
788 struct ad_xattr_entry *e = &ad->adx_entries[i];
790 hoff = (hoff + 3) & ~3;
792 e->adx_offset = RIVAL(p, hoff + 0);
793 e->adx_length = RIVAL(p, hoff + 4);
794 e->adx_flags = RSVAL(p, hoff + 8);
795 e->adx_namelen = *(p + hoff + 10);
797 if (e->adx_offset >= ad->adx_header.adx_total_size) {
798 DBG_ERR("Bad adx_offset: %" PRIx32 "\n",
799 e->adx_offset);
800 return false;
803 if ((e->adx_offset + e->adx_length) < e->adx_offset) {
804 DBG_ERR("Bad adx_length: %" PRIx32 "\n",
805 e->adx_length);
806 return false;
809 if ((e->adx_offset + e->adx_length) >
810 ad->adx_header.adx_total_size)
812 DBG_ERR("Bad adx_length: %" PRIx32 "\n",
813 e->adx_length);
814 return false;
817 if (e->adx_namelen == 0) {
818 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
819 e->adx_namelen);
820 return false;
822 if ((hoff + 11 + e->adx_namelen) < hoff + 11) {
823 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
824 e->adx_namelen);
825 return false;
827 if ((hoff + 11 + e->adx_namelen) >
828 ad->adx_header.adx_data_start)
830 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
831 e->adx_namelen);
832 return false;
835 e->adx_name = talloc_strndup(ad->adx_entries,
836 p + hoff + 11,
837 e->adx_namelen);
838 if (e->adx_name == NULL) {
839 return false;
842 DBG_DEBUG("xattr [%s] offset [0x%x] size [0x%x]\n",
843 e->adx_name, e->adx_offset, e->adx_length);
844 dump_data(10, (uint8_t *)(ad->ad_data + e->adx_offset),
845 e->adx_length);
847 hoff += 11 + e->adx_namelen;
850 return true;
854 * Unpack an AppleDouble blob into a struct adoble
856 static bool ad_unpack(struct adouble *ad, const size_t nentries,
857 size_t filesize)
859 size_t bufsize = talloc_get_size(ad->ad_data);
860 size_t adentries, i;
861 uint32_t eid, len, off;
862 bool ok;
865 * The size of the buffer ad->ad_data is checked when read, so
866 * we wouldn't have to check our own offsets, a few extra
867 * checks won't hurt though. We have to check the offsets we
868 * read from the buffer anyway.
871 if (bufsize < (AD_HEADER_LEN + (AD_ENTRY_LEN * nentries))) {
872 DEBUG(1, ("bad size\n"));
873 return false;
876 ad->ad_magic = RIVAL(ad->ad_data, 0);
877 ad->ad_version = RIVAL(ad->ad_data, ADEDOFF_VERSION);
878 if ((ad->ad_magic != AD_MAGIC) || (ad->ad_version != AD_VERSION)) {
879 DEBUG(1, ("wrong magic or version\n"));
880 return false;
883 memcpy(ad->ad_filler, ad->ad_data + ADEDOFF_FILLER, ADEDLEN_FILLER);
885 adentries = RSVAL(ad->ad_data, ADEDOFF_NENTRIES);
886 if (adentries != nentries) {
887 DEBUG(1, ("invalid number of entries: %zu\n",
888 adentries));
889 return false;
892 /* now, read in the entry bits */
893 for (i = 0; i < adentries; i++) {
894 eid = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN));
895 eid = get_eid(eid);
896 off = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 4);
897 len = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 8);
899 if (!eid || eid >= ADEID_MAX) {
900 DEBUG(1, ("bogus eid %d\n", eid));
901 return false;
905 * All entries other than the resource fork are
906 * expected to be read into the ad_data buffer, so
907 * ensure the specified offset is within that bound
909 if ((off > bufsize) && (eid != ADEID_RFORK)) {
910 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
911 eid, off, len));
912 return false;
916 * All entries besides FinderInfo and resource fork
917 * must fit into the buffer. FinderInfo is special as
918 * it may be larger then the default 32 bytes (if it
919 * contains marshalled xattrs), but we will fixup that
920 * in ad_convert(). And the resource fork is never
921 * accessed directly by the ad_data buf (also see
922 * comment above) anyway.
924 if ((eid != ADEID_RFORK) &&
925 (eid != ADEID_FINDERI) &&
926 ((off + len) > bufsize)) {
927 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
928 eid, off, len));
929 return false;
933 * That would be obviously broken
935 if (off > filesize) {
936 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
937 eid, off, len));
938 return false;
942 * Check for any entry that has its end beyond the
943 * filesize.
945 if (off + len < off) {
946 DEBUG(1, ("offset wrap in eid %d: off: %" PRIu32
947 ", len: %" PRIu32 "\n",
948 eid, off, len));
949 return false;
952 if (off + len > filesize) {
954 * If this is the resource fork entry, we fix
955 * up the length, for any other entry we bail
956 * out.
958 if (eid != ADEID_RFORK) {
959 DEBUG(1, ("bogus eid %d: off: %" PRIu32
960 ", len: %" PRIu32 "\n",
961 eid, off, len));
962 return false;
966 * Fixup the resource fork entry by limiting
967 * the size to entryoffset - filesize.
969 len = filesize - off;
970 DEBUG(1, ("Limiting ADEID_RFORK: off: %" PRIu32
971 ", len: %" PRIu32 "\n", off, len));
974 ad->ad_eid[eid].ade_off = off;
975 ad->ad_eid[eid].ade_len = len;
978 ok = ad_unpack_xattrs(ad);
979 if (!ok) {
980 return false;
983 return true;
986 static bool ad_convert_move_reso(struct adouble *ad,
987 const struct smb_filename *smb_fname)
989 char *map = MAP_FAILED;
990 size_t maplen;
991 ssize_t len;
992 int rc;
993 bool ok;
995 if (ad_getentrylen(ad, ADEID_RFORK) == 0) {
996 return true;
999 maplen = ad_getentryoff(ad, ADEID_RFORK) +
1000 ad_getentrylen(ad, ADEID_RFORK);
1002 /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
1003 map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
1004 ad->ad_fd, 0);
1005 if (map == MAP_FAILED) {
1006 DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
1007 return false;
1011 memmove(map + ADEDOFF_RFORK_DOT_UND,
1012 map + ad_getentryoff(ad, ADEID_RFORK),
1013 ad_getentrylen(ad, ADEID_RFORK));
1015 rc = munmap(map, maplen);
1016 if (rc != 0) {
1017 DBG_ERR("munmap failed: %s\n", strerror(errno));
1018 return false;
1021 ad_setentryoff(ad, ADEID_RFORK, ADEDOFF_RFORK_DOT_UND);
1023 ok = ad_pack(ad);
1024 if (!ok) {
1025 DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name);
1026 return false;
1029 len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
1030 if (len != AD_DATASZ_DOT_UND) {
1031 DBG_ERR("%s: bad size: %zd\n", smb_fname->base_name, len);
1032 return false;
1035 return true;
1038 static bool ad_convert_xattr(struct adouble *ad,
1039 const struct smb_filename *smb_fname,
1040 bool *converted_xattr)
1042 static struct char_mappings **string_replace_cmaps = NULL;
1043 char *map = MAP_FAILED;
1044 size_t maplen;
1045 uint16_t i;
1046 ssize_t len;
1047 int saved_errno = 0;
1048 NTSTATUS status;
1049 int rc;
1050 bool ok;
1052 *converted_xattr = false;
1054 if (ad_getentrylen(ad, ADEID_FINDERI) == ADEDLEN_FINDERI) {
1055 return true;
1058 if (string_replace_cmaps == NULL) {
1059 const char **mappings = NULL;
1061 mappings = str_list_make_v3_const(
1062 talloc_tos(), fruit_catia_maps, NULL);
1063 if (mappings == NULL) {
1064 return false;
1066 string_replace_cmaps = string_replace_init_map(mappings);
1067 TALLOC_FREE(mappings);
1070 maplen = ad_getentryoff(ad, ADEID_RFORK) +
1071 ad_getentrylen(ad, ADEID_RFORK);
1073 /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
1074 map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
1075 ad->ad_fd, 0);
1076 if (map == MAP_FAILED) {
1077 DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
1078 return false;
1081 for (i = 0; i < ad->adx_header.adx_num_attrs; i++) {
1082 struct ad_xattr_entry *e = &ad->adx_entries[i];
1083 char *mapped_name = NULL;
1084 char *tmp = NULL;
1085 struct smb_filename *stream_name = NULL;
1086 files_struct *fsp = NULL;
1087 ssize_t nwritten;
1089 status = string_replace_allocate(ad->ad_handle->conn,
1090 e->adx_name,
1091 string_replace_cmaps,
1092 talloc_tos(),
1093 &mapped_name,
1094 vfs_translate_to_windows);
1095 if (!NT_STATUS_IS_OK(status) &&
1096 !NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))
1098 DBG_ERR("string_replace_allocate failed\n");
1099 ok = false;
1100 goto fail;
1103 tmp = mapped_name;
1104 mapped_name = talloc_asprintf(talloc_tos(), ":%s", tmp);
1105 TALLOC_FREE(tmp);
1106 if (mapped_name == NULL) {
1107 ok = false;
1108 goto fail;
1111 stream_name = synthetic_smb_fname(talloc_tos(),
1112 smb_fname->base_name,
1113 mapped_name,
1114 NULL,
1115 smb_fname->flags);
1116 TALLOC_FREE(mapped_name);
1117 if (stream_name == NULL) {
1118 DBG_ERR("synthetic_smb_fname failed\n");
1119 ok = false;
1120 goto fail;
1123 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1125 status = SMB_VFS_CREATE_FILE(
1126 ad->ad_handle->conn, /* conn */
1127 NULL, /* req */
1128 0, /* root_dir_fid */
1129 stream_name, /* fname */
1130 FILE_GENERIC_WRITE, /* access_mask */
1131 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
1132 FILE_OPEN_IF, /* create_disposition */
1133 0, /* create_options */
1134 0, /* file_attributes */
1135 INTERNAL_OPEN_ONLY, /* oplock_request */
1136 NULL, /* lease */
1137 0, /* allocation_size */
1138 0, /* private_flags */
1139 NULL, /* sd */
1140 NULL, /* ea_list */
1141 &fsp, /* result */
1142 NULL, /* psbuf */
1143 NULL, NULL); /* create context */
1144 TALLOC_FREE(stream_name);
1145 if (!NT_STATUS_IS_OK(status)) {
1146 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1147 ok = false;
1148 goto fail;
1151 nwritten = SMB_VFS_PWRITE(fsp,
1152 map + e->adx_offset,
1153 e->adx_length,
1155 if (nwritten == -1) {
1156 DBG_ERR("SMB_VFS_PWRITE failed\n");
1157 saved_errno = errno;
1158 close_file(NULL, fsp, ERROR_CLOSE);
1159 errno = saved_errno;
1160 ok = false;
1161 goto fail;
1164 status = close_file(NULL, fsp, NORMAL_CLOSE);
1165 if (!NT_STATUS_IS_OK(status)) {
1166 ok = false;
1167 goto fail;
1169 fsp = NULL;
1172 ad_setentrylen(ad, ADEID_FINDERI, ADEDLEN_FINDERI);
1174 ok = ad_pack(ad);
1175 if (!ok) {
1176 DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name);
1177 goto fail;
1180 len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
1181 if (len != AD_DATASZ_DOT_UND) {
1182 DBG_ERR("%s: bad size: %zd\n", smb_fname->base_name, len);
1183 ok = false;
1184 goto fail;
1187 ok = ad_convert_move_reso(ad, smb_fname);
1188 if (!ok) {
1189 goto fail;
1192 *converted_xattr = true;
1193 ok = true;
1195 fail:
1196 rc = munmap(map, maplen);
1197 if (rc != 0) {
1198 DBG_ERR("munmap failed: %s\n", strerror(errno));
1199 return false;
1202 return ok;
1205 static bool ad_convert_finderinfo(struct adouble *ad,
1206 const struct smb_filename *smb_fname)
1208 char *p_ad = NULL;
1209 AfpInfo *ai = NULL;
1210 DATA_BLOB aiblob;
1211 struct smb_filename *stream_name = NULL;
1212 files_struct *fsp = NULL;
1213 size_t size;
1214 ssize_t nwritten;
1215 NTSTATUS status;
1216 int saved_errno = 0;
1217 int cmp;
1219 cmp = memcmp(ad->ad_filler, AD_FILLER_TAG_OSX, ADEDLEN_FILLER);
1220 if (cmp != 0) {
1221 return true;
1224 p_ad = ad_get_entry(ad, ADEID_FINDERI);
1225 if (p_ad == NULL) {
1226 return false;
1229 ai = afpinfo_new(talloc_tos());
1230 if (ai == NULL) {
1231 return false;
1234 memcpy(ai->afpi_FinderInfo, p_ad, ADEDLEN_FINDERI);
1236 aiblob = data_blob_talloc(talloc_tos(), NULL, AFP_INFO_SIZE);
1237 if (aiblob.data == NULL) {
1238 TALLOC_FREE(ai);
1239 return false;
1242 size = afpinfo_pack(ai, (char *)aiblob.data);
1243 TALLOC_FREE(ai);
1244 if (size != AFP_INFO_SIZE) {
1245 return false;
1248 stream_name = synthetic_smb_fname(talloc_tos(),
1249 smb_fname->base_name,
1250 AFPINFO_STREAM,
1251 NULL,
1252 smb_fname->flags);
1253 if (stream_name == NULL) {
1254 data_blob_free(&aiblob);
1255 DBG_ERR("synthetic_smb_fname failed\n");
1256 return false;
1259 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1261 status = SMB_VFS_CREATE_FILE(
1262 ad->ad_handle->conn, /* conn */
1263 NULL, /* req */
1264 0, /* root_dir_fid */
1265 stream_name, /* fname */
1266 FILE_GENERIC_WRITE, /* access_mask */
1267 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
1268 FILE_OPEN_IF, /* create_disposition */
1269 0, /* create_options */
1270 0, /* file_attributes */
1271 INTERNAL_OPEN_ONLY, /* oplock_request */
1272 NULL, /* lease */
1273 0, /* allocation_size */
1274 0, /* private_flags */
1275 NULL, /* sd */
1276 NULL, /* ea_list */
1277 &fsp, /* result */
1278 NULL, /* psbuf */
1279 NULL, NULL); /* create context */
1280 TALLOC_FREE(stream_name);
1281 if (!NT_STATUS_IS_OK(status)) {
1282 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1283 return false;
1286 nwritten = SMB_VFS_PWRITE(fsp,
1287 aiblob.data,
1288 aiblob.length,
1290 if (nwritten == -1) {
1291 DBG_ERR("SMB_VFS_PWRITE failed\n");
1292 saved_errno = errno;
1293 close_file(NULL, fsp, ERROR_CLOSE);
1294 errno = saved_errno;
1295 return false;
1298 status = close_file(NULL, fsp, NORMAL_CLOSE);
1299 if (!NT_STATUS_IS_OK(status)) {
1300 return false;
1302 fsp = NULL;
1304 return true;
1307 static bool ad_convert_truncate(struct adouble *ad,
1308 const struct smb_filename *smb_fname)
1310 int rc;
1313 * FIXME: direct ftruncate(), but we don't have a fsp for the
1314 * VFS call
1316 rc = ftruncate(ad->ad_fd, ADEDOFF_RFORK_DOT_UND +
1317 ad_getentrylen(ad, ADEID_RFORK));
1318 if (rc != 0) {
1319 return false;
1322 return true;
1325 static bool ad_convert_blank_rfork(struct adouble *ad,
1326 bool *blank)
1328 struct fruit_config_data *config = NULL;
1329 uint8_t *map = MAP_FAILED;
1330 size_t maplen;
1331 int cmp;
1332 ssize_t len;
1333 int rc;
1334 bool ok;
1336 *blank = false;
1338 SMB_VFS_HANDLE_GET_DATA(ad->ad_handle, config,
1339 struct fruit_config_data, return false);
1341 if (!config->wipe_intentionally_left_blank_rfork) {
1342 return true;
1345 if (ad_getentrylen(ad, ADEID_RFORK) != sizeof(empty_resourcefork)) {
1346 return true;
1349 maplen = ad_getentryoff(ad, ADEID_RFORK) +
1350 ad_getentrylen(ad, ADEID_RFORK);
1352 /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
1353 map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
1354 ad->ad_fd, 0);
1355 if (map == MAP_FAILED) {
1356 DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
1357 return false;
1360 cmp = memcmp(map + ADEDOFF_RFORK_DOT_UND,
1361 empty_resourcefork,
1362 sizeof(empty_resourcefork));
1363 rc = munmap(map, maplen);
1364 if (rc != 0) {
1365 DBG_ERR("munmap failed: %s\n", strerror(errno));
1366 return false;
1369 if (cmp != 0) {
1370 return true;
1373 ad_setentrylen(ad, ADEID_RFORK, 0);
1375 ok = ad_pack(ad);
1376 if (!ok) {
1377 return false;
1380 len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
1381 if (len != AD_DATASZ_DOT_UND) {
1382 return false;
1385 *blank = true;
1386 return true;
1389 static bool ad_convert_delete_adfile(struct adouble *ad,
1390 const struct smb_filename *smb_fname)
1392 struct fruit_config_data *config = NULL;
1393 struct smb_filename *ad_name = NULL;
1394 int rc;
1396 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
1397 return true;
1400 SMB_VFS_HANDLE_GET_DATA(ad->ad_handle, config,
1401 struct fruit_config_data, return false);
1403 if (!config->delete_empty_adfiles) {
1404 return true;
1407 rc = adouble_path(talloc_tos(), smb_fname, &ad_name);
1408 if (rc != 0) {
1409 return false;
1412 rc = SMB_VFS_NEXT_UNLINK(ad->ad_handle, ad_name);
1413 if (rc != 0) {
1414 DBG_ERR("Unlinking [%s] failed: %s\n",
1415 smb_fname_str_dbg(ad_name), strerror(errno));
1416 TALLOC_FREE(ad_name);
1417 return false;
1420 DBG_WARNING("Unlinked [%s] after conversion\n", smb_fname_str_dbg(ad_name));
1421 TALLOC_FREE(ad_name);
1423 return true;
1427 * Convert from Apple's ._ file to Netatalk
1429 * Apple's AppleDouble may contain a FinderInfo entry longer then 32
1430 * bytes containing packed xattrs.
1432 * @return -1 in case an error occurred, 0 if no conversion was done, 1
1433 * otherwise
1435 static int ad_convert(struct adouble *ad,
1436 const struct smb_filename *smb_fname)
1438 bool ok;
1439 bool converted_xattr = false;
1440 bool blank;
1442 ok = ad_convert_xattr(ad, smb_fname, &converted_xattr);
1443 if (!ok) {
1444 return -1;
1447 ok = ad_convert_blank_rfork(ad, &blank);
1448 if (!ok) {
1449 return -1;
1452 if (converted_xattr || blank) {
1453 ok = ad_convert_truncate(ad, smb_fname);
1454 if (!ok) {
1455 return -1;
1459 ok = ad_convert_finderinfo(ad, smb_fname);
1460 if (!ok) {
1461 DBG_ERR("Failed to convert [%s]\n",
1462 smb_fname_str_dbg(smb_fname));
1463 return -1;
1466 ok = ad_convert_delete_adfile(ad, smb_fname);
1467 if (!ok) {
1468 return -1;
1471 return 0;
1475 * Read and parse Netatalk AppleDouble metadata xattr
1477 static ssize_t ad_read_meta(struct adouble *ad,
1478 const struct smb_filename *smb_fname)
1480 int rc = 0;
1481 ssize_t ealen;
1482 bool ok;
1484 DEBUG(10, ("reading meta xattr for %s\n", smb_fname->base_name));
1486 ealen = SMB_VFS_GETXATTR(ad->ad_handle->conn, smb_fname,
1487 AFPINFO_EA_NETATALK, ad->ad_data,
1488 AD_DATASZ_XATTR);
1489 if (ealen == -1) {
1490 switch (errno) {
1491 case ENOATTR:
1492 case ENOENT:
1493 if (errno == ENOATTR) {
1494 errno = ENOENT;
1496 rc = -1;
1497 goto exit;
1498 default:
1499 DEBUG(2, ("error reading meta xattr: %s\n",
1500 strerror(errno)));
1501 rc = -1;
1502 goto exit;
1505 if (ealen != AD_DATASZ_XATTR) {
1506 DEBUG(2, ("bad size %zd\n", ealen));
1507 errno = EINVAL;
1508 rc = -1;
1509 goto exit;
1512 /* Now parse entries */
1513 ok = ad_unpack(ad, ADEID_NUM_XATTR, AD_DATASZ_XATTR);
1514 if (!ok) {
1515 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1516 errno = EINVAL;
1517 rc = -1;
1518 goto exit;
1521 if (!ad_getentryoff(ad, ADEID_FINDERI)
1522 || !ad_getentryoff(ad, ADEID_COMMENT)
1523 || !ad_getentryoff(ad, ADEID_FILEDATESI)
1524 || !ad_getentryoff(ad, ADEID_AFPFILEI)
1525 || !ad_getentryoff(ad, ADEID_PRIVDEV)
1526 || !ad_getentryoff(ad, ADEID_PRIVINO)
1527 || !ad_getentryoff(ad, ADEID_PRIVSYN)
1528 || !ad_getentryoff(ad, ADEID_PRIVID)) {
1529 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1530 errno = EINVAL;
1531 rc = -1;
1532 goto exit;
1535 exit:
1536 DEBUG(10, ("reading meta xattr for %s, rc: %d\n",
1537 smb_fname->base_name, rc));
1539 if (rc != 0) {
1540 ealen = -1;
1541 if (errno == EINVAL) {
1542 become_root();
1543 removexattr(smb_fname->base_name, AFPINFO_EA_NETATALK);
1544 unbecome_root();
1545 errno = ENOENT;
1548 return ealen;
1551 static int ad_open_rsrc_xattr(const struct smb_filename *smb_fname,
1552 int flags,
1553 mode_t mode)
1555 #ifdef HAVE_ATTROPEN
1556 /* FIXME: direct Solaris xattr syscall */
1557 return attropen(smb_fname->base_name,
1558 AFPRESOURCE_EA_NETATALK, flags, mode);
1559 #else
1560 errno = ENOSYS;
1561 return -1;
1562 #endif
1565 static int ad_open_rsrc_adouble(const struct smb_filename *smb_fname,
1566 int flags,
1567 mode_t mode)
1569 int ret;
1570 int fd;
1571 struct smb_filename *adp_smb_fname = NULL;
1573 ret = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
1574 if (ret != 0) {
1575 return -1;
1578 fd = open(adp_smb_fname->base_name, flags, mode);
1579 TALLOC_FREE(adp_smb_fname);
1581 return fd;
1584 static int ad_open_rsrc(vfs_handle_struct *handle,
1585 const struct smb_filename *smb_fname,
1586 int flags,
1587 mode_t mode)
1589 struct fruit_config_data *config = NULL;
1590 int fd;
1592 SMB_VFS_HANDLE_GET_DATA(handle, config,
1593 struct fruit_config_data, return -1);
1595 if (config->rsrc == FRUIT_RSRC_XATTR) {
1596 fd = ad_open_rsrc_xattr(smb_fname, flags, mode);
1597 } else {
1598 fd = ad_open_rsrc_adouble(smb_fname, flags, mode);
1601 return fd;
1605 * Here's the deal: for ADOUBLE_META we can do without an fd as we can issue
1606 * path based xattr calls. For ADOUBLE_RSRC however we need a full-fledged fd
1607 * for file IO on the ._ file.
1609 static int ad_open(vfs_handle_struct *handle,
1610 struct adouble *ad,
1611 files_struct *fsp,
1612 const struct smb_filename *smb_fname,
1613 int flags,
1614 mode_t mode)
1616 int fd;
1618 DBG_DEBUG("Path [%s] type [%s]\n", smb_fname->base_name,
1619 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
1621 if (ad->ad_type == ADOUBLE_META) {
1622 return 0;
1625 if ((fsp != NULL) && (fsp->fh != NULL) && (fsp->fh->fd != -1)) {
1626 ad->ad_fd = fsp->fh->fd;
1627 ad->ad_opened = false;
1628 return 0;
1631 fd = ad_open_rsrc(handle, smb_fname, flags, mode);
1632 if (fd == -1) {
1633 return -1;
1635 ad->ad_opened = true;
1636 ad->ad_fd = fd;
1638 DBG_DEBUG("Path [%s] type [%s] fd [%d]\n",
1639 smb_fname->base_name,
1640 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc", fd);
1642 return 0;
1645 static ssize_t ad_read_rsrc_xattr(struct adouble *ad)
1647 int ret;
1648 SMB_STRUCT_STAT st;
1650 /* FIXME: direct sys_fstat(), don't have an fsp */
1651 ret = sys_fstat(ad->ad_fd, &st,
1652 lp_fake_directory_create_times(
1653 SNUM(ad->ad_handle->conn)));
1654 if (ret != 0) {
1655 return -1;
1658 ad_setentrylen(ad, ADEID_RFORK, st.st_ex_size);
1659 return st.st_ex_size;
1662 static ssize_t ad_read_rsrc_adouble(struct adouble *ad,
1663 const struct smb_filename *smb_fname)
1665 SMB_STRUCT_STAT sbuf;
1666 char *p_ad = NULL;
1667 size_t size;
1668 ssize_t len;
1669 int ret;
1670 bool ok;
1672 ret = sys_fstat(ad->ad_fd, &sbuf, lp_fake_directory_create_times(
1673 SNUM(ad->ad_handle->conn)));
1674 if (ret != 0) {
1675 return -1;
1679 * AppleDouble file header content and size, two cases:
1681 * - without xattrs it is exactly AD_DATASZ_DOT_UND (82) bytes large
1682 * - with embedded xattrs it can be larger, up to AD_XATTR_MAX_HDR_SIZE
1684 * Read as much as we can up to AD_XATTR_MAX_HDR_SIZE.
1686 size = sbuf.st_ex_size;
1687 if (size > talloc_array_length(ad->ad_data)) {
1688 if (size > AD_XATTR_MAX_HDR_SIZE) {
1689 size = AD_XATTR_MAX_HDR_SIZE;
1691 p_ad = talloc_realloc(ad, ad->ad_data, char, size);
1692 if (p_ad == NULL) {
1693 return -1;
1695 ad->ad_data = p_ad;
1698 len = sys_pread(ad->ad_fd, ad->ad_data,
1699 talloc_array_length(ad->ad_data), 0);
1700 if (len != talloc_array_length(ad->ad_data)) {
1701 DBG_NOTICE("%s %s: bad size: %zd\n",
1702 smb_fname->base_name, strerror(errno), len);
1703 return -1;
1706 /* Now parse entries */
1707 ok = ad_unpack(ad, ADEID_NUM_DOT_UND, sbuf.st_ex_size);
1708 if (!ok) {
1709 DBG_ERR("invalid AppleDouble resource %s\n",
1710 smb_fname->base_name);
1711 errno = EINVAL;
1712 return -1;
1715 if ((ad_getentryoff(ad, ADEID_FINDERI) != ADEDOFF_FINDERI_DOT_UND)
1716 || (ad_getentrylen(ad, ADEID_FINDERI) < ADEDLEN_FINDERI)
1717 || (ad_getentryoff(ad, ADEID_RFORK) < ADEDOFF_RFORK_DOT_UND)) {
1718 DBG_ERR("invalid AppleDouble resource %s\n",
1719 smb_fname->base_name);
1720 errno = EINVAL;
1721 return -1;
1725 * Try to fixup AppleDouble files created by OS X with xattrs
1726 * appended to the ADEID_FINDERI entry.
1729 ret = ad_convert(ad, smb_fname);
1730 if (ret != 0) {
1731 DBG_WARNING("Failed to convert [%s]\n", smb_fname->base_name);
1732 return len;
1735 return len;
1739 * Read and parse resource fork, either ._ AppleDouble file or xattr
1741 static ssize_t ad_read_rsrc(struct adouble *ad,
1742 const struct smb_filename *smb_fname)
1744 struct fruit_config_data *config = NULL;
1745 ssize_t len;
1747 SMB_VFS_HANDLE_GET_DATA(ad->ad_handle, config,
1748 struct fruit_config_data, return -1);
1750 if (config->rsrc == FRUIT_RSRC_XATTR) {
1751 len = ad_read_rsrc_xattr(ad);
1752 } else {
1753 len = ad_read_rsrc_adouble(ad, smb_fname);
1756 return len;
1760 * Read and unpack an AppleDouble metadata xattr or resource
1762 static ssize_t ad_read(struct adouble *ad, const struct smb_filename *smb_fname)
1764 switch (ad->ad_type) {
1765 case ADOUBLE_META:
1766 return ad_read_meta(ad, smb_fname);
1767 case ADOUBLE_RSRC:
1768 return ad_read_rsrc(ad, smb_fname);
1769 default:
1770 return -1;
1774 static int adouble_destructor(struct adouble *ad)
1776 if ((ad->ad_fd != -1) && ad->ad_opened) {
1777 close(ad->ad_fd);
1778 ad->ad_fd = -1;
1780 return 0;
1784 * Allocate a struct adouble without initialiing it
1786 * The struct is either hang of the fsp extension context or if fsp is
1787 * NULL from ctx.
1789 * @param[in] ctx talloc context
1790 * @param[in] handle vfs handle
1791 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1793 * @return adouble handle
1795 static struct adouble *ad_alloc(TALLOC_CTX *ctx, vfs_handle_struct *handle,
1796 adouble_type_t type)
1798 int rc = 0;
1799 size_t adsize = 0;
1800 struct adouble *ad;
1801 struct fruit_config_data *config;
1803 SMB_VFS_HANDLE_GET_DATA(handle, config,
1804 struct fruit_config_data, return NULL);
1806 switch (type) {
1807 case ADOUBLE_META:
1808 adsize = AD_DATASZ_XATTR;
1809 break;
1810 case ADOUBLE_RSRC:
1811 if (config->rsrc == FRUIT_RSRC_ADFILE) {
1812 adsize = AD_DATASZ_DOT_UND;
1814 break;
1815 default:
1816 return NULL;
1819 ad = talloc_zero(ctx, struct adouble);
1820 if (ad == NULL) {
1821 rc = -1;
1822 goto exit;
1825 if (adsize) {
1826 ad->ad_data = talloc_zero_array(ad, char, adsize);
1827 if (ad->ad_data == NULL) {
1828 rc = -1;
1829 goto exit;
1833 ad->ad_handle = handle;
1834 ad->ad_type = type;
1835 ad->ad_magic = AD_MAGIC;
1836 ad->ad_version = AD_VERSION;
1837 ad->ad_fd = -1;
1839 talloc_set_destructor(ad, adouble_destructor);
1841 exit:
1842 if (rc != 0) {
1843 TALLOC_FREE(ad);
1845 return ad;
1849 * Allocate and initialize a new struct adouble
1851 * @param[in] ctx talloc context
1852 * @param[in] handle vfs handle
1853 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1855 * @return adouble handle, initialized
1857 static struct adouble *ad_init(TALLOC_CTX *ctx, vfs_handle_struct *handle,
1858 adouble_type_t type)
1860 int rc = 0;
1861 const struct ad_entry_order *eid;
1862 struct adouble *ad = NULL;
1863 struct fruit_config_data *config;
1864 time_t t = time(NULL);
1866 SMB_VFS_HANDLE_GET_DATA(handle, config,
1867 struct fruit_config_data, return NULL);
1869 switch (type) {
1870 case ADOUBLE_META:
1871 eid = entry_order_meta_xattr;
1872 break;
1873 case ADOUBLE_RSRC:
1874 if (config->rsrc == FRUIT_RSRC_ADFILE) {
1875 eid = entry_order_dot_und;
1876 } else {
1877 eid = entry_order_rsrc_xattr;
1879 break;
1880 default:
1881 return NULL;
1884 ad = ad_alloc(ctx, handle, type);
1885 if (ad == NULL) {
1886 return NULL;
1889 while (eid->id) {
1890 ad->ad_eid[eid->id].ade_off = eid->offset;
1891 ad->ad_eid[eid->id].ade_len = eid->len;
1892 eid++;
1895 /* put something sane in the date fields */
1896 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, t);
1897 ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, t);
1898 ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, t);
1899 ad_setdate(ad, AD_DATE_BACKUP, htonl(AD_DATE_START));
1901 if (rc != 0) {
1902 TALLOC_FREE(ad);
1904 return ad;
1907 static struct adouble *ad_get_internal(TALLOC_CTX *ctx,
1908 vfs_handle_struct *handle,
1909 files_struct *fsp,
1910 const struct smb_filename *smb_fname,
1911 adouble_type_t type)
1913 int rc = 0;
1914 ssize_t len;
1915 struct adouble *ad = NULL;
1916 int mode;
1918 if (fsp != NULL) {
1919 smb_fname = fsp->base_fsp->fsp_name;
1922 DEBUG(10, ("ad_get(%s) called for %s\n",
1923 type == ADOUBLE_META ? "meta" : "rsrc",
1924 smb_fname->base_name));
1926 ad = ad_alloc(ctx, handle, type);
1927 if (ad == NULL) {
1928 rc = -1;
1929 goto exit;
1932 /* Try rw first so we can use the fd in ad_convert() */
1933 mode = O_RDWR;
1935 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
1936 if (rc == -1 && ((errno == EROFS) || (errno == EACCES))) {
1937 mode = O_RDONLY;
1938 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
1940 if (rc == -1) {
1941 DBG_DEBUG("ad_open [%s] error [%s]\n",
1942 smb_fname->base_name, strerror(errno));
1943 goto exit;
1947 len = ad_read(ad, smb_fname);
1948 if (len == -1) {
1949 DEBUG(10, ("error reading AppleDouble for %s\n",
1950 smb_fname->base_name));
1951 rc = -1;
1952 goto exit;
1955 exit:
1956 DEBUG(10, ("ad_get(%s) for %s returning %d\n",
1957 type == ADOUBLE_META ? "meta" : "rsrc",
1958 smb_fname->base_name, rc));
1960 if (rc != 0) {
1961 TALLOC_FREE(ad);
1963 return ad;
1967 * Return AppleDouble data for a file
1969 * @param[in] ctx talloc context
1970 * @param[in] handle vfs handle
1971 * @param[in] smb_fname pathname to file or directory
1972 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1974 * @return talloced struct adouble or NULL on error
1976 static struct adouble *ad_get(TALLOC_CTX *ctx,
1977 vfs_handle_struct *handle,
1978 const struct smb_filename *smb_fname,
1979 adouble_type_t type)
1981 return ad_get_internal(ctx, handle, NULL, smb_fname, type);
1985 * Return AppleDouble data for a file
1987 * @param[in] ctx talloc context
1988 * @param[in] handle vfs handle
1989 * @param[in] fsp fsp to use for IO
1990 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1992 * @return talloced struct adouble or NULL on error
1994 static struct adouble *ad_fget(TALLOC_CTX *ctx, vfs_handle_struct *handle,
1995 files_struct *fsp, adouble_type_t type)
1997 return ad_get_internal(ctx, handle, fsp, NULL, type);
2001 * Set AppleDouble metadata on a file or directory
2003 * @param[in] ad adouble handle
2005 * @param[in] smb_fname pathname to file or directory
2007 * @return status code, 0 means success
2009 static int ad_set(struct adouble *ad, const struct smb_filename *smb_fname)
2011 bool ok;
2012 int ret;
2014 DBG_DEBUG("Path [%s]\n", smb_fname->base_name);
2016 if (ad->ad_type != ADOUBLE_META) {
2017 DBG_ERR("ad_set on [%s] used with ADOUBLE_RSRC\n",
2018 smb_fname->base_name);
2019 return -1;
2022 ok = ad_pack(ad);
2023 if (!ok) {
2024 return -1;
2027 ret = SMB_VFS_SETXATTR(ad->ad_handle->conn,
2028 smb_fname,
2029 AFPINFO_EA_NETATALK,
2030 ad->ad_data,
2031 AD_DATASZ_XATTR, 0);
2033 DBG_DEBUG("Path [%s] ret [%d]\n", smb_fname->base_name, ret);
2035 return ret;
2039 * Set AppleDouble metadata on a file or directory
2041 * @param[in] ad adouble handle
2042 * @param[in] fsp file handle
2044 * @return status code, 0 means success
2046 static int ad_fset(struct adouble *ad, files_struct *fsp)
2048 int rc = -1;
2049 ssize_t len;
2050 bool ok;
2052 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
2054 if ((fsp == NULL)
2055 || (fsp->fh == NULL)
2056 || (fsp->fh->fd == -1))
2058 smb_panic("bad fsp");
2061 ok = ad_pack(ad);
2062 if (!ok) {
2063 return -1;
2066 switch (ad->ad_type) {
2067 case ADOUBLE_META:
2068 rc = SMB_VFS_NEXT_SETXATTR(ad->ad_handle,
2069 fsp->fsp_name,
2070 AFPINFO_EA_NETATALK,
2071 ad->ad_data,
2072 AD_DATASZ_XATTR, 0);
2073 break;
2075 case ADOUBLE_RSRC:
2076 len = SMB_VFS_NEXT_PWRITE(ad->ad_handle,
2077 fsp,
2078 ad->ad_data,
2079 AD_DATASZ_DOT_UND,
2081 if (len != AD_DATASZ_DOT_UND) {
2082 DBG_ERR("short write on %s: %zd", fsp_str_dbg(fsp), len);
2083 return -1;
2085 rc = 0;
2086 break;
2088 default:
2089 return -1;
2092 DBG_DEBUG("Path [%s] rc [%d]\n", fsp_str_dbg(fsp), rc);
2094 return rc;
2097 /*****************************************************************************
2098 * Helper functions
2099 *****************************************************************************/
2101 static bool is_afpinfo_stream(const struct smb_filename *smb_fname)
2103 if (strncasecmp_m(smb_fname->stream_name,
2104 AFPINFO_STREAM_NAME,
2105 strlen(AFPINFO_STREAM_NAME)) == 0) {
2106 return true;
2108 return false;
2111 static bool is_afpresource_stream(const struct smb_filename *smb_fname)
2113 if (strncasecmp_m(smb_fname->stream_name,
2114 AFPRESOURCE_STREAM_NAME,
2115 strlen(AFPRESOURCE_STREAM_NAME)) == 0) {
2116 return true;
2118 return false;
2122 * Test whether stream is an Apple stream, not used atm
2124 #if 0
2125 static bool is_apple_stream(const struct smb_filename *smb_fname)
2127 if (is_afpinfo_stream(smb_fname)) {
2128 return true;
2130 if (is_afpresource_stream(smb_fname)) {
2131 return true;
2133 return false;
2135 #endif
2138 * Initialize config struct from our smb.conf config parameters
2140 static int init_fruit_config(vfs_handle_struct *handle)
2142 struct fruit_config_data *config;
2143 int enumval;
2144 const char *tm_size_str = NULL;
2146 config = talloc_zero(handle->conn, struct fruit_config_data);
2147 if (!config) {
2148 DEBUG(1, ("talloc_zero() failed\n"));
2149 errno = ENOMEM;
2150 return -1;
2154 * Versions up to Samba 4.5.x had a spelling bug in the
2155 * fruit:resource option calling lp_parm_enum with
2156 * "res*s*ource" (ie two s).
2158 * In Samba 4.6 we accept both the wrong and the correct
2159 * spelling, in Samba 4.7 the bad spelling will be removed.
2161 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2162 "ressource", fruit_rsrc, FRUIT_RSRC_ADFILE);
2163 if (enumval == -1) {
2164 DEBUG(1, ("value for %s: resource type unknown\n",
2165 FRUIT_PARAM_TYPE_NAME));
2166 return -1;
2168 config->rsrc = (enum fruit_rsrc)enumval;
2170 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2171 "resource", fruit_rsrc, enumval);
2172 if (enumval == -1) {
2173 DEBUG(1, ("value for %s: resource type unknown\n",
2174 FRUIT_PARAM_TYPE_NAME));
2175 return -1;
2177 config->rsrc = (enum fruit_rsrc)enumval;
2179 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2180 "metadata", fruit_meta, FRUIT_META_NETATALK);
2181 if (enumval == -1) {
2182 DEBUG(1, ("value for %s: metadata type unknown\n",
2183 FRUIT_PARAM_TYPE_NAME));
2184 return -1;
2186 config->meta = (enum fruit_meta)enumval;
2188 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2189 "locking", fruit_locking, FRUIT_LOCKING_NONE);
2190 if (enumval == -1) {
2191 DEBUG(1, ("value for %s: locking type unknown\n",
2192 FRUIT_PARAM_TYPE_NAME));
2193 return -1;
2195 config->locking = (enum fruit_locking)enumval;
2197 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2198 "encoding", fruit_encoding, FRUIT_ENC_PRIVATE);
2199 if (enumval == -1) {
2200 DEBUG(1, ("value for %s: encoding type unknown\n",
2201 FRUIT_PARAM_TYPE_NAME));
2202 return -1;
2204 config->encoding = (enum fruit_encoding)enumval;
2206 if (config->rsrc == FRUIT_RSRC_ADFILE) {
2207 config->veto_appledouble = lp_parm_bool(SNUM(handle->conn),
2208 FRUIT_PARAM_TYPE_NAME,
2209 "veto_appledouble",
2210 true);
2213 config->use_aapl = lp_parm_bool(
2214 -1, FRUIT_PARAM_TYPE_NAME, "aapl", true);
2216 config->time_machine = lp_parm_bool(
2217 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "time machine", false);
2219 config->unix_info_enabled = lp_parm_bool(
2220 -1, FRUIT_PARAM_TYPE_NAME, "nfs_aces", true);
2222 config->use_copyfile = lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME,
2223 "copyfile", false);
2225 config->posix_rename = lp_parm_bool(
2226 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "posix_rename", true);
2228 config->aapl_zero_file_id =
2229 lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME, "zero_file_id", true);
2231 config->readdir_attr_rsize = lp_parm_bool(
2232 SNUM(handle->conn), "readdir_attr", "aapl_rsize", true);
2234 config->readdir_attr_finder_info = lp_parm_bool(
2235 SNUM(handle->conn), "readdir_attr", "aapl_finder_info", true);
2237 config->readdir_attr_max_access = lp_parm_bool(
2238 SNUM(handle->conn), "readdir_attr", "aapl_max_access", true);
2240 config->model = lp_parm_const_string(
2241 -1, FRUIT_PARAM_TYPE_NAME, "model", "MacSamba");
2243 tm_size_str = lp_parm_const_string(
2244 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2245 "time machine max size", NULL);
2246 if (tm_size_str != NULL) {
2247 config->time_machine_max_size = conv_str_size(tm_size_str);
2250 config->wipe_intentionally_left_blank_rfork = lp_parm_bool(
2251 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2252 "wipe_intentionally_left_blank_rfork", false);
2254 config->delete_empty_adfiles = lp_parm_bool(
2255 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2256 "delete_empty_adfiles", false);
2258 SMB_VFS_HANDLE_SET_DATA(handle, config,
2259 NULL, struct fruit_config_data,
2260 return -1);
2262 return 0;
2266 * Prepend "._" to a basename
2267 * Return a new struct smb_filename with stream_name == NULL.
2269 static int adouble_path(TALLOC_CTX *ctx,
2270 const struct smb_filename *smb_fname_in,
2271 struct smb_filename **pp_smb_fname_out)
2273 char *parent;
2274 const char *base;
2275 struct smb_filename *smb_fname = cp_smb_filename(ctx,
2276 smb_fname_in);
2278 if (smb_fname == NULL) {
2279 return -1;
2282 /* We need streamname to be NULL */
2283 TALLOC_FREE(smb_fname->stream_name);
2285 /* And we're replacing base_name. */
2286 TALLOC_FREE(smb_fname->base_name);
2288 if (!parent_dirname(smb_fname, smb_fname_in->base_name,
2289 &parent, &base)) {
2290 TALLOC_FREE(smb_fname);
2291 return -1;
2294 smb_fname->base_name = talloc_asprintf(smb_fname,
2295 "%s/._%s", parent, base);
2296 if (smb_fname->base_name == NULL) {
2297 TALLOC_FREE(smb_fname);
2298 return -1;
2301 *pp_smb_fname_out = smb_fname;
2303 return 0;
2307 * Allocate and initialize an AfpInfo struct
2309 static AfpInfo *afpinfo_new(TALLOC_CTX *ctx)
2311 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2312 if (ai == NULL) {
2313 return NULL;
2315 ai->afpi_Signature = AFP_Signature;
2316 ai->afpi_Version = AFP_Version;
2317 ai->afpi_BackupTime = AD_DATE_START;
2318 return ai;
2322 * Pack an AfpInfo struct into a buffer
2324 * Buffer size must be at least AFP_INFO_SIZE
2325 * Returns size of packed buffer
2327 static ssize_t afpinfo_pack(const AfpInfo *ai, char *buf)
2329 memset(buf, 0, AFP_INFO_SIZE);
2331 RSIVAL(buf, 0, ai->afpi_Signature);
2332 RSIVAL(buf, 4, ai->afpi_Version);
2333 RSIVAL(buf, 12, ai->afpi_BackupTime);
2334 memcpy(buf + 16, ai->afpi_FinderInfo, sizeof(ai->afpi_FinderInfo));
2336 return AFP_INFO_SIZE;
2340 * Unpack a buffer into a AfpInfo structure
2342 * Buffer size must be at least AFP_INFO_SIZE
2343 * Returns allocated AfpInfo struct
2345 static AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data)
2347 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2348 if (ai == NULL) {
2349 return NULL;
2352 ai->afpi_Signature = RIVAL(data, 0);
2353 ai->afpi_Version = RIVAL(data, 4);
2354 ai->afpi_BackupTime = RIVAL(data, 12);
2355 memcpy(ai->afpi_FinderInfo, (const char *)data + 16,
2356 sizeof(ai->afpi_FinderInfo));
2358 if (ai->afpi_Signature != AFP_Signature
2359 || ai->afpi_Version != AFP_Version) {
2360 DEBUG(1, ("Bad AfpInfo signature or version\n"));
2361 TALLOC_FREE(ai);
2364 return ai;
2368 * Fake an inode number from the md5 hash of the (xattr) name
2370 static SMB_INO_T fruit_inode(const SMB_STRUCT_STAT *sbuf, const char *sname)
2372 MD5_CTX ctx;
2373 unsigned char hash[16];
2374 SMB_INO_T result;
2375 char *upper_sname;
2377 upper_sname = talloc_strdup_upper(talloc_tos(), sname);
2378 SMB_ASSERT(upper_sname != NULL);
2380 MD5Init(&ctx);
2381 MD5Update(&ctx, (const unsigned char *)&(sbuf->st_ex_dev),
2382 sizeof(sbuf->st_ex_dev));
2383 MD5Update(&ctx, (const unsigned char *)&(sbuf->st_ex_ino),
2384 sizeof(sbuf->st_ex_ino));
2385 MD5Update(&ctx, (unsigned char *)upper_sname,
2386 talloc_get_size(upper_sname)-1);
2387 MD5Final(hash, &ctx);
2389 TALLOC_FREE(upper_sname);
2391 /* Hopefully all the variation is in the lower 4 (or 8) bytes! */
2392 memcpy(&result, hash, sizeof(result));
2394 DEBUG(10, ("fruit_inode \"%s\": ino=0x%llu\n",
2395 sname, (unsigned long long)result));
2397 return result;
2400 static bool add_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
2401 struct stream_struct **streams,
2402 const char *name, off_t size,
2403 off_t alloc_size)
2405 struct stream_struct *tmp;
2407 tmp = talloc_realloc(mem_ctx, *streams, struct stream_struct,
2408 (*num_streams)+1);
2409 if (tmp == NULL) {
2410 return false;
2413 tmp[*num_streams].name = talloc_asprintf(tmp, "%s:$DATA", name);
2414 if (tmp[*num_streams].name == NULL) {
2415 return false;
2418 tmp[*num_streams].size = size;
2419 tmp[*num_streams].alloc_size = alloc_size;
2421 *streams = tmp;
2422 *num_streams += 1;
2423 return true;
2426 static bool filter_empty_rsrc_stream(unsigned int *num_streams,
2427 struct stream_struct **streams)
2429 struct stream_struct *tmp = *streams;
2430 unsigned int i;
2432 if (*num_streams == 0) {
2433 return true;
2436 for (i = 0; i < *num_streams; i++) {
2437 if (strequal_m(tmp[i].name, AFPRESOURCE_STREAM)) {
2438 break;
2442 if (i == *num_streams) {
2443 return true;
2446 if (tmp[i].size > 0) {
2447 return true;
2450 TALLOC_FREE(tmp[i].name);
2451 if (*num_streams - 1 > i) {
2452 memmove(&tmp[i], &tmp[i+1],
2453 (*num_streams - i - 1) * sizeof(struct stream_struct));
2456 *num_streams -= 1;
2457 return true;
2460 static bool del_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
2461 struct stream_struct **streams,
2462 const char *name)
2464 struct stream_struct *tmp = *streams;
2465 unsigned int i;
2467 if (*num_streams == 0) {
2468 return true;
2471 for (i = 0; i < *num_streams; i++) {
2472 if (strequal_m(tmp[i].name, name)) {
2473 break;
2477 if (i == *num_streams) {
2478 return true;
2481 TALLOC_FREE(tmp[i].name);
2482 if (*num_streams - 1 > i) {
2483 memmove(&tmp[i], &tmp[i+1],
2484 (*num_streams - i - 1) * sizeof(struct stream_struct));
2487 *num_streams -= 1;
2488 return true;
2491 static bool ad_empty_finderinfo(const struct adouble *ad)
2493 int cmp;
2494 char emptybuf[ADEDLEN_FINDERI] = {0};
2495 char *fi = NULL;
2497 fi = ad_get_entry(ad, ADEID_FINDERI);
2498 if (fi == NULL) {
2499 DBG_ERR("Missing FinderInfo in struct adouble [%p]\n", ad);
2500 return false;
2503 cmp = memcmp(emptybuf, fi, ADEDLEN_FINDERI);
2504 return (cmp == 0);
2507 static bool ai_empty_finderinfo(const AfpInfo *ai)
2509 int cmp;
2510 char emptybuf[ADEDLEN_FINDERI] = {0};
2512 cmp = memcmp(emptybuf, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
2513 return (cmp == 0);
2517 * Update btime with btime from Netatalk
2519 static void update_btime(vfs_handle_struct *handle,
2520 struct smb_filename *smb_fname)
2522 uint32_t t;
2523 struct timespec creation_time = {0};
2524 struct adouble *ad;
2525 struct fruit_config_data *config = NULL;
2527 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
2528 return);
2530 switch (config->meta) {
2531 case FRUIT_META_STREAM:
2532 return;
2533 case FRUIT_META_NETATALK:
2534 /* Handled below */
2535 break;
2536 default:
2537 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
2538 return;
2541 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
2542 if (ad == NULL) {
2543 return;
2545 if (ad_getdate(ad, AD_DATE_UNIX | AD_DATE_CREATE, &t) != 0) {
2546 TALLOC_FREE(ad);
2547 return;
2549 TALLOC_FREE(ad);
2551 creation_time.tv_sec = convert_uint32_t_to_time_t(t);
2552 update_stat_ex_create_time(&smb_fname->st, creation_time);
2554 return;
2558 * Map an access mask to a Netatalk single byte byte range lock
2560 static off_t access_to_netatalk_brl(enum apple_fork fork_type,
2561 uint32_t access_mask)
2563 off_t offset;
2565 switch (access_mask) {
2566 case FILE_READ_DATA:
2567 offset = AD_FILELOCK_OPEN_RD;
2568 break;
2570 case FILE_WRITE_DATA:
2571 case FILE_APPEND_DATA:
2572 offset = AD_FILELOCK_OPEN_WR;
2573 break;
2575 default:
2576 offset = AD_FILELOCK_OPEN_NONE;
2577 break;
2580 if (fork_type == APPLE_FORK_RSRC) {
2581 if (offset == AD_FILELOCK_OPEN_NONE) {
2582 offset = AD_FILELOCK_RSRC_OPEN_NONE;
2583 } else {
2584 offset += 2;
2588 return offset;
2592 * Map a deny mode to a Netatalk brl
2594 static off_t denymode_to_netatalk_brl(enum apple_fork fork_type,
2595 uint32_t deny_mode)
2597 off_t offset;
2599 switch (deny_mode) {
2600 case DENY_READ:
2601 offset = AD_FILELOCK_DENY_RD;
2602 break;
2604 case DENY_WRITE:
2605 offset = AD_FILELOCK_DENY_WR;
2606 break;
2608 default:
2609 smb_panic("denymode_to_netatalk_brl: bad deny mode\n");
2612 if (fork_type == APPLE_FORK_RSRC) {
2613 offset += 2;
2616 return offset;
2620 * Call fcntl() with an exclusive F_GETLK request in order to
2621 * determine if there's an exisiting shared lock
2623 * @return true if the requested lock was found or any error occurred
2624 * false if the lock was not found
2626 static bool test_netatalk_lock(files_struct *fsp, off_t in_offset)
2628 bool result;
2629 off_t offset = in_offset;
2630 off_t len = 1;
2631 int type = F_WRLCK;
2632 pid_t pid;
2634 result = SMB_VFS_GETLOCK(fsp, &offset, &len, &type, &pid);
2635 if (result == false) {
2636 return true;
2639 if (type != F_UNLCK) {
2640 return true;
2643 return false;
2646 static NTSTATUS fruit_check_access(vfs_handle_struct *handle,
2647 files_struct *fsp,
2648 uint32_t access_mask,
2649 uint32_t deny_mode)
2651 NTSTATUS status = NT_STATUS_OK;
2652 bool open_for_reading, open_for_writing, deny_read, deny_write;
2653 off_t off;
2654 bool have_read = false;
2655 int flags;
2657 /* FIXME: hardcoded data fork, add resource fork */
2658 enum apple_fork fork_type = APPLE_FORK_DATA;
2660 DEBUG(10, ("fruit_check_access: %s, am: %s/%s, dm: %s/%s\n",
2661 fsp_str_dbg(fsp),
2662 access_mask & FILE_READ_DATA ? "READ" :"-",
2663 access_mask & FILE_WRITE_DATA ? "WRITE" : "-",
2664 deny_mode & DENY_READ ? "DENY_READ" : "-",
2665 deny_mode & DENY_WRITE ? "DENY_WRITE" : "-"));
2667 if (fsp->fh->fd == -1) {
2668 return NT_STATUS_OK;
2671 flags = fcntl(fsp->fh->fd, F_GETFL);
2672 if (flags == -1) {
2673 DBG_ERR("fcntl get flags [%s] fd [%d] failed [%s]\n",
2674 fsp_str_dbg(fsp), fsp->fh->fd, strerror(errno));
2675 return map_nt_error_from_unix(errno);
2678 if (flags & (O_RDONLY|O_RDWR)) {
2680 * Applying fcntl read locks requires an fd opened for
2681 * reading. This means we won't be applying locks for
2682 * files openend write-only, but what can we do...
2684 have_read = true;
2688 * Check read access and deny read mode
2690 if ((access_mask & FILE_READ_DATA) || (deny_mode & DENY_READ)) {
2691 /* Check access */
2692 open_for_reading = test_netatalk_lock(
2693 fsp, access_to_netatalk_brl(fork_type, FILE_READ_DATA));
2695 deny_read = test_netatalk_lock(
2696 fsp, denymode_to_netatalk_brl(fork_type, DENY_READ));
2698 DEBUG(10, ("read: %s, deny_write: %s\n",
2699 open_for_reading == true ? "yes" : "no",
2700 deny_read == true ? "yes" : "no"));
2702 if (((access_mask & FILE_READ_DATA) && deny_read)
2703 || ((deny_mode & DENY_READ) && open_for_reading)) {
2704 return NT_STATUS_SHARING_VIOLATION;
2707 /* Set locks */
2708 if ((access_mask & FILE_READ_DATA) && have_read) {
2709 struct byte_range_lock *br_lck = NULL;
2711 off = access_to_netatalk_brl(fork_type, FILE_READ_DATA);
2712 br_lck = do_lock(
2713 handle->conn->sconn->msg_ctx, fsp,
2714 fsp->op->global->open_persistent_id, 1, off,
2715 READ_LOCK, POSIX_LOCK, false,
2716 &status, NULL);
2718 TALLOC_FREE(br_lck);
2720 if (!NT_STATUS_IS_OK(status)) {
2721 return status;
2725 if ((deny_mode & DENY_READ) && have_read) {
2726 struct byte_range_lock *br_lck = NULL;
2728 off = denymode_to_netatalk_brl(fork_type, DENY_READ);
2729 br_lck = do_lock(
2730 handle->conn->sconn->msg_ctx, fsp,
2731 fsp->op->global->open_persistent_id, 1, off,
2732 READ_LOCK, POSIX_LOCK, false,
2733 &status, NULL);
2735 TALLOC_FREE(br_lck);
2737 if (!NT_STATUS_IS_OK(status)) {
2738 return status;
2744 * Check write access and deny write mode
2746 if ((access_mask & FILE_WRITE_DATA) || (deny_mode & DENY_WRITE)) {
2747 /* Check access */
2748 open_for_writing = test_netatalk_lock(
2749 fsp, access_to_netatalk_brl(fork_type, FILE_WRITE_DATA));
2751 deny_write = test_netatalk_lock(
2752 fsp, denymode_to_netatalk_brl(fork_type, DENY_WRITE));
2754 DEBUG(10, ("write: %s, deny_write: %s\n",
2755 open_for_writing == true ? "yes" : "no",
2756 deny_write == true ? "yes" : "no"));
2758 if (((access_mask & FILE_WRITE_DATA) && deny_write)
2759 || ((deny_mode & DENY_WRITE) && open_for_writing)) {
2760 return NT_STATUS_SHARING_VIOLATION;
2763 /* Set locks */
2764 if ((access_mask & FILE_WRITE_DATA) && have_read) {
2765 struct byte_range_lock *br_lck = NULL;
2767 off = access_to_netatalk_brl(fork_type, FILE_WRITE_DATA);
2768 br_lck = do_lock(
2769 handle->conn->sconn->msg_ctx, fsp,
2770 fsp->op->global->open_persistent_id, 1, off,
2771 READ_LOCK, POSIX_LOCK, false,
2772 &status, NULL);
2774 TALLOC_FREE(br_lck);
2776 if (!NT_STATUS_IS_OK(status)) {
2777 return status;
2780 if ((deny_mode & DENY_WRITE) && have_read) {
2781 struct byte_range_lock *br_lck = NULL;
2783 off = denymode_to_netatalk_brl(fork_type, DENY_WRITE);
2784 br_lck = do_lock(
2785 handle->conn->sconn->msg_ctx, fsp,
2786 fsp->op->global->open_persistent_id, 1, off,
2787 READ_LOCK, POSIX_LOCK, false,
2788 &status, NULL);
2790 TALLOC_FREE(br_lck);
2792 if (!NT_STATUS_IS_OK(status)) {
2793 return status;
2798 return status;
2801 static NTSTATUS check_aapl(vfs_handle_struct *handle,
2802 struct smb_request *req,
2803 const struct smb2_create_blobs *in_context_blobs,
2804 struct smb2_create_blobs *out_context_blobs)
2806 struct fruit_config_data *config;
2807 NTSTATUS status;
2808 struct smb2_create_blob *aapl = NULL;
2809 uint32_t cmd;
2810 bool ok;
2811 uint8_t p[16];
2812 DATA_BLOB blob = data_blob_talloc(req, NULL, 0);
2813 uint64_t req_bitmap, client_caps;
2814 uint64_t server_caps = SMB2_CRTCTX_AAPL_UNIX_BASED;
2815 smb_ucs2_t *model;
2816 size_t modellen;
2818 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
2819 return NT_STATUS_UNSUCCESSFUL);
2821 if (!config->use_aapl
2822 || in_context_blobs == NULL
2823 || out_context_blobs == NULL) {
2824 return NT_STATUS_OK;
2827 aapl = smb2_create_blob_find(in_context_blobs,
2828 SMB2_CREATE_TAG_AAPL);
2829 if (aapl == NULL) {
2830 return NT_STATUS_OK;
2833 if (aapl->data.length != 24) {
2834 DEBUG(1, ("unexpected AAPL ctxt length: %ju\n",
2835 (uintmax_t)aapl->data.length));
2836 return NT_STATUS_INVALID_PARAMETER;
2839 cmd = IVAL(aapl->data.data, 0);
2840 if (cmd != SMB2_CRTCTX_AAPL_SERVER_QUERY) {
2841 DEBUG(1, ("unsupported AAPL cmd: %d\n", cmd));
2842 return NT_STATUS_INVALID_PARAMETER;
2845 req_bitmap = BVAL(aapl->data.data, 8);
2846 client_caps = BVAL(aapl->data.data, 16);
2848 SIVAL(p, 0, SMB2_CRTCTX_AAPL_SERVER_QUERY);
2849 SIVAL(p, 4, 0);
2850 SBVAL(p, 8, req_bitmap);
2851 ok = data_blob_append(req, &blob, p, 16);
2852 if (!ok) {
2853 return NT_STATUS_UNSUCCESSFUL;
2856 if (req_bitmap & SMB2_CRTCTX_AAPL_SERVER_CAPS) {
2857 if ((client_caps & SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR) &&
2858 (handle->conn->tcon->compat->fs_capabilities & FILE_NAMED_STREAMS)) {
2859 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR;
2860 config->readdir_attr_enabled = true;
2863 if (config->use_copyfile) {
2864 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_OSX_COPYFILE;
2865 config->copyfile_enabled = true;
2869 * The client doesn't set the flag, so we can't check
2870 * for it and just set it unconditionally
2872 if (config->unix_info_enabled) {
2873 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_NFS_ACE;
2876 SBVAL(p, 0, server_caps);
2877 ok = data_blob_append(req, &blob, p, 8);
2878 if (!ok) {
2879 return NT_STATUS_UNSUCCESSFUL;
2883 if (req_bitmap & SMB2_CRTCTX_AAPL_VOLUME_CAPS) {
2884 int val = lp_case_sensitive(SNUM(handle->conn->tcon->compat));
2885 uint64_t caps = 0;
2887 switch (val) {
2888 case Auto:
2889 break;
2891 case True:
2892 caps |= SMB2_CRTCTX_AAPL_CASE_SENSITIVE;
2893 break;
2895 default:
2896 break;
2899 if (config->time_machine) {
2900 caps |= SMB2_CRTCTX_AAPL_FULL_SYNC;
2903 SBVAL(p, 0, caps);
2905 ok = data_blob_append(req, &blob, p, 8);
2906 if (!ok) {
2907 return NT_STATUS_UNSUCCESSFUL;
2911 if (req_bitmap & SMB2_CRTCTX_AAPL_MODEL_INFO) {
2912 ok = convert_string_talloc(req,
2913 CH_UNIX, CH_UTF16LE,
2914 config->model, strlen(config->model),
2915 &model, &modellen);
2916 if (!ok) {
2917 return NT_STATUS_UNSUCCESSFUL;
2920 SIVAL(p, 0, 0);
2921 SIVAL(p + 4, 0, modellen);
2922 ok = data_blob_append(req, &blob, p, 8);
2923 if (!ok) {
2924 talloc_free(model);
2925 return NT_STATUS_UNSUCCESSFUL;
2928 ok = data_blob_append(req, &blob, model, modellen);
2929 talloc_free(model);
2930 if (!ok) {
2931 return NT_STATUS_UNSUCCESSFUL;
2935 status = smb2_create_blob_add(out_context_blobs,
2936 out_context_blobs,
2937 SMB2_CREATE_TAG_AAPL,
2938 blob);
2939 if (NT_STATUS_IS_OK(status)) {
2940 global_fruit_config.nego_aapl = true;
2941 if (config->aapl_zero_file_id) {
2942 aapl_force_zero_file_id(handle->conn->sconn);
2946 return status;
2949 static bool readdir_attr_meta_finderi_stream(
2950 struct vfs_handle_struct *handle,
2951 const struct smb_filename *smb_fname,
2952 AfpInfo *ai)
2954 struct smb_filename *stream_name = NULL;
2955 files_struct *fsp = NULL;
2956 ssize_t nread;
2957 NTSTATUS status;
2958 int ret;
2959 bool ok;
2960 uint8_t buf[AFP_INFO_SIZE];
2962 stream_name = synthetic_smb_fname(talloc_tos(),
2963 smb_fname->base_name,
2964 AFPINFO_STREAM_NAME,
2965 NULL, smb_fname->flags);
2966 if (stream_name == NULL) {
2967 return false;
2970 ret = SMB_VFS_STAT(handle->conn, stream_name);
2971 if (ret != 0) {
2972 return false;
2975 status = SMB_VFS_CREATE_FILE(
2976 handle->conn, /* conn */
2977 NULL, /* req */
2978 0, /* root_dir_fid */
2979 stream_name, /* fname */
2980 FILE_READ_DATA, /* access_mask */
2981 (FILE_SHARE_READ | FILE_SHARE_WRITE | /* share_access */
2982 FILE_SHARE_DELETE),
2983 FILE_OPEN, /* create_disposition*/
2984 0, /* create_options */
2985 0, /* file_attributes */
2986 INTERNAL_OPEN_ONLY, /* oplock_request */
2987 NULL, /* lease */
2988 0, /* allocation_size */
2989 0, /* private_flags */
2990 NULL, /* sd */
2991 NULL, /* ea_list */
2992 &fsp, /* result */
2993 NULL, /* pinfo */
2994 NULL, NULL); /* create context */
2996 TALLOC_FREE(stream_name);
2998 if (!NT_STATUS_IS_OK(status)) {
2999 return false;
3002 nread = SMB_VFS_PREAD(fsp, &buf[0], AFP_INFO_SIZE, 0);
3003 if (nread != AFP_INFO_SIZE) {
3004 DBG_ERR("short read [%s] [%zd/%d]\n",
3005 smb_fname_str_dbg(stream_name), nread, AFP_INFO_SIZE);
3006 ok = false;
3007 goto fail;
3010 memcpy(&ai->afpi_FinderInfo[0], &buf[AFP_OFF_FinderInfo],
3011 AFP_FinderSize);
3013 ok = true;
3015 fail:
3016 if (fsp != NULL) {
3017 close_file(NULL, fsp, NORMAL_CLOSE);
3020 return ok;
3023 static bool readdir_attr_meta_finderi_netatalk(
3024 struct vfs_handle_struct *handle,
3025 const struct smb_filename *smb_fname,
3026 AfpInfo *ai)
3028 struct adouble *ad = NULL;
3029 char *p = NULL;
3031 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
3032 if (ad == NULL) {
3033 return false;
3036 p = ad_get_entry(ad, ADEID_FINDERI);
3037 if (p == NULL) {
3038 DBG_ERR("No ADEID_FINDERI for [%s]\n", smb_fname->base_name);
3039 TALLOC_FREE(ad);
3040 return false;
3043 memcpy(&ai->afpi_FinderInfo[0], p, AFP_FinderSize);
3044 TALLOC_FREE(ad);
3045 return true;
3048 static bool readdir_attr_meta_finderi(struct vfs_handle_struct *handle,
3049 const struct smb_filename *smb_fname,
3050 struct readdir_attr_data *attr_data)
3052 struct fruit_config_data *config = NULL;
3053 uint32_t date_added;
3054 AfpInfo ai = {0};
3055 bool ok;
3057 SMB_VFS_HANDLE_GET_DATA(handle, config,
3058 struct fruit_config_data,
3059 return false);
3061 switch (config->meta) {
3062 case FRUIT_META_NETATALK:
3063 ok = readdir_attr_meta_finderi_netatalk(
3064 handle, smb_fname, &ai);
3065 break;
3067 case FRUIT_META_STREAM:
3068 ok = readdir_attr_meta_finderi_stream(
3069 handle, smb_fname, &ai);
3070 break;
3072 default:
3073 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3074 return false;
3077 if (!ok) {
3078 /* Don't bother with errors, it's likely ENOENT */
3079 return true;
3082 if (S_ISREG(smb_fname->st.st_ex_mode)) {
3083 /* finder_type */
3084 memcpy(&attr_data->attr_data.aapl.finder_info[0],
3085 &ai.afpi_FinderInfo[0], 4);
3087 /* finder_creator */
3088 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 4,
3089 &ai.afpi_FinderInfo[4], 4);
3092 /* finder_flags */
3093 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 8,
3094 &ai.afpi_FinderInfo[8], 2);
3096 /* finder_ext_flags */
3097 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 10,
3098 &ai.afpi_FinderInfo[24], 2);
3100 /* creation date */
3101 date_added = convert_time_t_to_uint32_t(
3102 smb_fname->st.st_ex_btime.tv_sec - AD_DATE_DELTA);
3104 RSIVAL(&attr_data->attr_data.aapl.finder_info[0], 12, date_added);
3106 return true;
3109 static uint64_t readdir_attr_rfork_size_adouble(
3110 struct vfs_handle_struct *handle,
3111 const struct smb_filename *smb_fname)
3113 struct adouble *ad = NULL;
3114 uint64_t rfork_size;
3116 ad = ad_get(talloc_tos(), handle, smb_fname,
3117 ADOUBLE_RSRC);
3118 if (ad == NULL) {
3119 return 0;
3122 rfork_size = ad_getentrylen(ad, ADEID_RFORK);
3123 TALLOC_FREE(ad);
3125 return rfork_size;
3128 static uint64_t readdir_attr_rfork_size_stream(
3129 struct vfs_handle_struct *handle,
3130 const struct smb_filename *smb_fname)
3132 struct smb_filename *stream_name = NULL;
3133 int ret;
3134 uint64_t rfork_size;
3136 stream_name = synthetic_smb_fname(talloc_tos(),
3137 smb_fname->base_name,
3138 AFPRESOURCE_STREAM_NAME,
3139 NULL, 0);
3140 if (stream_name == NULL) {
3141 return 0;
3144 ret = SMB_VFS_STAT(handle->conn, stream_name);
3145 if (ret != 0) {
3146 TALLOC_FREE(stream_name);
3147 return 0;
3150 rfork_size = stream_name->st.st_ex_size;
3151 TALLOC_FREE(stream_name);
3153 return rfork_size;
3156 static uint64_t readdir_attr_rfork_size(struct vfs_handle_struct *handle,
3157 const struct smb_filename *smb_fname)
3159 struct fruit_config_data *config = NULL;
3160 uint64_t rfork_size;
3162 SMB_VFS_HANDLE_GET_DATA(handle, config,
3163 struct fruit_config_data,
3164 return 0);
3166 switch (config->rsrc) {
3167 case FRUIT_RSRC_ADFILE:
3168 case FRUIT_RSRC_XATTR:
3169 rfork_size = readdir_attr_rfork_size_adouble(handle,
3170 smb_fname);
3171 break;
3173 case FRUIT_META_STREAM:
3174 rfork_size = readdir_attr_rfork_size_stream(handle,
3175 smb_fname);
3176 break;
3178 default:
3179 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3180 rfork_size = 0;
3181 break;
3184 return rfork_size;
3187 static NTSTATUS readdir_attr_macmeta(struct vfs_handle_struct *handle,
3188 const struct smb_filename *smb_fname,
3189 struct readdir_attr_data *attr_data)
3191 NTSTATUS status = NT_STATUS_OK;
3192 struct fruit_config_data *config = NULL;
3193 bool ok;
3195 SMB_VFS_HANDLE_GET_DATA(handle, config,
3196 struct fruit_config_data,
3197 return NT_STATUS_UNSUCCESSFUL);
3200 /* Ensure we return a default value in the creation_date field */
3201 RSIVAL(&attr_data->attr_data.aapl.finder_info, 12, AD_DATE_START);
3204 * Resource fork length
3207 if (config->readdir_attr_rsize) {
3208 uint64_t rfork_size;
3210 rfork_size = readdir_attr_rfork_size(handle, smb_fname);
3211 attr_data->attr_data.aapl.rfork_size = rfork_size;
3215 * FinderInfo
3218 if (config->readdir_attr_finder_info) {
3219 ok = readdir_attr_meta_finderi(handle, smb_fname, attr_data);
3220 if (!ok) {
3221 status = NT_STATUS_INTERNAL_ERROR;
3225 return status;
3228 static NTSTATUS remove_virtual_nfs_aces(struct security_descriptor *psd)
3230 NTSTATUS status;
3231 uint32_t i;
3233 if (psd->dacl == NULL) {
3234 return NT_STATUS_OK;
3237 for (i = 0; i < psd->dacl->num_aces; i++) {
3238 /* MS NFS style mode/uid/gid */
3239 int cmp = dom_sid_compare_domain(
3240 &global_sid_Unix_NFS,
3241 &psd->dacl->aces[i].trustee);
3242 if (cmp != 0) {
3243 /* Normal ACE entry. */
3244 continue;
3248 * security_descriptor_dacl_del()
3249 * *must* return NT_STATUS_OK as we know
3250 * we have something to remove.
3253 status = security_descriptor_dacl_del(psd,
3254 &psd->dacl->aces[i].trustee);
3255 if (!NT_STATUS_IS_OK(status)) {
3256 DBG_WARNING("failed to remove MS NFS style ACE: %s\n",
3257 nt_errstr(status));
3258 return status;
3262 * security_descriptor_dacl_del() may delete more
3263 * then one entry subsequent to this one if the
3264 * SID matches, but we only need to ensure that
3265 * we stay looking at the same element in the array.
3267 i--;
3269 return NT_STATUS_OK;
3272 /* Search MS NFS style ACE with UNIX mode */
3273 static NTSTATUS check_ms_nfs(vfs_handle_struct *handle,
3274 files_struct *fsp,
3275 struct security_descriptor *psd,
3276 mode_t *pmode,
3277 bool *pdo_chmod)
3279 uint32_t i;
3280 struct fruit_config_data *config = NULL;
3282 *pdo_chmod = false;
3284 SMB_VFS_HANDLE_GET_DATA(handle, config,
3285 struct fruit_config_data,
3286 return NT_STATUS_UNSUCCESSFUL);
3288 if (!global_fruit_config.nego_aapl) {
3289 return NT_STATUS_OK;
3291 if (psd->dacl == NULL || !config->unix_info_enabled) {
3292 return NT_STATUS_OK;
3295 for (i = 0; i < psd->dacl->num_aces; i++) {
3296 if (dom_sid_compare_domain(
3297 &global_sid_Unix_NFS_Mode,
3298 &psd->dacl->aces[i].trustee) == 0) {
3299 *pmode = (mode_t)psd->dacl->aces[i].trustee.sub_auths[2];
3300 *pmode &= (S_IRWXU | S_IRWXG | S_IRWXO);
3301 *pdo_chmod = true;
3303 DEBUG(10, ("MS NFS chmod request %s, %04o\n",
3304 fsp_str_dbg(fsp), (unsigned)(*pmode)));
3305 break;
3310 * Remove any incoming virtual ACE entries generated by
3311 * fruit_fget_nt_acl().
3314 return remove_virtual_nfs_aces(psd);
3317 /****************************************************************************
3318 * VFS ops
3319 ****************************************************************************/
3321 static int fruit_connect(vfs_handle_struct *handle,
3322 const char *service,
3323 const char *user)
3325 int rc;
3326 char *list = NULL, *newlist = NULL;
3327 struct fruit_config_data *config;
3329 DEBUG(10, ("fruit_connect\n"));
3331 rc = SMB_VFS_NEXT_CONNECT(handle, service, user);
3332 if (rc < 0) {
3333 return rc;
3336 rc = init_fruit_config(handle);
3337 if (rc != 0) {
3338 return rc;
3341 SMB_VFS_HANDLE_GET_DATA(handle, config,
3342 struct fruit_config_data, return -1);
3344 if (config->veto_appledouble) {
3345 list = lp_veto_files(talloc_tos(), SNUM(handle->conn));
3347 if (list) {
3348 if (strstr(list, "/" ADOUBLE_NAME_PREFIX "*/") == NULL) {
3349 newlist = talloc_asprintf(
3350 list,
3351 "%s/" ADOUBLE_NAME_PREFIX "*/",
3352 list);
3353 lp_do_parameter(SNUM(handle->conn),
3354 "veto files",
3355 newlist);
3357 } else {
3358 lp_do_parameter(SNUM(handle->conn),
3359 "veto files",
3360 "/" ADOUBLE_NAME_PREFIX "*/");
3363 TALLOC_FREE(list);
3366 if (config->encoding == FRUIT_ENC_NATIVE) {
3367 lp_do_parameter(SNUM(handle->conn),
3368 "catia:mappings",
3369 fruit_catia_maps);
3372 if (config->time_machine) {
3373 DBG_NOTICE("Enabling durable handles for Time Machine "
3374 "support on [%s]\n", service);
3375 lp_do_parameter(SNUM(handle->conn), "durable handles", "yes");
3376 lp_do_parameter(SNUM(handle->conn), "kernel oplocks", "no");
3377 lp_do_parameter(SNUM(handle->conn), "kernel share modes", "no");
3378 if (!lp_strict_sync(SNUM(handle->conn))) {
3379 DBG_WARNING("Time Machine without strict sync is not "
3380 "recommended!\n");
3382 lp_do_parameter(SNUM(handle->conn), "posix locking", "no");
3385 return rc;
3388 static int fruit_open_meta_stream(vfs_handle_struct *handle,
3389 struct smb_filename *smb_fname,
3390 files_struct *fsp,
3391 int flags,
3392 mode_t mode)
3394 AfpInfo *ai = NULL;
3395 char afpinfo_buf[AFP_INFO_SIZE];
3396 ssize_t len, written;
3397 int hostfd = -1;
3398 int rc = -1;
3400 hostfd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3401 if (hostfd == -1) {
3402 return -1;
3405 if (!(flags & (O_CREAT | O_TRUNC))) {
3406 return hostfd;
3409 ai = afpinfo_new(talloc_tos());
3410 if (ai == NULL) {
3411 rc = -1;
3412 goto fail;
3415 len = afpinfo_pack(ai, afpinfo_buf);
3416 if (len != AFP_INFO_SIZE) {
3417 rc = -1;
3418 goto fail;
3421 /* Set fd, needed in SMB_VFS_NEXT_PWRITE() */
3422 fsp->fh->fd = hostfd;
3424 written = SMB_VFS_NEXT_PWRITE(handle, fsp, afpinfo_buf,
3425 AFP_INFO_SIZE, 0);
3426 fsp->fh->fd = -1;
3427 if (written != AFP_INFO_SIZE) {
3428 DBG_ERR("bad write [%zd/%d]\n", written, AFP_INFO_SIZE);
3429 rc = -1;
3430 goto fail;
3433 rc = 0;
3435 fail:
3436 DBG_DEBUG("rc=%d, fd=%d\n", rc, hostfd);
3438 if (rc != 0) {
3439 int saved_errno = errno;
3440 if (hostfd >= 0) {
3441 fsp->fh->fd = hostfd;
3442 SMB_VFS_NEXT_CLOSE(handle, fsp);
3444 hostfd = -1;
3445 errno = saved_errno;
3447 return hostfd;
3450 static int fruit_open_meta_netatalk(vfs_handle_struct *handle,
3451 struct smb_filename *smb_fname,
3452 files_struct *fsp,
3453 int flags,
3454 mode_t mode)
3456 int rc;
3457 int fakefd = -1;
3458 struct adouble *ad = NULL;
3459 int fds[2];
3461 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3464 * Return a valid fd, but ensure any attempt to use it returns an error
3465 * (EPIPE). All operations on the smb_fname or the fsp will use path
3466 * based syscalls.
3468 rc = pipe(fds);
3469 if (rc != 0) {
3470 goto exit;
3472 fakefd = fds[0];
3473 close(fds[1]);
3475 if (flags & (O_CREAT | O_TRUNC)) {
3477 * The attribute does not exist or needs to be truncated,
3478 * create an AppleDouble EA
3480 ad = ad_init(fsp, handle, ADOUBLE_META);
3481 if (ad == NULL) {
3482 rc = -1;
3483 goto exit;
3486 rc = ad_set(ad, fsp->fsp_name);
3487 if (rc != 0) {
3488 rc = -1;
3489 goto exit;
3492 TALLOC_FREE(ad);
3495 exit:
3496 DEBUG(10, ("fruit_open meta rc=%d, fd=%d\n", rc, fakefd));
3497 if (rc != 0) {
3498 int saved_errno = errno;
3499 if (fakefd >= 0) {
3500 close(fakefd);
3502 fakefd = -1;
3503 errno = saved_errno;
3505 return fakefd;
3508 static int fruit_open_meta(vfs_handle_struct *handle,
3509 struct smb_filename *smb_fname,
3510 files_struct *fsp, int flags, mode_t mode)
3512 int fd;
3513 struct fruit_config_data *config = NULL;
3514 struct fio *fio = NULL;
3516 DBG_DEBUG("path [%s]\n", smb_fname_str_dbg(smb_fname));
3518 SMB_VFS_HANDLE_GET_DATA(handle, config,
3519 struct fruit_config_data, return -1);
3521 switch (config->meta) {
3522 case FRUIT_META_STREAM:
3523 fd = fruit_open_meta_stream(handle, smb_fname,
3524 fsp, flags, mode);
3525 break;
3527 case FRUIT_META_NETATALK:
3528 fd = fruit_open_meta_netatalk(handle, smb_fname,
3529 fsp, flags, mode);
3530 break;
3532 default:
3533 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3534 return -1;
3537 DBG_DEBUG("path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3539 if (fd == -1) {
3540 return -1;
3543 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3544 fio->type = ADOUBLE_META;
3545 fio->config = config;
3547 return fd;
3550 static int fruit_open_rsrc_adouble(vfs_handle_struct *handle,
3551 struct smb_filename *smb_fname,
3552 files_struct *fsp,
3553 int flags,
3554 mode_t mode)
3556 int rc = 0;
3557 struct adouble *ad = NULL;
3558 struct smb_filename *smb_fname_base = NULL;
3559 struct fruit_config_data *config = NULL;
3560 int hostfd = -1;
3562 SMB_VFS_HANDLE_GET_DATA(handle, config,
3563 struct fruit_config_data, return -1);
3565 if ((!(flags & O_CREAT)) &&
3566 S_ISDIR(fsp->base_fsp->fsp_name->st.st_ex_mode))
3568 /* sorry, but directories don't habe a resource fork */
3569 rc = -1;
3570 goto exit;
3573 rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_base);
3574 if (rc != 0) {
3575 goto exit;
3578 /* We always need read/write access for the metadata header too */
3579 flags &= ~(O_RDONLY | O_WRONLY);
3580 flags |= O_RDWR;
3582 hostfd = SMB_VFS_NEXT_OPEN(handle, smb_fname_base, fsp,
3583 flags, mode);
3584 if (hostfd == -1) {
3585 rc = -1;
3586 goto exit;
3589 if (flags & (O_CREAT | O_TRUNC)) {
3590 ad = ad_init(fsp, handle, ADOUBLE_RSRC);
3591 if (ad == NULL) {
3592 rc = -1;
3593 goto exit;
3596 fsp->fh->fd = hostfd;
3598 rc = ad_fset(ad, fsp);
3599 fsp->fh->fd = -1;
3600 if (rc != 0) {
3601 rc = -1;
3602 goto exit;
3604 TALLOC_FREE(ad);
3607 exit:
3609 TALLOC_FREE(smb_fname_base);
3611 DEBUG(10, ("fruit_open resource fork: rc=%d, fd=%d\n", rc, hostfd));
3612 if (rc != 0) {
3613 int saved_errno = errno;
3614 if (hostfd >= 0) {
3616 * BUGBUGBUG -- we would need to call
3617 * fd_close_posix here, but we don't have a
3618 * full fsp yet
3620 fsp->fh->fd = hostfd;
3621 SMB_VFS_CLOSE(fsp);
3623 hostfd = -1;
3624 errno = saved_errno;
3626 return hostfd;
3629 static int fruit_open_rsrc_xattr(vfs_handle_struct *handle,
3630 struct smb_filename *smb_fname,
3631 files_struct *fsp,
3632 int flags,
3633 mode_t mode)
3635 #ifdef HAVE_ATTROPEN
3636 int fd = -1;
3638 fd = attropen(smb_fname->base_name,
3639 AFPRESOURCE_EA_NETATALK,
3640 flags,
3641 mode);
3642 if (fd == -1) {
3643 return -1;
3646 return fd;
3648 #else
3649 errno = ENOSYS;
3650 return -1;
3651 #endif
3654 static int fruit_open_rsrc(vfs_handle_struct *handle,
3655 struct smb_filename *smb_fname,
3656 files_struct *fsp, int flags, mode_t mode)
3658 int fd;
3659 struct fruit_config_data *config = NULL;
3660 struct fio *fio = NULL;
3662 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3664 SMB_VFS_HANDLE_GET_DATA(handle, config,
3665 struct fruit_config_data, return -1);
3667 switch (config->rsrc) {
3668 case FRUIT_RSRC_STREAM:
3669 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3670 break;
3672 case FRUIT_RSRC_ADFILE:
3673 fd = fruit_open_rsrc_adouble(handle, smb_fname,
3674 fsp, flags, mode);
3675 break;
3677 case FRUIT_RSRC_XATTR:
3678 fd = fruit_open_rsrc_xattr(handle, smb_fname,
3679 fsp, flags, mode);
3680 break;
3682 default:
3683 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3684 return -1;
3687 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3689 if (fd == -1) {
3690 return -1;
3693 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3694 fio->type = ADOUBLE_RSRC;
3695 fio->config = config;
3697 return fd;
3700 static int fruit_open(vfs_handle_struct *handle,
3701 struct smb_filename *smb_fname,
3702 files_struct *fsp, int flags, mode_t mode)
3704 int fd;
3706 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3708 if (!is_ntfs_stream_smb_fname(smb_fname)) {
3709 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3712 if (is_afpinfo_stream(smb_fname)) {
3713 fd = fruit_open_meta(handle, smb_fname, fsp, flags, mode);
3714 } else if (is_afpresource_stream(smb_fname)) {
3715 fd = fruit_open_rsrc(handle, smb_fname, fsp, flags, mode);
3716 } else {
3717 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3720 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3722 return fd;
3725 static int fruit_rename(struct vfs_handle_struct *handle,
3726 const struct smb_filename *smb_fname_src,
3727 const struct smb_filename *smb_fname_dst)
3729 int rc = -1;
3730 struct fruit_config_data *config = NULL;
3731 struct smb_filename *src_adp_smb_fname = NULL;
3732 struct smb_filename *dst_adp_smb_fname = NULL;
3734 SMB_VFS_HANDLE_GET_DATA(handle, config,
3735 struct fruit_config_data, return -1);
3737 if (!VALID_STAT(smb_fname_src->st)) {
3738 DBG_ERR("Need valid stat for [%s]\n",
3739 smb_fname_str_dbg(smb_fname_src));
3740 return -1;
3743 rc = SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
3744 if (rc != 0) {
3745 return -1;
3748 if ((config->rsrc != FRUIT_RSRC_ADFILE) ||
3749 (!S_ISREG(smb_fname_src->st.st_ex_mode)))
3751 return 0;
3754 rc = adouble_path(talloc_tos(), smb_fname_src, &src_adp_smb_fname);
3755 if (rc != 0) {
3756 goto done;
3759 rc = adouble_path(talloc_tos(), smb_fname_dst, &dst_adp_smb_fname);
3760 if (rc != 0) {
3761 goto done;
3764 DBG_DEBUG("%s -> %s\n",
3765 smb_fname_str_dbg(src_adp_smb_fname),
3766 smb_fname_str_dbg(dst_adp_smb_fname));
3768 rc = SMB_VFS_NEXT_RENAME(handle, src_adp_smb_fname, dst_adp_smb_fname);
3769 if (errno == ENOENT) {
3770 rc = 0;
3773 done:
3774 TALLOC_FREE(src_adp_smb_fname);
3775 TALLOC_FREE(dst_adp_smb_fname);
3776 return rc;
3779 static int fruit_unlink_meta_stream(vfs_handle_struct *handle,
3780 const struct smb_filename *smb_fname)
3782 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3785 static int fruit_unlink_meta_netatalk(vfs_handle_struct *handle,
3786 const struct smb_filename *smb_fname)
3788 return SMB_VFS_REMOVEXATTR(handle->conn,
3789 smb_fname,
3790 AFPINFO_EA_NETATALK);
3793 static int fruit_unlink_meta(vfs_handle_struct *handle,
3794 const struct smb_filename *smb_fname)
3796 struct fruit_config_data *config = NULL;
3797 int rc;
3799 SMB_VFS_HANDLE_GET_DATA(handle, config,
3800 struct fruit_config_data, return -1);
3802 switch (config->meta) {
3803 case FRUIT_META_STREAM:
3804 rc = fruit_unlink_meta_stream(handle, smb_fname);
3805 break;
3807 case FRUIT_META_NETATALK:
3808 rc = fruit_unlink_meta_netatalk(handle, smb_fname);
3809 break;
3811 default:
3812 DBG_ERR("Unsupported meta config [%d]\n", config->meta);
3813 return -1;
3816 return rc;
3819 static int fruit_unlink_rsrc_stream(vfs_handle_struct *handle,
3820 const struct smb_filename *smb_fname,
3821 bool force_unlink)
3823 int ret;
3825 if (!force_unlink) {
3826 struct smb_filename *smb_fname_cp = NULL;
3827 off_t size;
3829 smb_fname_cp = cp_smb_filename(talloc_tos(), smb_fname);
3830 if (smb_fname_cp == NULL) {
3831 return -1;
3835 * 0 byte resource fork streams are not listed by
3836 * vfs_streaminfo, as a result stream cleanup/deletion of file
3837 * deletion doesn't remove the resourcefork stream.
3840 ret = SMB_VFS_NEXT_STAT(handle, smb_fname_cp);
3841 if (ret != 0) {
3842 TALLOC_FREE(smb_fname_cp);
3843 DBG_ERR("stat [%s] failed [%s]\n",
3844 smb_fname_str_dbg(smb_fname_cp), strerror(errno));
3845 return -1;
3848 size = smb_fname_cp->st.st_ex_size;
3849 TALLOC_FREE(smb_fname_cp);
3851 if (size > 0) {
3852 /* OS X ignores resource fork stream delete requests */
3853 return 0;
3857 ret = SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3858 if ((ret != 0) && (errno == ENOENT) && force_unlink) {
3859 ret = 0;
3862 return ret;
3865 static int fruit_unlink_rsrc_adouble(vfs_handle_struct *handle,
3866 const struct smb_filename *smb_fname,
3867 bool force_unlink)
3869 int rc;
3870 struct adouble *ad = NULL;
3871 struct smb_filename *adp_smb_fname = NULL;
3873 if (!force_unlink) {
3874 ad = ad_get(talloc_tos(), handle, smb_fname,
3875 ADOUBLE_RSRC);
3876 if (ad == NULL) {
3877 errno = ENOENT;
3878 return -1;
3883 * 0 byte resource fork streams are not listed by
3884 * vfs_streaminfo, as a result stream cleanup/deletion of file
3885 * deletion doesn't remove the resourcefork stream.
3888 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
3889 /* OS X ignores resource fork stream delete requests */
3890 TALLOC_FREE(ad);
3891 return 0;
3894 TALLOC_FREE(ad);
3897 rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
3898 if (rc != 0) {
3899 return -1;
3902 rc = SMB_VFS_NEXT_UNLINK(handle, adp_smb_fname);
3903 TALLOC_FREE(adp_smb_fname);
3904 if ((rc != 0) && (errno == ENOENT) && force_unlink) {
3905 rc = 0;
3908 return rc;
3911 static int fruit_unlink_rsrc_xattr(vfs_handle_struct *handle,
3912 const struct smb_filename *smb_fname,
3913 bool force_unlink)
3916 * OS X ignores resource fork stream delete requests, so nothing to do
3917 * here. Removing the file will remove the xattr anyway, so we don't
3918 * have to take care of removing 0 byte resource forks that could be
3919 * left behind.
3921 return 0;
3924 static int fruit_unlink_rsrc(vfs_handle_struct *handle,
3925 const struct smb_filename *smb_fname,
3926 bool force_unlink)
3928 struct fruit_config_data *config = NULL;
3929 int rc;
3931 SMB_VFS_HANDLE_GET_DATA(handle, config,
3932 struct fruit_config_data, return -1);
3934 switch (config->rsrc) {
3935 case FRUIT_RSRC_STREAM:
3936 rc = fruit_unlink_rsrc_stream(handle, smb_fname, force_unlink);
3937 break;
3939 case FRUIT_RSRC_ADFILE:
3940 rc = fruit_unlink_rsrc_adouble(handle, smb_fname, force_unlink);
3941 break;
3943 case FRUIT_RSRC_XATTR:
3944 rc = fruit_unlink_rsrc_xattr(handle, smb_fname, force_unlink);
3945 break;
3947 default:
3948 DBG_ERR("Unsupported rsrc config [%d]\n", config->rsrc);
3949 return -1;
3952 return rc;
3955 static int fruit_unlink(vfs_handle_struct *handle,
3956 const struct smb_filename *smb_fname)
3958 int rc;
3959 struct fruit_config_data *config = NULL;
3960 struct smb_filename *rsrc_smb_fname = NULL;
3962 SMB_VFS_HANDLE_GET_DATA(handle, config,
3963 struct fruit_config_data, return -1);
3965 if (is_afpinfo_stream(smb_fname)) {
3966 return fruit_unlink_meta(handle, smb_fname);
3967 } else if (is_afpresource_stream(smb_fname)) {
3968 return fruit_unlink_rsrc(handle, smb_fname, false);
3969 } if (is_ntfs_stream_smb_fname(smb_fname)) {
3970 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3974 * A request to delete the base file. Because 0 byte resource
3975 * fork streams are not listed by fruit_streaminfo,
3976 * delete_all_streams() can't remove 0 byte resource fork
3977 * streams, so we have to cleanup this here.
3979 rsrc_smb_fname = synthetic_smb_fname(talloc_tos(),
3980 smb_fname->base_name,
3981 AFPRESOURCE_STREAM_NAME,
3982 NULL,
3983 smb_fname->flags);
3984 if (rsrc_smb_fname == NULL) {
3985 return -1;
3988 rc = fruit_unlink_rsrc(handle, rsrc_smb_fname, true);
3989 if ((rc != 0) && (errno != ENOENT)) {
3990 DBG_ERR("Forced unlink of [%s] failed [%s]\n",
3991 smb_fname_str_dbg(rsrc_smb_fname), strerror(errno));
3992 TALLOC_FREE(rsrc_smb_fname);
3993 return -1;
3995 TALLOC_FREE(rsrc_smb_fname);
3997 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4000 static int fruit_chmod(vfs_handle_struct *handle,
4001 const struct smb_filename *smb_fname,
4002 mode_t mode)
4004 int rc = -1;
4005 struct fruit_config_data *config = NULL;
4006 struct smb_filename *smb_fname_adp = NULL;
4008 rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname, mode);
4009 if (rc != 0) {
4010 return rc;
4013 SMB_VFS_HANDLE_GET_DATA(handle, config,
4014 struct fruit_config_data, return -1);
4016 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4017 return 0;
4020 if (!VALID_STAT(smb_fname->st)) {
4021 return 0;
4024 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
4025 return 0;
4028 rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_adp);
4029 if (rc != 0) {
4030 return -1;
4033 DEBUG(10, ("fruit_chmod: %s\n", smb_fname_adp->base_name));
4035 rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname_adp, mode);
4036 if (errno == ENOENT) {
4037 rc = 0;
4040 TALLOC_FREE(smb_fname_adp);
4041 return rc;
4044 static int fruit_chown(vfs_handle_struct *handle,
4045 const struct smb_filename *smb_fname,
4046 uid_t uid,
4047 gid_t gid)
4049 int rc = -1;
4050 struct fruit_config_data *config = NULL;
4051 struct smb_filename *adp_smb_fname = NULL;
4053 rc = SMB_VFS_NEXT_CHOWN(handle, smb_fname, uid, gid);
4054 if (rc != 0) {
4055 return rc;
4058 SMB_VFS_HANDLE_GET_DATA(handle, config,
4059 struct fruit_config_data, return -1);
4061 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4062 return 0;
4065 if (!VALID_STAT(smb_fname->st)) {
4066 return 0;
4069 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
4070 return 0;
4073 rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
4074 if (rc != 0) {
4075 goto done;
4078 DEBUG(10, ("fruit_chown: %s\n", adp_smb_fname->base_name));
4080 rc = SMB_VFS_NEXT_CHOWN(handle, adp_smb_fname, uid, gid);
4081 if (errno == ENOENT) {
4082 rc = 0;
4085 done:
4086 TALLOC_FREE(adp_smb_fname);
4087 return rc;
4090 static int fruit_rmdir(struct vfs_handle_struct *handle,
4091 const struct smb_filename *smb_fname)
4093 DIR *dh = NULL;
4094 struct dirent *de;
4095 struct fruit_config_data *config;
4097 SMB_VFS_HANDLE_GET_DATA(handle, config,
4098 struct fruit_config_data, return -1);
4100 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4101 goto exit_rmdir;
4105 * Due to there is no way to change bDeleteVetoFiles variable
4106 * from this module, need to clean up ourselves
4109 dh = SMB_VFS_OPENDIR(handle->conn, smb_fname, NULL, 0);
4110 if (dh == NULL) {
4111 goto exit_rmdir;
4114 while ((de = SMB_VFS_READDIR(handle->conn, dh, NULL)) != NULL) {
4115 int match;
4116 struct adouble *ad = NULL;
4117 char *p = NULL;
4118 struct smb_filename *ad_smb_fname = NULL;
4119 int ret;
4121 match = strncmp(de->d_name,
4122 ADOUBLE_NAME_PREFIX,
4123 strlen(ADOUBLE_NAME_PREFIX));
4124 if (match != 0) {
4125 continue;
4128 p = talloc_asprintf(talloc_tos(), "%s/%s",
4129 smb_fname->base_name, de->d_name);
4130 if (p == NULL) {
4131 DBG_ERR("talloc_asprintf failed\n");
4132 return -1;
4135 ad_smb_fname = synthetic_smb_fname(talloc_tos(), p,
4136 NULL, NULL,
4137 smb_fname->flags);
4138 TALLOC_FREE(p);
4139 if (ad_smb_fname == NULL) {
4140 DBG_ERR("synthetic_smb_fname failed\n");
4141 return -1;
4145 * Check whether it's a valid AppleDouble file, if
4146 * yes, delete it, ignore it otherwise.
4148 ad = ad_get(talloc_tos(), handle, ad_smb_fname, ADOUBLE_RSRC);
4149 if (ad == NULL) {
4150 TALLOC_FREE(ad_smb_fname);
4151 TALLOC_FREE(p);
4152 continue;
4154 TALLOC_FREE(ad);
4156 ret = SMB_VFS_NEXT_UNLINK(handle, ad_smb_fname);
4157 if (ret != 0) {
4158 DBG_ERR("Deleting [%s] failed\n",
4159 smb_fname_str_dbg(ad_smb_fname));
4161 TALLOC_FREE(ad_smb_fname);
4164 exit_rmdir:
4165 if (dh) {
4166 SMB_VFS_CLOSEDIR(handle->conn, dh);
4168 return SMB_VFS_NEXT_RMDIR(handle, smb_fname);
4171 static ssize_t fruit_pread_meta_stream(vfs_handle_struct *handle,
4172 files_struct *fsp, void *data,
4173 size_t n, off_t offset)
4175 ssize_t nread;
4176 int ret;
4178 nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4180 if (nread == n) {
4181 return nread;
4184 DBG_ERR("Removing [%s] after short read [%zd]\n",
4185 fsp_str_dbg(fsp), nread);
4187 ret = SMB_VFS_NEXT_UNLINK(handle, fsp->fsp_name);
4188 if (ret != 0) {
4189 DBG_ERR("Removing [%s] failed\n", fsp_str_dbg(fsp));
4190 return -1;
4193 errno = EINVAL;
4194 return -1;
4197 static ssize_t fruit_pread_meta_adouble(vfs_handle_struct *handle,
4198 files_struct *fsp, void *data,
4199 size_t n, off_t offset)
4201 AfpInfo *ai = NULL;
4202 struct adouble *ad = NULL;
4203 char afpinfo_buf[AFP_INFO_SIZE];
4204 char *p = NULL;
4205 ssize_t nread;
4207 ai = afpinfo_new(talloc_tos());
4208 if (ai == NULL) {
4209 return -1;
4212 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
4213 if (ad == NULL) {
4214 nread = -1;
4215 goto fail;
4218 p = ad_get_entry(ad, ADEID_FINDERI);
4219 if (p == NULL) {
4220 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
4221 nread = -1;
4222 goto fail;
4225 memcpy(&ai->afpi_FinderInfo[0], p, ADEDLEN_FINDERI);
4227 nread = afpinfo_pack(ai, afpinfo_buf);
4228 if (nread != AFP_INFO_SIZE) {
4229 nread = -1;
4230 goto fail;
4233 memcpy(data, afpinfo_buf, n);
4234 nread = n;
4236 fail:
4237 TALLOC_FREE(ai);
4238 return nread;
4241 static ssize_t fruit_pread_meta(vfs_handle_struct *handle,
4242 files_struct *fsp, void *data,
4243 size_t n, off_t offset)
4245 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4246 ssize_t nread;
4247 ssize_t to_return;
4250 * OS X has a off-by-1 error in the offset calculation, so we're
4251 * bug compatible here. It won't hurt, as any relevant real
4252 * world read requests from the AFP_AfpInfo stream will be
4253 * offset=0 n=60. offset is ignored anyway, see below.
4255 if ((offset < 0) || (offset >= AFP_INFO_SIZE + 1)) {
4256 return 0;
4259 if (fio == NULL) {
4260 DBG_ERR("Failed to fetch fsp extension");
4261 return -1;
4264 /* Yes, macOS always reads from offset 0 */
4265 offset = 0;
4266 to_return = MIN(n, AFP_INFO_SIZE);
4268 switch (fio->config->meta) {
4269 case FRUIT_META_STREAM:
4270 nread = fruit_pread_meta_stream(handle, fsp, data,
4271 to_return, offset);
4272 break;
4274 case FRUIT_META_NETATALK:
4275 nread = fruit_pread_meta_adouble(handle, fsp, data,
4276 to_return, offset);
4277 break;
4279 default:
4280 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
4281 return -1;
4284 return nread;
4287 static ssize_t fruit_pread_rsrc_stream(vfs_handle_struct *handle,
4288 files_struct *fsp, void *data,
4289 size_t n, off_t offset)
4291 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4294 static ssize_t fruit_pread_rsrc_xattr(vfs_handle_struct *handle,
4295 files_struct *fsp, void *data,
4296 size_t n, off_t offset)
4298 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4301 static ssize_t fruit_pread_rsrc_adouble(vfs_handle_struct *handle,
4302 files_struct *fsp, void *data,
4303 size_t n, off_t offset)
4305 struct adouble *ad = NULL;
4306 ssize_t nread;
4308 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
4309 if (ad == NULL) {
4310 return -1;
4313 nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n,
4314 offset + ad_getentryoff(ad, ADEID_RFORK));
4316 TALLOC_FREE(ad);
4317 return nread;
4320 static ssize_t fruit_pread_rsrc(vfs_handle_struct *handle,
4321 files_struct *fsp, void *data,
4322 size_t n, off_t offset)
4324 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4325 ssize_t nread;
4327 if (fio == NULL) {
4328 errno = EINVAL;
4329 return -1;
4332 switch (fio->config->rsrc) {
4333 case FRUIT_RSRC_STREAM:
4334 nread = fruit_pread_rsrc_stream(handle, fsp, data, n, offset);
4335 break;
4337 case FRUIT_RSRC_ADFILE:
4338 nread = fruit_pread_rsrc_adouble(handle, fsp, data, n, offset);
4339 break;
4341 case FRUIT_RSRC_XATTR:
4342 nread = fruit_pread_rsrc_xattr(handle, fsp, data, n, offset);
4343 break;
4345 default:
4346 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
4347 return -1;
4350 return nread;
4353 static ssize_t fruit_pread(vfs_handle_struct *handle,
4354 files_struct *fsp, void *data,
4355 size_t n, off_t offset)
4357 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4358 ssize_t nread;
4360 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4361 fsp_str_dbg(fsp), (intmax_t)offset, n);
4363 if (fio == NULL) {
4364 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4367 if (fio->type == ADOUBLE_META) {
4368 nread = fruit_pread_meta(handle, fsp, data, n, offset);
4369 } else {
4370 nread = fruit_pread_rsrc(handle, fsp, data, n, offset);
4373 DBG_DEBUG("Path [%s] nread [%zd]\n", fsp_str_dbg(fsp), nread);
4374 return nread;
4377 static bool fruit_must_handle_aio_stream(struct fio *fio)
4379 if (fio == NULL) {
4380 return false;
4383 if ((fio->type == ADOUBLE_META) &&
4384 (fio->config->meta == FRUIT_META_NETATALK))
4386 return true;
4389 if ((fio->type == ADOUBLE_RSRC) &&
4390 (fio->config->rsrc == FRUIT_RSRC_ADFILE))
4392 return true;
4395 return false;
4398 struct fruit_pread_state {
4399 ssize_t nread;
4400 struct vfs_aio_state vfs_aio_state;
4403 static void fruit_pread_done(struct tevent_req *subreq);
4405 static struct tevent_req *fruit_pread_send(
4406 struct vfs_handle_struct *handle,
4407 TALLOC_CTX *mem_ctx,
4408 struct tevent_context *ev,
4409 struct files_struct *fsp,
4410 void *data,
4411 size_t n, off_t offset)
4413 struct tevent_req *req = NULL;
4414 struct tevent_req *subreq = NULL;
4415 struct fruit_pread_state *state = NULL;
4416 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4418 req = tevent_req_create(mem_ctx, &state,
4419 struct fruit_pread_state);
4420 if (req == NULL) {
4421 return NULL;
4424 if (fruit_must_handle_aio_stream(fio)) {
4425 state->nread = SMB_VFS_PREAD(fsp, data, n, offset);
4426 if (state->nread != n) {
4427 if (state->nread != -1) {
4428 errno = EIO;
4430 tevent_req_error(req, errno);
4431 return tevent_req_post(req, ev);
4433 tevent_req_done(req);
4434 return tevent_req_post(req, ev);
4437 subreq = SMB_VFS_NEXT_PREAD_SEND(state, ev, handle, fsp,
4438 data, n, offset);
4439 if (tevent_req_nomem(req, subreq)) {
4440 return tevent_req_post(req, ev);
4442 tevent_req_set_callback(subreq, fruit_pread_done, req);
4443 return req;
4446 static void fruit_pread_done(struct tevent_req *subreq)
4448 struct tevent_req *req = tevent_req_callback_data(
4449 subreq, struct tevent_req);
4450 struct fruit_pread_state *state = tevent_req_data(
4451 req, struct fruit_pread_state);
4453 state->nread = SMB_VFS_PREAD_RECV(subreq, &state->vfs_aio_state);
4454 TALLOC_FREE(subreq);
4456 if (tevent_req_error(req, state->vfs_aio_state.error)) {
4457 return;
4459 tevent_req_done(req);
4462 static ssize_t fruit_pread_recv(struct tevent_req *req,
4463 struct vfs_aio_state *vfs_aio_state)
4465 struct fruit_pread_state *state = tevent_req_data(
4466 req, struct fruit_pread_state);
4468 if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
4469 return -1;
4472 *vfs_aio_state = state->vfs_aio_state;
4473 return state->nread;
4476 static ssize_t fruit_pwrite_meta_stream(vfs_handle_struct *handle,
4477 files_struct *fsp, const void *data,
4478 size_t n, off_t offset)
4480 AfpInfo *ai = NULL;
4481 size_t nwritten;
4482 bool ok;
4484 ai = afpinfo_unpack(talloc_tos(), data);
4485 if (ai == NULL) {
4486 return -1;
4489 nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4490 if (nwritten != n) {
4491 return -1;
4494 if (!ai_empty_finderinfo(ai)) {
4495 return n;
4498 ok = set_delete_on_close(
4499 fsp,
4500 true,
4501 handle->conn->session_info->security_token,
4502 handle->conn->session_info->unix_token);
4503 if (!ok) {
4504 DBG_ERR("set_delete_on_close on [%s] failed\n",
4505 fsp_str_dbg(fsp));
4506 return -1;
4509 return n;
4512 static ssize_t fruit_pwrite_meta_netatalk(vfs_handle_struct *handle,
4513 files_struct *fsp, const void *data,
4514 size_t n, off_t offset)
4516 struct adouble *ad = NULL;
4517 AfpInfo *ai = NULL;
4518 char *p = NULL;
4519 int ret;
4520 bool ok;
4522 ai = afpinfo_unpack(talloc_tos(), data);
4523 if (ai == NULL) {
4524 return -1;
4527 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
4528 if (ad == NULL) {
4529 ad = ad_init(talloc_tos(), handle, ADOUBLE_META);
4530 if (ad == NULL) {
4531 return -1;
4534 p = ad_get_entry(ad, ADEID_FINDERI);
4535 if (p == NULL) {
4536 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
4537 TALLOC_FREE(ad);
4538 return -1;
4541 memcpy(p, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
4543 ret = ad_fset(ad, fsp);
4544 if (ret != 0) {
4545 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
4546 TALLOC_FREE(ad);
4547 return -1;
4550 TALLOC_FREE(ad);
4552 if (!ai_empty_finderinfo(ai)) {
4553 return n;
4556 ok = set_delete_on_close(
4557 fsp,
4558 true,
4559 handle->conn->session_info->security_token,
4560 handle->conn->session_info->unix_token);
4561 if (!ok) {
4562 DBG_ERR("set_delete_on_close on [%s] failed\n",
4563 fsp_str_dbg(fsp));
4564 return -1;
4567 return n;
4570 static ssize_t fruit_pwrite_meta(vfs_handle_struct *handle,
4571 files_struct *fsp, const void *data,
4572 size_t n, off_t offset)
4574 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4575 ssize_t nwritten;
4578 * Writing an all 0 blob to the metadata stream
4579 * results in the stream being removed on a macOS
4580 * server. This ensures we behave the same and it
4581 * verified by the "delete AFP_AfpInfo by writing all
4582 * 0" test.
4584 if (n != AFP_INFO_SIZE || offset != 0) {
4585 DBG_ERR("unexpected offset=%jd or size=%jd\n",
4586 (intmax_t)offset, (intmax_t)n);
4587 return -1;
4590 if (fio == NULL) {
4591 DBG_ERR("Failed to fetch fsp extension");
4592 return -1;
4595 switch (fio->config->meta) {
4596 case FRUIT_META_STREAM:
4597 nwritten = fruit_pwrite_meta_stream(handle, fsp, data,
4598 n, offset);
4599 break;
4601 case FRUIT_META_NETATALK:
4602 nwritten = fruit_pwrite_meta_netatalk(handle, fsp, data,
4603 n, offset);
4604 break;
4606 default:
4607 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
4608 return -1;
4611 return nwritten;
4614 static ssize_t fruit_pwrite_rsrc_stream(vfs_handle_struct *handle,
4615 files_struct *fsp, const void *data,
4616 size_t n, off_t offset)
4618 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4621 static ssize_t fruit_pwrite_rsrc_xattr(vfs_handle_struct *handle,
4622 files_struct *fsp, const void *data,
4623 size_t n, off_t offset)
4625 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4628 static ssize_t fruit_pwrite_rsrc_adouble(vfs_handle_struct *handle,
4629 files_struct *fsp, const void *data,
4630 size_t n, off_t offset)
4632 struct adouble *ad = NULL;
4633 ssize_t nwritten;
4634 int ret;
4636 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
4637 if (ad == NULL) {
4638 DBG_ERR("ad_get [%s] failed\n", fsp_str_dbg(fsp));
4639 return -1;
4642 nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n,
4643 offset + ad_getentryoff(ad, ADEID_RFORK));
4644 if (nwritten != n) {
4645 DBG_ERR("Short write on [%s] [%zd/%zd]\n",
4646 fsp_str_dbg(fsp), nwritten, n);
4647 TALLOC_FREE(ad);
4648 return -1;
4651 if ((n + offset) > ad_getentrylen(ad, ADEID_RFORK)) {
4652 ad_setentrylen(ad, ADEID_RFORK, n + offset);
4653 ret = ad_fset(ad, fsp);
4654 if (ret != 0) {
4655 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
4656 TALLOC_FREE(ad);
4657 return -1;
4661 TALLOC_FREE(ad);
4662 return n;
4665 static ssize_t fruit_pwrite_rsrc(vfs_handle_struct *handle,
4666 files_struct *fsp, const void *data,
4667 size_t n, off_t offset)
4669 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4670 ssize_t nwritten;
4672 if (fio == NULL) {
4673 DBG_ERR("Failed to fetch fsp extension");
4674 return -1;
4677 switch (fio->config->rsrc) {
4678 case FRUIT_RSRC_STREAM:
4679 nwritten = fruit_pwrite_rsrc_stream(handle, fsp, data, n, offset);
4680 break;
4682 case FRUIT_RSRC_ADFILE:
4683 nwritten = fruit_pwrite_rsrc_adouble(handle, fsp, data, n, offset);
4684 break;
4686 case FRUIT_RSRC_XATTR:
4687 nwritten = fruit_pwrite_rsrc_xattr(handle, fsp, data, n, offset);
4688 break;
4690 default:
4691 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
4692 return -1;
4695 return nwritten;
4698 static ssize_t fruit_pwrite(vfs_handle_struct *handle,
4699 files_struct *fsp, const void *data,
4700 size_t n, off_t offset)
4702 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4703 ssize_t nwritten;
4705 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4706 fsp_str_dbg(fsp), (intmax_t)offset, n);
4708 if (fio == NULL) {
4709 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4712 if (fio->type == ADOUBLE_META) {
4713 nwritten = fruit_pwrite_meta(handle, fsp, data, n, offset);
4714 } else {
4715 nwritten = fruit_pwrite_rsrc(handle, fsp, data, n, offset);
4718 DBG_DEBUG("Path [%s] nwritten=%zd\n", fsp_str_dbg(fsp), nwritten);
4719 return nwritten;
4722 struct fruit_pwrite_state {
4723 ssize_t nwritten;
4724 struct vfs_aio_state vfs_aio_state;
4727 static void fruit_pwrite_done(struct tevent_req *subreq);
4729 static struct tevent_req *fruit_pwrite_send(
4730 struct vfs_handle_struct *handle,
4731 TALLOC_CTX *mem_ctx,
4732 struct tevent_context *ev,
4733 struct files_struct *fsp,
4734 const void *data,
4735 size_t n, off_t offset)
4737 struct tevent_req *req = NULL;
4738 struct tevent_req *subreq = NULL;
4739 struct fruit_pwrite_state *state = NULL;
4740 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4742 req = tevent_req_create(mem_ctx, &state,
4743 struct fruit_pwrite_state);
4744 if (req == NULL) {
4745 return NULL;
4748 if (fruit_must_handle_aio_stream(fio)) {
4749 state->nwritten = SMB_VFS_PWRITE(fsp, data, n, offset);
4750 if (state->nwritten != n) {
4751 if (state->nwritten != -1) {
4752 errno = EIO;
4754 tevent_req_error(req, errno);
4755 return tevent_req_post(req, ev);
4757 tevent_req_done(req);
4758 return tevent_req_post(req, ev);
4761 subreq = SMB_VFS_NEXT_PWRITE_SEND(state, ev, handle, fsp,
4762 data, n, offset);
4763 if (tevent_req_nomem(req, subreq)) {
4764 return tevent_req_post(req, ev);
4766 tevent_req_set_callback(subreq, fruit_pwrite_done, req);
4767 return req;
4770 static void fruit_pwrite_done(struct tevent_req *subreq)
4772 struct tevent_req *req = tevent_req_callback_data(
4773 subreq, struct tevent_req);
4774 struct fruit_pwrite_state *state = tevent_req_data(
4775 req, struct fruit_pwrite_state);
4777 state->nwritten = SMB_VFS_PWRITE_RECV(subreq, &state->vfs_aio_state);
4778 TALLOC_FREE(subreq);
4780 if (tevent_req_error(req, state->vfs_aio_state.error)) {
4781 return;
4783 tevent_req_done(req);
4786 static ssize_t fruit_pwrite_recv(struct tevent_req *req,
4787 struct vfs_aio_state *vfs_aio_state)
4789 struct fruit_pwrite_state *state = tevent_req_data(
4790 req, struct fruit_pwrite_state);
4792 if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
4793 return -1;
4796 *vfs_aio_state = state->vfs_aio_state;
4797 return state->nwritten;
4801 * Helper to stat/lstat the base file of an smb_fname.
4803 static int fruit_stat_base(vfs_handle_struct *handle,
4804 struct smb_filename *smb_fname,
4805 bool follow_links)
4807 char *tmp_stream_name;
4808 int rc;
4810 tmp_stream_name = smb_fname->stream_name;
4811 smb_fname->stream_name = NULL;
4812 if (follow_links) {
4813 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
4814 } else {
4815 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
4817 smb_fname->stream_name = tmp_stream_name;
4818 return rc;
4821 static int fruit_stat_meta_stream(vfs_handle_struct *handle,
4822 struct smb_filename *smb_fname,
4823 bool follow_links)
4825 int ret;
4827 if (follow_links) {
4828 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
4829 } else {
4830 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
4833 return ret;
4836 static int fruit_stat_meta_netatalk(vfs_handle_struct *handle,
4837 struct smb_filename *smb_fname,
4838 bool follow_links)
4840 struct adouble *ad = NULL;
4842 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
4843 if (ad == NULL) {
4844 DBG_INFO("fruit_stat_meta %s: %s\n",
4845 smb_fname_str_dbg(smb_fname), strerror(errno));
4846 errno = ENOENT;
4847 return -1;
4849 TALLOC_FREE(ad);
4851 /* Populate the stat struct with info from the base file. */
4852 if (fruit_stat_base(handle, smb_fname, follow_links) == -1) {
4853 return -1;
4855 smb_fname->st.st_ex_size = AFP_INFO_SIZE;
4856 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
4857 smb_fname->stream_name);
4858 return 0;
4861 static int fruit_stat_meta(vfs_handle_struct *handle,
4862 struct smb_filename *smb_fname,
4863 bool follow_links)
4865 struct fruit_config_data *config = NULL;
4866 int ret;
4868 SMB_VFS_HANDLE_GET_DATA(handle, config,
4869 struct fruit_config_data, return -1);
4871 switch (config->meta) {
4872 case FRUIT_META_STREAM:
4873 ret = fruit_stat_meta_stream(handle, smb_fname, follow_links);
4874 break;
4876 case FRUIT_META_NETATALK:
4877 ret = fruit_stat_meta_netatalk(handle, smb_fname, follow_links);
4878 break;
4880 default:
4881 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
4882 return -1;
4885 return ret;
4888 static int fruit_stat_rsrc_netatalk(vfs_handle_struct *handle,
4889 struct smb_filename *smb_fname,
4890 bool follow_links)
4892 struct adouble *ad = NULL;
4893 int ret;
4895 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
4896 if (ad == NULL) {
4897 errno = ENOENT;
4898 return -1;
4901 /* Populate the stat struct with info from the base file. */
4902 ret = fruit_stat_base(handle, smb_fname, follow_links);
4903 if (ret != 0) {
4904 TALLOC_FREE(ad);
4905 return -1;
4908 smb_fname->st.st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
4909 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
4910 smb_fname->stream_name);
4911 TALLOC_FREE(ad);
4912 return 0;
4915 static int fruit_stat_rsrc_stream(vfs_handle_struct *handle,
4916 struct smb_filename *smb_fname,
4917 bool follow_links)
4919 int ret;
4921 if (follow_links) {
4922 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
4923 } else {
4924 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
4927 return ret;
4930 static int fruit_stat_rsrc_xattr(vfs_handle_struct *handle,
4931 struct smb_filename *smb_fname,
4932 bool follow_links)
4934 #ifdef HAVE_ATTROPEN
4935 int ret;
4936 int fd = -1;
4938 /* Populate the stat struct with info from the base file. */
4939 ret = fruit_stat_base(handle, smb_fname, follow_links);
4940 if (ret != 0) {
4941 return -1;
4944 fd = attropen(smb_fname->base_name,
4945 AFPRESOURCE_EA_NETATALK,
4946 O_RDONLY);
4947 if (fd == -1) {
4948 return 0;
4951 ret = sys_fstat(fd, &smb_fname->st, false);
4952 if (ret != 0) {
4953 close(fd);
4954 DBG_ERR("fstat [%s:%s] failed\n", smb_fname->base_name,
4955 AFPRESOURCE_EA_NETATALK);
4956 return -1;
4958 close(fd);
4959 fd = -1;
4961 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
4962 smb_fname->stream_name);
4964 return ret;
4966 #else
4967 errno = ENOSYS;
4968 return -1;
4969 #endif
4972 static int fruit_stat_rsrc(vfs_handle_struct *handle,
4973 struct smb_filename *smb_fname,
4974 bool follow_links)
4976 struct fruit_config_data *config = NULL;
4977 int ret;
4979 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
4981 SMB_VFS_HANDLE_GET_DATA(handle, config,
4982 struct fruit_config_data, return -1);
4984 switch (config->rsrc) {
4985 case FRUIT_RSRC_STREAM:
4986 ret = fruit_stat_rsrc_stream(handle, smb_fname, follow_links);
4987 break;
4989 case FRUIT_RSRC_XATTR:
4990 ret = fruit_stat_rsrc_xattr(handle, smb_fname, follow_links);
4991 break;
4993 case FRUIT_RSRC_ADFILE:
4994 ret = fruit_stat_rsrc_netatalk(handle, smb_fname, follow_links);
4995 break;
4997 default:
4998 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
4999 return -1;
5002 return ret;
5005 static int fruit_stat(vfs_handle_struct *handle,
5006 struct smb_filename *smb_fname)
5008 int rc = -1;
5010 DEBUG(10, ("fruit_stat called for %s\n",
5011 smb_fname_str_dbg(smb_fname)));
5013 if (!is_ntfs_stream_smb_fname(smb_fname)
5014 || is_ntfs_default_stream_smb_fname(smb_fname)) {
5015 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
5016 if (rc == 0) {
5017 update_btime(handle, smb_fname);
5019 return rc;
5023 * Note if lp_posix_paths() is true, we can never
5024 * get here as is_ntfs_stream_smb_fname() is
5025 * always false. So we never need worry about
5026 * not following links here.
5029 if (is_afpinfo_stream(smb_fname)) {
5030 rc = fruit_stat_meta(handle, smb_fname, true);
5031 } else if (is_afpresource_stream(smb_fname)) {
5032 rc = fruit_stat_rsrc(handle, smb_fname, true);
5033 } else {
5034 return SMB_VFS_NEXT_STAT(handle, smb_fname);
5037 if (rc == 0) {
5038 update_btime(handle, smb_fname);
5039 smb_fname->st.st_ex_mode &= ~S_IFMT;
5040 smb_fname->st.st_ex_mode |= S_IFREG;
5041 smb_fname->st.st_ex_blocks =
5042 smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
5044 return rc;
5047 static int fruit_lstat(vfs_handle_struct *handle,
5048 struct smb_filename *smb_fname)
5050 int rc = -1;
5052 DEBUG(10, ("fruit_lstat called for %s\n",
5053 smb_fname_str_dbg(smb_fname)));
5055 if (!is_ntfs_stream_smb_fname(smb_fname)
5056 || is_ntfs_default_stream_smb_fname(smb_fname)) {
5057 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5058 if (rc == 0) {
5059 update_btime(handle, smb_fname);
5061 return rc;
5064 if (is_afpinfo_stream(smb_fname)) {
5065 rc = fruit_stat_meta(handle, smb_fname, false);
5066 } else if (is_afpresource_stream(smb_fname)) {
5067 rc = fruit_stat_rsrc(handle, smb_fname, false);
5068 } else {
5069 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5072 if (rc == 0) {
5073 update_btime(handle, smb_fname);
5074 smb_fname->st.st_ex_mode &= ~S_IFMT;
5075 smb_fname->st.st_ex_mode |= S_IFREG;
5076 smb_fname->st.st_ex_blocks =
5077 smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
5079 return rc;
5082 static int fruit_fstat_meta_stream(vfs_handle_struct *handle,
5083 files_struct *fsp,
5084 SMB_STRUCT_STAT *sbuf)
5086 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5089 static int fruit_fstat_meta_netatalk(vfs_handle_struct *handle,
5090 files_struct *fsp,
5091 SMB_STRUCT_STAT *sbuf)
5093 int ret;
5095 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5096 if (ret != 0) {
5097 return -1;
5100 *sbuf = fsp->base_fsp->fsp_name->st;
5101 sbuf->st_ex_size = AFP_INFO_SIZE;
5102 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5104 return 0;
5107 static int fruit_fstat_meta(vfs_handle_struct *handle,
5108 files_struct *fsp,
5109 SMB_STRUCT_STAT *sbuf,
5110 struct fio *fio)
5112 int ret;
5114 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
5116 switch (fio->config->meta) {
5117 case FRUIT_META_STREAM:
5118 ret = fruit_fstat_meta_stream(handle, fsp, sbuf);
5119 break;
5121 case FRUIT_META_NETATALK:
5122 ret = fruit_fstat_meta_netatalk(handle, fsp, sbuf);
5123 break;
5125 default:
5126 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
5127 return -1;
5130 DBG_DEBUG("Path [%s] ret [%d]\n", fsp_str_dbg(fsp), ret);
5131 return ret;
5134 static int fruit_fstat_rsrc_xattr(vfs_handle_struct *handle,
5135 files_struct *fsp,
5136 SMB_STRUCT_STAT *sbuf)
5138 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5141 static int fruit_fstat_rsrc_stream(vfs_handle_struct *handle,
5142 files_struct *fsp,
5143 SMB_STRUCT_STAT *sbuf)
5145 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5148 static int fruit_fstat_rsrc_adouble(vfs_handle_struct *handle,
5149 files_struct *fsp,
5150 SMB_STRUCT_STAT *sbuf)
5152 struct adouble *ad = NULL;
5153 int ret;
5155 /* Populate the stat struct with info from the base file. */
5156 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5157 if (ret == -1) {
5158 return -1;
5161 ad = ad_get(talloc_tos(), handle,
5162 fsp->base_fsp->fsp_name,
5163 ADOUBLE_RSRC);
5164 if (ad == NULL) {
5165 DBG_ERR("ad_get [%s] failed [%s]\n",
5166 fsp_str_dbg(fsp), strerror(errno));
5167 return -1;
5170 *sbuf = fsp->base_fsp->fsp_name->st;
5171 sbuf->st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
5172 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5174 TALLOC_FREE(ad);
5175 return 0;
5178 static int fruit_fstat_rsrc(vfs_handle_struct *handle, files_struct *fsp,
5179 SMB_STRUCT_STAT *sbuf, struct fio *fio)
5181 int ret;
5183 switch (fio->config->rsrc) {
5184 case FRUIT_RSRC_STREAM:
5185 ret = fruit_fstat_rsrc_stream(handle, fsp, sbuf);
5186 break;
5188 case FRUIT_RSRC_ADFILE:
5189 ret = fruit_fstat_rsrc_adouble(handle, fsp, sbuf);
5190 break;
5192 case FRUIT_RSRC_XATTR:
5193 ret = fruit_fstat_rsrc_xattr(handle, fsp, sbuf);
5194 break;
5196 default:
5197 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
5198 return -1;
5201 return ret;
5204 static int fruit_fstat(vfs_handle_struct *handle, files_struct *fsp,
5205 SMB_STRUCT_STAT *sbuf)
5207 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5208 int rc;
5210 if (fio == NULL) {
5211 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5214 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
5216 if (fio->type == ADOUBLE_META) {
5217 rc = fruit_fstat_meta(handle, fsp, sbuf, fio);
5218 } else {
5219 rc = fruit_fstat_rsrc(handle, fsp, sbuf, fio);
5222 if (rc == 0) {
5223 sbuf->st_ex_mode &= ~S_IFMT;
5224 sbuf->st_ex_mode |= S_IFREG;
5225 sbuf->st_ex_blocks = sbuf->st_ex_size / STAT_ST_BLOCKSIZE + 1;
5228 DBG_DEBUG("Path [%s] rc [%d] size [%"PRIdMAX"]\n",
5229 fsp_str_dbg(fsp), rc, (intmax_t)sbuf->st_ex_size);
5230 return rc;
5233 static NTSTATUS delete_invalid_meta_stream(
5234 vfs_handle_struct *handle,
5235 const struct smb_filename *smb_fname,
5236 TALLOC_CTX *mem_ctx,
5237 unsigned int *pnum_streams,
5238 struct stream_struct **pstreams)
5240 struct smb_filename *sname = NULL;
5241 int ret;
5242 bool ok;
5244 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams, AFPINFO_STREAM);
5245 if (!ok) {
5246 return NT_STATUS_INTERNAL_ERROR;
5249 sname = synthetic_smb_fname(talloc_tos(),
5250 smb_fname->base_name,
5251 AFPINFO_STREAM_NAME,
5252 NULL, 0);
5253 if (sname == NULL) {
5254 return NT_STATUS_NO_MEMORY;
5257 ret = SMB_VFS_NEXT_UNLINK(handle, sname);
5258 TALLOC_FREE(sname);
5259 if (ret != 0) {
5260 DBG_ERR("Removing [%s] failed\n", smb_fname_str_dbg(sname));
5261 return map_nt_error_from_unix(errno);
5264 return NT_STATUS_OK;
5267 static NTSTATUS fruit_streaminfo_meta_stream(
5268 vfs_handle_struct *handle,
5269 struct files_struct *fsp,
5270 const struct smb_filename *smb_fname,
5271 TALLOC_CTX *mem_ctx,
5272 unsigned int *pnum_streams,
5273 struct stream_struct **pstreams)
5275 struct stream_struct *stream = *pstreams;
5276 unsigned int num_streams = *pnum_streams;
5277 struct smb_filename *sname = NULL;
5278 char *full_name = NULL;
5279 uint32_t name_hash;
5280 struct share_mode_lock *lck = NULL;
5281 struct file_id id = {0};
5282 bool delete_on_close_set;
5283 int i;
5284 int ret;
5285 NTSTATUS status;
5286 bool ok;
5288 for (i = 0; i < num_streams; i++) {
5289 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
5290 break;
5294 if (i == num_streams) {
5295 return NT_STATUS_OK;
5298 if (stream[i].size != AFP_INFO_SIZE) {
5299 DBG_ERR("Removing invalid AFPINFO_STREAM size [%jd] from [%s]\n",
5300 (intmax_t)stream[i].size, smb_fname_str_dbg(smb_fname));
5302 return delete_invalid_meta_stream(handle, smb_fname, mem_ctx,
5303 pnum_streams, pstreams);
5307 * Now check if there's a delete-on-close pending on the stream. If so,
5308 * hide the stream. This behaviour was verified against a macOS 10.12
5309 * SMB server.
5312 sname = synthetic_smb_fname(talloc_tos(),
5313 smb_fname->base_name,
5314 AFPINFO_STREAM_NAME,
5315 NULL, 0);
5316 if (sname == NULL) {
5317 status = NT_STATUS_NO_MEMORY;
5318 goto out;
5321 ret = SMB_VFS_NEXT_STAT(handle, sname);
5322 if (ret != 0) {
5323 status = map_nt_error_from_unix(errno);
5324 goto out;
5327 id = SMB_VFS_NEXT_FILE_ID_CREATE(handle, &sname->st);
5329 lck = get_existing_share_mode_lock(talloc_tos(), id);
5330 if (lck == NULL) {
5331 status = NT_STATUS_OK;
5332 goto out;
5335 full_name = talloc_asprintf(talloc_tos(),
5336 "%s%s",
5337 sname->base_name,
5338 AFPINFO_STREAM);
5339 if (full_name == NULL) {
5340 status = NT_STATUS_NO_MEMORY;
5341 goto out;
5344 status = file_name_hash(handle->conn, full_name, &name_hash);
5345 if (!NT_STATUS_IS_OK(status)) {
5346 goto out;
5349 delete_on_close_set = is_delete_on_close_set(lck, name_hash);
5350 if (delete_on_close_set) {
5351 ok = del_fruit_stream(mem_ctx,
5352 pnum_streams,
5353 pstreams,
5354 AFPINFO_STREAM);
5355 if (!ok) {
5356 status = NT_STATUS_INTERNAL_ERROR;
5357 goto out;
5361 status = NT_STATUS_OK;
5363 out:
5364 TALLOC_FREE(sname);
5365 TALLOC_FREE(lck);
5366 TALLOC_FREE(full_name);
5367 return status;
5370 static NTSTATUS fruit_streaminfo_meta_netatalk(
5371 vfs_handle_struct *handle,
5372 struct files_struct *fsp,
5373 const struct smb_filename *smb_fname,
5374 TALLOC_CTX *mem_ctx,
5375 unsigned int *pnum_streams,
5376 struct stream_struct **pstreams)
5378 struct stream_struct *stream = *pstreams;
5379 unsigned int num_streams = *pnum_streams;
5380 struct adouble *ad = NULL;
5381 bool is_fi_empty;
5382 int i;
5383 bool ok;
5385 /* Remove the Netatalk xattr from the list */
5386 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5387 ":" NETATALK_META_XATTR ":$DATA");
5388 if (!ok) {
5389 return NT_STATUS_NO_MEMORY;
5393 * Check if there's a AFPINFO_STREAM from the VFS streams
5394 * backend and if yes, remove it from the list
5396 for (i = 0; i < num_streams; i++) {
5397 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
5398 break;
5402 if (i < num_streams) {
5403 DBG_WARNING("Unexpected AFPINFO_STREAM on [%s]\n",
5404 smb_fname_str_dbg(smb_fname));
5406 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5407 AFPINFO_STREAM);
5408 if (!ok) {
5409 return NT_STATUS_INTERNAL_ERROR;
5413 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5414 if (ad == NULL) {
5415 return NT_STATUS_OK;
5418 is_fi_empty = ad_empty_finderinfo(ad);
5419 TALLOC_FREE(ad);
5421 if (is_fi_empty) {
5422 return NT_STATUS_OK;
5425 ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
5426 AFPINFO_STREAM_NAME, AFP_INFO_SIZE,
5427 smb_roundup(handle->conn, AFP_INFO_SIZE));
5428 if (!ok) {
5429 return NT_STATUS_NO_MEMORY;
5432 return NT_STATUS_OK;
5435 static NTSTATUS fruit_streaminfo_meta(vfs_handle_struct *handle,
5436 struct files_struct *fsp,
5437 const struct smb_filename *smb_fname,
5438 TALLOC_CTX *mem_ctx,
5439 unsigned int *pnum_streams,
5440 struct stream_struct **pstreams)
5442 struct fruit_config_data *config = NULL;
5443 NTSTATUS status;
5445 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5446 return NT_STATUS_INTERNAL_ERROR);
5448 switch (config->meta) {
5449 case FRUIT_META_NETATALK:
5450 status = fruit_streaminfo_meta_netatalk(handle, fsp, smb_fname,
5451 mem_ctx, pnum_streams,
5452 pstreams);
5453 break;
5455 case FRUIT_META_STREAM:
5456 status = fruit_streaminfo_meta_stream(handle, fsp, smb_fname,
5457 mem_ctx, pnum_streams,
5458 pstreams);
5459 break;
5461 default:
5462 return NT_STATUS_INTERNAL_ERROR;
5465 return status;
5468 static NTSTATUS fruit_streaminfo_rsrc_stream(
5469 vfs_handle_struct *handle,
5470 struct files_struct *fsp,
5471 const struct smb_filename *smb_fname,
5472 TALLOC_CTX *mem_ctx,
5473 unsigned int *pnum_streams,
5474 struct stream_struct **pstreams)
5476 bool ok;
5478 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
5479 if (!ok) {
5480 DBG_ERR("Filtering resource stream failed\n");
5481 return NT_STATUS_INTERNAL_ERROR;
5483 return NT_STATUS_OK;
5486 static NTSTATUS fruit_streaminfo_rsrc_xattr(
5487 vfs_handle_struct *handle,
5488 struct files_struct *fsp,
5489 const struct smb_filename *smb_fname,
5490 TALLOC_CTX *mem_ctx,
5491 unsigned int *pnum_streams,
5492 struct stream_struct **pstreams)
5494 bool ok;
5496 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
5497 if (!ok) {
5498 DBG_ERR("Filtering resource stream failed\n");
5499 return NT_STATUS_INTERNAL_ERROR;
5501 return NT_STATUS_OK;
5504 static NTSTATUS fruit_streaminfo_rsrc_adouble(
5505 vfs_handle_struct *handle,
5506 struct files_struct *fsp,
5507 const struct smb_filename *smb_fname,
5508 TALLOC_CTX *mem_ctx,
5509 unsigned int *pnum_streams,
5510 struct stream_struct **pstreams)
5512 struct stream_struct *stream = *pstreams;
5513 unsigned int num_streams = *pnum_streams;
5514 struct adouble *ad = NULL;
5515 bool ok;
5516 size_t rlen;
5517 int i;
5520 * Check if there's a AFPRESOURCE_STREAM from the VFS streams backend
5521 * and if yes, remove it from the list
5523 for (i = 0; i < num_streams; i++) {
5524 if (strequal_m(stream[i].name, AFPRESOURCE_STREAM)) {
5525 break;
5529 if (i < num_streams) {
5530 DBG_WARNING("Unexpected AFPRESOURCE_STREAM on [%s]\n",
5531 smb_fname_str_dbg(smb_fname));
5533 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5534 AFPRESOURCE_STREAM);
5535 if (!ok) {
5536 return NT_STATUS_INTERNAL_ERROR;
5540 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
5541 if (ad == NULL) {
5542 return NT_STATUS_OK;
5545 rlen = ad_getentrylen(ad, ADEID_RFORK);
5546 TALLOC_FREE(ad);
5548 if (rlen == 0) {
5549 return NT_STATUS_OK;
5552 ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
5553 AFPRESOURCE_STREAM_NAME, rlen,
5554 smb_roundup(handle->conn, rlen));
5555 if (!ok) {
5556 return NT_STATUS_NO_MEMORY;
5559 return NT_STATUS_OK;
5562 static NTSTATUS fruit_streaminfo_rsrc(vfs_handle_struct *handle,
5563 struct files_struct *fsp,
5564 const struct smb_filename *smb_fname,
5565 TALLOC_CTX *mem_ctx,
5566 unsigned int *pnum_streams,
5567 struct stream_struct **pstreams)
5569 struct fruit_config_data *config = NULL;
5570 NTSTATUS status;
5572 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5573 return NT_STATUS_INTERNAL_ERROR);
5575 switch (config->rsrc) {
5576 case FRUIT_RSRC_STREAM:
5577 status = fruit_streaminfo_rsrc_stream(handle, fsp, smb_fname,
5578 mem_ctx, pnum_streams,
5579 pstreams);
5580 break;
5582 case FRUIT_RSRC_XATTR:
5583 status = fruit_streaminfo_rsrc_xattr(handle, fsp, smb_fname,
5584 mem_ctx, pnum_streams,
5585 pstreams);
5586 break;
5588 case FRUIT_RSRC_ADFILE:
5589 status = fruit_streaminfo_rsrc_adouble(handle, fsp, smb_fname,
5590 mem_ctx, pnum_streams,
5591 pstreams);
5592 break;
5594 default:
5595 return NT_STATUS_INTERNAL_ERROR;
5598 return status;
5601 static NTSTATUS fruit_streaminfo(vfs_handle_struct *handle,
5602 struct files_struct *fsp,
5603 const struct smb_filename *smb_fname,
5604 TALLOC_CTX *mem_ctx,
5605 unsigned int *pnum_streams,
5606 struct stream_struct **pstreams)
5608 struct fruit_config_data *config = NULL;
5609 NTSTATUS status;
5611 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5612 return NT_STATUS_UNSUCCESSFUL);
5614 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
5616 status = SMB_VFS_NEXT_STREAMINFO(handle, fsp, smb_fname, mem_ctx,
5617 pnum_streams, pstreams);
5618 if (!NT_STATUS_IS_OK(status)) {
5619 return status;
5622 status = fruit_streaminfo_meta(handle, fsp, smb_fname,
5623 mem_ctx, pnum_streams, pstreams);
5624 if (!NT_STATUS_IS_OK(status)) {
5625 return status;
5628 status = fruit_streaminfo_rsrc(handle, fsp, smb_fname,
5629 mem_ctx, pnum_streams, pstreams);
5630 if (!NT_STATUS_IS_OK(status)) {
5631 return status;
5634 return NT_STATUS_OK;
5637 static int fruit_ntimes(vfs_handle_struct *handle,
5638 const struct smb_filename *smb_fname,
5639 struct smb_file_time *ft)
5641 int rc = 0;
5642 struct adouble *ad = NULL;
5643 struct fruit_config_data *config = NULL;
5645 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5646 return -1);
5648 if ((config->meta != FRUIT_META_NETATALK) ||
5649 null_timespec(ft->create_time))
5651 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
5654 DEBUG(10,("set btime for %s to %s\n", smb_fname_str_dbg(smb_fname),
5655 time_to_asc(convert_timespec_to_time_t(ft->create_time))));
5657 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5658 if (ad == NULL) {
5659 goto exit;
5662 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX,
5663 convert_time_t_to_uint32_t(ft->create_time.tv_sec));
5665 rc = ad_set(ad, smb_fname);
5667 exit:
5669 TALLOC_FREE(ad);
5670 if (rc != 0) {
5671 DEBUG(1, ("fruit_ntimes: %s\n", smb_fname_str_dbg(smb_fname)));
5672 return -1;
5674 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
5677 static int fruit_fallocate(struct vfs_handle_struct *handle,
5678 struct files_struct *fsp,
5679 uint32_t mode,
5680 off_t offset,
5681 off_t len)
5683 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5685 if (fio == NULL) {
5686 return SMB_VFS_NEXT_FALLOCATE(handle, fsp, mode, offset, len);
5689 /* Let the pwrite code path handle it. */
5690 errno = ENOSYS;
5691 return -1;
5694 static int fruit_ftruncate_rsrc_xattr(struct vfs_handle_struct *handle,
5695 struct files_struct *fsp,
5696 off_t offset)
5698 if (offset == 0) {
5699 return SMB_VFS_FREMOVEXATTR(fsp, AFPRESOURCE_EA_NETATALK);
5702 #ifdef HAVE_ATTROPEN
5703 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
5704 #endif
5705 return 0;
5708 static int fruit_ftruncate_rsrc_adouble(struct vfs_handle_struct *handle,
5709 struct files_struct *fsp,
5710 off_t offset)
5712 int rc;
5713 struct adouble *ad = NULL;
5714 off_t ad_off;
5716 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
5717 if (ad == NULL) {
5718 DBG_DEBUG("ad_get [%s] failed [%s]\n",
5719 fsp_str_dbg(fsp), strerror(errno));
5720 return -1;
5723 ad_off = ad_getentryoff(ad, ADEID_RFORK);
5725 rc = ftruncate(fsp->fh->fd, offset + ad_off);
5726 if (rc != 0) {
5727 TALLOC_FREE(ad);
5728 return -1;
5731 ad_setentrylen(ad, ADEID_RFORK, offset);
5733 rc = ad_fset(ad, fsp);
5734 if (rc != 0) {
5735 DBG_ERR("ad_fset [%s] failed [%s]\n",
5736 fsp_str_dbg(fsp), strerror(errno));
5737 TALLOC_FREE(ad);
5738 return -1;
5741 TALLOC_FREE(ad);
5742 return 0;
5745 static int fruit_ftruncate_rsrc_stream(struct vfs_handle_struct *handle,
5746 struct files_struct *fsp,
5747 off_t offset)
5749 if (offset == 0) {
5750 return SMB_VFS_NEXT_UNLINK(handle, fsp->fsp_name);
5753 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
5756 static int fruit_ftruncate_rsrc(struct vfs_handle_struct *handle,
5757 struct files_struct *fsp,
5758 off_t offset)
5760 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5761 int ret;
5763 if (fio == NULL) {
5764 DBG_ERR("Failed to fetch fsp extension");
5765 return -1;
5768 switch (fio->config->rsrc) {
5769 case FRUIT_RSRC_XATTR:
5770 ret = fruit_ftruncate_rsrc_xattr(handle, fsp, offset);
5771 break;
5773 case FRUIT_RSRC_ADFILE:
5774 ret = fruit_ftruncate_rsrc_adouble(handle, fsp, offset);
5775 break;
5777 case FRUIT_RSRC_STREAM:
5778 ret = fruit_ftruncate_rsrc_stream(handle, fsp, offset);
5779 break;
5781 default:
5782 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
5783 return -1;
5787 return ret;
5790 static int fruit_ftruncate_meta(struct vfs_handle_struct *handle,
5791 struct files_struct *fsp,
5792 off_t offset)
5794 if (offset > 60) {
5795 DBG_WARNING("ftruncate %s to %jd",
5796 fsp_str_dbg(fsp), (intmax_t)offset);
5797 /* OS X returns NT_STATUS_ALLOTTED_SPACE_EXCEEDED */
5798 errno = EOVERFLOW;
5799 return -1;
5802 /* OS X returns success but does nothing */
5803 DBG_INFO("ignoring ftruncate %s to %jd\n",
5804 fsp_str_dbg(fsp), (intmax_t)offset);
5805 return 0;
5808 static int fruit_ftruncate(struct vfs_handle_struct *handle,
5809 struct files_struct *fsp,
5810 off_t offset)
5812 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5813 int ret;
5815 DBG_DEBUG("Path [%s] offset [%"PRIdMAX"]\n", fsp_str_dbg(fsp),
5816 (intmax_t)offset);
5818 if (fio == NULL) {
5819 if (offset == 0 &&
5820 global_fruit_config.nego_aapl &&
5821 is_ntfs_stream_smb_fname(fsp->fsp_name) &&
5822 !is_ntfs_default_stream_smb_fname(fsp->fsp_name))
5824 return SMB_VFS_NEXT_UNLINK(handle, fsp->fsp_name);
5826 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
5829 if (fio->type == ADOUBLE_META) {
5830 ret = fruit_ftruncate_meta(handle, fsp, offset);
5831 } else {
5832 ret = fruit_ftruncate_rsrc(handle, fsp, offset);
5835 DBG_DEBUG("Path [%s] result [%d]\n", fsp_str_dbg(fsp), ret);
5836 return ret;
5839 static NTSTATUS fruit_create_file(vfs_handle_struct *handle,
5840 struct smb_request *req,
5841 uint16_t root_dir_fid,
5842 struct smb_filename *smb_fname,
5843 uint32_t access_mask,
5844 uint32_t share_access,
5845 uint32_t create_disposition,
5846 uint32_t create_options,
5847 uint32_t file_attributes,
5848 uint32_t oplock_request,
5849 struct smb2_lease *lease,
5850 uint64_t allocation_size,
5851 uint32_t private_flags,
5852 struct security_descriptor *sd,
5853 struct ea_list *ea_list,
5854 files_struct **result,
5855 int *pinfo,
5856 const struct smb2_create_blobs *in_context_blobs,
5857 struct smb2_create_blobs *out_context_blobs)
5859 NTSTATUS status;
5860 struct fruit_config_data *config = NULL;
5861 files_struct *fsp = NULL;
5863 status = check_aapl(handle, req, in_context_blobs, out_context_blobs);
5864 if (!NT_STATUS_IS_OK(status)) {
5865 goto fail;
5868 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5869 return NT_STATUS_UNSUCCESSFUL);
5871 status = SMB_VFS_NEXT_CREATE_FILE(
5872 handle, req, root_dir_fid, smb_fname,
5873 access_mask, share_access,
5874 create_disposition, create_options,
5875 file_attributes, oplock_request,
5876 lease,
5877 allocation_size, private_flags,
5878 sd, ea_list, result,
5879 pinfo, in_context_blobs, out_context_blobs);
5880 if (!NT_STATUS_IS_OK(status)) {
5881 return status;
5884 fsp = *result;
5886 if (global_fruit_config.nego_aapl) {
5887 if (config->posix_rename && fsp->is_directory) {
5889 * Enable POSIX directory rename behaviour
5891 fsp->posix_flags |= FSP_POSIX_FLAGS_RENAME;
5896 * If this is a plain open for existing files, opening an 0
5897 * byte size resource fork MUST fail with
5898 * NT_STATUS_OBJECT_NAME_NOT_FOUND.
5900 * Cf the vfs_fruit torture tests in test_rfork_create().
5902 if (is_afpresource_stream(fsp->fsp_name) &&
5903 create_disposition == FILE_OPEN)
5905 if (fsp->fsp_name->st.st_ex_size == 0) {
5906 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
5907 goto fail;
5911 if (is_ntfs_stream_smb_fname(smb_fname)
5912 || fsp->is_directory) {
5913 return status;
5916 if (config->locking == FRUIT_LOCKING_NETATALK) {
5917 status = fruit_check_access(
5918 handle, *result,
5919 access_mask,
5920 map_share_mode_to_deny_mode(share_access, 0));
5921 if (!NT_STATUS_IS_OK(status)) {
5922 goto fail;
5926 return status;
5928 fail:
5929 DEBUG(10, ("fruit_create_file: %s\n", nt_errstr(status)));
5931 if (fsp) {
5932 close_file(req, fsp, ERROR_CLOSE);
5933 *result = fsp = NULL;
5936 return status;
5939 static NTSTATUS fruit_readdir_attr(struct vfs_handle_struct *handle,
5940 const struct smb_filename *fname,
5941 TALLOC_CTX *mem_ctx,
5942 struct readdir_attr_data **pattr_data)
5944 struct fruit_config_data *config = NULL;
5945 struct readdir_attr_data *attr_data;
5946 NTSTATUS status;
5948 SMB_VFS_HANDLE_GET_DATA(handle, config,
5949 struct fruit_config_data,
5950 return NT_STATUS_UNSUCCESSFUL);
5952 if (!global_fruit_config.nego_aapl) {
5953 return SMB_VFS_NEXT_READDIR_ATTR(handle, fname, mem_ctx, pattr_data);
5956 DEBUG(10, ("fruit_readdir_attr %s\n", fname->base_name));
5958 *pattr_data = talloc_zero(mem_ctx, struct readdir_attr_data);
5959 if (*pattr_data == NULL) {
5960 return NT_STATUS_UNSUCCESSFUL;
5962 attr_data = *pattr_data;
5963 attr_data->type = RDATTR_AAPL;
5966 * Mac metadata: compressed FinderInfo, resource fork length
5967 * and creation date
5969 status = readdir_attr_macmeta(handle, fname, attr_data);
5970 if (!NT_STATUS_IS_OK(status)) {
5972 * Error handling is tricky: if we return failure from
5973 * this function, the corresponding directory entry
5974 * will to be passed to the client, so we really just
5975 * want to error out on fatal errors.
5977 if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
5978 goto fail;
5983 * UNIX mode
5985 if (config->unix_info_enabled) {
5986 attr_data->attr_data.aapl.unix_mode = fname->st.st_ex_mode;
5990 * max_access
5992 if (!config->readdir_attr_max_access) {
5993 attr_data->attr_data.aapl.max_access = FILE_GENERIC_ALL;
5994 } else {
5995 status = smbd_calculate_access_mask(
5996 handle->conn,
5997 fname,
5998 false,
5999 SEC_FLAG_MAXIMUM_ALLOWED,
6000 &attr_data->attr_data.aapl.max_access);
6001 if (!NT_STATUS_IS_OK(status)) {
6002 goto fail;
6006 return NT_STATUS_OK;
6008 fail:
6009 DEBUG(1, ("fruit_readdir_attr %s, error: %s\n",
6010 fname->base_name, nt_errstr(status)));
6011 TALLOC_FREE(*pattr_data);
6012 return status;
6015 static NTSTATUS fruit_fget_nt_acl(vfs_handle_struct *handle,
6016 files_struct *fsp,
6017 uint32_t security_info,
6018 TALLOC_CTX *mem_ctx,
6019 struct security_descriptor **ppdesc)
6021 NTSTATUS status;
6022 struct security_ace ace;
6023 struct dom_sid sid;
6024 struct fruit_config_data *config;
6026 SMB_VFS_HANDLE_GET_DATA(handle, config,
6027 struct fruit_config_data,
6028 return NT_STATUS_UNSUCCESSFUL);
6030 status = SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
6031 mem_ctx, ppdesc);
6032 if (!NT_STATUS_IS_OK(status)) {
6033 return status;
6037 * Add MS NFS style ACEs with uid, gid and mode
6039 if (!global_fruit_config.nego_aapl) {
6040 return NT_STATUS_OK;
6042 if (!config->unix_info_enabled) {
6043 return NT_STATUS_OK;
6046 /* First remove any existing ACE's with NFS style mode/uid/gid SIDs. */
6047 status = remove_virtual_nfs_aces(*ppdesc);
6048 if (!NT_STATUS_IS_OK(status)) {
6049 DBG_WARNING("failed to remove MS NFS style ACEs\n");
6050 return status;
6053 /* MS NFS style mode */
6054 sid_compose(&sid, &global_sid_Unix_NFS_Mode, fsp->fsp_name->st.st_ex_mode);
6055 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6056 status = security_descriptor_dacl_add(*ppdesc, &ace);
6057 if (!NT_STATUS_IS_OK(status)) {
6058 DEBUG(1,("failed to add MS NFS style ACE\n"));
6059 return status;
6062 /* MS NFS style uid */
6063 sid_compose(&sid, &global_sid_Unix_NFS_Users, fsp->fsp_name->st.st_ex_uid);
6064 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6065 status = security_descriptor_dacl_add(*ppdesc, &ace);
6066 if (!NT_STATUS_IS_OK(status)) {
6067 DEBUG(1,("failed to add MS NFS style ACE\n"));
6068 return status;
6071 /* MS NFS style gid */
6072 sid_compose(&sid, &global_sid_Unix_NFS_Groups, fsp->fsp_name->st.st_ex_gid);
6073 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6074 status = security_descriptor_dacl_add(*ppdesc, &ace);
6075 if (!NT_STATUS_IS_OK(status)) {
6076 DEBUG(1,("failed to add MS NFS style ACE\n"));
6077 return status;
6080 return NT_STATUS_OK;
6083 static NTSTATUS fruit_fset_nt_acl(vfs_handle_struct *handle,
6084 files_struct *fsp,
6085 uint32_t security_info_sent,
6086 const struct security_descriptor *orig_psd)
6088 NTSTATUS status;
6089 bool do_chmod;
6090 mode_t ms_nfs_mode = 0;
6091 int result;
6092 struct security_descriptor *psd = NULL;
6093 uint32_t orig_num_aces = 0;
6095 if (orig_psd->dacl != NULL) {
6096 orig_num_aces = orig_psd->dacl->num_aces;
6099 psd = security_descriptor_copy(talloc_tos(), orig_psd);
6100 if (psd == NULL) {
6101 return NT_STATUS_NO_MEMORY;
6104 DBG_DEBUG("fruit_fset_nt_acl: %s\n", fsp_str_dbg(fsp));
6106 status = check_ms_nfs(handle, fsp, psd, &ms_nfs_mode, &do_chmod);
6107 if (!NT_STATUS_IS_OK(status)) {
6108 DEBUG(1, ("fruit_fset_nt_acl: check_ms_nfs failed%s\n", fsp_str_dbg(fsp)));
6109 TALLOC_FREE(psd);
6110 return status;
6114 * If only ms_nfs ACE entries were sent, ensure we set the DACL
6115 * sent/present flags correctly now we've removed them.
6118 if (orig_num_aces != 0) {
6120 * Are there any ACE's left ?
6122 if (psd->dacl->num_aces == 0) {
6123 /* No - clear the DACL sent/present flags. */
6124 security_info_sent &= ~SECINFO_DACL;
6125 psd->type &= ~SEC_DESC_DACL_PRESENT;
6129 status = SMB_VFS_NEXT_FSET_NT_ACL(handle, fsp, security_info_sent, psd);
6130 if (!NT_STATUS_IS_OK(status)) {
6131 DEBUG(1, ("fruit_fset_nt_acl: SMB_VFS_NEXT_FSET_NT_ACL failed%s\n", fsp_str_dbg(fsp)));
6132 TALLOC_FREE(psd);
6133 return status;
6136 if (do_chmod) {
6137 if (fsp->fh->fd != -1) {
6138 result = SMB_VFS_FCHMOD(fsp, ms_nfs_mode);
6139 } else {
6140 result = SMB_VFS_CHMOD(fsp->conn,
6141 fsp->fsp_name,
6142 ms_nfs_mode);
6145 if (result != 0) {
6146 DEBUG(1, ("chmod: %s, result: %d, %04o error %s\n", fsp_str_dbg(fsp),
6147 result, (unsigned)ms_nfs_mode,
6148 strerror(errno)));
6149 status = map_nt_error_from_unix(errno);
6150 TALLOC_FREE(psd);
6151 return status;
6155 TALLOC_FREE(psd);
6156 return NT_STATUS_OK;
6159 static struct vfs_offload_ctx *fruit_offload_ctx;
6161 struct fruit_offload_read_state {
6162 struct vfs_handle_struct *handle;
6163 struct tevent_context *ev;
6164 files_struct *fsp;
6165 uint32_t fsctl;
6166 DATA_BLOB token;
6169 static void fruit_offload_read_done(struct tevent_req *subreq);
6171 static struct tevent_req *fruit_offload_read_send(
6172 TALLOC_CTX *mem_ctx,
6173 struct tevent_context *ev,
6174 struct vfs_handle_struct *handle,
6175 files_struct *fsp,
6176 uint32_t fsctl,
6177 uint32_t ttl,
6178 off_t offset,
6179 size_t to_copy)
6181 struct tevent_req *req = NULL;
6182 struct tevent_req *subreq = NULL;
6183 struct fruit_offload_read_state *state = NULL;
6185 req = tevent_req_create(mem_ctx, &state,
6186 struct fruit_offload_read_state);
6187 if (req == NULL) {
6188 return NULL;
6190 *state = (struct fruit_offload_read_state) {
6191 .handle = handle,
6192 .ev = ev,
6193 .fsp = fsp,
6194 .fsctl = fsctl,
6197 subreq = SMB_VFS_NEXT_OFFLOAD_READ_SEND(mem_ctx, ev, handle, fsp,
6198 fsctl, ttl, offset, to_copy);
6199 if (tevent_req_nomem(subreq, req)) {
6200 return tevent_req_post(req, ev);
6202 tevent_req_set_callback(subreq, fruit_offload_read_done, req);
6203 return req;
6206 static void fruit_offload_read_done(struct tevent_req *subreq)
6208 struct tevent_req *req = tevent_req_callback_data(
6209 subreq, struct tevent_req);
6210 struct fruit_offload_read_state *state = tevent_req_data(
6211 req, struct fruit_offload_read_state);
6212 NTSTATUS status;
6214 status = SMB_VFS_NEXT_OFFLOAD_READ_RECV(subreq,
6215 state->handle,
6216 state,
6217 &state->token);
6218 TALLOC_FREE(subreq);
6219 if (tevent_req_nterror(req, status)) {
6220 return;
6223 if (state->fsctl != FSCTL_SRV_REQUEST_RESUME_KEY) {
6224 tevent_req_done(req);
6225 return;
6228 status = vfs_offload_token_ctx_init(state->fsp->conn->sconn->client,
6229 &fruit_offload_ctx);
6230 if (tevent_req_nterror(req, status)) {
6231 return;
6234 status = vfs_offload_token_db_store_fsp(fruit_offload_ctx,
6235 state->fsp,
6236 &state->token);
6237 if (tevent_req_nterror(req, status)) {
6238 return;
6241 tevent_req_done(req);
6242 return;
6245 static NTSTATUS fruit_offload_read_recv(struct tevent_req *req,
6246 struct vfs_handle_struct *handle,
6247 TALLOC_CTX *mem_ctx,
6248 DATA_BLOB *token)
6250 struct fruit_offload_read_state *state = tevent_req_data(
6251 req, struct fruit_offload_read_state);
6252 NTSTATUS status;
6254 if (tevent_req_is_nterror(req, &status)) {
6255 tevent_req_received(req);
6256 return status;
6259 token->length = state->token.length;
6260 token->data = talloc_move(mem_ctx, &state->token.data);
6262 tevent_req_received(req);
6263 return NT_STATUS_OK;
6266 struct fruit_offload_write_state {
6267 struct vfs_handle_struct *handle;
6268 off_t copied;
6269 struct files_struct *src_fsp;
6270 struct files_struct *dst_fsp;
6271 bool is_copyfile;
6274 static void fruit_offload_write_done(struct tevent_req *subreq);
6275 static struct tevent_req *fruit_offload_write_send(struct vfs_handle_struct *handle,
6276 TALLOC_CTX *mem_ctx,
6277 struct tevent_context *ev,
6278 uint32_t fsctl,
6279 DATA_BLOB *token,
6280 off_t transfer_offset,
6281 struct files_struct *dest_fsp,
6282 off_t dest_off,
6283 off_t num)
6285 struct tevent_req *req, *subreq;
6286 struct fruit_offload_write_state *state;
6287 NTSTATUS status;
6288 struct fruit_config_data *config;
6289 off_t src_off = transfer_offset;
6290 files_struct *src_fsp = NULL;
6291 off_t to_copy = num;
6292 bool copyfile_enabled = false;
6294 DEBUG(10,("soff: %ju, doff: %ju, len: %ju\n",
6295 (uintmax_t)src_off, (uintmax_t)dest_off, (uintmax_t)num));
6297 SMB_VFS_HANDLE_GET_DATA(handle, config,
6298 struct fruit_config_data,
6299 return NULL);
6301 req = tevent_req_create(mem_ctx, &state,
6302 struct fruit_offload_write_state);
6303 if (req == NULL) {
6304 return NULL;
6306 state->handle = handle;
6307 state->dst_fsp = dest_fsp;
6309 switch (fsctl) {
6310 case FSCTL_SRV_COPYCHUNK:
6311 case FSCTL_SRV_COPYCHUNK_WRITE:
6312 copyfile_enabled = config->copyfile_enabled;
6313 break;
6314 default:
6315 break;
6319 * Check if this a OS X copyfile style copychunk request with
6320 * a requested chunk count of 0 that was translated to a
6321 * offload_write_send VFS call overloading the parameters src_off
6322 * = dest_off = num = 0.
6324 if (copyfile_enabled && num == 0 && src_off == 0 && dest_off == 0) {
6325 status = vfs_offload_token_db_fetch_fsp(
6326 fruit_offload_ctx, token, &src_fsp);
6327 if (tevent_req_nterror(req, status)) {
6328 return tevent_req_post(req, ev);
6330 state->src_fsp = src_fsp;
6332 status = vfs_stat_fsp(src_fsp);
6333 if (tevent_req_nterror(req, status)) {
6334 return tevent_req_post(req, ev);
6337 to_copy = src_fsp->fsp_name->st.st_ex_size;
6338 state->is_copyfile = true;
6341 subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle,
6342 mem_ctx,
6344 fsctl,
6345 token,
6346 transfer_offset,
6347 dest_fsp,
6348 dest_off,
6349 to_copy);
6350 if (tevent_req_nomem(subreq, req)) {
6351 return tevent_req_post(req, ev);
6354 tevent_req_set_callback(subreq, fruit_offload_write_done, req);
6355 return req;
6358 static void fruit_offload_write_done(struct tevent_req *subreq)
6360 struct tevent_req *req = tevent_req_callback_data(
6361 subreq, struct tevent_req);
6362 struct fruit_offload_write_state *state = tevent_req_data(
6363 req, struct fruit_offload_write_state);
6364 NTSTATUS status;
6365 unsigned int num_streams = 0;
6366 struct stream_struct *streams = NULL;
6367 unsigned int i;
6368 struct smb_filename *src_fname_tmp = NULL;
6369 struct smb_filename *dst_fname_tmp = NULL;
6371 status = SMB_VFS_NEXT_OFFLOAD_WRITE_RECV(state->handle,
6372 subreq,
6373 &state->copied);
6374 TALLOC_FREE(subreq);
6375 if (tevent_req_nterror(req, status)) {
6376 return;
6379 if (!state->is_copyfile) {
6380 tevent_req_done(req);
6381 return;
6385 * Now copy all remaining streams. We know the share supports
6386 * streams, because we're in vfs_fruit. We don't do this async
6387 * because streams are few and small.
6389 status = vfs_streaminfo(state->handle->conn, state->src_fsp,
6390 state->src_fsp->fsp_name,
6391 req, &num_streams, &streams);
6392 if (tevent_req_nterror(req, status)) {
6393 return;
6396 if (num_streams == 1) {
6397 /* There is always one stream, ::$DATA. */
6398 tevent_req_done(req);
6399 return;
6402 for (i = 0; i < num_streams; i++) {
6403 DEBUG(10, ("%s: stream: '%s'/%zu\n",
6404 __func__, streams[i].name, (size_t)streams[i].size));
6406 src_fname_tmp = synthetic_smb_fname(
6407 req,
6408 state->src_fsp->fsp_name->base_name,
6409 streams[i].name,
6410 NULL,
6411 state->src_fsp->fsp_name->flags);
6412 if (tevent_req_nomem(src_fname_tmp, req)) {
6413 return;
6416 if (is_ntfs_default_stream_smb_fname(src_fname_tmp)) {
6417 TALLOC_FREE(src_fname_tmp);
6418 continue;
6421 dst_fname_tmp = synthetic_smb_fname(
6422 req,
6423 state->dst_fsp->fsp_name->base_name,
6424 streams[i].name,
6425 NULL,
6426 state->dst_fsp->fsp_name->flags);
6427 if (tevent_req_nomem(dst_fname_tmp, req)) {
6428 TALLOC_FREE(src_fname_tmp);
6429 return;
6432 status = copy_file(req,
6433 state->handle->conn,
6434 src_fname_tmp,
6435 dst_fname_tmp,
6436 OPENX_FILE_CREATE_IF_NOT_EXIST,
6437 0, false);
6438 if (!NT_STATUS_IS_OK(status)) {
6439 DEBUG(1, ("%s: copy %s to %s failed: %s\n", __func__,
6440 smb_fname_str_dbg(src_fname_tmp),
6441 smb_fname_str_dbg(dst_fname_tmp),
6442 nt_errstr(status)));
6443 TALLOC_FREE(src_fname_tmp);
6444 TALLOC_FREE(dst_fname_tmp);
6445 tevent_req_nterror(req, status);
6446 return;
6449 TALLOC_FREE(src_fname_tmp);
6450 TALLOC_FREE(dst_fname_tmp);
6453 TALLOC_FREE(streams);
6454 TALLOC_FREE(src_fname_tmp);
6455 TALLOC_FREE(dst_fname_tmp);
6456 tevent_req_done(req);
6459 static NTSTATUS fruit_offload_write_recv(struct vfs_handle_struct *handle,
6460 struct tevent_req *req,
6461 off_t *copied)
6463 struct fruit_offload_write_state *state = tevent_req_data(
6464 req, struct fruit_offload_write_state);
6465 NTSTATUS status;
6467 if (tevent_req_is_nterror(req, &status)) {
6468 DEBUG(1, ("server side copy chunk failed: %s\n",
6469 nt_errstr(status)));
6470 *copied = 0;
6471 tevent_req_received(req);
6472 return status;
6475 *copied = state->copied;
6476 tevent_req_received(req);
6478 return NT_STATUS_OK;
6481 static char *fruit_get_bandsize_line(char **lines, int numlines)
6483 static regex_t re;
6484 static bool re_initialized = false;
6485 int i;
6486 int ret;
6488 if (!re_initialized) {
6489 ret = regcomp(&re, "^[[:blank:]]*<key>band-size</key>$", 0);
6490 if (ret != 0) {
6491 return NULL;
6493 re_initialized = true;
6496 for (i = 0; i < numlines; i++) {
6497 regmatch_t matches[1];
6499 ret = regexec(&re, lines[i], 1, matches, 0);
6500 if (ret == 0) {
6502 * Check if the match was on the last line, sa we want
6503 * the subsequent line.
6505 if (i + 1 == numlines) {
6506 return NULL;
6508 return lines[i + 1];
6510 if (ret != REG_NOMATCH) {
6511 return NULL;
6515 return NULL;
6518 static bool fruit_get_bandsize_from_line(char *line, size_t *_band_size)
6520 static regex_t re;
6521 static bool re_initialized = false;
6522 regmatch_t matches[2];
6523 uint64_t band_size;
6524 int ret;
6525 bool ok;
6527 if (!re_initialized) {
6528 ret = regcomp(&re,
6529 "^[[:blank:]]*"
6530 "<integer>\\([[:digit:]]*\\)</integer>$",
6532 if (ret != 0) {
6533 return false;
6535 re_initialized = true;
6538 ret = regexec(&re, line, 2, matches, 0);
6539 if (ret != 0) {
6540 DBG_ERR("regex failed [%s]\n", line);
6541 return false;
6544 line[matches[1].rm_eo] = '\0';
6546 ok = conv_str_u64(&line[matches[1].rm_so], &band_size);
6547 if (!ok) {
6548 return false;
6550 *_band_size = (size_t)band_size;
6551 return true;
6555 * This reads and parses an Info.plist from a TM sparsebundle looking for the
6556 * "band-size" key and value.
6558 static bool fruit_get_bandsize(vfs_handle_struct *handle,
6559 const char *dir,
6560 size_t *band_size)
6562 #define INFO_PLIST_MAX_SIZE 64*1024
6563 char *plist = NULL;
6564 struct smb_filename *smb_fname = NULL;
6565 files_struct *fsp = NULL;
6566 uint8_t *file_data = NULL;
6567 char **lines = NULL;
6568 char *band_size_line = NULL;
6569 size_t plist_file_size;
6570 ssize_t nread;
6571 int numlines;
6572 int ret;
6573 bool ok = false;
6574 NTSTATUS status;
6576 plist = talloc_asprintf(talloc_tos(),
6577 "%s/%s/Info.plist",
6578 handle->conn->connectpath,
6579 dir);
6580 if (plist == NULL) {
6581 ok = false;
6582 goto out;
6585 smb_fname = synthetic_smb_fname(talloc_tos(), plist, NULL, NULL, 0);
6586 if (smb_fname == NULL) {
6587 ok = false;
6588 goto out;
6591 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
6592 if (ret != 0) {
6593 DBG_INFO("Ignoring Sparsebundle without Info.plist [%s]\n", dir);
6594 ok = true;
6595 goto out;
6598 plist_file_size = smb_fname->st.st_ex_size;
6600 if (plist_file_size > INFO_PLIST_MAX_SIZE) {
6601 DBG_INFO("%s is too large, ignoring\n", plist);
6602 ok = true;
6603 goto out;
6606 status = SMB_VFS_NEXT_CREATE_FILE(
6607 handle, /* conn */
6608 NULL, /* req */
6609 0, /* root_dir_fid */
6610 smb_fname, /* fname */
6611 FILE_GENERIC_READ, /* access_mask */
6612 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
6613 FILE_OPEN, /* create_disposition */
6614 0, /* create_options */
6615 0, /* file_attributes */
6616 INTERNAL_OPEN_ONLY, /* oplock_request */
6617 NULL, /* lease */
6618 0, /* allocation_size */
6619 0, /* private_flags */
6620 NULL, /* sd */
6621 NULL, /* ea_list */
6622 &fsp, /* result */
6623 NULL, /* psbuf */
6624 NULL, NULL); /* create context */
6625 if (!NT_STATUS_IS_OK(status)) {
6626 DBG_INFO("Opening [%s] failed [%s]\n",
6627 smb_fname_str_dbg(smb_fname), nt_errstr(status));
6628 ok = false;
6629 goto out;
6632 file_data = talloc_array(talloc_tos(), uint8_t, plist_file_size);
6633 if (file_data == NULL) {
6634 ok = false;
6635 goto out;
6638 nread = SMB_VFS_NEXT_PREAD(handle, fsp, file_data, plist_file_size, 0);
6639 if (nread != plist_file_size) {
6640 DBG_ERR("Short read on [%s]: %zu/%zd\n",
6641 fsp_str_dbg(fsp), nread, plist_file_size);
6642 ok = false;
6643 goto out;
6647 status = close_file(NULL, fsp, NORMAL_CLOSE);
6648 fsp = NULL;
6649 if (!NT_STATUS_IS_OK(status)) {
6650 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
6651 ok = false;
6652 goto out;
6655 lines = file_lines_parse((char *)file_data,
6656 plist_file_size,
6657 &numlines,
6658 talloc_tos());
6659 if (lines == NULL) {
6660 ok = false;
6661 goto out;
6664 band_size_line = fruit_get_bandsize_line(lines, numlines);
6665 if (band_size_line == NULL) {
6666 DBG_ERR("Didn't find band-size key in [%s]\n",
6667 smb_fname_str_dbg(smb_fname));
6668 ok = false;
6669 goto out;
6672 ok = fruit_get_bandsize_from_line(band_size_line, band_size);
6673 if (!ok) {
6674 DBG_ERR("fruit_get_bandsize_from_line failed\n");
6675 goto out;
6678 DBG_DEBUG("Parsed band-size [%zu] for [%s]\n", *band_size, plist);
6680 out:
6681 if (fsp != NULL) {
6682 status = close_file(NULL, fsp, NORMAL_CLOSE);
6683 if (!NT_STATUS_IS_OK(status)) {
6684 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
6686 fsp = NULL;
6688 TALLOC_FREE(plist);
6689 TALLOC_FREE(smb_fname);
6690 TALLOC_FREE(file_data);
6691 TALLOC_FREE(lines);
6692 return ok;
6695 struct fruit_disk_free_state {
6696 off_t total_size;
6699 static bool fruit_get_num_bands(vfs_handle_struct *handle,
6700 char *bundle,
6701 size_t *_nbands)
6703 char *path = NULL;
6704 struct smb_filename *bands_dir = NULL;
6705 DIR *d = NULL;
6706 struct dirent *e = NULL;
6707 size_t nbands;
6708 int ret;
6710 path = talloc_asprintf(talloc_tos(),
6711 "%s/%s/bands",
6712 handle->conn->connectpath,
6713 bundle);
6714 if (path == NULL) {
6715 return false;
6718 bands_dir = synthetic_smb_fname(talloc_tos(),
6719 path,
6720 NULL,
6721 NULL,
6723 TALLOC_FREE(path);
6724 if (bands_dir == NULL) {
6725 return false;
6728 d = SMB_VFS_NEXT_OPENDIR(handle, bands_dir, NULL, 0);
6729 if (d == NULL) {
6730 TALLOC_FREE(bands_dir);
6731 return false;
6734 nbands = 0;
6736 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
6737 e != NULL;
6738 e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
6740 if (ISDOT(e->d_name) || ISDOTDOT(e->d_name)) {
6741 continue;
6743 nbands++;
6746 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
6747 if (ret != 0) {
6748 TALLOC_FREE(bands_dir);
6749 return false;
6752 DBG_DEBUG("%zu bands in [%s]\n", nbands, smb_fname_str_dbg(bands_dir));
6754 TALLOC_FREE(bands_dir);
6756 *_nbands = nbands;
6757 return true;
6760 static bool fruit_tmsize_do_dirent(vfs_handle_struct *handle,
6761 struct fruit_disk_free_state *state,
6762 struct dirent *e)
6764 bool ok;
6765 char *p = NULL;
6766 size_t sparsebundle_strlen = strlen("sparsebundle");
6767 size_t bandsize = 0;
6768 size_t nbands;
6769 off_t tm_size;
6771 p = strstr(e->d_name, "sparsebundle");
6772 if (p == NULL) {
6773 return true;
6776 if (p[sparsebundle_strlen] != '\0') {
6777 return true;
6780 DBG_DEBUG("Processing sparsebundle [%s]\n", e->d_name);
6782 ok = fruit_get_bandsize(handle, e->d_name, &bandsize);
6783 if (!ok) {
6785 * Beware of race conditions: this may be an uninitialized
6786 * Info.plist that a client is just creating. We don't want let
6787 * this to trigger complete failure.
6789 DBG_ERR("Processing sparsebundle [%s] failed\n", e->d_name);
6790 return true;
6793 ok = fruit_get_num_bands(handle, e->d_name, &nbands);
6794 if (!ok) {
6796 * Beware of race conditions: this may be a backup sparsebundle
6797 * in an early stage lacking a bands subdirectory. We don't want
6798 * let this to trigger complete failure.
6800 DBG_ERR("Processing sparsebundle [%s] failed\n", e->d_name);
6801 return true;
6804 if (bandsize > SIZE_MAX/nbands) {
6805 DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
6806 bandsize, nbands);
6807 return false;
6809 tm_size = bandsize * nbands;
6811 if (state->total_size + tm_size < state->total_size) {
6812 DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
6813 bandsize, nbands);
6814 return false;
6817 state->total_size += tm_size;
6819 DBG_DEBUG("[%s] tm_size [%jd] total_size [%jd]\n",
6820 e->d_name, (intmax_t)tm_size, (intmax_t)state->total_size);
6822 return true;
6826 * Calculate used size of a TimeMachine volume
6828 * This assumes that the volume is used only for TimeMachine.
6830 * - readdir(basedir of share), then
6831 * - for every element that matches regex "^\(.*\)\.sparsebundle$" :
6832 * - parse "\1.sparsebundle/Info.plist" and read the band-size XML key
6833 * - count band files in "\1.sparsebundle/bands/"
6834 * - calculate used size of all bands: band_count * band_size
6836 static uint64_t fruit_disk_free(vfs_handle_struct *handle,
6837 const struct smb_filename *smb_fname,
6838 uint64_t *_bsize,
6839 uint64_t *_dfree,
6840 uint64_t *_dsize)
6842 struct fruit_config_data *config = NULL;
6843 struct fruit_disk_free_state state = {0};
6844 DIR *d = NULL;
6845 struct dirent *e = NULL;
6846 uint64_t dfree;
6847 uint64_t dsize;
6848 int ret;
6849 bool ok;
6851 SMB_VFS_HANDLE_GET_DATA(handle, config,
6852 struct fruit_config_data,
6853 return UINT64_MAX);
6855 if (!config->time_machine ||
6856 config->time_machine_max_size == 0)
6858 return SMB_VFS_NEXT_DISK_FREE(handle,
6859 smb_fname,
6860 _bsize,
6861 _dfree,
6862 _dsize);
6865 d = SMB_VFS_NEXT_OPENDIR(handle, smb_fname, NULL, 0);
6866 if (d == NULL) {
6867 return UINT64_MAX;
6870 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
6871 e != NULL;
6872 e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
6874 ok = fruit_tmsize_do_dirent(handle, &state, e);
6875 if (!ok) {
6876 SMB_VFS_NEXT_CLOSEDIR(handle, d);
6877 return UINT64_MAX;
6881 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
6882 if (ret != 0) {
6883 return UINT64_MAX;
6886 dsize = config->time_machine_max_size / 512;
6887 dfree = dsize - (state.total_size / 512);
6888 if (dfree > dsize) {
6889 dfree = 0;
6892 *_bsize = 512;
6893 *_dsize = dsize;
6894 *_dfree = dfree;
6895 return dfree / 2;
6898 static struct vfs_fn_pointers vfs_fruit_fns = {
6899 .connect_fn = fruit_connect,
6900 .disk_free_fn = fruit_disk_free,
6902 /* File operations */
6903 .chmod_fn = fruit_chmod,
6904 .chown_fn = fruit_chown,
6905 .unlink_fn = fruit_unlink,
6906 .rename_fn = fruit_rename,
6907 .rmdir_fn = fruit_rmdir,
6908 .open_fn = fruit_open,
6909 .pread_fn = fruit_pread,
6910 .pwrite_fn = fruit_pwrite,
6911 .pread_send_fn = fruit_pread_send,
6912 .pread_recv_fn = fruit_pread_recv,
6913 .pwrite_send_fn = fruit_pwrite_send,
6914 .pwrite_recv_fn = fruit_pwrite_recv,
6915 .stat_fn = fruit_stat,
6916 .lstat_fn = fruit_lstat,
6917 .fstat_fn = fruit_fstat,
6918 .streaminfo_fn = fruit_streaminfo,
6919 .ntimes_fn = fruit_ntimes,
6920 .ftruncate_fn = fruit_ftruncate,
6921 .fallocate_fn = fruit_fallocate,
6922 .create_file_fn = fruit_create_file,
6923 .readdir_attr_fn = fruit_readdir_attr,
6924 .offload_read_send_fn = fruit_offload_read_send,
6925 .offload_read_recv_fn = fruit_offload_read_recv,
6926 .offload_write_send_fn = fruit_offload_write_send,
6927 .offload_write_recv_fn = fruit_offload_write_recv,
6929 /* NT ACL operations */
6930 .fget_nt_acl_fn = fruit_fget_nt_acl,
6931 .fset_nt_acl_fn = fruit_fset_nt_acl,
6934 static_decl_vfs;
6935 NTSTATUS vfs_fruit_init(TALLOC_CTX *ctx)
6937 NTSTATUS ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "fruit",
6938 &vfs_fruit_fns);
6939 if (!NT_STATUS_IS_OK(ret)) {
6940 return ret;
6943 vfs_fruit_debug_level = debug_add_class("fruit");
6944 if (vfs_fruit_debug_level == -1) {
6945 vfs_fruit_debug_level = DBGC_VFS;
6946 DEBUG(0, ("%s: Couldn't register custom debugging class!\n",
6947 "vfs_fruit_init"));
6948 } else {
6949 DEBUG(10, ("%s: Debug class number of '%s': %d\n",
6950 "vfs_fruit_init","fruit",vfs_fruit_debug_level));
6953 return ret;