s3:smbd: use lp_smbd_search_ask_sharemode()
[Samba.git] / source3 / modules / vfs_fruit.c
blob19101efba740ce6de504cc97c2219add7101c34d
1 /*
2 * OS X and Netatalk interoperability VFS module for Samba-3.x
4 * Copyright (C) Ralph Boehme, 2013, 2014
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
20 #include "includes.h"
21 #include "MacExtensions.h"
22 #include "smbd/smbd.h"
23 #include "system/filesys.h"
24 #include "lib/util/time.h"
25 #include "../lib/crypto/md5.h"
26 #include "system/shmem.h"
27 #include "locking/proto.h"
28 #include "smbd/globals.h"
29 #include "messages.h"
30 #include "libcli/security/security.h"
31 #include "../libcli/smb/smb2_create_ctx.h"
32 #include "lib/util/sys_rw.h"
33 #include "lib/util/tevent_ntstatus.h"
34 #include "lib/util/tevent_unix.h"
35 #include "offload_token.h"
36 #include "string_replace.h"
39 * Enhanced OS X and Netatalk compatibility
40 * ========================================
42 * This modules takes advantage of vfs_streams_xattr and
43 * vfs_catia. VFS modules vfs_fruit and vfs_streams_xattr must be
44 * loaded in the correct order:
46 * vfs modules = catia fruit streams_xattr
48 * The module intercepts the OS X special streams "AFP_AfpInfo" and
49 * "AFP_Resource" and handles them in a special way. All other named
50 * streams are deferred to vfs_streams_xattr.
52 * The OS X client maps all NTFS illegal characters to the Unicode
53 * private range. This module optionally stores the charcters using
54 * their native ASCII encoding using vfs_catia. If you're not enabling
55 * this feature, you can skip catia from vfs modules.
57 * Finally, open modes are optionally checked against Netatalk AFP
58 * share modes.
60 * The "AFP_AfpInfo" named stream is a binary blob containing OS X
61 * extended metadata for files and directories. This module optionally
62 * reads and stores this metadata in a way compatible with Netatalk 3
63 * which stores the metadata in an EA "org.netatalk.metadata". Cf
64 * source3/include/MacExtensions.h for a description of the binary
65 * blobs content.
67 * The "AFP_Resource" named stream may be arbitrarily large, thus it
68 * can't be stored in an xattr on most filesystem. ZFS on Solaris is
69 * the only available filesystem where xattrs can be of any size and
70 * the OS supports using the file APIs for xattrs.
72 * The AFP_Resource stream is stored in an AppleDouble file prepending
73 * "._" to the filename. On Solaris with ZFS the stream is optionally
74 * stored in an EA "org.netatalk.resource".
77 * Extended Attributes
78 * ===================
80 * The OS X SMB client sends xattrs as ADS too. For xattr interop with
81 * other protocols you may want to adjust the xattr names the VFS
82 * module vfs_streams_xattr uses for storing ADS's. This defaults to
83 * user.DosStream.ADS_NAME:$DATA and can be changed by specifying
84 * these module parameters:
86 * streams_xattr:prefix = user.
87 * streams_xattr:store_stream_type = false
90 * TODO
91 * ====
93 * - log diagnostic if any needed VFS module is not loaded
94 * (eg with lp_vfs_objects())
95 * - add tests
98 static int vfs_fruit_debug_level = DBGC_VFS;
100 static struct global_fruit_config {
101 bool nego_aapl; /* client negotiated AAPL */
103 } global_fruit_config;
105 #undef DBGC_CLASS
106 #define DBGC_CLASS vfs_fruit_debug_level
108 #define FRUIT_PARAM_TYPE_NAME "fruit"
109 #define ADOUBLE_NAME_PREFIX "._"
111 #define NETATALK_META_XATTR "org.netatalk.Metadata"
112 #define NETATALK_RSRC_XATTR "org.netatalk.ResourceFork"
114 #if defined(HAVE_ATTROPEN)
115 #define AFPINFO_EA_NETATALK NETATALK_META_XATTR
116 #define AFPRESOURCE_EA_NETATALK NETATALK_RSRC_XATTR
117 #else
118 #define AFPINFO_EA_NETATALK "user." NETATALK_META_XATTR
119 #define AFPRESOURCE_EA_NETATALK "user." NETATALK_RSRC_XATTR
120 #endif
122 enum apple_fork {APPLE_FORK_DATA, APPLE_FORK_RSRC};
124 enum fruit_rsrc {FRUIT_RSRC_STREAM, FRUIT_RSRC_ADFILE, FRUIT_RSRC_XATTR};
125 enum fruit_meta {FRUIT_META_STREAM, FRUIT_META_NETATALK};
126 enum fruit_locking {FRUIT_LOCKING_NETATALK, FRUIT_LOCKING_NONE};
127 enum fruit_encoding {FRUIT_ENC_NATIVE, FRUIT_ENC_PRIVATE};
129 struct fruit_config_data {
130 enum fruit_rsrc rsrc;
131 enum fruit_meta meta;
132 enum fruit_locking locking;
133 enum fruit_encoding encoding;
134 bool use_aapl; /* config from smb.conf */
135 bool use_copyfile;
136 bool readdir_attr_enabled;
137 bool unix_info_enabled;
138 bool copyfile_enabled;
139 bool veto_appledouble;
140 bool posix_rename;
141 bool aapl_zero_file_id;
142 const char *model;
143 bool time_machine;
144 off_t time_machine_max_size;
145 bool wipe_intentionally_left_blank_rfork;
146 bool delete_empty_adfiles;
149 * Additional options, all enabled by default,
150 * possibly useful for analyzing performance. The associated
151 * operations with each of them may be expensive, so having
152 * the chance to disable them individually gives a chance
153 * tweaking the setup for the particular usecase.
155 bool readdir_attr_rsize;
156 bool readdir_attr_finder_info;
157 bool readdir_attr_max_access;
160 static const struct enum_list fruit_rsrc[] = {
161 {FRUIT_RSRC_STREAM, "stream"}, /* pass on to vfs_streams_xattr */
162 {FRUIT_RSRC_ADFILE, "file"}, /* ._ AppleDouble file */
163 {FRUIT_RSRC_XATTR, "xattr"}, /* Netatalk compatible xattr (ZFS only) */
164 { -1, NULL}
167 static const struct enum_list fruit_meta[] = {
168 {FRUIT_META_STREAM, "stream"}, /* pass on to vfs_streams_xattr */
169 {FRUIT_META_NETATALK, "netatalk"}, /* Netatalk compatible xattr */
170 { -1, NULL}
173 static const struct enum_list fruit_locking[] = {
174 {FRUIT_LOCKING_NETATALK, "netatalk"}, /* synchronize locks with Netatalk */
175 {FRUIT_LOCKING_NONE, "none"},
176 { -1, NULL}
179 static const struct enum_list fruit_encoding[] = {
180 {FRUIT_ENC_NATIVE, "native"}, /* map unicode private chars to ASCII */
181 {FRUIT_ENC_PRIVATE, "private"}, /* keep unicode private chars */
182 { -1, NULL}
185 static const char *fruit_catia_maps =
186 "0x01:0xf001,0x02:0xf002,0x03:0xf003,0x04:0xf004,"
187 "0x05:0xf005,0x06:0xf006,0x07:0xf007,0x08:0xf008,"
188 "0x09:0xf009,0x0a:0xf00a,0x0b:0xf00b,0x0c:0xf00c,"
189 "0x0d:0xf00d,0x0e:0xf00e,0x0f:0xf00f,0x10:0xf010,"
190 "0x11:0xf011,0x12:0xf012,0x13:0xf013,0x14:0xf014,"
191 "0x15:0xf015,0x16:0xf016,0x17:0xf017,0x18:0xf018,"
192 "0x19:0xf019,0x1a:0xf01a,0x1b:0xf01b,0x1c:0xf01c,"
193 "0x1d:0xf01d,0x1e:0xf01e,0x1f:0xf01f,"
194 "0x22:0xf020,0x2a:0xf021,0x3a:0xf022,0x3c:0xf023,"
195 "0x3e:0xf024,0x3f:0xf025,0x5c:0xf026,0x7c:0xf027,"
196 "0x0d:0xf00d";
198 /*****************************************************************************
199 * Defines, functions and data structures that deal with AppleDouble
200 *****************************************************************************/
203 * There are two AppleDouble blobs we deal with:
205 * - ADOUBLE_META - AppleDouble blob used by Netatalk for storing
206 * metadata in an xattr
208 * - ADOUBLE_RSRC - AppleDouble blob used by OS X and Netatalk in
209 * ._ files
211 typedef enum {ADOUBLE_META, ADOUBLE_RSRC} adouble_type_t;
213 /* Version info */
214 #define AD_VERSION2 0x00020000
215 #define AD_VERSION AD_VERSION2
218 * AppleDouble entry IDs.
220 #define ADEID_DFORK 1
221 #define ADEID_RFORK 2
222 #define ADEID_NAME 3
223 #define ADEID_COMMENT 4
224 #define ADEID_ICONBW 5
225 #define ADEID_ICONCOL 6
226 #define ADEID_FILEI 7
227 #define ADEID_FILEDATESI 8
228 #define ADEID_FINDERI 9
229 #define ADEID_MACFILEI 10
230 #define ADEID_PRODOSFILEI 11
231 #define ADEID_MSDOSFILEI 12
232 #define ADEID_SHORTNAME 13
233 #define ADEID_AFPFILEI 14
234 #define ADEID_DID 15
236 /* Private Netatalk entries */
237 #define ADEID_PRIVDEV 16
238 #define ADEID_PRIVINO 17
239 #define ADEID_PRIVSYN 18
240 #define ADEID_PRIVID 19
241 #define ADEID_MAX (ADEID_PRIVID + 1)
244 * These are the real ids for the private entries,
245 * as stored in the adouble file
247 #define AD_DEV 0x80444556
248 #define AD_INO 0x80494E4F
249 #define AD_SYN 0x8053594E
250 #define AD_ID 0x8053567E
252 /* Number of actually used entries */
253 #define ADEID_NUM_XATTR 8
254 #define ADEID_NUM_DOT_UND 2
255 #define ADEID_NUM_RSRC_XATTR 1
257 /* AppleDouble magic */
258 #define AD_APPLESINGLE_MAGIC 0x00051600
259 #define AD_APPLEDOUBLE_MAGIC 0x00051607
260 #define AD_MAGIC AD_APPLEDOUBLE_MAGIC
262 /* Sizes of relevant entry bits */
263 #define ADEDLEN_MAGIC 4
264 #define ADEDLEN_VERSION 4
265 #define ADEDLEN_FILLER 16
266 #define AD_FILLER_TAG "Netatalk " /* should be 16 bytes */
267 #define AD_FILLER_TAG_OSX "Mac OS X " /* should be 16 bytes */
268 #define ADEDLEN_NENTRIES 2
269 #define AD_HEADER_LEN (ADEDLEN_MAGIC + ADEDLEN_VERSION + \
270 ADEDLEN_FILLER + ADEDLEN_NENTRIES) /* 26 */
271 #define AD_ENTRY_LEN_EID 4
272 #define AD_ENTRY_LEN_OFF 4
273 #define AD_ENTRY_LEN_LEN 4
274 #define AD_ENTRY_LEN (AD_ENTRY_LEN_EID + AD_ENTRY_LEN_OFF + AD_ENTRY_LEN_LEN)
276 /* Field widths */
277 #define ADEDLEN_NAME 255
278 #define ADEDLEN_COMMENT 200
279 #define ADEDLEN_FILEI 16
280 #define ADEDLEN_FINDERI 32
281 #define ADEDLEN_FILEDATESI 16
282 #define ADEDLEN_SHORTNAME 12 /* length up to 8.3 */
283 #define ADEDLEN_AFPFILEI 4
284 #define ADEDLEN_MACFILEI 4
285 #define ADEDLEN_PRODOSFILEI 8
286 #define ADEDLEN_MSDOSFILEI 2
287 #define ADEDLEN_DID 4
288 #define ADEDLEN_PRIVDEV 8
289 #define ADEDLEN_PRIVINO 8
290 #define ADEDLEN_PRIVSYN 8
291 #define ADEDLEN_PRIVID 4
293 /* Offsets */
294 #define ADEDOFF_MAGIC 0
295 #define ADEDOFF_VERSION (ADEDOFF_MAGIC + ADEDLEN_MAGIC)
296 #define ADEDOFF_FILLER (ADEDOFF_VERSION + ADEDLEN_VERSION)
297 #define ADEDOFF_NENTRIES (ADEDOFF_FILLER + ADEDLEN_FILLER)
299 #define ADEDOFF_FINDERI_XATTR (AD_HEADER_LEN + \
300 (ADEID_NUM_XATTR * AD_ENTRY_LEN))
301 #define ADEDOFF_COMMENT_XATTR (ADEDOFF_FINDERI_XATTR + ADEDLEN_FINDERI)
302 #define ADEDOFF_FILEDATESI_XATTR (ADEDOFF_COMMENT_XATTR + ADEDLEN_COMMENT)
303 #define ADEDOFF_AFPFILEI_XATTR (ADEDOFF_FILEDATESI_XATTR + \
304 ADEDLEN_FILEDATESI)
305 #define ADEDOFF_PRIVDEV_XATTR (ADEDOFF_AFPFILEI_XATTR + ADEDLEN_AFPFILEI)
306 #define ADEDOFF_PRIVINO_XATTR (ADEDOFF_PRIVDEV_XATTR + ADEDLEN_PRIVDEV)
307 #define ADEDOFF_PRIVSYN_XATTR (ADEDOFF_PRIVINO_XATTR + ADEDLEN_PRIVINO)
308 #define ADEDOFF_PRIVID_XATTR (ADEDOFF_PRIVSYN_XATTR + ADEDLEN_PRIVSYN)
310 #define ADEDOFF_FINDERI_DOT_UND (AD_HEADER_LEN + \
311 (ADEID_NUM_DOT_UND * AD_ENTRY_LEN))
312 #define ADEDOFF_RFORK_DOT_UND (ADEDOFF_FINDERI_DOT_UND + ADEDLEN_FINDERI)
314 #define AD_DATASZ_XATTR (AD_HEADER_LEN + \
315 (ADEID_NUM_XATTR * AD_ENTRY_LEN) + \
316 ADEDLEN_FINDERI + ADEDLEN_COMMENT + \
317 ADEDLEN_FILEDATESI + ADEDLEN_AFPFILEI + \
318 ADEDLEN_PRIVDEV + ADEDLEN_PRIVINO + \
319 ADEDLEN_PRIVSYN + ADEDLEN_PRIVID)
321 #if AD_DATASZ_XATTR != 402
322 #error bad size for AD_DATASZ_XATTR
323 #endif
325 #define AD_DATASZ_DOT_UND (AD_HEADER_LEN + \
326 (ADEID_NUM_DOT_UND * AD_ENTRY_LEN) + \
327 ADEDLEN_FINDERI)
328 #if AD_DATASZ_DOT_UND != 82
329 #error bad size for AD_DATASZ_DOT_UND
330 #endif
333 * Sharemode locks fcntl() offsets
335 #if _FILE_OFFSET_BITS == 64 || defined(HAVE_LARGEFILE)
336 #define AD_FILELOCK_BASE (UINT64_C(0x7FFFFFFFFFFFFFFF) - 9)
337 #else
338 #define AD_FILELOCK_BASE (UINT32_C(0x7FFFFFFF) - 9)
339 #endif
340 #define BYTELOCK_MAX (AD_FILELOCK_BASE - 1)
342 #define AD_FILELOCK_OPEN_WR (AD_FILELOCK_BASE + 0)
343 #define AD_FILELOCK_OPEN_RD (AD_FILELOCK_BASE + 1)
344 #define AD_FILELOCK_RSRC_OPEN_WR (AD_FILELOCK_BASE + 2)
345 #define AD_FILELOCK_RSRC_OPEN_RD (AD_FILELOCK_BASE + 3)
346 #define AD_FILELOCK_DENY_WR (AD_FILELOCK_BASE + 4)
347 #define AD_FILELOCK_DENY_RD (AD_FILELOCK_BASE + 5)
348 #define AD_FILELOCK_RSRC_DENY_WR (AD_FILELOCK_BASE + 6)
349 #define AD_FILELOCK_RSRC_DENY_RD (AD_FILELOCK_BASE + 7)
350 #define AD_FILELOCK_OPEN_NONE (AD_FILELOCK_BASE + 8)
351 #define AD_FILELOCK_RSRC_OPEN_NONE (AD_FILELOCK_BASE + 9)
353 /* Time stuff we overload the bits a little */
354 #define AD_DATE_CREATE 0
355 #define AD_DATE_MODIFY 4
356 #define AD_DATE_BACKUP 8
357 #define AD_DATE_ACCESS 12
358 #define AD_DATE_MASK (AD_DATE_CREATE | AD_DATE_MODIFY | \
359 AD_DATE_BACKUP | AD_DATE_ACCESS)
360 #define AD_DATE_UNIX (1 << 10)
361 #define AD_DATE_START 0x80000000
362 #define AD_DATE_DELTA 946684800
363 #define AD_DATE_FROM_UNIX(x) (htonl((x) - AD_DATE_DELTA))
364 #define AD_DATE_TO_UNIX(x) (ntohl(x) + AD_DATE_DELTA)
366 #define AD_XATTR_HDR_MAGIC 0x41545452 /* 'ATTR' */
367 #define AD_XATTR_MAX_ENTRIES 1024 /* Some arbitrarily enforced limit */
368 #define AD_XATTR_HDR_SIZE 36
369 #define AD_XATTR_MAX_HDR_SIZE 65536
371 /* Accessor macros */
372 #define ad_getentrylen(ad,eid) ((ad)->ad_eid[(eid)].ade_len)
373 #define ad_getentryoff(ad,eid) ((ad)->ad_eid[(eid)].ade_off)
374 #define ad_setentrylen(ad,eid,len) ((ad)->ad_eid[(eid)].ade_len = (len))
375 #define ad_setentryoff(ad,eid,off) ((ad)->ad_eid[(eid)].ade_off = (off))
378 * Both struct ad_xattr_header and struct ad_xattr_entry describe the in memory
379 * representation as well as the on-disk format.
381 * The ad_xattr_header follows the FinderInfo data in the FinderInfo entry if
382 * the length of the FinderInfo entry is larger then 32 bytes. It is then
383 * preceeded with 2 bytes padding.
385 * Cf: https://opensource.apple.com/source/xnu/xnu-4570.1.46/bsd/vfs/vfs_xattr.c
388 struct ad_xattr_header {
389 uint32_t adx_magic; /* ATTR_HDR_MAGIC */
390 uint32_t adx_debug_tag; /* for debugging == file id of owning file */
391 uint32_t adx_total_size; /* file offset of end of attribute header + entries + data */
392 uint32_t adx_data_start; /* file offset to attribute data area */
393 uint32_t adx_data_length; /* length of attribute data area */
394 uint32_t adx_reserved[3];
395 uint16_t adx_flags;
396 uint16_t adx_num_attrs;
399 /* On-disk entries are aligned on 4 byte boundaries */
400 struct ad_xattr_entry {
401 uint32_t adx_offset; /* file offset to data */
402 uint32_t adx_length; /* size of attribute data */
403 uint16_t adx_flags;
404 uint8_t adx_namelen; /* included the NULL terminator */
405 char *adx_name; /* NULL-terminated UTF-8 name */
408 struct ad_entry {
409 size_t ade_off;
410 size_t ade_len;
413 struct adouble {
414 vfs_handle_struct *ad_handle;
415 int ad_fd;
416 bool ad_opened;
417 adouble_type_t ad_type;
418 uint32_t ad_magic;
419 uint32_t ad_version;
420 uint8_t ad_filler[ADEDLEN_FILLER];
421 struct ad_entry ad_eid[ADEID_MAX];
422 char *ad_data;
423 struct ad_xattr_header adx_header;
424 struct ad_xattr_entry *adx_entries;
427 struct ad_entry_order {
428 uint32_t id, offset, len;
431 /* Netatalk AppleDouble metadata xattr */
432 static const
433 struct ad_entry_order entry_order_meta_xattr[ADEID_NUM_XATTR + 1] = {
434 {ADEID_FINDERI, ADEDOFF_FINDERI_XATTR, ADEDLEN_FINDERI},
435 {ADEID_COMMENT, ADEDOFF_COMMENT_XATTR, 0},
436 {ADEID_FILEDATESI, ADEDOFF_FILEDATESI_XATTR, ADEDLEN_FILEDATESI},
437 {ADEID_AFPFILEI, ADEDOFF_AFPFILEI_XATTR, ADEDLEN_AFPFILEI},
438 {ADEID_PRIVDEV, ADEDOFF_PRIVDEV_XATTR, 0},
439 {ADEID_PRIVINO, ADEDOFF_PRIVINO_XATTR, 0},
440 {ADEID_PRIVSYN, ADEDOFF_PRIVSYN_XATTR, 0},
441 {ADEID_PRIVID, ADEDOFF_PRIVID_XATTR, 0},
442 {0, 0, 0}
445 /* AppleDouble resource fork file (the ones prefixed by "._") */
446 static const
447 struct ad_entry_order entry_order_dot_und[ADEID_NUM_DOT_UND + 1] = {
448 {ADEID_FINDERI, ADEDOFF_FINDERI_DOT_UND, ADEDLEN_FINDERI},
449 {ADEID_RFORK, ADEDOFF_RFORK_DOT_UND, 0},
450 {0, 0, 0}
454 * Fake AppleDouble entry oder for resource fork xattr. The xattr
455 * isn't an AppleDouble file, it simply contains the resource data,
456 * but in order to be able to use some API calls like ad_getentryoff()
457 * we build a fake/helper struct adouble with this entry order struct.
459 static const
460 struct ad_entry_order entry_order_rsrc_xattr[ADEID_NUM_RSRC_XATTR + 1] = {
461 {ADEID_RFORK, 0, 0},
462 {0, 0, 0}
465 /* Conversion from enumerated id to on-disk AppleDouble id */
466 #define AD_EID_DISK(a) (set_eid[a])
467 static const uint32_t set_eid[] = {
468 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
469 AD_DEV, AD_INO, AD_SYN, AD_ID
472 static char empty_resourcefork[] = {
473 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
474 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
475 0x54, 0x68, 0x69, 0x73, 0x20, 0x72, 0x65, 0x73,
476 0x6F, 0x75, 0x72, 0x63, 0x65, 0x20, 0x66, 0x6F,
477 0x72, 0x6B, 0x20, 0x69, 0x6E, 0x74, 0x65, 0x6E,
478 0x74, 0x69, 0x6F, 0x6E, 0x61, 0x6C, 0x6C, 0x79,
479 0x20, 0x6C, 0x65, 0x66, 0x74, 0x20, 0x62, 0x6C,
480 0x61, 0x6E, 0x6B, 0x20, 0x20, 0x20, 0x00, 0x00,
481 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
482 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
483 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
484 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
485 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
486 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
487 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
488 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
489 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
490 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
491 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
492 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
493 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
494 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
495 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
496 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
497 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
498 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
499 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
500 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
501 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
502 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
503 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
504 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
505 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
506 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
507 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
508 0x00, 0x1C, 0x00, 0x1E, 0xFF, 0xFF
511 struct fio {
512 /* tcon config handle */
513 struct fruit_config_data *config;
515 /* Denote stream type, meta or rsrc */
516 adouble_type_t type;
518 /* Whether the create created the stream */
519 bool created;
522 * AFP_AfpInfo stream created, but not written yet, thus still a fake
523 * pipe fd. This is set to true in fruit_open_meta if there was no
524 * exisiting stream but the caller requested O_CREAT. It is later set to
525 * false when we get a write on the stream that then does open and
526 * create the stream.
528 bool fake_fd;
529 int flags;
530 int mode;
534 * Forward declarations
536 static struct adouble *ad_init(TALLOC_CTX *ctx, vfs_handle_struct *handle,
537 adouble_type_t type);
538 static int ad_set(struct adouble *ad, const struct smb_filename *smb_fname);
539 static int ad_fset(struct adouble *ad, files_struct *fsp);
540 static int adouble_path(TALLOC_CTX *ctx,
541 const struct smb_filename *smb_fname__in,
542 struct smb_filename **ppsmb_fname_out);
543 static AfpInfo *afpinfo_new(TALLOC_CTX *ctx);
544 static ssize_t afpinfo_pack(const AfpInfo *ai, char *buf);
545 static AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data);
549 * Return a pointer to an AppleDouble entry
551 * Returns NULL if the entry is not present
553 static char *ad_get_entry(const struct adouble *ad, int eid)
555 off_t off = ad_getentryoff(ad, eid);
556 size_t len = ad_getentrylen(ad, eid);
558 if (off == 0 || len == 0) {
559 return NULL;
562 return ad->ad_data + off;
566 * Get a date
568 static int ad_getdate(const struct adouble *ad,
569 unsigned int dateoff,
570 uint32_t *date)
572 bool xlate = (dateoff & AD_DATE_UNIX);
573 char *p = NULL;
575 dateoff &= AD_DATE_MASK;
576 p = ad_get_entry(ad, ADEID_FILEDATESI);
577 if (p == NULL) {
578 return -1;
581 if (dateoff > AD_DATE_ACCESS) {
582 return -1;
585 memcpy(date, p + dateoff, sizeof(uint32_t));
587 if (xlate) {
588 *date = AD_DATE_TO_UNIX(*date);
590 return 0;
594 * Set a date
596 static int ad_setdate(struct adouble *ad, unsigned int dateoff, uint32_t date)
598 bool xlate = (dateoff & AD_DATE_UNIX);
599 char *p = NULL;
601 p = ad_get_entry(ad, ADEID_FILEDATESI);
602 if (p == NULL) {
603 return -1;
606 dateoff &= AD_DATE_MASK;
607 if (xlate) {
608 date = AD_DATE_FROM_UNIX(date);
611 if (dateoff > AD_DATE_ACCESS) {
612 return -1;
615 memcpy(p + dateoff, &date, sizeof(date));
617 return 0;
622 * Map on-disk AppleDouble id to enumerated id
624 static uint32_t get_eid(uint32_t eid)
626 if (eid <= 15) {
627 return eid;
630 switch (eid) {
631 case AD_DEV:
632 return ADEID_PRIVDEV;
633 case AD_INO:
634 return ADEID_PRIVINO;
635 case AD_SYN:
636 return ADEID_PRIVSYN;
637 case AD_ID:
638 return ADEID_PRIVID;
639 default:
640 break;
643 return 0;
647 * Pack AppleDouble structure into data buffer
649 static bool ad_pack(struct adouble *ad)
651 uint32_t eid;
652 uint16_t nent;
653 uint32_t bufsize;
654 uint32_t offset = 0;
656 bufsize = talloc_get_size(ad->ad_data);
657 if (bufsize < AD_DATASZ_DOT_UND) {
658 DBG_ERR("bad buffer size [0x%" PRIx32 "]\n", bufsize);
659 return false;
662 if (offset + ADEDLEN_MAGIC < offset ||
663 offset + ADEDLEN_MAGIC >= bufsize) {
664 return false;
666 RSIVAL(ad->ad_data, offset, ad->ad_magic);
667 offset += ADEDLEN_MAGIC;
669 if (offset + ADEDLEN_VERSION < offset ||
670 offset + ADEDLEN_VERSION >= bufsize) {
671 return false;
673 RSIVAL(ad->ad_data, offset, ad->ad_version);
674 offset += ADEDLEN_VERSION;
676 if (offset + ADEDLEN_FILLER < offset ||
677 offset + ADEDLEN_FILLER >= bufsize) {
678 return false;
680 if (ad->ad_type == ADOUBLE_RSRC) {
681 memcpy(ad->ad_data + offset, AD_FILLER_TAG, ADEDLEN_FILLER);
683 offset += ADEDLEN_FILLER;
685 if (offset + ADEDLEN_NENTRIES < offset ||
686 offset + ADEDLEN_NENTRIES >= bufsize) {
687 return false;
689 offset += ADEDLEN_NENTRIES;
691 for (eid = 0, nent = 0; eid < ADEID_MAX; eid++) {
692 if (ad->ad_eid[eid].ade_off == 0) {
694 * ade_off is also used as indicator whether a
695 * specific entry is used or not
697 continue;
700 if (offset + AD_ENTRY_LEN_EID < offset ||
701 offset + AD_ENTRY_LEN_EID >= bufsize) {
702 return false;
704 RSIVAL(ad->ad_data, offset, AD_EID_DISK(eid));
705 offset += AD_ENTRY_LEN_EID;
707 if (offset + AD_ENTRY_LEN_OFF < offset ||
708 offset + AD_ENTRY_LEN_OFF >= bufsize) {
709 return false;
711 RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_off);
712 offset += AD_ENTRY_LEN_OFF;
714 if (offset + AD_ENTRY_LEN_LEN < offset ||
715 offset + AD_ENTRY_LEN_LEN >= bufsize) {
716 return false;
718 RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_len);
719 offset += AD_ENTRY_LEN_LEN;
721 nent++;
724 if (ADEDOFF_NENTRIES + 2 >= bufsize) {
725 return false;
727 RSSVAL(ad->ad_data, ADEDOFF_NENTRIES, nent);
729 return true;
732 static bool ad_unpack_xattrs(struct adouble *ad)
734 struct ad_xattr_header *h = &ad->adx_header;
735 const char *p = ad->ad_data;
736 uint32_t hoff;
737 uint32_t i;
739 if (ad_getentrylen(ad, ADEID_FINDERI) <= ADEDLEN_FINDERI) {
740 return true;
743 /* 2 bytes padding */
744 hoff = ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI + 2;
746 h->adx_magic = RIVAL(p, hoff + 0);
747 h->adx_debug_tag = RIVAL(p, hoff + 4); /* Not used -> not checked */
748 h->adx_total_size = RIVAL(p, hoff + 8);
749 h->adx_data_start = RIVAL(p, hoff + 12);
750 h->adx_data_length = RIVAL(p, hoff + 16);
751 h->adx_flags = RSVAL(p, hoff + 32); /* Not used -> not checked */
752 h->adx_num_attrs = RSVAL(p, hoff + 34);
754 if (h->adx_magic != AD_XATTR_HDR_MAGIC) {
755 DBG_ERR("Bad magic: 0x%" PRIx32 "\n", h->adx_magic);
756 return false;
759 if (h->adx_total_size > ad_getentryoff(ad, ADEID_RFORK)) {
760 DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size);
761 return false;
763 if (h->adx_total_size > AD_XATTR_MAX_HDR_SIZE) {
764 DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size);
765 return false;
768 if (h->adx_data_start < (hoff + AD_XATTR_HDR_SIZE)) {
769 DBG_ERR("Bad start: 0x%" PRIx32 "\n", h->adx_data_start);
770 return false;
773 if ((h->adx_data_start + h->adx_data_length) < h->adx_data_start) {
774 DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length);
775 return false;
777 if ((h->adx_data_start + h->adx_data_length) >
778 ad->adx_header.adx_total_size)
780 DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length);
781 return false;
784 if (h->adx_num_attrs > AD_XATTR_MAX_ENTRIES) {
785 DBG_ERR("Bad num xattrs: %" PRIu16 "\n", h->adx_num_attrs);
786 return false;
789 if (h->adx_num_attrs == 0) {
790 return true;
793 ad->adx_entries = talloc_zero_array(
794 ad, struct ad_xattr_entry, h->adx_num_attrs);
795 if (ad->adx_entries == NULL) {
796 return false;
799 hoff += AD_XATTR_HDR_SIZE;
801 for (i = 0; i < h->adx_num_attrs; i++) {
802 struct ad_xattr_entry *e = &ad->adx_entries[i];
804 hoff = (hoff + 3) & ~3;
806 e->adx_offset = RIVAL(p, hoff + 0);
807 e->adx_length = RIVAL(p, hoff + 4);
808 e->adx_flags = RSVAL(p, hoff + 8);
809 e->adx_namelen = *(p + hoff + 10);
811 if (e->adx_offset >= ad->adx_header.adx_total_size) {
812 DBG_ERR("Bad adx_offset: %" PRIx32 "\n",
813 e->adx_offset);
814 return false;
817 if ((e->adx_offset + e->adx_length) < e->adx_offset) {
818 DBG_ERR("Bad adx_length: %" PRIx32 "\n",
819 e->adx_length);
820 return false;
823 if ((e->adx_offset + e->adx_length) >
824 ad->adx_header.adx_total_size)
826 DBG_ERR("Bad adx_length: %" PRIx32 "\n",
827 e->adx_length);
828 return false;
831 if (e->adx_namelen == 0) {
832 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
833 e->adx_namelen);
834 return false;
836 if ((hoff + 11 + e->adx_namelen) < hoff + 11) {
837 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
838 e->adx_namelen);
839 return false;
841 if ((hoff + 11 + e->adx_namelen) >
842 ad->adx_header.adx_data_start)
844 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
845 e->adx_namelen);
846 return false;
849 e->adx_name = talloc_strndup(ad->adx_entries,
850 p + hoff + 11,
851 e->adx_namelen);
852 if (e->adx_name == NULL) {
853 return false;
856 DBG_DEBUG("xattr [%s] offset [0x%x] size [0x%x]\n",
857 e->adx_name, e->adx_offset, e->adx_length);
858 dump_data(10, (uint8_t *)(ad->ad_data + e->adx_offset),
859 e->adx_length);
861 hoff += 11 + e->adx_namelen;
864 return true;
868 * Unpack an AppleDouble blob into a struct adoble
870 static bool ad_unpack(struct adouble *ad, const size_t nentries,
871 size_t filesize)
873 size_t bufsize = talloc_get_size(ad->ad_data);
874 size_t adentries, i;
875 uint32_t eid, len, off;
876 bool ok;
879 * The size of the buffer ad->ad_data is checked when read, so
880 * we wouldn't have to check our own offsets, a few extra
881 * checks won't hurt though. We have to check the offsets we
882 * read from the buffer anyway.
885 if (bufsize < (AD_HEADER_LEN + (AD_ENTRY_LEN * nentries))) {
886 DEBUG(1, ("bad size\n"));
887 return false;
890 ad->ad_magic = RIVAL(ad->ad_data, 0);
891 ad->ad_version = RIVAL(ad->ad_data, ADEDOFF_VERSION);
892 if ((ad->ad_magic != AD_MAGIC) || (ad->ad_version != AD_VERSION)) {
893 DEBUG(1, ("wrong magic or version\n"));
894 return false;
897 memcpy(ad->ad_filler, ad->ad_data + ADEDOFF_FILLER, ADEDLEN_FILLER);
899 adentries = RSVAL(ad->ad_data, ADEDOFF_NENTRIES);
900 if (adentries != nentries) {
901 DEBUG(1, ("invalid number of entries: %zu\n",
902 adentries));
903 return false;
906 /* now, read in the entry bits */
907 for (i = 0; i < adentries; i++) {
908 eid = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN));
909 eid = get_eid(eid);
910 off = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 4);
911 len = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 8);
913 if (!eid || eid >= ADEID_MAX) {
914 DEBUG(1, ("bogus eid %d\n", eid));
915 return false;
919 * All entries other than the resource fork are
920 * expected to be read into the ad_data buffer, so
921 * ensure the specified offset is within that bound
923 if ((off > bufsize) && (eid != ADEID_RFORK)) {
924 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
925 eid, off, len));
926 return false;
930 * All entries besides FinderInfo and resource fork
931 * must fit into the buffer. FinderInfo is special as
932 * it may be larger then the default 32 bytes (if it
933 * contains marshalled xattrs), but we will fixup that
934 * in ad_convert(). And the resource fork is never
935 * accessed directly by the ad_data buf (also see
936 * comment above) anyway.
938 if ((eid != ADEID_RFORK) &&
939 (eid != ADEID_FINDERI) &&
940 ((off + len) > bufsize)) {
941 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
942 eid, off, len));
943 return false;
947 * That would be obviously broken
949 if (off > filesize) {
950 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
951 eid, off, len));
952 return false;
956 * Check for any entry that has its end beyond the
957 * filesize.
959 if (off + len < off) {
960 DEBUG(1, ("offset wrap in eid %d: off: %" PRIu32
961 ", len: %" PRIu32 "\n",
962 eid, off, len));
963 return false;
966 if (off + len > filesize) {
968 * If this is the resource fork entry, we fix
969 * up the length, for any other entry we bail
970 * out.
972 if (eid != ADEID_RFORK) {
973 DEBUG(1, ("bogus eid %d: off: %" PRIu32
974 ", len: %" PRIu32 "\n",
975 eid, off, len));
976 return false;
980 * Fixup the resource fork entry by limiting
981 * the size to entryoffset - filesize.
983 len = filesize - off;
984 DEBUG(1, ("Limiting ADEID_RFORK: off: %" PRIu32
985 ", len: %" PRIu32 "\n", off, len));
988 ad->ad_eid[eid].ade_off = off;
989 ad->ad_eid[eid].ade_len = len;
992 ok = ad_unpack_xattrs(ad);
993 if (!ok) {
994 return false;
997 return true;
1000 static bool ad_convert_move_reso(struct adouble *ad,
1001 const struct smb_filename *smb_fname)
1003 char *map = MAP_FAILED;
1004 size_t maplen;
1005 ssize_t len;
1006 int rc;
1007 bool ok;
1009 if (ad_getentrylen(ad, ADEID_RFORK) == 0) {
1010 return true;
1013 maplen = ad_getentryoff(ad, ADEID_RFORK) +
1014 ad_getentrylen(ad, ADEID_RFORK);
1016 /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
1017 map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
1018 ad->ad_fd, 0);
1019 if (map == MAP_FAILED) {
1020 DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
1021 return false;
1025 memmove(map + ADEDOFF_RFORK_DOT_UND,
1026 map + ad_getentryoff(ad, ADEID_RFORK),
1027 ad_getentrylen(ad, ADEID_RFORK));
1029 rc = munmap(map, maplen);
1030 if (rc != 0) {
1031 DBG_ERR("munmap failed: %s\n", strerror(errno));
1032 return false;
1035 ad_setentryoff(ad, ADEID_RFORK, ADEDOFF_RFORK_DOT_UND);
1037 ok = ad_pack(ad);
1038 if (!ok) {
1039 DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name);
1040 return false;
1043 len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
1044 if (len != AD_DATASZ_DOT_UND) {
1045 DBG_ERR("%s: bad size: %zd\n", smb_fname->base_name, len);
1046 return false;
1049 return true;
1052 static bool ad_convert_xattr(struct adouble *ad,
1053 const struct smb_filename *smb_fname,
1054 bool *converted_xattr)
1056 static struct char_mappings **string_replace_cmaps = NULL;
1057 char *map = MAP_FAILED;
1058 size_t maplen;
1059 uint16_t i;
1060 ssize_t len;
1061 int saved_errno = 0;
1062 NTSTATUS status;
1063 int rc;
1064 bool ok;
1066 *converted_xattr = false;
1068 if (ad_getentrylen(ad, ADEID_FINDERI) == ADEDLEN_FINDERI) {
1069 return true;
1072 if (string_replace_cmaps == NULL) {
1073 const char **mappings = NULL;
1075 mappings = str_list_make_v3_const(
1076 talloc_tos(), fruit_catia_maps, NULL);
1077 if (mappings == NULL) {
1078 return false;
1080 string_replace_cmaps = string_replace_init_map(mappings);
1081 TALLOC_FREE(mappings);
1084 maplen = ad_getentryoff(ad, ADEID_RFORK) +
1085 ad_getentrylen(ad, ADEID_RFORK);
1087 /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
1088 map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
1089 ad->ad_fd, 0);
1090 if (map == MAP_FAILED) {
1091 DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
1092 return false;
1095 for (i = 0; i < ad->adx_header.adx_num_attrs; i++) {
1096 struct ad_xattr_entry *e = &ad->adx_entries[i];
1097 char *mapped_name = NULL;
1098 char *tmp = NULL;
1099 struct smb_filename *stream_name = NULL;
1100 files_struct *fsp = NULL;
1101 ssize_t nwritten;
1103 status = string_replace_allocate(ad->ad_handle->conn,
1104 e->adx_name,
1105 string_replace_cmaps,
1106 talloc_tos(),
1107 &mapped_name,
1108 vfs_translate_to_windows);
1109 if (!NT_STATUS_IS_OK(status) &&
1110 !NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))
1112 DBG_ERR("string_replace_allocate failed\n");
1113 ok = false;
1114 goto fail;
1117 tmp = mapped_name;
1118 mapped_name = talloc_asprintf(talloc_tos(), ":%s", tmp);
1119 TALLOC_FREE(tmp);
1120 if (mapped_name == NULL) {
1121 ok = false;
1122 goto fail;
1125 stream_name = synthetic_smb_fname(talloc_tos(),
1126 smb_fname->base_name,
1127 mapped_name,
1128 NULL,
1129 smb_fname->flags);
1130 TALLOC_FREE(mapped_name);
1131 if (stream_name == NULL) {
1132 DBG_ERR("synthetic_smb_fname failed\n");
1133 ok = false;
1134 goto fail;
1137 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1139 status = SMB_VFS_CREATE_FILE(
1140 ad->ad_handle->conn, /* conn */
1141 NULL, /* req */
1142 0, /* root_dir_fid */
1143 stream_name, /* fname */
1144 FILE_GENERIC_WRITE, /* access_mask */
1145 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
1146 FILE_OPEN_IF, /* create_disposition */
1147 0, /* create_options */
1148 0, /* file_attributes */
1149 INTERNAL_OPEN_ONLY, /* oplock_request */
1150 NULL, /* lease */
1151 0, /* allocation_size */
1152 0, /* private_flags */
1153 NULL, /* sd */
1154 NULL, /* ea_list */
1155 &fsp, /* result */
1156 NULL, /* psbuf */
1157 NULL, NULL); /* create context */
1158 TALLOC_FREE(stream_name);
1159 if (!NT_STATUS_IS_OK(status)) {
1160 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1161 ok = false;
1162 goto fail;
1165 nwritten = SMB_VFS_PWRITE(fsp,
1166 map + e->adx_offset,
1167 e->adx_length,
1169 if (nwritten == -1) {
1170 DBG_ERR("SMB_VFS_PWRITE failed\n");
1171 saved_errno = errno;
1172 close_file(NULL, fsp, ERROR_CLOSE);
1173 errno = saved_errno;
1174 ok = false;
1175 goto fail;
1178 status = close_file(NULL, fsp, NORMAL_CLOSE);
1179 if (!NT_STATUS_IS_OK(status)) {
1180 ok = false;
1181 goto fail;
1183 fsp = NULL;
1186 ad_setentrylen(ad, ADEID_FINDERI, ADEDLEN_FINDERI);
1188 ok = ad_pack(ad);
1189 if (!ok) {
1190 DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name);
1191 goto fail;
1194 len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
1195 if (len != AD_DATASZ_DOT_UND) {
1196 DBG_ERR("%s: bad size: %zd\n", smb_fname->base_name, len);
1197 ok = false;
1198 goto fail;
1201 ok = ad_convert_move_reso(ad, smb_fname);
1202 if (!ok) {
1203 goto fail;
1206 *converted_xattr = true;
1207 ok = true;
1209 fail:
1210 rc = munmap(map, maplen);
1211 if (rc != 0) {
1212 DBG_ERR("munmap failed: %s\n", strerror(errno));
1213 return false;
1216 return ok;
1219 static bool ad_convert_finderinfo(struct adouble *ad,
1220 const struct smb_filename *smb_fname)
1222 char *p_ad = NULL;
1223 AfpInfo *ai = NULL;
1224 DATA_BLOB aiblob;
1225 struct smb_filename *stream_name = NULL;
1226 files_struct *fsp = NULL;
1227 size_t size;
1228 ssize_t nwritten;
1229 NTSTATUS status;
1230 int saved_errno = 0;
1231 int cmp;
1233 cmp = memcmp(ad->ad_filler, AD_FILLER_TAG_OSX, ADEDLEN_FILLER);
1234 if (cmp != 0) {
1235 return true;
1238 p_ad = ad_get_entry(ad, ADEID_FINDERI);
1239 if (p_ad == NULL) {
1240 return false;
1243 ai = afpinfo_new(talloc_tos());
1244 if (ai == NULL) {
1245 return false;
1248 memcpy(ai->afpi_FinderInfo, p_ad, ADEDLEN_FINDERI);
1250 aiblob = data_blob_talloc(talloc_tos(), NULL, AFP_INFO_SIZE);
1251 if (aiblob.data == NULL) {
1252 TALLOC_FREE(ai);
1253 return false;
1256 size = afpinfo_pack(ai, (char *)aiblob.data);
1257 TALLOC_FREE(ai);
1258 if (size != AFP_INFO_SIZE) {
1259 return false;
1262 stream_name = synthetic_smb_fname(talloc_tos(),
1263 smb_fname->base_name,
1264 AFPINFO_STREAM,
1265 NULL,
1266 smb_fname->flags);
1267 if (stream_name == NULL) {
1268 data_blob_free(&aiblob);
1269 DBG_ERR("synthetic_smb_fname failed\n");
1270 return false;
1273 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1275 status = SMB_VFS_CREATE_FILE(
1276 ad->ad_handle->conn, /* conn */
1277 NULL, /* req */
1278 0, /* root_dir_fid */
1279 stream_name, /* fname */
1280 FILE_GENERIC_WRITE, /* access_mask */
1281 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
1282 FILE_OPEN_IF, /* create_disposition */
1283 0, /* create_options */
1284 0, /* file_attributes */
1285 INTERNAL_OPEN_ONLY, /* oplock_request */
1286 NULL, /* lease */
1287 0, /* allocation_size */
1288 0, /* private_flags */
1289 NULL, /* sd */
1290 NULL, /* ea_list */
1291 &fsp, /* result */
1292 NULL, /* psbuf */
1293 NULL, NULL); /* create context */
1294 TALLOC_FREE(stream_name);
1295 if (!NT_STATUS_IS_OK(status)) {
1296 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1297 return false;
1300 nwritten = SMB_VFS_PWRITE(fsp,
1301 aiblob.data,
1302 aiblob.length,
1304 if (nwritten == -1) {
1305 DBG_ERR("SMB_VFS_PWRITE failed\n");
1306 saved_errno = errno;
1307 close_file(NULL, fsp, ERROR_CLOSE);
1308 errno = saved_errno;
1309 return false;
1312 status = close_file(NULL, fsp, NORMAL_CLOSE);
1313 if (!NT_STATUS_IS_OK(status)) {
1314 return false;
1316 fsp = NULL;
1318 return true;
1321 static bool ad_convert_truncate(struct adouble *ad,
1322 const struct smb_filename *smb_fname)
1324 int rc;
1327 * FIXME: direct ftruncate(), but we don't have a fsp for the
1328 * VFS call
1330 rc = ftruncate(ad->ad_fd, ADEDOFF_RFORK_DOT_UND +
1331 ad_getentrylen(ad, ADEID_RFORK));
1332 if (rc != 0) {
1333 return false;
1336 return true;
1339 static bool ad_convert_blank_rfork(struct adouble *ad,
1340 bool *blank)
1342 struct fruit_config_data *config = NULL;
1343 uint8_t *map = MAP_FAILED;
1344 size_t maplen;
1345 int cmp;
1346 ssize_t len;
1347 int rc;
1348 bool ok;
1350 *blank = false;
1352 SMB_VFS_HANDLE_GET_DATA(ad->ad_handle, config,
1353 struct fruit_config_data, return false);
1355 if (!config->wipe_intentionally_left_blank_rfork) {
1356 return true;
1359 if (ad_getentrylen(ad, ADEID_RFORK) != sizeof(empty_resourcefork)) {
1360 return true;
1363 maplen = ad_getentryoff(ad, ADEID_RFORK) +
1364 ad_getentrylen(ad, ADEID_RFORK);
1366 /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
1367 map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
1368 ad->ad_fd, 0);
1369 if (map == MAP_FAILED) {
1370 DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
1371 return false;
1374 cmp = memcmp(map + ADEDOFF_RFORK_DOT_UND,
1375 empty_resourcefork,
1376 sizeof(empty_resourcefork));
1377 rc = munmap(map, maplen);
1378 if (rc != 0) {
1379 DBG_ERR("munmap failed: %s\n", strerror(errno));
1380 return false;
1383 if (cmp != 0) {
1384 return true;
1387 ad_setentrylen(ad, ADEID_RFORK, 0);
1389 ok = ad_pack(ad);
1390 if (!ok) {
1391 return false;
1394 len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
1395 if (len != AD_DATASZ_DOT_UND) {
1396 return false;
1399 *blank = true;
1400 return true;
1403 static bool ad_convert_delete_adfile(struct adouble *ad,
1404 const struct smb_filename *smb_fname)
1406 struct fruit_config_data *config = NULL;
1407 struct smb_filename *ad_name = NULL;
1408 int rc;
1410 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
1411 return true;
1414 SMB_VFS_HANDLE_GET_DATA(ad->ad_handle, config,
1415 struct fruit_config_data, return false);
1417 if (!config->delete_empty_adfiles) {
1418 return true;
1421 rc = adouble_path(talloc_tos(), smb_fname, &ad_name);
1422 if (rc != 0) {
1423 return false;
1426 rc = SMB_VFS_NEXT_UNLINK(ad->ad_handle, ad_name);
1427 if (rc != 0) {
1428 DBG_ERR("Unlinking [%s] failed: %s\n",
1429 smb_fname_str_dbg(ad_name), strerror(errno));
1430 TALLOC_FREE(ad_name);
1431 return false;
1434 DBG_WARNING("Unlinked [%s] after conversion\n", smb_fname_str_dbg(ad_name));
1435 TALLOC_FREE(ad_name);
1437 return true;
1441 * Convert from Apple's ._ file to Netatalk
1443 * Apple's AppleDouble may contain a FinderInfo entry longer then 32
1444 * bytes containing packed xattrs.
1446 * @return -1 in case an error occurred, 0 if no conversion was done, 1
1447 * otherwise
1449 static int ad_convert(struct adouble *ad,
1450 const struct smb_filename *smb_fname)
1452 bool ok;
1453 bool converted_xattr = false;
1454 bool blank;
1456 ok = ad_convert_xattr(ad, smb_fname, &converted_xattr);
1457 if (!ok) {
1458 return -1;
1461 ok = ad_convert_blank_rfork(ad, &blank);
1462 if (!ok) {
1463 return -1;
1466 if (converted_xattr || blank) {
1467 ok = ad_convert_truncate(ad, smb_fname);
1468 if (!ok) {
1469 return -1;
1473 ok = ad_convert_finderinfo(ad, smb_fname);
1474 if (!ok) {
1475 DBG_ERR("Failed to convert [%s]\n",
1476 smb_fname_str_dbg(smb_fname));
1477 return -1;
1480 ok = ad_convert_delete_adfile(ad, smb_fname);
1481 if (!ok) {
1482 return -1;
1485 return 0;
1489 * Read and parse Netatalk AppleDouble metadata xattr
1491 static ssize_t ad_read_meta(struct adouble *ad,
1492 const struct smb_filename *smb_fname)
1494 int rc = 0;
1495 ssize_t ealen;
1496 bool ok;
1498 DEBUG(10, ("reading meta xattr for %s\n", smb_fname->base_name));
1500 ealen = SMB_VFS_GETXATTR(ad->ad_handle->conn, smb_fname,
1501 AFPINFO_EA_NETATALK, ad->ad_data,
1502 AD_DATASZ_XATTR);
1503 if (ealen == -1) {
1504 switch (errno) {
1505 case ENOATTR:
1506 case ENOENT:
1507 if (errno == ENOATTR) {
1508 errno = ENOENT;
1510 rc = -1;
1511 goto exit;
1512 default:
1513 DEBUG(2, ("error reading meta xattr: %s\n",
1514 strerror(errno)));
1515 rc = -1;
1516 goto exit;
1519 if (ealen != AD_DATASZ_XATTR) {
1520 DEBUG(2, ("bad size %zd\n", ealen));
1521 errno = EINVAL;
1522 rc = -1;
1523 goto exit;
1526 /* Now parse entries */
1527 ok = ad_unpack(ad, ADEID_NUM_XATTR, AD_DATASZ_XATTR);
1528 if (!ok) {
1529 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1530 errno = EINVAL;
1531 rc = -1;
1532 goto exit;
1535 if (!ad_getentryoff(ad, ADEID_FINDERI)
1536 || !ad_getentryoff(ad, ADEID_COMMENT)
1537 || !ad_getentryoff(ad, ADEID_FILEDATESI)
1538 || !ad_getentryoff(ad, ADEID_AFPFILEI)
1539 || !ad_getentryoff(ad, ADEID_PRIVDEV)
1540 || !ad_getentryoff(ad, ADEID_PRIVINO)
1541 || !ad_getentryoff(ad, ADEID_PRIVSYN)
1542 || !ad_getentryoff(ad, ADEID_PRIVID)) {
1543 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1544 errno = EINVAL;
1545 rc = -1;
1546 goto exit;
1549 exit:
1550 DEBUG(10, ("reading meta xattr for %s, rc: %d\n",
1551 smb_fname->base_name, rc));
1553 if (rc != 0) {
1554 ealen = -1;
1555 if (errno == EINVAL) {
1556 become_root();
1557 removexattr(smb_fname->base_name, AFPINFO_EA_NETATALK);
1558 unbecome_root();
1559 errno = ENOENT;
1562 return ealen;
1565 static int ad_open_rsrc_xattr(const struct smb_filename *smb_fname,
1566 int flags,
1567 mode_t mode)
1569 #ifdef HAVE_ATTROPEN
1570 /* FIXME: direct Solaris xattr syscall */
1571 return attropen(smb_fname->base_name,
1572 AFPRESOURCE_EA_NETATALK, flags, mode);
1573 #else
1574 errno = ENOSYS;
1575 return -1;
1576 #endif
1579 static int ad_open_rsrc_adouble(const struct smb_filename *smb_fname,
1580 int flags,
1581 mode_t mode)
1583 int ret;
1584 int fd;
1585 struct smb_filename *adp_smb_fname = NULL;
1587 ret = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
1588 if (ret != 0) {
1589 return -1;
1592 fd = open(adp_smb_fname->base_name, flags, mode);
1593 TALLOC_FREE(adp_smb_fname);
1595 return fd;
1598 static int ad_open_rsrc(vfs_handle_struct *handle,
1599 const struct smb_filename *smb_fname,
1600 int flags,
1601 mode_t mode)
1603 struct fruit_config_data *config = NULL;
1604 int fd;
1606 SMB_VFS_HANDLE_GET_DATA(handle, config,
1607 struct fruit_config_data, return -1);
1609 if (config->rsrc == FRUIT_RSRC_XATTR) {
1610 fd = ad_open_rsrc_xattr(smb_fname, flags, mode);
1611 } else {
1612 fd = ad_open_rsrc_adouble(smb_fname, flags, mode);
1615 return fd;
1619 * Here's the deal: for ADOUBLE_META we can do without an fd as we can issue
1620 * path based xattr calls. For ADOUBLE_RSRC however we need a full-fledged fd
1621 * for file IO on the ._ file.
1623 static int ad_open(vfs_handle_struct *handle,
1624 struct adouble *ad,
1625 files_struct *fsp,
1626 const struct smb_filename *smb_fname,
1627 int flags,
1628 mode_t mode)
1630 int fd;
1632 DBG_DEBUG("Path [%s] type [%s]\n", smb_fname->base_name,
1633 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
1635 if (ad->ad_type == ADOUBLE_META) {
1636 return 0;
1639 if ((fsp != NULL) && (fsp->fh != NULL) && (fsp->fh->fd != -1)) {
1640 ad->ad_fd = fsp->fh->fd;
1641 ad->ad_opened = false;
1642 return 0;
1645 fd = ad_open_rsrc(handle, smb_fname, flags, mode);
1646 if (fd == -1) {
1647 return -1;
1649 ad->ad_opened = true;
1650 ad->ad_fd = fd;
1652 DBG_DEBUG("Path [%s] type [%s] fd [%d]\n",
1653 smb_fname->base_name,
1654 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc", fd);
1656 return 0;
1659 static ssize_t ad_read_rsrc_xattr(struct adouble *ad)
1661 int ret;
1662 SMB_STRUCT_STAT st;
1664 /* FIXME: direct sys_fstat(), don't have an fsp */
1665 ret = sys_fstat(ad->ad_fd, &st,
1666 lp_fake_directory_create_times(
1667 SNUM(ad->ad_handle->conn)));
1668 if (ret != 0) {
1669 return -1;
1672 ad_setentrylen(ad, ADEID_RFORK, st.st_ex_size);
1673 return st.st_ex_size;
1676 static ssize_t ad_read_rsrc_adouble(struct adouble *ad,
1677 const struct smb_filename *smb_fname)
1679 SMB_STRUCT_STAT sbuf;
1680 char *p_ad = NULL;
1681 size_t size;
1682 ssize_t len;
1683 int ret;
1684 bool ok;
1686 ret = sys_fstat(ad->ad_fd, &sbuf, lp_fake_directory_create_times(
1687 SNUM(ad->ad_handle->conn)));
1688 if (ret != 0) {
1689 return -1;
1693 * AppleDouble file header content and size, two cases:
1695 * - without xattrs it is exactly AD_DATASZ_DOT_UND (82) bytes large
1696 * - with embedded xattrs it can be larger, up to AD_XATTR_MAX_HDR_SIZE
1698 * Read as much as we can up to AD_XATTR_MAX_HDR_SIZE.
1700 size = sbuf.st_ex_size;
1701 if (size > talloc_array_length(ad->ad_data)) {
1702 if (size > AD_XATTR_MAX_HDR_SIZE) {
1703 size = AD_XATTR_MAX_HDR_SIZE;
1705 p_ad = talloc_realloc(ad, ad->ad_data, char, size);
1706 if (p_ad == NULL) {
1707 return -1;
1709 ad->ad_data = p_ad;
1712 len = sys_pread(ad->ad_fd, ad->ad_data,
1713 talloc_array_length(ad->ad_data), 0);
1714 if (len != talloc_array_length(ad->ad_data)) {
1715 DBG_NOTICE("%s %s: bad size: %zd\n",
1716 smb_fname->base_name, strerror(errno), len);
1717 return -1;
1720 /* Now parse entries */
1721 ok = ad_unpack(ad, ADEID_NUM_DOT_UND, sbuf.st_ex_size);
1722 if (!ok) {
1723 DBG_ERR("invalid AppleDouble resource %s\n",
1724 smb_fname->base_name);
1725 errno = EINVAL;
1726 return -1;
1729 if ((ad_getentryoff(ad, ADEID_FINDERI) != ADEDOFF_FINDERI_DOT_UND)
1730 || (ad_getentrylen(ad, ADEID_FINDERI) < ADEDLEN_FINDERI)
1731 || (ad_getentryoff(ad, ADEID_RFORK) < ADEDOFF_RFORK_DOT_UND)) {
1732 DBG_ERR("invalid AppleDouble resource %s\n",
1733 smb_fname->base_name);
1734 errno = EINVAL;
1735 return -1;
1739 * Try to fixup AppleDouble files created by OS X with xattrs
1740 * appended to the ADEID_FINDERI entry.
1743 ret = ad_convert(ad, smb_fname);
1744 if (ret != 0) {
1745 DBG_WARNING("Failed to convert [%s]\n", smb_fname->base_name);
1746 return len;
1749 return len;
1753 * Read and parse resource fork, either ._ AppleDouble file or xattr
1755 static ssize_t ad_read_rsrc(struct adouble *ad,
1756 const struct smb_filename *smb_fname)
1758 struct fruit_config_data *config = NULL;
1759 ssize_t len;
1761 SMB_VFS_HANDLE_GET_DATA(ad->ad_handle, config,
1762 struct fruit_config_data, return -1);
1764 if (config->rsrc == FRUIT_RSRC_XATTR) {
1765 len = ad_read_rsrc_xattr(ad);
1766 } else {
1767 len = ad_read_rsrc_adouble(ad, smb_fname);
1770 return len;
1774 * Read and unpack an AppleDouble metadata xattr or resource
1776 static ssize_t ad_read(struct adouble *ad, const struct smb_filename *smb_fname)
1778 switch (ad->ad_type) {
1779 case ADOUBLE_META:
1780 return ad_read_meta(ad, smb_fname);
1781 case ADOUBLE_RSRC:
1782 return ad_read_rsrc(ad, smb_fname);
1783 default:
1784 return -1;
1788 static int adouble_destructor(struct adouble *ad)
1790 if ((ad->ad_fd != -1) && ad->ad_opened) {
1791 close(ad->ad_fd);
1792 ad->ad_fd = -1;
1794 return 0;
1798 * Allocate a struct adouble without initialiing it
1800 * The struct is either hang of the fsp extension context or if fsp is
1801 * NULL from ctx.
1803 * @param[in] ctx talloc context
1804 * @param[in] handle vfs handle
1805 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1807 * @return adouble handle
1809 static struct adouble *ad_alloc(TALLOC_CTX *ctx, vfs_handle_struct *handle,
1810 adouble_type_t type)
1812 int rc = 0;
1813 size_t adsize = 0;
1814 struct adouble *ad;
1815 struct fruit_config_data *config;
1817 SMB_VFS_HANDLE_GET_DATA(handle, config,
1818 struct fruit_config_data, return NULL);
1820 switch (type) {
1821 case ADOUBLE_META:
1822 adsize = AD_DATASZ_XATTR;
1823 break;
1824 case ADOUBLE_RSRC:
1825 if (config->rsrc == FRUIT_RSRC_ADFILE) {
1826 adsize = AD_DATASZ_DOT_UND;
1828 break;
1829 default:
1830 return NULL;
1833 ad = talloc_zero(ctx, struct adouble);
1834 if (ad == NULL) {
1835 rc = -1;
1836 goto exit;
1839 if (adsize) {
1840 ad->ad_data = talloc_zero_array(ad, char, adsize);
1841 if (ad->ad_data == NULL) {
1842 rc = -1;
1843 goto exit;
1847 ad->ad_handle = handle;
1848 ad->ad_type = type;
1849 ad->ad_magic = AD_MAGIC;
1850 ad->ad_version = AD_VERSION;
1851 ad->ad_fd = -1;
1853 talloc_set_destructor(ad, adouble_destructor);
1855 exit:
1856 if (rc != 0) {
1857 TALLOC_FREE(ad);
1859 return ad;
1863 * Allocate and initialize a new struct adouble
1865 * @param[in] ctx talloc context
1866 * @param[in] handle vfs handle
1867 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1869 * @return adouble handle, initialized
1871 static struct adouble *ad_init(TALLOC_CTX *ctx, vfs_handle_struct *handle,
1872 adouble_type_t type)
1874 int rc = 0;
1875 const struct ad_entry_order *eid;
1876 struct adouble *ad = NULL;
1877 struct fruit_config_data *config;
1878 time_t t = time(NULL);
1880 SMB_VFS_HANDLE_GET_DATA(handle, config,
1881 struct fruit_config_data, return NULL);
1883 switch (type) {
1884 case ADOUBLE_META:
1885 eid = entry_order_meta_xattr;
1886 break;
1887 case ADOUBLE_RSRC:
1888 if (config->rsrc == FRUIT_RSRC_ADFILE) {
1889 eid = entry_order_dot_und;
1890 } else {
1891 eid = entry_order_rsrc_xattr;
1893 break;
1894 default:
1895 return NULL;
1898 ad = ad_alloc(ctx, handle, type);
1899 if (ad == NULL) {
1900 return NULL;
1903 while (eid->id) {
1904 ad->ad_eid[eid->id].ade_off = eid->offset;
1905 ad->ad_eid[eid->id].ade_len = eid->len;
1906 eid++;
1909 /* put something sane in the date fields */
1910 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, t);
1911 ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, t);
1912 ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, t);
1913 ad_setdate(ad, AD_DATE_BACKUP, htonl(AD_DATE_START));
1915 if (rc != 0) {
1916 TALLOC_FREE(ad);
1918 return ad;
1921 static struct adouble *ad_get_internal(TALLOC_CTX *ctx,
1922 vfs_handle_struct *handle,
1923 files_struct *fsp,
1924 const struct smb_filename *smb_fname,
1925 adouble_type_t type)
1927 int rc = 0;
1928 ssize_t len;
1929 struct adouble *ad = NULL;
1930 int mode;
1932 if (fsp != NULL) {
1933 smb_fname = fsp->base_fsp->fsp_name;
1936 DEBUG(10, ("ad_get(%s) called for %s\n",
1937 type == ADOUBLE_META ? "meta" : "rsrc",
1938 smb_fname->base_name));
1940 ad = ad_alloc(ctx, handle, type);
1941 if (ad == NULL) {
1942 rc = -1;
1943 goto exit;
1946 /* Try rw first so we can use the fd in ad_convert() */
1947 mode = O_RDWR;
1949 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
1950 if (rc == -1 && ((errno == EROFS) || (errno == EACCES))) {
1951 mode = O_RDONLY;
1952 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
1954 if (rc == -1) {
1955 DBG_DEBUG("ad_open [%s] error [%s]\n",
1956 smb_fname->base_name, strerror(errno));
1957 goto exit;
1961 len = ad_read(ad, smb_fname);
1962 if (len == -1) {
1963 DEBUG(10, ("error reading AppleDouble for %s\n",
1964 smb_fname->base_name));
1965 rc = -1;
1966 goto exit;
1969 exit:
1970 DEBUG(10, ("ad_get(%s) for %s returning %d\n",
1971 type == ADOUBLE_META ? "meta" : "rsrc",
1972 smb_fname->base_name, rc));
1974 if (rc != 0) {
1975 TALLOC_FREE(ad);
1977 return ad;
1981 * Return AppleDouble data for a file
1983 * @param[in] ctx talloc context
1984 * @param[in] handle vfs handle
1985 * @param[in] smb_fname pathname to file or directory
1986 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1988 * @return talloced struct adouble or NULL on error
1990 static struct adouble *ad_get(TALLOC_CTX *ctx,
1991 vfs_handle_struct *handle,
1992 const struct smb_filename *smb_fname,
1993 adouble_type_t type)
1995 return ad_get_internal(ctx, handle, NULL, smb_fname, type);
1999 * Return AppleDouble data for a file
2001 * @param[in] ctx talloc context
2002 * @param[in] handle vfs handle
2003 * @param[in] fsp fsp to use for IO
2004 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2006 * @return talloced struct adouble or NULL on error
2008 static struct adouble *ad_fget(TALLOC_CTX *ctx, vfs_handle_struct *handle,
2009 files_struct *fsp, adouble_type_t type)
2011 return ad_get_internal(ctx, handle, fsp, NULL, type);
2015 * Set AppleDouble metadata on a file or directory
2017 * @param[in] ad adouble handle
2019 * @param[in] smb_fname pathname to file or directory
2021 * @return status code, 0 means success
2023 static int ad_set(struct adouble *ad, const struct smb_filename *smb_fname)
2025 bool ok;
2026 int ret;
2028 DBG_DEBUG("Path [%s]\n", smb_fname->base_name);
2030 if (ad->ad_type != ADOUBLE_META) {
2031 DBG_ERR("ad_set on [%s] used with ADOUBLE_RSRC\n",
2032 smb_fname->base_name);
2033 return -1;
2036 ok = ad_pack(ad);
2037 if (!ok) {
2038 return -1;
2041 ret = SMB_VFS_SETXATTR(ad->ad_handle->conn,
2042 smb_fname,
2043 AFPINFO_EA_NETATALK,
2044 ad->ad_data,
2045 AD_DATASZ_XATTR, 0);
2047 DBG_DEBUG("Path [%s] ret [%d]\n", smb_fname->base_name, ret);
2049 return ret;
2053 * Set AppleDouble metadata on a file or directory
2055 * @param[in] ad adouble handle
2056 * @param[in] fsp file handle
2058 * @return status code, 0 means success
2060 static int ad_fset(struct adouble *ad, files_struct *fsp)
2062 int rc = -1;
2063 ssize_t len;
2064 bool ok;
2066 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
2068 if ((fsp == NULL)
2069 || (fsp->fh == NULL)
2070 || (fsp->fh->fd == -1))
2072 smb_panic("bad fsp");
2075 ok = ad_pack(ad);
2076 if (!ok) {
2077 return -1;
2080 switch (ad->ad_type) {
2081 case ADOUBLE_META:
2082 rc = SMB_VFS_NEXT_SETXATTR(ad->ad_handle,
2083 fsp->fsp_name,
2084 AFPINFO_EA_NETATALK,
2085 ad->ad_data,
2086 AD_DATASZ_XATTR, 0);
2087 break;
2089 case ADOUBLE_RSRC:
2090 len = SMB_VFS_NEXT_PWRITE(ad->ad_handle,
2091 fsp,
2092 ad->ad_data,
2093 AD_DATASZ_DOT_UND,
2095 if (len != AD_DATASZ_DOT_UND) {
2096 DBG_ERR("short write on %s: %zd", fsp_str_dbg(fsp), len);
2097 return -1;
2099 rc = 0;
2100 break;
2102 default:
2103 return -1;
2106 DBG_DEBUG("Path [%s] rc [%d]\n", fsp_str_dbg(fsp), rc);
2108 return rc;
2111 /*****************************************************************************
2112 * Helper functions
2113 *****************************************************************************/
2115 static bool is_afpinfo_stream(const struct smb_filename *smb_fname)
2117 if (strncasecmp_m(smb_fname->stream_name,
2118 AFPINFO_STREAM_NAME,
2119 strlen(AFPINFO_STREAM_NAME)) == 0) {
2120 return true;
2122 return false;
2125 static bool is_afpresource_stream(const struct smb_filename *smb_fname)
2127 if (strncasecmp_m(smb_fname->stream_name,
2128 AFPRESOURCE_STREAM_NAME,
2129 strlen(AFPRESOURCE_STREAM_NAME)) == 0) {
2130 return true;
2132 return false;
2136 * Test whether stream is an Apple stream, not used atm
2138 #if 0
2139 static bool is_apple_stream(const struct smb_filename *smb_fname)
2141 if (is_afpinfo_stream(smb_fname)) {
2142 return true;
2144 if (is_afpresource_stream(smb_fname)) {
2145 return true;
2147 return false;
2149 #endif
2152 * Initialize config struct from our smb.conf config parameters
2154 static int init_fruit_config(vfs_handle_struct *handle)
2156 struct fruit_config_data *config;
2157 int enumval;
2158 const char *tm_size_str = NULL;
2160 config = talloc_zero(handle->conn, struct fruit_config_data);
2161 if (!config) {
2162 DEBUG(1, ("talloc_zero() failed\n"));
2163 errno = ENOMEM;
2164 return -1;
2168 * Versions up to Samba 4.5.x had a spelling bug in the
2169 * fruit:resource option calling lp_parm_enum with
2170 * "res*s*ource" (ie two s).
2172 * In Samba 4.6 we accept both the wrong and the correct
2173 * spelling, in Samba 4.7 the bad spelling will be removed.
2175 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2176 "ressource", fruit_rsrc, FRUIT_RSRC_ADFILE);
2177 if (enumval == -1) {
2178 DEBUG(1, ("value for %s: resource type unknown\n",
2179 FRUIT_PARAM_TYPE_NAME));
2180 return -1;
2182 config->rsrc = (enum fruit_rsrc)enumval;
2184 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2185 "resource", fruit_rsrc, enumval);
2186 if (enumval == -1) {
2187 DEBUG(1, ("value for %s: resource type unknown\n",
2188 FRUIT_PARAM_TYPE_NAME));
2189 return -1;
2191 config->rsrc = (enum fruit_rsrc)enumval;
2193 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2194 "metadata", fruit_meta, FRUIT_META_NETATALK);
2195 if (enumval == -1) {
2196 DEBUG(1, ("value for %s: metadata type unknown\n",
2197 FRUIT_PARAM_TYPE_NAME));
2198 return -1;
2200 config->meta = (enum fruit_meta)enumval;
2202 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2203 "locking", fruit_locking, FRUIT_LOCKING_NONE);
2204 if (enumval == -1) {
2205 DEBUG(1, ("value for %s: locking type unknown\n",
2206 FRUIT_PARAM_TYPE_NAME));
2207 return -1;
2209 config->locking = (enum fruit_locking)enumval;
2211 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2212 "encoding", fruit_encoding, FRUIT_ENC_PRIVATE);
2213 if (enumval == -1) {
2214 DEBUG(1, ("value for %s: encoding type unknown\n",
2215 FRUIT_PARAM_TYPE_NAME));
2216 return -1;
2218 config->encoding = (enum fruit_encoding)enumval;
2220 if (config->rsrc == FRUIT_RSRC_ADFILE) {
2221 config->veto_appledouble = lp_parm_bool(SNUM(handle->conn),
2222 FRUIT_PARAM_TYPE_NAME,
2223 "veto_appledouble",
2224 true);
2227 config->use_aapl = lp_parm_bool(
2228 -1, FRUIT_PARAM_TYPE_NAME, "aapl", true);
2230 config->time_machine = lp_parm_bool(
2231 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "time machine", false);
2233 config->unix_info_enabled = lp_parm_bool(
2234 -1, FRUIT_PARAM_TYPE_NAME, "nfs_aces", true);
2236 config->use_copyfile = lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME,
2237 "copyfile", false);
2239 config->posix_rename = lp_parm_bool(
2240 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "posix_rename", true);
2242 config->aapl_zero_file_id =
2243 lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME, "zero_file_id", true);
2245 config->readdir_attr_rsize = lp_parm_bool(
2246 SNUM(handle->conn), "readdir_attr", "aapl_rsize", true);
2248 config->readdir_attr_finder_info = lp_parm_bool(
2249 SNUM(handle->conn), "readdir_attr", "aapl_finder_info", true);
2251 config->readdir_attr_max_access = lp_parm_bool(
2252 SNUM(handle->conn), "readdir_attr", "aapl_max_access", true);
2254 config->model = lp_parm_const_string(
2255 -1, FRUIT_PARAM_TYPE_NAME, "model", "MacSamba");
2257 tm_size_str = lp_parm_const_string(
2258 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2259 "time machine max size", NULL);
2260 if (tm_size_str != NULL) {
2261 config->time_machine_max_size = conv_str_size(tm_size_str);
2264 config->wipe_intentionally_left_blank_rfork = lp_parm_bool(
2265 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2266 "wipe_intentionally_left_blank_rfork", false);
2268 config->delete_empty_adfiles = lp_parm_bool(
2269 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2270 "delete_empty_adfiles", false);
2272 SMB_VFS_HANDLE_SET_DATA(handle, config,
2273 NULL, struct fruit_config_data,
2274 return -1);
2276 return 0;
2280 * Prepend "._" to a basename
2281 * Return a new struct smb_filename with stream_name == NULL.
2283 static int adouble_path(TALLOC_CTX *ctx,
2284 const struct smb_filename *smb_fname_in,
2285 struct smb_filename **pp_smb_fname_out)
2287 char *parent;
2288 const char *base;
2289 struct smb_filename *smb_fname = cp_smb_filename(ctx,
2290 smb_fname_in);
2292 if (smb_fname == NULL) {
2293 return -1;
2296 /* We need streamname to be NULL */
2297 TALLOC_FREE(smb_fname->stream_name);
2299 /* And we're replacing base_name. */
2300 TALLOC_FREE(smb_fname->base_name);
2302 if (!parent_dirname(smb_fname, smb_fname_in->base_name,
2303 &parent, &base)) {
2304 TALLOC_FREE(smb_fname);
2305 return -1;
2308 smb_fname->base_name = talloc_asprintf(smb_fname,
2309 "%s/._%s", parent, base);
2310 if (smb_fname->base_name == NULL) {
2311 TALLOC_FREE(smb_fname);
2312 return -1;
2315 *pp_smb_fname_out = smb_fname;
2317 return 0;
2321 * Allocate and initialize an AfpInfo struct
2323 static AfpInfo *afpinfo_new(TALLOC_CTX *ctx)
2325 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2326 if (ai == NULL) {
2327 return NULL;
2329 ai->afpi_Signature = AFP_Signature;
2330 ai->afpi_Version = AFP_Version;
2331 ai->afpi_BackupTime = AD_DATE_START;
2332 return ai;
2336 * Pack an AfpInfo struct into a buffer
2338 * Buffer size must be at least AFP_INFO_SIZE
2339 * Returns size of packed buffer
2341 static ssize_t afpinfo_pack(const AfpInfo *ai, char *buf)
2343 memset(buf, 0, AFP_INFO_SIZE);
2345 RSIVAL(buf, 0, ai->afpi_Signature);
2346 RSIVAL(buf, 4, ai->afpi_Version);
2347 RSIVAL(buf, 12, ai->afpi_BackupTime);
2348 memcpy(buf + 16, ai->afpi_FinderInfo, sizeof(ai->afpi_FinderInfo));
2350 return AFP_INFO_SIZE;
2354 * Unpack a buffer into a AfpInfo structure
2356 * Buffer size must be at least AFP_INFO_SIZE
2357 * Returns allocated AfpInfo struct
2359 static AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data)
2361 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2362 if (ai == NULL) {
2363 return NULL;
2366 ai->afpi_Signature = RIVAL(data, 0);
2367 ai->afpi_Version = RIVAL(data, 4);
2368 ai->afpi_BackupTime = RIVAL(data, 12);
2369 memcpy(ai->afpi_FinderInfo, (const char *)data + 16,
2370 sizeof(ai->afpi_FinderInfo));
2372 if (ai->afpi_Signature != AFP_Signature
2373 || ai->afpi_Version != AFP_Version) {
2374 DEBUG(1, ("Bad AfpInfo signature or version\n"));
2375 TALLOC_FREE(ai);
2378 return ai;
2382 * Fake an inode number from the md5 hash of the (xattr) name
2384 static SMB_INO_T fruit_inode(const SMB_STRUCT_STAT *sbuf, const char *sname)
2386 MD5_CTX ctx;
2387 unsigned char hash[16];
2388 SMB_INO_T result;
2389 char *upper_sname;
2391 DBG_DEBUG("fruit_inode called for %ju/%ju [%s]\n",
2392 (uintmax_t)sbuf->st_ex_dev,
2393 (uintmax_t)sbuf->st_ex_ino, sname);
2395 upper_sname = talloc_strdup_upper(talloc_tos(), sname);
2396 SMB_ASSERT(upper_sname != NULL);
2398 MD5Init(&ctx);
2399 MD5Update(&ctx, (const unsigned char *)&(sbuf->st_ex_dev),
2400 sizeof(sbuf->st_ex_dev));
2401 MD5Update(&ctx, (const unsigned char *)&(sbuf->st_ex_ino),
2402 sizeof(sbuf->st_ex_ino));
2403 MD5Update(&ctx, (unsigned char *)upper_sname,
2404 talloc_get_size(upper_sname)-1);
2405 MD5Final(hash, &ctx);
2407 TALLOC_FREE(upper_sname);
2409 /* Hopefully all the variation is in the lower 4 (or 8) bytes! */
2410 memcpy(&result, hash, sizeof(result));
2412 DBG_DEBUG("fruit_inode \"%s\": ino=%ju\n",
2413 sname, (uintmax_t)result);
2415 return result;
2418 static bool add_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
2419 struct stream_struct **streams,
2420 const char *name, off_t size,
2421 off_t alloc_size)
2423 struct stream_struct *tmp;
2425 tmp = talloc_realloc(mem_ctx, *streams, struct stream_struct,
2426 (*num_streams)+1);
2427 if (tmp == NULL) {
2428 return false;
2431 tmp[*num_streams].name = talloc_asprintf(tmp, "%s:$DATA", name);
2432 if (tmp[*num_streams].name == NULL) {
2433 return false;
2436 tmp[*num_streams].size = size;
2437 tmp[*num_streams].alloc_size = alloc_size;
2439 *streams = tmp;
2440 *num_streams += 1;
2441 return true;
2444 static bool filter_empty_rsrc_stream(unsigned int *num_streams,
2445 struct stream_struct **streams)
2447 struct stream_struct *tmp = *streams;
2448 unsigned int i;
2450 if (*num_streams == 0) {
2451 return true;
2454 for (i = 0; i < *num_streams; i++) {
2455 if (strequal_m(tmp[i].name, AFPRESOURCE_STREAM)) {
2456 break;
2460 if (i == *num_streams) {
2461 return true;
2464 if (tmp[i].size > 0) {
2465 return true;
2468 TALLOC_FREE(tmp[i].name);
2469 if (*num_streams - 1 > i) {
2470 memmove(&tmp[i], &tmp[i+1],
2471 (*num_streams - i - 1) * sizeof(struct stream_struct));
2474 *num_streams -= 1;
2475 return true;
2478 static bool del_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
2479 struct stream_struct **streams,
2480 const char *name)
2482 struct stream_struct *tmp = *streams;
2483 unsigned int i;
2485 if (*num_streams == 0) {
2486 return true;
2489 for (i = 0; i < *num_streams; i++) {
2490 if (strequal_m(tmp[i].name, name)) {
2491 break;
2495 if (i == *num_streams) {
2496 return true;
2499 TALLOC_FREE(tmp[i].name);
2500 if (*num_streams - 1 > i) {
2501 memmove(&tmp[i], &tmp[i+1],
2502 (*num_streams - i - 1) * sizeof(struct stream_struct));
2505 *num_streams -= 1;
2506 return true;
2509 static bool ad_empty_finderinfo(const struct adouble *ad)
2511 int cmp;
2512 char emptybuf[ADEDLEN_FINDERI] = {0};
2513 char *fi = NULL;
2515 fi = ad_get_entry(ad, ADEID_FINDERI);
2516 if (fi == NULL) {
2517 DBG_ERR("Missing FinderInfo in struct adouble [%p]\n", ad);
2518 return false;
2521 cmp = memcmp(emptybuf, fi, ADEDLEN_FINDERI);
2522 return (cmp == 0);
2525 static bool ai_empty_finderinfo(const AfpInfo *ai)
2527 int cmp;
2528 char emptybuf[ADEDLEN_FINDERI] = {0};
2530 cmp = memcmp(emptybuf, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
2531 return (cmp == 0);
2535 * Update btime with btime from Netatalk
2537 static void update_btime(vfs_handle_struct *handle,
2538 struct smb_filename *smb_fname)
2540 uint32_t t;
2541 struct timespec creation_time = {0};
2542 struct adouble *ad;
2543 struct fruit_config_data *config = NULL;
2545 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
2546 return);
2548 switch (config->meta) {
2549 case FRUIT_META_STREAM:
2550 return;
2551 case FRUIT_META_NETATALK:
2552 /* Handled below */
2553 break;
2554 default:
2555 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
2556 return;
2559 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
2560 if (ad == NULL) {
2561 return;
2563 if (ad_getdate(ad, AD_DATE_UNIX | AD_DATE_CREATE, &t) != 0) {
2564 TALLOC_FREE(ad);
2565 return;
2567 TALLOC_FREE(ad);
2569 creation_time.tv_sec = convert_uint32_t_to_time_t(t);
2570 update_stat_ex_create_time(&smb_fname->st, creation_time);
2572 return;
2576 * Map an access mask to a Netatalk single byte byte range lock
2578 static off_t access_to_netatalk_brl(enum apple_fork fork_type,
2579 uint32_t access_mask)
2581 off_t offset;
2583 switch (access_mask) {
2584 case FILE_READ_DATA:
2585 offset = AD_FILELOCK_OPEN_RD;
2586 break;
2588 case FILE_WRITE_DATA:
2589 case FILE_APPEND_DATA:
2590 offset = AD_FILELOCK_OPEN_WR;
2591 break;
2593 default:
2594 offset = AD_FILELOCK_OPEN_NONE;
2595 break;
2598 if (fork_type == APPLE_FORK_RSRC) {
2599 if (offset == AD_FILELOCK_OPEN_NONE) {
2600 offset = AD_FILELOCK_RSRC_OPEN_NONE;
2601 } else {
2602 offset += 2;
2606 return offset;
2610 * Map a deny mode to a Netatalk brl
2612 static off_t denymode_to_netatalk_brl(enum apple_fork fork_type,
2613 uint32_t deny_mode)
2615 off_t offset = 0;
2617 switch (deny_mode) {
2618 case DENY_READ:
2619 offset = AD_FILELOCK_DENY_RD;
2620 break;
2622 case DENY_WRITE:
2623 offset = AD_FILELOCK_DENY_WR;
2624 break;
2626 default:
2627 smb_panic("denymode_to_netatalk_brl: bad deny mode\n");
2630 if (fork_type == APPLE_FORK_RSRC) {
2631 offset += 2;
2634 return offset;
2638 * Call fcntl() with an exclusive F_GETLK request in order to
2639 * determine if there's an exisiting shared lock
2641 * @return true if the requested lock was found or any error occurred
2642 * false if the lock was not found
2644 static bool test_netatalk_lock(files_struct *fsp, off_t in_offset)
2646 bool result;
2647 off_t offset = in_offset;
2648 off_t len = 1;
2649 int type = F_WRLCK;
2650 pid_t pid;
2652 result = SMB_VFS_GETLOCK(fsp, &offset, &len, &type, &pid);
2653 if (result == false) {
2654 return true;
2657 if (type != F_UNLCK) {
2658 return true;
2661 return false;
2664 static NTSTATUS fruit_check_access(vfs_handle_struct *handle,
2665 files_struct *fsp,
2666 uint32_t access_mask,
2667 uint32_t deny_mode)
2669 NTSTATUS status = NT_STATUS_OK;
2670 bool open_for_reading, open_for_writing, deny_read, deny_write;
2671 off_t off;
2672 bool have_read = false;
2673 int flags;
2675 /* FIXME: hardcoded data fork, add resource fork */
2676 enum apple_fork fork_type = APPLE_FORK_DATA;
2678 DEBUG(10, ("fruit_check_access: %s, am: %s/%s, dm: %s/%s\n",
2679 fsp_str_dbg(fsp),
2680 access_mask & FILE_READ_DATA ? "READ" :"-",
2681 access_mask & FILE_WRITE_DATA ? "WRITE" : "-",
2682 deny_mode & DENY_READ ? "DENY_READ" : "-",
2683 deny_mode & DENY_WRITE ? "DENY_WRITE" : "-"));
2685 if (fsp->fh->fd == -1) {
2686 return NT_STATUS_OK;
2689 flags = fcntl(fsp->fh->fd, F_GETFL);
2690 if (flags == -1) {
2691 DBG_ERR("fcntl get flags [%s] fd [%d] failed [%s]\n",
2692 fsp_str_dbg(fsp), fsp->fh->fd, strerror(errno));
2693 return map_nt_error_from_unix(errno);
2696 if (flags & (O_RDONLY|O_RDWR)) {
2698 * Applying fcntl read locks requires an fd opened for
2699 * reading. This means we won't be applying locks for
2700 * files openend write-only, but what can we do...
2702 have_read = true;
2706 * Check read access and deny read mode
2708 if ((access_mask & FILE_READ_DATA) || (deny_mode & DENY_READ)) {
2709 /* Check access */
2710 open_for_reading = test_netatalk_lock(
2711 fsp, access_to_netatalk_brl(fork_type, FILE_READ_DATA));
2713 deny_read = test_netatalk_lock(
2714 fsp, denymode_to_netatalk_brl(fork_type, DENY_READ));
2716 DEBUG(10, ("read: %s, deny_write: %s\n",
2717 open_for_reading == true ? "yes" : "no",
2718 deny_read == true ? "yes" : "no"));
2720 if (((access_mask & FILE_READ_DATA) && deny_read)
2721 || ((deny_mode & DENY_READ) && open_for_reading)) {
2722 return NT_STATUS_SHARING_VIOLATION;
2725 /* Set locks */
2726 if ((access_mask & FILE_READ_DATA) && have_read) {
2727 struct byte_range_lock *br_lck = NULL;
2729 off = access_to_netatalk_brl(fork_type, FILE_READ_DATA);
2730 br_lck = do_lock(
2731 handle->conn->sconn->msg_ctx, fsp,
2732 fsp->op->global->open_persistent_id, 1, off,
2733 READ_LOCK, POSIX_LOCK, false,
2734 &status, NULL);
2736 TALLOC_FREE(br_lck);
2738 if (!NT_STATUS_IS_OK(status)) {
2739 return status;
2743 if ((deny_mode & DENY_READ) && have_read) {
2744 struct byte_range_lock *br_lck = NULL;
2746 off = denymode_to_netatalk_brl(fork_type, DENY_READ);
2747 br_lck = do_lock(
2748 handle->conn->sconn->msg_ctx, fsp,
2749 fsp->op->global->open_persistent_id, 1, off,
2750 READ_LOCK, POSIX_LOCK, false,
2751 &status, NULL);
2753 TALLOC_FREE(br_lck);
2755 if (!NT_STATUS_IS_OK(status)) {
2756 return status;
2762 * Check write access and deny write mode
2764 if ((access_mask & FILE_WRITE_DATA) || (deny_mode & DENY_WRITE)) {
2765 /* Check access */
2766 open_for_writing = test_netatalk_lock(
2767 fsp, access_to_netatalk_brl(fork_type, FILE_WRITE_DATA));
2769 deny_write = test_netatalk_lock(
2770 fsp, denymode_to_netatalk_brl(fork_type, DENY_WRITE));
2772 DEBUG(10, ("write: %s, deny_write: %s\n",
2773 open_for_writing == true ? "yes" : "no",
2774 deny_write == true ? "yes" : "no"));
2776 if (((access_mask & FILE_WRITE_DATA) && deny_write)
2777 || ((deny_mode & DENY_WRITE) && open_for_writing)) {
2778 return NT_STATUS_SHARING_VIOLATION;
2781 /* Set locks */
2782 if ((access_mask & FILE_WRITE_DATA) && have_read) {
2783 struct byte_range_lock *br_lck = NULL;
2785 off = access_to_netatalk_brl(fork_type, FILE_WRITE_DATA);
2786 br_lck = do_lock(
2787 handle->conn->sconn->msg_ctx, fsp,
2788 fsp->op->global->open_persistent_id, 1, off,
2789 READ_LOCK, POSIX_LOCK, false,
2790 &status, NULL);
2792 TALLOC_FREE(br_lck);
2794 if (!NT_STATUS_IS_OK(status)) {
2795 return status;
2798 if ((deny_mode & DENY_WRITE) && have_read) {
2799 struct byte_range_lock *br_lck = NULL;
2801 off = denymode_to_netatalk_brl(fork_type, DENY_WRITE);
2802 br_lck = do_lock(
2803 handle->conn->sconn->msg_ctx, fsp,
2804 fsp->op->global->open_persistent_id, 1, off,
2805 READ_LOCK, POSIX_LOCK, false,
2806 &status, NULL);
2808 TALLOC_FREE(br_lck);
2810 if (!NT_STATUS_IS_OK(status)) {
2811 return status;
2816 return status;
2819 static NTSTATUS check_aapl(vfs_handle_struct *handle,
2820 struct smb_request *req,
2821 const struct smb2_create_blobs *in_context_blobs,
2822 struct smb2_create_blobs *out_context_blobs)
2824 struct fruit_config_data *config;
2825 NTSTATUS status;
2826 struct smb2_create_blob *aapl = NULL;
2827 uint32_t cmd;
2828 bool ok;
2829 uint8_t p[16];
2830 DATA_BLOB blob = data_blob_talloc(req, NULL, 0);
2831 uint64_t req_bitmap, client_caps;
2832 uint64_t server_caps = SMB2_CRTCTX_AAPL_UNIX_BASED;
2833 smb_ucs2_t *model;
2834 size_t modellen;
2836 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
2837 return NT_STATUS_UNSUCCESSFUL);
2839 if (!config->use_aapl
2840 || in_context_blobs == NULL
2841 || out_context_blobs == NULL) {
2842 return NT_STATUS_OK;
2845 aapl = smb2_create_blob_find(in_context_blobs,
2846 SMB2_CREATE_TAG_AAPL);
2847 if (aapl == NULL) {
2848 return NT_STATUS_OK;
2851 if (aapl->data.length != 24) {
2852 DEBUG(1, ("unexpected AAPL ctxt length: %ju\n",
2853 (uintmax_t)aapl->data.length));
2854 return NT_STATUS_INVALID_PARAMETER;
2857 cmd = IVAL(aapl->data.data, 0);
2858 if (cmd != SMB2_CRTCTX_AAPL_SERVER_QUERY) {
2859 DEBUG(1, ("unsupported AAPL cmd: %d\n", cmd));
2860 return NT_STATUS_INVALID_PARAMETER;
2863 req_bitmap = BVAL(aapl->data.data, 8);
2864 client_caps = BVAL(aapl->data.data, 16);
2866 SIVAL(p, 0, SMB2_CRTCTX_AAPL_SERVER_QUERY);
2867 SIVAL(p, 4, 0);
2868 SBVAL(p, 8, req_bitmap);
2869 ok = data_blob_append(req, &blob, p, 16);
2870 if (!ok) {
2871 return NT_STATUS_UNSUCCESSFUL;
2874 if (req_bitmap & SMB2_CRTCTX_AAPL_SERVER_CAPS) {
2875 if ((client_caps & SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR) &&
2876 (handle->conn->tcon->compat->fs_capabilities & FILE_NAMED_STREAMS)) {
2877 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR;
2878 config->readdir_attr_enabled = true;
2881 if (config->use_copyfile) {
2882 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_OSX_COPYFILE;
2883 config->copyfile_enabled = true;
2887 * The client doesn't set the flag, so we can't check
2888 * for it and just set it unconditionally
2890 if (config->unix_info_enabled) {
2891 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_NFS_ACE;
2894 SBVAL(p, 0, server_caps);
2895 ok = data_blob_append(req, &blob, p, 8);
2896 if (!ok) {
2897 return NT_STATUS_UNSUCCESSFUL;
2901 if (req_bitmap & SMB2_CRTCTX_AAPL_VOLUME_CAPS) {
2902 int val = lp_case_sensitive(SNUM(handle->conn->tcon->compat));
2903 uint64_t caps = 0;
2905 switch (val) {
2906 case Auto:
2907 break;
2909 case True:
2910 caps |= SMB2_CRTCTX_AAPL_CASE_SENSITIVE;
2911 break;
2913 default:
2914 break;
2917 if (config->time_machine) {
2918 caps |= SMB2_CRTCTX_AAPL_FULL_SYNC;
2921 SBVAL(p, 0, caps);
2923 ok = data_blob_append(req, &blob, p, 8);
2924 if (!ok) {
2925 return NT_STATUS_UNSUCCESSFUL;
2929 if (req_bitmap & SMB2_CRTCTX_AAPL_MODEL_INFO) {
2930 ok = convert_string_talloc(req,
2931 CH_UNIX, CH_UTF16LE,
2932 config->model, strlen(config->model),
2933 &model, &modellen);
2934 if (!ok) {
2935 return NT_STATUS_UNSUCCESSFUL;
2938 SIVAL(p, 0, 0);
2939 SIVAL(p + 4, 0, modellen);
2940 ok = data_blob_append(req, &blob, p, 8);
2941 if (!ok) {
2942 talloc_free(model);
2943 return NT_STATUS_UNSUCCESSFUL;
2946 ok = data_blob_append(req, &blob, model, modellen);
2947 talloc_free(model);
2948 if (!ok) {
2949 return NT_STATUS_UNSUCCESSFUL;
2953 status = smb2_create_blob_add(out_context_blobs,
2954 out_context_blobs,
2955 SMB2_CREATE_TAG_AAPL,
2956 blob);
2957 if (NT_STATUS_IS_OK(status)) {
2958 global_fruit_config.nego_aapl = true;
2959 if (config->aapl_zero_file_id) {
2960 aapl_force_zero_file_id(handle->conn->sconn);
2964 return status;
2967 static bool readdir_attr_meta_finderi_stream(
2968 struct vfs_handle_struct *handle,
2969 const struct smb_filename *smb_fname,
2970 AfpInfo *ai)
2972 struct smb_filename *stream_name = NULL;
2973 files_struct *fsp = NULL;
2974 ssize_t nread;
2975 NTSTATUS status;
2976 int ret;
2977 bool ok;
2978 uint8_t buf[AFP_INFO_SIZE];
2980 stream_name = synthetic_smb_fname(talloc_tos(),
2981 smb_fname->base_name,
2982 AFPINFO_STREAM_NAME,
2983 NULL, smb_fname->flags);
2984 if (stream_name == NULL) {
2985 return false;
2988 ret = SMB_VFS_STAT(handle->conn, stream_name);
2989 if (ret != 0) {
2990 return false;
2993 status = SMB_VFS_CREATE_FILE(
2994 handle->conn, /* conn */
2995 NULL, /* req */
2996 0, /* root_dir_fid */
2997 stream_name, /* fname */
2998 FILE_READ_DATA, /* access_mask */
2999 (FILE_SHARE_READ | FILE_SHARE_WRITE | /* share_access */
3000 FILE_SHARE_DELETE),
3001 FILE_OPEN, /* create_disposition*/
3002 0, /* create_options */
3003 0, /* file_attributes */
3004 INTERNAL_OPEN_ONLY, /* oplock_request */
3005 NULL, /* lease */
3006 0, /* allocation_size */
3007 0, /* private_flags */
3008 NULL, /* sd */
3009 NULL, /* ea_list */
3010 &fsp, /* result */
3011 NULL, /* pinfo */
3012 NULL, NULL); /* create context */
3014 TALLOC_FREE(stream_name);
3016 if (!NT_STATUS_IS_OK(status)) {
3017 return false;
3020 nread = SMB_VFS_PREAD(fsp, &buf[0], AFP_INFO_SIZE, 0);
3021 if (nread != AFP_INFO_SIZE) {
3022 DBG_ERR("short read [%s] [%zd/%d]\n",
3023 smb_fname_str_dbg(stream_name), nread, AFP_INFO_SIZE);
3024 ok = false;
3025 goto fail;
3028 memcpy(&ai->afpi_FinderInfo[0], &buf[AFP_OFF_FinderInfo],
3029 AFP_FinderSize);
3031 ok = true;
3033 fail:
3034 if (fsp != NULL) {
3035 close_file(NULL, fsp, NORMAL_CLOSE);
3038 return ok;
3041 static bool readdir_attr_meta_finderi_netatalk(
3042 struct vfs_handle_struct *handle,
3043 const struct smb_filename *smb_fname,
3044 AfpInfo *ai)
3046 struct adouble *ad = NULL;
3047 char *p = NULL;
3049 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
3050 if (ad == NULL) {
3051 return false;
3054 p = ad_get_entry(ad, ADEID_FINDERI);
3055 if (p == NULL) {
3056 DBG_ERR("No ADEID_FINDERI for [%s]\n", smb_fname->base_name);
3057 TALLOC_FREE(ad);
3058 return false;
3061 memcpy(&ai->afpi_FinderInfo[0], p, AFP_FinderSize);
3062 TALLOC_FREE(ad);
3063 return true;
3066 static bool readdir_attr_meta_finderi(struct vfs_handle_struct *handle,
3067 const struct smb_filename *smb_fname,
3068 struct readdir_attr_data *attr_data)
3070 struct fruit_config_data *config = NULL;
3071 uint32_t date_added;
3072 AfpInfo ai = {0};
3073 bool ok;
3075 SMB_VFS_HANDLE_GET_DATA(handle, config,
3076 struct fruit_config_data,
3077 return false);
3079 switch (config->meta) {
3080 case FRUIT_META_NETATALK:
3081 ok = readdir_attr_meta_finderi_netatalk(
3082 handle, smb_fname, &ai);
3083 break;
3085 case FRUIT_META_STREAM:
3086 ok = readdir_attr_meta_finderi_stream(
3087 handle, smb_fname, &ai);
3088 break;
3090 default:
3091 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3092 return false;
3095 if (!ok) {
3096 /* Don't bother with errors, it's likely ENOENT */
3097 return true;
3100 if (S_ISREG(smb_fname->st.st_ex_mode)) {
3101 /* finder_type */
3102 memcpy(&attr_data->attr_data.aapl.finder_info[0],
3103 &ai.afpi_FinderInfo[0], 4);
3105 /* finder_creator */
3106 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 4,
3107 &ai.afpi_FinderInfo[4], 4);
3110 /* finder_flags */
3111 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 8,
3112 &ai.afpi_FinderInfo[8], 2);
3114 /* finder_ext_flags */
3115 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 10,
3116 &ai.afpi_FinderInfo[24], 2);
3118 /* creation date */
3119 date_added = convert_time_t_to_uint32_t(
3120 smb_fname->st.st_ex_btime.tv_sec - AD_DATE_DELTA);
3122 RSIVAL(&attr_data->attr_data.aapl.finder_info[0], 12, date_added);
3124 return true;
3127 static uint64_t readdir_attr_rfork_size_adouble(
3128 struct vfs_handle_struct *handle,
3129 const struct smb_filename *smb_fname)
3131 struct adouble *ad = NULL;
3132 uint64_t rfork_size;
3134 ad = ad_get(talloc_tos(), handle, smb_fname,
3135 ADOUBLE_RSRC);
3136 if (ad == NULL) {
3137 return 0;
3140 rfork_size = ad_getentrylen(ad, ADEID_RFORK);
3141 TALLOC_FREE(ad);
3143 return rfork_size;
3146 static uint64_t readdir_attr_rfork_size_stream(
3147 struct vfs_handle_struct *handle,
3148 const struct smb_filename *smb_fname)
3150 struct smb_filename *stream_name = NULL;
3151 int ret;
3152 uint64_t rfork_size;
3154 stream_name = synthetic_smb_fname(talloc_tos(),
3155 smb_fname->base_name,
3156 AFPRESOURCE_STREAM_NAME,
3157 NULL, 0);
3158 if (stream_name == NULL) {
3159 return 0;
3162 ret = SMB_VFS_STAT(handle->conn, stream_name);
3163 if (ret != 0) {
3164 TALLOC_FREE(stream_name);
3165 return 0;
3168 rfork_size = stream_name->st.st_ex_size;
3169 TALLOC_FREE(stream_name);
3171 return rfork_size;
3174 static uint64_t readdir_attr_rfork_size(struct vfs_handle_struct *handle,
3175 const struct smb_filename *smb_fname)
3177 struct fruit_config_data *config = NULL;
3178 uint64_t rfork_size;
3180 SMB_VFS_HANDLE_GET_DATA(handle, config,
3181 struct fruit_config_data,
3182 return 0);
3184 switch (config->rsrc) {
3185 case FRUIT_RSRC_ADFILE:
3186 case FRUIT_RSRC_XATTR:
3187 rfork_size = readdir_attr_rfork_size_adouble(handle,
3188 smb_fname);
3189 break;
3191 case FRUIT_META_STREAM:
3192 rfork_size = readdir_attr_rfork_size_stream(handle,
3193 smb_fname);
3194 break;
3196 default:
3197 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3198 rfork_size = 0;
3199 break;
3202 return rfork_size;
3205 static NTSTATUS readdir_attr_macmeta(struct vfs_handle_struct *handle,
3206 const struct smb_filename *smb_fname,
3207 struct readdir_attr_data *attr_data)
3209 NTSTATUS status = NT_STATUS_OK;
3210 struct fruit_config_data *config = NULL;
3211 bool ok;
3213 SMB_VFS_HANDLE_GET_DATA(handle, config,
3214 struct fruit_config_data,
3215 return NT_STATUS_UNSUCCESSFUL);
3218 /* Ensure we return a default value in the creation_date field */
3219 RSIVAL(&attr_data->attr_data.aapl.finder_info, 12, AD_DATE_START);
3222 * Resource fork length
3225 if (config->readdir_attr_rsize) {
3226 uint64_t rfork_size;
3228 rfork_size = readdir_attr_rfork_size(handle, smb_fname);
3229 attr_data->attr_data.aapl.rfork_size = rfork_size;
3233 * FinderInfo
3236 if (config->readdir_attr_finder_info) {
3237 ok = readdir_attr_meta_finderi(handle, smb_fname, attr_data);
3238 if (!ok) {
3239 status = NT_STATUS_INTERNAL_ERROR;
3243 return status;
3246 static NTSTATUS remove_virtual_nfs_aces(struct security_descriptor *psd)
3248 NTSTATUS status;
3249 uint32_t i;
3251 if (psd->dacl == NULL) {
3252 return NT_STATUS_OK;
3255 for (i = 0; i < psd->dacl->num_aces; i++) {
3256 /* MS NFS style mode/uid/gid */
3257 int cmp = dom_sid_compare_domain(
3258 &global_sid_Unix_NFS,
3259 &psd->dacl->aces[i].trustee);
3260 if (cmp != 0) {
3261 /* Normal ACE entry. */
3262 continue;
3266 * security_descriptor_dacl_del()
3267 * *must* return NT_STATUS_OK as we know
3268 * we have something to remove.
3271 status = security_descriptor_dacl_del(psd,
3272 &psd->dacl->aces[i].trustee);
3273 if (!NT_STATUS_IS_OK(status)) {
3274 DBG_WARNING("failed to remove MS NFS style ACE: %s\n",
3275 nt_errstr(status));
3276 return status;
3280 * security_descriptor_dacl_del() may delete more
3281 * then one entry subsequent to this one if the
3282 * SID matches, but we only need to ensure that
3283 * we stay looking at the same element in the array.
3285 i--;
3287 return NT_STATUS_OK;
3290 /* Search MS NFS style ACE with UNIX mode */
3291 static NTSTATUS check_ms_nfs(vfs_handle_struct *handle,
3292 files_struct *fsp,
3293 struct security_descriptor *psd,
3294 mode_t *pmode,
3295 bool *pdo_chmod)
3297 uint32_t i;
3298 struct fruit_config_data *config = NULL;
3300 *pdo_chmod = false;
3302 SMB_VFS_HANDLE_GET_DATA(handle, config,
3303 struct fruit_config_data,
3304 return NT_STATUS_UNSUCCESSFUL);
3306 if (!global_fruit_config.nego_aapl) {
3307 return NT_STATUS_OK;
3309 if (psd->dacl == NULL || !config->unix_info_enabled) {
3310 return NT_STATUS_OK;
3313 for (i = 0; i < psd->dacl->num_aces; i++) {
3314 if (dom_sid_compare_domain(
3315 &global_sid_Unix_NFS_Mode,
3316 &psd->dacl->aces[i].trustee) == 0) {
3317 *pmode = (mode_t)psd->dacl->aces[i].trustee.sub_auths[2];
3318 *pmode &= (S_IRWXU | S_IRWXG | S_IRWXO);
3319 *pdo_chmod = true;
3321 DEBUG(10, ("MS NFS chmod request %s, %04o\n",
3322 fsp_str_dbg(fsp), (unsigned)(*pmode)));
3323 break;
3328 * Remove any incoming virtual ACE entries generated by
3329 * fruit_fget_nt_acl().
3332 return remove_virtual_nfs_aces(psd);
3335 /****************************************************************************
3336 * VFS ops
3337 ****************************************************************************/
3339 static int fruit_connect(vfs_handle_struct *handle,
3340 const char *service,
3341 const char *user)
3343 int rc;
3344 char *list = NULL, *newlist = NULL;
3345 struct fruit_config_data *config;
3347 DEBUG(10, ("fruit_connect\n"));
3349 rc = SMB_VFS_NEXT_CONNECT(handle, service, user);
3350 if (rc < 0) {
3351 return rc;
3354 rc = init_fruit_config(handle);
3355 if (rc != 0) {
3356 return rc;
3359 SMB_VFS_HANDLE_GET_DATA(handle, config,
3360 struct fruit_config_data, return -1);
3362 if (config->veto_appledouble) {
3363 list = lp_veto_files(talloc_tos(), SNUM(handle->conn));
3365 if (list) {
3366 if (strstr(list, "/" ADOUBLE_NAME_PREFIX "*/") == NULL) {
3367 newlist = talloc_asprintf(
3368 list,
3369 "%s/" ADOUBLE_NAME_PREFIX "*/",
3370 list);
3371 lp_do_parameter(SNUM(handle->conn),
3372 "veto files",
3373 newlist);
3375 } else {
3376 lp_do_parameter(SNUM(handle->conn),
3377 "veto files",
3378 "/" ADOUBLE_NAME_PREFIX "*/");
3381 TALLOC_FREE(list);
3384 if (config->encoding == FRUIT_ENC_NATIVE) {
3385 lp_do_parameter(SNUM(handle->conn),
3386 "catia:mappings",
3387 fruit_catia_maps);
3390 if (config->time_machine) {
3391 DBG_NOTICE("Enabling durable handles for Time Machine "
3392 "support on [%s]\n", service);
3393 lp_do_parameter(SNUM(handle->conn), "durable handles", "yes");
3394 lp_do_parameter(SNUM(handle->conn), "kernel oplocks", "no");
3395 lp_do_parameter(SNUM(handle->conn), "kernel share modes", "no");
3396 if (!lp_strict_sync(SNUM(handle->conn))) {
3397 DBG_WARNING("Time Machine without strict sync is not "
3398 "recommended!\n");
3400 lp_do_parameter(SNUM(handle->conn), "posix locking", "no");
3403 return rc;
3406 static int fruit_fake_fd(void)
3408 int pipe_fds[2];
3409 int fd;
3410 int ret;
3413 * Return a valid fd, but ensure any attempt to use it returns
3414 * an error (EPIPE). Once we get a write on the handle, we open
3415 * the real fd.
3417 ret = pipe(pipe_fds);
3418 if (ret != 0) {
3419 return -1;
3421 fd = pipe_fds[0];
3422 close(pipe_fds[1]);
3424 return fd;
3427 static int fruit_open_meta_stream(vfs_handle_struct *handle,
3428 struct smb_filename *smb_fname,
3429 files_struct *fsp,
3430 int flags,
3431 mode_t mode)
3433 struct fruit_config_data *config = NULL;
3434 struct fio *fio = NULL;
3435 int open_flags = flags & ~O_CREAT;
3436 int fd;
3438 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3440 SMB_VFS_HANDLE_GET_DATA(handle, config,
3441 struct fruit_config_data, return -1);
3443 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3444 fio->type = ADOUBLE_META;
3445 fio->config = config;
3447 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, open_flags, mode);
3448 if (fd != -1) {
3449 return fd;
3452 if (!(flags & O_CREAT)) {
3453 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
3454 return -1;
3457 fd = fruit_fake_fd();
3458 if (fd == -1) {
3459 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
3460 return -1;
3463 fio->fake_fd = true;
3464 fio->flags = flags;
3465 fio->mode = mode;
3467 return fd;
3470 static int fruit_open_meta_netatalk(vfs_handle_struct *handle,
3471 struct smb_filename *smb_fname,
3472 files_struct *fsp,
3473 int flags,
3474 mode_t mode)
3476 struct fruit_config_data *config = NULL;
3477 struct fio *fio = NULL;
3478 struct adouble *ad = NULL;
3479 bool meta_exists = false;
3480 int fd;
3482 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3484 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
3485 if (ad != NULL) {
3486 meta_exists = true;
3489 TALLOC_FREE(ad);
3491 if (!meta_exists && !(flags & O_CREAT)) {
3492 errno = ENOENT;
3493 return -1;
3496 fd = fruit_fake_fd();
3497 if (fd == -1) {
3498 return -1;
3501 SMB_VFS_HANDLE_GET_DATA(handle, config,
3502 struct fruit_config_data, return -1);
3504 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3505 fio->type = ADOUBLE_META;
3506 fio->config = config;
3507 fio->fake_fd = true;
3508 fio->flags = flags;
3509 fio->mode = mode;
3511 return fd;
3514 static int fruit_open_meta(vfs_handle_struct *handle,
3515 struct smb_filename *smb_fname,
3516 files_struct *fsp, int flags, mode_t mode)
3518 int fd;
3519 struct fruit_config_data *config = NULL;
3521 DBG_DEBUG("path [%s]\n", smb_fname_str_dbg(smb_fname));
3523 SMB_VFS_HANDLE_GET_DATA(handle, config,
3524 struct fruit_config_data, return -1);
3526 switch (config->meta) {
3527 case FRUIT_META_STREAM:
3528 fd = fruit_open_meta_stream(handle, smb_fname,
3529 fsp, flags, mode);
3530 break;
3532 case FRUIT_META_NETATALK:
3533 fd = fruit_open_meta_netatalk(handle, smb_fname,
3534 fsp, flags, mode);
3535 break;
3537 default:
3538 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3539 return -1;
3542 DBG_DEBUG("path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3544 return fd;
3547 static int fruit_open_rsrc_adouble(vfs_handle_struct *handle,
3548 struct smb_filename *smb_fname,
3549 files_struct *fsp,
3550 int flags,
3551 mode_t mode)
3553 int rc = 0;
3554 struct adouble *ad = NULL;
3555 struct smb_filename *smb_fname_base = NULL;
3556 struct fruit_config_data *config = NULL;
3557 int hostfd = -1;
3559 SMB_VFS_HANDLE_GET_DATA(handle, config,
3560 struct fruit_config_data, return -1);
3562 if ((!(flags & O_CREAT)) &&
3563 S_ISDIR(fsp->base_fsp->fsp_name->st.st_ex_mode))
3565 /* sorry, but directories don't habe a resource fork */
3566 rc = -1;
3567 goto exit;
3570 rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_base);
3571 if (rc != 0) {
3572 goto exit;
3575 /* We always need read/write access for the metadata header too */
3576 flags &= ~(O_RDONLY | O_WRONLY);
3577 flags |= O_RDWR;
3579 hostfd = SMB_VFS_NEXT_OPEN(handle, smb_fname_base, fsp,
3580 flags, mode);
3581 if (hostfd == -1) {
3582 rc = -1;
3583 goto exit;
3586 if (flags & (O_CREAT | O_TRUNC)) {
3587 ad = ad_init(fsp, handle, ADOUBLE_RSRC);
3588 if (ad == NULL) {
3589 rc = -1;
3590 goto exit;
3593 fsp->fh->fd = hostfd;
3595 rc = ad_fset(ad, fsp);
3596 fsp->fh->fd = -1;
3597 if (rc != 0) {
3598 rc = -1;
3599 goto exit;
3601 TALLOC_FREE(ad);
3604 exit:
3606 TALLOC_FREE(smb_fname_base);
3608 DEBUG(10, ("fruit_open resource fork: rc=%d, fd=%d\n", rc, hostfd));
3609 if (rc != 0) {
3610 int saved_errno = errno;
3611 if (hostfd >= 0) {
3613 * BUGBUGBUG -- we would need to call
3614 * fd_close_posix here, but we don't have a
3615 * full fsp yet
3617 fsp->fh->fd = hostfd;
3618 SMB_VFS_CLOSE(fsp);
3620 hostfd = -1;
3621 errno = saved_errno;
3623 return hostfd;
3626 static int fruit_open_rsrc_xattr(vfs_handle_struct *handle,
3627 struct smb_filename *smb_fname,
3628 files_struct *fsp,
3629 int flags,
3630 mode_t mode)
3632 #ifdef HAVE_ATTROPEN
3633 int fd = -1;
3635 fd = attropen(smb_fname->base_name,
3636 AFPRESOURCE_EA_NETATALK,
3637 flags,
3638 mode);
3639 if (fd == -1) {
3640 return -1;
3643 return fd;
3645 #else
3646 errno = ENOSYS;
3647 return -1;
3648 #endif
3651 static int fruit_open_rsrc(vfs_handle_struct *handle,
3652 struct smb_filename *smb_fname,
3653 files_struct *fsp, int flags, mode_t mode)
3655 int fd;
3656 struct fruit_config_data *config = NULL;
3657 struct fio *fio = NULL;
3659 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3661 SMB_VFS_HANDLE_GET_DATA(handle, config,
3662 struct fruit_config_data, return -1);
3664 switch (config->rsrc) {
3665 case FRUIT_RSRC_STREAM:
3666 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3667 break;
3669 case FRUIT_RSRC_ADFILE:
3670 fd = fruit_open_rsrc_adouble(handle, smb_fname,
3671 fsp, flags, mode);
3672 break;
3674 case FRUIT_RSRC_XATTR:
3675 fd = fruit_open_rsrc_xattr(handle, smb_fname,
3676 fsp, flags, mode);
3677 break;
3679 default:
3680 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3681 return -1;
3684 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3686 if (fd == -1) {
3687 return -1;
3690 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3691 fio->type = ADOUBLE_RSRC;
3692 fio->config = config;
3694 return fd;
3697 static int fruit_open(vfs_handle_struct *handle,
3698 struct smb_filename *smb_fname,
3699 files_struct *fsp, int flags, mode_t mode)
3701 int fd;
3703 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3705 if (!is_ntfs_stream_smb_fname(smb_fname)) {
3706 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3709 if (is_afpinfo_stream(smb_fname)) {
3710 fd = fruit_open_meta(handle, smb_fname, fsp, flags, mode);
3711 } else if (is_afpresource_stream(smb_fname)) {
3712 fd = fruit_open_rsrc(handle, smb_fname, fsp, flags, mode);
3713 } else {
3714 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3717 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3719 return fd;
3722 static int fruit_rename(struct vfs_handle_struct *handle,
3723 const struct smb_filename *smb_fname_src,
3724 const struct smb_filename *smb_fname_dst)
3726 int rc = -1;
3727 struct fruit_config_data *config = NULL;
3728 struct smb_filename *src_adp_smb_fname = NULL;
3729 struct smb_filename *dst_adp_smb_fname = NULL;
3731 SMB_VFS_HANDLE_GET_DATA(handle, config,
3732 struct fruit_config_data, return -1);
3734 if (!VALID_STAT(smb_fname_src->st)) {
3735 DBG_ERR("Need valid stat for [%s]\n",
3736 smb_fname_str_dbg(smb_fname_src));
3737 return -1;
3740 rc = SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
3741 if (rc != 0) {
3742 return -1;
3745 if ((config->rsrc != FRUIT_RSRC_ADFILE) ||
3746 (!S_ISREG(smb_fname_src->st.st_ex_mode)))
3748 return 0;
3751 rc = adouble_path(talloc_tos(), smb_fname_src, &src_adp_smb_fname);
3752 if (rc != 0) {
3753 goto done;
3756 rc = adouble_path(talloc_tos(), smb_fname_dst, &dst_adp_smb_fname);
3757 if (rc != 0) {
3758 goto done;
3761 DBG_DEBUG("%s -> %s\n",
3762 smb_fname_str_dbg(src_adp_smb_fname),
3763 smb_fname_str_dbg(dst_adp_smb_fname));
3765 rc = SMB_VFS_NEXT_RENAME(handle, src_adp_smb_fname, dst_adp_smb_fname);
3766 if (errno == ENOENT) {
3767 rc = 0;
3770 done:
3771 TALLOC_FREE(src_adp_smb_fname);
3772 TALLOC_FREE(dst_adp_smb_fname);
3773 return rc;
3776 static int fruit_unlink_meta_stream(vfs_handle_struct *handle,
3777 const struct smb_filename *smb_fname)
3779 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3782 static int fruit_unlink_meta_netatalk(vfs_handle_struct *handle,
3783 const struct smb_filename *smb_fname)
3785 return SMB_VFS_REMOVEXATTR(handle->conn,
3786 smb_fname,
3787 AFPINFO_EA_NETATALK);
3790 static int fruit_unlink_meta(vfs_handle_struct *handle,
3791 const struct smb_filename *smb_fname)
3793 struct fruit_config_data *config = NULL;
3794 int rc;
3796 SMB_VFS_HANDLE_GET_DATA(handle, config,
3797 struct fruit_config_data, return -1);
3799 switch (config->meta) {
3800 case FRUIT_META_STREAM:
3801 rc = fruit_unlink_meta_stream(handle, smb_fname);
3802 break;
3804 case FRUIT_META_NETATALK:
3805 rc = fruit_unlink_meta_netatalk(handle, smb_fname);
3806 break;
3808 default:
3809 DBG_ERR("Unsupported meta config [%d]\n", config->meta);
3810 return -1;
3813 return rc;
3816 static int fruit_unlink_rsrc_stream(vfs_handle_struct *handle,
3817 const struct smb_filename *smb_fname,
3818 bool force_unlink)
3820 int ret;
3822 if (!force_unlink) {
3823 struct smb_filename *smb_fname_cp = NULL;
3824 off_t size;
3826 smb_fname_cp = cp_smb_filename(talloc_tos(), smb_fname);
3827 if (smb_fname_cp == NULL) {
3828 return -1;
3832 * 0 byte resource fork streams are not listed by
3833 * vfs_streaminfo, as a result stream cleanup/deletion of file
3834 * deletion doesn't remove the resourcefork stream.
3837 ret = SMB_VFS_NEXT_STAT(handle, smb_fname_cp);
3838 if (ret != 0) {
3839 TALLOC_FREE(smb_fname_cp);
3840 DBG_ERR("stat [%s] failed [%s]\n",
3841 smb_fname_str_dbg(smb_fname_cp), strerror(errno));
3842 return -1;
3845 size = smb_fname_cp->st.st_ex_size;
3846 TALLOC_FREE(smb_fname_cp);
3848 if (size > 0) {
3849 /* OS X ignores resource fork stream delete requests */
3850 return 0;
3854 ret = SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3855 if ((ret != 0) && (errno == ENOENT) && force_unlink) {
3856 ret = 0;
3859 return ret;
3862 static int fruit_unlink_rsrc_adouble(vfs_handle_struct *handle,
3863 const struct smb_filename *smb_fname,
3864 bool force_unlink)
3866 int rc;
3867 struct adouble *ad = NULL;
3868 struct smb_filename *adp_smb_fname = NULL;
3870 if (!force_unlink) {
3871 ad = ad_get(talloc_tos(), handle, smb_fname,
3872 ADOUBLE_RSRC);
3873 if (ad == NULL) {
3874 errno = ENOENT;
3875 return -1;
3880 * 0 byte resource fork streams are not listed by
3881 * vfs_streaminfo, as a result stream cleanup/deletion of file
3882 * deletion doesn't remove the resourcefork stream.
3885 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
3886 /* OS X ignores resource fork stream delete requests */
3887 TALLOC_FREE(ad);
3888 return 0;
3891 TALLOC_FREE(ad);
3894 rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
3895 if (rc != 0) {
3896 return -1;
3899 rc = SMB_VFS_NEXT_UNLINK(handle, adp_smb_fname);
3900 TALLOC_FREE(adp_smb_fname);
3901 if ((rc != 0) && (errno == ENOENT) && force_unlink) {
3902 rc = 0;
3905 return rc;
3908 static int fruit_unlink_rsrc_xattr(vfs_handle_struct *handle,
3909 const struct smb_filename *smb_fname,
3910 bool force_unlink)
3913 * OS X ignores resource fork stream delete requests, so nothing to do
3914 * here. Removing the file will remove the xattr anyway, so we don't
3915 * have to take care of removing 0 byte resource forks that could be
3916 * left behind.
3918 return 0;
3921 static int fruit_unlink_rsrc(vfs_handle_struct *handle,
3922 const struct smb_filename *smb_fname,
3923 bool force_unlink)
3925 struct fruit_config_data *config = NULL;
3926 int rc;
3928 SMB_VFS_HANDLE_GET_DATA(handle, config,
3929 struct fruit_config_data, return -1);
3931 switch (config->rsrc) {
3932 case FRUIT_RSRC_STREAM:
3933 rc = fruit_unlink_rsrc_stream(handle, smb_fname, force_unlink);
3934 break;
3936 case FRUIT_RSRC_ADFILE:
3937 rc = fruit_unlink_rsrc_adouble(handle, smb_fname, force_unlink);
3938 break;
3940 case FRUIT_RSRC_XATTR:
3941 rc = fruit_unlink_rsrc_xattr(handle, smb_fname, force_unlink);
3942 break;
3944 default:
3945 DBG_ERR("Unsupported rsrc config [%d]\n", config->rsrc);
3946 return -1;
3949 return rc;
3952 static int fruit_unlink(vfs_handle_struct *handle,
3953 const struct smb_filename *smb_fname)
3955 int rc;
3956 struct fruit_config_data *config = NULL;
3957 struct smb_filename *rsrc_smb_fname = NULL;
3959 SMB_VFS_HANDLE_GET_DATA(handle, config,
3960 struct fruit_config_data, return -1);
3962 if (is_afpinfo_stream(smb_fname)) {
3963 return fruit_unlink_meta(handle, smb_fname);
3964 } else if (is_afpresource_stream(smb_fname)) {
3965 return fruit_unlink_rsrc(handle, smb_fname, false);
3966 } if (is_ntfs_stream_smb_fname(smb_fname)) {
3967 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3971 * A request to delete the base file. Because 0 byte resource
3972 * fork streams are not listed by fruit_streaminfo,
3973 * delete_all_streams() can't remove 0 byte resource fork
3974 * streams, so we have to cleanup this here.
3976 rsrc_smb_fname = synthetic_smb_fname(talloc_tos(),
3977 smb_fname->base_name,
3978 AFPRESOURCE_STREAM_NAME,
3979 NULL,
3980 smb_fname->flags);
3981 if (rsrc_smb_fname == NULL) {
3982 return -1;
3985 rc = fruit_unlink_rsrc(handle, rsrc_smb_fname, true);
3986 if ((rc != 0) && (errno != ENOENT)) {
3987 DBG_ERR("Forced unlink of [%s] failed [%s]\n",
3988 smb_fname_str_dbg(rsrc_smb_fname), strerror(errno));
3989 TALLOC_FREE(rsrc_smb_fname);
3990 return -1;
3992 TALLOC_FREE(rsrc_smb_fname);
3994 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3997 static int fruit_chmod(vfs_handle_struct *handle,
3998 const struct smb_filename *smb_fname,
3999 mode_t mode)
4001 int rc = -1;
4002 struct fruit_config_data *config = NULL;
4003 struct smb_filename *smb_fname_adp = NULL;
4005 rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname, mode);
4006 if (rc != 0) {
4007 return rc;
4010 SMB_VFS_HANDLE_GET_DATA(handle, config,
4011 struct fruit_config_data, return -1);
4013 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4014 return 0;
4017 if (!VALID_STAT(smb_fname->st)) {
4018 return 0;
4021 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
4022 return 0;
4025 rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_adp);
4026 if (rc != 0) {
4027 return -1;
4030 DEBUG(10, ("fruit_chmod: %s\n", smb_fname_adp->base_name));
4032 rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname_adp, mode);
4033 if (errno == ENOENT) {
4034 rc = 0;
4037 TALLOC_FREE(smb_fname_adp);
4038 return rc;
4041 static int fruit_chown(vfs_handle_struct *handle,
4042 const struct smb_filename *smb_fname,
4043 uid_t uid,
4044 gid_t gid)
4046 int rc = -1;
4047 struct fruit_config_data *config = NULL;
4048 struct smb_filename *adp_smb_fname = NULL;
4050 rc = SMB_VFS_NEXT_CHOWN(handle, smb_fname, uid, gid);
4051 if (rc != 0) {
4052 return rc;
4055 SMB_VFS_HANDLE_GET_DATA(handle, config,
4056 struct fruit_config_data, return -1);
4058 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4059 return 0;
4062 if (!VALID_STAT(smb_fname->st)) {
4063 return 0;
4066 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
4067 return 0;
4070 rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
4071 if (rc != 0) {
4072 goto done;
4075 DEBUG(10, ("fruit_chown: %s\n", adp_smb_fname->base_name));
4077 rc = SMB_VFS_NEXT_CHOWN(handle, adp_smb_fname, uid, gid);
4078 if (errno == ENOENT) {
4079 rc = 0;
4082 done:
4083 TALLOC_FREE(adp_smb_fname);
4084 return rc;
4087 static int fruit_rmdir(struct vfs_handle_struct *handle,
4088 const struct smb_filename *smb_fname)
4090 DIR *dh = NULL;
4091 struct dirent *de;
4092 struct fruit_config_data *config;
4094 SMB_VFS_HANDLE_GET_DATA(handle, config,
4095 struct fruit_config_data, return -1);
4097 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4098 goto exit_rmdir;
4102 * Due to there is no way to change bDeleteVetoFiles variable
4103 * from this module, need to clean up ourselves
4106 dh = SMB_VFS_OPENDIR(handle->conn, smb_fname, NULL, 0);
4107 if (dh == NULL) {
4108 goto exit_rmdir;
4111 while ((de = SMB_VFS_READDIR(handle->conn, dh, NULL)) != NULL) {
4112 int match;
4113 struct adouble *ad = NULL;
4114 char *p = NULL;
4115 struct smb_filename *ad_smb_fname = NULL;
4116 int ret;
4118 match = strncmp(de->d_name,
4119 ADOUBLE_NAME_PREFIX,
4120 strlen(ADOUBLE_NAME_PREFIX));
4121 if (match != 0) {
4122 continue;
4125 p = talloc_asprintf(talloc_tos(), "%s/%s",
4126 smb_fname->base_name, de->d_name);
4127 if (p == NULL) {
4128 DBG_ERR("talloc_asprintf failed\n");
4129 return -1;
4132 ad_smb_fname = synthetic_smb_fname(talloc_tos(), p,
4133 NULL, NULL,
4134 smb_fname->flags);
4135 TALLOC_FREE(p);
4136 if (ad_smb_fname == NULL) {
4137 DBG_ERR("synthetic_smb_fname failed\n");
4138 return -1;
4142 * Check whether it's a valid AppleDouble file, if
4143 * yes, delete it, ignore it otherwise.
4145 ad = ad_get(talloc_tos(), handle, ad_smb_fname, ADOUBLE_RSRC);
4146 if (ad == NULL) {
4147 TALLOC_FREE(ad_smb_fname);
4148 TALLOC_FREE(p);
4149 continue;
4151 TALLOC_FREE(ad);
4153 ret = SMB_VFS_NEXT_UNLINK(handle, ad_smb_fname);
4154 if (ret != 0) {
4155 DBG_ERR("Deleting [%s] failed\n",
4156 smb_fname_str_dbg(ad_smb_fname));
4158 TALLOC_FREE(ad_smb_fname);
4161 exit_rmdir:
4162 if (dh) {
4163 SMB_VFS_CLOSEDIR(handle->conn, dh);
4165 return SMB_VFS_NEXT_RMDIR(handle, smb_fname);
4168 static ssize_t fruit_pread_meta_stream(vfs_handle_struct *handle,
4169 files_struct *fsp, void *data,
4170 size_t n, off_t offset)
4172 ssize_t nread;
4173 int ret;
4175 nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4176 if (nread == -1 || nread == n) {
4177 return nread;
4180 DBG_ERR("Removing [%s] after short read [%zd]\n",
4181 fsp_str_dbg(fsp), nread);
4183 ret = SMB_VFS_NEXT_UNLINK(handle, fsp->fsp_name);
4184 if (ret != 0) {
4185 DBG_ERR("Removing [%s] failed\n", fsp_str_dbg(fsp));
4186 return -1;
4189 errno = EINVAL;
4190 return -1;
4193 static ssize_t fruit_pread_meta_adouble(vfs_handle_struct *handle,
4194 files_struct *fsp, void *data,
4195 size_t n, off_t offset)
4197 AfpInfo *ai = NULL;
4198 struct adouble *ad = NULL;
4199 char afpinfo_buf[AFP_INFO_SIZE];
4200 char *p = NULL;
4201 ssize_t nread;
4203 ai = afpinfo_new(talloc_tos());
4204 if (ai == NULL) {
4205 return -1;
4208 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
4209 if (ad == NULL) {
4210 nread = -1;
4211 goto fail;
4214 p = ad_get_entry(ad, ADEID_FINDERI);
4215 if (p == NULL) {
4216 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
4217 nread = -1;
4218 goto fail;
4221 memcpy(&ai->afpi_FinderInfo[0], p, ADEDLEN_FINDERI);
4223 nread = afpinfo_pack(ai, afpinfo_buf);
4224 if (nread != AFP_INFO_SIZE) {
4225 nread = -1;
4226 goto fail;
4229 memcpy(data, afpinfo_buf, n);
4230 nread = n;
4232 fail:
4233 TALLOC_FREE(ai);
4234 return nread;
4237 static ssize_t fruit_pread_meta(vfs_handle_struct *handle,
4238 files_struct *fsp, void *data,
4239 size_t n, off_t offset)
4241 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4242 ssize_t nread;
4243 ssize_t to_return;
4246 * OS X has a off-by-1 error in the offset calculation, so we're
4247 * bug compatible here. It won't hurt, as any relevant real
4248 * world read requests from the AFP_AfpInfo stream will be
4249 * offset=0 n=60. offset is ignored anyway, see below.
4251 if ((offset < 0) || (offset >= AFP_INFO_SIZE + 1)) {
4252 return 0;
4255 if (fio == NULL) {
4256 DBG_ERR("Failed to fetch fsp extension");
4257 return -1;
4260 /* Yes, macOS always reads from offset 0 */
4261 offset = 0;
4262 to_return = MIN(n, AFP_INFO_SIZE);
4264 switch (fio->config->meta) {
4265 case FRUIT_META_STREAM:
4266 nread = fruit_pread_meta_stream(handle, fsp, data,
4267 to_return, offset);
4268 break;
4270 case FRUIT_META_NETATALK:
4271 nread = fruit_pread_meta_adouble(handle, fsp, data,
4272 to_return, offset);
4273 break;
4275 default:
4276 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
4277 return -1;
4280 if (nread == -1 && fio->created) {
4281 AfpInfo *ai = NULL;
4282 char afpinfo_buf[AFP_INFO_SIZE];
4284 ai = afpinfo_new(talloc_tos());
4285 if (ai == NULL) {
4286 return -1;
4289 nread = afpinfo_pack(ai, afpinfo_buf);
4290 TALLOC_FREE(ai);
4291 if (nread != AFP_INFO_SIZE) {
4292 return -1;
4295 memcpy(data, afpinfo_buf, to_return);
4296 return to_return;
4299 return nread;
4302 static ssize_t fruit_pread_rsrc_stream(vfs_handle_struct *handle,
4303 files_struct *fsp, void *data,
4304 size_t n, off_t offset)
4306 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4309 static ssize_t fruit_pread_rsrc_xattr(vfs_handle_struct *handle,
4310 files_struct *fsp, void *data,
4311 size_t n, off_t offset)
4313 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4316 static ssize_t fruit_pread_rsrc_adouble(vfs_handle_struct *handle,
4317 files_struct *fsp, void *data,
4318 size_t n, off_t offset)
4320 struct adouble *ad = NULL;
4321 ssize_t nread;
4323 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
4324 if (ad == NULL) {
4325 return -1;
4328 nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n,
4329 offset + ad_getentryoff(ad, ADEID_RFORK));
4331 TALLOC_FREE(ad);
4332 return nread;
4335 static ssize_t fruit_pread_rsrc(vfs_handle_struct *handle,
4336 files_struct *fsp, void *data,
4337 size_t n, off_t offset)
4339 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4340 ssize_t nread;
4342 if (fio == NULL) {
4343 errno = EINVAL;
4344 return -1;
4347 switch (fio->config->rsrc) {
4348 case FRUIT_RSRC_STREAM:
4349 nread = fruit_pread_rsrc_stream(handle, fsp, data, n, offset);
4350 break;
4352 case FRUIT_RSRC_ADFILE:
4353 nread = fruit_pread_rsrc_adouble(handle, fsp, data, n, offset);
4354 break;
4356 case FRUIT_RSRC_XATTR:
4357 nread = fruit_pread_rsrc_xattr(handle, fsp, data, n, offset);
4358 break;
4360 default:
4361 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
4362 return -1;
4365 return nread;
4368 static ssize_t fruit_pread(vfs_handle_struct *handle,
4369 files_struct *fsp, void *data,
4370 size_t n, off_t offset)
4372 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4373 ssize_t nread;
4375 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4376 fsp_str_dbg(fsp), (intmax_t)offset, n);
4378 if (fio == NULL) {
4379 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4382 if (fio->type == ADOUBLE_META) {
4383 nread = fruit_pread_meta(handle, fsp, data, n, offset);
4384 } else {
4385 nread = fruit_pread_rsrc(handle, fsp, data, n, offset);
4388 DBG_DEBUG("Path [%s] nread [%zd]\n", fsp_str_dbg(fsp), nread);
4389 return nread;
4392 static bool fruit_must_handle_aio_stream(struct fio *fio)
4394 if (fio == NULL) {
4395 return false;
4398 if (fio->type == ADOUBLE_META) {
4399 return true;
4402 if ((fio->type == ADOUBLE_RSRC) &&
4403 (fio->config->rsrc == FRUIT_RSRC_ADFILE))
4405 return true;
4408 return false;
4411 struct fruit_pread_state {
4412 ssize_t nread;
4413 struct vfs_aio_state vfs_aio_state;
4416 static void fruit_pread_done(struct tevent_req *subreq);
4418 static struct tevent_req *fruit_pread_send(
4419 struct vfs_handle_struct *handle,
4420 TALLOC_CTX *mem_ctx,
4421 struct tevent_context *ev,
4422 struct files_struct *fsp,
4423 void *data,
4424 size_t n, off_t offset)
4426 struct tevent_req *req = NULL;
4427 struct tevent_req *subreq = NULL;
4428 struct fruit_pread_state *state = NULL;
4429 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4431 req = tevent_req_create(mem_ctx, &state,
4432 struct fruit_pread_state);
4433 if (req == NULL) {
4434 return NULL;
4437 if (fruit_must_handle_aio_stream(fio)) {
4438 state->nread = SMB_VFS_PREAD(fsp, data, n, offset);
4439 if (state->nread != n) {
4440 if (state->nread != -1) {
4441 errno = EIO;
4443 tevent_req_error(req, errno);
4444 return tevent_req_post(req, ev);
4446 tevent_req_done(req);
4447 return tevent_req_post(req, ev);
4450 subreq = SMB_VFS_NEXT_PREAD_SEND(state, ev, handle, fsp,
4451 data, n, offset);
4452 if (tevent_req_nomem(req, subreq)) {
4453 return tevent_req_post(req, ev);
4455 tevent_req_set_callback(subreq, fruit_pread_done, req);
4456 return req;
4459 static void fruit_pread_done(struct tevent_req *subreq)
4461 struct tevent_req *req = tevent_req_callback_data(
4462 subreq, struct tevent_req);
4463 struct fruit_pread_state *state = tevent_req_data(
4464 req, struct fruit_pread_state);
4466 state->nread = SMB_VFS_PREAD_RECV(subreq, &state->vfs_aio_state);
4467 TALLOC_FREE(subreq);
4469 if (tevent_req_error(req, state->vfs_aio_state.error)) {
4470 return;
4472 tevent_req_done(req);
4475 static ssize_t fruit_pread_recv(struct tevent_req *req,
4476 struct vfs_aio_state *vfs_aio_state)
4478 struct fruit_pread_state *state = tevent_req_data(
4479 req, struct fruit_pread_state);
4481 if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
4482 return -1;
4485 *vfs_aio_state = state->vfs_aio_state;
4486 return state->nread;
4489 static ssize_t fruit_pwrite_meta_stream(vfs_handle_struct *handle,
4490 files_struct *fsp, const void *data,
4491 size_t n, off_t offset)
4493 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4494 AfpInfo *ai = NULL;
4495 size_t nwritten;
4496 int ret;
4497 bool ok;
4499 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4500 fsp_str_dbg(fsp), (intmax_t)offset, n);
4502 if (fio == NULL) {
4503 return -1;
4506 if (fio->fake_fd) {
4507 int fd;
4509 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
4510 if (ret != 0) {
4511 DBG_ERR("Close [%s] failed: %s\n",
4512 fsp_str_dbg(fsp), strerror(errno));
4513 fsp->fh->fd = -1;
4514 return -1;
4517 fd = SMB_VFS_NEXT_OPEN(handle,
4518 fsp->fsp_name,
4519 fsp,
4520 fio->flags,
4521 fio->mode);
4522 if (fd == -1) {
4523 DBG_ERR("On-demand create [%s] in write failed: %s\n",
4524 fsp_str_dbg(fsp), strerror(errno));
4525 return -1;
4527 fsp->fh->fd = fd;
4528 fio->fake_fd = false;
4531 ai = afpinfo_unpack(talloc_tos(), data);
4532 if (ai == NULL) {
4533 return -1;
4536 if (ai_empty_finderinfo(ai)) {
4538 * Writing an all 0 blob to the metadata stream results in the
4539 * stream being removed on a macOS server. This ensures we
4540 * behave the same and it verified by the "delete AFP_AfpInfo by
4541 * writing all 0" test.
4543 ret = SMB_VFS_NEXT_FTRUNCATE(handle, fsp, 0);
4544 if (ret != 0) {
4545 DBG_ERR("SMB_VFS_NEXT_FTRUNCATE on [%s] failed\n",
4546 fsp_str_dbg(fsp));
4547 return -1;
4550 ok = set_delete_on_close(
4551 fsp,
4552 true,
4553 handle->conn->session_info->security_token,
4554 handle->conn->session_info->unix_token);
4555 if (!ok) {
4556 DBG_ERR("set_delete_on_close on [%s] failed\n",
4557 fsp_str_dbg(fsp));
4558 return -1;
4560 return n;
4563 nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4564 if (nwritten != n) {
4565 return -1;
4568 return n;
4571 static ssize_t fruit_pwrite_meta_netatalk(vfs_handle_struct *handle,
4572 files_struct *fsp, const void *data,
4573 size_t n, off_t offset)
4575 struct adouble *ad = NULL;
4576 AfpInfo *ai = NULL;
4577 char *p = NULL;
4578 int ret;
4579 bool ok;
4581 ai = afpinfo_unpack(talloc_tos(), data);
4582 if (ai == NULL) {
4583 return -1;
4586 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
4587 if (ad == NULL) {
4588 ad = ad_init(talloc_tos(), handle, ADOUBLE_META);
4589 if (ad == NULL) {
4590 return -1;
4593 p = ad_get_entry(ad, ADEID_FINDERI);
4594 if (p == NULL) {
4595 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
4596 TALLOC_FREE(ad);
4597 return -1;
4600 memcpy(p, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
4602 ret = ad_fset(ad, fsp);
4603 if (ret != 0) {
4604 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
4605 TALLOC_FREE(ad);
4606 return -1;
4609 TALLOC_FREE(ad);
4611 if (!ai_empty_finderinfo(ai)) {
4612 return n;
4616 * Writing an all 0 blob to the metadata stream results in the stream
4617 * being removed on a macOS server. This ensures we behave the same and
4618 * it verified by the "delete AFP_AfpInfo by writing all 0" test.
4621 ok = set_delete_on_close(
4622 fsp,
4623 true,
4624 handle->conn->session_info->security_token,
4625 handle->conn->session_info->unix_token);
4626 if (!ok) {
4627 DBG_ERR("set_delete_on_close on [%s] failed\n",
4628 fsp_str_dbg(fsp));
4629 return -1;
4632 return n;
4635 static ssize_t fruit_pwrite_meta(vfs_handle_struct *handle,
4636 files_struct *fsp, const void *data,
4637 size_t n, off_t offset)
4639 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4640 ssize_t nwritten;
4641 uint8_t buf[AFP_INFO_SIZE];
4642 size_t to_write;
4643 size_t to_copy;
4644 int cmp;
4646 if (fio == NULL) {
4647 DBG_ERR("Failed to fetch fsp extension");
4648 return -1;
4651 if (n < 3) {
4652 errno = EINVAL;
4653 return -1;
4656 if (offset != 0 && n < 60) {
4657 errno = EINVAL;
4658 return -1;
4661 cmp = memcmp(data, "AFP", 3);
4662 if (cmp != 0) {
4663 errno = EINVAL;
4664 return -1;
4667 if (n <= AFP_OFF_FinderInfo) {
4669 * Nothing to do here really, just return
4671 return n;
4674 offset = 0;
4676 to_copy = n;
4677 if (to_copy > AFP_INFO_SIZE) {
4678 to_copy = AFP_INFO_SIZE;
4680 memcpy(buf, data, to_copy);
4682 to_write = n;
4683 if (to_write != AFP_INFO_SIZE) {
4684 to_write = AFP_INFO_SIZE;
4687 switch (fio->config->meta) {
4688 case FRUIT_META_STREAM:
4689 nwritten = fruit_pwrite_meta_stream(handle,
4690 fsp,
4691 buf,
4692 to_write,
4693 offset);
4694 break;
4696 case FRUIT_META_NETATALK:
4697 nwritten = fruit_pwrite_meta_netatalk(handle,
4698 fsp,
4699 buf,
4700 to_write,
4701 offset);
4702 break;
4704 default:
4705 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
4706 return -1;
4709 if (nwritten != to_write) {
4710 return -1;
4714 * Return the requested amount, verified against macOS SMB server
4716 return n;
4719 static ssize_t fruit_pwrite_rsrc_stream(vfs_handle_struct *handle,
4720 files_struct *fsp, const void *data,
4721 size_t n, off_t offset)
4723 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4726 static ssize_t fruit_pwrite_rsrc_xattr(vfs_handle_struct *handle,
4727 files_struct *fsp, const void *data,
4728 size_t n, off_t offset)
4730 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4733 static ssize_t fruit_pwrite_rsrc_adouble(vfs_handle_struct *handle,
4734 files_struct *fsp, const void *data,
4735 size_t n, off_t offset)
4737 struct adouble *ad = NULL;
4738 ssize_t nwritten;
4739 int ret;
4741 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
4742 if (ad == NULL) {
4743 DBG_ERR("ad_get [%s] failed\n", fsp_str_dbg(fsp));
4744 return -1;
4747 nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n,
4748 offset + ad_getentryoff(ad, ADEID_RFORK));
4749 if (nwritten != n) {
4750 DBG_ERR("Short write on [%s] [%zd/%zd]\n",
4751 fsp_str_dbg(fsp), nwritten, n);
4752 TALLOC_FREE(ad);
4753 return -1;
4756 if ((n + offset) > ad_getentrylen(ad, ADEID_RFORK)) {
4757 ad_setentrylen(ad, ADEID_RFORK, n + offset);
4758 ret = ad_fset(ad, fsp);
4759 if (ret != 0) {
4760 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
4761 TALLOC_FREE(ad);
4762 return -1;
4766 TALLOC_FREE(ad);
4767 return n;
4770 static ssize_t fruit_pwrite_rsrc(vfs_handle_struct *handle,
4771 files_struct *fsp, const void *data,
4772 size_t n, off_t offset)
4774 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4775 ssize_t nwritten;
4777 if (fio == NULL) {
4778 DBG_ERR("Failed to fetch fsp extension");
4779 return -1;
4782 switch (fio->config->rsrc) {
4783 case FRUIT_RSRC_STREAM:
4784 nwritten = fruit_pwrite_rsrc_stream(handle, fsp, data, n, offset);
4785 break;
4787 case FRUIT_RSRC_ADFILE:
4788 nwritten = fruit_pwrite_rsrc_adouble(handle, fsp, data, n, offset);
4789 break;
4791 case FRUIT_RSRC_XATTR:
4792 nwritten = fruit_pwrite_rsrc_xattr(handle, fsp, data, n, offset);
4793 break;
4795 default:
4796 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
4797 return -1;
4800 return nwritten;
4803 static ssize_t fruit_pwrite(vfs_handle_struct *handle,
4804 files_struct *fsp, const void *data,
4805 size_t n, off_t offset)
4807 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4808 ssize_t nwritten;
4810 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4811 fsp_str_dbg(fsp), (intmax_t)offset, n);
4813 if (fio == NULL) {
4814 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4817 if (fio->type == ADOUBLE_META) {
4818 nwritten = fruit_pwrite_meta(handle, fsp, data, n, offset);
4819 } else {
4820 nwritten = fruit_pwrite_rsrc(handle, fsp, data, n, offset);
4823 DBG_DEBUG("Path [%s] nwritten=%zd\n", fsp_str_dbg(fsp), nwritten);
4824 return nwritten;
4827 struct fruit_pwrite_state {
4828 ssize_t nwritten;
4829 struct vfs_aio_state vfs_aio_state;
4832 static void fruit_pwrite_done(struct tevent_req *subreq);
4834 static struct tevent_req *fruit_pwrite_send(
4835 struct vfs_handle_struct *handle,
4836 TALLOC_CTX *mem_ctx,
4837 struct tevent_context *ev,
4838 struct files_struct *fsp,
4839 const void *data,
4840 size_t n, off_t offset)
4842 struct tevent_req *req = NULL;
4843 struct tevent_req *subreq = NULL;
4844 struct fruit_pwrite_state *state = NULL;
4845 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4847 req = tevent_req_create(mem_ctx, &state,
4848 struct fruit_pwrite_state);
4849 if (req == NULL) {
4850 return NULL;
4853 if (fruit_must_handle_aio_stream(fio)) {
4854 state->nwritten = SMB_VFS_PWRITE(fsp, data, n, offset);
4855 if (state->nwritten != n) {
4856 if (state->nwritten != -1) {
4857 errno = EIO;
4859 tevent_req_error(req, errno);
4860 return tevent_req_post(req, ev);
4862 tevent_req_done(req);
4863 return tevent_req_post(req, ev);
4866 subreq = SMB_VFS_NEXT_PWRITE_SEND(state, ev, handle, fsp,
4867 data, n, offset);
4868 if (tevent_req_nomem(req, subreq)) {
4869 return tevent_req_post(req, ev);
4871 tevent_req_set_callback(subreq, fruit_pwrite_done, req);
4872 return req;
4875 static void fruit_pwrite_done(struct tevent_req *subreq)
4877 struct tevent_req *req = tevent_req_callback_data(
4878 subreq, struct tevent_req);
4879 struct fruit_pwrite_state *state = tevent_req_data(
4880 req, struct fruit_pwrite_state);
4882 state->nwritten = SMB_VFS_PWRITE_RECV(subreq, &state->vfs_aio_state);
4883 TALLOC_FREE(subreq);
4885 if (tevent_req_error(req, state->vfs_aio_state.error)) {
4886 return;
4888 tevent_req_done(req);
4891 static ssize_t fruit_pwrite_recv(struct tevent_req *req,
4892 struct vfs_aio_state *vfs_aio_state)
4894 struct fruit_pwrite_state *state = tevent_req_data(
4895 req, struct fruit_pwrite_state);
4897 if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
4898 return -1;
4901 *vfs_aio_state = state->vfs_aio_state;
4902 return state->nwritten;
4906 * Helper to stat/lstat the base file of an smb_fname.
4908 static int fruit_stat_base(vfs_handle_struct *handle,
4909 struct smb_filename *smb_fname,
4910 bool follow_links)
4912 char *tmp_stream_name;
4913 int rc;
4915 tmp_stream_name = smb_fname->stream_name;
4916 smb_fname->stream_name = NULL;
4917 if (follow_links) {
4918 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
4919 } else {
4920 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
4922 smb_fname->stream_name = tmp_stream_name;
4924 DBG_DEBUG("fruit_stat_base [%s] dev [%ju] ino [%ju]\n",
4925 smb_fname->base_name,
4926 (uintmax_t)smb_fname->st.st_ex_dev,
4927 (uintmax_t)smb_fname->st.st_ex_ino);
4928 return rc;
4931 static int fruit_stat_meta_stream(vfs_handle_struct *handle,
4932 struct smb_filename *smb_fname,
4933 bool follow_links)
4935 int ret;
4936 ino_t ino;
4938 ret = fruit_stat_base(handle, smb_fname, false);
4939 if (ret != 0) {
4940 return -1;
4943 ino = fruit_inode(&smb_fname->st, smb_fname->stream_name);
4945 if (follow_links) {
4946 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
4947 } else {
4948 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
4951 smb_fname->st.st_ex_ino = ino;
4953 return ret;
4956 static int fruit_stat_meta_netatalk(vfs_handle_struct *handle,
4957 struct smb_filename *smb_fname,
4958 bool follow_links)
4960 struct adouble *ad = NULL;
4962 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
4963 if (ad == NULL) {
4964 DBG_INFO("fruit_stat_meta %s: %s\n",
4965 smb_fname_str_dbg(smb_fname), strerror(errno));
4966 errno = ENOENT;
4967 return -1;
4969 TALLOC_FREE(ad);
4971 /* Populate the stat struct with info from the base file. */
4972 if (fruit_stat_base(handle, smb_fname, follow_links) == -1) {
4973 return -1;
4975 smb_fname->st.st_ex_size = AFP_INFO_SIZE;
4976 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
4977 smb_fname->stream_name);
4978 return 0;
4981 static int fruit_stat_meta(vfs_handle_struct *handle,
4982 struct smb_filename *smb_fname,
4983 bool follow_links)
4985 struct fruit_config_data *config = NULL;
4986 int ret;
4988 SMB_VFS_HANDLE_GET_DATA(handle, config,
4989 struct fruit_config_data, return -1);
4991 switch (config->meta) {
4992 case FRUIT_META_STREAM:
4993 ret = fruit_stat_meta_stream(handle, smb_fname, follow_links);
4994 break;
4996 case FRUIT_META_NETATALK:
4997 ret = fruit_stat_meta_netatalk(handle, smb_fname, follow_links);
4998 break;
5000 default:
5001 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
5002 return -1;
5005 return ret;
5008 static int fruit_stat_rsrc_netatalk(vfs_handle_struct *handle,
5009 struct smb_filename *smb_fname,
5010 bool follow_links)
5012 struct adouble *ad = NULL;
5013 int ret;
5015 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
5016 if (ad == NULL) {
5017 errno = ENOENT;
5018 return -1;
5021 /* Populate the stat struct with info from the base file. */
5022 ret = fruit_stat_base(handle, smb_fname, follow_links);
5023 if (ret != 0) {
5024 TALLOC_FREE(ad);
5025 return -1;
5028 smb_fname->st.st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
5029 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5030 smb_fname->stream_name);
5031 TALLOC_FREE(ad);
5032 return 0;
5035 static int fruit_stat_rsrc_stream(vfs_handle_struct *handle,
5036 struct smb_filename *smb_fname,
5037 bool follow_links)
5039 int ret;
5041 if (follow_links) {
5042 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
5043 } else {
5044 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5047 return ret;
5050 static int fruit_stat_rsrc_xattr(vfs_handle_struct *handle,
5051 struct smb_filename *smb_fname,
5052 bool follow_links)
5054 #ifdef HAVE_ATTROPEN
5055 int ret;
5056 int fd = -1;
5058 /* Populate the stat struct with info from the base file. */
5059 ret = fruit_stat_base(handle, smb_fname, follow_links);
5060 if (ret != 0) {
5061 return -1;
5064 fd = attropen(smb_fname->base_name,
5065 AFPRESOURCE_EA_NETATALK,
5066 O_RDONLY);
5067 if (fd == -1) {
5068 return 0;
5071 ret = sys_fstat(fd, &smb_fname->st, false);
5072 if (ret != 0) {
5073 close(fd);
5074 DBG_ERR("fstat [%s:%s] failed\n", smb_fname->base_name,
5075 AFPRESOURCE_EA_NETATALK);
5076 return -1;
5078 close(fd);
5079 fd = -1;
5081 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5082 smb_fname->stream_name);
5084 return ret;
5086 #else
5087 errno = ENOSYS;
5088 return -1;
5089 #endif
5092 static int fruit_stat_rsrc(vfs_handle_struct *handle,
5093 struct smb_filename *smb_fname,
5094 bool follow_links)
5096 struct fruit_config_data *config = NULL;
5097 int ret;
5099 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
5101 SMB_VFS_HANDLE_GET_DATA(handle, config,
5102 struct fruit_config_data, return -1);
5104 switch (config->rsrc) {
5105 case FRUIT_RSRC_STREAM:
5106 ret = fruit_stat_rsrc_stream(handle, smb_fname, follow_links);
5107 break;
5109 case FRUIT_RSRC_XATTR:
5110 ret = fruit_stat_rsrc_xattr(handle, smb_fname, follow_links);
5111 break;
5113 case FRUIT_RSRC_ADFILE:
5114 ret = fruit_stat_rsrc_netatalk(handle, smb_fname, follow_links);
5115 break;
5117 default:
5118 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
5119 return -1;
5122 return ret;
5125 static int fruit_stat(vfs_handle_struct *handle,
5126 struct smb_filename *smb_fname)
5128 int rc = -1;
5130 DEBUG(10, ("fruit_stat called for %s\n",
5131 smb_fname_str_dbg(smb_fname)));
5133 if (!is_ntfs_stream_smb_fname(smb_fname)
5134 || is_ntfs_default_stream_smb_fname(smb_fname)) {
5135 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
5136 if (rc == 0) {
5137 update_btime(handle, smb_fname);
5139 return rc;
5143 * Note if lp_posix_paths() is true, we can never
5144 * get here as is_ntfs_stream_smb_fname() is
5145 * always false. So we never need worry about
5146 * not following links here.
5149 if (is_afpinfo_stream(smb_fname)) {
5150 rc = fruit_stat_meta(handle, smb_fname, true);
5151 } else if (is_afpresource_stream(smb_fname)) {
5152 rc = fruit_stat_rsrc(handle, smb_fname, true);
5153 } else {
5154 return SMB_VFS_NEXT_STAT(handle, smb_fname);
5157 if (rc == 0) {
5158 update_btime(handle, smb_fname);
5159 smb_fname->st.st_ex_mode &= ~S_IFMT;
5160 smb_fname->st.st_ex_mode |= S_IFREG;
5161 smb_fname->st.st_ex_blocks =
5162 smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
5164 return rc;
5167 static int fruit_lstat(vfs_handle_struct *handle,
5168 struct smb_filename *smb_fname)
5170 int rc = -1;
5172 DEBUG(10, ("fruit_lstat called for %s\n",
5173 smb_fname_str_dbg(smb_fname)));
5175 if (!is_ntfs_stream_smb_fname(smb_fname)
5176 || is_ntfs_default_stream_smb_fname(smb_fname)) {
5177 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5178 if (rc == 0) {
5179 update_btime(handle, smb_fname);
5181 return rc;
5184 if (is_afpinfo_stream(smb_fname)) {
5185 rc = fruit_stat_meta(handle, smb_fname, false);
5186 } else if (is_afpresource_stream(smb_fname)) {
5187 rc = fruit_stat_rsrc(handle, smb_fname, false);
5188 } else {
5189 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5192 if (rc == 0) {
5193 update_btime(handle, smb_fname);
5194 smb_fname->st.st_ex_mode &= ~S_IFMT;
5195 smb_fname->st.st_ex_mode |= S_IFREG;
5196 smb_fname->st.st_ex_blocks =
5197 smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
5199 return rc;
5202 static int fruit_fstat_meta_stream(vfs_handle_struct *handle,
5203 files_struct *fsp,
5204 SMB_STRUCT_STAT *sbuf)
5206 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5207 struct smb_filename smb_fname;
5208 ino_t ino;
5209 int ret;
5211 if (fio == NULL) {
5212 return -1;
5215 if (fio->fake_fd) {
5216 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5217 if (ret != 0) {
5218 return -1;
5221 *sbuf = fsp->base_fsp->fsp_name->st;
5222 sbuf->st_ex_size = AFP_INFO_SIZE;
5223 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5224 return 0;
5227 smb_fname = (struct smb_filename) {
5228 .base_name = fsp->fsp_name->base_name,
5231 ret = fruit_stat_base(handle, &smb_fname, false);
5232 if (ret != 0) {
5233 return -1;
5235 *sbuf = smb_fname.st;
5237 ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5239 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5240 if (ret != 0) {
5241 return -1;
5244 sbuf->st_ex_ino = ino;
5245 return 0;
5248 static int fruit_fstat_meta_netatalk(vfs_handle_struct *handle,
5249 files_struct *fsp,
5250 SMB_STRUCT_STAT *sbuf)
5252 int ret;
5254 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5255 if (ret != 0) {
5256 return -1;
5259 *sbuf = fsp->base_fsp->fsp_name->st;
5260 sbuf->st_ex_size = AFP_INFO_SIZE;
5261 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5263 return 0;
5266 static int fruit_fstat_meta(vfs_handle_struct *handle,
5267 files_struct *fsp,
5268 SMB_STRUCT_STAT *sbuf,
5269 struct fio *fio)
5271 int ret;
5273 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
5275 switch (fio->config->meta) {
5276 case FRUIT_META_STREAM:
5277 ret = fruit_fstat_meta_stream(handle, fsp, sbuf);
5278 break;
5280 case FRUIT_META_NETATALK:
5281 ret = fruit_fstat_meta_netatalk(handle, fsp, sbuf);
5282 break;
5284 default:
5285 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
5286 return -1;
5289 DBG_DEBUG("Path [%s] ret [%d]\n", fsp_str_dbg(fsp), ret);
5290 return ret;
5293 static int fruit_fstat_rsrc_xattr(vfs_handle_struct *handle,
5294 files_struct *fsp,
5295 SMB_STRUCT_STAT *sbuf)
5297 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5300 static int fruit_fstat_rsrc_stream(vfs_handle_struct *handle,
5301 files_struct *fsp,
5302 SMB_STRUCT_STAT *sbuf)
5304 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5307 static int fruit_fstat_rsrc_adouble(vfs_handle_struct *handle,
5308 files_struct *fsp,
5309 SMB_STRUCT_STAT *sbuf)
5311 struct adouble *ad = NULL;
5312 int ret;
5314 /* Populate the stat struct with info from the base file. */
5315 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5316 if (ret == -1) {
5317 return -1;
5320 ad = ad_get(talloc_tos(), handle,
5321 fsp->base_fsp->fsp_name,
5322 ADOUBLE_RSRC);
5323 if (ad == NULL) {
5324 DBG_ERR("ad_get [%s] failed [%s]\n",
5325 fsp_str_dbg(fsp), strerror(errno));
5326 return -1;
5329 *sbuf = fsp->base_fsp->fsp_name->st;
5330 sbuf->st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
5331 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5333 TALLOC_FREE(ad);
5334 return 0;
5337 static int fruit_fstat_rsrc(vfs_handle_struct *handle, files_struct *fsp,
5338 SMB_STRUCT_STAT *sbuf, struct fio *fio)
5340 int ret;
5342 switch (fio->config->rsrc) {
5343 case FRUIT_RSRC_STREAM:
5344 ret = fruit_fstat_rsrc_stream(handle, fsp, sbuf);
5345 break;
5347 case FRUIT_RSRC_ADFILE:
5348 ret = fruit_fstat_rsrc_adouble(handle, fsp, sbuf);
5349 break;
5351 case FRUIT_RSRC_XATTR:
5352 ret = fruit_fstat_rsrc_xattr(handle, fsp, sbuf);
5353 break;
5355 default:
5356 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
5357 return -1;
5360 return ret;
5363 static int fruit_fstat(vfs_handle_struct *handle, files_struct *fsp,
5364 SMB_STRUCT_STAT *sbuf)
5366 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5367 int rc;
5369 if (fio == NULL) {
5370 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5373 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
5375 if (fio->type == ADOUBLE_META) {
5376 rc = fruit_fstat_meta(handle, fsp, sbuf, fio);
5377 } else {
5378 rc = fruit_fstat_rsrc(handle, fsp, sbuf, fio);
5381 if (rc == 0) {
5382 sbuf->st_ex_mode &= ~S_IFMT;
5383 sbuf->st_ex_mode |= S_IFREG;
5384 sbuf->st_ex_blocks = sbuf->st_ex_size / STAT_ST_BLOCKSIZE + 1;
5387 DBG_DEBUG("Path [%s] rc [%d] size [%"PRIdMAX"]\n",
5388 fsp_str_dbg(fsp), rc, (intmax_t)sbuf->st_ex_size);
5389 return rc;
5392 static NTSTATUS delete_invalid_meta_stream(
5393 vfs_handle_struct *handle,
5394 const struct smb_filename *smb_fname,
5395 TALLOC_CTX *mem_ctx,
5396 unsigned int *pnum_streams,
5397 struct stream_struct **pstreams,
5398 off_t size)
5400 struct smb_filename *sname = NULL;
5401 int ret;
5402 bool ok;
5404 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams, AFPINFO_STREAM);
5405 if (!ok) {
5406 return NT_STATUS_INTERNAL_ERROR;
5409 if (size == 0) {
5410 return NT_STATUS_OK;
5413 sname = synthetic_smb_fname(talloc_tos(),
5414 smb_fname->base_name,
5415 AFPINFO_STREAM_NAME,
5416 NULL, 0);
5417 if (sname == NULL) {
5418 return NT_STATUS_NO_MEMORY;
5421 ret = SMB_VFS_NEXT_UNLINK(handle, sname);
5422 TALLOC_FREE(sname);
5423 if (ret != 0) {
5424 DBG_ERR("Removing [%s] failed\n", smb_fname_str_dbg(sname));
5425 return map_nt_error_from_unix(errno);
5428 return NT_STATUS_OK;
5431 static NTSTATUS fruit_streaminfo_meta_stream(
5432 vfs_handle_struct *handle,
5433 struct files_struct *fsp,
5434 const struct smb_filename *smb_fname,
5435 TALLOC_CTX *mem_ctx,
5436 unsigned int *pnum_streams,
5437 struct stream_struct **pstreams)
5439 struct stream_struct *stream = *pstreams;
5440 unsigned int num_streams = *pnum_streams;
5441 int i;
5443 for (i = 0; i < num_streams; i++) {
5444 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
5445 break;
5449 if (i == num_streams) {
5450 return NT_STATUS_OK;
5453 if (stream[i].size != AFP_INFO_SIZE) {
5454 DBG_ERR("Removing invalid AFPINFO_STREAM size [%jd] from [%s]\n",
5455 (intmax_t)stream[i].size, smb_fname_str_dbg(smb_fname));
5457 return delete_invalid_meta_stream(handle,
5458 smb_fname,
5459 mem_ctx,
5460 pnum_streams,
5461 pstreams,
5462 stream[i].size);
5466 return NT_STATUS_OK;
5469 static NTSTATUS fruit_streaminfo_meta_netatalk(
5470 vfs_handle_struct *handle,
5471 struct files_struct *fsp,
5472 const struct smb_filename *smb_fname,
5473 TALLOC_CTX *mem_ctx,
5474 unsigned int *pnum_streams,
5475 struct stream_struct **pstreams)
5477 struct stream_struct *stream = *pstreams;
5478 unsigned int num_streams = *pnum_streams;
5479 struct adouble *ad = NULL;
5480 bool is_fi_empty;
5481 int i;
5482 bool ok;
5484 /* Remove the Netatalk xattr from the list */
5485 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5486 ":" NETATALK_META_XATTR ":$DATA");
5487 if (!ok) {
5488 return NT_STATUS_NO_MEMORY;
5492 * Check if there's a AFPINFO_STREAM from the VFS streams
5493 * backend and if yes, remove it from the list
5495 for (i = 0; i < num_streams; i++) {
5496 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
5497 break;
5501 if (i < num_streams) {
5502 DBG_WARNING("Unexpected AFPINFO_STREAM on [%s]\n",
5503 smb_fname_str_dbg(smb_fname));
5505 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5506 AFPINFO_STREAM);
5507 if (!ok) {
5508 return NT_STATUS_INTERNAL_ERROR;
5512 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5513 if (ad == NULL) {
5514 return NT_STATUS_OK;
5517 is_fi_empty = ad_empty_finderinfo(ad);
5518 TALLOC_FREE(ad);
5520 if (is_fi_empty) {
5521 return NT_STATUS_OK;
5524 ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
5525 AFPINFO_STREAM_NAME, AFP_INFO_SIZE,
5526 smb_roundup(handle->conn, AFP_INFO_SIZE));
5527 if (!ok) {
5528 return NT_STATUS_NO_MEMORY;
5531 return NT_STATUS_OK;
5534 static NTSTATUS fruit_streaminfo_meta(vfs_handle_struct *handle,
5535 struct files_struct *fsp,
5536 const struct smb_filename *smb_fname,
5537 TALLOC_CTX *mem_ctx,
5538 unsigned int *pnum_streams,
5539 struct stream_struct **pstreams)
5541 struct fruit_config_data *config = NULL;
5542 NTSTATUS status;
5544 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5545 return NT_STATUS_INTERNAL_ERROR);
5547 switch (config->meta) {
5548 case FRUIT_META_NETATALK:
5549 status = fruit_streaminfo_meta_netatalk(handle, fsp, smb_fname,
5550 mem_ctx, pnum_streams,
5551 pstreams);
5552 break;
5554 case FRUIT_META_STREAM:
5555 status = fruit_streaminfo_meta_stream(handle, fsp, smb_fname,
5556 mem_ctx, pnum_streams,
5557 pstreams);
5558 break;
5560 default:
5561 return NT_STATUS_INTERNAL_ERROR;
5564 return status;
5567 static NTSTATUS fruit_streaminfo_rsrc_stream(
5568 vfs_handle_struct *handle,
5569 struct files_struct *fsp,
5570 const struct smb_filename *smb_fname,
5571 TALLOC_CTX *mem_ctx,
5572 unsigned int *pnum_streams,
5573 struct stream_struct **pstreams)
5575 bool ok;
5577 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
5578 if (!ok) {
5579 DBG_ERR("Filtering resource stream failed\n");
5580 return NT_STATUS_INTERNAL_ERROR;
5582 return NT_STATUS_OK;
5585 static NTSTATUS fruit_streaminfo_rsrc_xattr(
5586 vfs_handle_struct *handle,
5587 struct files_struct *fsp,
5588 const struct smb_filename *smb_fname,
5589 TALLOC_CTX *mem_ctx,
5590 unsigned int *pnum_streams,
5591 struct stream_struct **pstreams)
5593 bool ok;
5595 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
5596 if (!ok) {
5597 DBG_ERR("Filtering resource stream failed\n");
5598 return NT_STATUS_INTERNAL_ERROR;
5600 return NT_STATUS_OK;
5603 static NTSTATUS fruit_streaminfo_rsrc_adouble(
5604 vfs_handle_struct *handle,
5605 struct files_struct *fsp,
5606 const struct smb_filename *smb_fname,
5607 TALLOC_CTX *mem_ctx,
5608 unsigned int *pnum_streams,
5609 struct stream_struct **pstreams)
5611 struct stream_struct *stream = *pstreams;
5612 unsigned int num_streams = *pnum_streams;
5613 struct adouble *ad = NULL;
5614 bool ok;
5615 size_t rlen;
5616 int i;
5619 * Check if there's a AFPRESOURCE_STREAM from the VFS streams backend
5620 * and if yes, remove it from the list
5622 for (i = 0; i < num_streams; i++) {
5623 if (strequal_m(stream[i].name, AFPRESOURCE_STREAM)) {
5624 break;
5628 if (i < num_streams) {
5629 DBG_WARNING("Unexpected AFPRESOURCE_STREAM on [%s]\n",
5630 smb_fname_str_dbg(smb_fname));
5632 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5633 AFPRESOURCE_STREAM);
5634 if (!ok) {
5635 return NT_STATUS_INTERNAL_ERROR;
5639 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
5640 if (ad == NULL) {
5641 return NT_STATUS_OK;
5644 rlen = ad_getentrylen(ad, ADEID_RFORK);
5645 TALLOC_FREE(ad);
5647 if (rlen == 0) {
5648 return NT_STATUS_OK;
5651 ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
5652 AFPRESOURCE_STREAM_NAME, rlen,
5653 smb_roundup(handle->conn, rlen));
5654 if (!ok) {
5655 return NT_STATUS_NO_MEMORY;
5658 return NT_STATUS_OK;
5661 static NTSTATUS fruit_streaminfo_rsrc(vfs_handle_struct *handle,
5662 struct files_struct *fsp,
5663 const struct smb_filename *smb_fname,
5664 TALLOC_CTX *mem_ctx,
5665 unsigned int *pnum_streams,
5666 struct stream_struct **pstreams)
5668 struct fruit_config_data *config = NULL;
5669 NTSTATUS status;
5671 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5672 return NT_STATUS_INTERNAL_ERROR);
5674 switch (config->rsrc) {
5675 case FRUIT_RSRC_STREAM:
5676 status = fruit_streaminfo_rsrc_stream(handle, fsp, smb_fname,
5677 mem_ctx, pnum_streams,
5678 pstreams);
5679 break;
5681 case FRUIT_RSRC_XATTR:
5682 status = fruit_streaminfo_rsrc_xattr(handle, fsp, smb_fname,
5683 mem_ctx, pnum_streams,
5684 pstreams);
5685 break;
5687 case FRUIT_RSRC_ADFILE:
5688 status = fruit_streaminfo_rsrc_adouble(handle, fsp, smb_fname,
5689 mem_ctx, pnum_streams,
5690 pstreams);
5691 break;
5693 default:
5694 return NT_STATUS_INTERNAL_ERROR;
5697 return status;
5700 static void fruit_filter_empty_streams(unsigned int *pnum_streams,
5701 struct stream_struct **pstreams)
5703 unsigned num_streams = *pnum_streams;
5704 struct stream_struct *streams = *pstreams;
5705 unsigned i = 0;
5707 if (!global_fruit_config.nego_aapl) {
5708 return;
5711 while (i < num_streams) {
5712 struct smb_filename smb_fname = (struct smb_filename) {
5713 .stream_name = streams[i].name,
5716 if (is_ntfs_default_stream_smb_fname(&smb_fname)
5717 || streams[i].size > 0)
5719 i++;
5720 continue;
5723 streams[i] = streams[num_streams - 1];
5724 num_streams--;
5727 *pnum_streams = num_streams;
5730 static NTSTATUS fruit_streaminfo(vfs_handle_struct *handle,
5731 struct files_struct *fsp,
5732 const struct smb_filename *smb_fname,
5733 TALLOC_CTX *mem_ctx,
5734 unsigned int *pnum_streams,
5735 struct stream_struct **pstreams)
5737 struct fruit_config_data *config = NULL;
5738 NTSTATUS status;
5740 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5741 return NT_STATUS_UNSUCCESSFUL);
5743 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
5745 status = SMB_VFS_NEXT_STREAMINFO(handle, fsp, smb_fname, mem_ctx,
5746 pnum_streams, pstreams);
5747 if (!NT_STATUS_IS_OK(status)) {
5748 return status;
5751 fruit_filter_empty_streams(pnum_streams, pstreams);
5753 status = fruit_streaminfo_meta(handle, fsp, smb_fname,
5754 mem_ctx, pnum_streams, pstreams);
5755 if (!NT_STATUS_IS_OK(status)) {
5756 return status;
5759 status = fruit_streaminfo_rsrc(handle, fsp, smb_fname,
5760 mem_ctx, pnum_streams, pstreams);
5761 if (!NT_STATUS_IS_OK(status)) {
5762 return status;
5765 return NT_STATUS_OK;
5768 static int fruit_ntimes(vfs_handle_struct *handle,
5769 const struct smb_filename *smb_fname,
5770 struct smb_file_time *ft)
5772 int rc = 0;
5773 struct adouble *ad = NULL;
5774 struct fruit_config_data *config = NULL;
5776 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5777 return -1);
5779 if ((config->meta != FRUIT_META_NETATALK) ||
5780 null_timespec(ft->create_time))
5782 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
5785 DEBUG(10,("set btime for %s to %s\n", smb_fname_str_dbg(smb_fname),
5786 time_to_asc(convert_timespec_to_time_t(ft->create_time))));
5788 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5789 if (ad == NULL) {
5790 goto exit;
5793 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX,
5794 convert_time_t_to_uint32_t(ft->create_time.tv_sec));
5796 rc = ad_set(ad, smb_fname);
5798 exit:
5800 TALLOC_FREE(ad);
5801 if (rc != 0) {
5802 DEBUG(1, ("fruit_ntimes: %s\n", smb_fname_str_dbg(smb_fname)));
5803 return -1;
5805 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
5808 static int fruit_fallocate(struct vfs_handle_struct *handle,
5809 struct files_struct *fsp,
5810 uint32_t mode,
5811 off_t offset,
5812 off_t len)
5814 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5816 if (fio == NULL) {
5817 return SMB_VFS_NEXT_FALLOCATE(handle, fsp, mode, offset, len);
5820 /* Let the pwrite code path handle it. */
5821 errno = ENOSYS;
5822 return -1;
5825 static int fruit_ftruncate_rsrc_xattr(struct vfs_handle_struct *handle,
5826 struct files_struct *fsp,
5827 off_t offset)
5829 #ifdef HAVE_ATTROPEN
5830 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
5831 #endif
5832 return 0;
5835 static int fruit_ftruncate_rsrc_adouble(struct vfs_handle_struct *handle,
5836 struct files_struct *fsp,
5837 off_t offset)
5839 int rc;
5840 struct adouble *ad = NULL;
5841 off_t ad_off;
5843 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
5844 if (ad == NULL) {
5845 DBG_DEBUG("ad_get [%s] failed [%s]\n",
5846 fsp_str_dbg(fsp), strerror(errno));
5847 return -1;
5850 ad_off = ad_getentryoff(ad, ADEID_RFORK);
5852 rc = ftruncate(fsp->fh->fd, offset + ad_off);
5853 if (rc != 0) {
5854 TALLOC_FREE(ad);
5855 return -1;
5858 ad_setentrylen(ad, ADEID_RFORK, offset);
5860 rc = ad_fset(ad, fsp);
5861 if (rc != 0) {
5862 DBG_ERR("ad_fset [%s] failed [%s]\n",
5863 fsp_str_dbg(fsp), strerror(errno));
5864 TALLOC_FREE(ad);
5865 return -1;
5868 TALLOC_FREE(ad);
5869 return 0;
5872 static int fruit_ftruncate_rsrc_stream(struct vfs_handle_struct *handle,
5873 struct files_struct *fsp,
5874 off_t offset)
5876 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
5879 static int fruit_ftruncate_rsrc(struct vfs_handle_struct *handle,
5880 struct files_struct *fsp,
5881 off_t offset)
5883 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5884 int ret;
5886 if (fio == NULL) {
5887 DBG_ERR("Failed to fetch fsp extension");
5888 return -1;
5891 switch (fio->config->rsrc) {
5892 case FRUIT_RSRC_XATTR:
5893 ret = fruit_ftruncate_rsrc_xattr(handle, fsp, offset);
5894 break;
5896 case FRUIT_RSRC_ADFILE:
5897 ret = fruit_ftruncate_rsrc_adouble(handle, fsp, offset);
5898 break;
5900 case FRUIT_RSRC_STREAM:
5901 ret = fruit_ftruncate_rsrc_stream(handle, fsp, offset);
5902 break;
5904 default:
5905 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
5906 return -1;
5910 return ret;
5913 static int fruit_ftruncate_meta(struct vfs_handle_struct *handle,
5914 struct files_struct *fsp,
5915 off_t offset)
5917 if (offset > 60) {
5918 DBG_WARNING("ftruncate %s to %jd",
5919 fsp_str_dbg(fsp), (intmax_t)offset);
5920 /* OS X returns NT_STATUS_ALLOTTED_SPACE_EXCEEDED */
5921 errno = EOVERFLOW;
5922 return -1;
5925 /* OS X returns success but does nothing */
5926 DBG_INFO("ignoring ftruncate %s to %jd\n",
5927 fsp_str_dbg(fsp), (intmax_t)offset);
5928 return 0;
5931 static int fruit_ftruncate(struct vfs_handle_struct *handle,
5932 struct files_struct *fsp,
5933 off_t offset)
5935 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5936 int ret;
5938 DBG_DEBUG("Path [%s] offset [%"PRIdMAX"]\n", fsp_str_dbg(fsp),
5939 (intmax_t)offset);
5941 if (fio == NULL) {
5942 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
5945 if (fio->type == ADOUBLE_META) {
5946 ret = fruit_ftruncate_meta(handle, fsp, offset);
5947 } else {
5948 ret = fruit_ftruncate_rsrc(handle, fsp, offset);
5951 DBG_DEBUG("Path [%s] result [%d]\n", fsp_str_dbg(fsp), ret);
5952 return ret;
5955 static NTSTATUS fruit_create_file(vfs_handle_struct *handle,
5956 struct smb_request *req,
5957 uint16_t root_dir_fid,
5958 struct smb_filename *smb_fname,
5959 uint32_t access_mask,
5960 uint32_t share_access,
5961 uint32_t create_disposition,
5962 uint32_t create_options,
5963 uint32_t file_attributes,
5964 uint32_t oplock_request,
5965 struct smb2_lease *lease,
5966 uint64_t allocation_size,
5967 uint32_t private_flags,
5968 struct security_descriptor *sd,
5969 struct ea_list *ea_list,
5970 files_struct **result,
5971 int *pinfo,
5972 const struct smb2_create_blobs *in_context_blobs,
5973 struct smb2_create_blobs *out_context_blobs)
5975 NTSTATUS status;
5976 struct fruit_config_data *config = NULL;
5977 files_struct *fsp = NULL;
5978 struct fio *fio = NULL;
5980 status = check_aapl(handle, req, in_context_blobs, out_context_blobs);
5981 if (!NT_STATUS_IS_OK(status)) {
5982 goto fail;
5985 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5986 return NT_STATUS_UNSUCCESSFUL);
5988 status = SMB_VFS_NEXT_CREATE_FILE(
5989 handle, req, root_dir_fid, smb_fname,
5990 access_mask, share_access,
5991 create_disposition, create_options,
5992 file_attributes, oplock_request,
5993 lease,
5994 allocation_size, private_flags,
5995 sd, ea_list, result,
5996 pinfo, in_context_blobs, out_context_blobs);
5997 if (!NT_STATUS_IS_OK(status)) {
5998 return status;
6001 fsp = *result;
6003 if (global_fruit_config.nego_aapl) {
6004 if (config->posix_rename && fsp->is_directory) {
6006 * Enable POSIX directory rename behaviour
6008 fsp->posix_flags |= FSP_POSIX_FLAGS_RENAME;
6013 * If this is a plain open for existing files, opening an 0
6014 * byte size resource fork MUST fail with
6015 * NT_STATUS_OBJECT_NAME_NOT_FOUND.
6017 * Cf the vfs_fruit torture tests in test_rfork_create().
6019 if (global_fruit_config.nego_aapl &&
6020 create_disposition == FILE_OPEN &&
6021 smb_fname->st.st_ex_size == 0 &&
6022 is_ntfs_stream_smb_fname(smb_fname) &&
6023 !(is_ntfs_default_stream_smb_fname(smb_fname)))
6025 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
6026 goto fail;
6029 fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
6030 if (fio != NULL && pinfo != NULL && *pinfo == FILE_WAS_CREATED) {
6031 fio->created = true;
6034 if (is_ntfs_stream_smb_fname(smb_fname)
6035 || fsp->is_directory) {
6036 return status;
6039 if (config->locking == FRUIT_LOCKING_NETATALK) {
6040 status = fruit_check_access(
6041 handle, *result,
6042 access_mask,
6043 map_share_mode_to_deny_mode(share_access, 0));
6044 if (!NT_STATUS_IS_OK(status)) {
6045 goto fail;
6049 return status;
6051 fail:
6052 DEBUG(10, ("fruit_create_file: %s\n", nt_errstr(status)));
6054 if (fsp) {
6055 close_file(req, fsp, ERROR_CLOSE);
6056 *result = fsp = NULL;
6059 return status;
6062 static NTSTATUS fruit_readdir_attr(struct vfs_handle_struct *handle,
6063 const struct smb_filename *fname,
6064 TALLOC_CTX *mem_ctx,
6065 struct readdir_attr_data **pattr_data)
6067 struct fruit_config_data *config = NULL;
6068 struct readdir_attr_data *attr_data;
6069 NTSTATUS status;
6071 SMB_VFS_HANDLE_GET_DATA(handle, config,
6072 struct fruit_config_data,
6073 return NT_STATUS_UNSUCCESSFUL);
6075 if (!global_fruit_config.nego_aapl) {
6076 return SMB_VFS_NEXT_READDIR_ATTR(handle, fname, mem_ctx, pattr_data);
6079 DEBUG(10, ("fruit_readdir_attr %s\n", fname->base_name));
6081 *pattr_data = talloc_zero(mem_ctx, struct readdir_attr_data);
6082 if (*pattr_data == NULL) {
6083 return NT_STATUS_UNSUCCESSFUL;
6085 attr_data = *pattr_data;
6086 attr_data->type = RDATTR_AAPL;
6089 * Mac metadata: compressed FinderInfo, resource fork length
6090 * and creation date
6092 status = readdir_attr_macmeta(handle, fname, attr_data);
6093 if (!NT_STATUS_IS_OK(status)) {
6095 * Error handling is tricky: if we return failure from
6096 * this function, the corresponding directory entry
6097 * will to be passed to the client, so we really just
6098 * want to error out on fatal errors.
6100 if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
6101 goto fail;
6106 * UNIX mode
6108 if (config->unix_info_enabled) {
6109 attr_data->attr_data.aapl.unix_mode = fname->st.st_ex_mode;
6113 * max_access
6115 if (!config->readdir_attr_max_access) {
6116 attr_data->attr_data.aapl.max_access = FILE_GENERIC_ALL;
6117 } else {
6118 status = smbd_calculate_access_mask(
6119 handle->conn,
6120 fname,
6121 false,
6122 SEC_FLAG_MAXIMUM_ALLOWED,
6123 &attr_data->attr_data.aapl.max_access);
6124 if (!NT_STATUS_IS_OK(status)) {
6125 goto fail;
6129 return NT_STATUS_OK;
6131 fail:
6132 DEBUG(1, ("fruit_readdir_attr %s, error: %s\n",
6133 fname->base_name, nt_errstr(status)));
6134 TALLOC_FREE(*pattr_data);
6135 return status;
6138 static NTSTATUS fruit_fget_nt_acl(vfs_handle_struct *handle,
6139 files_struct *fsp,
6140 uint32_t security_info,
6141 TALLOC_CTX *mem_ctx,
6142 struct security_descriptor **ppdesc)
6144 NTSTATUS status;
6145 struct security_ace ace;
6146 struct dom_sid sid;
6147 struct fruit_config_data *config;
6149 SMB_VFS_HANDLE_GET_DATA(handle, config,
6150 struct fruit_config_data,
6151 return NT_STATUS_UNSUCCESSFUL);
6153 status = SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
6154 mem_ctx, ppdesc);
6155 if (!NT_STATUS_IS_OK(status)) {
6156 return status;
6160 * Add MS NFS style ACEs with uid, gid and mode
6162 if (!global_fruit_config.nego_aapl) {
6163 return NT_STATUS_OK;
6165 if (!config->unix_info_enabled) {
6166 return NT_STATUS_OK;
6169 /* First remove any existing ACE's with NFS style mode/uid/gid SIDs. */
6170 status = remove_virtual_nfs_aces(*ppdesc);
6171 if (!NT_STATUS_IS_OK(status)) {
6172 DBG_WARNING("failed to remove MS NFS style ACEs\n");
6173 return status;
6176 /* MS NFS style mode */
6177 sid_compose(&sid, &global_sid_Unix_NFS_Mode, fsp->fsp_name->st.st_ex_mode);
6178 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6179 status = security_descriptor_dacl_add(*ppdesc, &ace);
6180 if (!NT_STATUS_IS_OK(status)) {
6181 DEBUG(1,("failed to add MS NFS style ACE\n"));
6182 return status;
6185 /* MS NFS style uid */
6186 sid_compose(&sid, &global_sid_Unix_NFS_Users, fsp->fsp_name->st.st_ex_uid);
6187 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6188 status = security_descriptor_dacl_add(*ppdesc, &ace);
6189 if (!NT_STATUS_IS_OK(status)) {
6190 DEBUG(1,("failed to add MS NFS style ACE\n"));
6191 return status;
6194 /* MS NFS style gid */
6195 sid_compose(&sid, &global_sid_Unix_NFS_Groups, fsp->fsp_name->st.st_ex_gid);
6196 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6197 status = security_descriptor_dacl_add(*ppdesc, &ace);
6198 if (!NT_STATUS_IS_OK(status)) {
6199 DEBUG(1,("failed to add MS NFS style ACE\n"));
6200 return status;
6203 return NT_STATUS_OK;
6206 static NTSTATUS fruit_fset_nt_acl(vfs_handle_struct *handle,
6207 files_struct *fsp,
6208 uint32_t security_info_sent,
6209 const struct security_descriptor *orig_psd)
6211 NTSTATUS status;
6212 bool do_chmod;
6213 mode_t ms_nfs_mode = 0;
6214 int result;
6215 struct security_descriptor *psd = NULL;
6216 uint32_t orig_num_aces = 0;
6218 if (orig_psd->dacl != NULL) {
6219 orig_num_aces = orig_psd->dacl->num_aces;
6222 psd = security_descriptor_copy(talloc_tos(), orig_psd);
6223 if (psd == NULL) {
6224 return NT_STATUS_NO_MEMORY;
6227 DBG_DEBUG("fruit_fset_nt_acl: %s\n", fsp_str_dbg(fsp));
6229 status = check_ms_nfs(handle, fsp, psd, &ms_nfs_mode, &do_chmod);
6230 if (!NT_STATUS_IS_OK(status)) {
6231 DEBUG(1, ("fruit_fset_nt_acl: check_ms_nfs failed%s\n", fsp_str_dbg(fsp)));
6232 TALLOC_FREE(psd);
6233 return status;
6237 * If only ms_nfs ACE entries were sent, ensure we set the DACL
6238 * sent/present flags correctly now we've removed them.
6241 if (orig_num_aces != 0) {
6243 * Are there any ACE's left ?
6245 if (psd->dacl->num_aces == 0) {
6246 /* No - clear the DACL sent/present flags. */
6247 security_info_sent &= ~SECINFO_DACL;
6248 psd->type &= ~SEC_DESC_DACL_PRESENT;
6252 status = SMB_VFS_NEXT_FSET_NT_ACL(handle, fsp, security_info_sent, psd);
6253 if (!NT_STATUS_IS_OK(status)) {
6254 DEBUG(1, ("fruit_fset_nt_acl: SMB_VFS_NEXT_FSET_NT_ACL failed%s\n", fsp_str_dbg(fsp)));
6255 TALLOC_FREE(psd);
6256 return status;
6259 if (do_chmod) {
6260 if (fsp->fh->fd != -1) {
6261 result = SMB_VFS_FCHMOD(fsp, ms_nfs_mode);
6262 } else {
6263 result = SMB_VFS_CHMOD(fsp->conn,
6264 fsp->fsp_name,
6265 ms_nfs_mode);
6268 if (result != 0) {
6269 DEBUG(1, ("chmod: %s, result: %d, %04o error %s\n", fsp_str_dbg(fsp),
6270 result, (unsigned)ms_nfs_mode,
6271 strerror(errno)));
6272 status = map_nt_error_from_unix(errno);
6273 TALLOC_FREE(psd);
6274 return status;
6278 TALLOC_FREE(psd);
6279 return NT_STATUS_OK;
6282 static struct vfs_offload_ctx *fruit_offload_ctx;
6284 struct fruit_offload_read_state {
6285 struct vfs_handle_struct *handle;
6286 struct tevent_context *ev;
6287 files_struct *fsp;
6288 uint32_t fsctl;
6289 DATA_BLOB token;
6292 static void fruit_offload_read_done(struct tevent_req *subreq);
6294 static struct tevent_req *fruit_offload_read_send(
6295 TALLOC_CTX *mem_ctx,
6296 struct tevent_context *ev,
6297 struct vfs_handle_struct *handle,
6298 files_struct *fsp,
6299 uint32_t fsctl,
6300 uint32_t ttl,
6301 off_t offset,
6302 size_t to_copy)
6304 struct tevent_req *req = NULL;
6305 struct tevent_req *subreq = NULL;
6306 struct fruit_offload_read_state *state = NULL;
6308 req = tevent_req_create(mem_ctx, &state,
6309 struct fruit_offload_read_state);
6310 if (req == NULL) {
6311 return NULL;
6313 *state = (struct fruit_offload_read_state) {
6314 .handle = handle,
6315 .ev = ev,
6316 .fsp = fsp,
6317 .fsctl = fsctl,
6320 subreq = SMB_VFS_NEXT_OFFLOAD_READ_SEND(mem_ctx, ev, handle, fsp,
6321 fsctl, ttl, offset, to_copy);
6322 if (tevent_req_nomem(subreq, req)) {
6323 return tevent_req_post(req, ev);
6325 tevent_req_set_callback(subreq, fruit_offload_read_done, req);
6326 return req;
6329 static void fruit_offload_read_done(struct tevent_req *subreq)
6331 struct tevent_req *req = tevent_req_callback_data(
6332 subreq, struct tevent_req);
6333 struct fruit_offload_read_state *state = tevent_req_data(
6334 req, struct fruit_offload_read_state);
6335 NTSTATUS status;
6337 status = SMB_VFS_NEXT_OFFLOAD_READ_RECV(subreq,
6338 state->handle,
6339 state,
6340 &state->token);
6341 TALLOC_FREE(subreq);
6342 if (tevent_req_nterror(req, status)) {
6343 return;
6346 if (state->fsctl != FSCTL_SRV_REQUEST_RESUME_KEY) {
6347 tevent_req_done(req);
6348 return;
6351 status = vfs_offload_token_ctx_init(state->fsp->conn->sconn->client,
6352 &fruit_offload_ctx);
6353 if (tevent_req_nterror(req, status)) {
6354 return;
6357 status = vfs_offload_token_db_store_fsp(fruit_offload_ctx,
6358 state->fsp,
6359 &state->token);
6360 if (tevent_req_nterror(req, status)) {
6361 return;
6364 tevent_req_done(req);
6365 return;
6368 static NTSTATUS fruit_offload_read_recv(struct tevent_req *req,
6369 struct vfs_handle_struct *handle,
6370 TALLOC_CTX *mem_ctx,
6371 DATA_BLOB *token)
6373 struct fruit_offload_read_state *state = tevent_req_data(
6374 req, struct fruit_offload_read_state);
6375 NTSTATUS status;
6377 if (tevent_req_is_nterror(req, &status)) {
6378 tevent_req_received(req);
6379 return status;
6382 token->length = state->token.length;
6383 token->data = talloc_move(mem_ctx, &state->token.data);
6385 tevent_req_received(req);
6386 return NT_STATUS_OK;
6389 struct fruit_offload_write_state {
6390 struct vfs_handle_struct *handle;
6391 off_t copied;
6392 struct files_struct *src_fsp;
6393 struct files_struct *dst_fsp;
6394 bool is_copyfile;
6397 static void fruit_offload_write_done(struct tevent_req *subreq);
6398 static struct tevent_req *fruit_offload_write_send(struct vfs_handle_struct *handle,
6399 TALLOC_CTX *mem_ctx,
6400 struct tevent_context *ev,
6401 uint32_t fsctl,
6402 DATA_BLOB *token,
6403 off_t transfer_offset,
6404 struct files_struct *dest_fsp,
6405 off_t dest_off,
6406 off_t num)
6408 struct tevent_req *req, *subreq;
6409 struct fruit_offload_write_state *state;
6410 NTSTATUS status;
6411 struct fruit_config_data *config;
6412 off_t src_off = transfer_offset;
6413 files_struct *src_fsp = NULL;
6414 off_t to_copy = num;
6415 bool copyfile_enabled = false;
6417 DEBUG(10,("soff: %ju, doff: %ju, len: %ju\n",
6418 (uintmax_t)src_off, (uintmax_t)dest_off, (uintmax_t)num));
6420 SMB_VFS_HANDLE_GET_DATA(handle, config,
6421 struct fruit_config_data,
6422 return NULL);
6424 req = tevent_req_create(mem_ctx, &state,
6425 struct fruit_offload_write_state);
6426 if (req == NULL) {
6427 return NULL;
6429 state->handle = handle;
6430 state->dst_fsp = dest_fsp;
6432 switch (fsctl) {
6433 case FSCTL_SRV_COPYCHUNK:
6434 case FSCTL_SRV_COPYCHUNK_WRITE:
6435 copyfile_enabled = config->copyfile_enabled;
6436 break;
6437 default:
6438 break;
6442 * Check if this a OS X copyfile style copychunk request with
6443 * a requested chunk count of 0 that was translated to a
6444 * offload_write_send VFS call overloading the parameters src_off
6445 * = dest_off = num = 0.
6447 if (copyfile_enabled && num == 0 && src_off == 0 && dest_off == 0) {
6448 status = vfs_offload_token_db_fetch_fsp(
6449 fruit_offload_ctx, token, &src_fsp);
6450 if (tevent_req_nterror(req, status)) {
6451 return tevent_req_post(req, ev);
6453 state->src_fsp = src_fsp;
6455 status = vfs_stat_fsp(src_fsp);
6456 if (tevent_req_nterror(req, status)) {
6457 return tevent_req_post(req, ev);
6460 to_copy = src_fsp->fsp_name->st.st_ex_size;
6461 state->is_copyfile = true;
6464 subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle,
6465 mem_ctx,
6467 fsctl,
6468 token,
6469 transfer_offset,
6470 dest_fsp,
6471 dest_off,
6472 to_copy);
6473 if (tevent_req_nomem(subreq, req)) {
6474 return tevent_req_post(req, ev);
6477 tevent_req_set_callback(subreq, fruit_offload_write_done, req);
6478 return req;
6481 static void fruit_offload_write_done(struct tevent_req *subreq)
6483 struct tevent_req *req = tevent_req_callback_data(
6484 subreq, struct tevent_req);
6485 struct fruit_offload_write_state *state = tevent_req_data(
6486 req, struct fruit_offload_write_state);
6487 NTSTATUS status;
6488 unsigned int num_streams = 0;
6489 struct stream_struct *streams = NULL;
6490 unsigned int i;
6491 struct smb_filename *src_fname_tmp = NULL;
6492 struct smb_filename *dst_fname_tmp = NULL;
6494 status = SMB_VFS_NEXT_OFFLOAD_WRITE_RECV(state->handle,
6495 subreq,
6496 &state->copied);
6497 TALLOC_FREE(subreq);
6498 if (tevent_req_nterror(req, status)) {
6499 return;
6502 if (!state->is_copyfile) {
6503 tevent_req_done(req);
6504 return;
6508 * Now copy all remaining streams. We know the share supports
6509 * streams, because we're in vfs_fruit. We don't do this async
6510 * because streams are few and small.
6512 status = vfs_streaminfo(state->handle->conn, state->src_fsp,
6513 state->src_fsp->fsp_name,
6514 req, &num_streams, &streams);
6515 if (tevent_req_nterror(req, status)) {
6516 return;
6519 if (num_streams == 1) {
6520 /* There is always one stream, ::$DATA. */
6521 tevent_req_done(req);
6522 return;
6525 for (i = 0; i < num_streams; i++) {
6526 DEBUG(10, ("%s: stream: '%s'/%zu\n",
6527 __func__, streams[i].name, (size_t)streams[i].size));
6529 src_fname_tmp = synthetic_smb_fname(
6530 req,
6531 state->src_fsp->fsp_name->base_name,
6532 streams[i].name,
6533 NULL,
6534 state->src_fsp->fsp_name->flags);
6535 if (tevent_req_nomem(src_fname_tmp, req)) {
6536 return;
6539 if (is_ntfs_default_stream_smb_fname(src_fname_tmp)) {
6540 TALLOC_FREE(src_fname_tmp);
6541 continue;
6544 dst_fname_tmp = synthetic_smb_fname(
6545 req,
6546 state->dst_fsp->fsp_name->base_name,
6547 streams[i].name,
6548 NULL,
6549 state->dst_fsp->fsp_name->flags);
6550 if (tevent_req_nomem(dst_fname_tmp, req)) {
6551 TALLOC_FREE(src_fname_tmp);
6552 return;
6555 status = copy_file(req,
6556 state->handle->conn,
6557 src_fname_tmp,
6558 dst_fname_tmp,
6559 OPENX_FILE_CREATE_IF_NOT_EXIST,
6560 0, false);
6561 if (!NT_STATUS_IS_OK(status)) {
6562 DEBUG(1, ("%s: copy %s to %s failed: %s\n", __func__,
6563 smb_fname_str_dbg(src_fname_tmp),
6564 smb_fname_str_dbg(dst_fname_tmp),
6565 nt_errstr(status)));
6566 TALLOC_FREE(src_fname_tmp);
6567 TALLOC_FREE(dst_fname_tmp);
6568 tevent_req_nterror(req, status);
6569 return;
6572 TALLOC_FREE(src_fname_tmp);
6573 TALLOC_FREE(dst_fname_tmp);
6576 TALLOC_FREE(streams);
6577 TALLOC_FREE(src_fname_tmp);
6578 TALLOC_FREE(dst_fname_tmp);
6579 tevent_req_done(req);
6582 static NTSTATUS fruit_offload_write_recv(struct vfs_handle_struct *handle,
6583 struct tevent_req *req,
6584 off_t *copied)
6586 struct fruit_offload_write_state *state = tevent_req_data(
6587 req, struct fruit_offload_write_state);
6588 NTSTATUS status;
6590 if (tevent_req_is_nterror(req, &status)) {
6591 DEBUG(1, ("server side copy chunk failed: %s\n",
6592 nt_errstr(status)));
6593 *copied = 0;
6594 tevent_req_received(req);
6595 return status;
6598 *copied = state->copied;
6599 tevent_req_received(req);
6601 return NT_STATUS_OK;
6604 static char *fruit_get_bandsize_line(char **lines, int numlines)
6606 static regex_t re;
6607 static bool re_initialized = false;
6608 int i;
6609 int ret;
6611 if (!re_initialized) {
6612 ret = regcomp(&re, "^[[:blank:]]*<key>band-size</key>$", 0);
6613 if (ret != 0) {
6614 return NULL;
6616 re_initialized = true;
6619 for (i = 0; i < numlines; i++) {
6620 regmatch_t matches[1];
6622 ret = regexec(&re, lines[i], 1, matches, 0);
6623 if (ret == 0) {
6625 * Check if the match was on the last line, sa we want
6626 * the subsequent line.
6628 if (i + 1 == numlines) {
6629 return NULL;
6631 return lines[i + 1];
6633 if (ret != REG_NOMATCH) {
6634 return NULL;
6638 return NULL;
6641 static bool fruit_get_bandsize_from_line(char *line, size_t *_band_size)
6643 static regex_t re;
6644 static bool re_initialized = false;
6645 regmatch_t matches[2];
6646 uint64_t band_size;
6647 int ret;
6648 bool ok;
6650 if (!re_initialized) {
6651 ret = regcomp(&re,
6652 "^[[:blank:]]*"
6653 "<integer>\\([[:digit:]]*\\)</integer>$",
6655 if (ret != 0) {
6656 return false;
6658 re_initialized = true;
6661 ret = regexec(&re, line, 2, matches, 0);
6662 if (ret != 0) {
6663 DBG_ERR("regex failed [%s]\n", line);
6664 return false;
6667 line[matches[1].rm_eo] = '\0';
6669 ok = conv_str_u64(&line[matches[1].rm_so], &band_size);
6670 if (!ok) {
6671 return false;
6673 *_band_size = (size_t)band_size;
6674 return true;
6678 * This reads and parses an Info.plist from a TM sparsebundle looking for the
6679 * "band-size" key and value.
6681 static bool fruit_get_bandsize(vfs_handle_struct *handle,
6682 const char *dir,
6683 size_t *band_size)
6685 #define INFO_PLIST_MAX_SIZE 64*1024
6686 char *plist = NULL;
6687 struct smb_filename *smb_fname = NULL;
6688 files_struct *fsp = NULL;
6689 uint8_t *file_data = NULL;
6690 char **lines = NULL;
6691 char *band_size_line = NULL;
6692 size_t plist_file_size;
6693 ssize_t nread;
6694 int numlines;
6695 int ret;
6696 bool ok = false;
6697 NTSTATUS status;
6699 plist = talloc_asprintf(talloc_tos(),
6700 "%s/%s/Info.plist",
6701 handle->conn->connectpath,
6702 dir);
6703 if (plist == NULL) {
6704 ok = false;
6705 goto out;
6708 smb_fname = synthetic_smb_fname(talloc_tos(), plist, NULL, NULL, 0);
6709 if (smb_fname == NULL) {
6710 ok = false;
6711 goto out;
6714 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
6715 if (ret != 0) {
6716 DBG_INFO("Ignoring Sparsebundle without Info.plist [%s]\n", dir);
6717 ok = true;
6718 goto out;
6721 plist_file_size = smb_fname->st.st_ex_size;
6723 if (plist_file_size > INFO_PLIST_MAX_SIZE) {
6724 DBG_INFO("%s is too large, ignoring\n", plist);
6725 ok = true;
6726 goto out;
6729 status = SMB_VFS_NEXT_CREATE_FILE(
6730 handle, /* conn */
6731 NULL, /* req */
6732 0, /* root_dir_fid */
6733 smb_fname, /* fname */
6734 FILE_GENERIC_READ, /* access_mask */
6735 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
6736 FILE_OPEN, /* create_disposition */
6737 0, /* create_options */
6738 0, /* file_attributes */
6739 INTERNAL_OPEN_ONLY, /* oplock_request */
6740 NULL, /* lease */
6741 0, /* allocation_size */
6742 0, /* private_flags */
6743 NULL, /* sd */
6744 NULL, /* ea_list */
6745 &fsp, /* result */
6746 NULL, /* psbuf */
6747 NULL, NULL); /* create context */
6748 if (!NT_STATUS_IS_OK(status)) {
6749 DBG_INFO("Opening [%s] failed [%s]\n",
6750 smb_fname_str_dbg(smb_fname), nt_errstr(status));
6751 ok = false;
6752 goto out;
6755 file_data = talloc_array(talloc_tos(), uint8_t, plist_file_size);
6756 if (file_data == NULL) {
6757 ok = false;
6758 goto out;
6761 nread = SMB_VFS_NEXT_PREAD(handle, fsp, file_data, plist_file_size, 0);
6762 if (nread != plist_file_size) {
6763 DBG_ERR("Short read on [%s]: %zu/%zd\n",
6764 fsp_str_dbg(fsp), nread, plist_file_size);
6765 ok = false;
6766 goto out;
6770 status = close_file(NULL, fsp, NORMAL_CLOSE);
6771 fsp = NULL;
6772 if (!NT_STATUS_IS_OK(status)) {
6773 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
6774 ok = false;
6775 goto out;
6778 lines = file_lines_parse((char *)file_data,
6779 plist_file_size,
6780 &numlines,
6781 talloc_tos());
6782 if (lines == NULL) {
6783 ok = false;
6784 goto out;
6787 band_size_line = fruit_get_bandsize_line(lines, numlines);
6788 if (band_size_line == NULL) {
6789 DBG_ERR("Didn't find band-size key in [%s]\n",
6790 smb_fname_str_dbg(smb_fname));
6791 ok = false;
6792 goto out;
6795 ok = fruit_get_bandsize_from_line(band_size_line, band_size);
6796 if (!ok) {
6797 DBG_ERR("fruit_get_bandsize_from_line failed\n");
6798 goto out;
6801 DBG_DEBUG("Parsed band-size [%zu] for [%s]\n", *band_size, plist);
6803 out:
6804 if (fsp != NULL) {
6805 status = close_file(NULL, fsp, NORMAL_CLOSE);
6806 if (!NT_STATUS_IS_OK(status)) {
6807 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
6809 fsp = NULL;
6811 TALLOC_FREE(plist);
6812 TALLOC_FREE(smb_fname);
6813 TALLOC_FREE(file_data);
6814 TALLOC_FREE(lines);
6815 return ok;
6818 struct fruit_disk_free_state {
6819 off_t total_size;
6822 static bool fruit_get_num_bands(vfs_handle_struct *handle,
6823 char *bundle,
6824 size_t *_nbands)
6826 char *path = NULL;
6827 struct smb_filename *bands_dir = NULL;
6828 DIR *d = NULL;
6829 struct dirent *e = NULL;
6830 size_t nbands;
6831 int ret;
6833 path = talloc_asprintf(talloc_tos(),
6834 "%s/%s/bands",
6835 handle->conn->connectpath,
6836 bundle);
6837 if (path == NULL) {
6838 return false;
6841 bands_dir = synthetic_smb_fname(talloc_tos(),
6842 path,
6843 NULL,
6844 NULL,
6846 TALLOC_FREE(path);
6847 if (bands_dir == NULL) {
6848 return false;
6851 d = SMB_VFS_NEXT_OPENDIR(handle, bands_dir, NULL, 0);
6852 if (d == NULL) {
6853 TALLOC_FREE(bands_dir);
6854 return false;
6857 nbands = 0;
6859 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
6860 e != NULL;
6861 e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
6863 if (ISDOT(e->d_name) || ISDOTDOT(e->d_name)) {
6864 continue;
6866 nbands++;
6869 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
6870 if (ret != 0) {
6871 TALLOC_FREE(bands_dir);
6872 return false;
6875 DBG_DEBUG("%zu bands in [%s]\n", nbands, smb_fname_str_dbg(bands_dir));
6877 TALLOC_FREE(bands_dir);
6879 *_nbands = nbands;
6880 return true;
6883 static bool fruit_tmsize_do_dirent(vfs_handle_struct *handle,
6884 struct fruit_disk_free_state *state,
6885 struct dirent *e)
6887 bool ok;
6888 char *p = NULL;
6889 size_t sparsebundle_strlen = strlen("sparsebundle");
6890 size_t bandsize = 0;
6891 size_t nbands;
6892 off_t tm_size;
6894 p = strstr(e->d_name, "sparsebundle");
6895 if (p == NULL) {
6896 return true;
6899 if (p[sparsebundle_strlen] != '\0') {
6900 return true;
6903 DBG_DEBUG("Processing sparsebundle [%s]\n", e->d_name);
6905 ok = fruit_get_bandsize(handle, e->d_name, &bandsize);
6906 if (!ok) {
6908 * Beware of race conditions: this may be an uninitialized
6909 * Info.plist that a client is just creating. We don't want let
6910 * this to trigger complete failure.
6912 DBG_ERR("Processing sparsebundle [%s] failed\n", e->d_name);
6913 return true;
6916 ok = fruit_get_num_bands(handle, e->d_name, &nbands);
6917 if (!ok) {
6919 * Beware of race conditions: this may be a backup sparsebundle
6920 * in an early stage lacking a bands subdirectory. We don't want
6921 * let this to trigger complete failure.
6923 DBG_ERR("Processing sparsebundle [%s] failed\n", e->d_name);
6924 return true;
6927 if (bandsize > SIZE_MAX/nbands) {
6928 DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
6929 bandsize, nbands);
6930 return false;
6932 tm_size = bandsize * nbands;
6934 if (state->total_size + tm_size < state->total_size) {
6935 DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
6936 bandsize, nbands);
6937 return false;
6940 state->total_size += tm_size;
6942 DBG_DEBUG("[%s] tm_size [%jd] total_size [%jd]\n",
6943 e->d_name, (intmax_t)tm_size, (intmax_t)state->total_size);
6945 return true;
6949 * Calculate used size of a TimeMachine volume
6951 * This assumes that the volume is used only for TimeMachine.
6953 * - readdir(basedir of share), then
6954 * - for every element that matches regex "^\(.*\)\.sparsebundle$" :
6955 * - parse "\1.sparsebundle/Info.plist" and read the band-size XML key
6956 * - count band files in "\1.sparsebundle/bands/"
6957 * - calculate used size of all bands: band_count * band_size
6959 static uint64_t fruit_disk_free(vfs_handle_struct *handle,
6960 const struct smb_filename *smb_fname,
6961 uint64_t *_bsize,
6962 uint64_t *_dfree,
6963 uint64_t *_dsize)
6965 struct fruit_config_data *config = NULL;
6966 struct fruit_disk_free_state state = {0};
6967 DIR *d = NULL;
6968 struct dirent *e = NULL;
6969 uint64_t dfree;
6970 uint64_t dsize;
6971 int ret;
6972 bool ok;
6974 SMB_VFS_HANDLE_GET_DATA(handle, config,
6975 struct fruit_config_data,
6976 return UINT64_MAX);
6978 if (!config->time_machine ||
6979 config->time_machine_max_size == 0)
6981 return SMB_VFS_NEXT_DISK_FREE(handle,
6982 smb_fname,
6983 _bsize,
6984 _dfree,
6985 _dsize);
6988 d = SMB_VFS_NEXT_OPENDIR(handle, smb_fname, NULL, 0);
6989 if (d == NULL) {
6990 return UINT64_MAX;
6993 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
6994 e != NULL;
6995 e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
6997 ok = fruit_tmsize_do_dirent(handle, &state, e);
6998 if (!ok) {
6999 SMB_VFS_NEXT_CLOSEDIR(handle, d);
7000 return UINT64_MAX;
7004 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
7005 if (ret != 0) {
7006 return UINT64_MAX;
7009 dsize = config->time_machine_max_size / 512;
7010 dfree = dsize - (state.total_size / 512);
7011 if (dfree > dsize) {
7012 dfree = 0;
7015 *_bsize = 512;
7016 *_dsize = dsize;
7017 *_dfree = dfree;
7018 return dfree / 2;
7021 static struct vfs_fn_pointers vfs_fruit_fns = {
7022 .connect_fn = fruit_connect,
7023 .disk_free_fn = fruit_disk_free,
7025 /* File operations */
7026 .chmod_fn = fruit_chmod,
7027 .chown_fn = fruit_chown,
7028 .unlink_fn = fruit_unlink,
7029 .rename_fn = fruit_rename,
7030 .rmdir_fn = fruit_rmdir,
7031 .open_fn = fruit_open,
7032 .pread_fn = fruit_pread,
7033 .pwrite_fn = fruit_pwrite,
7034 .pread_send_fn = fruit_pread_send,
7035 .pread_recv_fn = fruit_pread_recv,
7036 .pwrite_send_fn = fruit_pwrite_send,
7037 .pwrite_recv_fn = fruit_pwrite_recv,
7038 .stat_fn = fruit_stat,
7039 .lstat_fn = fruit_lstat,
7040 .fstat_fn = fruit_fstat,
7041 .streaminfo_fn = fruit_streaminfo,
7042 .ntimes_fn = fruit_ntimes,
7043 .ftruncate_fn = fruit_ftruncate,
7044 .fallocate_fn = fruit_fallocate,
7045 .create_file_fn = fruit_create_file,
7046 .readdir_attr_fn = fruit_readdir_attr,
7047 .offload_read_send_fn = fruit_offload_read_send,
7048 .offload_read_recv_fn = fruit_offload_read_recv,
7049 .offload_write_send_fn = fruit_offload_write_send,
7050 .offload_write_recv_fn = fruit_offload_write_recv,
7052 /* NT ACL operations */
7053 .fget_nt_acl_fn = fruit_fget_nt_acl,
7054 .fset_nt_acl_fn = fruit_fset_nt_acl,
7057 static_decl_vfs;
7058 NTSTATUS vfs_fruit_init(TALLOC_CTX *ctx)
7060 NTSTATUS ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "fruit",
7061 &vfs_fruit_fns);
7062 if (!NT_STATUS_IS_OK(ret)) {
7063 return ret;
7066 vfs_fruit_debug_level = debug_add_class("fruit");
7067 if (vfs_fruit_debug_level == -1) {
7068 vfs_fruit_debug_level = DBGC_VFS;
7069 DEBUG(0, ("%s: Couldn't register custom debugging class!\n",
7070 "vfs_fruit_init"));
7071 } else {
7072 DEBUG(10, ("%s: Debug class number of '%s': %d\n",
7073 "vfs_fruit_init","fruit",vfs_fruit_debug_level));
7076 return ret;