2 * OS X and Netatalk interoperability VFS module for Samba-3.x
4 * Copyright (C) Ralph Boehme, 2013, 2014
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
21 #include "MacExtensions.h"
22 #include "smbd/smbd.h"
23 #include "system/filesys.h"
24 #include "lib/util/time.h"
25 #include "system/shmem.h"
26 #include "locking/proto.h"
27 #include "smbd/globals.h"
29 #include "libcli/security/security.h"
30 #include "../libcli/smb/smb2_create_ctx.h"
31 #include "lib/util/sys_rw.h"
32 #include "lib/util/tevent_ntstatus.h"
33 #include "lib/util/tevent_unix.h"
34 #include "offload_token.h"
35 #include "string_replace.h"
37 #include <gnutls/gnutls.h>
38 #include <gnutls/crypto.h>
41 * Enhanced OS X and Netatalk compatibility
42 * ========================================
44 * This modules takes advantage of vfs_streams_xattr and
45 * vfs_catia. VFS modules vfs_fruit and vfs_streams_xattr must be
46 * loaded in the correct order:
48 * vfs modules = catia fruit streams_xattr
50 * The module intercepts the OS X special streams "AFP_AfpInfo" and
51 * "AFP_Resource" and handles them in a special way. All other named
52 * streams are deferred to vfs_streams_xattr.
54 * The OS X client maps all NTFS illegal characters to the Unicode
55 * private range. This module optionally stores the charcters using
56 * their native ASCII encoding using vfs_catia. If you're not enabling
57 * this feature, you can skip catia from vfs modules.
59 * Finally, open modes are optionally checked against Netatalk AFP
62 * The "AFP_AfpInfo" named stream is a binary blob containing OS X
63 * extended metadata for files and directories. This module optionally
64 * reads and stores this metadata in a way compatible with Netatalk 3
65 * which stores the metadata in an EA "org.netatalk.metadata". Cf
66 * source3/include/MacExtensions.h for a description of the binary
69 * The "AFP_Resource" named stream may be arbitrarily large, thus it
70 * can't be stored in an xattr on most filesystem. ZFS on Solaris is
71 * the only available filesystem where xattrs can be of any size and
72 * the OS supports using the file APIs for xattrs.
74 * The AFP_Resource stream is stored in an AppleDouble file prepending
75 * "._" to the filename. On Solaris with ZFS the stream is optionally
76 * stored in an EA "org.netatalk.resource".
82 * The OS X SMB client sends xattrs as ADS too. For xattr interop with
83 * other protocols you may want to adjust the xattr names the VFS
84 * module vfs_streams_xattr uses for storing ADS's. This defaults to
85 * user.DosStream.ADS_NAME:$DATA and can be changed by specifying
86 * these module parameters:
88 * streams_xattr:prefix = user.
89 * streams_xattr:store_stream_type = false
95 * - log diagnostic if any needed VFS module is not loaded
96 * (eg with lp_vfs_objects())
100 static int vfs_fruit_debug_level = DBGC_VFS;
102 static struct global_fruit_config {
103 bool nego_aapl; /* client negotiated AAPL */
105 } global_fruit_config;
108 #define DBGC_CLASS vfs_fruit_debug_level
110 #define FRUIT_PARAM_TYPE_NAME "fruit"
111 #define ADOUBLE_NAME_PREFIX "._"
113 #define NETATALK_META_XATTR "org.netatalk.Metadata"
114 #define NETATALK_RSRC_XATTR "org.netatalk.ResourceFork"
116 #if defined(HAVE_ATTROPEN)
117 #define AFPINFO_EA_NETATALK NETATALK_META_XATTR
118 #define AFPRESOURCE_EA_NETATALK NETATALK_RSRC_XATTR
120 #define AFPINFO_EA_NETATALK "user." NETATALK_META_XATTR
121 #define AFPRESOURCE_EA_NETATALK "user." NETATALK_RSRC_XATTR
124 enum apple_fork {APPLE_FORK_DATA, APPLE_FORK_RSRC};
126 enum fruit_rsrc {FRUIT_RSRC_STREAM, FRUIT_RSRC_ADFILE, FRUIT_RSRC_XATTR};
127 enum fruit_meta {FRUIT_META_STREAM, FRUIT_META_NETATALK};
128 enum fruit_locking {FRUIT_LOCKING_NETATALK, FRUIT_LOCKING_NONE};
129 enum fruit_encoding {FRUIT_ENC_NATIVE, FRUIT_ENC_PRIVATE};
131 struct fruit_config_data {
132 enum fruit_rsrc rsrc;
133 enum fruit_meta meta;
134 enum fruit_locking locking;
135 enum fruit_encoding encoding;
136 bool use_aapl; /* config from smb.conf */
138 bool readdir_attr_enabled;
139 bool unix_info_enabled;
140 bool copyfile_enabled;
141 bool veto_appledouble;
143 bool aapl_zero_file_id;
146 off_t time_machine_max_size;
147 bool wipe_intentionally_left_blank_rfork;
148 bool delete_empty_adfiles;
151 * Additional options, all enabled by default,
152 * possibly useful for analyzing performance. The associated
153 * operations with each of them may be expensive, so having
154 * the chance to disable them individually gives a chance
155 * tweaking the setup for the particular usecase.
157 bool readdir_attr_rsize;
158 bool readdir_attr_finder_info;
159 bool readdir_attr_max_access;
162 static const struct enum_list fruit_rsrc[] = {
163 {FRUIT_RSRC_STREAM, "stream"}, /* pass on to vfs_streams_xattr */
164 {FRUIT_RSRC_ADFILE, "file"}, /* ._ AppleDouble file */
165 {FRUIT_RSRC_XATTR, "xattr"}, /* Netatalk compatible xattr (ZFS only) */
169 static const struct enum_list fruit_meta[] = {
170 {FRUIT_META_STREAM, "stream"}, /* pass on to vfs_streams_xattr */
171 {FRUIT_META_NETATALK, "netatalk"}, /* Netatalk compatible xattr */
175 static const struct enum_list fruit_locking[] = {
176 {FRUIT_LOCKING_NETATALK, "netatalk"}, /* synchronize locks with Netatalk */
177 {FRUIT_LOCKING_NONE, "none"},
181 static const struct enum_list fruit_encoding[] = {
182 {FRUIT_ENC_NATIVE, "native"}, /* map unicode private chars to ASCII */
183 {FRUIT_ENC_PRIVATE, "private"}, /* keep unicode private chars */
187 static const char *fruit_catia_maps =
188 "0x01:0xf001,0x02:0xf002,0x03:0xf003,0x04:0xf004,"
189 "0x05:0xf005,0x06:0xf006,0x07:0xf007,0x08:0xf008,"
190 "0x09:0xf009,0x0a:0xf00a,0x0b:0xf00b,0x0c:0xf00c,"
191 "0x0d:0xf00d,0x0e:0xf00e,0x0f:0xf00f,0x10:0xf010,"
192 "0x11:0xf011,0x12:0xf012,0x13:0xf013,0x14:0xf014,"
193 "0x15:0xf015,0x16:0xf016,0x17:0xf017,0x18:0xf018,"
194 "0x19:0xf019,0x1a:0xf01a,0x1b:0xf01b,0x1c:0xf01c,"
195 "0x1d:0xf01d,0x1e:0xf01e,0x1f:0xf01f,"
196 "0x22:0xf020,0x2a:0xf021,0x3a:0xf022,0x3c:0xf023,"
197 "0x3e:0xf024,0x3f:0xf025,0x5c:0xf026,0x7c:0xf027,"
200 /*****************************************************************************
201 * Defines, functions and data structures that deal with AppleDouble
202 *****************************************************************************/
205 * There are two AppleDouble blobs we deal with:
207 * - ADOUBLE_META - AppleDouble blob used by Netatalk for storing
208 * metadata in an xattr
210 * - ADOUBLE_RSRC - AppleDouble blob used by OS X and Netatalk in
213 typedef enum {ADOUBLE_META, ADOUBLE_RSRC} adouble_type_t;
216 #define AD_VERSION2 0x00020000
217 #define AD_VERSION AD_VERSION2
220 * AppleDouble entry IDs.
222 #define ADEID_DFORK 1
223 #define ADEID_RFORK 2
225 #define ADEID_COMMENT 4
226 #define ADEID_ICONBW 5
227 #define ADEID_ICONCOL 6
228 #define ADEID_FILEI 7
229 #define ADEID_FILEDATESI 8
230 #define ADEID_FINDERI 9
231 #define ADEID_MACFILEI 10
232 #define ADEID_PRODOSFILEI 11
233 #define ADEID_MSDOSFILEI 12
234 #define ADEID_SHORTNAME 13
235 #define ADEID_AFPFILEI 14
238 /* Private Netatalk entries */
239 #define ADEID_PRIVDEV 16
240 #define ADEID_PRIVINO 17
241 #define ADEID_PRIVSYN 18
242 #define ADEID_PRIVID 19
243 #define ADEID_MAX (ADEID_PRIVID + 1)
246 * These are the real ids for the private entries,
247 * as stored in the adouble file
249 #define AD_DEV 0x80444556
250 #define AD_INO 0x80494E4F
251 #define AD_SYN 0x8053594E
252 #define AD_ID 0x8053567E
254 /* Number of actually used entries */
255 #define ADEID_NUM_XATTR 8
256 #define ADEID_NUM_DOT_UND 2
257 #define ADEID_NUM_RSRC_XATTR 1
259 /* AppleDouble magic */
260 #define AD_APPLESINGLE_MAGIC 0x00051600
261 #define AD_APPLEDOUBLE_MAGIC 0x00051607
262 #define AD_MAGIC AD_APPLEDOUBLE_MAGIC
264 /* Sizes of relevant entry bits */
265 #define ADEDLEN_MAGIC 4
266 #define ADEDLEN_VERSION 4
267 #define ADEDLEN_FILLER 16
268 #define AD_FILLER_TAG "Netatalk " /* should be 16 bytes */
269 #define AD_FILLER_TAG_OSX "Mac OS X " /* should be 16 bytes */
270 #define ADEDLEN_NENTRIES 2
271 #define AD_HEADER_LEN (ADEDLEN_MAGIC + ADEDLEN_VERSION + \
272 ADEDLEN_FILLER + ADEDLEN_NENTRIES) /* 26 */
273 #define AD_ENTRY_LEN_EID 4
274 #define AD_ENTRY_LEN_OFF 4
275 #define AD_ENTRY_LEN_LEN 4
276 #define AD_ENTRY_LEN (AD_ENTRY_LEN_EID + AD_ENTRY_LEN_OFF + AD_ENTRY_LEN_LEN)
279 #define ADEDLEN_NAME 255
280 #define ADEDLEN_COMMENT 200
281 #define ADEDLEN_FILEI 16
282 #define ADEDLEN_FINDERI 32
283 #define ADEDLEN_FILEDATESI 16
284 #define ADEDLEN_SHORTNAME 12 /* length up to 8.3 */
285 #define ADEDLEN_AFPFILEI 4
286 #define ADEDLEN_MACFILEI 4
287 #define ADEDLEN_PRODOSFILEI 8
288 #define ADEDLEN_MSDOSFILEI 2
289 #define ADEDLEN_DID 4
290 #define ADEDLEN_PRIVDEV 8
291 #define ADEDLEN_PRIVINO 8
292 #define ADEDLEN_PRIVSYN 8
293 #define ADEDLEN_PRIVID 4
296 #define ADEDOFF_MAGIC 0
297 #define ADEDOFF_VERSION (ADEDOFF_MAGIC + ADEDLEN_MAGIC)
298 #define ADEDOFF_FILLER (ADEDOFF_VERSION + ADEDLEN_VERSION)
299 #define ADEDOFF_NENTRIES (ADEDOFF_FILLER + ADEDLEN_FILLER)
301 #define ADEDOFF_FINDERI_XATTR (AD_HEADER_LEN + \
302 (ADEID_NUM_XATTR * AD_ENTRY_LEN))
303 #define ADEDOFF_COMMENT_XATTR (ADEDOFF_FINDERI_XATTR + ADEDLEN_FINDERI)
304 #define ADEDOFF_FILEDATESI_XATTR (ADEDOFF_COMMENT_XATTR + ADEDLEN_COMMENT)
305 #define ADEDOFF_AFPFILEI_XATTR (ADEDOFF_FILEDATESI_XATTR + \
307 #define ADEDOFF_PRIVDEV_XATTR (ADEDOFF_AFPFILEI_XATTR + ADEDLEN_AFPFILEI)
308 #define ADEDOFF_PRIVINO_XATTR (ADEDOFF_PRIVDEV_XATTR + ADEDLEN_PRIVDEV)
309 #define ADEDOFF_PRIVSYN_XATTR (ADEDOFF_PRIVINO_XATTR + ADEDLEN_PRIVINO)
310 #define ADEDOFF_PRIVID_XATTR (ADEDOFF_PRIVSYN_XATTR + ADEDLEN_PRIVSYN)
312 #define ADEDOFF_FINDERI_DOT_UND (AD_HEADER_LEN + \
313 (ADEID_NUM_DOT_UND * AD_ENTRY_LEN))
314 #define ADEDOFF_RFORK_DOT_UND (ADEDOFF_FINDERI_DOT_UND + ADEDLEN_FINDERI)
316 #define AD_DATASZ_XATTR (AD_HEADER_LEN + \
317 (ADEID_NUM_XATTR * AD_ENTRY_LEN) + \
318 ADEDLEN_FINDERI + ADEDLEN_COMMENT + \
319 ADEDLEN_FILEDATESI + ADEDLEN_AFPFILEI + \
320 ADEDLEN_PRIVDEV + ADEDLEN_PRIVINO + \
321 ADEDLEN_PRIVSYN + ADEDLEN_PRIVID)
323 #if AD_DATASZ_XATTR != 402
324 #error bad size for AD_DATASZ_XATTR
327 #define AD_DATASZ_DOT_UND (AD_HEADER_LEN + \
328 (ADEID_NUM_DOT_UND * AD_ENTRY_LEN) + \
330 #if AD_DATASZ_DOT_UND != 82
331 #error bad size for AD_DATASZ_DOT_UND
335 * Sharemode locks fcntl() offsets
337 #if _FILE_OFFSET_BITS == 64 || defined(HAVE_LARGEFILE)
338 #define AD_FILELOCK_BASE (UINT64_C(0x7FFFFFFFFFFFFFFF) - 9)
340 #define AD_FILELOCK_BASE (UINT32_C(0x7FFFFFFF) - 9)
342 #define BYTELOCK_MAX (AD_FILELOCK_BASE - 1)
344 #define AD_FILELOCK_OPEN_WR (AD_FILELOCK_BASE + 0)
345 #define AD_FILELOCK_OPEN_RD (AD_FILELOCK_BASE + 1)
346 #define AD_FILELOCK_RSRC_OPEN_WR (AD_FILELOCK_BASE + 2)
347 #define AD_FILELOCK_RSRC_OPEN_RD (AD_FILELOCK_BASE + 3)
348 #define AD_FILELOCK_DENY_WR (AD_FILELOCK_BASE + 4)
349 #define AD_FILELOCK_DENY_RD (AD_FILELOCK_BASE + 5)
350 #define AD_FILELOCK_RSRC_DENY_WR (AD_FILELOCK_BASE + 6)
351 #define AD_FILELOCK_RSRC_DENY_RD (AD_FILELOCK_BASE + 7)
352 #define AD_FILELOCK_OPEN_NONE (AD_FILELOCK_BASE + 8)
353 #define AD_FILELOCK_RSRC_OPEN_NONE (AD_FILELOCK_BASE + 9)
355 /* Time stuff we overload the bits a little */
356 #define AD_DATE_CREATE 0
357 #define AD_DATE_MODIFY 4
358 #define AD_DATE_BACKUP 8
359 #define AD_DATE_ACCESS 12
360 #define AD_DATE_MASK (AD_DATE_CREATE | AD_DATE_MODIFY | \
361 AD_DATE_BACKUP | AD_DATE_ACCESS)
362 #define AD_DATE_UNIX (1 << 10)
363 #define AD_DATE_START 0x80000000
364 #define AD_DATE_DELTA 946684800
365 #define AD_DATE_FROM_UNIX(x) (htonl((x) - AD_DATE_DELTA))
366 #define AD_DATE_TO_UNIX(x) (ntohl(x) + AD_DATE_DELTA)
368 #define AD_XATTR_HDR_MAGIC 0x41545452 /* 'ATTR' */
369 #define AD_XATTR_MAX_ENTRIES 1024 /* Some arbitrarily enforced limit */
370 #define AD_XATTR_HDR_SIZE 36
371 #define AD_XATTR_MAX_HDR_SIZE 65536
373 /* Accessor macros */
374 #define ad_getentrylen(ad,eid) ((ad)->ad_eid[(eid)].ade_len)
375 #define ad_getentryoff(ad,eid) ((ad)->ad_eid[(eid)].ade_off)
376 #define ad_setentrylen(ad,eid,len) ((ad)->ad_eid[(eid)].ade_len = (len))
377 #define ad_setentryoff(ad,eid,off) ((ad)->ad_eid[(eid)].ade_off = (off))
380 * Both struct ad_xattr_header and struct ad_xattr_entry describe the in memory
381 * representation as well as the on-disk format.
383 * The ad_xattr_header follows the FinderInfo data in the FinderInfo entry if
384 * the length of the FinderInfo entry is larger then 32 bytes. It is then
385 * preceeded with 2 bytes padding.
387 * Cf: https://opensource.apple.com/source/xnu/xnu-4570.1.46/bsd/vfs/vfs_xattr.c
390 struct ad_xattr_header {
391 uint32_t adx_magic; /* ATTR_HDR_MAGIC */
392 uint32_t adx_debug_tag; /* for debugging == file id of owning file */
393 uint32_t adx_total_size; /* file offset of end of attribute header + entries + data */
394 uint32_t adx_data_start; /* file offset to attribute data area */
395 uint32_t adx_data_length; /* length of attribute data area */
396 uint32_t adx_reserved[3];
398 uint16_t adx_num_attrs;
401 /* On-disk entries are aligned on 4 byte boundaries */
402 struct ad_xattr_entry {
403 uint32_t adx_offset; /* file offset to data */
404 uint32_t adx_length; /* size of attribute data */
406 uint8_t adx_namelen; /* included the NULL terminator */
407 char *adx_name; /* NULL-terminated UTF-8 name */
418 adouble_type_t ad_type;
421 uint8_t ad_filler[ADEDLEN_FILLER];
422 struct ad_entry ad_eid[ADEID_MAX];
424 struct ad_xattr_header adx_header;
425 struct ad_xattr_entry *adx_entries;
428 struct ad_entry_order {
429 uint32_t id, offset, len;
432 /* Netatalk AppleDouble metadata xattr */
434 struct ad_entry_order entry_order_meta_xattr[ADEID_NUM_XATTR + 1] = {
435 {ADEID_FINDERI, ADEDOFF_FINDERI_XATTR, ADEDLEN_FINDERI},
436 {ADEID_COMMENT, ADEDOFF_COMMENT_XATTR, 0},
437 {ADEID_FILEDATESI, ADEDOFF_FILEDATESI_XATTR, ADEDLEN_FILEDATESI},
438 {ADEID_AFPFILEI, ADEDOFF_AFPFILEI_XATTR, ADEDLEN_AFPFILEI},
439 {ADEID_PRIVDEV, ADEDOFF_PRIVDEV_XATTR, 0},
440 {ADEID_PRIVINO, ADEDOFF_PRIVINO_XATTR, 0},
441 {ADEID_PRIVSYN, ADEDOFF_PRIVSYN_XATTR, 0},
442 {ADEID_PRIVID, ADEDOFF_PRIVID_XATTR, 0},
446 /* AppleDouble resource fork file (the ones prefixed by "._") */
448 struct ad_entry_order entry_order_dot_und[ADEID_NUM_DOT_UND + 1] = {
449 {ADEID_FINDERI, ADEDOFF_FINDERI_DOT_UND, ADEDLEN_FINDERI},
450 {ADEID_RFORK, ADEDOFF_RFORK_DOT_UND, 0},
455 * Fake AppleDouble entry oder for resource fork xattr. The xattr
456 * isn't an AppleDouble file, it simply contains the resource data,
457 * but in order to be able to use some API calls like ad_getentryoff()
458 * we build a fake/helper struct adouble with this entry order struct.
461 struct ad_entry_order entry_order_rsrc_xattr[ADEID_NUM_RSRC_XATTR + 1] = {
466 /* Conversion from enumerated id to on-disk AppleDouble id */
467 #define AD_EID_DISK(a) (set_eid[a])
468 static const uint32_t set_eid[] = {
469 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
470 AD_DEV, AD_INO, AD_SYN, AD_ID
473 static char empty_resourcefork[] = {
474 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
475 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
476 0x54, 0x68, 0x69, 0x73, 0x20, 0x72, 0x65, 0x73,
477 0x6F, 0x75, 0x72, 0x63, 0x65, 0x20, 0x66, 0x6F,
478 0x72, 0x6B, 0x20, 0x69, 0x6E, 0x74, 0x65, 0x6E,
479 0x74, 0x69, 0x6F, 0x6E, 0x61, 0x6C, 0x6C, 0x79,
480 0x20, 0x6C, 0x65, 0x66, 0x74, 0x20, 0x62, 0x6C,
481 0x61, 0x6E, 0x6B, 0x20, 0x20, 0x20, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
506 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
507 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
508 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
509 0x00, 0x1C, 0x00, 0x1E, 0xFF, 0xFF
513 /* tcon config handle */
514 struct fruit_config_data *config;
516 /* Denote stream type, meta or rsrc */
519 /* Whether the create created the stream */
523 * AFP_AfpInfo stream created, but not written yet, thus still a fake
524 * pipe fd. This is set to true in fruit_open_meta if there was no
525 * exisiting stream but the caller requested O_CREAT. It is later set to
526 * false when we get a write on the stream that then does open and
535 * Forward declarations
537 static struct adouble *ad_init(TALLOC_CTX *ctx, vfs_handle_struct *handle,
538 adouble_type_t type);
539 static struct adouble *ad_get(TALLOC_CTX *ctx,
540 vfs_handle_struct *handle,
541 const struct smb_filename *smb_fname,
542 adouble_type_t type);
543 static int ad_set(vfs_handle_struct *handle,
545 const struct smb_filename *smb_fname);
546 static int ad_fset(struct vfs_handle_struct *handle,
549 static int adouble_path(TALLOC_CTX *ctx,
550 const struct smb_filename *smb_fname__in,
551 struct smb_filename **ppsmb_fname_out);
552 static AfpInfo *afpinfo_new(TALLOC_CTX *ctx);
553 static ssize_t afpinfo_pack(const AfpInfo *ai, char *buf);
554 static AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data);
558 * Return a pointer to an AppleDouble entry
560 * Returns NULL if the entry is not present
562 static char *ad_get_entry(const struct adouble *ad, int eid)
564 off_t off = ad_getentryoff(ad, eid);
565 size_t len = ad_getentrylen(ad, eid);
567 if (off == 0 || len == 0) {
571 return ad->ad_data + off;
577 static int ad_getdate(const struct adouble *ad,
578 unsigned int dateoff,
581 bool xlate = (dateoff & AD_DATE_UNIX);
584 dateoff &= AD_DATE_MASK;
585 p = ad_get_entry(ad, ADEID_FILEDATESI);
590 if (dateoff > AD_DATE_ACCESS) {
594 memcpy(date, p + dateoff, sizeof(uint32_t));
597 *date = AD_DATE_TO_UNIX(*date);
605 static int ad_setdate(struct adouble *ad, unsigned int dateoff, uint32_t date)
607 bool xlate = (dateoff & AD_DATE_UNIX);
610 p = ad_get_entry(ad, ADEID_FILEDATESI);
615 dateoff &= AD_DATE_MASK;
617 date = AD_DATE_FROM_UNIX(date);
620 if (dateoff > AD_DATE_ACCESS) {
624 memcpy(p + dateoff, &date, sizeof(date));
631 * Map on-disk AppleDouble id to enumerated id
633 static uint32_t get_eid(uint32_t eid)
641 return ADEID_PRIVDEV;
643 return ADEID_PRIVINO;
645 return ADEID_PRIVSYN;
656 * Pack AppleDouble structure into data buffer
658 static bool ad_pack(struct adouble *ad)
665 bufsize = talloc_get_size(ad->ad_data);
666 if (bufsize < AD_DATASZ_DOT_UND) {
667 DBG_ERR("bad buffer size [0x%" PRIx32 "]\n", bufsize);
671 if (offset + ADEDLEN_MAGIC < offset ||
672 offset + ADEDLEN_MAGIC >= bufsize) {
675 RSIVAL(ad->ad_data, offset, ad->ad_magic);
676 offset += ADEDLEN_MAGIC;
678 if (offset + ADEDLEN_VERSION < offset ||
679 offset + ADEDLEN_VERSION >= bufsize) {
682 RSIVAL(ad->ad_data, offset, ad->ad_version);
683 offset += ADEDLEN_VERSION;
685 if (offset + ADEDLEN_FILLER < offset ||
686 offset + ADEDLEN_FILLER >= bufsize) {
689 if (ad->ad_type == ADOUBLE_RSRC) {
690 memcpy(ad->ad_data + offset, AD_FILLER_TAG, ADEDLEN_FILLER);
692 offset += ADEDLEN_FILLER;
694 if (offset + ADEDLEN_NENTRIES < offset ||
695 offset + ADEDLEN_NENTRIES >= bufsize) {
698 offset += ADEDLEN_NENTRIES;
700 for (eid = 0, nent = 0; eid < ADEID_MAX; eid++) {
701 if (ad->ad_eid[eid].ade_off == 0) {
703 * ade_off is also used as indicator whether a
704 * specific entry is used or not
709 if (offset + AD_ENTRY_LEN_EID < offset ||
710 offset + AD_ENTRY_LEN_EID >= bufsize) {
713 RSIVAL(ad->ad_data, offset, AD_EID_DISK(eid));
714 offset += AD_ENTRY_LEN_EID;
716 if (offset + AD_ENTRY_LEN_OFF < offset ||
717 offset + AD_ENTRY_LEN_OFF >= bufsize) {
720 RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_off);
721 offset += AD_ENTRY_LEN_OFF;
723 if (offset + AD_ENTRY_LEN_LEN < offset ||
724 offset + AD_ENTRY_LEN_LEN >= bufsize) {
727 RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_len);
728 offset += AD_ENTRY_LEN_LEN;
733 if (ADEDOFF_NENTRIES + 2 >= bufsize) {
736 RSSVAL(ad->ad_data, ADEDOFF_NENTRIES, nent);
741 static bool ad_unpack_xattrs(struct adouble *ad)
743 struct ad_xattr_header *h = &ad->adx_header;
744 const char *p = ad->ad_data;
748 if (ad_getentrylen(ad, ADEID_FINDERI) <= ADEDLEN_FINDERI) {
752 /* 2 bytes padding */
753 hoff = ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI + 2;
755 h->adx_magic = RIVAL(p, hoff + 0);
756 h->adx_debug_tag = RIVAL(p, hoff + 4); /* Not used -> not checked */
757 h->adx_total_size = RIVAL(p, hoff + 8);
758 h->adx_data_start = RIVAL(p, hoff + 12);
759 h->adx_data_length = RIVAL(p, hoff + 16);
760 h->adx_flags = RSVAL(p, hoff + 32); /* Not used -> not checked */
761 h->adx_num_attrs = RSVAL(p, hoff + 34);
763 if (h->adx_magic != AD_XATTR_HDR_MAGIC) {
764 DBG_ERR("Bad magic: 0x%" PRIx32 "\n", h->adx_magic);
768 if (h->adx_total_size > ad_getentryoff(ad, ADEID_RFORK)) {
769 DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size);
772 if (h->adx_total_size > AD_XATTR_MAX_HDR_SIZE) {
773 DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size);
777 if (h->adx_data_start < (hoff + AD_XATTR_HDR_SIZE)) {
778 DBG_ERR("Bad start: 0x%" PRIx32 "\n", h->adx_data_start);
782 if ((h->adx_data_start + h->adx_data_length) < h->adx_data_start) {
783 DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length);
786 if ((h->adx_data_start + h->adx_data_length) >
787 ad->adx_header.adx_total_size)
789 DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length);
793 if (h->adx_num_attrs > AD_XATTR_MAX_ENTRIES) {
794 DBG_ERR("Bad num xattrs: %" PRIu16 "\n", h->adx_num_attrs);
798 if (h->adx_num_attrs == 0) {
802 ad->adx_entries = talloc_zero_array(
803 ad, struct ad_xattr_entry, h->adx_num_attrs);
804 if (ad->adx_entries == NULL) {
808 hoff += AD_XATTR_HDR_SIZE;
810 for (i = 0; i < h->adx_num_attrs; i++) {
811 struct ad_xattr_entry *e = &ad->adx_entries[i];
813 hoff = (hoff + 3) & ~3;
815 e->adx_offset = RIVAL(p, hoff + 0);
816 e->adx_length = RIVAL(p, hoff + 4);
817 e->adx_flags = RSVAL(p, hoff + 8);
818 e->adx_namelen = *(p + hoff + 10);
820 if (e->adx_offset >= ad->adx_header.adx_total_size) {
821 DBG_ERR("Bad adx_offset: %" PRIx32 "\n",
826 if ((e->adx_offset + e->adx_length) < e->adx_offset) {
827 DBG_ERR("Bad adx_length: %" PRIx32 "\n",
832 if ((e->adx_offset + e->adx_length) >
833 ad->adx_header.adx_total_size)
835 DBG_ERR("Bad adx_length: %" PRIx32 "\n",
840 if (e->adx_namelen == 0) {
841 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
845 if ((hoff + 11 + e->adx_namelen) < hoff + 11) {
846 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
850 if ((hoff + 11 + e->adx_namelen) >
851 ad->adx_header.adx_data_start)
853 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
858 e->adx_name = talloc_strndup(ad->adx_entries,
861 if (e->adx_name == NULL) {
865 DBG_DEBUG("xattr [%s] offset [0x%x] size [0x%x]\n",
866 e->adx_name, e->adx_offset, e->adx_length);
867 dump_data(10, (uint8_t *)(ad->ad_data + e->adx_offset),
870 hoff += 11 + e->adx_namelen;
877 * Unpack an AppleDouble blob into a struct adoble
879 static bool ad_unpack(struct adouble *ad, const size_t nentries,
882 size_t bufsize = talloc_get_size(ad->ad_data);
884 uint32_t eid, len, off;
888 * The size of the buffer ad->ad_data is checked when read, so
889 * we wouldn't have to check our own offsets, a few extra
890 * checks won't hurt though. We have to check the offsets we
891 * read from the buffer anyway.
894 if (bufsize < (AD_HEADER_LEN + (AD_ENTRY_LEN * nentries))) {
895 DEBUG(1, ("bad size\n"));
899 ad->ad_magic = RIVAL(ad->ad_data, 0);
900 ad->ad_version = RIVAL(ad->ad_data, ADEDOFF_VERSION);
901 if ((ad->ad_magic != AD_MAGIC) || (ad->ad_version != AD_VERSION)) {
902 DEBUG(1, ("wrong magic or version\n"));
906 memcpy(ad->ad_filler, ad->ad_data + ADEDOFF_FILLER, ADEDLEN_FILLER);
908 adentries = RSVAL(ad->ad_data, ADEDOFF_NENTRIES);
909 if (adentries != nentries) {
910 DEBUG(1, ("invalid number of entries: %zu\n",
915 /* now, read in the entry bits */
916 for (i = 0; i < adentries; i++) {
917 eid = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN));
919 off = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 4);
920 len = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 8);
922 if (!eid || eid >= ADEID_MAX) {
923 DEBUG(1, ("bogus eid %d\n", eid));
928 * All entries other than the resource fork are
929 * expected to be read into the ad_data buffer, so
930 * ensure the specified offset is within that bound
932 if ((off > bufsize) && (eid != ADEID_RFORK)) {
933 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
939 * All entries besides FinderInfo and resource fork
940 * must fit into the buffer. FinderInfo is special as
941 * it may be larger then the default 32 bytes (if it
942 * contains marshalled xattrs), but we will fixup that
943 * in ad_convert(). And the resource fork is never
944 * accessed directly by the ad_data buf (also see
945 * comment above) anyway.
947 if ((eid != ADEID_RFORK) &&
948 (eid != ADEID_FINDERI) &&
949 ((off + len) > bufsize)) {
950 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
956 * That would be obviously broken
958 if (off > filesize) {
959 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
965 * Check for any entry that has its end beyond the
968 if (off + len < off) {
969 DEBUG(1, ("offset wrap in eid %d: off: %" PRIu32
970 ", len: %" PRIu32 "\n",
975 if (off + len > filesize) {
977 * If this is the resource fork entry, we fix
978 * up the length, for any other entry we bail
981 if (eid != ADEID_RFORK) {
982 DEBUG(1, ("bogus eid %d: off: %" PRIu32
983 ", len: %" PRIu32 "\n",
989 * Fixup the resource fork entry by limiting
990 * the size to entryoffset - filesize.
992 len = filesize - off;
993 DEBUG(1, ("Limiting ADEID_RFORK: off: %" PRIu32
994 ", len: %" PRIu32 "\n", off, len));
997 ad->ad_eid[eid].ade_off = off;
998 ad->ad_eid[eid].ade_len = len;
1001 ok = ad_unpack_xattrs(ad);
1009 static bool ad_convert_move_reso(struct adouble *ad,
1010 const struct smb_filename *smb_fname)
1012 char *map = MAP_FAILED;
1018 if (ad_getentrylen(ad, ADEID_RFORK) == 0) {
1022 maplen = ad_getentryoff(ad, ADEID_RFORK) +
1023 ad_getentrylen(ad, ADEID_RFORK);
1025 /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
1026 map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
1028 if (map == MAP_FAILED) {
1029 DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
1034 memmove(map + ADEDOFF_RFORK_DOT_UND,
1035 map + ad_getentryoff(ad, ADEID_RFORK),
1036 ad_getentrylen(ad, ADEID_RFORK));
1038 rc = munmap(map, maplen);
1040 DBG_ERR("munmap failed: %s\n", strerror(errno));
1044 ad_setentryoff(ad, ADEID_RFORK, ADEDOFF_RFORK_DOT_UND);
1048 DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name);
1052 len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
1053 if (len != AD_DATASZ_DOT_UND) {
1054 DBG_ERR("%s: bad size: %zd\n", smb_fname->base_name, len);
1061 static bool ad_convert_xattr(vfs_handle_struct *handle,
1063 const struct smb_filename *smb_fname,
1064 bool *converted_xattr)
1066 static struct char_mappings **string_replace_cmaps = NULL;
1067 char *map = MAP_FAILED;
1071 int saved_errno = 0;
1076 *converted_xattr = false;
1078 if (ad_getentrylen(ad, ADEID_FINDERI) == ADEDLEN_FINDERI) {
1082 if (string_replace_cmaps == NULL) {
1083 const char **mappings = NULL;
1085 mappings = str_list_make_v3_const(
1086 talloc_tos(), fruit_catia_maps, NULL);
1087 if (mappings == NULL) {
1090 string_replace_cmaps = string_replace_init_map(mappings);
1091 TALLOC_FREE(mappings);
1094 maplen = ad_getentryoff(ad, ADEID_RFORK) +
1095 ad_getentrylen(ad, ADEID_RFORK);
1097 /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
1098 map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
1100 if (map == MAP_FAILED) {
1101 DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
1105 for (i = 0; i < ad->adx_header.adx_num_attrs; i++) {
1106 struct ad_xattr_entry *e = &ad->adx_entries[i];
1107 char *mapped_name = NULL;
1109 struct smb_filename *stream_name = NULL;
1110 files_struct *fsp = NULL;
1113 status = string_replace_allocate(handle->conn,
1115 string_replace_cmaps,
1118 vfs_translate_to_windows);
1119 if (!NT_STATUS_IS_OK(status) &&
1120 !NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))
1122 DBG_ERR("string_replace_allocate failed\n");
1128 mapped_name = talloc_asprintf(talloc_tos(), ":%s", tmp);
1130 if (mapped_name == NULL) {
1135 stream_name = synthetic_smb_fname(talloc_tos(),
1136 smb_fname->base_name,
1140 TALLOC_FREE(mapped_name);
1141 if (stream_name == NULL) {
1142 DBG_ERR("synthetic_smb_fname failed\n");
1147 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1149 status = SMB_VFS_CREATE_FILE(
1150 handle->conn, /* conn */
1152 0, /* root_dir_fid */
1153 stream_name, /* fname */
1154 FILE_GENERIC_WRITE, /* access_mask */
1155 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
1156 FILE_OPEN_IF, /* create_disposition */
1157 0, /* create_options */
1158 0, /* file_attributes */
1159 INTERNAL_OPEN_ONLY, /* oplock_request */
1161 0, /* allocation_size */
1162 0, /* private_flags */
1167 NULL, NULL); /* create context */
1168 TALLOC_FREE(stream_name);
1169 if (!NT_STATUS_IS_OK(status)) {
1170 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1175 nwritten = SMB_VFS_PWRITE(fsp,
1176 map + e->adx_offset,
1179 if (nwritten == -1) {
1180 DBG_ERR("SMB_VFS_PWRITE failed\n");
1181 saved_errno = errno;
1182 close_file(NULL, fsp, ERROR_CLOSE);
1183 errno = saved_errno;
1188 status = close_file(NULL, fsp, NORMAL_CLOSE);
1189 if (!NT_STATUS_IS_OK(status)) {
1196 ad_setentrylen(ad, ADEID_FINDERI, ADEDLEN_FINDERI);
1200 DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name);
1204 len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
1205 if (len != AD_DATASZ_DOT_UND) {
1206 DBG_ERR("%s: bad size: %zd\n", smb_fname->base_name, len);
1211 ok = ad_convert_move_reso(ad, smb_fname);
1216 *converted_xattr = true;
1220 rc = munmap(map, maplen);
1222 DBG_ERR("munmap failed: %s\n", strerror(errno));
1229 static bool ad_convert_finderinfo(vfs_handle_struct *handle,
1231 const struct smb_filename *smb_fname)
1236 struct smb_filename *stream_name = NULL;
1237 files_struct *fsp = NULL;
1241 int saved_errno = 0;
1244 cmp = memcmp(ad->ad_filler, AD_FILLER_TAG_OSX, ADEDLEN_FILLER);
1249 p_ad = ad_get_entry(ad, ADEID_FINDERI);
1254 ai = afpinfo_new(talloc_tos());
1259 memcpy(ai->afpi_FinderInfo, p_ad, ADEDLEN_FINDERI);
1261 aiblob = data_blob_talloc(talloc_tos(), NULL, AFP_INFO_SIZE);
1262 if (aiblob.data == NULL) {
1267 size = afpinfo_pack(ai, (char *)aiblob.data);
1269 if (size != AFP_INFO_SIZE) {
1273 stream_name = synthetic_smb_fname(talloc_tos(),
1274 smb_fname->base_name,
1278 if (stream_name == NULL) {
1279 data_blob_free(&aiblob);
1280 DBG_ERR("synthetic_smb_fname failed\n");
1284 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1286 status = SMB_VFS_CREATE_FILE(
1287 handle->conn, /* conn */
1289 0, /* root_dir_fid */
1290 stream_name, /* fname */
1291 FILE_GENERIC_WRITE, /* access_mask */
1292 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
1293 FILE_OPEN_IF, /* create_disposition */
1294 0, /* create_options */
1295 0, /* file_attributes */
1296 INTERNAL_OPEN_ONLY, /* oplock_request */
1298 0, /* allocation_size */
1299 0, /* private_flags */
1304 NULL, NULL); /* create context */
1305 TALLOC_FREE(stream_name);
1306 if (!NT_STATUS_IS_OK(status)) {
1307 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1311 nwritten = SMB_VFS_PWRITE(fsp,
1315 if (nwritten == -1) {
1316 DBG_ERR("SMB_VFS_PWRITE failed\n");
1317 saved_errno = errno;
1318 close_file(NULL, fsp, ERROR_CLOSE);
1319 errno = saved_errno;
1323 status = close_file(NULL, fsp, NORMAL_CLOSE);
1324 if (!NT_STATUS_IS_OK(status)) {
1332 static bool ad_convert_truncate(struct adouble *ad,
1333 const struct smb_filename *smb_fname)
1338 * FIXME: direct ftruncate(), but we don't have a fsp for the
1341 rc = ftruncate(ad->ad_fd, ADEDOFF_RFORK_DOT_UND +
1342 ad_getentrylen(ad, ADEID_RFORK));
1350 static bool ad_convert_blank_rfork(vfs_handle_struct *handle,
1354 struct fruit_config_data *config = NULL;
1355 uint8_t *map = MAP_FAILED;
1364 SMB_VFS_HANDLE_GET_DATA(handle, config,
1365 struct fruit_config_data, return false);
1367 if (!config->wipe_intentionally_left_blank_rfork) {
1371 if (ad_getentrylen(ad, ADEID_RFORK) != sizeof(empty_resourcefork)) {
1375 maplen = ad_getentryoff(ad, ADEID_RFORK) +
1376 ad_getentrylen(ad, ADEID_RFORK);
1378 /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
1379 map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
1381 if (map == MAP_FAILED) {
1382 DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
1386 cmp = memcmp(map + ADEDOFF_RFORK_DOT_UND,
1388 sizeof(empty_resourcefork));
1389 rc = munmap(map, maplen);
1391 DBG_ERR("munmap failed: %s\n", strerror(errno));
1399 ad_setentrylen(ad, ADEID_RFORK, 0);
1406 len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
1407 if (len != AD_DATASZ_DOT_UND) {
1415 static bool ad_convert_delete_adfile(vfs_handle_struct *handle,
1417 const struct smb_filename *smb_fname)
1419 struct fruit_config_data *config = NULL;
1420 struct smb_filename *ad_name = NULL;
1423 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
1427 SMB_VFS_HANDLE_GET_DATA(handle, config,
1428 struct fruit_config_data, return false);
1430 if (!config->delete_empty_adfiles) {
1434 rc = adouble_path(talloc_tos(), smb_fname, &ad_name);
1439 rc = SMB_VFS_NEXT_UNLINK(handle, ad_name);
1441 DBG_ERR("Unlinking [%s] failed: %s\n",
1442 smb_fname_str_dbg(ad_name), strerror(errno));
1443 TALLOC_FREE(ad_name);
1447 DBG_WARNING("Unlinked [%s] after conversion\n", smb_fname_str_dbg(ad_name));
1448 TALLOC_FREE(ad_name);
1454 * Convert from Apple's ._ file to Netatalk
1456 * Apple's AppleDouble may contain a FinderInfo entry longer then 32
1457 * bytes containing packed xattrs.
1459 * @return -1 in case an error occurred, 0 if no conversion was done, 1
1462 static int ad_convert(struct vfs_handle_struct *handle,
1463 const struct smb_filename *smb_fname)
1465 struct adouble *ad = NULL;
1467 bool converted_xattr = false;
1471 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
1476 ok = ad_convert_xattr(handle, ad, smb_fname, &converted_xattr);
1482 ok = ad_convert_blank_rfork(handle, ad, &blank);
1488 if (converted_xattr || blank) {
1489 ok = ad_convert_truncate(ad, smb_fname);
1496 ok = ad_convert_finderinfo(handle, ad, smb_fname);
1498 DBG_ERR("Failed to convert [%s]\n",
1499 smb_fname_str_dbg(smb_fname));
1504 ok = ad_convert_delete_adfile(handle, ad, smb_fname);
1517 * Read and parse Netatalk AppleDouble metadata xattr
1519 static ssize_t ad_read_meta(vfs_handle_struct *handle,
1521 const struct smb_filename *smb_fname)
1527 DEBUG(10, ("reading meta xattr for %s\n", smb_fname->base_name));
1529 ealen = SMB_VFS_GETXATTR(handle->conn, smb_fname,
1530 AFPINFO_EA_NETATALK, ad->ad_data,
1536 if (errno == ENOATTR) {
1542 DEBUG(2, ("error reading meta xattr: %s\n",
1548 if (ealen != AD_DATASZ_XATTR) {
1549 DEBUG(2, ("bad size %zd\n", ealen));
1555 /* Now parse entries */
1556 ok = ad_unpack(ad, ADEID_NUM_XATTR, AD_DATASZ_XATTR);
1558 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1564 if (!ad_getentryoff(ad, ADEID_FINDERI)
1565 || !ad_getentryoff(ad, ADEID_COMMENT)
1566 || !ad_getentryoff(ad, ADEID_FILEDATESI)
1567 || !ad_getentryoff(ad, ADEID_AFPFILEI)
1568 || !ad_getentryoff(ad, ADEID_PRIVDEV)
1569 || !ad_getentryoff(ad, ADEID_PRIVINO)
1570 || !ad_getentryoff(ad, ADEID_PRIVSYN)
1571 || !ad_getentryoff(ad, ADEID_PRIVID)) {
1572 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1579 DEBUG(10, ("reading meta xattr for %s, rc: %d\n",
1580 smb_fname->base_name, rc));
1584 if (errno == EINVAL) {
1586 (void)SMB_VFS_REMOVEXATTR(handle->conn,
1588 AFPINFO_EA_NETATALK);
1596 static int ad_open_rsrc_adouble(const struct smb_filename *smb_fname,
1602 struct smb_filename *adp_smb_fname = NULL;
1604 ret = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
1609 fd = open(adp_smb_fname->base_name, flags, mode);
1610 TALLOC_FREE(adp_smb_fname);
1615 static int ad_open_rsrc(vfs_handle_struct *handle,
1616 const struct smb_filename *smb_fname,
1620 return ad_open_rsrc_adouble(smb_fname, flags, mode);
1624 * Here's the deal: for ADOUBLE_META we can do without an fd as we can issue
1625 * path based xattr calls. For ADOUBLE_RSRC however we need a full-fledged fd
1626 * for file IO on the ._ file.
1628 static int ad_open(vfs_handle_struct *handle,
1631 const struct smb_filename *smb_fname,
1637 DBG_DEBUG("Path [%s] type [%s]\n", smb_fname->base_name,
1638 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
1640 if (ad->ad_type == ADOUBLE_META) {
1644 if ((fsp != NULL) && (fsp->fh != NULL) && (fsp->fh->fd != -1)) {
1645 ad->ad_fd = fsp->fh->fd;
1646 ad->ad_opened = false;
1650 fd = ad_open_rsrc(handle, smb_fname, flags, mode);
1654 ad->ad_opened = true;
1657 DBG_DEBUG("Path [%s] type [%s] fd [%d]\n",
1658 smb_fname->base_name,
1659 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc", fd);
1664 static ssize_t ad_read_rsrc_adouble(vfs_handle_struct *handle,
1666 const struct smb_filename *smb_fname)
1668 SMB_STRUCT_STAT sbuf;
1675 ret = sys_fstat(ad->ad_fd, &sbuf, lp_fake_directory_create_times(
1676 SNUM(handle->conn)));
1682 * AppleDouble file header content and size, two cases:
1684 * - without xattrs it is exactly AD_DATASZ_DOT_UND (82) bytes large
1685 * - with embedded xattrs it can be larger, up to AD_XATTR_MAX_HDR_SIZE
1687 * Read as much as we can up to AD_XATTR_MAX_HDR_SIZE.
1689 size = sbuf.st_ex_size;
1690 if (size > talloc_array_length(ad->ad_data)) {
1691 if (size > AD_XATTR_MAX_HDR_SIZE) {
1692 size = AD_XATTR_MAX_HDR_SIZE;
1694 p_ad = talloc_realloc(ad, ad->ad_data, char, size);
1701 len = sys_pread(ad->ad_fd, ad->ad_data,
1702 talloc_array_length(ad->ad_data), 0);
1703 if (len != talloc_array_length(ad->ad_data)) {
1704 DBG_NOTICE("%s %s: bad size: %zd\n",
1705 smb_fname->base_name, strerror(errno), len);
1709 /* Now parse entries */
1710 ok = ad_unpack(ad, ADEID_NUM_DOT_UND, sbuf.st_ex_size);
1712 DBG_ERR("invalid AppleDouble resource %s\n",
1713 smb_fname->base_name);
1718 if ((ad_getentryoff(ad, ADEID_FINDERI) != ADEDOFF_FINDERI_DOT_UND)
1719 || (ad_getentrylen(ad, ADEID_FINDERI) < ADEDLEN_FINDERI)
1720 || (ad_getentryoff(ad, ADEID_RFORK) < ADEDOFF_RFORK_DOT_UND)) {
1721 DBG_ERR("invalid AppleDouble resource %s\n",
1722 smb_fname->base_name);
1731 * Read and parse resource fork, either ._ AppleDouble file or xattr
1733 static ssize_t ad_read_rsrc(vfs_handle_struct *handle,
1735 const struct smb_filename *smb_fname)
1737 return ad_read_rsrc_adouble(handle, ad, smb_fname);
1741 * Read and unpack an AppleDouble metadata xattr or resource
1743 static ssize_t ad_read(vfs_handle_struct *handle,
1745 const struct smb_filename *smb_fname)
1747 switch (ad->ad_type) {
1749 return ad_read_meta(handle, ad, smb_fname);
1751 return ad_read_rsrc(handle, ad, smb_fname);
1757 static int adouble_destructor(struct adouble *ad)
1759 if ((ad->ad_fd != -1) && ad->ad_opened) {
1767 * Allocate a struct adouble without initialiing it
1769 * The struct is either hang of the fsp extension context or if fsp is
1772 * @param[in] ctx talloc context
1773 * @param[in] handle vfs handle
1774 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1776 * @return adouble handle
1778 static struct adouble *ad_alloc(TALLOC_CTX *ctx, vfs_handle_struct *handle,
1779 adouble_type_t type)
1784 struct fruit_config_data *config;
1786 SMB_VFS_HANDLE_GET_DATA(handle, config,
1787 struct fruit_config_data, return NULL);
1791 adsize = AD_DATASZ_XATTR;
1794 if (config->rsrc == FRUIT_RSRC_ADFILE) {
1795 adsize = AD_DATASZ_DOT_UND;
1802 ad = talloc_zero(ctx, struct adouble);
1809 ad->ad_data = talloc_zero_array(ad, char, adsize);
1810 if (ad->ad_data == NULL) {
1817 ad->ad_magic = AD_MAGIC;
1818 ad->ad_version = AD_VERSION;
1821 talloc_set_destructor(ad, adouble_destructor);
1831 * Allocate and initialize a new struct adouble
1833 * @param[in] ctx talloc context
1834 * @param[in] handle vfs handle
1835 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1837 * @return adouble handle, initialized
1839 static struct adouble *ad_init(TALLOC_CTX *ctx, vfs_handle_struct *handle,
1840 adouble_type_t type)
1843 const struct ad_entry_order *eid;
1844 struct adouble *ad = NULL;
1845 time_t t = time(NULL);
1849 eid = entry_order_meta_xattr;
1852 eid = entry_order_dot_und;
1858 ad = ad_alloc(ctx, handle, type);
1864 ad->ad_eid[eid->id].ade_off = eid->offset;
1865 ad->ad_eid[eid->id].ade_len = eid->len;
1869 /* put something sane in the date fields */
1870 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, t);
1871 ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, t);
1872 ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, t);
1873 ad_setdate(ad, AD_DATE_BACKUP, htonl(AD_DATE_START));
1881 static struct adouble *ad_get_internal(TALLOC_CTX *ctx,
1882 vfs_handle_struct *handle,
1884 const struct smb_filename *smb_fname,
1885 adouble_type_t type)
1889 struct adouble *ad = NULL;
1893 smb_fname = fsp->base_fsp->fsp_name;
1896 DEBUG(10, ("ad_get(%s) called for %s\n",
1897 type == ADOUBLE_META ? "meta" : "rsrc",
1898 smb_fname->base_name));
1900 ad = ad_alloc(ctx, handle, type);
1906 /* Try rw first so we can use the fd in ad_convert() */
1909 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
1910 if (rc == -1 && ((errno == EROFS) || (errno == EACCES))) {
1912 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
1915 DBG_DEBUG("ad_open [%s] error [%s]\n",
1916 smb_fname->base_name, strerror(errno));
1921 len = ad_read(handle, ad, smb_fname);
1923 DEBUG(10, ("error reading AppleDouble for %s\n",
1924 smb_fname->base_name));
1930 DEBUG(10, ("ad_get(%s) for %s returning %d\n",
1931 type == ADOUBLE_META ? "meta" : "rsrc",
1932 smb_fname->base_name, rc));
1941 * Return AppleDouble data for a file
1943 * @param[in] ctx talloc context
1944 * @param[in] handle vfs handle
1945 * @param[in] smb_fname pathname to file or directory
1946 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1948 * @return talloced struct adouble or NULL on error
1950 static struct adouble *ad_get(TALLOC_CTX *ctx,
1951 vfs_handle_struct *handle,
1952 const struct smb_filename *smb_fname,
1953 adouble_type_t type)
1955 return ad_get_internal(ctx, handle, NULL, smb_fname, type);
1959 * Return AppleDouble data for a file
1961 * @param[in] ctx talloc context
1962 * @param[in] handle vfs handle
1963 * @param[in] fsp fsp to use for IO
1964 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1966 * @return talloced struct adouble or NULL on error
1968 static struct adouble *ad_fget(TALLOC_CTX *ctx, vfs_handle_struct *handle,
1969 files_struct *fsp, adouble_type_t type)
1971 return ad_get_internal(ctx, handle, fsp, NULL, type);
1975 * Set AppleDouble metadata on a file or directory
1977 * @param[in] ad adouble handle
1979 * @param[in] smb_fname pathname to file or directory
1981 * @return status code, 0 means success
1983 static int ad_set(vfs_handle_struct *handle,
1985 const struct smb_filename *smb_fname)
1990 DBG_DEBUG("Path [%s]\n", smb_fname->base_name);
1992 if (ad->ad_type != ADOUBLE_META) {
1993 DBG_ERR("ad_set on [%s] used with ADOUBLE_RSRC\n",
1994 smb_fname->base_name);
2003 ret = SMB_VFS_SETXATTR(handle->conn,
2005 AFPINFO_EA_NETATALK,
2007 AD_DATASZ_XATTR, 0);
2009 DBG_DEBUG("Path [%s] ret [%d]\n", smb_fname->base_name, ret);
2015 * Set AppleDouble metadata on a file or directory
2017 * @param[in] ad adouble handle
2018 * @param[in] fsp file handle
2020 * @return status code, 0 means success
2022 static int ad_fset(struct vfs_handle_struct *handle,
2030 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
2033 || (fsp->fh == NULL)
2034 || (fsp->fh->fd == -1))
2036 smb_panic("bad fsp");
2044 switch (ad->ad_type) {
2046 rc = SMB_VFS_NEXT_SETXATTR(handle,
2048 AFPINFO_EA_NETATALK,
2050 AD_DATASZ_XATTR, 0);
2054 len = SMB_VFS_NEXT_PWRITE(handle,
2059 if (len != AD_DATASZ_DOT_UND) {
2060 DBG_ERR("short write on %s: %zd", fsp_str_dbg(fsp), len);
2070 DBG_DEBUG("Path [%s] rc [%d]\n", fsp_str_dbg(fsp), rc);
2075 /*****************************************************************************
2077 *****************************************************************************/
2079 static bool is_afpinfo_stream(const struct smb_filename *smb_fname)
2081 if (strncasecmp_m(smb_fname->stream_name,
2082 AFPINFO_STREAM_NAME,
2083 strlen(AFPINFO_STREAM_NAME)) == 0) {
2089 static bool is_afpresource_stream(const struct smb_filename *smb_fname)
2091 if (strncasecmp_m(smb_fname->stream_name,
2092 AFPRESOURCE_STREAM_NAME,
2093 strlen(AFPRESOURCE_STREAM_NAME)) == 0) {
2100 * Test whether stream is an Apple stream.
2102 static bool is_apple_stream(const struct smb_filename *smb_fname)
2104 if (is_afpinfo_stream(smb_fname)) {
2107 if (is_afpresource_stream(smb_fname)) {
2113 static bool is_adouble_file(const char *path)
2115 const char *p = NULL;
2118 p = strrchr(path, '/');
2126 ADOUBLE_NAME_PREFIX,
2127 strlen(ADOUBLE_NAME_PREFIX));
2135 * Initialize config struct from our smb.conf config parameters
2137 static int init_fruit_config(vfs_handle_struct *handle)
2139 struct fruit_config_data *config;
2141 const char *tm_size_str = NULL;
2143 config = talloc_zero(handle->conn, struct fruit_config_data);
2145 DEBUG(1, ("talloc_zero() failed\n"));
2151 * Versions up to Samba 4.5.x had a spelling bug in the
2152 * fruit:resource option calling lp_parm_enum with
2153 * "res*s*ource" (ie two s).
2155 * In Samba 4.6 we accept both the wrong and the correct
2156 * spelling, in Samba 4.7 the bad spelling will be removed.
2158 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2159 "ressource", fruit_rsrc, FRUIT_RSRC_ADFILE);
2160 if (enumval == -1) {
2161 DEBUG(1, ("value for %s: resource type unknown\n",
2162 FRUIT_PARAM_TYPE_NAME));
2165 config->rsrc = (enum fruit_rsrc)enumval;
2167 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2168 "resource", fruit_rsrc, enumval);
2169 if (enumval == -1) {
2170 DEBUG(1, ("value for %s: resource type unknown\n",
2171 FRUIT_PARAM_TYPE_NAME));
2174 config->rsrc = (enum fruit_rsrc)enumval;
2176 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2177 "metadata", fruit_meta, FRUIT_META_NETATALK);
2178 if (enumval == -1) {
2179 DEBUG(1, ("value for %s: metadata type unknown\n",
2180 FRUIT_PARAM_TYPE_NAME));
2183 config->meta = (enum fruit_meta)enumval;
2185 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2186 "locking", fruit_locking, FRUIT_LOCKING_NONE);
2187 if (enumval == -1) {
2188 DEBUG(1, ("value for %s: locking type unknown\n",
2189 FRUIT_PARAM_TYPE_NAME));
2192 config->locking = (enum fruit_locking)enumval;
2194 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2195 "encoding", fruit_encoding, FRUIT_ENC_PRIVATE);
2196 if (enumval == -1) {
2197 DEBUG(1, ("value for %s: encoding type unknown\n",
2198 FRUIT_PARAM_TYPE_NAME));
2201 config->encoding = (enum fruit_encoding)enumval;
2203 if (config->rsrc == FRUIT_RSRC_ADFILE) {
2204 config->veto_appledouble = lp_parm_bool(SNUM(handle->conn),
2205 FRUIT_PARAM_TYPE_NAME,
2210 config->use_aapl = lp_parm_bool(
2211 -1, FRUIT_PARAM_TYPE_NAME, "aapl", true);
2213 config->time_machine = lp_parm_bool(
2214 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "time machine", false);
2216 config->unix_info_enabled = lp_parm_bool(
2217 -1, FRUIT_PARAM_TYPE_NAME, "nfs_aces", true);
2219 config->use_copyfile = lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME,
2222 config->posix_rename = lp_parm_bool(
2223 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "posix_rename", true);
2225 config->aapl_zero_file_id =
2226 lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME, "zero_file_id", true);
2228 config->readdir_attr_rsize = lp_parm_bool(
2229 SNUM(handle->conn), "readdir_attr", "aapl_rsize", true);
2231 config->readdir_attr_finder_info = lp_parm_bool(
2232 SNUM(handle->conn), "readdir_attr", "aapl_finder_info", true);
2234 config->readdir_attr_max_access = lp_parm_bool(
2235 SNUM(handle->conn), "readdir_attr", "aapl_max_access", true);
2237 config->model = lp_parm_const_string(
2238 -1, FRUIT_PARAM_TYPE_NAME, "model", "MacSamba");
2240 tm_size_str = lp_parm_const_string(
2241 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2242 "time machine max size", NULL);
2243 if (tm_size_str != NULL) {
2244 config->time_machine_max_size = conv_str_size(tm_size_str);
2247 config->wipe_intentionally_left_blank_rfork = lp_parm_bool(
2248 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2249 "wipe_intentionally_left_blank_rfork", false);
2251 config->delete_empty_adfiles = lp_parm_bool(
2252 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2253 "delete_empty_adfiles", false);
2255 SMB_VFS_HANDLE_SET_DATA(handle, config,
2256 NULL, struct fruit_config_data,
2263 * Prepend "._" to a basename
2264 * Return a new struct smb_filename with stream_name == NULL.
2266 static int adouble_path(TALLOC_CTX *ctx,
2267 const struct smb_filename *smb_fname_in,
2268 struct smb_filename **pp_smb_fname_out)
2272 struct smb_filename *smb_fname = cp_smb_filename(ctx,
2275 if (smb_fname == NULL) {
2279 /* We need streamname to be NULL */
2280 TALLOC_FREE(smb_fname->stream_name);
2282 /* And we're replacing base_name. */
2283 TALLOC_FREE(smb_fname->base_name);
2285 if (!parent_dirname(smb_fname, smb_fname_in->base_name,
2287 TALLOC_FREE(smb_fname);
2291 smb_fname->base_name = talloc_asprintf(smb_fname,
2292 "%s/._%s", parent, base);
2293 if (smb_fname->base_name == NULL) {
2294 TALLOC_FREE(smb_fname);
2298 *pp_smb_fname_out = smb_fname;
2304 * Allocate and initialize an AfpInfo struct
2306 static AfpInfo *afpinfo_new(TALLOC_CTX *ctx)
2308 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2312 ai->afpi_Signature = AFP_Signature;
2313 ai->afpi_Version = AFP_Version;
2314 ai->afpi_BackupTime = AD_DATE_START;
2319 * Pack an AfpInfo struct into a buffer
2321 * Buffer size must be at least AFP_INFO_SIZE
2322 * Returns size of packed buffer
2324 static ssize_t afpinfo_pack(const AfpInfo *ai, char *buf)
2326 memset(buf, 0, AFP_INFO_SIZE);
2328 RSIVAL(buf, 0, ai->afpi_Signature);
2329 RSIVAL(buf, 4, ai->afpi_Version);
2330 RSIVAL(buf, 12, ai->afpi_BackupTime);
2331 memcpy(buf + 16, ai->afpi_FinderInfo, sizeof(ai->afpi_FinderInfo));
2333 return AFP_INFO_SIZE;
2337 * Unpack a buffer into a AfpInfo structure
2339 * Buffer size must be at least AFP_INFO_SIZE
2340 * Returns allocated AfpInfo struct
2342 static AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data)
2344 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2349 ai->afpi_Signature = RIVAL(data, 0);
2350 ai->afpi_Version = RIVAL(data, 4);
2351 ai->afpi_BackupTime = RIVAL(data, 12);
2352 memcpy(ai->afpi_FinderInfo, (const char *)data + 16,
2353 sizeof(ai->afpi_FinderInfo));
2355 if (ai->afpi_Signature != AFP_Signature
2356 || ai->afpi_Version != AFP_Version) {
2357 DEBUG(1, ("Bad AfpInfo signature or version\n"));
2365 * Fake an inode number from the md5 hash of the (xattr) name
2367 static SMB_INO_T fruit_inode(const SMB_STRUCT_STAT *sbuf, const char *sname)
2369 gnutls_hash_hd_t hash_hnd = NULL;
2370 unsigned char hash[16];
2371 SMB_INO_T result = 0;
2375 DBG_DEBUG("fruit_inode called for %ju/%ju [%s]\n",
2376 (uintmax_t)sbuf->st_ex_dev,
2377 (uintmax_t)sbuf->st_ex_ino, sname);
2379 upper_sname = talloc_strdup_upper(talloc_tos(), sname);
2380 SMB_ASSERT(upper_sname != NULL);
2382 rc = gnutls_hash_init(&hash_hnd, GNUTLS_DIG_MD5);
2387 rc = gnutls_hash(hash_hnd, &(sbuf->st_ex_dev), sizeof(sbuf->st_ex_dev));
2389 gnutls_hash_deinit(hash_hnd, NULL);
2392 rc = gnutls_hash(hash_hnd,
2394 sizeof(sbuf->st_ex_ino));
2396 gnutls_hash_deinit(hash_hnd, NULL);
2399 rc = gnutls_hash(hash_hnd,
2401 talloc_get_size(upper_sname) - 1);
2403 gnutls_hash_deinit(hash_hnd, NULL);
2407 gnutls_hash_deinit(hash_hnd, hash);
2409 /* Hopefully all the variation is in the lower 4 (or 8) bytes! */
2410 memcpy(&result, hash, sizeof(result));
2413 DBG_DEBUG("fruit_inode \"%s\": ino=%ju\n",
2414 sname, (uintmax_t)result);
2417 TALLOC_FREE(upper_sname);
2422 static bool add_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
2423 struct stream_struct **streams,
2424 const char *name, off_t size,
2427 struct stream_struct *tmp;
2429 tmp = talloc_realloc(mem_ctx, *streams, struct stream_struct,
2435 tmp[*num_streams].name = talloc_asprintf(tmp, "%s:$DATA", name);
2436 if (tmp[*num_streams].name == NULL) {
2440 tmp[*num_streams].size = size;
2441 tmp[*num_streams].alloc_size = alloc_size;
2448 static bool filter_empty_rsrc_stream(unsigned int *num_streams,
2449 struct stream_struct **streams)
2451 struct stream_struct *tmp = *streams;
2454 if (*num_streams == 0) {
2458 for (i = 0; i < *num_streams; i++) {
2459 if (strequal_m(tmp[i].name, AFPRESOURCE_STREAM)) {
2464 if (i == *num_streams) {
2468 if (tmp[i].size > 0) {
2472 TALLOC_FREE(tmp[i].name);
2473 if (*num_streams - 1 > i) {
2474 memmove(&tmp[i], &tmp[i+1],
2475 (*num_streams - i - 1) * sizeof(struct stream_struct));
2482 static bool del_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
2483 struct stream_struct **streams,
2486 struct stream_struct *tmp = *streams;
2489 if (*num_streams == 0) {
2493 for (i = 0; i < *num_streams; i++) {
2494 if (strequal_m(tmp[i].name, name)) {
2499 if (i == *num_streams) {
2503 TALLOC_FREE(tmp[i].name);
2504 if (*num_streams - 1 > i) {
2505 memmove(&tmp[i], &tmp[i+1],
2506 (*num_streams - i - 1) * sizeof(struct stream_struct));
2513 static bool ad_empty_finderinfo(const struct adouble *ad)
2516 char emptybuf[ADEDLEN_FINDERI] = {0};
2519 fi = ad_get_entry(ad, ADEID_FINDERI);
2521 DBG_ERR("Missing FinderInfo in struct adouble [%p]\n", ad);
2525 cmp = memcmp(emptybuf, fi, ADEDLEN_FINDERI);
2529 static bool ai_empty_finderinfo(const AfpInfo *ai)
2532 char emptybuf[ADEDLEN_FINDERI] = {0};
2534 cmp = memcmp(emptybuf, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
2539 * Update btime with btime from Netatalk
2541 static void update_btime(vfs_handle_struct *handle,
2542 struct smb_filename *smb_fname)
2545 struct timespec creation_time = {0};
2547 struct fruit_config_data *config = NULL;
2549 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
2552 switch (config->meta) {
2553 case FRUIT_META_STREAM:
2555 case FRUIT_META_NETATALK:
2559 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
2563 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
2567 if (ad_getdate(ad, AD_DATE_UNIX | AD_DATE_CREATE, &t) != 0) {
2573 creation_time.tv_sec = convert_uint32_t_to_time_t(t);
2574 update_stat_ex_create_time(&smb_fname->st, creation_time);
2580 * Map an access mask to a Netatalk single byte byte range lock
2582 static off_t access_to_netatalk_brl(enum apple_fork fork_type,
2583 uint32_t access_mask)
2587 switch (access_mask) {
2588 case FILE_READ_DATA:
2589 offset = AD_FILELOCK_OPEN_RD;
2592 case FILE_WRITE_DATA:
2593 case FILE_APPEND_DATA:
2594 offset = AD_FILELOCK_OPEN_WR;
2598 offset = AD_FILELOCK_OPEN_NONE;
2602 if (fork_type == APPLE_FORK_RSRC) {
2603 if (offset == AD_FILELOCK_OPEN_NONE) {
2604 offset = AD_FILELOCK_RSRC_OPEN_NONE;
2614 * Map a deny mode to a Netatalk brl
2616 static off_t denymode_to_netatalk_brl(enum apple_fork fork_type,
2621 switch (deny_mode) {
2623 offset = AD_FILELOCK_DENY_RD;
2627 offset = AD_FILELOCK_DENY_WR;
2631 smb_panic("denymode_to_netatalk_brl: bad deny mode\n");
2634 if (fork_type == APPLE_FORK_RSRC) {
2642 * Call fcntl() with an exclusive F_GETLK request in order to
2643 * determine if there's an exisiting shared lock
2645 * @return true if the requested lock was found or any error occurred
2646 * false if the lock was not found
2648 static bool test_netatalk_lock(files_struct *fsp, off_t in_offset)
2651 off_t offset = in_offset;
2656 result = SMB_VFS_GETLOCK(fsp, &offset, &len, &type, &pid);
2657 if (result == false) {
2661 if (type != F_UNLCK) {
2668 static NTSTATUS fruit_check_access(vfs_handle_struct *handle,
2670 uint32_t access_mask,
2671 uint32_t share_mode)
2673 NTSTATUS status = NT_STATUS_OK;
2675 bool share_for_read = (share_mode & FILE_SHARE_READ);
2676 bool share_for_write = (share_mode & FILE_SHARE_WRITE);
2677 bool netatalk_already_open_for_reading = false;
2678 bool netatalk_already_open_for_writing = false;
2679 bool netatalk_already_open_with_deny_read = false;
2680 bool netatalk_already_open_with_deny_write = false;
2682 /* FIXME: hardcoded data fork, add resource fork */
2683 enum apple_fork fork_type = APPLE_FORK_DATA;
2685 DBG_DEBUG("fruit_check_access: %s, am: %s/%s, sm: 0x%x\n",
2687 access_mask & FILE_READ_DATA ? "READ" :"-",
2688 access_mask & FILE_WRITE_DATA ? "WRITE" : "-",
2691 if (fsp->fh->fd == -1) {
2692 return NT_STATUS_OK;
2695 /* Read NetATalk opens and deny modes on the file. */
2696 netatalk_already_open_for_reading = test_netatalk_lock(fsp,
2697 access_to_netatalk_brl(fork_type,
2700 netatalk_already_open_with_deny_read = test_netatalk_lock(fsp,
2701 denymode_to_netatalk_brl(fork_type,
2704 netatalk_already_open_for_writing = test_netatalk_lock(fsp,
2705 access_to_netatalk_brl(fork_type,
2708 netatalk_already_open_with_deny_write = test_netatalk_lock(fsp,
2709 denymode_to_netatalk_brl(fork_type,
2712 /* If there are any conflicts - sharing violation. */
2713 if ((access_mask & FILE_READ_DATA) &&
2714 netatalk_already_open_with_deny_read) {
2715 return NT_STATUS_SHARING_VIOLATION;
2718 if (!share_for_read &&
2719 netatalk_already_open_for_reading) {
2720 return NT_STATUS_SHARING_VIOLATION;
2723 if ((access_mask & FILE_WRITE_DATA) &&
2724 netatalk_already_open_with_deny_write) {
2725 return NT_STATUS_SHARING_VIOLATION;
2728 if (!share_for_write &&
2729 netatalk_already_open_for_writing) {
2730 return NT_STATUS_SHARING_VIOLATION;
2733 if (!(access_mask & FILE_READ_DATA)) {
2735 * Nothing we can do here, we need read access
2738 return NT_STATUS_OK;
2741 /* Set NetAtalk locks matching our access */
2742 if (access_mask & FILE_READ_DATA) {
2743 struct byte_range_lock *br_lck = NULL;
2745 off = access_to_netatalk_brl(fork_type, FILE_READ_DATA);
2747 handle->conn->sconn->msg_ctx, fsp,
2748 fsp->op->global->open_persistent_id, 1, off,
2749 READ_LOCK, POSIX_LOCK, false,
2752 TALLOC_FREE(br_lck);
2754 if (!NT_STATUS_IS_OK(status)) {
2759 if (!share_for_read) {
2760 struct byte_range_lock *br_lck = NULL;
2762 off = denymode_to_netatalk_brl(fork_type, DENY_READ);
2764 handle->conn->sconn->msg_ctx, fsp,
2765 fsp->op->global->open_persistent_id, 1, off,
2766 READ_LOCK, POSIX_LOCK, false,
2769 TALLOC_FREE(br_lck);
2771 if (!NT_STATUS_IS_OK(status)) {
2776 if (access_mask & FILE_WRITE_DATA) {
2777 struct byte_range_lock *br_lck = NULL;
2779 off = access_to_netatalk_brl(fork_type, FILE_WRITE_DATA);
2781 handle->conn->sconn->msg_ctx, fsp,
2782 fsp->op->global->open_persistent_id, 1, off,
2783 READ_LOCK, POSIX_LOCK, false,
2786 TALLOC_FREE(br_lck);
2788 if (!NT_STATUS_IS_OK(status)) {
2793 if (!share_for_write) {
2794 struct byte_range_lock *br_lck = NULL;
2796 off = denymode_to_netatalk_brl(fork_type, DENY_WRITE);
2798 handle->conn->sconn->msg_ctx, fsp,
2799 fsp->op->global->open_persistent_id, 1, off,
2800 READ_LOCK, POSIX_LOCK, false,
2803 TALLOC_FREE(br_lck);
2805 if (!NT_STATUS_IS_OK(status)) {
2810 return NT_STATUS_OK;
2813 static NTSTATUS check_aapl(vfs_handle_struct *handle,
2814 struct smb_request *req,
2815 const struct smb2_create_blobs *in_context_blobs,
2816 struct smb2_create_blobs *out_context_blobs)
2818 struct fruit_config_data *config;
2820 struct smb2_create_blob *aapl = NULL;
2824 DATA_BLOB blob = data_blob_talloc(req, NULL, 0);
2825 uint64_t req_bitmap, client_caps;
2826 uint64_t server_caps = SMB2_CRTCTX_AAPL_UNIX_BASED;
2830 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
2831 return NT_STATUS_UNSUCCESSFUL);
2833 if (!config->use_aapl
2834 || in_context_blobs == NULL
2835 || out_context_blobs == NULL) {
2836 return NT_STATUS_OK;
2839 aapl = smb2_create_blob_find(in_context_blobs,
2840 SMB2_CREATE_TAG_AAPL);
2842 return NT_STATUS_OK;
2845 if (aapl->data.length != 24) {
2846 DEBUG(1, ("unexpected AAPL ctxt length: %ju\n",
2847 (uintmax_t)aapl->data.length));
2848 return NT_STATUS_INVALID_PARAMETER;
2851 cmd = IVAL(aapl->data.data, 0);
2852 if (cmd != SMB2_CRTCTX_AAPL_SERVER_QUERY) {
2853 DEBUG(1, ("unsupported AAPL cmd: %d\n", cmd));
2854 return NT_STATUS_INVALID_PARAMETER;
2857 req_bitmap = BVAL(aapl->data.data, 8);
2858 client_caps = BVAL(aapl->data.data, 16);
2860 SIVAL(p, 0, SMB2_CRTCTX_AAPL_SERVER_QUERY);
2862 SBVAL(p, 8, req_bitmap);
2863 ok = data_blob_append(req, &blob, p, 16);
2865 return NT_STATUS_UNSUCCESSFUL;
2868 if (req_bitmap & SMB2_CRTCTX_AAPL_SERVER_CAPS) {
2869 if ((client_caps & SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR) &&
2870 (handle->conn->tcon->compat->fs_capabilities & FILE_NAMED_STREAMS)) {
2871 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR;
2872 config->readdir_attr_enabled = true;
2875 if (config->use_copyfile) {
2876 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_OSX_COPYFILE;
2877 config->copyfile_enabled = true;
2881 * The client doesn't set the flag, so we can't check
2882 * for it and just set it unconditionally
2884 if (config->unix_info_enabled) {
2885 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_NFS_ACE;
2888 SBVAL(p, 0, server_caps);
2889 ok = data_blob_append(req, &blob, p, 8);
2891 return NT_STATUS_UNSUCCESSFUL;
2895 if (req_bitmap & SMB2_CRTCTX_AAPL_VOLUME_CAPS) {
2896 int val = lp_case_sensitive(SNUM(handle->conn->tcon->compat));
2904 caps |= SMB2_CRTCTX_AAPL_CASE_SENSITIVE;
2911 if (config->time_machine) {
2912 caps |= SMB2_CRTCTX_AAPL_FULL_SYNC;
2917 ok = data_blob_append(req, &blob, p, 8);
2919 return NT_STATUS_UNSUCCESSFUL;
2923 if (req_bitmap & SMB2_CRTCTX_AAPL_MODEL_INFO) {
2924 ok = convert_string_talloc(req,
2925 CH_UNIX, CH_UTF16LE,
2926 config->model, strlen(config->model),
2929 return NT_STATUS_UNSUCCESSFUL;
2933 SIVAL(p + 4, 0, modellen);
2934 ok = data_blob_append(req, &blob, p, 8);
2937 return NT_STATUS_UNSUCCESSFUL;
2940 ok = data_blob_append(req, &blob, model, modellen);
2943 return NT_STATUS_UNSUCCESSFUL;
2947 status = smb2_create_blob_add(out_context_blobs,
2949 SMB2_CREATE_TAG_AAPL,
2951 if (NT_STATUS_IS_OK(status)) {
2952 global_fruit_config.nego_aapl = true;
2953 if (config->aapl_zero_file_id) {
2954 aapl_force_zero_file_id(handle->conn->sconn);
2961 static bool readdir_attr_meta_finderi_stream(
2962 struct vfs_handle_struct *handle,
2963 const struct smb_filename *smb_fname,
2966 struct smb_filename *stream_name = NULL;
2967 files_struct *fsp = NULL;
2972 uint8_t buf[AFP_INFO_SIZE];
2974 stream_name = synthetic_smb_fname(talloc_tos(),
2975 smb_fname->base_name,
2976 AFPINFO_STREAM_NAME,
2977 NULL, smb_fname->flags);
2978 if (stream_name == NULL) {
2982 ret = SMB_VFS_STAT(handle->conn, stream_name);
2987 status = SMB_VFS_CREATE_FILE(
2988 handle->conn, /* conn */
2990 0, /* root_dir_fid */
2991 stream_name, /* fname */
2992 FILE_READ_DATA, /* access_mask */
2993 (FILE_SHARE_READ | FILE_SHARE_WRITE | /* share_access */
2995 FILE_OPEN, /* create_disposition*/
2996 0, /* create_options */
2997 0, /* file_attributes */
2998 INTERNAL_OPEN_ONLY, /* oplock_request */
3000 0, /* allocation_size */
3001 0, /* private_flags */
3006 NULL, NULL); /* create context */
3008 TALLOC_FREE(stream_name);
3010 if (!NT_STATUS_IS_OK(status)) {
3014 nread = SMB_VFS_PREAD(fsp, &buf[0], AFP_INFO_SIZE, 0);
3015 if (nread != AFP_INFO_SIZE) {
3016 DBG_ERR("short read [%s] [%zd/%d]\n",
3017 smb_fname_str_dbg(stream_name), nread, AFP_INFO_SIZE);
3022 memcpy(&ai->afpi_FinderInfo[0], &buf[AFP_OFF_FinderInfo],
3029 close_file(NULL, fsp, NORMAL_CLOSE);
3035 static bool readdir_attr_meta_finderi_netatalk(
3036 struct vfs_handle_struct *handle,
3037 const struct smb_filename *smb_fname,
3040 struct adouble *ad = NULL;
3043 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
3048 p = ad_get_entry(ad, ADEID_FINDERI);
3050 DBG_ERR("No ADEID_FINDERI for [%s]\n", smb_fname->base_name);
3055 memcpy(&ai->afpi_FinderInfo[0], p, AFP_FinderSize);
3060 static bool readdir_attr_meta_finderi(struct vfs_handle_struct *handle,
3061 const struct smb_filename *smb_fname,
3062 struct readdir_attr_data *attr_data)
3064 struct fruit_config_data *config = NULL;
3065 uint32_t date_added;
3069 SMB_VFS_HANDLE_GET_DATA(handle, config,
3070 struct fruit_config_data,
3073 switch (config->meta) {
3074 case FRUIT_META_NETATALK:
3075 ok = readdir_attr_meta_finderi_netatalk(
3076 handle, smb_fname, &ai);
3079 case FRUIT_META_STREAM:
3080 ok = readdir_attr_meta_finderi_stream(
3081 handle, smb_fname, &ai);
3085 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3090 /* Don't bother with errors, it's likely ENOENT */
3094 if (S_ISREG(smb_fname->st.st_ex_mode)) {
3096 memcpy(&attr_data->attr_data.aapl.finder_info[0],
3097 &ai.afpi_FinderInfo[0], 4);
3099 /* finder_creator */
3100 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 4,
3101 &ai.afpi_FinderInfo[4], 4);
3105 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 8,
3106 &ai.afpi_FinderInfo[8], 2);
3108 /* finder_ext_flags */
3109 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 10,
3110 &ai.afpi_FinderInfo[24], 2);
3113 date_added = convert_time_t_to_uint32_t(
3114 smb_fname->st.st_ex_btime.tv_sec - AD_DATE_DELTA);
3116 RSIVAL(&attr_data->attr_data.aapl.finder_info[0], 12, date_added);
3121 static uint64_t readdir_attr_rfork_size_adouble(
3122 struct vfs_handle_struct *handle,
3123 const struct smb_filename *smb_fname)
3125 struct adouble *ad = NULL;
3126 uint64_t rfork_size;
3128 ad = ad_get(talloc_tos(), handle, smb_fname,
3134 rfork_size = ad_getentrylen(ad, ADEID_RFORK);
3140 static uint64_t readdir_attr_rfork_size_stream(
3141 struct vfs_handle_struct *handle,
3142 const struct smb_filename *smb_fname)
3144 struct smb_filename *stream_name = NULL;
3146 uint64_t rfork_size;
3148 stream_name = synthetic_smb_fname(talloc_tos(),
3149 smb_fname->base_name,
3150 AFPRESOURCE_STREAM_NAME,
3152 if (stream_name == NULL) {
3156 ret = SMB_VFS_STAT(handle->conn, stream_name);
3158 TALLOC_FREE(stream_name);
3162 rfork_size = stream_name->st.st_ex_size;
3163 TALLOC_FREE(stream_name);
3168 static uint64_t readdir_attr_rfork_size(struct vfs_handle_struct *handle,
3169 const struct smb_filename *smb_fname)
3171 struct fruit_config_data *config = NULL;
3172 uint64_t rfork_size;
3174 SMB_VFS_HANDLE_GET_DATA(handle, config,
3175 struct fruit_config_data,
3178 switch (config->rsrc) {
3179 case FRUIT_RSRC_ADFILE:
3180 rfork_size = readdir_attr_rfork_size_adouble(handle,
3184 case FRUIT_RSRC_XATTR:
3185 case FRUIT_RSRC_STREAM:
3186 rfork_size = readdir_attr_rfork_size_stream(handle,
3191 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3199 static NTSTATUS readdir_attr_macmeta(struct vfs_handle_struct *handle,
3200 const struct smb_filename *smb_fname,
3201 struct readdir_attr_data *attr_data)
3203 NTSTATUS status = NT_STATUS_OK;
3204 struct fruit_config_data *config = NULL;
3207 SMB_VFS_HANDLE_GET_DATA(handle, config,
3208 struct fruit_config_data,
3209 return NT_STATUS_UNSUCCESSFUL);
3212 /* Ensure we return a default value in the creation_date field */
3213 RSIVAL(&attr_data->attr_data.aapl.finder_info, 12, AD_DATE_START);
3216 * Resource fork length
3219 if (config->readdir_attr_rsize) {
3220 uint64_t rfork_size;
3222 rfork_size = readdir_attr_rfork_size(handle, smb_fname);
3223 attr_data->attr_data.aapl.rfork_size = rfork_size;
3230 if (config->readdir_attr_finder_info) {
3231 ok = readdir_attr_meta_finderi(handle, smb_fname, attr_data);
3233 status = NT_STATUS_INTERNAL_ERROR;
3240 static NTSTATUS remove_virtual_nfs_aces(struct security_descriptor *psd)
3245 if (psd->dacl == NULL) {
3246 return NT_STATUS_OK;
3249 for (i = 0; i < psd->dacl->num_aces; i++) {
3250 /* MS NFS style mode/uid/gid */
3251 int cmp = dom_sid_compare_domain(
3252 &global_sid_Unix_NFS,
3253 &psd->dacl->aces[i].trustee);
3255 /* Normal ACE entry. */
3260 * security_descriptor_dacl_del()
3261 * *must* return NT_STATUS_OK as we know
3262 * we have something to remove.
3265 status = security_descriptor_dacl_del(psd,
3266 &psd->dacl->aces[i].trustee);
3267 if (!NT_STATUS_IS_OK(status)) {
3268 DBG_WARNING("failed to remove MS NFS style ACE: %s\n",
3274 * security_descriptor_dacl_del() may delete more
3275 * then one entry subsequent to this one if the
3276 * SID matches, but we only need to ensure that
3277 * we stay looking at the same element in the array.
3281 return NT_STATUS_OK;
3284 /* Search MS NFS style ACE with UNIX mode */
3285 static NTSTATUS check_ms_nfs(vfs_handle_struct *handle,
3287 struct security_descriptor *psd,
3292 struct fruit_config_data *config = NULL;
3296 SMB_VFS_HANDLE_GET_DATA(handle, config,
3297 struct fruit_config_data,
3298 return NT_STATUS_UNSUCCESSFUL);
3300 if (!global_fruit_config.nego_aapl) {
3301 return NT_STATUS_OK;
3303 if (psd->dacl == NULL || !config->unix_info_enabled) {
3304 return NT_STATUS_OK;
3307 for (i = 0; i < psd->dacl->num_aces; i++) {
3308 if (dom_sid_compare_domain(
3309 &global_sid_Unix_NFS_Mode,
3310 &psd->dacl->aces[i].trustee) == 0) {
3311 *pmode = (mode_t)psd->dacl->aces[i].trustee.sub_auths[2];
3312 *pmode &= (S_IRWXU | S_IRWXG | S_IRWXO);
3315 DEBUG(10, ("MS NFS chmod request %s, %04o\n",
3316 fsp_str_dbg(fsp), (unsigned)(*pmode)));
3322 * Remove any incoming virtual ACE entries generated by
3323 * fruit_fget_nt_acl().
3326 return remove_virtual_nfs_aces(psd);
3329 /****************************************************************************
3331 ****************************************************************************/
3333 static int fruit_connect(vfs_handle_struct *handle,
3334 const char *service,
3338 char *list = NULL, *newlist = NULL;
3339 struct fruit_config_data *config;
3341 DEBUG(10, ("fruit_connect\n"));
3343 rc = SMB_VFS_NEXT_CONNECT(handle, service, user);
3348 rc = init_fruit_config(handle);
3353 SMB_VFS_HANDLE_GET_DATA(handle, config,
3354 struct fruit_config_data, return -1);
3356 if (config->veto_appledouble) {
3357 list = lp_veto_files(talloc_tos(), SNUM(handle->conn));
3360 if (strstr(list, "/" ADOUBLE_NAME_PREFIX "*/") == NULL) {
3361 newlist = talloc_asprintf(
3363 "%s/" ADOUBLE_NAME_PREFIX "*/",
3365 lp_do_parameter(SNUM(handle->conn),
3370 lp_do_parameter(SNUM(handle->conn),
3372 "/" ADOUBLE_NAME_PREFIX "*/");
3378 if (config->encoding == FRUIT_ENC_NATIVE) {
3379 lp_do_parameter(SNUM(handle->conn),
3384 if (config->time_machine) {
3385 DBG_NOTICE("Enabling durable handles for Time Machine "
3386 "support on [%s]\n", service);
3387 lp_do_parameter(SNUM(handle->conn), "durable handles", "yes");
3388 lp_do_parameter(SNUM(handle->conn), "kernel oplocks", "no");
3389 lp_do_parameter(SNUM(handle->conn), "kernel share modes", "no");
3390 if (!lp_strict_sync(SNUM(handle->conn))) {
3391 DBG_WARNING("Time Machine without strict sync is not "
3394 lp_do_parameter(SNUM(handle->conn), "posix locking", "no");
3400 static int fruit_fake_fd(void)
3407 * Return a valid fd, but ensure any attempt to use it returns
3408 * an error (EPIPE). Once we get a write on the handle, we open
3411 ret = pipe(pipe_fds);
3421 static int fruit_open_meta_stream(vfs_handle_struct *handle,
3422 struct smb_filename *smb_fname,
3427 struct fruit_config_data *config = NULL;
3428 struct fio *fio = NULL;
3429 int open_flags = flags & ~O_CREAT;
3432 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3434 SMB_VFS_HANDLE_GET_DATA(handle, config,
3435 struct fruit_config_data, return -1);
3437 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3438 fio->type = ADOUBLE_META;
3439 fio->config = config;
3441 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, open_flags, mode);
3446 if (!(flags & O_CREAT)) {
3447 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
3451 fd = fruit_fake_fd();
3453 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
3457 fio->fake_fd = true;
3464 static int fruit_open_meta_netatalk(vfs_handle_struct *handle,
3465 struct smb_filename *smb_fname,
3470 struct fruit_config_data *config = NULL;
3471 struct fio *fio = NULL;
3472 struct adouble *ad = NULL;
3473 bool meta_exists = false;
3476 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3478 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
3485 if (!meta_exists && !(flags & O_CREAT)) {
3490 fd = fruit_fake_fd();
3495 SMB_VFS_HANDLE_GET_DATA(handle, config,
3496 struct fruit_config_data, return -1);
3498 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3499 fio->type = ADOUBLE_META;
3500 fio->config = config;
3501 fio->fake_fd = true;
3508 static int fruit_open_meta(vfs_handle_struct *handle,
3509 struct smb_filename *smb_fname,
3510 files_struct *fsp, int flags, mode_t mode)
3513 struct fruit_config_data *config = NULL;
3515 DBG_DEBUG("path [%s]\n", smb_fname_str_dbg(smb_fname));
3517 SMB_VFS_HANDLE_GET_DATA(handle, config,
3518 struct fruit_config_data, return -1);
3520 switch (config->meta) {
3521 case FRUIT_META_STREAM:
3522 fd = fruit_open_meta_stream(handle, smb_fname,
3526 case FRUIT_META_NETATALK:
3527 fd = fruit_open_meta_netatalk(handle, smb_fname,
3532 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3536 DBG_DEBUG("path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3541 static int fruit_open_rsrc_adouble(vfs_handle_struct *handle,
3542 struct smb_filename *smb_fname,
3548 struct adouble *ad = NULL;
3549 struct smb_filename *smb_fname_base = NULL;
3550 struct fruit_config_data *config = NULL;
3553 SMB_VFS_HANDLE_GET_DATA(handle, config,
3554 struct fruit_config_data, return -1);
3556 if ((!(flags & O_CREAT)) &&
3557 S_ISDIR(fsp->base_fsp->fsp_name->st.st_ex_mode))
3559 /* sorry, but directories don't habe a resource fork */
3564 rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_base);
3569 /* We always need read/write access for the metadata header too */
3570 flags &= ~(O_RDONLY | O_WRONLY);
3573 hostfd = SMB_VFS_NEXT_OPEN(handle, smb_fname_base, fsp,
3580 if (flags & (O_CREAT | O_TRUNC)) {
3581 ad = ad_init(fsp, handle, ADOUBLE_RSRC);
3587 fsp->fh->fd = hostfd;
3589 rc = ad_fset(handle, ad, fsp);
3600 TALLOC_FREE(smb_fname_base);
3602 DEBUG(10, ("fruit_open resource fork: rc=%d, fd=%d\n", rc, hostfd));
3604 int saved_errno = errno;
3607 * BUGBUGBUG -- we would need to call
3608 * fd_close_posix here, but we don't have a
3611 fsp->fh->fd = hostfd;
3615 errno = saved_errno;
3620 static int fruit_open_rsrc_xattr(vfs_handle_struct *handle,
3621 struct smb_filename *smb_fname,
3626 #ifdef HAVE_ATTROPEN
3629 fd = attropen(smb_fname->base_name,
3630 AFPRESOURCE_EA_NETATALK,
3645 static int fruit_open_rsrc(vfs_handle_struct *handle,
3646 struct smb_filename *smb_fname,
3647 files_struct *fsp, int flags, mode_t mode)
3650 struct fruit_config_data *config = NULL;
3651 struct fio *fio = NULL;
3653 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3655 SMB_VFS_HANDLE_GET_DATA(handle, config,
3656 struct fruit_config_data, return -1);
3658 switch (config->rsrc) {
3659 case FRUIT_RSRC_STREAM:
3660 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3663 case FRUIT_RSRC_ADFILE:
3664 fd = fruit_open_rsrc_adouble(handle, smb_fname,
3668 case FRUIT_RSRC_XATTR:
3669 fd = fruit_open_rsrc_xattr(handle, smb_fname,
3674 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3678 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3684 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3685 fio->type = ADOUBLE_RSRC;
3686 fio->config = config;
3691 static int fruit_open(vfs_handle_struct *handle,
3692 struct smb_filename *smb_fname,
3693 files_struct *fsp, int flags, mode_t mode)
3697 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3699 if (!is_ntfs_stream_smb_fname(smb_fname)) {
3700 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3703 if (is_afpinfo_stream(smb_fname)) {
3704 fd = fruit_open_meta(handle, smb_fname, fsp, flags, mode);
3705 } else if (is_afpresource_stream(smb_fname)) {
3706 fd = fruit_open_rsrc(handle, smb_fname, fsp, flags, mode);
3708 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3711 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3716 static int fruit_close_meta(vfs_handle_struct *handle,
3720 struct fruit_config_data *config = NULL;
3722 SMB_VFS_HANDLE_GET_DATA(handle, config,
3723 struct fruit_config_data, return -1);
3725 switch (config->meta) {
3726 case FRUIT_META_STREAM:
3727 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3730 case FRUIT_META_NETATALK:
3731 ret = close(fsp->fh->fd);
3736 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3744 static int fruit_close_rsrc(vfs_handle_struct *handle,
3748 struct fruit_config_data *config = NULL;
3750 SMB_VFS_HANDLE_GET_DATA(handle, config,
3751 struct fruit_config_data, return -1);
3753 switch (config->rsrc) {
3754 case FRUIT_RSRC_STREAM:
3755 case FRUIT_RSRC_ADFILE:
3756 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3759 case FRUIT_RSRC_XATTR:
3760 ret = close(fsp->fh->fd);
3765 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3772 static int fruit_close(vfs_handle_struct *handle,
3780 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(fsp->fsp_name), fd);
3782 if (!is_ntfs_stream_smb_fname(fsp->fsp_name)) {
3783 return SMB_VFS_NEXT_CLOSE(handle, fsp);
3786 if (is_afpinfo_stream(fsp->fsp_name)) {
3787 ret = fruit_close_meta(handle, fsp);
3788 } else if (is_afpresource_stream(fsp->fsp_name)) {
3789 ret = fruit_close_rsrc(handle, fsp);
3791 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3797 static int fruit_rename(struct vfs_handle_struct *handle,
3798 const struct smb_filename *smb_fname_src,
3799 const struct smb_filename *smb_fname_dst)
3802 struct fruit_config_data *config = NULL;
3803 struct smb_filename *src_adp_smb_fname = NULL;
3804 struct smb_filename *dst_adp_smb_fname = NULL;
3806 SMB_VFS_HANDLE_GET_DATA(handle, config,
3807 struct fruit_config_data, return -1);
3809 if (!VALID_STAT(smb_fname_src->st)) {
3810 DBG_ERR("Need valid stat for [%s]\n",
3811 smb_fname_str_dbg(smb_fname_src));
3815 rc = SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
3820 if ((config->rsrc != FRUIT_RSRC_ADFILE) ||
3821 (!S_ISREG(smb_fname_src->st.st_ex_mode)))
3826 rc = adouble_path(talloc_tos(), smb_fname_src, &src_adp_smb_fname);
3831 rc = adouble_path(talloc_tos(), smb_fname_dst, &dst_adp_smb_fname);
3836 DBG_DEBUG("%s -> %s\n",
3837 smb_fname_str_dbg(src_adp_smb_fname),
3838 smb_fname_str_dbg(dst_adp_smb_fname));
3840 rc = SMB_VFS_NEXT_RENAME(handle, src_adp_smb_fname, dst_adp_smb_fname);
3841 if (errno == ENOENT) {
3846 TALLOC_FREE(src_adp_smb_fname);
3847 TALLOC_FREE(dst_adp_smb_fname);
3851 static int fruit_unlink_meta_stream(vfs_handle_struct *handle,
3852 const struct smb_filename *smb_fname)
3854 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3857 static int fruit_unlink_meta_netatalk(vfs_handle_struct *handle,
3858 const struct smb_filename *smb_fname)
3860 return SMB_VFS_REMOVEXATTR(handle->conn,
3862 AFPINFO_EA_NETATALK);
3865 static int fruit_unlink_meta(vfs_handle_struct *handle,
3866 const struct smb_filename *smb_fname)
3868 struct fruit_config_data *config = NULL;
3871 SMB_VFS_HANDLE_GET_DATA(handle, config,
3872 struct fruit_config_data, return -1);
3874 switch (config->meta) {
3875 case FRUIT_META_STREAM:
3876 rc = fruit_unlink_meta_stream(handle, smb_fname);
3879 case FRUIT_META_NETATALK:
3880 rc = fruit_unlink_meta_netatalk(handle, smb_fname);
3884 DBG_ERR("Unsupported meta config [%d]\n", config->meta);
3891 static int fruit_unlink_rsrc_stream(vfs_handle_struct *handle,
3892 const struct smb_filename *smb_fname,
3897 if (!force_unlink) {
3898 struct smb_filename *smb_fname_cp = NULL;
3901 smb_fname_cp = cp_smb_filename(talloc_tos(), smb_fname);
3902 if (smb_fname_cp == NULL) {
3907 * 0 byte resource fork streams are not listed by
3908 * vfs_streaminfo, as a result stream cleanup/deletion of file
3909 * deletion doesn't remove the resourcefork stream.
3912 ret = SMB_VFS_NEXT_STAT(handle, smb_fname_cp);
3914 TALLOC_FREE(smb_fname_cp);
3915 DBG_ERR("stat [%s] failed [%s]\n",
3916 smb_fname_str_dbg(smb_fname_cp), strerror(errno));
3920 size = smb_fname_cp->st.st_ex_size;
3921 TALLOC_FREE(smb_fname_cp);
3924 /* OS X ignores resource fork stream delete requests */
3929 ret = SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3930 if ((ret != 0) && (errno == ENOENT) && force_unlink) {
3937 static int fruit_unlink_rsrc_adouble(vfs_handle_struct *handle,
3938 const struct smb_filename *smb_fname,
3942 struct adouble *ad = NULL;
3943 struct smb_filename *adp_smb_fname = NULL;
3945 if (!force_unlink) {
3946 ad = ad_get(talloc_tos(), handle, smb_fname,
3955 * 0 byte resource fork streams are not listed by
3956 * vfs_streaminfo, as a result stream cleanup/deletion of file
3957 * deletion doesn't remove the resourcefork stream.
3960 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
3961 /* OS X ignores resource fork stream delete requests */
3969 rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
3974 rc = SMB_VFS_NEXT_UNLINK(handle, adp_smb_fname);
3975 TALLOC_FREE(adp_smb_fname);
3976 if ((rc != 0) && (errno == ENOENT) && force_unlink) {
3983 static int fruit_unlink_rsrc_xattr(vfs_handle_struct *handle,
3984 const struct smb_filename *smb_fname,
3988 * OS X ignores resource fork stream delete requests, so nothing to do
3989 * here. Removing the file will remove the xattr anyway, so we don't
3990 * have to take care of removing 0 byte resource forks that could be
3996 static int fruit_unlink_rsrc(vfs_handle_struct *handle,
3997 const struct smb_filename *smb_fname,
4000 struct fruit_config_data *config = NULL;
4003 SMB_VFS_HANDLE_GET_DATA(handle, config,
4004 struct fruit_config_data, return -1);
4006 switch (config->rsrc) {
4007 case FRUIT_RSRC_STREAM:
4008 rc = fruit_unlink_rsrc_stream(handle, smb_fname, force_unlink);
4011 case FRUIT_RSRC_ADFILE:
4012 rc = fruit_unlink_rsrc_adouble(handle, smb_fname, force_unlink);
4015 case FRUIT_RSRC_XATTR:
4016 rc = fruit_unlink_rsrc_xattr(handle, smb_fname, force_unlink);
4020 DBG_ERR("Unsupported rsrc config [%d]\n", config->rsrc);
4027 static int fruit_unlink(vfs_handle_struct *handle,
4028 const struct smb_filename *smb_fname)
4031 struct fruit_config_data *config = NULL;
4032 struct smb_filename *rsrc_smb_fname = NULL;
4034 SMB_VFS_HANDLE_GET_DATA(handle, config,
4035 struct fruit_config_data, return -1);
4037 if (is_afpinfo_stream(smb_fname)) {
4038 return fruit_unlink_meta(handle, smb_fname);
4039 } else if (is_afpresource_stream(smb_fname)) {
4040 return fruit_unlink_rsrc(handle, smb_fname, false);
4041 } else if (is_ntfs_stream_smb_fname(smb_fname)) {
4042 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4043 } else if (is_adouble_file(smb_fname->base_name)) {
4044 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4048 * A request to delete the base file. Because 0 byte resource
4049 * fork streams are not listed by fruit_streaminfo,
4050 * delete_all_streams() can't remove 0 byte resource fork
4051 * streams, so we have to cleanup this here.
4053 rsrc_smb_fname = synthetic_smb_fname(talloc_tos(),
4054 smb_fname->base_name,
4055 AFPRESOURCE_STREAM_NAME,
4058 if (rsrc_smb_fname == NULL) {
4062 rc = fruit_unlink_rsrc(handle, rsrc_smb_fname, true);
4063 if ((rc != 0) && (errno != ENOENT)) {
4064 DBG_ERR("Forced unlink of [%s] failed [%s]\n",
4065 smb_fname_str_dbg(rsrc_smb_fname), strerror(errno));
4066 TALLOC_FREE(rsrc_smb_fname);
4069 TALLOC_FREE(rsrc_smb_fname);
4071 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4074 static int fruit_chmod(vfs_handle_struct *handle,
4075 const struct smb_filename *smb_fname,
4079 struct fruit_config_data *config = NULL;
4080 struct smb_filename *smb_fname_adp = NULL;
4082 rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname, mode);
4087 SMB_VFS_HANDLE_GET_DATA(handle, config,
4088 struct fruit_config_data, return -1);
4090 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4094 if (!VALID_STAT(smb_fname->st)) {
4098 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
4102 rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_adp);
4107 DEBUG(10, ("fruit_chmod: %s\n", smb_fname_adp->base_name));
4109 rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname_adp, mode);
4110 if (errno == ENOENT) {
4114 TALLOC_FREE(smb_fname_adp);
4118 static int fruit_chown(vfs_handle_struct *handle,
4119 const struct smb_filename *smb_fname,
4124 struct fruit_config_data *config = NULL;
4125 struct smb_filename *adp_smb_fname = NULL;
4127 rc = SMB_VFS_NEXT_CHOWN(handle, smb_fname, uid, gid);
4132 SMB_VFS_HANDLE_GET_DATA(handle, config,
4133 struct fruit_config_data, return -1);
4135 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4139 if (!VALID_STAT(smb_fname->st)) {
4143 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
4147 rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
4152 DEBUG(10, ("fruit_chown: %s\n", adp_smb_fname->base_name));
4154 rc = SMB_VFS_NEXT_CHOWN(handle, adp_smb_fname, uid, gid);
4155 if (errno == ENOENT) {
4160 TALLOC_FREE(adp_smb_fname);
4164 static int fruit_rmdir(struct vfs_handle_struct *handle,
4165 const struct smb_filename *smb_fname)
4169 struct fruit_config_data *config;
4171 SMB_VFS_HANDLE_GET_DATA(handle, config,
4172 struct fruit_config_data, return -1);
4174 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4179 * Due to there is no way to change bDeleteVetoFiles variable
4180 * from this module, need to clean up ourselves
4183 dh = SMB_VFS_OPENDIR(handle->conn, smb_fname, NULL, 0);
4188 while ((de = SMB_VFS_READDIR(handle->conn, dh, NULL)) != NULL) {
4189 struct adouble *ad = NULL;
4191 struct smb_filename *ad_smb_fname = NULL;
4194 if (!is_adouble_file(de->d_name)) {
4198 p = talloc_asprintf(talloc_tos(), "%s/%s",
4199 smb_fname->base_name, de->d_name);
4201 DBG_ERR("talloc_asprintf failed\n");
4205 ad_smb_fname = synthetic_smb_fname(talloc_tos(), p,
4209 if (ad_smb_fname == NULL) {
4210 DBG_ERR("synthetic_smb_fname failed\n");
4215 * Check whether it's a valid AppleDouble file, if
4216 * yes, delete it, ignore it otherwise.
4218 ad = ad_get(talloc_tos(), handle, ad_smb_fname, ADOUBLE_RSRC);
4220 TALLOC_FREE(ad_smb_fname);
4226 ret = SMB_VFS_NEXT_UNLINK(handle, ad_smb_fname);
4228 DBG_ERR("Deleting [%s] failed\n",
4229 smb_fname_str_dbg(ad_smb_fname));
4231 TALLOC_FREE(ad_smb_fname);
4236 SMB_VFS_CLOSEDIR(handle->conn, dh);
4238 return SMB_VFS_NEXT_RMDIR(handle, smb_fname);
4241 static ssize_t fruit_pread_meta_stream(vfs_handle_struct *handle,
4242 files_struct *fsp, void *data,
4243 size_t n, off_t offset)
4248 nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4249 if (nread == -1 || nread == n) {
4253 DBG_ERR("Removing [%s] after short read [%zd]\n",
4254 fsp_str_dbg(fsp), nread);
4256 ret = SMB_VFS_NEXT_UNLINK(handle, fsp->fsp_name);
4258 DBG_ERR("Removing [%s] failed\n", fsp_str_dbg(fsp));
4266 static ssize_t fruit_pread_meta_adouble(vfs_handle_struct *handle,
4267 files_struct *fsp, void *data,
4268 size_t n, off_t offset)
4271 struct adouble *ad = NULL;
4272 char afpinfo_buf[AFP_INFO_SIZE];
4276 ai = afpinfo_new(talloc_tos());
4281 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
4287 p = ad_get_entry(ad, ADEID_FINDERI);
4289 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
4294 memcpy(&ai->afpi_FinderInfo[0], p, ADEDLEN_FINDERI);
4296 nread = afpinfo_pack(ai, afpinfo_buf);
4297 if (nread != AFP_INFO_SIZE) {
4302 memcpy(data, afpinfo_buf, n);
4310 static ssize_t fruit_pread_meta(vfs_handle_struct *handle,
4311 files_struct *fsp, void *data,
4312 size_t n, off_t offset)
4314 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4319 * OS X has a off-by-1 error in the offset calculation, so we're
4320 * bug compatible here. It won't hurt, as any relevant real
4321 * world read requests from the AFP_AfpInfo stream will be
4322 * offset=0 n=60. offset is ignored anyway, see below.
4324 if ((offset < 0) || (offset >= AFP_INFO_SIZE + 1)) {
4329 DBG_ERR("Failed to fetch fsp extension");
4333 /* Yes, macOS always reads from offset 0 */
4335 to_return = MIN(n, AFP_INFO_SIZE);
4337 switch (fio->config->meta) {
4338 case FRUIT_META_STREAM:
4339 nread = fruit_pread_meta_stream(handle, fsp, data,
4343 case FRUIT_META_NETATALK:
4344 nread = fruit_pread_meta_adouble(handle, fsp, data,
4349 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
4353 if (nread == -1 && fio->created) {
4355 char afpinfo_buf[AFP_INFO_SIZE];
4357 ai = afpinfo_new(talloc_tos());
4362 nread = afpinfo_pack(ai, afpinfo_buf);
4364 if (nread != AFP_INFO_SIZE) {
4368 memcpy(data, afpinfo_buf, to_return);
4375 static ssize_t fruit_pread_rsrc_stream(vfs_handle_struct *handle,
4376 files_struct *fsp, void *data,
4377 size_t n, off_t offset)
4379 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4382 static ssize_t fruit_pread_rsrc_xattr(vfs_handle_struct *handle,
4383 files_struct *fsp, void *data,
4384 size_t n, off_t offset)
4386 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4389 static ssize_t fruit_pread_rsrc_adouble(vfs_handle_struct *handle,
4390 files_struct *fsp, void *data,
4391 size_t n, off_t offset)
4393 struct adouble *ad = NULL;
4396 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
4401 nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n,
4402 offset + ad_getentryoff(ad, ADEID_RFORK));
4408 static ssize_t fruit_pread_rsrc(vfs_handle_struct *handle,
4409 files_struct *fsp, void *data,
4410 size_t n, off_t offset)
4412 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4420 switch (fio->config->rsrc) {
4421 case FRUIT_RSRC_STREAM:
4422 nread = fruit_pread_rsrc_stream(handle, fsp, data, n, offset);
4425 case FRUIT_RSRC_ADFILE:
4426 nread = fruit_pread_rsrc_adouble(handle, fsp, data, n, offset);
4429 case FRUIT_RSRC_XATTR:
4430 nread = fruit_pread_rsrc_xattr(handle, fsp, data, n, offset);
4434 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
4441 static ssize_t fruit_pread(vfs_handle_struct *handle,
4442 files_struct *fsp, void *data,
4443 size_t n, off_t offset)
4445 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4448 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4449 fsp_str_dbg(fsp), (intmax_t)offset, n);
4452 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4455 if (fio->type == ADOUBLE_META) {
4456 nread = fruit_pread_meta(handle, fsp, data, n, offset);
4458 nread = fruit_pread_rsrc(handle, fsp, data, n, offset);
4461 DBG_DEBUG("Path [%s] nread [%zd]\n", fsp_str_dbg(fsp), nread);
4465 static bool fruit_must_handle_aio_stream(struct fio *fio)
4471 if (fio->type == ADOUBLE_META) {
4475 if ((fio->type == ADOUBLE_RSRC) &&
4476 (fio->config->rsrc == FRUIT_RSRC_ADFILE))
4484 struct fruit_pread_state {
4486 struct vfs_aio_state vfs_aio_state;
4489 static void fruit_pread_done(struct tevent_req *subreq);
4491 static struct tevent_req *fruit_pread_send(
4492 struct vfs_handle_struct *handle,
4493 TALLOC_CTX *mem_ctx,
4494 struct tevent_context *ev,
4495 struct files_struct *fsp,
4497 size_t n, off_t offset)
4499 struct tevent_req *req = NULL;
4500 struct tevent_req *subreq = NULL;
4501 struct fruit_pread_state *state = NULL;
4502 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4504 req = tevent_req_create(mem_ctx, &state,
4505 struct fruit_pread_state);
4510 if (fruit_must_handle_aio_stream(fio)) {
4511 state->nread = SMB_VFS_PREAD(fsp, data, n, offset);
4512 if (state->nread != n) {
4513 if (state->nread != -1) {
4516 tevent_req_error(req, errno);
4517 return tevent_req_post(req, ev);
4519 tevent_req_done(req);
4520 return tevent_req_post(req, ev);
4523 subreq = SMB_VFS_NEXT_PREAD_SEND(state, ev, handle, fsp,
4525 if (tevent_req_nomem(req, subreq)) {
4526 return tevent_req_post(req, ev);
4528 tevent_req_set_callback(subreq, fruit_pread_done, req);
4532 static void fruit_pread_done(struct tevent_req *subreq)
4534 struct tevent_req *req = tevent_req_callback_data(
4535 subreq, struct tevent_req);
4536 struct fruit_pread_state *state = tevent_req_data(
4537 req, struct fruit_pread_state);
4539 state->nread = SMB_VFS_PREAD_RECV(subreq, &state->vfs_aio_state);
4540 TALLOC_FREE(subreq);
4542 if (tevent_req_error(req, state->vfs_aio_state.error)) {
4545 tevent_req_done(req);
4548 static ssize_t fruit_pread_recv(struct tevent_req *req,
4549 struct vfs_aio_state *vfs_aio_state)
4551 struct fruit_pread_state *state = tevent_req_data(
4552 req, struct fruit_pread_state);
4554 if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
4558 *vfs_aio_state = state->vfs_aio_state;
4559 return state->nread;
4562 static ssize_t fruit_pwrite_meta_stream(vfs_handle_struct *handle,
4563 files_struct *fsp, const void *data,
4564 size_t n, off_t offset)
4566 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4572 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4573 fsp_str_dbg(fsp), (intmax_t)offset, n);
4582 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
4584 DBG_ERR("Close [%s] failed: %s\n",
4585 fsp_str_dbg(fsp), strerror(errno));
4590 fd = SMB_VFS_NEXT_OPEN(handle,
4596 DBG_ERR("On-demand create [%s] in write failed: %s\n",
4597 fsp_str_dbg(fsp), strerror(errno));
4601 fio->fake_fd = false;
4604 ai = afpinfo_unpack(talloc_tos(), data);
4609 if (ai_empty_finderinfo(ai)) {
4611 * Writing an all 0 blob to the metadata stream results in the
4612 * stream being removed on a macOS server. This ensures we
4613 * behave the same and it verified by the "delete AFP_AfpInfo by
4614 * writing all 0" test.
4616 ret = SMB_VFS_NEXT_FTRUNCATE(handle, fsp, 0);
4618 DBG_ERR("SMB_VFS_NEXT_FTRUNCATE on [%s] failed\n",
4623 ok = set_delete_on_close(
4626 handle->conn->session_info->security_token,
4627 handle->conn->session_info->unix_token);
4629 DBG_ERR("set_delete_on_close on [%s] failed\n",
4636 nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4637 if (nwritten != n) {
4644 static ssize_t fruit_pwrite_meta_netatalk(vfs_handle_struct *handle,
4645 files_struct *fsp, const void *data,
4646 size_t n, off_t offset)
4648 struct adouble *ad = NULL;
4654 ai = afpinfo_unpack(talloc_tos(), data);
4659 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
4661 ad = ad_init(talloc_tos(), handle, ADOUBLE_META);
4666 p = ad_get_entry(ad, ADEID_FINDERI);
4668 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
4673 memcpy(p, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
4675 ret = ad_fset(handle, ad, fsp);
4677 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
4684 if (!ai_empty_finderinfo(ai)) {
4689 * Writing an all 0 blob to the metadata stream results in the stream
4690 * being removed on a macOS server. This ensures we behave the same and
4691 * it verified by the "delete AFP_AfpInfo by writing all 0" test.
4694 ok = set_delete_on_close(
4697 handle->conn->session_info->security_token,
4698 handle->conn->session_info->unix_token);
4700 DBG_ERR("set_delete_on_close on [%s] failed\n",
4708 static ssize_t fruit_pwrite_meta(vfs_handle_struct *handle,
4709 files_struct *fsp, const void *data,
4710 size_t n, off_t offset)
4712 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4714 uint8_t buf[AFP_INFO_SIZE];
4720 DBG_ERR("Failed to fetch fsp extension");
4729 if (offset != 0 && n < 60) {
4734 cmp = memcmp(data, "AFP", 3);
4740 if (n <= AFP_OFF_FinderInfo) {
4742 * Nothing to do here really, just return
4750 if (to_copy > AFP_INFO_SIZE) {
4751 to_copy = AFP_INFO_SIZE;
4753 memcpy(buf, data, to_copy);
4756 if (to_write != AFP_INFO_SIZE) {
4757 to_write = AFP_INFO_SIZE;
4760 switch (fio->config->meta) {
4761 case FRUIT_META_STREAM:
4762 nwritten = fruit_pwrite_meta_stream(handle,
4769 case FRUIT_META_NETATALK:
4770 nwritten = fruit_pwrite_meta_netatalk(handle,
4778 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
4782 if (nwritten != to_write) {
4787 * Return the requested amount, verified against macOS SMB server
4792 static ssize_t fruit_pwrite_rsrc_stream(vfs_handle_struct *handle,
4793 files_struct *fsp, const void *data,
4794 size_t n, off_t offset)
4796 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4799 static ssize_t fruit_pwrite_rsrc_xattr(vfs_handle_struct *handle,
4800 files_struct *fsp, const void *data,
4801 size_t n, off_t offset)
4803 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4806 static ssize_t fruit_pwrite_rsrc_adouble(vfs_handle_struct *handle,
4807 files_struct *fsp, const void *data,
4808 size_t n, off_t offset)
4810 struct adouble *ad = NULL;
4814 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
4816 DBG_ERR("ad_get [%s] failed\n", fsp_str_dbg(fsp));
4820 nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n,
4821 offset + ad_getentryoff(ad, ADEID_RFORK));
4822 if (nwritten != n) {
4823 DBG_ERR("Short write on [%s] [%zd/%zd]\n",
4824 fsp_str_dbg(fsp), nwritten, n);
4829 if ((n + offset) > ad_getentrylen(ad, ADEID_RFORK)) {
4830 ad_setentrylen(ad, ADEID_RFORK, n + offset);
4831 ret = ad_fset(handle, ad, fsp);
4833 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
4843 static ssize_t fruit_pwrite_rsrc(vfs_handle_struct *handle,
4844 files_struct *fsp, const void *data,
4845 size_t n, off_t offset)
4847 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4851 DBG_ERR("Failed to fetch fsp extension");
4855 switch (fio->config->rsrc) {
4856 case FRUIT_RSRC_STREAM:
4857 nwritten = fruit_pwrite_rsrc_stream(handle, fsp, data, n, offset);
4860 case FRUIT_RSRC_ADFILE:
4861 nwritten = fruit_pwrite_rsrc_adouble(handle, fsp, data, n, offset);
4864 case FRUIT_RSRC_XATTR:
4865 nwritten = fruit_pwrite_rsrc_xattr(handle, fsp, data, n, offset);
4869 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
4876 static ssize_t fruit_pwrite(vfs_handle_struct *handle,
4877 files_struct *fsp, const void *data,
4878 size_t n, off_t offset)
4880 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4883 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4884 fsp_str_dbg(fsp), (intmax_t)offset, n);
4887 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4890 if (fio->type == ADOUBLE_META) {
4891 nwritten = fruit_pwrite_meta(handle, fsp, data, n, offset);
4893 nwritten = fruit_pwrite_rsrc(handle, fsp, data, n, offset);
4896 DBG_DEBUG("Path [%s] nwritten=%zd\n", fsp_str_dbg(fsp), nwritten);
4900 struct fruit_pwrite_state {
4902 struct vfs_aio_state vfs_aio_state;
4905 static void fruit_pwrite_done(struct tevent_req *subreq);
4907 static struct tevent_req *fruit_pwrite_send(
4908 struct vfs_handle_struct *handle,
4909 TALLOC_CTX *mem_ctx,
4910 struct tevent_context *ev,
4911 struct files_struct *fsp,
4913 size_t n, off_t offset)
4915 struct tevent_req *req = NULL;
4916 struct tevent_req *subreq = NULL;
4917 struct fruit_pwrite_state *state = NULL;
4918 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4920 req = tevent_req_create(mem_ctx, &state,
4921 struct fruit_pwrite_state);
4926 if (fruit_must_handle_aio_stream(fio)) {
4927 state->nwritten = SMB_VFS_PWRITE(fsp, data, n, offset);
4928 if (state->nwritten != n) {
4929 if (state->nwritten != -1) {
4932 tevent_req_error(req, errno);
4933 return tevent_req_post(req, ev);
4935 tevent_req_done(req);
4936 return tevent_req_post(req, ev);
4939 subreq = SMB_VFS_NEXT_PWRITE_SEND(state, ev, handle, fsp,
4941 if (tevent_req_nomem(req, subreq)) {
4942 return tevent_req_post(req, ev);
4944 tevent_req_set_callback(subreq, fruit_pwrite_done, req);
4948 static void fruit_pwrite_done(struct tevent_req *subreq)
4950 struct tevent_req *req = tevent_req_callback_data(
4951 subreq, struct tevent_req);
4952 struct fruit_pwrite_state *state = tevent_req_data(
4953 req, struct fruit_pwrite_state);
4955 state->nwritten = SMB_VFS_PWRITE_RECV(subreq, &state->vfs_aio_state);
4956 TALLOC_FREE(subreq);
4958 if (tevent_req_error(req, state->vfs_aio_state.error)) {
4961 tevent_req_done(req);
4964 static ssize_t fruit_pwrite_recv(struct tevent_req *req,
4965 struct vfs_aio_state *vfs_aio_state)
4967 struct fruit_pwrite_state *state = tevent_req_data(
4968 req, struct fruit_pwrite_state);
4970 if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
4974 *vfs_aio_state = state->vfs_aio_state;
4975 return state->nwritten;
4979 * Helper to stat/lstat the base file of an smb_fname.
4981 static int fruit_stat_base(vfs_handle_struct *handle,
4982 struct smb_filename *smb_fname,
4985 char *tmp_stream_name;
4988 tmp_stream_name = smb_fname->stream_name;
4989 smb_fname->stream_name = NULL;
4991 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
4993 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
4995 smb_fname->stream_name = tmp_stream_name;
4997 DBG_DEBUG("fruit_stat_base [%s] dev [%ju] ino [%ju]\n",
4998 smb_fname->base_name,
4999 (uintmax_t)smb_fname->st.st_ex_dev,
5000 (uintmax_t)smb_fname->st.st_ex_ino);
5004 static int fruit_stat_meta_stream(vfs_handle_struct *handle,
5005 struct smb_filename *smb_fname,
5011 ret = fruit_stat_base(handle, smb_fname, false);
5016 ino = fruit_inode(&smb_fname->st, smb_fname->stream_name);
5019 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
5021 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5024 smb_fname->st.st_ex_ino = ino;
5029 static int fruit_stat_meta_netatalk(vfs_handle_struct *handle,
5030 struct smb_filename *smb_fname,
5033 struct adouble *ad = NULL;
5035 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5037 DBG_INFO("fruit_stat_meta %s: %s\n",
5038 smb_fname_str_dbg(smb_fname), strerror(errno));
5044 /* Populate the stat struct with info from the base file. */
5045 if (fruit_stat_base(handle, smb_fname, follow_links) == -1) {
5048 smb_fname->st.st_ex_size = AFP_INFO_SIZE;
5049 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5050 smb_fname->stream_name);
5054 static int fruit_stat_meta(vfs_handle_struct *handle,
5055 struct smb_filename *smb_fname,
5058 struct fruit_config_data *config = NULL;
5061 SMB_VFS_HANDLE_GET_DATA(handle, config,
5062 struct fruit_config_data, return -1);
5064 switch (config->meta) {
5065 case FRUIT_META_STREAM:
5066 ret = fruit_stat_meta_stream(handle, smb_fname, follow_links);
5069 case FRUIT_META_NETATALK:
5070 ret = fruit_stat_meta_netatalk(handle, smb_fname, follow_links);
5074 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
5081 static int fruit_stat_rsrc_netatalk(vfs_handle_struct *handle,
5082 struct smb_filename *smb_fname,
5085 struct adouble *ad = NULL;
5088 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
5094 /* Populate the stat struct with info from the base file. */
5095 ret = fruit_stat_base(handle, smb_fname, follow_links);
5101 smb_fname->st.st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
5102 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5103 smb_fname->stream_name);
5108 static int fruit_stat_rsrc_stream(vfs_handle_struct *handle,
5109 struct smb_filename *smb_fname,
5115 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
5117 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5123 static int fruit_stat_rsrc_xattr(vfs_handle_struct *handle,
5124 struct smb_filename *smb_fname,
5127 #ifdef HAVE_ATTROPEN
5131 /* Populate the stat struct with info from the base file. */
5132 ret = fruit_stat_base(handle, smb_fname, follow_links);
5137 fd = attropen(smb_fname->base_name,
5138 AFPRESOURCE_EA_NETATALK,
5144 ret = sys_fstat(fd, &smb_fname->st, false);
5147 DBG_ERR("fstat [%s:%s] failed\n", smb_fname->base_name,
5148 AFPRESOURCE_EA_NETATALK);
5154 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5155 smb_fname->stream_name);
5165 static int fruit_stat_rsrc(vfs_handle_struct *handle,
5166 struct smb_filename *smb_fname,
5169 struct fruit_config_data *config = NULL;
5172 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
5174 SMB_VFS_HANDLE_GET_DATA(handle, config,
5175 struct fruit_config_data, return -1);
5177 switch (config->rsrc) {
5178 case FRUIT_RSRC_STREAM:
5179 ret = fruit_stat_rsrc_stream(handle, smb_fname, follow_links);
5182 case FRUIT_RSRC_XATTR:
5183 ret = fruit_stat_rsrc_xattr(handle, smb_fname, follow_links);
5186 case FRUIT_RSRC_ADFILE:
5187 ret = fruit_stat_rsrc_netatalk(handle, smb_fname, follow_links);
5191 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
5198 static int fruit_stat(vfs_handle_struct *handle,
5199 struct smb_filename *smb_fname)
5203 DEBUG(10, ("fruit_stat called for %s\n",
5204 smb_fname_str_dbg(smb_fname)));
5206 if (!is_ntfs_stream_smb_fname(smb_fname)
5207 || is_ntfs_default_stream_smb_fname(smb_fname)) {
5208 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
5210 update_btime(handle, smb_fname);
5216 * Note if lp_posix_paths() is true, we can never
5217 * get here as is_ntfs_stream_smb_fname() is
5218 * always false. So we never need worry about
5219 * not following links here.
5222 if (is_afpinfo_stream(smb_fname)) {
5223 rc = fruit_stat_meta(handle, smb_fname, true);
5224 } else if (is_afpresource_stream(smb_fname)) {
5225 rc = fruit_stat_rsrc(handle, smb_fname, true);
5227 return SMB_VFS_NEXT_STAT(handle, smb_fname);
5231 update_btime(handle, smb_fname);
5232 smb_fname->st.st_ex_mode &= ~S_IFMT;
5233 smb_fname->st.st_ex_mode |= S_IFREG;
5234 smb_fname->st.st_ex_blocks =
5235 smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
5240 static int fruit_lstat(vfs_handle_struct *handle,
5241 struct smb_filename *smb_fname)
5245 DEBUG(10, ("fruit_lstat called for %s\n",
5246 smb_fname_str_dbg(smb_fname)));
5248 if (!is_ntfs_stream_smb_fname(smb_fname)
5249 || is_ntfs_default_stream_smb_fname(smb_fname)) {
5250 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5252 update_btime(handle, smb_fname);
5257 if (is_afpinfo_stream(smb_fname)) {
5258 rc = fruit_stat_meta(handle, smb_fname, false);
5259 } else if (is_afpresource_stream(smb_fname)) {
5260 rc = fruit_stat_rsrc(handle, smb_fname, false);
5262 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5266 update_btime(handle, smb_fname);
5267 smb_fname->st.st_ex_mode &= ~S_IFMT;
5268 smb_fname->st.st_ex_mode |= S_IFREG;
5269 smb_fname->st.st_ex_blocks =
5270 smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
5275 static int fruit_fstat_meta_stream(vfs_handle_struct *handle,
5277 SMB_STRUCT_STAT *sbuf)
5279 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5280 struct smb_filename smb_fname;
5289 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5294 *sbuf = fsp->base_fsp->fsp_name->st;
5295 sbuf->st_ex_size = AFP_INFO_SIZE;
5296 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5300 smb_fname = (struct smb_filename) {
5301 .base_name = fsp->fsp_name->base_name,
5304 ret = fruit_stat_base(handle, &smb_fname, false);
5308 *sbuf = smb_fname.st;
5310 ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5312 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5317 sbuf->st_ex_ino = ino;
5321 static int fruit_fstat_meta_netatalk(vfs_handle_struct *handle,
5323 SMB_STRUCT_STAT *sbuf)
5327 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5332 *sbuf = fsp->base_fsp->fsp_name->st;
5333 sbuf->st_ex_size = AFP_INFO_SIZE;
5334 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5339 static int fruit_fstat_meta(vfs_handle_struct *handle,
5341 SMB_STRUCT_STAT *sbuf,
5346 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
5348 switch (fio->config->meta) {
5349 case FRUIT_META_STREAM:
5350 ret = fruit_fstat_meta_stream(handle, fsp, sbuf);
5353 case FRUIT_META_NETATALK:
5354 ret = fruit_fstat_meta_netatalk(handle, fsp, sbuf);
5358 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
5362 DBG_DEBUG("Path [%s] ret [%d]\n", fsp_str_dbg(fsp), ret);
5366 static int fruit_fstat_rsrc_xattr(vfs_handle_struct *handle,
5368 SMB_STRUCT_STAT *sbuf)
5370 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5373 static int fruit_fstat_rsrc_stream(vfs_handle_struct *handle,
5375 SMB_STRUCT_STAT *sbuf)
5377 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5380 static int fruit_fstat_rsrc_adouble(vfs_handle_struct *handle,
5382 SMB_STRUCT_STAT *sbuf)
5384 struct adouble *ad = NULL;
5387 /* Populate the stat struct with info from the base file. */
5388 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5393 ad = ad_get(talloc_tos(), handle,
5394 fsp->base_fsp->fsp_name,
5397 DBG_ERR("ad_get [%s] failed [%s]\n",
5398 fsp_str_dbg(fsp), strerror(errno));
5402 *sbuf = fsp->base_fsp->fsp_name->st;
5403 sbuf->st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
5404 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5410 static int fruit_fstat_rsrc(vfs_handle_struct *handle, files_struct *fsp,
5411 SMB_STRUCT_STAT *sbuf, struct fio *fio)
5415 switch (fio->config->rsrc) {
5416 case FRUIT_RSRC_STREAM:
5417 ret = fruit_fstat_rsrc_stream(handle, fsp, sbuf);
5420 case FRUIT_RSRC_ADFILE:
5421 ret = fruit_fstat_rsrc_adouble(handle, fsp, sbuf);
5424 case FRUIT_RSRC_XATTR:
5425 ret = fruit_fstat_rsrc_xattr(handle, fsp, sbuf);
5429 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
5436 static int fruit_fstat(vfs_handle_struct *handle, files_struct *fsp,
5437 SMB_STRUCT_STAT *sbuf)
5439 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5443 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5446 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
5448 if (fio->type == ADOUBLE_META) {
5449 rc = fruit_fstat_meta(handle, fsp, sbuf, fio);
5451 rc = fruit_fstat_rsrc(handle, fsp, sbuf, fio);
5455 sbuf->st_ex_mode &= ~S_IFMT;
5456 sbuf->st_ex_mode |= S_IFREG;
5457 sbuf->st_ex_blocks = sbuf->st_ex_size / STAT_ST_BLOCKSIZE + 1;
5460 DBG_DEBUG("Path [%s] rc [%d] size [%"PRIdMAX"]\n",
5461 fsp_str_dbg(fsp), rc, (intmax_t)sbuf->st_ex_size);
5465 static NTSTATUS delete_invalid_meta_stream(
5466 vfs_handle_struct *handle,
5467 const struct smb_filename *smb_fname,
5468 TALLOC_CTX *mem_ctx,
5469 unsigned int *pnum_streams,
5470 struct stream_struct **pstreams,
5473 struct smb_filename *sname = NULL;
5477 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams, AFPINFO_STREAM);
5479 return NT_STATUS_INTERNAL_ERROR;
5483 return NT_STATUS_OK;
5486 sname = synthetic_smb_fname(talloc_tos(),
5487 smb_fname->base_name,
5488 AFPINFO_STREAM_NAME,
5490 if (sname == NULL) {
5491 return NT_STATUS_NO_MEMORY;
5494 ret = SMB_VFS_NEXT_UNLINK(handle, sname);
5497 DBG_ERR("Removing [%s] failed\n", smb_fname_str_dbg(sname));
5498 return map_nt_error_from_unix(errno);
5501 return NT_STATUS_OK;
5504 static NTSTATUS fruit_streaminfo_meta_stream(
5505 vfs_handle_struct *handle,
5506 struct files_struct *fsp,
5507 const struct smb_filename *smb_fname,
5508 TALLOC_CTX *mem_ctx,
5509 unsigned int *pnum_streams,
5510 struct stream_struct **pstreams)
5512 struct stream_struct *stream = *pstreams;
5513 unsigned int num_streams = *pnum_streams;
5516 for (i = 0; i < num_streams; i++) {
5517 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
5522 if (i == num_streams) {
5523 return NT_STATUS_OK;
5526 if (stream[i].size != AFP_INFO_SIZE) {
5527 DBG_ERR("Removing invalid AFPINFO_STREAM size [%jd] from [%s]\n",
5528 (intmax_t)stream[i].size, smb_fname_str_dbg(smb_fname));
5530 return delete_invalid_meta_stream(handle,
5539 return NT_STATUS_OK;
5542 static NTSTATUS fruit_streaminfo_meta_netatalk(
5543 vfs_handle_struct *handle,
5544 struct files_struct *fsp,
5545 const struct smb_filename *smb_fname,
5546 TALLOC_CTX *mem_ctx,
5547 unsigned int *pnum_streams,
5548 struct stream_struct **pstreams)
5550 struct stream_struct *stream = *pstreams;
5551 unsigned int num_streams = *pnum_streams;
5552 struct adouble *ad = NULL;
5557 /* Remove the Netatalk xattr from the list */
5558 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5559 ":" NETATALK_META_XATTR ":$DATA");
5561 return NT_STATUS_NO_MEMORY;
5565 * Check if there's a AFPINFO_STREAM from the VFS streams
5566 * backend and if yes, remove it from the list
5568 for (i = 0; i < num_streams; i++) {
5569 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
5574 if (i < num_streams) {
5575 DBG_WARNING("Unexpected AFPINFO_STREAM on [%s]\n",
5576 smb_fname_str_dbg(smb_fname));
5578 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5581 return NT_STATUS_INTERNAL_ERROR;
5585 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5587 return NT_STATUS_OK;
5590 is_fi_empty = ad_empty_finderinfo(ad);
5594 return NT_STATUS_OK;
5597 ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
5598 AFPINFO_STREAM_NAME, AFP_INFO_SIZE,
5599 smb_roundup(handle->conn, AFP_INFO_SIZE));
5601 return NT_STATUS_NO_MEMORY;
5604 return NT_STATUS_OK;
5607 static NTSTATUS fruit_streaminfo_meta(vfs_handle_struct *handle,
5608 struct files_struct *fsp,
5609 const struct smb_filename *smb_fname,
5610 TALLOC_CTX *mem_ctx,
5611 unsigned int *pnum_streams,
5612 struct stream_struct **pstreams)
5614 struct fruit_config_data *config = NULL;
5617 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5618 return NT_STATUS_INTERNAL_ERROR);
5620 switch (config->meta) {
5621 case FRUIT_META_NETATALK:
5622 status = fruit_streaminfo_meta_netatalk(handle, fsp, smb_fname,
5623 mem_ctx, pnum_streams,
5627 case FRUIT_META_STREAM:
5628 status = fruit_streaminfo_meta_stream(handle, fsp, smb_fname,
5629 mem_ctx, pnum_streams,
5634 return NT_STATUS_INTERNAL_ERROR;
5640 static NTSTATUS fruit_streaminfo_rsrc_stream(
5641 vfs_handle_struct *handle,
5642 struct files_struct *fsp,
5643 const struct smb_filename *smb_fname,
5644 TALLOC_CTX *mem_ctx,
5645 unsigned int *pnum_streams,
5646 struct stream_struct **pstreams)
5650 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
5652 DBG_ERR("Filtering resource stream failed\n");
5653 return NT_STATUS_INTERNAL_ERROR;
5655 return NT_STATUS_OK;
5658 static NTSTATUS fruit_streaminfo_rsrc_xattr(
5659 vfs_handle_struct *handle,
5660 struct files_struct *fsp,
5661 const struct smb_filename *smb_fname,
5662 TALLOC_CTX *mem_ctx,
5663 unsigned int *pnum_streams,
5664 struct stream_struct **pstreams)
5668 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
5670 DBG_ERR("Filtering resource stream failed\n");
5671 return NT_STATUS_INTERNAL_ERROR;
5673 return NT_STATUS_OK;
5676 static NTSTATUS fruit_streaminfo_rsrc_adouble(
5677 vfs_handle_struct *handle,
5678 struct files_struct *fsp,
5679 const struct smb_filename *smb_fname,
5680 TALLOC_CTX *mem_ctx,
5681 unsigned int *pnum_streams,
5682 struct stream_struct **pstreams)
5684 struct stream_struct *stream = *pstreams;
5685 unsigned int num_streams = *pnum_streams;
5686 struct adouble *ad = NULL;
5692 * Check if there's a AFPRESOURCE_STREAM from the VFS streams backend
5693 * and if yes, remove it from the list
5695 for (i = 0; i < num_streams; i++) {
5696 if (strequal_m(stream[i].name, AFPRESOURCE_STREAM)) {
5701 if (i < num_streams) {
5702 DBG_WARNING("Unexpected AFPRESOURCE_STREAM on [%s]\n",
5703 smb_fname_str_dbg(smb_fname));
5705 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5706 AFPRESOURCE_STREAM);
5708 return NT_STATUS_INTERNAL_ERROR;
5712 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
5714 return NT_STATUS_OK;
5717 rlen = ad_getentrylen(ad, ADEID_RFORK);
5721 return NT_STATUS_OK;
5724 ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
5725 AFPRESOURCE_STREAM_NAME, rlen,
5726 smb_roundup(handle->conn, rlen));
5728 return NT_STATUS_NO_MEMORY;
5731 return NT_STATUS_OK;
5734 static NTSTATUS fruit_streaminfo_rsrc(vfs_handle_struct *handle,
5735 struct files_struct *fsp,
5736 const struct smb_filename *smb_fname,
5737 TALLOC_CTX *mem_ctx,
5738 unsigned int *pnum_streams,
5739 struct stream_struct **pstreams)
5741 struct fruit_config_data *config = NULL;
5744 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5745 return NT_STATUS_INTERNAL_ERROR);
5747 switch (config->rsrc) {
5748 case FRUIT_RSRC_STREAM:
5749 status = fruit_streaminfo_rsrc_stream(handle, fsp, smb_fname,
5750 mem_ctx, pnum_streams,
5754 case FRUIT_RSRC_XATTR:
5755 status = fruit_streaminfo_rsrc_xattr(handle, fsp, smb_fname,
5756 mem_ctx, pnum_streams,
5760 case FRUIT_RSRC_ADFILE:
5761 status = fruit_streaminfo_rsrc_adouble(handle, fsp, smb_fname,
5762 mem_ctx, pnum_streams,
5767 return NT_STATUS_INTERNAL_ERROR;
5773 static void fruit_filter_empty_streams(unsigned int *pnum_streams,
5774 struct stream_struct **pstreams)
5776 unsigned num_streams = *pnum_streams;
5777 struct stream_struct *streams = *pstreams;
5780 if (!global_fruit_config.nego_aapl) {
5784 while (i < num_streams) {
5785 struct smb_filename smb_fname = (struct smb_filename) {
5786 .stream_name = streams[i].name,
5789 if (is_ntfs_default_stream_smb_fname(&smb_fname)
5790 || streams[i].size > 0)
5796 streams[i] = streams[num_streams - 1];
5800 *pnum_streams = num_streams;
5803 static NTSTATUS fruit_streaminfo(vfs_handle_struct *handle,
5804 struct files_struct *fsp,
5805 const struct smb_filename *smb_fname,
5806 TALLOC_CTX *mem_ctx,
5807 unsigned int *pnum_streams,
5808 struct stream_struct **pstreams)
5810 struct fruit_config_data *config = NULL;
5813 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5814 return NT_STATUS_UNSUCCESSFUL);
5816 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
5818 status = SMB_VFS_NEXT_STREAMINFO(handle, fsp, smb_fname, mem_ctx,
5819 pnum_streams, pstreams);
5820 if (!NT_STATUS_IS_OK(status)) {
5824 fruit_filter_empty_streams(pnum_streams, pstreams);
5826 status = fruit_streaminfo_meta(handle, fsp, smb_fname,
5827 mem_ctx, pnum_streams, pstreams);
5828 if (!NT_STATUS_IS_OK(status)) {
5832 status = fruit_streaminfo_rsrc(handle, fsp, smb_fname,
5833 mem_ctx, pnum_streams, pstreams);
5834 if (!NT_STATUS_IS_OK(status)) {
5838 return NT_STATUS_OK;
5841 static int fruit_ntimes(vfs_handle_struct *handle,
5842 const struct smb_filename *smb_fname,
5843 struct smb_file_time *ft)
5846 struct adouble *ad = NULL;
5847 struct fruit_config_data *config = NULL;
5849 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5852 if ((config->meta != FRUIT_META_NETATALK) ||
5853 null_timespec(ft->create_time))
5855 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
5858 DEBUG(10,("set btime for %s to %s\n", smb_fname_str_dbg(smb_fname),
5859 time_to_asc(convert_timespec_to_time_t(ft->create_time))));
5861 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5866 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX,
5867 convert_time_t_to_uint32_t(ft->create_time.tv_sec));
5869 rc = ad_set(handle, ad, smb_fname);
5875 DEBUG(1, ("fruit_ntimes: %s\n", smb_fname_str_dbg(smb_fname)));
5878 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
5881 static int fruit_fallocate(struct vfs_handle_struct *handle,
5882 struct files_struct *fsp,
5887 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5890 return SMB_VFS_NEXT_FALLOCATE(handle, fsp, mode, offset, len);
5893 /* Let the pwrite code path handle it. */
5898 static int fruit_ftruncate_rsrc_xattr(struct vfs_handle_struct *handle,
5899 struct files_struct *fsp,
5902 #ifdef HAVE_ATTROPEN
5903 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
5908 static int fruit_ftruncate_rsrc_adouble(struct vfs_handle_struct *handle,
5909 struct files_struct *fsp,
5913 struct adouble *ad = NULL;
5916 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
5918 DBG_DEBUG("ad_get [%s] failed [%s]\n",
5919 fsp_str_dbg(fsp), strerror(errno));
5923 ad_off = ad_getentryoff(ad, ADEID_RFORK);
5925 rc = ftruncate(fsp->fh->fd, offset + ad_off);
5931 ad_setentrylen(ad, ADEID_RFORK, offset);
5933 rc = ad_fset(handle, ad, fsp);
5935 DBG_ERR("ad_fset [%s] failed [%s]\n",
5936 fsp_str_dbg(fsp), strerror(errno));
5945 static int fruit_ftruncate_rsrc_stream(struct vfs_handle_struct *handle,
5946 struct files_struct *fsp,
5949 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
5952 static int fruit_ftruncate_rsrc(struct vfs_handle_struct *handle,
5953 struct files_struct *fsp,
5956 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5960 DBG_ERR("Failed to fetch fsp extension");
5964 switch (fio->config->rsrc) {
5965 case FRUIT_RSRC_XATTR:
5966 ret = fruit_ftruncate_rsrc_xattr(handle, fsp, offset);
5969 case FRUIT_RSRC_ADFILE:
5970 ret = fruit_ftruncate_rsrc_adouble(handle, fsp, offset);
5973 case FRUIT_RSRC_STREAM:
5974 ret = fruit_ftruncate_rsrc_stream(handle, fsp, offset);
5978 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
5986 static int fruit_ftruncate_meta(struct vfs_handle_struct *handle,
5987 struct files_struct *fsp,
5991 DBG_WARNING("ftruncate %s to %jd",
5992 fsp_str_dbg(fsp), (intmax_t)offset);
5993 /* OS X returns NT_STATUS_ALLOTTED_SPACE_EXCEEDED */
5998 /* OS X returns success but does nothing */
5999 DBG_INFO("ignoring ftruncate %s to %jd\n",
6000 fsp_str_dbg(fsp), (intmax_t)offset);
6004 static int fruit_ftruncate(struct vfs_handle_struct *handle,
6005 struct files_struct *fsp,
6008 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
6011 DBG_DEBUG("Path [%s] offset [%"PRIdMAX"]\n", fsp_str_dbg(fsp),
6015 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
6018 if (fio->type == ADOUBLE_META) {
6019 ret = fruit_ftruncate_meta(handle, fsp, offset);
6021 ret = fruit_ftruncate_rsrc(handle, fsp, offset);
6024 DBG_DEBUG("Path [%s] result [%d]\n", fsp_str_dbg(fsp), ret);
6028 static NTSTATUS fruit_create_file(vfs_handle_struct *handle,
6029 struct smb_request *req,
6030 uint16_t root_dir_fid,
6031 struct smb_filename *smb_fname,
6032 uint32_t access_mask,
6033 uint32_t share_access,
6034 uint32_t create_disposition,
6035 uint32_t create_options,
6036 uint32_t file_attributes,
6037 uint32_t oplock_request,
6038 struct smb2_lease *lease,
6039 uint64_t allocation_size,
6040 uint32_t private_flags,
6041 struct security_descriptor *sd,
6042 struct ea_list *ea_list,
6043 files_struct **result,
6045 const struct smb2_create_blobs *in_context_blobs,
6046 struct smb2_create_blobs *out_context_blobs)
6049 struct fruit_config_data *config = NULL;
6050 files_struct *fsp = NULL;
6051 struct fio *fio = NULL;
6052 bool internal_open = (oplock_request & INTERNAL_OPEN_ONLY);
6055 status = check_aapl(handle, req, in_context_blobs, out_context_blobs);
6056 if (!NT_STATUS_IS_OK(status)) {
6060 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
6061 return NT_STATUS_UNSUCCESSFUL);
6063 if (is_apple_stream(smb_fname) && !internal_open) {
6064 ret = ad_convert(handle, smb_fname);
6066 DBG_ERR("ad_convert() failed\n");
6067 return NT_STATUS_UNSUCCESSFUL;
6071 status = SMB_VFS_NEXT_CREATE_FILE(
6072 handle, req, root_dir_fid, smb_fname,
6073 access_mask, share_access,
6074 create_disposition, create_options,
6075 file_attributes, oplock_request,
6077 allocation_size, private_flags,
6078 sd, ea_list, result,
6079 pinfo, in_context_blobs, out_context_blobs);
6080 if (!NT_STATUS_IS_OK(status)) {
6086 if (global_fruit_config.nego_aapl) {
6087 if (config->posix_rename && fsp->is_directory) {
6089 * Enable POSIX directory rename behaviour
6091 fsp->posix_flags |= FSP_POSIX_FLAGS_RENAME;
6096 * If this is a plain open for existing files, opening an 0
6097 * byte size resource fork MUST fail with
6098 * NT_STATUS_OBJECT_NAME_NOT_FOUND.
6100 * Cf the vfs_fruit torture tests in test_rfork_create().
6102 if (global_fruit_config.nego_aapl &&
6103 create_disposition == FILE_OPEN &&
6104 smb_fname->st.st_ex_size == 0 &&
6105 is_ntfs_stream_smb_fname(smb_fname) &&
6106 !(is_ntfs_default_stream_smb_fname(smb_fname)))
6108 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
6112 fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
6113 if (fio != NULL && pinfo != NULL && *pinfo == FILE_WAS_CREATED) {
6114 fio->created = true;
6117 if (is_ntfs_stream_smb_fname(smb_fname)
6118 || fsp->is_directory) {
6122 if (config->locking == FRUIT_LOCKING_NETATALK) {
6123 status = fruit_check_access(
6127 if (!NT_STATUS_IS_OK(status)) {
6135 DEBUG(10, ("fruit_create_file: %s\n", nt_errstr(status)));
6138 close_file(req, fsp, ERROR_CLOSE);
6139 *result = fsp = NULL;
6145 static NTSTATUS fruit_readdir_attr(struct vfs_handle_struct *handle,
6146 const struct smb_filename *fname,
6147 TALLOC_CTX *mem_ctx,
6148 struct readdir_attr_data **pattr_data)
6150 struct fruit_config_data *config = NULL;
6151 struct readdir_attr_data *attr_data;
6155 SMB_VFS_HANDLE_GET_DATA(handle, config,
6156 struct fruit_config_data,
6157 return NT_STATUS_UNSUCCESSFUL);
6159 if (!global_fruit_config.nego_aapl) {
6160 return SMB_VFS_NEXT_READDIR_ATTR(handle, fname, mem_ctx, pattr_data);
6163 DEBUG(10, ("fruit_readdir_attr %s\n", fname->base_name));
6165 ret = ad_convert(handle, fname);
6167 DBG_ERR("ad_convert() failed\n");
6168 return NT_STATUS_UNSUCCESSFUL;
6171 *pattr_data = talloc_zero(mem_ctx, struct readdir_attr_data);
6172 if (*pattr_data == NULL) {
6173 return NT_STATUS_UNSUCCESSFUL;
6175 attr_data = *pattr_data;
6176 attr_data->type = RDATTR_AAPL;
6179 * Mac metadata: compressed FinderInfo, resource fork length
6182 status = readdir_attr_macmeta(handle, fname, attr_data);
6183 if (!NT_STATUS_IS_OK(status)) {
6185 * Error handling is tricky: if we return failure from
6186 * this function, the corresponding directory entry
6187 * will to be passed to the client, so we really just
6188 * want to error out on fatal errors.
6190 if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
6198 if (config->unix_info_enabled) {
6199 attr_data->attr_data.aapl.unix_mode = fname->st.st_ex_mode;
6205 if (!config->readdir_attr_max_access) {
6206 attr_data->attr_data.aapl.max_access = FILE_GENERIC_ALL;
6208 status = smbd_calculate_access_mask(
6212 SEC_FLAG_MAXIMUM_ALLOWED,
6213 &attr_data->attr_data.aapl.max_access);
6214 if (!NT_STATUS_IS_OK(status)) {
6219 return NT_STATUS_OK;
6222 DEBUG(1, ("fruit_readdir_attr %s, error: %s\n",
6223 fname->base_name, nt_errstr(status)));
6224 TALLOC_FREE(*pattr_data);
6228 static NTSTATUS fruit_fget_nt_acl(vfs_handle_struct *handle,
6230 uint32_t security_info,
6231 TALLOC_CTX *mem_ctx,
6232 struct security_descriptor **ppdesc)
6235 struct security_ace ace;
6237 struct fruit_config_data *config;
6239 SMB_VFS_HANDLE_GET_DATA(handle, config,
6240 struct fruit_config_data,
6241 return NT_STATUS_UNSUCCESSFUL);
6243 status = SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
6245 if (!NT_STATUS_IS_OK(status)) {
6250 * Add MS NFS style ACEs with uid, gid and mode
6252 if (!global_fruit_config.nego_aapl) {
6253 return NT_STATUS_OK;
6255 if (!config->unix_info_enabled) {
6256 return NT_STATUS_OK;
6259 /* First remove any existing ACE's with NFS style mode/uid/gid SIDs. */
6260 status = remove_virtual_nfs_aces(*ppdesc);
6261 if (!NT_STATUS_IS_OK(status)) {
6262 DBG_WARNING("failed to remove MS NFS style ACEs\n");
6266 /* MS NFS style mode */
6267 sid_compose(&sid, &global_sid_Unix_NFS_Mode, fsp->fsp_name->st.st_ex_mode);
6268 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6269 status = security_descriptor_dacl_add(*ppdesc, &ace);
6270 if (!NT_STATUS_IS_OK(status)) {
6271 DEBUG(1,("failed to add MS NFS style ACE\n"));
6275 /* MS NFS style uid */
6276 sid_compose(&sid, &global_sid_Unix_NFS_Users, fsp->fsp_name->st.st_ex_uid);
6277 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6278 status = security_descriptor_dacl_add(*ppdesc, &ace);
6279 if (!NT_STATUS_IS_OK(status)) {
6280 DEBUG(1,("failed to add MS NFS style ACE\n"));
6284 /* MS NFS style gid */
6285 sid_compose(&sid, &global_sid_Unix_NFS_Groups, fsp->fsp_name->st.st_ex_gid);
6286 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6287 status = security_descriptor_dacl_add(*ppdesc, &ace);
6288 if (!NT_STATUS_IS_OK(status)) {
6289 DEBUG(1,("failed to add MS NFS style ACE\n"));
6293 return NT_STATUS_OK;
6296 static NTSTATUS fruit_fset_nt_acl(vfs_handle_struct *handle,
6298 uint32_t security_info_sent,
6299 const struct security_descriptor *orig_psd)
6303 mode_t ms_nfs_mode = 0;
6305 struct security_descriptor *psd = NULL;
6306 uint32_t orig_num_aces = 0;
6308 if (orig_psd->dacl != NULL) {
6309 orig_num_aces = orig_psd->dacl->num_aces;
6312 psd = security_descriptor_copy(talloc_tos(), orig_psd);
6314 return NT_STATUS_NO_MEMORY;
6317 DBG_DEBUG("fruit_fset_nt_acl: %s\n", fsp_str_dbg(fsp));
6319 status = check_ms_nfs(handle, fsp, psd, &ms_nfs_mode, &do_chmod);
6320 if (!NT_STATUS_IS_OK(status)) {
6321 DEBUG(1, ("fruit_fset_nt_acl: check_ms_nfs failed%s\n", fsp_str_dbg(fsp)));
6327 * If only ms_nfs ACE entries were sent, ensure we set the DACL
6328 * sent/present flags correctly now we've removed them.
6331 if (orig_num_aces != 0) {
6333 * Are there any ACE's left ?
6335 if (psd->dacl->num_aces == 0) {
6336 /* No - clear the DACL sent/present flags. */
6337 security_info_sent &= ~SECINFO_DACL;
6338 psd->type &= ~SEC_DESC_DACL_PRESENT;
6342 status = SMB_VFS_NEXT_FSET_NT_ACL(handle, fsp, security_info_sent, psd);
6343 if (!NT_STATUS_IS_OK(status)) {
6344 DEBUG(1, ("fruit_fset_nt_acl: SMB_VFS_NEXT_FSET_NT_ACL failed%s\n", fsp_str_dbg(fsp)));
6350 if (fsp->fh->fd != -1) {
6351 result = SMB_VFS_FCHMOD(fsp, ms_nfs_mode);
6353 result = SMB_VFS_CHMOD(fsp->conn,
6359 DEBUG(1, ("chmod: %s, result: %d, %04o error %s\n", fsp_str_dbg(fsp),
6360 result, (unsigned)ms_nfs_mode,
6362 status = map_nt_error_from_unix(errno);
6369 return NT_STATUS_OK;
6372 static struct vfs_offload_ctx *fruit_offload_ctx;
6374 struct fruit_offload_read_state {
6375 struct vfs_handle_struct *handle;
6376 struct tevent_context *ev;
6382 static void fruit_offload_read_done(struct tevent_req *subreq);
6384 static struct tevent_req *fruit_offload_read_send(
6385 TALLOC_CTX *mem_ctx,
6386 struct tevent_context *ev,
6387 struct vfs_handle_struct *handle,
6394 struct tevent_req *req = NULL;
6395 struct tevent_req *subreq = NULL;
6396 struct fruit_offload_read_state *state = NULL;
6398 req = tevent_req_create(mem_ctx, &state,
6399 struct fruit_offload_read_state);
6403 *state = (struct fruit_offload_read_state) {
6410 subreq = SMB_VFS_NEXT_OFFLOAD_READ_SEND(mem_ctx, ev, handle, fsp,
6411 fsctl, ttl, offset, to_copy);
6412 if (tevent_req_nomem(subreq, req)) {
6413 return tevent_req_post(req, ev);
6415 tevent_req_set_callback(subreq, fruit_offload_read_done, req);
6419 static void fruit_offload_read_done(struct tevent_req *subreq)
6421 struct tevent_req *req = tevent_req_callback_data(
6422 subreq, struct tevent_req);
6423 struct fruit_offload_read_state *state = tevent_req_data(
6424 req, struct fruit_offload_read_state);
6427 status = SMB_VFS_NEXT_OFFLOAD_READ_RECV(subreq,
6431 TALLOC_FREE(subreq);
6432 if (tevent_req_nterror(req, status)) {
6436 if (state->fsctl != FSCTL_SRV_REQUEST_RESUME_KEY) {
6437 tevent_req_done(req);
6441 status = vfs_offload_token_ctx_init(state->fsp->conn->sconn->client,
6442 &fruit_offload_ctx);
6443 if (tevent_req_nterror(req, status)) {
6447 status = vfs_offload_token_db_store_fsp(fruit_offload_ctx,
6450 if (tevent_req_nterror(req, status)) {
6454 tevent_req_done(req);
6458 static NTSTATUS fruit_offload_read_recv(struct tevent_req *req,
6459 struct vfs_handle_struct *handle,
6460 TALLOC_CTX *mem_ctx,
6463 struct fruit_offload_read_state *state = tevent_req_data(
6464 req, struct fruit_offload_read_state);
6467 if (tevent_req_is_nterror(req, &status)) {
6468 tevent_req_received(req);
6472 token->length = state->token.length;
6473 token->data = talloc_move(mem_ctx, &state->token.data);
6475 tevent_req_received(req);
6476 return NT_STATUS_OK;
6479 struct fruit_offload_write_state {
6480 struct vfs_handle_struct *handle;
6482 struct files_struct *src_fsp;
6483 struct files_struct *dst_fsp;
6487 static void fruit_offload_write_done(struct tevent_req *subreq);
6488 static struct tevent_req *fruit_offload_write_send(struct vfs_handle_struct *handle,
6489 TALLOC_CTX *mem_ctx,
6490 struct tevent_context *ev,
6493 off_t transfer_offset,
6494 struct files_struct *dest_fsp,
6498 struct tevent_req *req, *subreq;
6499 struct fruit_offload_write_state *state;
6501 struct fruit_config_data *config;
6502 off_t src_off = transfer_offset;
6503 files_struct *src_fsp = NULL;
6504 off_t to_copy = num;
6505 bool copyfile_enabled = false;
6507 DEBUG(10,("soff: %ju, doff: %ju, len: %ju\n",
6508 (uintmax_t)src_off, (uintmax_t)dest_off, (uintmax_t)num));
6510 SMB_VFS_HANDLE_GET_DATA(handle, config,
6511 struct fruit_config_data,
6514 req = tevent_req_create(mem_ctx, &state,
6515 struct fruit_offload_write_state);
6519 state->handle = handle;
6520 state->dst_fsp = dest_fsp;
6523 case FSCTL_SRV_COPYCHUNK:
6524 case FSCTL_SRV_COPYCHUNK_WRITE:
6525 copyfile_enabled = config->copyfile_enabled;
6532 * Check if this a OS X copyfile style copychunk request with
6533 * a requested chunk count of 0 that was translated to a
6534 * offload_write_send VFS call overloading the parameters src_off
6535 * = dest_off = num = 0.
6537 if (copyfile_enabled && num == 0 && src_off == 0 && dest_off == 0) {
6538 status = vfs_offload_token_db_fetch_fsp(
6539 fruit_offload_ctx, token, &src_fsp);
6540 if (tevent_req_nterror(req, status)) {
6541 return tevent_req_post(req, ev);
6543 state->src_fsp = src_fsp;
6545 status = vfs_stat_fsp(src_fsp);
6546 if (tevent_req_nterror(req, status)) {
6547 return tevent_req_post(req, ev);
6550 to_copy = src_fsp->fsp_name->st.st_ex_size;
6551 state->is_copyfile = true;
6554 subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle,
6563 if (tevent_req_nomem(subreq, req)) {
6564 return tevent_req_post(req, ev);
6567 tevent_req_set_callback(subreq, fruit_offload_write_done, req);
6571 static void fruit_offload_write_done(struct tevent_req *subreq)
6573 struct tevent_req *req = tevent_req_callback_data(
6574 subreq, struct tevent_req);
6575 struct fruit_offload_write_state *state = tevent_req_data(
6576 req, struct fruit_offload_write_state);
6578 unsigned int num_streams = 0;
6579 struct stream_struct *streams = NULL;
6581 struct smb_filename *src_fname_tmp = NULL;
6582 struct smb_filename *dst_fname_tmp = NULL;
6584 status = SMB_VFS_NEXT_OFFLOAD_WRITE_RECV(state->handle,
6587 TALLOC_FREE(subreq);
6588 if (tevent_req_nterror(req, status)) {
6592 if (!state->is_copyfile) {
6593 tevent_req_done(req);
6598 * Now copy all remaining streams. We know the share supports
6599 * streams, because we're in vfs_fruit. We don't do this async
6600 * because streams are few and small.
6602 status = vfs_streaminfo(state->handle->conn, state->src_fsp,
6603 state->src_fsp->fsp_name,
6604 req, &num_streams, &streams);
6605 if (tevent_req_nterror(req, status)) {
6609 if (num_streams == 1) {
6610 /* There is always one stream, ::$DATA. */
6611 tevent_req_done(req);
6615 for (i = 0; i < num_streams; i++) {
6616 DEBUG(10, ("%s: stream: '%s'/%zu\n",
6617 __func__, streams[i].name, (size_t)streams[i].size));
6619 src_fname_tmp = synthetic_smb_fname(
6621 state->src_fsp->fsp_name->base_name,
6624 state->src_fsp->fsp_name->flags);
6625 if (tevent_req_nomem(src_fname_tmp, req)) {
6629 if (is_ntfs_default_stream_smb_fname(src_fname_tmp)) {
6630 TALLOC_FREE(src_fname_tmp);
6634 dst_fname_tmp = synthetic_smb_fname(
6636 state->dst_fsp->fsp_name->base_name,
6639 state->dst_fsp->fsp_name->flags);
6640 if (tevent_req_nomem(dst_fname_tmp, req)) {
6641 TALLOC_FREE(src_fname_tmp);
6645 status = copy_file(req,
6646 state->handle->conn,
6649 OPENX_FILE_CREATE_IF_NOT_EXIST,
6651 if (!NT_STATUS_IS_OK(status)) {
6652 DEBUG(1, ("%s: copy %s to %s failed: %s\n", __func__,
6653 smb_fname_str_dbg(src_fname_tmp),
6654 smb_fname_str_dbg(dst_fname_tmp),
6655 nt_errstr(status)));
6656 TALLOC_FREE(src_fname_tmp);
6657 TALLOC_FREE(dst_fname_tmp);
6658 tevent_req_nterror(req, status);
6662 TALLOC_FREE(src_fname_tmp);
6663 TALLOC_FREE(dst_fname_tmp);
6666 TALLOC_FREE(streams);
6667 TALLOC_FREE(src_fname_tmp);
6668 TALLOC_FREE(dst_fname_tmp);
6669 tevent_req_done(req);
6672 static NTSTATUS fruit_offload_write_recv(struct vfs_handle_struct *handle,
6673 struct tevent_req *req,
6676 struct fruit_offload_write_state *state = tevent_req_data(
6677 req, struct fruit_offload_write_state);
6680 if (tevent_req_is_nterror(req, &status)) {
6681 DEBUG(1, ("server side copy chunk failed: %s\n",
6682 nt_errstr(status)));
6684 tevent_req_received(req);
6688 *copied = state->copied;
6689 tevent_req_received(req);
6691 return NT_STATUS_OK;
6694 static char *fruit_get_bandsize_line(char **lines, int numlines)
6697 static bool re_initialized = false;
6701 if (!re_initialized) {
6702 ret = regcomp(&re, "^[[:blank:]]*<key>band-size</key>$", 0);
6706 re_initialized = true;
6709 for (i = 0; i < numlines; i++) {
6710 regmatch_t matches[1];
6712 ret = regexec(&re, lines[i], 1, matches, 0);
6715 * Check if the match was on the last line, sa we want
6716 * the subsequent line.
6718 if (i + 1 == numlines) {
6721 return lines[i + 1];
6723 if (ret != REG_NOMATCH) {
6731 static bool fruit_get_bandsize_from_line(char *line, size_t *_band_size)
6734 static bool re_initialized = false;
6735 regmatch_t matches[2];
6740 if (!re_initialized) {
6743 "<integer>\\([[:digit:]]*\\)</integer>$",
6748 re_initialized = true;
6751 ret = regexec(&re, line, 2, matches, 0);
6753 DBG_ERR("regex failed [%s]\n", line);
6757 line[matches[1].rm_eo] = '\0';
6759 ok = conv_str_u64(&line[matches[1].rm_so], &band_size);
6763 *_band_size = (size_t)band_size;
6768 * This reads and parses an Info.plist from a TM sparsebundle looking for the
6769 * "band-size" key and value.
6771 static bool fruit_get_bandsize(vfs_handle_struct *handle,
6775 #define INFO_PLIST_MAX_SIZE 64*1024
6777 struct smb_filename *smb_fname = NULL;
6778 files_struct *fsp = NULL;
6779 uint8_t *file_data = NULL;
6780 char **lines = NULL;
6781 char *band_size_line = NULL;
6782 size_t plist_file_size;
6789 plist = talloc_asprintf(talloc_tos(),
6791 handle->conn->connectpath,
6793 if (plist == NULL) {
6798 smb_fname = synthetic_smb_fname(talloc_tos(), plist, NULL, NULL, 0);
6799 if (smb_fname == NULL) {
6804 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
6806 DBG_INFO("Ignoring Sparsebundle without Info.plist [%s]\n", dir);
6811 plist_file_size = smb_fname->st.st_ex_size;
6813 if (plist_file_size > INFO_PLIST_MAX_SIZE) {
6814 DBG_INFO("%s is too large, ignoring\n", plist);
6819 status = SMB_VFS_NEXT_CREATE_FILE(
6822 0, /* root_dir_fid */
6823 smb_fname, /* fname */
6824 FILE_GENERIC_READ, /* access_mask */
6825 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
6826 FILE_OPEN, /* create_disposition */
6827 0, /* create_options */
6828 0, /* file_attributes */
6829 INTERNAL_OPEN_ONLY, /* oplock_request */
6831 0, /* allocation_size */
6832 0, /* private_flags */
6837 NULL, NULL); /* create context */
6838 if (!NT_STATUS_IS_OK(status)) {
6839 DBG_INFO("Opening [%s] failed [%s]\n",
6840 smb_fname_str_dbg(smb_fname), nt_errstr(status));
6845 file_data = talloc_array(talloc_tos(), uint8_t, plist_file_size);
6846 if (file_data == NULL) {
6851 nread = SMB_VFS_NEXT_PREAD(handle, fsp, file_data, plist_file_size, 0);
6852 if (nread != plist_file_size) {
6853 DBG_ERR("Short read on [%s]: %zu/%zd\n",
6854 fsp_str_dbg(fsp), nread, plist_file_size);
6860 status = close_file(NULL, fsp, NORMAL_CLOSE);
6862 if (!NT_STATUS_IS_OK(status)) {
6863 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
6868 lines = file_lines_parse((char *)file_data,
6872 if (lines == NULL) {
6877 band_size_line = fruit_get_bandsize_line(lines, numlines);
6878 if (band_size_line == NULL) {
6879 DBG_ERR("Didn't find band-size key in [%s]\n",
6880 smb_fname_str_dbg(smb_fname));
6885 ok = fruit_get_bandsize_from_line(band_size_line, band_size);
6887 DBG_ERR("fruit_get_bandsize_from_line failed\n");
6891 DBG_DEBUG("Parsed band-size [%zu] for [%s]\n", *band_size, plist);
6895 status = close_file(NULL, fsp, NORMAL_CLOSE);
6896 if (!NT_STATUS_IS_OK(status)) {
6897 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
6902 TALLOC_FREE(smb_fname);
6903 TALLOC_FREE(file_data);
6908 struct fruit_disk_free_state {
6912 static bool fruit_get_num_bands(vfs_handle_struct *handle,
6917 struct smb_filename *bands_dir = NULL;
6919 struct dirent *e = NULL;
6923 path = talloc_asprintf(talloc_tos(),
6925 handle->conn->connectpath,
6931 bands_dir = synthetic_smb_fname(talloc_tos(),
6937 if (bands_dir == NULL) {
6941 d = SMB_VFS_NEXT_OPENDIR(handle, bands_dir, NULL, 0);
6943 TALLOC_FREE(bands_dir);
6949 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
6951 e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
6953 if (ISDOT(e->d_name) || ISDOTDOT(e->d_name)) {
6959 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
6961 TALLOC_FREE(bands_dir);
6965 DBG_DEBUG("%zu bands in [%s]\n", nbands, smb_fname_str_dbg(bands_dir));
6967 TALLOC_FREE(bands_dir);
6973 static bool fruit_tmsize_do_dirent(vfs_handle_struct *handle,
6974 struct fruit_disk_free_state *state,
6979 size_t sparsebundle_strlen = strlen("sparsebundle");
6980 size_t bandsize = 0;
6984 p = strstr(e->d_name, "sparsebundle");
6989 if (p[sparsebundle_strlen] != '\0') {
6993 DBG_DEBUG("Processing sparsebundle [%s]\n", e->d_name);
6995 ok = fruit_get_bandsize(handle, e->d_name, &bandsize);
6998 * Beware of race conditions: this may be an uninitialized
6999 * Info.plist that a client is just creating. We don't want let
7000 * this to trigger complete failure.
7002 DBG_ERR("Processing sparsebundle [%s] failed\n", e->d_name);
7006 ok = fruit_get_num_bands(handle, e->d_name, &nbands);
7009 * Beware of race conditions: this may be a backup sparsebundle
7010 * in an early stage lacking a bands subdirectory. We don't want
7011 * let this to trigger complete failure.
7013 DBG_ERR("Processing sparsebundle [%s] failed\n", e->d_name);
7017 if (bandsize > SIZE_MAX/nbands) {
7018 DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
7022 tm_size = bandsize * nbands;
7024 if (state->total_size + tm_size < state->total_size) {
7025 DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
7030 state->total_size += tm_size;
7032 DBG_DEBUG("[%s] tm_size [%jd] total_size [%jd]\n",
7033 e->d_name, (intmax_t)tm_size, (intmax_t)state->total_size);
7039 * Calculate used size of a TimeMachine volume
7041 * This assumes that the volume is used only for TimeMachine.
7043 * - readdir(basedir of share), then
7044 * - for every element that matches regex "^\(.*\)\.sparsebundle$" :
7045 * - parse "\1.sparsebundle/Info.plist" and read the band-size XML key
7046 * - count band files in "\1.sparsebundle/bands/"
7047 * - calculate used size of all bands: band_count * band_size
7049 static uint64_t fruit_disk_free(vfs_handle_struct *handle,
7050 const struct smb_filename *smb_fname,
7055 struct fruit_config_data *config = NULL;
7056 struct fruit_disk_free_state state = {0};
7058 struct dirent *e = NULL;
7064 SMB_VFS_HANDLE_GET_DATA(handle, config,
7065 struct fruit_config_data,
7068 if (!config->time_machine ||
7069 config->time_machine_max_size == 0)
7071 return SMB_VFS_NEXT_DISK_FREE(handle,
7078 d = SMB_VFS_NEXT_OPENDIR(handle, smb_fname, NULL, 0);
7083 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
7085 e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
7087 ok = fruit_tmsize_do_dirent(handle, &state, e);
7089 SMB_VFS_NEXT_CLOSEDIR(handle, d);
7094 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
7099 dsize = config->time_machine_max_size / 512;
7100 dfree = dsize - (state.total_size / 512);
7101 if (dfree > dsize) {
7111 static struct vfs_fn_pointers vfs_fruit_fns = {
7112 .connect_fn = fruit_connect,
7113 .disk_free_fn = fruit_disk_free,
7115 /* File operations */
7116 .chmod_fn = fruit_chmod,
7117 .chown_fn = fruit_chown,
7118 .unlink_fn = fruit_unlink,
7119 .rename_fn = fruit_rename,
7120 .rmdir_fn = fruit_rmdir,
7121 .open_fn = fruit_open,
7122 .close_fn = fruit_close,
7123 .pread_fn = fruit_pread,
7124 .pwrite_fn = fruit_pwrite,
7125 .pread_send_fn = fruit_pread_send,
7126 .pread_recv_fn = fruit_pread_recv,
7127 .pwrite_send_fn = fruit_pwrite_send,
7128 .pwrite_recv_fn = fruit_pwrite_recv,
7129 .stat_fn = fruit_stat,
7130 .lstat_fn = fruit_lstat,
7131 .fstat_fn = fruit_fstat,
7132 .streaminfo_fn = fruit_streaminfo,
7133 .ntimes_fn = fruit_ntimes,
7134 .ftruncate_fn = fruit_ftruncate,
7135 .fallocate_fn = fruit_fallocate,
7136 .create_file_fn = fruit_create_file,
7137 .readdir_attr_fn = fruit_readdir_attr,
7138 .offload_read_send_fn = fruit_offload_read_send,
7139 .offload_read_recv_fn = fruit_offload_read_recv,
7140 .offload_write_send_fn = fruit_offload_write_send,
7141 .offload_write_recv_fn = fruit_offload_write_recv,
7143 /* NT ACL operations */
7144 .fget_nt_acl_fn = fruit_fget_nt_acl,
7145 .fset_nt_acl_fn = fruit_fset_nt_acl,
7149 NTSTATUS vfs_fruit_init(TALLOC_CTX *ctx)
7151 NTSTATUS ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "fruit",
7153 if (!NT_STATUS_IS_OK(ret)) {
7157 vfs_fruit_debug_level = debug_add_class("fruit");
7158 if (vfs_fruit_debug_level == -1) {
7159 vfs_fruit_debug_level = DBGC_VFS;
7160 DEBUG(0, ("%s: Couldn't register custom debugging class!\n",
7163 DEBUG(10, ("%s: Debug class number of '%s': %d\n",
7164 "vfs_fruit_init","fruit",vfs_fruit_debug_level));