2 * OS X and Netatalk interoperability VFS module for Samba-3.x
4 * Copyright (C) Ralph Boehme, 2013, 2014
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
21 #include "MacExtensions.h"
22 #include "smbd/smbd.h"
23 #include "system/filesys.h"
24 #include "lib/util/time.h"
25 #include "system/shmem.h"
26 #include "locking/proto.h"
27 #include "smbd/globals.h"
29 #include "libcli/security/security.h"
30 #include "../libcli/smb/smb2_create_ctx.h"
31 #include "lib/util/sys_rw.h"
32 #include "lib/util/tevent_ntstatus.h"
33 #include "lib/util/tevent_unix.h"
34 #include "offload_token.h"
35 #include "string_replace.h"
37 #include <gnutls/gnutls.h>
38 #include <gnutls/crypto.h>
41 * Enhanced OS X and Netatalk compatibility
42 * ========================================
44 * This modules takes advantage of vfs_streams_xattr and
45 * vfs_catia. VFS modules vfs_fruit and vfs_streams_xattr must be
46 * loaded in the correct order:
48 * vfs modules = catia fruit streams_xattr
50 * The module intercepts the OS X special streams "AFP_AfpInfo" and
51 * "AFP_Resource" and handles them in a special way. All other named
52 * streams are deferred to vfs_streams_xattr.
54 * The OS X client maps all NTFS illegal characters to the Unicode
55 * private range. This module optionally stores the charcters using
56 * their native ASCII encoding using vfs_catia. If you're not enabling
57 * this feature, you can skip catia from vfs modules.
59 * Finally, open modes are optionally checked against Netatalk AFP
62 * The "AFP_AfpInfo" named stream is a binary blob containing OS X
63 * extended metadata for files and directories. This module optionally
64 * reads and stores this metadata in a way compatible with Netatalk 3
65 * which stores the metadata in an EA "org.netatalk.metadata". Cf
66 * source3/include/MacExtensions.h for a description of the binary
69 * The "AFP_Resource" named stream may be arbitrarily large, thus it
70 * can't be stored in an xattr on most filesystem. ZFS on Solaris is
71 * the only available filesystem where xattrs can be of any size and
72 * the OS supports using the file APIs for xattrs.
74 * The AFP_Resource stream is stored in an AppleDouble file prepending
75 * "._" to the filename. On Solaris with ZFS the stream is optionally
76 * stored in an EA "org.netatalk.resource".
82 * The OS X SMB client sends xattrs as ADS too. For xattr interop with
83 * other protocols you may want to adjust the xattr names the VFS
84 * module vfs_streams_xattr uses for storing ADS's. This defaults to
85 * user.DosStream.ADS_NAME:$DATA and can be changed by specifying
86 * these module parameters:
88 * streams_xattr:prefix = user.
89 * streams_xattr:store_stream_type = false
95 * - log diagnostic if any needed VFS module is not loaded
96 * (eg with lp_vfs_objects())
100 static int vfs_fruit_debug_level = DBGC_VFS;
102 static struct global_fruit_config {
103 bool nego_aapl; /* client negotiated AAPL */
105 } global_fruit_config;
108 #define DBGC_CLASS vfs_fruit_debug_level
110 #define FRUIT_PARAM_TYPE_NAME "fruit"
111 #define ADOUBLE_NAME_PREFIX "._"
113 #define NETATALK_META_XATTR "org.netatalk.Metadata"
114 #define NETATALK_RSRC_XATTR "org.netatalk.ResourceFork"
116 #if defined(HAVE_ATTROPEN)
117 #define AFPINFO_EA_NETATALK NETATALK_META_XATTR
118 #define AFPRESOURCE_EA_NETATALK NETATALK_RSRC_XATTR
120 #define AFPINFO_EA_NETATALK "user." NETATALK_META_XATTR
121 #define AFPRESOURCE_EA_NETATALK "user." NETATALK_RSRC_XATTR
124 enum apple_fork {APPLE_FORK_DATA, APPLE_FORK_RSRC};
126 enum fruit_rsrc {FRUIT_RSRC_STREAM, FRUIT_RSRC_ADFILE, FRUIT_RSRC_XATTR};
127 enum fruit_meta {FRUIT_META_STREAM, FRUIT_META_NETATALK};
128 enum fruit_locking {FRUIT_LOCKING_NETATALK, FRUIT_LOCKING_NONE};
129 enum fruit_encoding {FRUIT_ENC_NATIVE, FRUIT_ENC_PRIVATE};
131 struct fruit_config_data {
132 enum fruit_rsrc rsrc;
133 enum fruit_meta meta;
134 enum fruit_locking locking;
135 enum fruit_encoding encoding;
136 bool use_aapl; /* config from smb.conf */
138 bool readdir_attr_enabled;
139 bool unix_info_enabled;
140 bool copyfile_enabled;
141 bool veto_appledouble;
143 bool aapl_zero_file_id;
146 off_t time_machine_max_size;
147 bool wipe_intentionally_left_blank_rfork;
148 bool delete_empty_adfiles;
151 * Additional options, all enabled by default,
152 * possibly useful for analyzing performance. The associated
153 * operations with each of them may be expensive, so having
154 * the chance to disable them individually gives a chance
155 * tweaking the setup for the particular usecase.
157 bool readdir_attr_rsize;
158 bool readdir_attr_finder_info;
159 bool readdir_attr_max_access;
162 static const struct enum_list fruit_rsrc[] = {
163 {FRUIT_RSRC_STREAM, "stream"}, /* pass on to vfs_streams_xattr */
164 {FRUIT_RSRC_ADFILE, "file"}, /* ._ AppleDouble file */
165 {FRUIT_RSRC_XATTR, "xattr"}, /* Netatalk compatible xattr (ZFS only) */
169 static const struct enum_list fruit_meta[] = {
170 {FRUIT_META_STREAM, "stream"}, /* pass on to vfs_streams_xattr */
171 {FRUIT_META_NETATALK, "netatalk"}, /* Netatalk compatible xattr */
175 static const struct enum_list fruit_locking[] = {
176 {FRUIT_LOCKING_NETATALK, "netatalk"}, /* synchronize locks with Netatalk */
177 {FRUIT_LOCKING_NONE, "none"},
181 static const struct enum_list fruit_encoding[] = {
182 {FRUIT_ENC_NATIVE, "native"}, /* map unicode private chars to ASCII */
183 {FRUIT_ENC_PRIVATE, "private"}, /* keep unicode private chars */
187 static const char *fruit_catia_maps =
188 "0x01:0xf001,0x02:0xf002,0x03:0xf003,0x04:0xf004,"
189 "0x05:0xf005,0x06:0xf006,0x07:0xf007,0x08:0xf008,"
190 "0x09:0xf009,0x0a:0xf00a,0x0b:0xf00b,0x0c:0xf00c,"
191 "0x0d:0xf00d,0x0e:0xf00e,0x0f:0xf00f,0x10:0xf010,"
192 "0x11:0xf011,0x12:0xf012,0x13:0xf013,0x14:0xf014,"
193 "0x15:0xf015,0x16:0xf016,0x17:0xf017,0x18:0xf018,"
194 "0x19:0xf019,0x1a:0xf01a,0x1b:0xf01b,0x1c:0xf01c,"
195 "0x1d:0xf01d,0x1e:0xf01e,0x1f:0xf01f,"
196 "0x22:0xf020,0x2a:0xf021,0x3a:0xf022,0x3c:0xf023,"
197 "0x3e:0xf024,0x3f:0xf025,0x5c:0xf026,0x7c:0xf027,"
200 /*****************************************************************************
201 * Defines, functions and data structures that deal with AppleDouble
202 *****************************************************************************/
205 * There are two AppleDouble blobs we deal with:
207 * - ADOUBLE_META - AppleDouble blob used by Netatalk for storing
208 * metadata in an xattr
210 * - ADOUBLE_RSRC - AppleDouble blob used by OS X and Netatalk in
213 typedef enum {ADOUBLE_META, ADOUBLE_RSRC} adouble_type_t;
216 #define AD_VERSION2 0x00020000
217 #define AD_VERSION AD_VERSION2
220 * AppleDouble entry IDs.
222 #define ADEID_DFORK 1
223 #define ADEID_RFORK 2
225 #define ADEID_COMMENT 4
226 #define ADEID_ICONBW 5
227 #define ADEID_ICONCOL 6
228 #define ADEID_FILEI 7
229 #define ADEID_FILEDATESI 8
230 #define ADEID_FINDERI 9
231 #define ADEID_MACFILEI 10
232 #define ADEID_PRODOSFILEI 11
233 #define ADEID_MSDOSFILEI 12
234 #define ADEID_SHORTNAME 13
235 #define ADEID_AFPFILEI 14
238 /* Private Netatalk entries */
239 #define ADEID_PRIVDEV 16
240 #define ADEID_PRIVINO 17
241 #define ADEID_PRIVSYN 18
242 #define ADEID_PRIVID 19
243 #define ADEID_MAX (ADEID_PRIVID + 1)
246 * These are the real ids for the private entries,
247 * as stored in the adouble file
249 #define AD_DEV 0x80444556
250 #define AD_INO 0x80494E4F
251 #define AD_SYN 0x8053594E
252 #define AD_ID 0x8053567E
254 /* Number of actually used entries */
255 #define ADEID_NUM_XATTR 8
256 #define ADEID_NUM_DOT_UND 2
257 #define ADEID_NUM_RSRC_XATTR 1
259 /* AppleDouble magic */
260 #define AD_APPLESINGLE_MAGIC 0x00051600
261 #define AD_APPLEDOUBLE_MAGIC 0x00051607
262 #define AD_MAGIC AD_APPLEDOUBLE_MAGIC
264 /* Sizes of relevant entry bits */
265 #define ADEDLEN_MAGIC 4
266 #define ADEDLEN_VERSION 4
267 #define ADEDLEN_FILLER 16
268 #define AD_FILLER_TAG "Netatalk " /* should be 16 bytes */
269 #define AD_FILLER_TAG_OSX "Mac OS X " /* should be 16 bytes */
270 #define ADEDLEN_NENTRIES 2
271 #define AD_HEADER_LEN (ADEDLEN_MAGIC + ADEDLEN_VERSION + \
272 ADEDLEN_FILLER + ADEDLEN_NENTRIES) /* 26 */
273 #define AD_ENTRY_LEN_EID 4
274 #define AD_ENTRY_LEN_OFF 4
275 #define AD_ENTRY_LEN_LEN 4
276 #define AD_ENTRY_LEN (AD_ENTRY_LEN_EID + AD_ENTRY_LEN_OFF + AD_ENTRY_LEN_LEN)
279 #define ADEDLEN_NAME 255
280 #define ADEDLEN_COMMENT 200
281 #define ADEDLEN_FILEI 16
282 #define ADEDLEN_FINDERI 32
283 #define ADEDLEN_FILEDATESI 16
284 #define ADEDLEN_SHORTNAME 12 /* length up to 8.3 */
285 #define ADEDLEN_AFPFILEI 4
286 #define ADEDLEN_MACFILEI 4
287 #define ADEDLEN_PRODOSFILEI 8
288 #define ADEDLEN_MSDOSFILEI 2
289 #define ADEDLEN_DID 4
290 #define ADEDLEN_PRIVDEV 8
291 #define ADEDLEN_PRIVINO 8
292 #define ADEDLEN_PRIVSYN 8
293 #define ADEDLEN_PRIVID 4
296 #define ADEDOFF_MAGIC 0
297 #define ADEDOFF_VERSION (ADEDOFF_MAGIC + ADEDLEN_MAGIC)
298 #define ADEDOFF_FILLER (ADEDOFF_VERSION + ADEDLEN_VERSION)
299 #define ADEDOFF_NENTRIES (ADEDOFF_FILLER + ADEDLEN_FILLER)
301 #define ADEDOFF_FINDERI_XATTR (AD_HEADER_LEN + \
302 (ADEID_NUM_XATTR * AD_ENTRY_LEN))
303 #define ADEDOFF_COMMENT_XATTR (ADEDOFF_FINDERI_XATTR + ADEDLEN_FINDERI)
304 #define ADEDOFF_FILEDATESI_XATTR (ADEDOFF_COMMENT_XATTR + ADEDLEN_COMMENT)
305 #define ADEDOFF_AFPFILEI_XATTR (ADEDOFF_FILEDATESI_XATTR + \
307 #define ADEDOFF_PRIVDEV_XATTR (ADEDOFF_AFPFILEI_XATTR + ADEDLEN_AFPFILEI)
308 #define ADEDOFF_PRIVINO_XATTR (ADEDOFF_PRIVDEV_XATTR + ADEDLEN_PRIVDEV)
309 #define ADEDOFF_PRIVSYN_XATTR (ADEDOFF_PRIVINO_XATTR + ADEDLEN_PRIVINO)
310 #define ADEDOFF_PRIVID_XATTR (ADEDOFF_PRIVSYN_XATTR + ADEDLEN_PRIVSYN)
312 #define ADEDOFF_FINDERI_DOT_UND (AD_HEADER_LEN + \
313 (ADEID_NUM_DOT_UND * AD_ENTRY_LEN))
314 #define ADEDOFF_RFORK_DOT_UND (ADEDOFF_FINDERI_DOT_UND + ADEDLEN_FINDERI)
316 #define AD_DATASZ_XATTR (AD_HEADER_LEN + \
317 (ADEID_NUM_XATTR * AD_ENTRY_LEN) + \
318 ADEDLEN_FINDERI + ADEDLEN_COMMENT + \
319 ADEDLEN_FILEDATESI + ADEDLEN_AFPFILEI + \
320 ADEDLEN_PRIVDEV + ADEDLEN_PRIVINO + \
321 ADEDLEN_PRIVSYN + ADEDLEN_PRIVID)
323 #if AD_DATASZ_XATTR != 402
324 #error bad size for AD_DATASZ_XATTR
327 #define AD_DATASZ_DOT_UND (AD_HEADER_LEN + \
328 (ADEID_NUM_DOT_UND * AD_ENTRY_LEN) + \
330 #if AD_DATASZ_DOT_UND != 82
331 #error bad size for AD_DATASZ_DOT_UND
335 * Sharemode locks fcntl() offsets
337 #if _FILE_OFFSET_BITS == 64 || defined(HAVE_LARGEFILE)
338 #define AD_FILELOCK_BASE (UINT64_C(0x7FFFFFFFFFFFFFFF) - 9)
340 #define AD_FILELOCK_BASE (UINT32_C(0x7FFFFFFF) - 9)
342 #define BYTELOCK_MAX (AD_FILELOCK_BASE - 1)
344 #define AD_FILELOCK_OPEN_WR (AD_FILELOCK_BASE + 0)
345 #define AD_FILELOCK_OPEN_RD (AD_FILELOCK_BASE + 1)
346 #define AD_FILELOCK_RSRC_OPEN_WR (AD_FILELOCK_BASE + 2)
347 #define AD_FILELOCK_RSRC_OPEN_RD (AD_FILELOCK_BASE + 3)
348 #define AD_FILELOCK_DENY_WR (AD_FILELOCK_BASE + 4)
349 #define AD_FILELOCK_DENY_RD (AD_FILELOCK_BASE + 5)
350 #define AD_FILELOCK_RSRC_DENY_WR (AD_FILELOCK_BASE + 6)
351 #define AD_FILELOCK_RSRC_DENY_RD (AD_FILELOCK_BASE + 7)
352 #define AD_FILELOCK_OPEN_NONE (AD_FILELOCK_BASE + 8)
353 #define AD_FILELOCK_RSRC_OPEN_NONE (AD_FILELOCK_BASE + 9)
355 /* Time stuff we overload the bits a little */
356 #define AD_DATE_CREATE 0
357 #define AD_DATE_MODIFY 4
358 #define AD_DATE_BACKUP 8
359 #define AD_DATE_ACCESS 12
360 #define AD_DATE_MASK (AD_DATE_CREATE | AD_DATE_MODIFY | \
361 AD_DATE_BACKUP | AD_DATE_ACCESS)
362 #define AD_DATE_UNIX (1 << 10)
363 #define AD_DATE_START 0x80000000
364 #define AD_DATE_DELTA 946684800
365 #define AD_DATE_FROM_UNIX(x) (htonl((x) - AD_DATE_DELTA))
366 #define AD_DATE_TO_UNIX(x) (ntohl(x) + AD_DATE_DELTA)
368 #define AD_XATTR_HDR_MAGIC 0x41545452 /* 'ATTR' */
369 #define AD_XATTR_MAX_ENTRIES 1024 /* Some arbitrarily enforced limit */
370 #define AD_XATTR_HDR_SIZE 36
371 #define AD_XATTR_MAX_HDR_SIZE 65536
373 /* Accessor macros */
374 #define ad_getentrylen(ad,eid) ((ad)->ad_eid[(eid)].ade_len)
375 #define ad_getentryoff(ad,eid) ((ad)->ad_eid[(eid)].ade_off)
376 #define ad_setentrylen(ad,eid,len) ((ad)->ad_eid[(eid)].ade_len = (len))
377 #define ad_setentryoff(ad,eid,off) ((ad)->ad_eid[(eid)].ade_off = (off))
380 * Both struct ad_xattr_header and struct ad_xattr_entry describe the in memory
381 * representation as well as the on-disk format.
383 * The ad_xattr_header follows the FinderInfo data in the FinderInfo entry if
384 * the length of the FinderInfo entry is larger then 32 bytes. It is then
385 * preceeded with 2 bytes padding.
387 * Cf: https://opensource.apple.com/source/xnu/xnu-4570.1.46/bsd/vfs/vfs_xattr.c
390 struct ad_xattr_header {
391 uint32_t adx_magic; /* ATTR_HDR_MAGIC */
392 uint32_t adx_debug_tag; /* for debugging == file id of owning file */
393 uint32_t adx_total_size; /* file offset of end of attribute header + entries + data */
394 uint32_t adx_data_start; /* file offset to attribute data area */
395 uint32_t adx_data_length; /* length of attribute data area */
396 uint32_t adx_reserved[3];
398 uint16_t adx_num_attrs;
401 /* On-disk entries are aligned on 4 byte boundaries */
402 struct ad_xattr_entry {
403 uint32_t adx_offset; /* file offset to data */
404 uint32_t adx_length; /* size of attribute data */
406 uint8_t adx_namelen; /* included the NULL terminator */
407 char *adx_name; /* NULL-terminated UTF-8 name */
416 files_struct *ad_fsp;
418 adouble_type_t ad_type;
421 uint8_t ad_filler[ADEDLEN_FILLER];
422 struct ad_entry ad_eid[ADEID_MAX];
424 struct ad_xattr_header adx_header;
425 struct ad_xattr_entry *adx_entries;
428 struct ad_entry_order {
429 uint32_t id, offset, len;
432 /* Netatalk AppleDouble metadata xattr */
434 struct ad_entry_order entry_order_meta_xattr[ADEID_NUM_XATTR + 1] = {
435 {ADEID_FINDERI, ADEDOFF_FINDERI_XATTR, ADEDLEN_FINDERI},
436 {ADEID_COMMENT, ADEDOFF_COMMENT_XATTR, 0},
437 {ADEID_FILEDATESI, ADEDOFF_FILEDATESI_XATTR, ADEDLEN_FILEDATESI},
438 {ADEID_AFPFILEI, ADEDOFF_AFPFILEI_XATTR, ADEDLEN_AFPFILEI},
439 {ADEID_PRIVDEV, ADEDOFF_PRIVDEV_XATTR, 0},
440 {ADEID_PRIVINO, ADEDOFF_PRIVINO_XATTR, 0},
441 {ADEID_PRIVSYN, ADEDOFF_PRIVSYN_XATTR, 0},
442 {ADEID_PRIVID, ADEDOFF_PRIVID_XATTR, 0},
446 /* AppleDouble resource fork file (the ones prefixed by "._") */
448 struct ad_entry_order entry_order_dot_und[ADEID_NUM_DOT_UND + 1] = {
449 {ADEID_FINDERI, ADEDOFF_FINDERI_DOT_UND, ADEDLEN_FINDERI},
450 {ADEID_RFORK, ADEDOFF_RFORK_DOT_UND, 0},
454 /* Conversion from enumerated id to on-disk AppleDouble id */
455 #define AD_EID_DISK(a) (set_eid[a])
456 static const uint32_t set_eid[] = {
457 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
458 AD_DEV, AD_INO, AD_SYN, AD_ID
461 static char empty_resourcefork[] = {
462 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
463 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
464 0x54, 0x68, 0x69, 0x73, 0x20, 0x72, 0x65, 0x73,
465 0x6F, 0x75, 0x72, 0x63, 0x65, 0x20, 0x66, 0x6F,
466 0x72, 0x6B, 0x20, 0x69, 0x6E, 0x74, 0x65, 0x6E,
467 0x74, 0x69, 0x6F, 0x6E, 0x61, 0x6C, 0x6C, 0x79,
468 0x20, 0x6C, 0x65, 0x66, 0x74, 0x20, 0x62, 0x6C,
469 0x61, 0x6E, 0x6B, 0x20, 0x20, 0x20, 0x00, 0x00,
470 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
471 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
472 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
473 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
474 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
475 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
476 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
477 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
478 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
479 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
480 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
481 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
482 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
483 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
484 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
485 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
486 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
487 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
488 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
489 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
490 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
491 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
492 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
493 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
494 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
495 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
496 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
497 0x00, 0x1C, 0x00, 0x1E, 0xFF, 0xFF
501 /* tcon config handle */
502 struct fruit_config_data *config;
504 /* Denote stream type, meta or rsrc */
507 /* Whether the create created the stream */
511 * AFP_AfpInfo stream created, but not written yet, thus still a fake
512 * pipe fd. This is set to true in fruit_open_meta if there was no
513 * exisiting stream but the caller requested O_CREAT. It is later set to
514 * false when we get a write on the stream that then does open and
523 * Forward declarations
525 static struct adouble *ad_init(TALLOC_CTX *ctx,
526 adouble_type_t type);
527 static struct adouble *ad_get(TALLOC_CTX *ctx,
528 vfs_handle_struct *handle,
529 const struct smb_filename *smb_fname,
530 adouble_type_t type);
531 static int ad_set(vfs_handle_struct *handle,
533 const struct smb_filename *smb_fname);
534 static int ad_fset(struct vfs_handle_struct *handle,
537 static int adouble_path(TALLOC_CTX *ctx,
538 const struct smb_filename *smb_fname__in,
539 struct smb_filename **ppsmb_fname_out);
540 static AfpInfo *afpinfo_new(TALLOC_CTX *ctx);
541 static ssize_t afpinfo_pack(const AfpInfo *ai, char *buf);
542 static AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data);
546 * Return a pointer to an AppleDouble entry
548 * Returns NULL if the entry is not present
550 static char *ad_get_entry(const struct adouble *ad, int eid)
552 off_t off = ad_getentryoff(ad, eid);
553 size_t len = ad_getentrylen(ad, eid);
555 if (off == 0 || len == 0) {
559 return ad->ad_data + off;
565 static int ad_getdate(const struct adouble *ad,
566 unsigned int dateoff,
569 bool xlate = (dateoff & AD_DATE_UNIX);
572 dateoff &= AD_DATE_MASK;
573 p = ad_get_entry(ad, ADEID_FILEDATESI);
578 if (dateoff > AD_DATE_ACCESS) {
582 memcpy(date, p + dateoff, sizeof(uint32_t));
585 *date = AD_DATE_TO_UNIX(*date);
593 static int ad_setdate(struct adouble *ad, unsigned int dateoff, uint32_t date)
595 bool xlate = (dateoff & AD_DATE_UNIX);
598 p = ad_get_entry(ad, ADEID_FILEDATESI);
603 dateoff &= AD_DATE_MASK;
605 date = AD_DATE_FROM_UNIX(date);
608 if (dateoff > AD_DATE_ACCESS) {
612 memcpy(p + dateoff, &date, sizeof(date));
619 * Map on-disk AppleDouble id to enumerated id
621 static uint32_t get_eid(uint32_t eid)
629 return ADEID_PRIVDEV;
631 return ADEID_PRIVINO;
633 return ADEID_PRIVSYN;
644 * Pack AppleDouble structure into data buffer
646 static bool ad_pack(struct adouble *ad)
653 bufsize = talloc_get_size(ad->ad_data);
654 if (bufsize < AD_DATASZ_DOT_UND) {
655 DBG_ERR("bad buffer size [0x%" PRIx32 "]\n", bufsize);
659 if (offset + ADEDLEN_MAGIC < offset ||
660 offset + ADEDLEN_MAGIC >= bufsize) {
663 RSIVAL(ad->ad_data, offset, ad->ad_magic);
664 offset += ADEDLEN_MAGIC;
666 if (offset + ADEDLEN_VERSION < offset ||
667 offset + ADEDLEN_VERSION >= bufsize) {
670 RSIVAL(ad->ad_data, offset, ad->ad_version);
671 offset += ADEDLEN_VERSION;
673 if (offset + ADEDLEN_FILLER < offset ||
674 offset + ADEDLEN_FILLER >= bufsize) {
677 if (ad->ad_type == ADOUBLE_RSRC) {
678 memcpy(ad->ad_data + offset, AD_FILLER_TAG, ADEDLEN_FILLER);
680 offset += ADEDLEN_FILLER;
682 if (offset + ADEDLEN_NENTRIES < offset ||
683 offset + ADEDLEN_NENTRIES >= bufsize) {
686 offset += ADEDLEN_NENTRIES;
688 for (eid = 0, nent = 0; eid < ADEID_MAX; eid++) {
689 if (ad->ad_eid[eid].ade_off == 0) {
691 * ade_off is also used as indicator whether a
692 * specific entry is used or not
697 if (offset + AD_ENTRY_LEN_EID < offset ||
698 offset + AD_ENTRY_LEN_EID >= bufsize) {
701 RSIVAL(ad->ad_data, offset, AD_EID_DISK(eid));
702 offset += AD_ENTRY_LEN_EID;
704 if (offset + AD_ENTRY_LEN_OFF < offset ||
705 offset + AD_ENTRY_LEN_OFF >= bufsize) {
708 RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_off);
709 offset += AD_ENTRY_LEN_OFF;
711 if (offset + AD_ENTRY_LEN_LEN < offset ||
712 offset + AD_ENTRY_LEN_LEN >= bufsize) {
715 RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_len);
716 offset += AD_ENTRY_LEN_LEN;
721 if (ADEDOFF_NENTRIES + 2 >= bufsize) {
724 RSSVAL(ad->ad_data, ADEDOFF_NENTRIES, nent);
729 static bool ad_unpack_xattrs(struct adouble *ad)
731 struct ad_xattr_header *h = &ad->adx_header;
732 const char *p = ad->ad_data;
736 if (ad_getentrylen(ad, ADEID_FINDERI) <= ADEDLEN_FINDERI) {
740 /* 2 bytes padding */
741 hoff = ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI + 2;
743 h->adx_magic = RIVAL(p, hoff + 0);
744 h->adx_debug_tag = RIVAL(p, hoff + 4); /* Not used -> not checked */
745 h->adx_total_size = RIVAL(p, hoff + 8);
746 h->adx_data_start = RIVAL(p, hoff + 12);
747 h->adx_data_length = RIVAL(p, hoff + 16);
748 h->adx_flags = RSVAL(p, hoff + 32); /* Not used -> not checked */
749 h->adx_num_attrs = RSVAL(p, hoff + 34);
751 if (h->adx_magic != AD_XATTR_HDR_MAGIC) {
752 DBG_ERR("Bad magic: 0x%" PRIx32 "\n", h->adx_magic);
756 if (h->adx_total_size > ad_getentryoff(ad, ADEID_RFORK)) {
757 DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size);
760 if (h->adx_total_size > AD_XATTR_MAX_HDR_SIZE) {
761 DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size);
765 if (h->adx_data_start < (hoff + AD_XATTR_HDR_SIZE)) {
766 DBG_ERR("Bad start: 0x%" PRIx32 "\n", h->adx_data_start);
770 if ((h->adx_data_start + h->adx_data_length) < h->adx_data_start) {
771 DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length);
774 if ((h->adx_data_start + h->adx_data_length) >
775 ad->adx_header.adx_total_size)
777 DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length);
781 if (h->adx_num_attrs > AD_XATTR_MAX_ENTRIES) {
782 DBG_ERR("Bad num xattrs: %" PRIu16 "\n", h->adx_num_attrs);
786 if (h->adx_num_attrs == 0) {
790 ad->adx_entries = talloc_zero_array(
791 ad, struct ad_xattr_entry, h->adx_num_attrs);
792 if (ad->adx_entries == NULL) {
796 hoff += AD_XATTR_HDR_SIZE;
798 for (i = 0; i < h->adx_num_attrs; i++) {
799 struct ad_xattr_entry *e = &ad->adx_entries[i];
801 hoff = (hoff + 3) & ~3;
803 e->adx_offset = RIVAL(p, hoff + 0);
804 e->adx_length = RIVAL(p, hoff + 4);
805 e->adx_flags = RSVAL(p, hoff + 8);
806 e->adx_namelen = *(p + hoff + 10);
808 if (e->adx_offset >= ad->adx_header.adx_total_size) {
809 DBG_ERR("Bad adx_offset: %" PRIx32 "\n",
814 if ((e->adx_offset + e->adx_length) < e->adx_offset) {
815 DBG_ERR("Bad adx_length: %" PRIx32 "\n",
820 if ((e->adx_offset + e->adx_length) >
821 ad->adx_header.adx_total_size)
823 DBG_ERR("Bad adx_length: %" PRIx32 "\n",
828 if (e->adx_namelen == 0) {
829 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
833 if ((hoff + 11 + e->adx_namelen) < hoff + 11) {
834 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
838 if ((hoff + 11 + e->adx_namelen) >
839 ad->adx_header.adx_data_start)
841 DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
846 e->adx_name = talloc_strndup(ad->adx_entries,
849 if (e->adx_name == NULL) {
853 DBG_DEBUG("xattr [%s] offset [0x%x] size [0x%x]\n",
854 e->adx_name, e->adx_offset, e->adx_length);
855 dump_data(10, (uint8_t *)(ad->ad_data + e->adx_offset),
858 hoff += 11 + e->adx_namelen;
865 * Unpack an AppleDouble blob into a struct adoble
867 static bool ad_unpack(struct adouble *ad, const size_t nentries,
870 size_t bufsize = talloc_get_size(ad->ad_data);
872 uint32_t eid, len, off;
876 * The size of the buffer ad->ad_data is checked when read, so
877 * we wouldn't have to check our own offsets, a few extra
878 * checks won't hurt though. We have to check the offsets we
879 * read from the buffer anyway.
882 if (bufsize < (AD_HEADER_LEN + (AD_ENTRY_LEN * nentries))) {
883 DEBUG(1, ("bad size\n"));
887 ad->ad_magic = RIVAL(ad->ad_data, 0);
888 ad->ad_version = RIVAL(ad->ad_data, ADEDOFF_VERSION);
889 if ((ad->ad_magic != AD_MAGIC) || (ad->ad_version != AD_VERSION)) {
890 DEBUG(1, ("wrong magic or version\n"));
894 memcpy(ad->ad_filler, ad->ad_data + ADEDOFF_FILLER, ADEDLEN_FILLER);
896 adentries = RSVAL(ad->ad_data, ADEDOFF_NENTRIES);
897 if (adentries != nentries) {
898 DEBUG(1, ("invalid number of entries: %zu\n",
903 /* now, read in the entry bits */
904 for (i = 0; i < adentries; i++) {
905 eid = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN));
907 off = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 4);
908 len = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 8);
910 if (!eid || eid >= ADEID_MAX) {
911 DEBUG(1, ("bogus eid %d\n", eid));
916 * All entries other than the resource fork are
917 * expected to be read into the ad_data buffer, so
918 * ensure the specified offset is within that bound
920 if ((off > bufsize) && (eid != ADEID_RFORK)) {
921 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
927 * All entries besides FinderInfo and resource fork
928 * must fit into the buffer. FinderInfo is special as
929 * it may be larger then the default 32 bytes (if it
930 * contains marshalled xattrs), but we will fixup that
931 * in ad_convert(). And the resource fork is never
932 * accessed directly by the ad_data buf (also see
933 * comment above) anyway.
935 if ((eid != ADEID_RFORK) &&
936 (eid != ADEID_FINDERI) &&
937 ((off + len) > bufsize)) {
938 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
944 * That would be obviously broken
946 if (off > filesize) {
947 DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
953 * Check for any entry that has its end beyond the
956 if (off + len < off) {
957 DEBUG(1, ("offset wrap in eid %d: off: %" PRIu32
958 ", len: %" PRIu32 "\n",
963 if (off + len > filesize) {
965 * If this is the resource fork entry, we fix
966 * up the length, for any other entry we bail
969 if (eid != ADEID_RFORK) {
970 DEBUG(1, ("bogus eid %d: off: %" PRIu32
971 ", len: %" PRIu32 "\n",
977 * Fixup the resource fork entry by limiting
978 * the size to entryoffset - filesize.
980 len = filesize - off;
981 DEBUG(1, ("Limiting ADEID_RFORK: off: %" PRIu32
982 ", len: %" PRIu32 "\n", off, len));
985 ad->ad_eid[eid].ade_off = off;
986 ad->ad_eid[eid].ade_len = len;
989 ok = ad_unpack_xattrs(ad);
997 static bool ad_convert_move_reso(vfs_handle_struct *handle,
999 const struct smb_filename *smb_fname)
1008 rforklen = ad_getentrylen(ad, ADEID_RFORK);
1009 if (rforklen == 0) {
1013 buf = talloc_size(ad, rforklen);
1016 * This allocates a buffer for reading the resource fork data in
1017 * one big swoop. Resource forks won't be larger then, say, 64
1018 * MB, I swear, so just doing the allocation with the talloc
1019 * limit as safeguard seems safe.
1021 DBG_ERR("Failed to allocate %zu bytes for rfork\n",
1026 rforkoff = ad_getentryoff(ad, ADEID_RFORK);
1028 n = SMB_VFS_PREAD(ad->ad_fsp, buf, rforklen, rforkoff);
1029 if (n != rforklen) {
1030 DBG_ERR("Reading %zu bytes from rfork [%s] failed: %s\n",
1031 rforklen, fsp_str_dbg(ad->ad_fsp), strerror(errno));
1035 rforkoff = ADEDOFF_RFORK_DOT_UND;
1037 n = SMB_VFS_PWRITE(ad->ad_fsp, buf, rforklen, rforkoff);
1038 if (n != rforklen) {
1039 DBG_ERR("Writing %zu bytes to rfork [%s] failed: %s\n",
1040 rforklen, fsp_str_dbg(ad->ad_fsp), strerror(errno));
1044 ad_setentryoff(ad, ADEID_RFORK, ADEDOFF_RFORK_DOT_UND);
1047 DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name);
1051 ret = ad_fset(handle, ad, ad->ad_fsp);
1053 DBG_ERR("ad_fset on [%s] failed\n", fsp_str_dbg(ad->ad_fsp));
1060 static bool ad_convert_xattr(vfs_handle_struct *handle,
1062 const struct smb_filename *smb_fname,
1063 bool *converted_xattr)
1065 static struct char_mappings **string_replace_cmaps = NULL;
1066 char *map = MAP_FAILED;
1070 int saved_errno = 0;
1075 *converted_xattr = false;
1077 if (ad_getentrylen(ad, ADEID_FINDERI) == ADEDLEN_FINDERI) {
1081 if (string_replace_cmaps == NULL) {
1082 const char **mappings = NULL;
1084 mappings = str_list_make_v3_const(
1085 talloc_tos(), fruit_catia_maps, NULL);
1086 if (mappings == NULL) {
1089 string_replace_cmaps = string_replace_init_map(mappings);
1090 TALLOC_FREE(mappings);
1093 maplen = ad_getentryoff(ad, ADEID_RFORK) +
1094 ad_getentrylen(ad, ADEID_RFORK);
1096 /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
1097 map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
1098 ad->ad_fsp->fh->fd, 0);
1099 if (map == MAP_FAILED) {
1100 DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
1104 for (i = 0; i < ad->adx_header.adx_num_attrs; i++) {
1105 struct ad_xattr_entry *e = &ad->adx_entries[i];
1106 char *mapped_name = NULL;
1108 struct smb_filename *stream_name = NULL;
1109 files_struct *fsp = NULL;
1112 status = string_replace_allocate(handle->conn,
1114 string_replace_cmaps,
1117 vfs_translate_to_windows);
1118 if (!NT_STATUS_IS_OK(status) &&
1119 !NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))
1121 DBG_ERR("string_replace_allocate failed\n");
1127 mapped_name = talloc_asprintf(talloc_tos(), ":%s", tmp);
1129 if (mapped_name == NULL) {
1134 stream_name = synthetic_smb_fname(talloc_tos(),
1135 smb_fname->base_name,
1139 TALLOC_FREE(mapped_name);
1140 if (stream_name == NULL) {
1141 DBG_ERR("synthetic_smb_fname failed\n");
1146 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1148 status = SMB_VFS_CREATE_FILE(
1149 handle->conn, /* conn */
1151 0, /* root_dir_fid */
1152 stream_name, /* fname */
1153 FILE_GENERIC_WRITE, /* access_mask */
1154 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
1155 FILE_OPEN_IF, /* create_disposition */
1156 0, /* create_options */
1157 0, /* file_attributes */
1158 INTERNAL_OPEN_ONLY, /* oplock_request */
1160 0, /* allocation_size */
1161 0, /* private_flags */
1166 NULL, NULL); /* create context */
1167 TALLOC_FREE(stream_name);
1168 if (!NT_STATUS_IS_OK(status)) {
1169 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1174 nwritten = SMB_VFS_PWRITE(fsp,
1175 map + e->adx_offset,
1178 if (nwritten == -1) {
1179 DBG_ERR("SMB_VFS_PWRITE failed\n");
1180 saved_errno = errno;
1181 close_file(NULL, fsp, ERROR_CLOSE);
1182 errno = saved_errno;
1187 status = close_file(NULL, fsp, NORMAL_CLOSE);
1188 if (!NT_STATUS_IS_OK(status)) {
1195 ad_setentrylen(ad, ADEID_FINDERI, ADEDLEN_FINDERI);
1199 DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name);
1203 len = sys_pwrite(ad->ad_fsp->fh->fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
1204 if (len != AD_DATASZ_DOT_UND) {
1205 DBG_ERR("%s: bad size: %zd\n", smb_fname->base_name, len);
1210 ok = ad_convert_move_reso(handle, ad, smb_fname);
1215 *converted_xattr = true;
1219 rc = munmap(map, maplen);
1221 DBG_ERR("munmap failed: %s\n", strerror(errno));
1228 static bool ad_convert_finderinfo(vfs_handle_struct *handle,
1230 const struct smb_filename *smb_fname)
1235 struct smb_filename *stream_name = NULL;
1236 files_struct *fsp = NULL;
1240 int saved_errno = 0;
1243 cmp = memcmp(ad->ad_filler, AD_FILLER_TAG_OSX, ADEDLEN_FILLER);
1248 p_ad = ad_get_entry(ad, ADEID_FINDERI);
1253 ai = afpinfo_new(talloc_tos());
1258 memcpy(ai->afpi_FinderInfo, p_ad, ADEDLEN_FINDERI);
1260 aiblob = data_blob_talloc(talloc_tos(), NULL, AFP_INFO_SIZE);
1261 if (aiblob.data == NULL) {
1266 size = afpinfo_pack(ai, (char *)aiblob.data);
1268 if (size != AFP_INFO_SIZE) {
1272 stream_name = synthetic_smb_fname(talloc_tos(),
1273 smb_fname->base_name,
1277 if (stream_name == NULL) {
1278 data_blob_free(&aiblob);
1279 DBG_ERR("synthetic_smb_fname failed\n");
1283 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1285 status = SMB_VFS_CREATE_FILE(
1286 handle->conn, /* conn */
1288 0, /* root_dir_fid */
1289 stream_name, /* fname */
1290 FILE_GENERIC_WRITE, /* access_mask */
1291 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
1292 FILE_OPEN_IF, /* create_disposition */
1293 0, /* create_options */
1294 0, /* file_attributes */
1295 INTERNAL_OPEN_ONLY, /* oplock_request */
1297 0, /* allocation_size */
1298 0, /* private_flags */
1303 NULL, NULL); /* create context */
1304 TALLOC_FREE(stream_name);
1305 if (!NT_STATUS_IS_OK(status)) {
1306 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1310 nwritten = SMB_VFS_PWRITE(fsp,
1314 if (nwritten == -1) {
1315 DBG_ERR("SMB_VFS_PWRITE failed\n");
1316 saved_errno = errno;
1317 close_file(NULL, fsp, ERROR_CLOSE);
1318 errno = saved_errno;
1322 status = close_file(NULL, fsp, NORMAL_CLOSE);
1323 if (!NT_STATUS_IS_OK(status)) {
1331 static bool ad_convert_truncate(struct adouble *ad,
1332 const struct smb_filename *smb_fname)
1337 * FIXME: direct ftruncate(), but we don't have a fsp for the
1340 rc = ftruncate(ad->ad_fsp->fh->fd, ADEDOFF_RFORK_DOT_UND +
1341 ad_getentrylen(ad, ADEID_RFORK));
1349 static bool ad_convert_blank_rfork(vfs_handle_struct *handle,
1353 struct fruit_config_data *config = NULL;
1354 uint8_t *map = MAP_FAILED;
1363 SMB_VFS_HANDLE_GET_DATA(handle, config,
1364 struct fruit_config_data, return false);
1366 if (!config->wipe_intentionally_left_blank_rfork) {
1370 if (ad_getentrylen(ad, ADEID_RFORK) != sizeof(empty_resourcefork)) {
1374 maplen = ad_getentryoff(ad, ADEID_RFORK) +
1375 ad_getentrylen(ad, ADEID_RFORK);
1377 /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
1378 map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
1379 ad->ad_fsp->fh->fd, 0);
1380 if (map == MAP_FAILED) {
1381 DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
1385 cmp = memcmp(map + ADEDOFF_RFORK_DOT_UND,
1387 sizeof(empty_resourcefork));
1388 rc = munmap(map, maplen);
1390 DBG_ERR("munmap failed: %s\n", strerror(errno));
1398 ad_setentrylen(ad, ADEID_RFORK, 0);
1405 len = sys_pwrite(ad->ad_fsp->fh->fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
1406 if (len != AD_DATASZ_DOT_UND) {
1414 static bool ad_convert_delete_adfile(vfs_handle_struct *handle,
1416 const struct smb_filename *smb_fname)
1418 struct fruit_config_data *config = NULL;
1419 struct smb_filename *ad_name = NULL;
1422 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
1426 SMB_VFS_HANDLE_GET_DATA(handle, config,
1427 struct fruit_config_data, return false);
1429 if (!config->delete_empty_adfiles) {
1433 rc = adouble_path(talloc_tos(), smb_fname, &ad_name);
1438 rc = SMB_VFS_NEXT_UNLINK(handle, ad_name);
1440 DBG_ERR("Unlinking [%s] failed: %s\n",
1441 smb_fname_str_dbg(ad_name), strerror(errno));
1442 TALLOC_FREE(ad_name);
1446 DBG_WARNING("Unlinked [%s] after conversion\n", smb_fname_str_dbg(ad_name));
1447 TALLOC_FREE(ad_name);
1453 * Convert from Apple's ._ file to Netatalk
1455 * Apple's AppleDouble may contain a FinderInfo entry longer then 32
1456 * bytes containing packed xattrs.
1458 * @return -1 in case an error occurred, 0 if no conversion was done, 1
1461 static int ad_convert(struct vfs_handle_struct *handle,
1462 const struct smb_filename *smb_fname)
1464 struct adouble *ad = NULL;
1466 bool converted_xattr = false;
1470 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
1475 ok = ad_convert_xattr(handle, ad, smb_fname, &converted_xattr);
1481 ok = ad_convert_blank_rfork(handle, ad, &blank);
1487 if (converted_xattr || blank) {
1488 ok = ad_convert_truncate(ad, smb_fname);
1495 ok = ad_convert_finderinfo(handle, ad, smb_fname);
1497 DBG_ERR("Failed to convert [%s]\n",
1498 smb_fname_str_dbg(smb_fname));
1503 ok = ad_convert_delete_adfile(handle, ad, smb_fname);
1516 * Read and parse Netatalk AppleDouble metadata xattr
1518 static ssize_t ad_read_meta(vfs_handle_struct *handle,
1520 const struct smb_filename *smb_fname)
1526 DEBUG(10, ("reading meta xattr for %s\n", smb_fname->base_name));
1528 ealen = SMB_VFS_GETXATTR(handle->conn, smb_fname,
1529 AFPINFO_EA_NETATALK, ad->ad_data,
1535 if (errno == ENOATTR) {
1541 DEBUG(2, ("error reading meta xattr: %s\n",
1547 if (ealen != AD_DATASZ_XATTR) {
1548 DEBUG(2, ("bad size %zd\n", ealen));
1554 /* Now parse entries */
1555 ok = ad_unpack(ad, ADEID_NUM_XATTR, AD_DATASZ_XATTR);
1557 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1563 if (!ad_getentryoff(ad, ADEID_FINDERI)
1564 || !ad_getentryoff(ad, ADEID_COMMENT)
1565 || !ad_getentryoff(ad, ADEID_FILEDATESI)
1566 || !ad_getentryoff(ad, ADEID_AFPFILEI)
1567 || !ad_getentryoff(ad, ADEID_PRIVDEV)
1568 || !ad_getentryoff(ad, ADEID_PRIVINO)
1569 || !ad_getentryoff(ad, ADEID_PRIVSYN)
1570 || !ad_getentryoff(ad, ADEID_PRIVID)) {
1571 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1578 DEBUG(10, ("reading meta xattr for %s, rc: %d\n",
1579 smb_fname->base_name, rc));
1583 if (errno == EINVAL) {
1585 (void)SMB_VFS_REMOVEXATTR(handle->conn,
1587 AFPINFO_EA_NETATALK);
1595 static int ad_open_rsrc(vfs_handle_struct *handle,
1596 const struct smb_filename *smb_fname,
1599 files_struct **_fsp)
1602 struct smb_filename *adp_smb_fname = NULL;
1603 files_struct *fsp = NULL;
1604 uint32_t access_mask;
1605 uint32_t share_access;
1606 uint32_t create_disposition;
1609 ret = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
1614 ret = SMB_VFS_STAT(handle->conn, adp_smb_fname);
1616 TALLOC_FREE(adp_smb_fname);
1620 access_mask = FILE_GENERIC_READ;
1621 share_access = FILE_SHARE_READ | FILE_SHARE_WRITE;
1622 create_disposition = FILE_OPEN;
1624 if (flags & O_RDWR) {
1625 access_mask |= FILE_GENERIC_WRITE;
1626 share_access &= ~FILE_SHARE_WRITE;
1629 status = SMB_VFS_CREATE_FILE(
1630 handle->conn, /* conn */
1632 0, /* root_dir_fid */
1637 0, /* create_options */
1638 0, /* file_attributes */
1639 INTERNAL_OPEN_ONLY, /* oplock_request */
1641 0, /* allocation_size */
1642 0, /* private_flags */
1647 NULL, NULL); /* create context */
1648 TALLOC_FREE(adp_smb_fname);
1649 if (!NT_STATUS_IS_OK(status)) {
1650 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1659 * Here's the deal: for ADOUBLE_META we can do without an fd as we can issue
1660 * path based xattr calls. For ADOUBLE_RSRC however we need a full-fledged fd
1661 * for file IO on the ._ file.
1663 static int ad_open(vfs_handle_struct *handle,
1666 const struct smb_filename *smb_fname,
1672 DBG_DEBUG("Path [%s] type [%s]\n", smb_fname->base_name,
1673 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
1675 if (ad->ad_type == ADOUBLE_META) {
1681 ad->ad_opened = false;
1685 ret = ad_open_rsrc(handle, smb_fname, flags, mode, &ad->ad_fsp);
1689 ad->ad_opened = true;
1691 DBG_DEBUG("Path [%s] type [%s]\n",
1692 smb_fname->base_name,
1693 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
1698 static ssize_t ad_read_rsrc_adouble(vfs_handle_struct *handle,
1700 const struct smb_filename *smb_fname)
1702 SMB_STRUCT_STAT sbuf;
1709 ret = sys_fstat(ad->ad_fsp->fh->fd, &sbuf, lp_fake_directory_create_times(
1710 SNUM(handle->conn)));
1716 * AppleDouble file header content and size, two cases:
1718 * - without xattrs it is exactly AD_DATASZ_DOT_UND (82) bytes large
1719 * - with embedded xattrs it can be larger, up to AD_XATTR_MAX_HDR_SIZE
1721 * Read as much as we can up to AD_XATTR_MAX_HDR_SIZE.
1723 size = sbuf.st_ex_size;
1724 if (size > talloc_array_length(ad->ad_data)) {
1725 if (size > AD_XATTR_MAX_HDR_SIZE) {
1726 size = AD_XATTR_MAX_HDR_SIZE;
1728 p_ad = talloc_realloc(ad, ad->ad_data, char, size);
1735 len = sys_pread(ad->ad_fsp->fh->fd, ad->ad_data,
1736 talloc_array_length(ad->ad_data), 0);
1737 if (len != talloc_array_length(ad->ad_data)) {
1738 DBG_NOTICE("%s %s: bad size: %zd\n",
1739 smb_fname->base_name, strerror(errno), len);
1743 /* Now parse entries */
1744 ok = ad_unpack(ad, ADEID_NUM_DOT_UND, sbuf.st_ex_size);
1746 DBG_ERR("invalid AppleDouble resource %s\n",
1747 smb_fname->base_name);
1752 if ((ad_getentryoff(ad, ADEID_FINDERI) != ADEDOFF_FINDERI_DOT_UND)
1753 || (ad_getentrylen(ad, ADEID_FINDERI) < ADEDLEN_FINDERI)
1754 || (ad_getentryoff(ad, ADEID_RFORK) < ADEDOFF_RFORK_DOT_UND)) {
1755 DBG_ERR("invalid AppleDouble resource %s\n",
1756 smb_fname->base_name);
1765 * Read and parse resource fork, either ._ AppleDouble file or xattr
1767 static ssize_t ad_read_rsrc(vfs_handle_struct *handle,
1769 const struct smb_filename *smb_fname)
1771 return ad_read_rsrc_adouble(handle, ad, smb_fname);
1775 * Read and unpack an AppleDouble metadata xattr or resource
1777 static ssize_t ad_read(vfs_handle_struct *handle,
1779 const struct smb_filename *smb_fname)
1781 switch (ad->ad_type) {
1783 return ad_read_meta(handle, ad, smb_fname);
1785 return ad_read_rsrc(handle, ad, smb_fname);
1791 static int adouble_destructor(struct adouble *ad)
1795 if (!ad->ad_opened) {
1799 SMB_ASSERT(ad->ad_fsp != NULL);
1801 status = close_file(NULL, ad->ad_fsp, NORMAL_CLOSE);
1802 if (!NT_STATUS_IS_OK(status)) {
1803 DBG_ERR("Closing [%s] failed: %s\n",
1804 fsp_str_dbg(ad->ad_fsp), nt_errstr(status));
1811 * Allocate a struct adouble without initialiing it
1813 * The struct is either hang of the fsp extension context or if fsp is
1816 * @param[in] ctx talloc context
1817 * @param[in] handle vfs handle
1818 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1820 * @return adouble handle
1822 static struct adouble *ad_alloc(TALLOC_CTX *ctx,
1823 adouble_type_t type)
1831 adsize = AD_DATASZ_XATTR;
1834 adsize = AD_DATASZ_DOT_UND;
1840 ad = talloc_zero(ctx, struct adouble);
1847 ad->ad_data = talloc_zero_array(ad, char, adsize);
1848 if (ad->ad_data == NULL) {
1855 ad->ad_magic = AD_MAGIC;
1856 ad->ad_version = AD_VERSION;
1858 talloc_set_destructor(ad, adouble_destructor);
1868 * Allocate and initialize a new struct adouble
1870 * @param[in] ctx talloc context
1871 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1873 * @return adouble handle, initialized
1875 static struct adouble *ad_init(TALLOC_CTX *ctx,
1876 adouble_type_t type)
1879 const struct ad_entry_order *eid;
1880 struct adouble *ad = NULL;
1881 time_t t = time(NULL);
1885 eid = entry_order_meta_xattr;
1888 eid = entry_order_dot_und;
1894 ad = ad_alloc(ctx, type);
1900 ad->ad_eid[eid->id].ade_off = eid->offset;
1901 ad->ad_eid[eid->id].ade_len = eid->len;
1905 /* put something sane in the date fields */
1906 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, t);
1907 ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, t);
1908 ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, t);
1909 ad_setdate(ad, AD_DATE_BACKUP, htonl(AD_DATE_START));
1917 static struct adouble *ad_get_internal(TALLOC_CTX *ctx,
1918 vfs_handle_struct *handle,
1920 const struct smb_filename *smb_fname,
1921 adouble_type_t type)
1925 struct adouble *ad = NULL;
1929 smb_fname = fsp->base_fsp->fsp_name;
1932 DEBUG(10, ("ad_get(%s) called for %s\n",
1933 type == ADOUBLE_META ? "meta" : "rsrc",
1934 smb_fname->base_name));
1936 ad = ad_alloc(ctx, type);
1942 /* Try rw first so we can use the fd in ad_convert() */
1945 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
1946 if (rc == -1 && ((errno == EROFS) || (errno == EACCES))) {
1948 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
1951 DBG_DEBUG("ad_open [%s] error [%s]\n",
1952 smb_fname->base_name, strerror(errno));
1957 len = ad_read(handle, ad, smb_fname);
1959 DEBUG(10, ("error reading AppleDouble for %s\n",
1960 smb_fname->base_name));
1966 DEBUG(10, ("ad_get(%s) for %s returning %d\n",
1967 type == ADOUBLE_META ? "meta" : "rsrc",
1968 smb_fname->base_name, rc));
1977 * Return AppleDouble data for a file
1979 * @param[in] ctx talloc context
1980 * @param[in] handle vfs handle
1981 * @param[in] smb_fname pathname to file or directory
1982 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1984 * @return talloced struct adouble or NULL on error
1986 static struct adouble *ad_get(TALLOC_CTX *ctx,
1987 vfs_handle_struct *handle,
1988 const struct smb_filename *smb_fname,
1989 adouble_type_t type)
1991 return ad_get_internal(ctx, handle, NULL, smb_fname, type);
1995 * Return AppleDouble data for a file
1997 * @param[in] ctx talloc context
1998 * @param[in] handle vfs handle
1999 * @param[in] fsp fsp to use for IO
2000 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
2002 * @return talloced struct adouble or NULL on error
2004 static struct adouble *ad_fget(TALLOC_CTX *ctx, vfs_handle_struct *handle,
2005 files_struct *fsp, adouble_type_t type)
2007 return ad_get_internal(ctx, handle, fsp, NULL, type);
2011 * Set AppleDouble metadata on a file or directory
2013 * @param[in] ad adouble handle
2015 * @param[in] smb_fname pathname to file or directory
2017 * @return status code, 0 means success
2019 static int ad_set(vfs_handle_struct *handle,
2021 const struct smb_filename *smb_fname)
2026 DBG_DEBUG("Path [%s]\n", smb_fname->base_name);
2028 if (ad->ad_type != ADOUBLE_META) {
2029 DBG_ERR("ad_set on [%s] used with ADOUBLE_RSRC\n",
2030 smb_fname->base_name);
2039 ret = SMB_VFS_SETXATTR(handle->conn,
2041 AFPINFO_EA_NETATALK,
2043 AD_DATASZ_XATTR, 0);
2045 DBG_DEBUG("Path [%s] ret [%d]\n", smb_fname->base_name, ret);
2051 * Set AppleDouble metadata on a file or directory
2053 * @param[in] ad adouble handle
2054 * @param[in] fsp file handle
2056 * @return status code, 0 means success
2058 static int ad_fset(struct vfs_handle_struct *handle,
2066 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
2069 || (fsp->fh == NULL)
2070 || (fsp->fh->fd == -1))
2072 smb_panic("bad fsp");
2080 switch (ad->ad_type) {
2082 rc = SMB_VFS_NEXT_SETXATTR(handle,
2084 AFPINFO_EA_NETATALK,
2086 AD_DATASZ_XATTR, 0);
2090 len = SMB_VFS_NEXT_PWRITE(handle,
2095 if (len != AD_DATASZ_DOT_UND) {
2096 DBG_ERR("short write on %s: %zd", fsp_str_dbg(fsp), len);
2106 DBG_DEBUG("Path [%s] rc [%d]\n", fsp_str_dbg(fsp), rc);
2111 /*****************************************************************************
2113 *****************************************************************************/
2115 static bool is_afpinfo_stream(const struct smb_filename *smb_fname)
2117 if (strncasecmp_m(smb_fname->stream_name,
2118 AFPINFO_STREAM_NAME,
2119 strlen(AFPINFO_STREAM_NAME)) == 0) {
2125 static bool is_afpresource_stream(const struct smb_filename *smb_fname)
2127 if (strncasecmp_m(smb_fname->stream_name,
2128 AFPRESOURCE_STREAM_NAME,
2129 strlen(AFPRESOURCE_STREAM_NAME)) == 0) {
2136 * Test whether stream is an Apple stream.
2138 static bool is_apple_stream(const struct smb_filename *smb_fname)
2140 if (is_afpinfo_stream(smb_fname)) {
2143 if (is_afpresource_stream(smb_fname)) {
2149 static bool is_adouble_file(const char *path)
2151 const char *p = NULL;
2154 p = strrchr(path, '/');
2162 ADOUBLE_NAME_PREFIX,
2163 strlen(ADOUBLE_NAME_PREFIX));
2171 * Initialize config struct from our smb.conf config parameters
2173 static int init_fruit_config(vfs_handle_struct *handle)
2175 struct fruit_config_data *config;
2177 const char *tm_size_str = NULL;
2179 config = talloc_zero(handle->conn, struct fruit_config_data);
2181 DEBUG(1, ("talloc_zero() failed\n"));
2187 * Versions up to Samba 4.5.x had a spelling bug in the
2188 * fruit:resource option calling lp_parm_enum with
2189 * "res*s*ource" (ie two s).
2191 * In Samba 4.6 we accept both the wrong and the correct
2192 * spelling, in Samba 4.7 the bad spelling will be removed.
2194 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2195 "ressource", fruit_rsrc, FRUIT_RSRC_ADFILE);
2196 if (enumval == -1) {
2197 DEBUG(1, ("value for %s: resource type unknown\n",
2198 FRUIT_PARAM_TYPE_NAME));
2201 config->rsrc = (enum fruit_rsrc)enumval;
2203 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2204 "resource", fruit_rsrc, enumval);
2205 if (enumval == -1) {
2206 DEBUG(1, ("value for %s: resource type unknown\n",
2207 FRUIT_PARAM_TYPE_NAME));
2210 config->rsrc = (enum fruit_rsrc)enumval;
2212 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2213 "metadata", fruit_meta, FRUIT_META_NETATALK);
2214 if (enumval == -1) {
2215 DEBUG(1, ("value for %s: metadata type unknown\n",
2216 FRUIT_PARAM_TYPE_NAME));
2219 config->meta = (enum fruit_meta)enumval;
2221 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2222 "locking", fruit_locking, FRUIT_LOCKING_NONE);
2223 if (enumval == -1) {
2224 DEBUG(1, ("value for %s: locking type unknown\n",
2225 FRUIT_PARAM_TYPE_NAME));
2228 config->locking = (enum fruit_locking)enumval;
2230 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2231 "encoding", fruit_encoding, FRUIT_ENC_PRIVATE);
2232 if (enumval == -1) {
2233 DEBUG(1, ("value for %s: encoding type unknown\n",
2234 FRUIT_PARAM_TYPE_NAME));
2237 config->encoding = (enum fruit_encoding)enumval;
2239 if (config->rsrc == FRUIT_RSRC_ADFILE) {
2240 config->veto_appledouble = lp_parm_bool(SNUM(handle->conn),
2241 FRUIT_PARAM_TYPE_NAME,
2246 config->use_aapl = lp_parm_bool(
2247 -1, FRUIT_PARAM_TYPE_NAME, "aapl", true);
2249 config->time_machine = lp_parm_bool(
2250 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "time machine", false);
2252 config->unix_info_enabled = lp_parm_bool(
2253 -1, FRUIT_PARAM_TYPE_NAME, "nfs_aces", true);
2255 config->use_copyfile = lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME,
2258 config->posix_rename = lp_parm_bool(
2259 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "posix_rename", true);
2261 config->aapl_zero_file_id =
2262 lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME, "zero_file_id", true);
2264 config->readdir_attr_rsize = lp_parm_bool(
2265 SNUM(handle->conn), "readdir_attr", "aapl_rsize", true);
2267 config->readdir_attr_finder_info = lp_parm_bool(
2268 SNUM(handle->conn), "readdir_attr", "aapl_finder_info", true);
2270 config->readdir_attr_max_access = lp_parm_bool(
2271 SNUM(handle->conn), "readdir_attr", "aapl_max_access", true);
2273 config->model = lp_parm_const_string(
2274 -1, FRUIT_PARAM_TYPE_NAME, "model", "MacSamba");
2276 tm_size_str = lp_parm_const_string(
2277 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2278 "time machine max size", NULL);
2279 if (tm_size_str != NULL) {
2280 config->time_machine_max_size = conv_str_size(tm_size_str);
2283 config->wipe_intentionally_left_blank_rfork = lp_parm_bool(
2284 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2285 "wipe_intentionally_left_blank_rfork", false);
2287 config->delete_empty_adfiles = lp_parm_bool(
2288 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2289 "delete_empty_adfiles", false);
2291 SMB_VFS_HANDLE_SET_DATA(handle, config,
2292 NULL, struct fruit_config_data,
2299 * Prepend "._" to a basename
2300 * Return a new struct smb_filename with stream_name == NULL.
2302 static int adouble_path(TALLOC_CTX *ctx,
2303 const struct smb_filename *smb_fname_in,
2304 struct smb_filename **pp_smb_fname_out)
2308 struct smb_filename *smb_fname = cp_smb_filename(ctx,
2311 if (smb_fname == NULL) {
2315 /* We need streamname to be NULL */
2316 TALLOC_FREE(smb_fname->stream_name);
2318 /* And we're replacing base_name. */
2319 TALLOC_FREE(smb_fname->base_name);
2321 if (!parent_dirname(smb_fname, smb_fname_in->base_name,
2323 TALLOC_FREE(smb_fname);
2327 smb_fname->base_name = talloc_asprintf(smb_fname,
2328 "%s/._%s", parent, base);
2329 if (smb_fname->base_name == NULL) {
2330 TALLOC_FREE(smb_fname);
2334 *pp_smb_fname_out = smb_fname;
2340 * Allocate and initialize an AfpInfo struct
2342 static AfpInfo *afpinfo_new(TALLOC_CTX *ctx)
2344 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2348 ai->afpi_Signature = AFP_Signature;
2349 ai->afpi_Version = AFP_Version;
2350 ai->afpi_BackupTime = AD_DATE_START;
2355 * Pack an AfpInfo struct into a buffer
2357 * Buffer size must be at least AFP_INFO_SIZE
2358 * Returns size of packed buffer
2360 static ssize_t afpinfo_pack(const AfpInfo *ai, char *buf)
2362 memset(buf, 0, AFP_INFO_SIZE);
2364 RSIVAL(buf, 0, ai->afpi_Signature);
2365 RSIVAL(buf, 4, ai->afpi_Version);
2366 RSIVAL(buf, 12, ai->afpi_BackupTime);
2367 memcpy(buf + 16, ai->afpi_FinderInfo, sizeof(ai->afpi_FinderInfo));
2369 return AFP_INFO_SIZE;
2373 * Unpack a buffer into a AfpInfo structure
2375 * Buffer size must be at least AFP_INFO_SIZE
2376 * Returns allocated AfpInfo struct
2378 static AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data)
2380 AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2385 ai->afpi_Signature = RIVAL(data, 0);
2386 ai->afpi_Version = RIVAL(data, 4);
2387 ai->afpi_BackupTime = RIVAL(data, 12);
2388 memcpy(ai->afpi_FinderInfo, (const char *)data + 16,
2389 sizeof(ai->afpi_FinderInfo));
2391 if (ai->afpi_Signature != AFP_Signature
2392 || ai->afpi_Version != AFP_Version) {
2393 DEBUG(1, ("Bad AfpInfo signature or version\n"));
2401 * Fake an inode number from the md5 hash of the (xattr) name
2403 static SMB_INO_T fruit_inode(const SMB_STRUCT_STAT *sbuf, const char *sname)
2405 gnutls_hash_hd_t hash_hnd = NULL;
2406 unsigned char hash[16];
2407 SMB_INO_T result = 0;
2411 DBG_DEBUG("fruit_inode called for %ju/%ju [%s]\n",
2412 (uintmax_t)sbuf->st_ex_dev,
2413 (uintmax_t)sbuf->st_ex_ino, sname);
2415 upper_sname = talloc_strdup_upper(talloc_tos(), sname);
2416 SMB_ASSERT(upper_sname != NULL);
2418 rc = gnutls_hash_init(&hash_hnd, GNUTLS_DIG_MD5);
2423 rc = gnutls_hash(hash_hnd, &(sbuf->st_ex_dev), sizeof(sbuf->st_ex_dev));
2425 gnutls_hash_deinit(hash_hnd, NULL);
2428 rc = gnutls_hash(hash_hnd,
2430 sizeof(sbuf->st_ex_ino));
2432 gnutls_hash_deinit(hash_hnd, NULL);
2435 rc = gnutls_hash(hash_hnd,
2437 talloc_get_size(upper_sname) - 1);
2439 gnutls_hash_deinit(hash_hnd, NULL);
2443 gnutls_hash_deinit(hash_hnd, hash);
2445 /* Hopefully all the variation is in the lower 4 (or 8) bytes! */
2446 memcpy(&result, hash, sizeof(result));
2449 DBG_DEBUG("fruit_inode \"%s\": ino=%ju\n",
2450 sname, (uintmax_t)result);
2453 TALLOC_FREE(upper_sname);
2458 static bool add_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
2459 struct stream_struct **streams,
2460 const char *name, off_t size,
2463 struct stream_struct *tmp;
2465 tmp = talloc_realloc(mem_ctx, *streams, struct stream_struct,
2471 tmp[*num_streams].name = talloc_asprintf(tmp, "%s:$DATA", name);
2472 if (tmp[*num_streams].name == NULL) {
2476 tmp[*num_streams].size = size;
2477 tmp[*num_streams].alloc_size = alloc_size;
2484 static bool filter_empty_rsrc_stream(unsigned int *num_streams,
2485 struct stream_struct **streams)
2487 struct stream_struct *tmp = *streams;
2490 if (*num_streams == 0) {
2494 for (i = 0; i < *num_streams; i++) {
2495 if (strequal_m(tmp[i].name, AFPRESOURCE_STREAM)) {
2500 if (i == *num_streams) {
2504 if (tmp[i].size > 0) {
2508 TALLOC_FREE(tmp[i].name);
2509 if (*num_streams - 1 > i) {
2510 memmove(&tmp[i], &tmp[i+1],
2511 (*num_streams - i - 1) * sizeof(struct stream_struct));
2518 static bool del_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
2519 struct stream_struct **streams,
2522 struct stream_struct *tmp = *streams;
2525 if (*num_streams == 0) {
2529 for (i = 0; i < *num_streams; i++) {
2530 if (strequal_m(tmp[i].name, name)) {
2535 if (i == *num_streams) {
2539 TALLOC_FREE(tmp[i].name);
2540 if (*num_streams - 1 > i) {
2541 memmove(&tmp[i], &tmp[i+1],
2542 (*num_streams - i - 1) * sizeof(struct stream_struct));
2549 static bool ad_empty_finderinfo(const struct adouble *ad)
2552 char emptybuf[ADEDLEN_FINDERI] = {0};
2555 fi = ad_get_entry(ad, ADEID_FINDERI);
2557 DBG_ERR("Missing FinderInfo in struct adouble [%p]\n", ad);
2561 cmp = memcmp(emptybuf, fi, ADEDLEN_FINDERI);
2565 static bool ai_empty_finderinfo(const AfpInfo *ai)
2568 char emptybuf[ADEDLEN_FINDERI] = {0};
2570 cmp = memcmp(emptybuf, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
2575 * Update btime with btime from Netatalk
2577 static void update_btime(vfs_handle_struct *handle,
2578 struct smb_filename *smb_fname)
2581 struct timespec creation_time = {0};
2583 struct fruit_config_data *config = NULL;
2585 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
2588 switch (config->meta) {
2589 case FRUIT_META_STREAM:
2591 case FRUIT_META_NETATALK:
2595 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
2599 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
2603 if (ad_getdate(ad, AD_DATE_UNIX | AD_DATE_CREATE, &t) != 0) {
2609 creation_time.tv_sec = convert_uint32_t_to_time_t(t);
2610 update_stat_ex_create_time(&smb_fname->st, creation_time);
2616 * Map an access mask to a Netatalk single byte byte range lock
2618 static off_t access_to_netatalk_brl(enum apple_fork fork_type,
2619 uint32_t access_mask)
2623 switch (access_mask) {
2624 case FILE_READ_DATA:
2625 offset = AD_FILELOCK_OPEN_RD;
2628 case FILE_WRITE_DATA:
2629 case FILE_APPEND_DATA:
2630 offset = AD_FILELOCK_OPEN_WR;
2634 offset = AD_FILELOCK_OPEN_NONE;
2638 if (fork_type == APPLE_FORK_RSRC) {
2639 if (offset == AD_FILELOCK_OPEN_NONE) {
2640 offset = AD_FILELOCK_RSRC_OPEN_NONE;
2650 * Map a deny mode to a Netatalk brl
2652 static off_t denymode_to_netatalk_brl(enum apple_fork fork_type,
2657 switch (deny_mode) {
2659 offset = AD_FILELOCK_DENY_RD;
2663 offset = AD_FILELOCK_DENY_WR;
2667 smb_panic("denymode_to_netatalk_brl: bad deny mode\n");
2670 if (fork_type == APPLE_FORK_RSRC) {
2678 * Call fcntl() with an exclusive F_GETLK request in order to
2679 * determine if there's an exisiting shared lock
2681 * @return true if the requested lock was found or any error occurred
2682 * false if the lock was not found
2684 static bool test_netatalk_lock(files_struct *fsp, off_t in_offset)
2687 off_t offset = in_offset;
2692 result = SMB_VFS_GETLOCK(fsp, &offset, &len, &type, &pid);
2693 if (result == false) {
2697 if (type != F_UNLCK) {
2704 static NTSTATUS fruit_check_access(vfs_handle_struct *handle,
2706 uint32_t access_mask,
2707 uint32_t share_mode)
2709 NTSTATUS status = NT_STATUS_OK;
2711 bool share_for_read = (share_mode & FILE_SHARE_READ);
2712 bool share_for_write = (share_mode & FILE_SHARE_WRITE);
2713 bool netatalk_already_open_for_reading = false;
2714 bool netatalk_already_open_for_writing = false;
2715 bool netatalk_already_open_with_deny_read = false;
2716 bool netatalk_already_open_with_deny_write = false;
2718 /* FIXME: hardcoded data fork, add resource fork */
2719 enum apple_fork fork_type = APPLE_FORK_DATA;
2721 DBG_DEBUG("fruit_check_access: %s, am: %s/%s, sm: 0x%x\n",
2723 access_mask & FILE_READ_DATA ? "READ" :"-",
2724 access_mask & FILE_WRITE_DATA ? "WRITE" : "-",
2727 if (fsp->fh->fd == -1) {
2728 return NT_STATUS_OK;
2731 /* Read NetATalk opens and deny modes on the file. */
2732 netatalk_already_open_for_reading = test_netatalk_lock(fsp,
2733 access_to_netatalk_brl(fork_type,
2736 netatalk_already_open_with_deny_read = test_netatalk_lock(fsp,
2737 denymode_to_netatalk_brl(fork_type,
2740 netatalk_already_open_for_writing = test_netatalk_lock(fsp,
2741 access_to_netatalk_brl(fork_type,
2744 netatalk_already_open_with_deny_write = test_netatalk_lock(fsp,
2745 denymode_to_netatalk_brl(fork_type,
2748 /* If there are any conflicts - sharing violation. */
2749 if ((access_mask & FILE_READ_DATA) &&
2750 netatalk_already_open_with_deny_read) {
2751 return NT_STATUS_SHARING_VIOLATION;
2754 if (!share_for_read &&
2755 netatalk_already_open_for_reading) {
2756 return NT_STATUS_SHARING_VIOLATION;
2759 if ((access_mask & FILE_WRITE_DATA) &&
2760 netatalk_already_open_with_deny_write) {
2761 return NT_STATUS_SHARING_VIOLATION;
2764 if (!share_for_write &&
2765 netatalk_already_open_for_writing) {
2766 return NT_STATUS_SHARING_VIOLATION;
2769 if (!(access_mask & FILE_READ_DATA)) {
2771 * Nothing we can do here, we need read access
2774 return NT_STATUS_OK;
2777 /* Set NetAtalk locks matching our access */
2778 if (access_mask & FILE_READ_DATA) {
2779 struct byte_range_lock *br_lck = NULL;
2781 off = access_to_netatalk_brl(fork_type, FILE_READ_DATA);
2783 handle->conn->sconn->msg_ctx, fsp,
2784 fsp->op->global->open_persistent_id, 1, off,
2785 READ_LOCK, POSIX_LOCK, false,
2788 TALLOC_FREE(br_lck);
2790 if (!NT_STATUS_IS_OK(status)) {
2795 if (!share_for_read) {
2796 struct byte_range_lock *br_lck = NULL;
2798 off = denymode_to_netatalk_brl(fork_type, DENY_READ);
2800 handle->conn->sconn->msg_ctx, fsp,
2801 fsp->op->global->open_persistent_id, 1, off,
2802 READ_LOCK, POSIX_LOCK, false,
2805 TALLOC_FREE(br_lck);
2807 if (!NT_STATUS_IS_OK(status)) {
2812 if (access_mask & FILE_WRITE_DATA) {
2813 struct byte_range_lock *br_lck = NULL;
2815 off = access_to_netatalk_brl(fork_type, FILE_WRITE_DATA);
2817 handle->conn->sconn->msg_ctx, fsp,
2818 fsp->op->global->open_persistent_id, 1, off,
2819 READ_LOCK, POSIX_LOCK, false,
2822 TALLOC_FREE(br_lck);
2824 if (!NT_STATUS_IS_OK(status)) {
2829 if (!share_for_write) {
2830 struct byte_range_lock *br_lck = NULL;
2832 off = denymode_to_netatalk_brl(fork_type, DENY_WRITE);
2834 handle->conn->sconn->msg_ctx, fsp,
2835 fsp->op->global->open_persistent_id, 1, off,
2836 READ_LOCK, POSIX_LOCK, false,
2839 TALLOC_FREE(br_lck);
2841 if (!NT_STATUS_IS_OK(status)) {
2846 return NT_STATUS_OK;
2849 static NTSTATUS check_aapl(vfs_handle_struct *handle,
2850 struct smb_request *req,
2851 const struct smb2_create_blobs *in_context_blobs,
2852 struct smb2_create_blobs *out_context_blobs)
2854 struct fruit_config_data *config;
2856 struct smb2_create_blob *aapl = NULL;
2860 DATA_BLOB blob = data_blob_talloc(req, NULL, 0);
2861 uint64_t req_bitmap, client_caps;
2862 uint64_t server_caps = SMB2_CRTCTX_AAPL_UNIX_BASED;
2866 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
2867 return NT_STATUS_UNSUCCESSFUL);
2869 if (!config->use_aapl
2870 || in_context_blobs == NULL
2871 || out_context_blobs == NULL) {
2872 return NT_STATUS_OK;
2875 aapl = smb2_create_blob_find(in_context_blobs,
2876 SMB2_CREATE_TAG_AAPL);
2878 return NT_STATUS_OK;
2881 if (aapl->data.length != 24) {
2882 DEBUG(1, ("unexpected AAPL ctxt length: %ju\n",
2883 (uintmax_t)aapl->data.length));
2884 return NT_STATUS_INVALID_PARAMETER;
2887 cmd = IVAL(aapl->data.data, 0);
2888 if (cmd != SMB2_CRTCTX_AAPL_SERVER_QUERY) {
2889 DEBUG(1, ("unsupported AAPL cmd: %d\n", cmd));
2890 return NT_STATUS_INVALID_PARAMETER;
2893 req_bitmap = BVAL(aapl->data.data, 8);
2894 client_caps = BVAL(aapl->data.data, 16);
2896 SIVAL(p, 0, SMB2_CRTCTX_AAPL_SERVER_QUERY);
2898 SBVAL(p, 8, req_bitmap);
2899 ok = data_blob_append(req, &blob, p, 16);
2901 return NT_STATUS_UNSUCCESSFUL;
2904 if (req_bitmap & SMB2_CRTCTX_AAPL_SERVER_CAPS) {
2905 if ((client_caps & SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR) &&
2906 (handle->conn->tcon->compat->fs_capabilities & FILE_NAMED_STREAMS)) {
2907 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR;
2908 config->readdir_attr_enabled = true;
2911 if (config->use_copyfile) {
2912 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_OSX_COPYFILE;
2913 config->copyfile_enabled = true;
2917 * The client doesn't set the flag, so we can't check
2918 * for it and just set it unconditionally
2920 if (config->unix_info_enabled) {
2921 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_NFS_ACE;
2924 SBVAL(p, 0, server_caps);
2925 ok = data_blob_append(req, &blob, p, 8);
2927 return NT_STATUS_UNSUCCESSFUL;
2931 if (req_bitmap & SMB2_CRTCTX_AAPL_VOLUME_CAPS) {
2932 int val = lp_case_sensitive(SNUM(handle->conn->tcon->compat));
2940 caps |= SMB2_CRTCTX_AAPL_CASE_SENSITIVE;
2947 if (config->time_machine) {
2948 caps |= SMB2_CRTCTX_AAPL_FULL_SYNC;
2953 ok = data_blob_append(req, &blob, p, 8);
2955 return NT_STATUS_UNSUCCESSFUL;
2959 if (req_bitmap & SMB2_CRTCTX_AAPL_MODEL_INFO) {
2960 ok = convert_string_talloc(req,
2961 CH_UNIX, CH_UTF16LE,
2962 config->model, strlen(config->model),
2965 return NT_STATUS_UNSUCCESSFUL;
2969 SIVAL(p + 4, 0, modellen);
2970 ok = data_blob_append(req, &blob, p, 8);
2973 return NT_STATUS_UNSUCCESSFUL;
2976 ok = data_blob_append(req, &blob, model, modellen);
2979 return NT_STATUS_UNSUCCESSFUL;
2983 status = smb2_create_blob_add(out_context_blobs,
2985 SMB2_CREATE_TAG_AAPL,
2987 if (NT_STATUS_IS_OK(status)) {
2988 global_fruit_config.nego_aapl = true;
2989 if (config->aapl_zero_file_id) {
2990 aapl_force_zero_file_id(handle->conn->sconn);
2997 static bool readdir_attr_meta_finderi_stream(
2998 struct vfs_handle_struct *handle,
2999 const struct smb_filename *smb_fname,
3002 struct smb_filename *stream_name = NULL;
3003 files_struct *fsp = NULL;
3008 uint8_t buf[AFP_INFO_SIZE];
3010 stream_name = synthetic_smb_fname(talloc_tos(),
3011 smb_fname->base_name,
3012 AFPINFO_STREAM_NAME,
3013 NULL, smb_fname->flags);
3014 if (stream_name == NULL) {
3018 ret = SMB_VFS_STAT(handle->conn, stream_name);
3023 status = SMB_VFS_CREATE_FILE(
3024 handle->conn, /* conn */
3026 0, /* root_dir_fid */
3027 stream_name, /* fname */
3028 FILE_READ_DATA, /* access_mask */
3029 (FILE_SHARE_READ | FILE_SHARE_WRITE | /* share_access */
3031 FILE_OPEN, /* create_disposition*/
3032 0, /* create_options */
3033 0, /* file_attributes */
3034 INTERNAL_OPEN_ONLY, /* oplock_request */
3036 0, /* allocation_size */
3037 0, /* private_flags */
3042 NULL, NULL); /* create context */
3044 TALLOC_FREE(stream_name);
3046 if (!NT_STATUS_IS_OK(status)) {
3050 nread = SMB_VFS_PREAD(fsp, &buf[0], AFP_INFO_SIZE, 0);
3051 if (nread != AFP_INFO_SIZE) {
3052 DBG_ERR("short read [%s] [%zd/%d]\n",
3053 smb_fname_str_dbg(stream_name), nread, AFP_INFO_SIZE);
3058 memcpy(&ai->afpi_FinderInfo[0], &buf[AFP_OFF_FinderInfo],
3065 close_file(NULL, fsp, NORMAL_CLOSE);
3071 static bool readdir_attr_meta_finderi_netatalk(
3072 struct vfs_handle_struct *handle,
3073 const struct smb_filename *smb_fname,
3076 struct adouble *ad = NULL;
3079 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
3084 p = ad_get_entry(ad, ADEID_FINDERI);
3086 DBG_ERR("No ADEID_FINDERI for [%s]\n", smb_fname->base_name);
3091 memcpy(&ai->afpi_FinderInfo[0], p, AFP_FinderSize);
3096 static bool readdir_attr_meta_finderi(struct vfs_handle_struct *handle,
3097 const struct smb_filename *smb_fname,
3098 struct readdir_attr_data *attr_data)
3100 struct fruit_config_data *config = NULL;
3101 uint32_t date_added;
3105 SMB_VFS_HANDLE_GET_DATA(handle, config,
3106 struct fruit_config_data,
3109 switch (config->meta) {
3110 case FRUIT_META_NETATALK:
3111 ok = readdir_attr_meta_finderi_netatalk(
3112 handle, smb_fname, &ai);
3115 case FRUIT_META_STREAM:
3116 ok = readdir_attr_meta_finderi_stream(
3117 handle, smb_fname, &ai);
3121 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3126 /* Don't bother with errors, it's likely ENOENT */
3130 if (S_ISREG(smb_fname->st.st_ex_mode)) {
3132 memcpy(&attr_data->attr_data.aapl.finder_info[0],
3133 &ai.afpi_FinderInfo[0], 4);
3135 /* finder_creator */
3136 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 4,
3137 &ai.afpi_FinderInfo[4], 4);
3141 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 8,
3142 &ai.afpi_FinderInfo[8], 2);
3144 /* finder_ext_flags */
3145 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 10,
3146 &ai.afpi_FinderInfo[24], 2);
3149 date_added = convert_time_t_to_uint32_t(
3150 smb_fname->st.st_ex_btime.tv_sec - AD_DATE_DELTA);
3152 RSIVAL(&attr_data->attr_data.aapl.finder_info[0], 12, date_added);
3157 static uint64_t readdir_attr_rfork_size_adouble(
3158 struct vfs_handle_struct *handle,
3159 const struct smb_filename *smb_fname)
3161 struct adouble *ad = NULL;
3162 uint64_t rfork_size;
3164 ad = ad_get(talloc_tos(), handle, smb_fname,
3170 rfork_size = ad_getentrylen(ad, ADEID_RFORK);
3176 static uint64_t readdir_attr_rfork_size_stream(
3177 struct vfs_handle_struct *handle,
3178 const struct smb_filename *smb_fname)
3180 struct smb_filename *stream_name = NULL;
3182 uint64_t rfork_size;
3184 stream_name = synthetic_smb_fname(talloc_tos(),
3185 smb_fname->base_name,
3186 AFPRESOURCE_STREAM_NAME,
3188 if (stream_name == NULL) {
3192 ret = SMB_VFS_STAT(handle->conn, stream_name);
3194 TALLOC_FREE(stream_name);
3198 rfork_size = stream_name->st.st_ex_size;
3199 TALLOC_FREE(stream_name);
3204 static uint64_t readdir_attr_rfork_size(struct vfs_handle_struct *handle,
3205 const struct smb_filename *smb_fname)
3207 struct fruit_config_data *config = NULL;
3208 uint64_t rfork_size;
3210 SMB_VFS_HANDLE_GET_DATA(handle, config,
3211 struct fruit_config_data,
3214 switch (config->rsrc) {
3215 case FRUIT_RSRC_ADFILE:
3216 rfork_size = readdir_attr_rfork_size_adouble(handle,
3220 case FRUIT_RSRC_XATTR:
3221 case FRUIT_RSRC_STREAM:
3222 rfork_size = readdir_attr_rfork_size_stream(handle,
3227 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3235 static NTSTATUS readdir_attr_macmeta(struct vfs_handle_struct *handle,
3236 const struct smb_filename *smb_fname,
3237 struct readdir_attr_data *attr_data)
3239 NTSTATUS status = NT_STATUS_OK;
3240 struct fruit_config_data *config = NULL;
3243 SMB_VFS_HANDLE_GET_DATA(handle, config,
3244 struct fruit_config_data,
3245 return NT_STATUS_UNSUCCESSFUL);
3248 /* Ensure we return a default value in the creation_date field */
3249 RSIVAL(&attr_data->attr_data.aapl.finder_info, 12, AD_DATE_START);
3252 * Resource fork length
3255 if (config->readdir_attr_rsize) {
3256 uint64_t rfork_size;
3258 rfork_size = readdir_attr_rfork_size(handle, smb_fname);
3259 attr_data->attr_data.aapl.rfork_size = rfork_size;
3266 if (config->readdir_attr_finder_info) {
3267 ok = readdir_attr_meta_finderi(handle, smb_fname, attr_data);
3269 status = NT_STATUS_INTERNAL_ERROR;
3276 static NTSTATUS remove_virtual_nfs_aces(struct security_descriptor *psd)
3281 if (psd->dacl == NULL) {
3282 return NT_STATUS_OK;
3285 for (i = 0; i < psd->dacl->num_aces; i++) {
3286 /* MS NFS style mode/uid/gid */
3287 int cmp = dom_sid_compare_domain(
3288 &global_sid_Unix_NFS,
3289 &psd->dacl->aces[i].trustee);
3291 /* Normal ACE entry. */
3296 * security_descriptor_dacl_del()
3297 * *must* return NT_STATUS_OK as we know
3298 * we have something to remove.
3301 status = security_descriptor_dacl_del(psd,
3302 &psd->dacl->aces[i].trustee);
3303 if (!NT_STATUS_IS_OK(status)) {
3304 DBG_WARNING("failed to remove MS NFS style ACE: %s\n",
3310 * security_descriptor_dacl_del() may delete more
3311 * then one entry subsequent to this one if the
3312 * SID matches, but we only need to ensure that
3313 * we stay looking at the same element in the array.
3317 return NT_STATUS_OK;
3320 /* Search MS NFS style ACE with UNIX mode */
3321 static NTSTATUS check_ms_nfs(vfs_handle_struct *handle,
3323 struct security_descriptor *psd,
3328 struct fruit_config_data *config = NULL;
3332 SMB_VFS_HANDLE_GET_DATA(handle, config,
3333 struct fruit_config_data,
3334 return NT_STATUS_UNSUCCESSFUL);
3336 if (!global_fruit_config.nego_aapl) {
3337 return NT_STATUS_OK;
3339 if (psd->dacl == NULL || !config->unix_info_enabled) {
3340 return NT_STATUS_OK;
3343 for (i = 0; i < psd->dacl->num_aces; i++) {
3344 if (dom_sid_compare_domain(
3345 &global_sid_Unix_NFS_Mode,
3346 &psd->dacl->aces[i].trustee) == 0) {
3347 *pmode = (mode_t)psd->dacl->aces[i].trustee.sub_auths[2];
3348 *pmode &= (S_IRWXU | S_IRWXG | S_IRWXO);
3351 DEBUG(10, ("MS NFS chmod request %s, %04o\n",
3352 fsp_str_dbg(fsp), (unsigned)(*pmode)));
3358 * Remove any incoming virtual ACE entries generated by
3359 * fruit_fget_nt_acl().
3362 return remove_virtual_nfs_aces(psd);
3365 /****************************************************************************
3367 ****************************************************************************/
3369 static int fruit_connect(vfs_handle_struct *handle,
3370 const char *service,
3374 char *list = NULL, *newlist = NULL;
3375 struct fruit_config_data *config;
3377 DEBUG(10, ("fruit_connect\n"));
3379 rc = SMB_VFS_NEXT_CONNECT(handle, service, user);
3384 rc = init_fruit_config(handle);
3389 SMB_VFS_HANDLE_GET_DATA(handle, config,
3390 struct fruit_config_data, return -1);
3392 if (config->veto_appledouble) {
3393 list = lp_veto_files(talloc_tos(), SNUM(handle->conn));
3396 if (strstr(list, "/" ADOUBLE_NAME_PREFIX "*/") == NULL) {
3397 newlist = talloc_asprintf(
3399 "%s/" ADOUBLE_NAME_PREFIX "*/",
3401 lp_do_parameter(SNUM(handle->conn),
3406 lp_do_parameter(SNUM(handle->conn),
3408 "/" ADOUBLE_NAME_PREFIX "*/");
3414 if (config->encoding == FRUIT_ENC_NATIVE) {
3415 lp_do_parameter(SNUM(handle->conn),
3420 if (config->time_machine) {
3421 DBG_NOTICE("Enabling durable handles for Time Machine "
3422 "support on [%s]\n", service);
3423 lp_do_parameter(SNUM(handle->conn), "durable handles", "yes");
3424 lp_do_parameter(SNUM(handle->conn), "kernel oplocks", "no");
3425 lp_do_parameter(SNUM(handle->conn), "kernel share modes", "no");
3426 if (!lp_strict_sync(SNUM(handle->conn))) {
3427 DBG_WARNING("Time Machine without strict sync is not "
3430 lp_do_parameter(SNUM(handle->conn), "posix locking", "no");
3436 static int fruit_fake_fd(void)
3443 * Return a valid fd, but ensure any attempt to use it returns
3444 * an error (EPIPE). Once we get a write on the handle, we open
3447 ret = pipe(pipe_fds);
3457 static int fruit_open_meta_stream(vfs_handle_struct *handle,
3458 struct smb_filename *smb_fname,
3463 struct fruit_config_data *config = NULL;
3464 struct fio *fio = NULL;
3465 int open_flags = flags & ~O_CREAT;
3468 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3470 SMB_VFS_HANDLE_GET_DATA(handle, config,
3471 struct fruit_config_data, return -1);
3473 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3474 fio->type = ADOUBLE_META;
3475 fio->config = config;
3477 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, open_flags, mode);
3482 if (!(flags & O_CREAT)) {
3483 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
3487 fd = fruit_fake_fd();
3489 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
3493 fio->fake_fd = true;
3500 static int fruit_open_meta_netatalk(vfs_handle_struct *handle,
3501 struct smb_filename *smb_fname,
3506 struct fruit_config_data *config = NULL;
3507 struct fio *fio = NULL;
3508 struct adouble *ad = NULL;
3509 bool meta_exists = false;
3512 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3514 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
3521 if (!meta_exists && !(flags & O_CREAT)) {
3526 fd = fruit_fake_fd();
3531 SMB_VFS_HANDLE_GET_DATA(handle, config,
3532 struct fruit_config_data, return -1);
3534 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3535 fio->type = ADOUBLE_META;
3536 fio->config = config;
3537 fio->fake_fd = true;
3544 static int fruit_open_meta(vfs_handle_struct *handle,
3545 struct smb_filename *smb_fname,
3546 files_struct *fsp, int flags, mode_t mode)
3549 struct fruit_config_data *config = NULL;
3551 DBG_DEBUG("path [%s]\n", smb_fname_str_dbg(smb_fname));
3553 SMB_VFS_HANDLE_GET_DATA(handle, config,
3554 struct fruit_config_data, return -1);
3556 switch (config->meta) {
3557 case FRUIT_META_STREAM:
3558 fd = fruit_open_meta_stream(handle, smb_fname,
3562 case FRUIT_META_NETATALK:
3563 fd = fruit_open_meta_netatalk(handle, smb_fname,
3568 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3572 DBG_DEBUG("path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3577 static int fruit_open_rsrc_adouble(vfs_handle_struct *handle,
3578 struct smb_filename *smb_fname,
3584 struct adouble *ad = NULL;
3585 struct smb_filename *smb_fname_base = NULL;
3586 struct fruit_config_data *config = NULL;
3589 SMB_VFS_HANDLE_GET_DATA(handle, config,
3590 struct fruit_config_data, return -1);
3592 if ((!(flags & O_CREAT)) &&
3593 S_ISDIR(fsp->base_fsp->fsp_name->st.st_ex_mode))
3595 /* sorry, but directories don't habe a resource fork */
3600 rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_base);
3605 /* We always need read/write access for the metadata header too */
3606 flags &= ~(O_RDONLY | O_WRONLY);
3609 hostfd = SMB_VFS_NEXT_OPEN(handle, smb_fname_base, fsp,
3616 if (flags & (O_CREAT | O_TRUNC)) {
3617 ad = ad_init(fsp, ADOUBLE_RSRC);
3623 fsp->fh->fd = hostfd;
3625 rc = ad_fset(handle, ad, fsp);
3636 TALLOC_FREE(smb_fname_base);
3638 DEBUG(10, ("fruit_open resource fork: rc=%d, fd=%d\n", rc, hostfd));
3640 int saved_errno = errno;
3643 * BUGBUGBUG -- we would need to call
3644 * fd_close_posix here, but we don't have a
3647 fsp->fh->fd = hostfd;
3651 errno = saved_errno;
3656 static int fruit_open_rsrc_xattr(vfs_handle_struct *handle,
3657 struct smb_filename *smb_fname,
3662 #ifdef HAVE_ATTROPEN
3665 fd = attropen(smb_fname->base_name,
3666 AFPRESOURCE_EA_NETATALK,
3681 static int fruit_open_rsrc(vfs_handle_struct *handle,
3682 struct smb_filename *smb_fname,
3683 files_struct *fsp, int flags, mode_t mode)
3686 struct fruit_config_data *config = NULL;
3687 struct fio *fio = NULL;
3689 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3691 SMB_VFS_HANDLE_GET_DATA(handle, config,
3692 struct fruit_config_data, return -1);
3694 switch (config->rsrc) {
3695 case FRUIT_RSRC_STREAM:
3696 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3699 case FRUIT_RSRC_ADFILE:
3700 fd = fruit_open_rsrc_adouble(handle, smb_fname,
3704 case FRUIT_RSRC_XATTR:
3705 fd = fruit_open_rsrc_xattr(handle, smb_fname,
3710 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3714 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3720 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3721 fio->type = ADOUBLE_RSRC;
3722 fio->config = config;
3727 static int fruit_open(vfs_handle_struct *handle,
3728 struct smb_filename *smb_fname,
3729 files_struct *fsp, int flags, mode_t mode)
3733 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3735 if (!is_ntfs_stream_smb_fname(smb_fname)) {
3736 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3739 if (is_afpinfo_stream(smb_fname)) {
3740 fd = fruit_open_meta(handle, smb_fname, fsp, flags, mode);
3741 } else if (is_afpresource_stream(smb_fname)) {
3742 fd = fruit_open_rsrc(handle, smb_fname, fsp, flags, mode);
3744 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3747 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3752 static int fruit_close_meta(vfs_handle_struct *handle,
3756 struct fruit_config_data *config = NULL;
3758 SMB_VFS_HANDLE_GET_DATA(handle, config,
3759 struct fruit_config_data, return -1);
3761 switch (config->meta) {
3762 case FRUIT_META_STREAM:
3763 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3766 case FRUIT_META_NETATALK:
3767 ret = close(fsp->fh->fd);
3772 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3780 static int fruit_close_rsrc(vfs_handle_struct *handle,
3784 struct fruit_config_data *config = NULL;
3786 SMB_VFS_HANDLE_GET_DATA(handle, config,
3787 struct fruit_config_data, return -1);
3789 switch (config->rsrc) {
3790 case FRUIT_RSRC_STREAM:
3791 case FRUIT_RSRC_ADFILE:
3792 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3795 case FRUIT_RSRC_XATTR:
3796 ret = close(fsp->fh->fd);
3801 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3808 static int fruit_close(vfs_handle_struct *handle,
3816 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(fsp->fsp_name), fd);
3818 if (!is_ntfs_stream_smb_fname(fsp->fsp_name)) {
3819 return SMB_VFS_NEXT_CLOSE(handle, fsp);
3822 if (is_afpinfo_stream(fsp->fsp_name)) {
3823 ret = fruit_close_meta(handle, fsp);
3824 } else if (is_afpresource_stream(fsp->fsp_name)) {
3825 ret = fruit_close_rsrc(handle, fsp);
3827 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
3833 static int fruit_rename(struct vfs_handle_struct *handle,
3834 const struct smb_filename *smb_fname_src,
3835 const struct smb_filename *smb_fname_dst)
3838 struct fruit_config_data *config = NULL;
3839 struct smb_filename *src_adp_smb_fname = NULL;
3840 struct smb_filename *dst_adp_smb_fname = NULL;
3842 SMB_VFS_HANDLE_GET_DATA(handle, config,
3843 struct fruit_config_data, return -1);
3845 if (!VALID_STAT(smb_fname_src->st)) {
3846 DBG_ERR("Need valid stat for [%s]\n",
3847 smb_fname_str_dbg(smb_fname_src));
3851 rc = SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
3856 if ((config->rsrc != FRUIT_RSRC_ADFILE) ||
3857 (!S_ISREG(smb_fname_src->st.st_ex_mode)))
3862 rc = adouble_path(talloc_tos(), smb_fname_src, &src_adp_smb_fname);
3867 rc = adouble_path(talloc_tos(), smb_fname_dst, &dst_adp_smb_fname);
3872 DBG_DEBUG("%s -> %s\n",
3873 smb_fname_str_dbg(src_adp_smb_fname),
3874 smb_fname_str_dbg(dst_adp_smb_fname));
3876 rc = SMB_VFS_NEXT_RENAME(handle, src_adp_smb_fname, dst_adp_smb_fname);
3877 if (errno == ENOENT) {
3882 TALLOC_FREE(src_adp_smb_fname);
3883 TALLOC_FREE(dst_adp_smb_fname);
3887 static int fruit_unlink_meta_stream(vfs_handle_struct *handle,
3888 const struct smb_filename *smb_fname)
3890 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3893 static int fruit_unlink_meta_netatalk(vfs_handle_struct *handle,
3894 const struct smb_filename *smb_fname)
3896 return SMB_VFS_REMOVEXATTR(handle->conn,
3898 AFPINFO_EA_NETATALK);
3901 static int fruit_unlink_meta(vfs_handle_struct *handle,
3902 const struct smb_filename *smb_fname)
3904 struct fruit_config_data *config = NULL;
3907 SMB_VFS_HANDLE_GET_DATA(handle, config,
3908 struct fruit_config_data, return -1);
3910 switch (config->meta) {
3911 case FRUIT_META_STREAM:
3912 rc = fruit_unlink_meta_stream(handle, smb_fname);
3915 case FRUIT_META_NETATALK:
3916 rc = fruit_unlink_meta_netatalk(handle, smb_fname);
3920 DBG_ERR("Unsupported meta config [%d]\n", config->meta);
3927 static int fruit_unlink_rsrc_stream(vfs_handle_struct *handle,
3928 const struct smb_filename *smb_fname,
3933 if (!force_unlink) {
3934 struct smb_filename *smb_fname_cp = NULL;
3937 smb_fname_cp = cp_smb_filename(talloc_tos(), smb_fname);
3938 if (smb_fname_cp == NULL) {
3943 * 0 byte resource fork streams are not listed by
3944 * vfs_streaminfo, as a result stream cleanup/deletion of file
3945 * deletion doesn't remove the resourcefork stream.
3948 ret = SMB_VFS_NEXT_STAT(handle, smb_fname_cp);
3950 TALLOC_FREE(smb_fname_cp);
3951 DBG_ERR("stat [%s] failed [%s]\n",
3952 smb_fname_str_dbg(smb_fname_cp), strerror(errno));
3956 size = smb_fname_cp->st.st_ex_size;
3957 TALLOC_FREE(smb_fname_cp);
3960 /* OS X ignores resource fork stream delete requests */
3965 ret = SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3966 if ((ret != 0) && (errno == ENOENT) && force_unlink) {
3973 static int fruit_unlink_rsrc_adouble(vfs_handle_struct *handle,
3974 const struct smb_filename *smb_fname,
3978 struct adouble *ad = NULL;
3979 struct smb_filename *adp_smb_fname = NULL;
3981 if (!force_unlink) {
3982 ad = ad_get(talloc_tos(), handle, smb_fname,
3991 * 0 byte resource fork streams are not listed by
3992 * vfs_streaminfo, as a result stream cleanup/deletion of file
3993 * deletion doesn't remove the resourcefork stream.
3996 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
3997 /* OS X ignores resource fork stream delete requests */
4005 rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
4010 rc = SMB_VFS_NEXT_UNLINK(handle, adp_smb_fname);
4011 TALLOC_FREE(adp_smb_fname);
4012 if ((rc != 0) && (errno == ENOENT) && force_unlink) {
4019 static int fruit_unlink_rsrc_xattr(vfs_handle_struct *handle,
4020 const struct smb_filename *smb_fname,
4024 * OS X ignores resource fork stream delete requests, so nothing to do
4025 * here. Removing the file will remove the xattr anyway, so we don't
4026 * have to take care of removing 0 byte resource forks that could be
4032 static int fruit_unlink_rsrc(vfs_handle_struct *handle,
4033 const struct smb_filename *smb_fname,
4036 struct fruit_config_data *config = NULL;
4039 SMB_VFS_HANDLE_GET_DATA(handle, config,
4040 struct fruit_config_data, return -1);
4042 switch (config->rsrc) {
4043 case FRUIT_RSRC_STREAM:
4044 rc = fruit_unlink_rsrc_stream(handle, smb_fname, force_unlink);
4047 case FRUIT_RSRC_ADFILE:
4048 rc = fruit_unlink_rsrc_adouble(handle, smb_fname, force_unlink);
4051 case FRUIT_RSRC_XATTR:
4052 rc = fruit_unlink_rsrc_xattr(handle, smb_fname, force_unlink);
4056 DBG_ERR("Unsupported rsrc config [%d]\n", config->rsrc);
4063 static int fruit_unlink(vfs_handle_struct *handle,
4064 const struct smb_filename *smb_fname)
4067 struct fruit_config_data *config = NULL;
4068 struct smb_filename *rsrc_smb_fname = NULL;
4070 SMB_VFS_HANDLE_GET_DATA(handle, config,
4071 struct fruit_config_data, return -1);
4073 if (is_afpinfo_stream(smb_fname)) {
4074 return fruit_unlink_meta(handle, smb_fname);
4075 } else if (is_afpresource_stream(smb_fname)) {
4076 return fruit_unlink_rsrc(handle, smb_fname, false);
4077 } else if (is_ntfs_stream_smb_fname(smb_fname)) {
4078 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4079 } else if (is_adouble_file(smb_fname->base_name)) {
4080 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4084 * A request to delete the base file. Because 0 byte resource
4085 * fork streams are not listed by fruit_streaminfo,
4086 * delete_all_streams() can't remove 0 byte resource fork
4087 * streams, so we have to cleanup this here.
4089 rsrc_smb_fname = synthetic_smb_fname(talloc_tos(),
4090 smb_fname->base_name,
4091 AFPRESOURCE_STREAM_NAME,
4094 if (rsrc_smb_fname == NULL) {
4098 rc = fruit_unlink_rsrc(handle, rsrc_smb_fname, true);
4099 if ((rc != 0) && (errno != ENOENT)) {
4100 DBG_ERR("Forced unlink of [%s] failed [%s]\n",
4101 smb_fname_str_dbg(rsrc_smb_fname), strerror(errno));
4102 TALLOC_FREE(rsrc_smb_fname);
4105 TALLOC_FREE(rsrc_smb_fname);
4107 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
4110 static int fruit_chmod(vfs_handle_struct *handle,
4111 const struct smb_filename *smb_fname,
4115 struct fruit_config_data *config = NULL;
4116 struct smb_filename *smb_fname_adp = NULL;
4118 rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname, mode);
4123 SMB_VFS_HANDLE_GET_DATA(handle, config,
4124 struct fruit_config_data, return -1);
4126 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4130 if (!VALID_STAT(smb_fname->st)) {
4134 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
4138 rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_adp);
4143 DEBUG(10, ("fruit_chmod: %s\n", smb_fname_adp->base_name));
4145 rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname_adp, mode);
4146 if (errno == ENOENT) {
4150 TALLOC_FREE(smb_fname_adp);
4154 static int fruit_chown(vfs_handle_struct *handle,
4155 const struct smb_filename *smb_fname,
4160 struct fruit_config_data *config = NULL;
4161 struct smb_filename *adp_smb_fname = NULL;
4163 rc = SMB_VFS_NEXT_CHOWN(handle, smb_fname, uid, gid);
4168 SMB_VFS_HANDLE_GET_DATA(handle, config,
4169 struct fruit_config_data, return -1);
4171 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4175 if (!VALID_STAT(smb_fname->st)) {
4179 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
4183 rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
4188 DEBUG(10, ("fruit_chown: %s\n", adp_smb_fname->base_name));
4190 rc = SMB_VFS_NEXT_CHOWN(handle, adp_smb_fname, uid, gid);
4191 if (errno == ENOENT) {
4196 TALLOC_FREE(adp_smb_fname);
4200 static int fruit_rmdir(struct vfs_handle_struct *handle,
4201 const struct smb_filename *smb_fname)
4205 struct fruit_config_data *config;
4207 SMB_VFS_HANDLE_GET_DATA(handle, config,
4208 struct fruit_config_data, return -1);
4210 if (config->rsrc != FRUIT_RSRC_ADFILE) {
4215 * Due to there is no way to change bDeleteVetoFiles variable
4216 * from this module, need to clean up ourselves
4219 dh = SMB_VFS_OPENDIR(handle->conn, smb_fname, NULL, 0);
4224 while ((de = SMB_VFS_READDIR(handle->conn, dh, NULL)) != NULL) {
4225 struct adouble *ad = NULL;
4227 struct smb_filename *ad_smb_fname = NULL;
4230 if (!is_adouble_file(de->d_name)) {
4234 p = talloc_asprintf(talloc_tos(), "%s/%s",
4235 smb_fname->base_name, de->d_name);
4237 DBG_ERR("talloc_asprintf failed\n");
4241 ad_smb_fname = synthetic_smb_fname(talloc_tos(), p,
4245 if (ad_smb_fname == NULL) {
4246 DBG_ERR("synthetic_smb_fname failed\n");
4251 * Check whether it's a valid AppleDouble file, if
4252 * yes, delete it, ignore it otherwise.
4254 ad = ad_get(talloc_tos(), handle, ad_smb_fname, ADOUBLE_RSRC);
4256 TALLOC_FREE(ad_smb_fname);
4262 ret = SMB_VFS_NEXT_UNLINK(handle, ad_smb_fname);
4264 DBG_ERR("Deleting [%s] failed\n",
4265 smb_fname_str_dbg(ad_smb_fname));
4267 TALLOC_FREE(ad_smb_fname);
4272 SMB_VFS_CLOSEDIR(handle->conn, dh);
4274 return SMB_VFS_NEXT_RMDIR(handle, smb_fname);
4277 static ssize_t fruit_pread_meta_stream(vfs_handle_struct *handle,
4278 files_struct *fsp, void *data,
4279 size_t n, off_t offset)
4284 nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4285 if (nread == -1 || nread == n) {
4289 DBG_ERR("Removing [%s] after short read [%zd]\n",
4290 fsp_str_dbg(fsp), nread);
4292 ret = SMB_VFS_NEXT_UNLINK(handle, fsp->fsp_name);
4294 DBG_ERR("Removing [%s] failed\n", fsp_str_dbg(fsp));
4302 static ssize_t fruit_pread_meta_adouble(vfs_handle_struct *handle,
4303 files_struct *fsp, void *data,
4304 size_t n, off_t offset)
4307 struct adouble *ad = NULL;
4308 char afpinfo_buf[AFP_INFO_SIZE];
4312 ai = afpinfo_new(talloc_tos());
4317 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
4323 p = ad_get_entry(ad, ADEID_FINDERI);
4325 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
4330 memcpy(&ai->afpi_FinderInfo[0], p, ADEDLEN_FINDERI);
4332 nread = afpinfo_pack(ai, afpinfo_buf);
4333 if (nread != AFP_INFO_SIZE) {
4338 memcpy(data, afpinfo_buf, n);
4346 static ssize_t fruit_pread_meta(vfs_handle_struct *handle,
4347 files_struct *fsp, void *data,
4348 size_t n, off_t offset)
4350 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4355 * OS X has a off-by-1 error in the offset calculation, so we're
4356 * bug compatible here. It won't hurt, as any relevant real
4357 * world read requests from the AFP_AfpInfo stream will be
4358 * offset=0 n=60. offset is ignored anyway, see below.
4360 if ((offset < 0) || (offset >= AFP_INFO_SIZE + 1)) {
4365 DBG_ERR("Failed to fetch fsp extension");
4369 /* Yes, macOS always reads from offset 0 */
4371 to_return = MIN(n, AFP_INFO_SIZE);
4373 switch (fio->config->meta) {
4374 case FRUIT_META_STREAM:
4375 nread = fruit_pread_meta_stream(handle, fsp, data,
4379 case FRUIT_META_NETATALK:
4380 nread = fruit_pread_meta_adouble(handle, fsp, data,
4385 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
4389 if (nread == -1 && fio->created) {
4391 char afpinfo_buf[AFP_INFO_SIZE];
4393 ai = afpinfo_new(talloc_tos());
4398 nread = afpinfo_pack(ai, afpinfo_buf);
4400 if (nread != AFP_INFO_SIZE) {
4404 memcpy(data, afpinfo_buf, to_return);
4411 static ssize_t fruit_pread_rsrc_stream(vfs_handle_struct *handle,
4412 files_struct *fsp, void *data,
4413 size_t n, off_t offset)
4415 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4418 static ssize_t fruit_pread_rsrc_xattr(vfs_handle_struct *handle,
4419 files_struct *fsp, void *data,
4420 size_t n, off_t offset)
4422 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4425 static ssize_t fruit_pread_rsrc_adouble(vfs_handle_struct *handle,
4426 files_struct *fsp, void *data,
4427 size_t n, off_t offset)
4429 struct adouble *ad = NULL;
4432 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
4437 nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n,
4438 offset + ad_getentryoff(ad, ADEID_RFORK));
4444 static ssize_t fruit_pread_rsrc(vfs_handle_struct *handle,
4445 files_struct *fsp, void *data,
4446 size_t n, off_t offset)
4448 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4456 switch (fio->config->rsrc) {
4457 case FRUIT_RSRC_STREAM:
4458 nread = fruit_pread_rsrc_stream(handle, fsp, data, n, offset);
4461 case FRUIT_RSRC_ADFILE:
4462 nread = fruit_pread_rsrc_adouble(handle, fsp, data, n, offset);
4465 case FRUIT_RSRC_XATTR:
4466 nread = fruit_pread_rsrc_xattr(handle, fsp, data, n, offset);
4470 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
4477 static ssize_t fruit_pread(vfs_handle_struct *handle,
4478 files_struct *fsp, void *data,
4479 size_t n, off_t offset)
4481 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4484 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4485 fsp_str_dbg(fsp), (intmax_t)offset, n);
4488 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
4491 if (fio->type == ADOUBLE_META) {
4492 nread = fruit_pread_meta(handle, fsp, data, n, offset);
4494 nread = fruit_pread_rsrc(handle, fsp, data, n, offset);
4497 DBG_DEBUG("Path [%s] nread [%zd]\n", fsp_str_dbg(fsp), nread);
4501 static bool fruit_must_handle_aio_stream(struct fio *fio)
4507 if (fio->type == ADOUBLE_META) {
4511 if ((fio->type == ADOUBLE_RSRC) &&
4512 (fio->config->rsrc == FRUIT_RSRC_ADFILE))
4520 struct fruit_pread_state {
4522 struct vfs_aio_state vfs_aio_state;
4525 static void fruit_pread_done(struct tevent_req *subreq);
4527 static struct tevent_req *fruit_pread_send(
4528 struct vfs_handle_struct *handle,
4529 TALLOC_CTX *mem_ctx,
4530 struct tevent_context *ev,
4531 struct files_struct *fsp,
4533 size_t n, off_t offset)
4535 struct tevent_req *req = NULL;
4536 struct tevent_req *subreq = NULL;
4537 struct fruit_pread_state *state = NULL;
4538 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4540 req = tevent_req_create(mem_ctx, &state,
4541 struct fruit_pread_state);
4546 if (fruit_must_handle_aio_stream(fio)) {
4547 state->nread = SMB_VFS_PREAD(fsp, data, n, offset);
4548 if (state->nread != n) {
4549 if (state->nread != -1) {
4552 tevent_req_error(req, errno);
4553 return tevent_req_post(req, ev);
4555 tevent_req_done(req);
4556 return tevent_req_post(req, ev);
4559 subreq = SMB_VFS_NEXT_PREAD_SEND(state, ev, handle, fsp,
4561 if (tevent_req_nomem(req, subreq)) {
4562 return tevent_req_post(req, ev);
4564 tevent_req_set_callback(subreq, fruit_pread_done, req);
4568 static void fruit_pread_done(struct tevent_req *subreq)
4570 struct tevent_req *req = tevent_req_callback_data(
4571 subreq, struct tevent_req);
4572 struct fruit_pread_state *state = tevent_req_data(
4573 req, struct fruit_pread_state);
4575 state->nread = SMB_VFS_PREAD_RECV(subreq, &state->vfs_aio_state);
4576 TALLOC_FREE(subreq);
4578 if (tevent_req_error(req, state->vfs_aio_state.error)) {
4581 tevent_req_done(req);
4584 static ssize_t fruit_pread_recv(struct tevent_req *req,
4585 struct vfs_aio_state *vfs_aio_state)
4587 struct fruit_pread_state *state = tevent_req_data(
4588 req, struct fruit_pread_state);
4590 if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
4594 *vfs_aio_state = state->vfs_aio_state;
4595 return state->nread;
4598 static ssize_t fruit_pwrite_meta_stream(vfs_handle_struct *handle,
4599 files_struct *fsp, const void *data,
4600 size_t n, off_t offset)
4602 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4608 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4609 fsp_str_dbg(fsp), (intmax_t)offset, n);
4618 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
4620 DBG_ERR("Close [%s] failed: %s\n",
4621 fsp_str_dbg(fsp), strerror(errno));
4626 fd = SMB_VFS_NEXT_OPEN(handle,
4632 DBG_ERR("On-demand create [%s] in write failed: %s\n",
4633 fsp_str_dbg(fsp), strerror(errno));
4637 fio->fake_fd = false;
4640 ai = afpinfo_unpack(talloc_tos(), data);
4645 if (ai_empty_finderinfo(ai)) {
4647 * Writing an all 0 blob to the metadata stream results in the
4648 * stream being removed on a macOS server. This ensures we
4649 * behave the same and it verified by the "delete AFP_AfpInfo by
4650 * writing all 0" test.
4652 ret = SMB_VFS_NEXT_FTRUNCATE(handle, fsp, 0);
4654 DBG_ERR("SMB_VFS_NEXT_FTRUNCATE on [%s] failed\n",
4659 ok = set_delete_on_close(
4662 handle->conn->session_info->security_token,
4663 handle->conn->session_info->unix_token);
4665 DBG_ERR("set_delete_on_close on [%s] failed\n",
4672 nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4673 if (nwritten != n) {
4680 static ssize_t fruit_pwrite_meta_netatalk(vfs_handle_struct *handle,
4681 files_struct *fsp, const void *data,
4682 size_t n, off_t offset)
4684 struct adouble *ad = NULL;
4690 ai = afpinfo_unpack(talloc_tos(), data);
4695 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
4697 ad = ad_init(talloc_tos(), ADOUBLE_META);
4702 p = ad_get_entry(ad, ADEID_FINDERI);
4704 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
4709 memcpy(p, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
4711 ret = ad_fset(handle, ad, fsp);
4713 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
4720 if (!ai_empty_finderinfo(ai)) {
4725 * Writing an all 0 blob to the metadata stream results in the stream
4726 * being removed on a macOS server. This ensures we behave the same and
4727 * it verified by the "delete AFP_AfpInfo by writing all 0" test.
4730 ok = set_delete_on_close(
4733 handle->conn->session_info->security_token,
4734 handle->conn->session_info->unix_token);
4736 DBG_ERR("set_delete_on_close on [%s] failed\n",
4744 static ssize_t fruit_pwrite_meta(vfs_handle_struct *handle,
4745 files_struct *fsp, const void *data,
4746 size_t n, off_t offset)
4748 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4750 uint8_t buf[AFP_INFO_SIZE];
4756 DBG_ERR("Failed to fetch fsp extension");
4765 if (offset != 0 && n < 60) {
4770 cmp = memcmp(data, "AFP", 3);
4776 if (n <= AFP_OFF_FinderInfo) {
4778 * Nothing to do here really, just return
4786 if (to_copy > AFP_INFO_SIZE) {
4787 to_copy = AFP_INFO_SIZE;
4789 memcpy(buf, data, to_copy);
4792 if (to_write != AFP_INFO_SIZE) {
4793 to_write = AFP_INFO_SIZE;
4796 switch (fio->config->meta) {
4797 case FRUIT_META_STREAM:
4798 nwritten = fruit_pwrite_meta_stream(handle,
4805 case FRUIT_META_NETATALK:
4806 nwritten = fruit_pwrite_meta_netatalk(handle,
4814 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
4818 if (nwritten != to_write) {
4823 * Return the requested amount, verified against macOS SMB server
4828 static ssize_t fruit_pwrite_rsrc_stream(vfs_handle_struct *handle,
4829 files_struct *fsp, const void *data,
4830 size_t n, off_t offset)
4832 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4835 static ssize_t fruit_pwrite_rsrc_xattr(vfs_handle_struct *handle,
4836 files_struct *fsp, const void *data,
4837 size_t n, off_t offset)
4839 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4842 static ssize_t fruit_pwrite_rsrc_adouble(vfs_handle_struct *handle,
4843 files_struct *fsp, const void *data,
4844 size_t n, off_t offset)
4846 struct adouble *ad = NULL;
4850 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
4852 DBG_ERR("ad_get [%s] failed\n", fsp_str_dbg(fsp));
4856 nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n,
4857 offset + ad_getentryoff(ad, ADEID_RFORK));
4858 if (nwritten != n) {
4859 DBG_ERR("Short write on [%s] [%zd/%zd]\n",
4860 fsp_str_dbg(fsp), nwritten, n);
4865 if ((n + offset) > ad_getentrylen(ad, ADEID_RFORK)) {
4866 ad_setentrylen(ad, ADEID_RFORK, n + offset);
4867 ret = ad_fset(handle, ad, fsp);
4869 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
4879 static ssize_t fruit_pwrite_rsrc(vfs_handle_struct *handle,
4880 files_struct *fsp, const void *data,
4881 size_t n, off_t offset)
4883 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4887 DBG_ERR("Failed to fetch fsp extension");
4891 switch (fio->config->rsrc) {
4892 case FRUIT_RSRC_STREAM:
4893 nwritten = fruit_pwrite_rsrc_stream(handle, fsp, data, n, offset);
4896 case FRUIT_RSRC_ADFILE:
4897 nwritten = fruit_pwrite_rsrc_adouble(handle, fsp, data, n, offset);
4900 case FRUIT_RSRC_XATTR:
4901 nwritten = fruit_pwrite_rsrc_xattr(handle, fsp, data, n, offset);
4905 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
4912 static ssize_t fruit_pwrite(vfs_handle_struct *handle,
4913 files_struct *fsp, const void *data,
4914 size_t n, off_t offset)
4916 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4919 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
4920 fsp_str_dbg(fsp), (intmax_t)offset, n);
4923 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
4926 if (fio->type == ADOUBLE_META) {
4927 nwritten = fruit_pwrite_meta(handle, fsp, data, n, offset);
4929 nwritten = fruit_pwrite_rsrc(handle, fsp, data, n, offset);
4932 DBG_DEBUG("Path [%s] nwritten=%zd\n", fsp_str_dbg(fsp), nwritten);
4936 struct fruit_pwrite_state {
4938 struct vfs_aio_state vfs_aio_state;
4941 static void fruit_pwrite_done(struct tevent_req *subreq);
4943 static struct tevent_req *fruit_pwrite_send(
4944 struct vfs_handle_struct *handle,
4945 TALLOC_CTX *mem_ctx,
4946 struct tevent_context *ev,
4947 struct files_struct *fsp,
4949 size_t n, off_t offset)
4951 struct tevent_req *req = NULL;
4952 struct tevent_req *subreq = NULL;
4953 struct fruit_pwrite_state *state = NULL;
4954 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
4956 req = tevent_req_create(mem_ctx, &state,
4957 struct fruit_pwrite_state);
4962 if (fruit_must_handle_aio_stream(fio)) {
4963 state->nwritten = SMB_VFS_PWRITE(fsp, data, n, offset);
4964 if (state->nwritten != n) {
4965 if (state->nwritten != -1) {
4968 tevent_req_error(req, errno);
4969 return tevent_req_post(req, ev);
4971 tevent_req_done(req);
4972 return tevent_req_post(req, ev);
4975 subreq = SMB_VFS_NEXT_PWRITE_SEND(state, ev, handle, fsp,
4977 if (tevent_req_nomem(req, subreq)) {
4978 return tevent_req_post(req, ev);
4980 tevent_req_set_callback(subreq, fruit_pwrite_done, req);
4984 static void fruit_pwrite_done(struct tevent_req *subreq)
4986 struct tevent_req *req = tevent_req_callback_data(
4987 subreq, struct tevent_req);
4988 struct fruit_pwrite_state *state = tevent_req_data(
4989 req, struct fruit_pwrite_state);
4991 state->nwritten = SMB_VFS_PWRITE_RECV(subreq, &state->vfs_aio_state);
4992 TALLOC_FREE(subreq);
4994 if (tevent_req_error(req, state->vfs_aio_state.error)) {
4997 tevent_req_done(req);
5000 static ssize_t fruit_pwrite_recv(struct tevent_req *req,
5001 struct vfs_aio_state *vfs_aio_state)
5003 struct fruit_pwrite_state *state = tevent_req_data(
5004 req, struct fruit_pwrite_state);
5006 if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
5010 *vfs_aio_state = state->vfs_aio_state;
5011 return state->nwritten;
5015 * Helper to stat/lstat the base file of an smb_fname.
5017 static int fruit_stat_base(vfs_handle_struct *handle,
5018 struct smb_filename *smb_fname,
5021 char *tmp_stream_name;
5024 tmp_stream_name = smb_fname->stream_name;
5025 smb_fname->stream_name = NULL;
5027 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
5029 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5031 smb_fname->stream_name = tmp_stream_name;
5033 DBG_DEBUG("fruit_stat_base [%s] dev [%ju] ino [%ju]\n",
5034 smb_fname->base_name,
5035 (uintmax_t)smb_fname->st.st_ex_dev,
5036 (uintmax_t)smb_fname->st.st_ex_ino);
5040 static int fruit_stat_meta_stream(vfs_handle_struct *handle,
5041 struct smb_filename *smb_fname,
5047 ret = fruit_stat_base(handle, smb_fname, false);
5052 ino = fruit_inode(&smb_fname->st, smb_fname->stream_name);
5055 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
5057 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5060 smb_fname->st.st_ex_ino = ino;
5065 static int fruit_stat_meta_netatalk(vfs_handle_struct *handle,
5066 struct smb_filename *smb_fname,
5069 struct adouble *ad = NULL;
5071 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5073 DBG_INFO("fruit_stat_meta %s: %s\n",
5074 smb_fname_str_dbg(smb_fname), strerror(errno));
5080 /* Populate the stat struct with info from the base file. */
5081 if (fruit_stat_base(handle, smb_fname, follow_links) == -1) {
5084 smb_fname->st.st_ex_size = AFP_INFO_SIZE;
5085 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5086 smb_fname->stream_name);
5090 static int fruit_stat_meta(vfs_handle_struct *handle,
5091 struct smb_filename *smb_fname,
5094 struct fruit_config_data *config = NULL;
5097 SMB_VFS_HANDLE_GET_DATA(handle, config,
5098 struct fruit_config_data, return -1);
5100 switch (config->meta) {
5101 case FRUIT_META_STREAM:
5102 ret = fruit_stat_meta_stream(handle, smb_fname, follow_links);
5105 case FRUIT_META_NETATALK:
5106 ret = fruit_stat_meta_netatalk(handle, smb_fname, follow_links);
5110 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
5117 static int fruit_stat_rsrc_netatalk(vfs_handle_struct *handle,
5118 struct smb_filename *smb_fname,
5121 struct adouble *ad = NULL;
5124 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
5130 /* Populate the stat struct with info from the base file. */
5131 ret = fruit_stat_base(handle, smb_fname, follow_links);
5137 smb_fname->st.st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
5138 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5139 smb_fname->stream_name);
5144 static int fruit_stat_rsrc_stream(vfs_handle_struct *handle,
5145 struct smb_filename *smb_fname,
5151 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
5153 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5159 static int fruit_stat_rsrc_xattr(vfs_handle_struct *handle,
5160 struct smb_filename *smb_fname,
5163 #ifdef HAVE_ATTROPEN
5167 /* Populate the stat struct with info from the base file. */
5168 ret = fruit_stat_base(handle, smb_fname, follow_links);
5173 fd = attropen(smb_fname->base_name,
5174 AFPRESOURCE_EA_NETATALK,
5180 ret = sys_fstat(fd, &smb_fname->st, false);
5183 DBG_ERR("fstat [%s:%s] failed\n", smb_fname->base_name,
5184 AFPRESOURCE_EA_NETATALK);
5190 smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
5191 smb_fname->stream_name);
5201 static int fruit_stat_rsrc(vfs_handle_struct *handle,
5202 struct smb_filename *smb_fname,
5205 struct fruit_config_data *config = NULL;
5208 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
5210 SMB_VFS_HANDLE_GET_DATA(handle, config,
5211 struct fruit_config_data, return -1);
5213 switch (config->rsrc) {
5214 case FRUIT_RSRC_STREAM:
5215 ret = fruit_stat_rsrc_stream(handle, smb_fname, follow_links);
5218 case FRUIT_RSRC_XATTR:
5219 ret = fruit_stat_rsrc_xattr(handle, smb_fname, follow_links);
5222 case FRUIT_RSRC_ADFILE:
5223 ret = fruit_stat_rsrc_netatalk(handle, smb_fname, follow_links);
5227 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
5234 static int fruit_stat(vfs_handle_struct *handle,
5235 struct smb_filename *smb_fname)
5239 DEBUG(10, ("fruit_stat called for %s\n",
5240 smb_fname_str_dbg(smb_fname)));
5242 if (!is_ntfs_stream_smb_fname(smb_fname)
5243 || is_ntfs_default_stream_smb_fname(smb_fname)) {
5244 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
5246 update_btime(handle, smb_fname);
5252 * Note if lp_posix_paths() is true, we can never
5253 * get here as is_ntfs_stream_smb_fname() is
5254 * always false. So we never need worry about
5255 * not following links here.
5258 if (is_afpinfo_stream(smb_fname)) {
5259 rc = fruit_stat_meta(handle, smb_fname, true);
5260 } else if (is_afpresource_stream(smb_fname)) {
5261 rc = fruit_stat_rsrc(handle, smb_fname, true);
5263 return SMB_VFS_NEXT_STAT(handle, smb_fname);
5267 update_btime(handle, smb_fname);
5268 smb_fname->st.st_ex_mode &= ~S_IFMT;
5269 smb_fname->st.st_ex_mode |= S_IFREG;
5270 smb_fname->st.st_ex_blocks =
5271 smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
5276 static int fruit_lstat(vfs_handle_struct *handle,
5277 struct smb_filename *smb_fname)
5281 DEBUG(10, ("fruit_lstat called for %s\n",
5282 smb_fname_str_dbg(smb_fname)));
5284 if (!is_ntfs_stream_smb_fname(smb_fname)
5285 || is_ntfs_default_stream_smb_fname(smb_fname)) {
5286 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5288 update_btime(handle, smb_fname);
5293 if (is_afpinfo_stream(smb_fname)) {
5294 rc = fruit_stat_meta(handle, smb_fname, false);
5295 } else if (is_afpresource_stream(smb_fname)) {
5296 rc = fruit_stat_rsrc(handle, smb_fname, false);
5298 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5302 update_btime(handle, smb_fname);
5303 smb_fname->st.st_ex_mode &= ~S_IFMT;
5304 smb_fname->st.st_ex_mode |= S_IFREG;
5305 smb_fname->st.st_ex_blocks =
5306 smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
5311 static int fruit_fstat_meta_stream(vfs_handle_struct *handle,
5313 SMB_STRUCT_STAT *sbuf)
5315 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5316 struct smb_filename smb_fname;
5325 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5330 *sbuf = fsp->base_fsp->fsp_name->st;
5331 sbuf->st_ex_size = AFP_INFO_SIZE;
5332 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5336 smb_fname = (struct smb_filename) {
5337 .base_name = fsp->fsp_name->base_name,
5340 ret = fruit_stat_base(handle, &smb_fname, false);
5344 *sbuf = smb_fname.st;
5346 ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5348 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5353 sbuf->st_ex_ino = ino;
5357 static int fruit_fstat_meta_netatalk(vfs_handle_struct *handle,
5359 SMB_STRUCT_STAT *sbuf)
5363 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5368 *sbuf = fsp->base_fsp->fsp_name->st;
5369 sbuf->st_ex_size = AFP_INFO_SIZE;
5370 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5375 static int fruit_fstat_meta(vfs_handle_struct *handle,
5377 SMB_STRUCT_STAT *sbuf,
5382 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
5384 switch (fio->config->meta) {
5385 case FRUIT_META_STREAM:
5386 ret = fruit_fstat_meta_stream(handle, fsp, sbuf);
5389 case FRUIT_META_NETATALK:
5390 ret = fruit_fstat_meta_netatalk(handle, fsp, sbuf);
5394 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
5398 DBG_DEBUG("Path [%s] ret [%d]\n", fsp_str_dbg(fsp), ret);
5402 static int fruit_fstat_rsrc_xattr(vfs_handle_struct *handle,
5404 SMB_STRUCT_STAT *sbuf)
5406 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5409 static int fruit_fstat_rsrc_stream(vfs_handle_struct *handle,
5411 SMB_STRUCT_STAT *sbuf)
5413 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5416 static int fruit_fstat_rsrc_adouble(vfs_handle_struct *handle,
5418 SMB_STRUCT_STAT *sbuf)
5420 struct adouble *ad = NULL;
5423 /* Populate the stat struct with info from the base file. */
5424 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
5429 ad = ad_get(talloc_tos(), handle,
5430 fsp->base_fsp->fsp_name,
5433 DBG_ERR("ad_get [%s] failed [%s]\n",
5434 fsp_str_dbg(fsp), strerror(errno));
5438 *sbuf = fsp->base_fsp->fsp_name->st;
5439 sbuf->st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
5440 sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
5446 static int fruit_fstat_rsrc(vfs_handle_struct *handle, files_struct *fsp,
5447 SMB_STRUCT_STAT *sbuf, struct fio *fio)
5451 switch (fio->config->rsrc) {
5452 case FRUIT_RSRC_STREAM:
5453 ret = fruit_fstat_rsrc_stream(handle, fsp, sbuf);
5456 case FRUIT_RSRC_ADFILE:
5457 ret = fruit_fstat_rsrc_adouble(handle, fsp, sbuf);
5460 case FRUIT_RSRC_XATTR:
5461 ret = fruit_fstat_rsrc_xattr(handle, fsp, sbuf);
5465 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
5472 static int fruit_fstat(vfs_handle_struct *handle, files_struct *fsp,
5473 SMB_STRUCT_STAT *sbuf)
5475 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5479 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
5482 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
5484 if (fio->type == ADOUBLE_META) {
5485 rc = fruit_fstat_meta(handle, fsp, sbuf, fio);
5487 rc = fruit_fstat_rsrc(handle, fsp, sbuf, fio);
5491 sbuf->st_ex_mode &= ~S_IFMT;
5492 sbuf->st_ex_mode |= S_IFREG;
5493 sbuf->st_ex_blocks = sbuf->st_ex_size / STAT_ST_BLOCKSIZE + 1;
5496 DBG_DEBUG("Path [%s] rc [%d] size [%"PRIdMAX"]\n",
5497 fsp_str_dbg(fsp), rc, (intmax_t)sbuf->st_ex_size);
5501 static NTSTATUS delete_invalid_meta_stream(
5502 vfs_handle_struct *handle,
5503 const struct smb_filename *smb_fname,
5504 TALLOC_CTX *mem_ctx,
5505 unsigned int *pnum_streams,
5506 struct stream_struct **pstreams,
5509 struct smb_filename *sname = NULL;
5513 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams, AFPINFO_STREAM);
5515 return NT_STATUS_INTERNAL_ERROR;
5519 return NT_STATUS_OK;
5522 sname = synthetic_smb_fname(talloc_tos(),
5523 smb_fname->base_name,
5524 AFPINFO_STREAM_NAME,
5526 if (sname == NULL) {
5527 return NT_STATUS_NO_MEMORY;
5530 ret = SMB_VFS_NEXT_UNLINK(handle, sname);
5533 DBG_ERR("Removing [%s] failed\n", smb_fname_str_dbg(sname));
5534 return map_nt_error_from_unix(errno);
5537 return NT_STATUS_OK;
5540 static NTSTATUS fruit_streaminfo_meta_stream(
5541 vfs_handle_struct *handle,
5542 struct files_struct *fsp,
5543 const struct smb_filename *smb_fname,
5544 TALLOC_CTX *mem_ctx,
5545 unsigned int *pnum_streams,
5546 struct stream_struct **pstreams)
5548 struct stream_struct *stream = *pstreams;
5549 unsigned int num_streams = *pnum_streams;
5552 for (i = 0; i < num_streams; i++) {
5553 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
5558 if (i == num_streams) {
5559 return NT_STATUS_OK;
5562 if (stream[i].size != AFP_INFO_SIZE) {
5563 DBG_ERR("Removing invalid AFPINFO_STREAM size [%jd] from [%s]\n",
5564 (intmax_t)stream[i].size, smb_fname_str_dbg(smb_fname));
5566 return delete_invalid_meta_stream(handle,
5575 return NT_STATUS_OK;
5578 static NTSTATUS fruit_streaminfo_meta_netatalk(
5579 vfs_handle_struct *handle,
5580 struct files_struct *fsp,
5581 const struct smb_filename *smb_fname,
5582 TALLOC_CTX *mem_ctx,
5583 unsigned int *pnum_streams,
5584 struct stream_struct **pstreams)
5586 struct stream_struct *stream = *pstreams;
5587 unsigned int num_streams = *pnum_streams;
5588 struct adouble *ad = NULL;
5593 /* Remove the Netatalk xattr from the list */
5594 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5595 ":" NETATALK_META_XATTR ":$DATA");
5597 return NT_STATUS_NO_MEMORY;
5601 * Check if there's a AFPINFO_STREAM from the VFS streams
5602 * backend and if yes, remove it from the list
5604 for (i = 0; i < num_streams; i++) {
5605 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
5610 if (i < num_streams) {
5611 DBG_WARNING("Unexpected AFPINFO_STREAM on [%s]\n",
5612 smb_fname_str_dbg(smb_fname));
5614 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5617 return NT_STATUS_INTERNAL_ERROR;
5621 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5623 return NT_STATUS_OK;
5626 is_fi_empty = ad_empty_finderinfo(ad);
5630 return NT_STATUS_OK;
5633 ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
5634 AFPINFO_STREAM_NAME, AFP_INFO_SIZE,
5635 smb_roundup(handle->conn, AFP_INFO_SIZE));
5637 return NT_STATUS_NO_MEMORY;
5640 return NT_STATUS_OK;
5643 static NTSTATUS fruit_streaminfo_meta(vfs_handle_struct *handle,
5644 struct files_struct *fsp,
5645 const struct smb_filename *smb_fname,
5646 TALLOC_CTX *mem_ctx,
5647 unsigned int *pnum_streams,
5648 struct stream_struct **pstreams)
5650 struct fruit_config_data *config = NULL;
5653 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5654 return NT_STATUS_INTERNAL_ERROR);
5656 switch (config->meta) {
5657 case FRUIT_META_NETATALK:
5658 status = fruit_streaminfo_meta_netatalk(handle, fsp, smb_fname,
5659 mem_ctx, pnum_streams,
5663 case FRUIT_META_STREAM:
5664 status = fruit_streaminfo_meta_stream(handle, fsp, smb_fname,
5665 mem_ctx, pnum_streams,
5670 return NT_STATUS_INTERNAL_ERROR;
5676 static NTSTATUS fruit_streaminfo_rsrc_stream(
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)
5686 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
5688 DBG_ERR("Filtering resource stream failed\n");
5689 return NT_STATUS_INTERNAL_ERROR;
5691 return NT_STATUS_OK;
5694 static NTSTATUS fruit_streaminfo_rsrc_xattr(
5695 vfs_handle_struct *handle,
5696 struct files_struct *fsp,
5697 const struct smb_filename *smb_fname,
5698 TALLOC_CTX *mem_ctx,
5699 unsigned int *pnum_streams,
5700 struct stream_struct **pstreams)
5704 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
5706 DBG_ERR("Filtering resource stream failed\n");
5707 return NT_STATUS_INTERNAL_ERROR;
5709 return NT_STATUS_OK;
5712 static NTSTATUS fruit_streaminfo_rsrc_adouble(
5713 vfs_handle_struct *handle,
5714 struct files_struct *fsp,
5715 const struct smb_filename *smb_fname,
5716 TALLOC_CTX *mem_ctx,
5717 unsigned int *pnum_streams,
5718 struct stream_struct **pstreams)
5720 struct stream_struct *stream = *pstreams;
5721 unsigned int num_streams = *pnum_streams;
5722 struct adouble *ad = NULL;
5728 * Check if there's a AFPRESOURCE_STREAM from the VFS streams backend
5729 * and if yes, remove it from the list
5731 for (i = 0; i < num_streams; i++) {
5732 if (strequal_m(stream[i].name, AFPRESOURCE_STREAM)) {
5737 if (i < num_streams) {
5738 DBG_WARNING("Unexpected AFPRESOURCE_STREAM on [%s]\n",
5739 smb_fname_str_dbg(smb_fname));
5741 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
5742 AFPRESOURCE_STREAM);
5744 return NT_STATUS_INTERNAL_ERROR;
5748 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
5750 return NT_STATUS_OK;
5753 rlen = ad_getentrylen(ad, ADEID_RFORK);
5757 return NT_STATUS_OK;
5760 ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
5761 AFPRESOURCE_STREAM_NAME, rlen,
5762 smb_roundup(handle->conn, rlen));
5764 return NT_STATUS_NO_MEMORY;
5767 return NT_STATUS_OK;
5770 static NTSTATUS fruit_streaminfo_rsrc(vfs_handle_struct *handle,
5771 struct files_struct *fsp,
5772 const struct smb_filename *smb_fname,
5773 TALLOC_CTX *mem_ctx,
5774 unsigned int *pnum_streams,
5775 struct stream_struct **pstreams)
5777 struct fruit_config_data *config = NULL;
5780 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5781 return NT_STATUS_INTERNAL_ERROR);
5783 switch (config->rsrc) {
5784 case FRUIT_RSRC_STREAM:
5785 status = fruit_streaminfo_rsrc_stream(handle, fsp, smb_fname,
5786 mem_ctx, pnum_streams,
5790 case FRUIT_RSRC_XATTR:
5791 status = fruit_streaminfo_rsrc_xattr(handle, fsp, smb_fname,
5792 mem_ctx, pnum_streams,
5796 case FRUIT_RSRC_ADFILE:
5797 status = fruit_streaminfo_rsrc_adouble(handle, fsp, smb_fname,
5798 mem_ctx, pnum_streams,
5803 return NT_STATUS_INTERNAL_ERROR;
5809 static void fruit_filter_empty_streams(unsigned int *pnum_streams,
5810 struct stream_struct **pstreams)
5812 unsigned num_streams = *pnum_streams;
5813 struct stream_struct *streams = *pstreams;
5816 if (!global_fruit_config.nego_aapl) {
5820 while (i < num_streams) {
5821 struct smb_filename smb_fname = (struct smb_filename) {
5822 .stream_name = streams[i].name,
5825 if (is_ntfs_default_stream_smb_fname(&smb_fname)
5826 || streams[i].size > 0)
5832 streams[i] = streams[num_streams - 1];
5836 *pnum_streams = num_streams;
5839 static NTSTATUS fruit_streaminfo(vfs_handle_struct *handle,
5840 struct files_struct *fsp,
5841 const struct smb_filename *smb_fname,
5842 TALLOC_CTX *mem_ctx,
5843 unsigned int *pnum_streams,
5844 struct stream_struct **pstreams)
5846 struct fruit_config_data *config = NULL;
5849 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5850 return NT_STATUS_UNSUCCESSFUL);
5852 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
5854 status = SMB_VFS_NEXT_STREAMINFO(handle, fsp, smb_fname, mem_ctx,
5855 pnum_streams, pstreams);
5856 if (!NT_STATUS_IS_OK(status)) {
5860 fruit_filter_empty_streams(pnum_streams, pstreams);
5862 status = fruit_streaminfo_meta(handle, fsp, smb_fname,
5863 mem_ctx, pnum_streams, pstreams);
5864 if (!NT_STATUS_IS_OK(status)) {
5868 status = fruit_streaminfo_rsrc(handle, fsp, smb_fname,
5869 mem_ctx, pnum_streams, pstreams);
5870 if (!NT_STATUS_IS_OK(status)) {
5874 return NT_STATUS_OK;
5877 static int fruit_ntimes(vfs_handle_struct *handle,
5878 const struct smb_filename *smb_fname,
5879 struct smb_file_time *ft)
5882 struct adouble *ad = NULL;
5883 struct fruit_config_data *config = NULL;
5885 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
5888 if ((config->meta != FRUIT_META_NETATALK) ||
5889 null_timespec(ft->create_time))
5891 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
5894 DEBUG(10,("set btime for %s to %s\n", smb_fname_str_dbg(smb_fname),
5895 time_to_asc(convert_timespec_to_time_t(ft->create_time))));
5897 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
5902 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX,
5903 convert_time_t_to_uint32_t(ft->create_time.tv_sec));
5905 rc = ad_set(handle, ad, smb_fname);
5911 DEBUG(1, ("fruit_ntimes: %s\n", smb_fname_str_dbg(smb_fname)));
5914 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
5917 static int fruit_fallocate(struct vfs_handle_struct *handle,
5918 struct files_struct *fsp,
5923 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5926 return SMB_VFS_NEXT_FALLOCATE(handle, fsp, mode, offset, len);
5929 /* Let the pwrite code path handle it. */
5934 static int fruit_ftruncate_rsrc_xattr(struct vfs_handle_struct *handle,
5935 struct files_struct *fsp,
5938 #ifdef HAVE_ATTROPEN
5939 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
5944 static int fruit_ftruncate_rsrc_adouble(struct vfs_handle_struct *handle,
5945 struct files_struct *fsp,
5949 struct adouble *ad = NULL;
5952 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
5954 DBG_DEBUG("ad_get [%s] failed [%s]\n",
5955 fsp_str_dbg(fsp), strerror(errno));
5959 ad_off = ad_getentryoff(ad, ADEID_RFORK);
5961 rc = ftruncate(fsp->fh->fd, offset + ad_off);
5967 ad_setentrylen(ad, ADEID_RFORK, offset);
5969 rc = ad_fset(handle, ad, fsp);
5971 DBG_ERR("ad_fset [%s] failed [%s]\n",
5972 fsp_str_dbg(fsp), strerror(errno));
5981 static int fruit_ftruncate_rsrc_stream(struct vfs_handle_struct *handle,
5982 struct files_struct *fsp,
5985 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
5988 static int fruit_ftruncate_rsrc(struct vfs_handle_struct *handle,
5989 struct files_struct *fsp,
5992 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
5996 DBG_ERR("Failed to fetch fsp extension");
6000 switch (fio->config->rsrc) {
6001 case FRUIT_RSRC_XATTR:
6002 ret = fruit_ftruncate_rsrc_xattr(handle, fsp, offset);
6005 case FRUIT_RSRC_ADFILE:
6006 ret = fruit_ftruncate_rsrc_adouble(handle, fsp, offset);
6009 case FRUIT_RSRC_STREAM:
6010 ret = fruit_ftruncate_rsrc_stream(handle, fsp, offset);
6014 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
6022 static int fruit_ftruncate_meta(struct vfs_handle_struct *handle,
6023 struct files_struct *fsp,
6027 DBG_WARNING("ftruncate %s to %jd",
6028 fsp_str_dbg(fsp), (intmax_t)offset);
6029 /* OS X returns NT_STATUS_ALLOTTED_SPACE_EXCEEDED */
6034 /* OS X returns success but does nothing */
6035 DBG_INFO("ignoring ftruncate %s to %jd\n",
6036 fsp_str_dbg(fsp), (intmax_t)offset);
6040 static int fruit_ftruncate(struct vfs_handle_struct *handle,
6041 struct files_struct *fsp,
6044 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
6047 DBG_DEBUG("Path [%s] offset [%"PRIdMAX"]\n", fsp_str_dbg(fsp),
6051 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
6054 if (fio->type == ADOUBLE_META) {
6055 ret = fruit_ftruncate_meta(handle, fsp, offset);
6057 ret = fruit_ftruncate_rsrc(handle, fsp, offset);
6060 DBG_DEBUG("Path [%s] result [%d]\n", fsp_str_dbg(fsp), ret);
6064 static NTSTATUS fruit_create_file(vfs_handle_struct *handle,
6065 struct smb_request *req,
6066 uint16_t root_dir_fid,
6067 struct smb_filename *smb_fname,
6068 uint32_t access_mask,
6069 uint32_t share_access,
6070 uint32_t create_disposition,
6071 uint32_t create_options,
6072 uint32_t file_attributes,
6073 uint32_t oplock_request,
6074 struct smb2_lease *lease,
6075 uint64_t allocation_size,
6076 uint32_t private_flags,
6077 struct security_descriptor *sd,
6078 struct ea_list *ea_list,
6079 files_struct **result,
6081 const struct smb2_create_blobs *in_context_blobs,
6082 struct smb2_create_blobs *out_context_blobs)
6085 struct fruit_config_data *config = NULL;
6086 files_struct *fsp = NULL;
6087 struct fio *fio = NULL;
6088 bool internal_open = (oplock_request & INTERNAL_OPEN_ONLY);
6091 status = check_aapl(handle, req, in_context_blobs, out_context_blobs);
6092 if (!NT_STATUS_IS_OK(status)) {
6096 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
6097 return NT_STATUS_UNSUCCESSFUL);
6099 if (is_apple_stream(smb_fname) && !internal_open) {
6100 ret = ad_convert(handle, smb_fname);
6102 DBG_ERR("ad_convert() failed\n");
6103 return NT_STATUS_UNSUCCESSFUL;
6107 status = SMB_VFS_NEXT_CREATE_FILE(
6108 handle, req, root_dir_fid, smb_fname,
6109 access_mask, share_access,
6110 create_disposition, create_options,
6111 file_attributes, oplock_request,
6113 allocation_size, private_flags,
6114 sd, ea_list, result,
6115 pinfo, in_context_blobs, out_context_blobs);
6116 if (!NT_STATUS_IS_OK(status)) {
6122 if (global_fruit_config.nego_aapl) {
6123 if (config->posix_rename && fsp->is_directory) {
6125 * Enable POSIX directory rename behaviour
6127 fsp->posix_flags |= FSP_POSIX_FLAGS_RENAME;
6132 * If this is a plain open for existing files, opening an 0
6133 * byte size resource fork MUST fail with
6134 * NT_STATUS_OBJECT_NAME_NOT_FOUND.
6136 * Cf the vfs_fruit torture tests in test_rfork_create().
6138 if (global_fruit_config.nego_aapl &&
6139 create_disposition == FILE_OPEN &&
6140 smb_fname->st.st_ex_size == 0 &&
6141 is_ntfs_stream_smb_fname(smb_fname) &&
6142 !(is_ntfs_default_stream_smb_fname(smb_fname)))
6144 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
6148 fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
6149 if (fio != NULL && pinfo != NULL && *pinfo == FILE_WAS_CREATED) {
6150 fio->created = true;
6153 if (is_ntfs_stream_smb_fname(smb_fname)
6154 || fsp->is_directory) {
6158 if ((config->locking == FRUIT_LOCKING_NETATALK) &&
6161 status = fruit_check_access(
6165 if (!NT_STATUS_IS_OK(status)) {
6173 DEBUG(10, ("fruit_create_file: %s\n", nt_errstr(status)));
6176 close_file(req, fsp, ERROR_CLOSE);
6177 *result = fsp = NULL;
6183 static NTSTATUS fruit_readdir_attr(struct vfs_handle_struct *handle,
6184 const struct smb_filename *fname,
6185 TALLOC_CTX *mem_ctx,
6186 struct readdir_attr_data **pattr_data)
6188 struct fruit_config_data *config = NULL;
6189 struct readdir_attr_data *attr_data;
6193 SMB_VFS_HANDLE_GET_DATA(handle, config,
6194 struct fruit_config_data,
6195 return NT_STATUS_UNSUCCESSFUL);
6197 if (!global_fruit_config.nego_aapl) {
6198 return SMB_VFS_NEXT_READDIR_ATTR(handle, fname, mem_ctx, pattr_data);
6201 DEBUG(10, ("fruit_readdir_attr %s\n", fname->base_name));
6203 ret = ad_convert(handle, fname);
6205 DBG_ERR("ad_convert() failed\n");
6206 return NT_STATUS_UNSUCCESSFUL;
6209 *pattr_data = talloc_zero(mem_ctx, struct readdir_attr_data);
6210 if (*pattr_data == NULL) {
6211 return NT_STATUS_UNSUCCESSFUL;
6213 attr_data = *pattr_data;
6214 attr_data->type = RDATTR_AAPL;
6217 * Mac metadata: compressed FinderInfo, resource fork length
6220 status = readdir_attr_macmeta(handle, fname, attr_data);
6221 if (!NT_STATUS_IS_OK(status)) {
6223 * Error handling is tricky: if we return failure from
6224 * this function, the corresponding directory entry
6225 * will to be passed to the client, so we really just
6226 * want to error out on fatal errors.
6228 if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
6236 if (config->unix_info_enabled) {
6237 attr_data->attr_data.aapl.unix_mode = fname->st.st_ex_mode;
6243 if (!config->readdir_attr_max_access) {
6244 attr_data->attr_data.aapl.max_access = FILE_GENERIC_ALL;
6246 status = smbd_calculate_access_mask(
6250 SEC_FLAG_MAXIMUM_ALLOWED,
6251 &attr_data->attr_data.aapl.max_access);
6252 if (!NT_STATUS_IS_OK(status)) {
6257 return NT_STATUS_OK;
6260 DEBUG(1, ("fruit_readdir_attr %s, error: %s\n",
6261 fname->base_name, nt_errstr(status)));
6262 TALLOC_FREE(*pattr_data);
6266 static NTSTATUS fruit_fget_nt_acl(vfs_handle_struct *handle,
6268 uint32_t security_info,
6269 TALLOC_CTX *mem_ctx,
6270 struct security_descriptor **ppdesc)
6273 struct security_ace ace;
6275 struct fruit_config_data *config;
6277 SMB_VFS_HANDLE_GET_DATA(handle, config,
6278 struct fruit_config_data,
6279 return NT_STATUS_UNSUCCESSFUL);
6281 status = SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
6283 if (!NT_STATUS_IS_OK(status)) {
6288 * Add MS NFS style ACEs with uid, gid and mode
6290 if (!global_fruit_config.nego_aapl) {
6291 return NT_STATUS_OK;
6293 if (!config->unix_info_enabled) {
6294 return NT_STATUS_OK;
6297 /* First remove any existing ACE's with NFS style mode/uid/gid SIDs. */
6298 status = remove_virtual_nfs_aces(*ppdesc);
6299 if (!NT_STATUS_IS_OK(status)) {
6300 DBG_WARNING("failed to remove MS NFS style ACEs\n");
6304 /* MS NFS style mode */
6305 sid_compose(&sid, &global_sid_Unix_NFS_Mode, fsp->fsp_name->st.st_ex_mode);
6306 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6307 status = security_descriptor_dacl_add(*ppdesc, &ace);
6308 if (!NT_STATUS_IS_OK(status)) {
6309 DEBUG(1,("failed to add MS NFS style ACE\n"));
6313 /* MS NFS style uid */
6314 sid_compose(&sid, &global_sid_Unix_NFS_Users, fsp->fsp_name->st.st_ex_uid);
6315 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6316 status = security_descriptor_dacl_add(*ppdesc, &ace);
6317 if (!NT_STATUS_IS_OK(status)) {
6318 DEBUG(1,("failed to add MS NFS style ACE\n"));
6322 /* MS NFS style gid */
6323 sid_compose(&sid, &global_sid_Unix_NFS_Groups, fsp->fsp_name->st.st_ex_gid);
6324 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
6325 status = security_descriptor_dacl_add(*ppdesc, &ace);
6326 if (!NT_STATUS_IS_OK(status)) {
6327 DEBUG(1,("failed to add MS NFS style ACE\n"));
6331 return NT_STATUS_OK;
6334 static NTSTATUS fruit_fset_nt_acl(vfs_handle_struct *handle,
6336 uint32_t security_info_sent,
6337 const struct security_descriptor *orig_psd)
6341 mode_t ms_nfs_mode = 0;
6343 struct security_descriptor *psd = NULL;
6344 uint32_t orig_num_aces = 0;
6346 if (orig_psd->dacl != NULL) {
6347 orig_num_aces = orig_psd->dacl->num_aces;
6350 psd = security_descriptor_copy(talloc_tos(), orig_psd);
6352 return NT_STATUS_NO_MEMORY;
6355 DBG_DEBUG("fruit_fset_nt_acl: %s\n", fsp_str_dbg(fsp));
6357 status = check_ms_nfs(handle, fsp, psd, &ms_nfs_mode, &do_chmod);
6358 if (!NT_STATUS_IS_OK(status)) {
6359 DEBUG(1, ("fruit_fset_nt_acl: check_ms_nfs failed%s\n", fsp_str_dbg(fsp)));
6365 * If only ms_nfs ACE entries were sent, ensure we set the DACL
6366 * sent/present flags correctly now we've removed them.
6369 if (orig_num_aces != 0) {
6371 * Are there any ACE's left ?
6373 if (psd->dacl->num_aces == 0) {
6374 /* No - clear the DACL sent/present flags. */
6375 security_info_sent &= ~SECINFO_DACL;
6376 psd->type &= ~SEC_DESC_DACL_PRESENT;
6380 status = SMB_VFS_NEXT_FSET_NT_ACL(handle, fsp, security_info_sent, psd);
6381 if (!NT_STATUS_IS_OK(status)) {
6382 DEBUG(1, ("fruit_fset_nt_acl: SMB_VFS_NEXT_FSET_NT_ACL failed%s\n", fsp_str_dbg(fsp)));
6388 if (fsp->fh->fd != -1) {
6389 result = SMB_VFS_FCHMOD(fsp, ms_nfs_mode);
6391 result = SMB_VFS_CHMOD(fsp->conn,
6397 DEBUG(1, ("chmod: %s, result: %d, %04o error %s\n", fsp_str_dbg(fsp),
6398 result, (unsigned)ms_nfs_mode,
6400 status = map_nt_error_from_unix(errno);
6407 return NT_STATUS_OK;
6410 static struct vfs_offload_ctx *fruit_offload_ctx;
6412 struct fruit_offload_read_state {
6413 struct vfs_handle_struct *handle;
6414 struct tevent_context *ev;
6420 static void fruit_offload_read_done(struct tevent_req *subreq);
6422 static struct tevent_req *fruit_offload_read_send(
6423 TALLOC_CTX *mem_ctx,
6424 struct tevent_context *ev,
6425 struct vfs_handle_struct *handle,
6432 struct tevent_req *req = NULL;
6433 struct tevent_req *subreq = NULL;
6434 struct fruit_offload_read_state *state = NULL;
6436 req = tevent_req_create(mem_ctx, &state,
6437 struct fruit_offload_read_state);
6441 *state = (struct fruit_offload_read_state) {
6448 subreq = SMB_VFS_NEXT_OFFLOAD_READ_SEND(mem_ctx, ev, handle, fsp,
6449 fsctl, ttl, offset, to_copy);
6450 if (tevent_req_nomem(subreq, req)) {
6451 return tevent_req_post(req, ev);
6453 tevent_req_set_callback(subreq, fruit_offload_read_done, req);
6457 static void fruit_offload_read_done(struct tevent_req *subreq)
6459 struct tevent_req *req = tevent_req_callback_data(
6460 subreq, struct tevent_req);
6461 struct fruit_offload_read_state *state = tevent_req_data(
6462 req, struct fruit_offload_read_state);
6465 status = SMB_VFS_NEXT_OFFLOAD_READ_RECV(subreq,
6469 TALLOC_FREE(subreq);
6470 if (tevent_req_nterror(req, status)) {
6474 if (state->fsctl != FSCTL_SRV_REQUEST_RESUME_KEY) {
6475 tevent_req_done(req);
6479 status = vfs_offload_token_ctx_init(state->fsp->conn->sconn->client,
6480 &fruit_offload_ctx);
6481 if (tevent_req_nterror(req, status)) {
6485 status = vfs_offload_token_db_store_fsp(fruit_offload_ctx,
6488 if (tevent_req_nterror(req, status)) {
6492 tevent_req_done(req);
6496 static NTSTATUS fruit_offload_read_recv(struct tevent_req *req,
6497 struct vfs_handle_struct *handle,
6498 TALLOC_CTX *mem_ctx,
6501 struct fruit_offload_read_state *state = tevent_req_data(
6502 req, struct fruit_offload_read_state);
6505 if (tevent_req_is_nterror(req, &status)) {
6506 tevent_req_received(req);
6510 token->length = state->token.length;
6511 token->data = talloc_move(mem_ctx, &state->token.data);
6513 tevent_req_received(req);
6514 return NT_STATUS_OK;
6517 struct fruit_offload_write_state {
6518 struct vfs_handle_struct *handle;
6520 struct files_struct *src_fsp;
6521 struct files_struct *dst_fsp;
6525 static void fruit_offload_write_done(struct tevent_req *subreq);
6526 static struct tevent_req *fruit_offload_write_send(struct vfs_handle_struct *handle,
6527 TALLOC_CTX *mem_ctx,
6528 struct tevent_context *ev,
6531 off_t transfer_offset,
6532 struct files_struct *dest_fsp,
6536 struct tevent_req *req, *subreq;
6537 struct fruit_offload_write_state *state;
6539 struct fruit_config_data *config;
6540 off_t src_off = transfer_offset;
6541 files_struct *src_fsp = NULL;
6542 off_t to_copy = num;
6543 bool copyfile_enabled = false;
6545 DEBUG(10,("soff: %ju, doff: %ju, len: %ju\n",
6546 (uintmax_t)src_off, (uintmax_t)dest_off, (uintmax_t)num));
6548 SMB_VFS_HANDLE_GET_DATA(handle, config,
6549 struct fruit_config_data,
6552 req = tevent_req_create(mem_ctx, &state,
6553 struct fruit_offload_write_state);
6557 state->handle = handle;
6558 state->dst_fsp = dest_fsp;
6561 case FSCTL_SRV_COPYCHUNK:
6562 case FSCTL_SRV_COPYCHUNK_WRITE:
6563 copyfile_enabled = config->copyfile_enabled;
6570 * Check if this a OS X copyfile style copychunk request with
6571 * a requested chunk count of 0 that was translated to a
6572 * offload_write_send VFS call overloading the parameters src_off
6573 * = dest_off = num = 0.
6575 if (copyfile_enabled && num == 0 && src_off == 0 && dest_off == 0) {
6576 status = vfs_offload_token_db_fetch_fsp(
6577 fruit_offload_ctx, token, &src_fsp);
6578 if (tevent_req_nterror(req, status)) {
6579 return tevent_req_post(req, ev);
6581 state->src_fsp = src_fsp;
6583 status = vfs_stat_fsp(src_fsp);
6584 if (tevent_req_nterror(req, status)) {
6585 return tevent_req_post(req, ev);
6588 to_copy = src_fsp->fsp_name->st.st_ex_size;
6589 state->is_copyfile = true;
6592 subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle,
6601 if (tevent_req_nomem(subreq, req)) {
6602 return tevent_req_post(req, ev);
6605 tevent_req_set_callback(subreq, fruit_offload_write_done, req);
6609 static void fruit_offload_write_done(struct tevent_req *subreq)
6611 struct tevent_req *req = tevent_req_callback_data(
6612 subreq, struct tevent_req);
6613 struct fruit_offload_write_state *state = tevent_req_data(
6614 req, struct fruit_offload_write_state);
6616 unsigned int num_streams = 0;
6617 struct stream_struct *streams = NULL;
6619 struct smb_filename *src_fname_tmp = NULL;
6620 struct smb_filename *dst_fname_tmp = NULL;
6622 status = SMB_VFS_NEXT_OFFLOAD_WRITE_RECV(state->handle,
6625 TALLOC_FREE(subreq);
6626 if (tevent_req_nterror(req, status)) {
6630 if (!state->is_copyfile) {
6631 tevent_req_done(req);
6636 * Now copy all remaining streams. We know the share supports
6637 * streams, because we're in vfs_fruit. We don't do this async
6638 * because streams are few and small.
6640 status = vfs_streaminfo(state->handle->conn, state->src_fsp,
6641 state->src_fsp->fsp_name,
6642 req, &num_streams, &streams);
6643 if (tevent_req_nterror(req, status)) {
6647 if (num_streams == 1) {
6648 /* There is always one stream, ::$DATA. */
6649 tevent_req_done(req);
6653 for (i = 0; i < num_streams; i++) {
6654 DEBUG(10, ("%s: stream: '%s'/%zu\n",
6655 __func__, streams[i].name, (size_t)streams[i].size));
6657 src_fname_tmp = synthetic_smb_fname(
6659 state->src_fsp->fsp_name->base_name,
6662 state->src_fsp->fsp_name->flags);
6663 if (tevent_req_nomem(src_fname_tmp, req)) {
6667 if (is_ntfs_default_stream_smb_fname(src_fname_tmp)) {
6668 TALLOC_FREE(src_fname_tmp);
6672 dst_fname_tmp = synthetic_smb_fname(
6674 state->dst_fsp->fsp_name->base_name,
6677 state->dst_fsp->fsp_name->flags);
6678 if (tevent_req_nomem(dst_fname_tmp, req)) {
6679 TALLOC_FREE(src_fname_tmp);
6683 status = copy_file(req,
6684 state->handle->conn,
6687 OPENX_FILE_CREATE_IF_NOT_EXIST,
6689 if (!NT_STATUS_IS_OK(status)) {
6690 DEBUG(1, ("%s: copy %s to %s failed: %s\n", __func__,
6691 smb_fname_str_dbg(src_fname_tmp),
6692 smb_fname_str_dbg(dst_fname_tmp),
6693 nt_errstr(status)));
6694 TALLOC_FREE(src_fname_tmp);
6695 TALLOC_FREE(dst_fname_tmp);
6696 tevent_req_nterror(req, status);
6700 TALLOC_FREE(src_fname_tmp);
6701 TALLOC_FREE(dst_fname_tmp);
6704 TALLOC_FREE(streams);
6705 TALLOC_FREE(src_fname_tmp);
6706 TALLOC_FREE(dst_fname_tmp);
6707 tevent_req_done(req);
6710 static NTSTATUS fruit_offload_write_recv(struct vfs_handle_struct *handle,
6711 struct tevent_req *req,
6714 struct fruit_offload_write_state *state = tevent_req_data(
6715 req, struct fruit_offload_write_state);
6718 if (tevent_req_is_nterror(req, &status)) {
6719 DEBUG(1, ("server side copy chunk failed: %s\n",
6720 nt_errstr(status)));
6722 tevent_req_received(req);
6726 *copied = state->copied;
6727 tevent_req_received(req);
6729 return NT_STATUS_OK;
6732 static char *fruit_get_bandsize_line(char **lines, int numlines)
6735 static bool re_initialized = false;
6739 if (!re_initialized) {
6740 ret = regcomp(&re, "^[[:blank:]]*<key>band-size</key>$", 0);
6744 re_initialized = true;
6747 for (i = 0; i < numlines; i++) {
6748 regmatch_t matches[1];
6750 ret = regexec(&re, lines[i], 1, matches, 0);
6753 * Check if the match was on the last line, sa we want
6754 * the subsequent line.
6756 if (i + 1 == numlines) {
6759 return lines[i + 1];
6761 if (ret != REG_NOMATCH) {
6769 static bool fruit_get_bandsize_from_line(char *line, size_t *_band_size)
6772 static bool re_initialized = false;
6773 regmatch_t matches[2];
6778 if (!re_initialized) {
6781 "<integer>\\([[:digit:]]*\\)</integer>$",
6786 re_initialized = true;
6789 ret = regexec(&re, line, 2, matches, 0);
6791 DBG_ERR("regex failed [%s]\n", line);
6795 line[matches[1].rm_eo] = '\0';
6797 ok = conv_str_u64(&line[matches[1].rm_so], &band_size);
6801 *_band_size = (size_t)band_size;
6806 * This reads and parses an Info.plist from a TM sparsebundle looking for the
6807 * "band-size" key and value.
6809 static bool fruit_get_bandsize(vfs_handle_struct *handle,
6813 #define INFO_PLIST_MAX_SIZE 64*1024
6815 struct smb_filename *smb_fname = NULL;
6816 files_struct *fsp = NULL;
6817 uint8_t *file_data = NULL;
6818 char **lines = NULL;
6819 char *band_size_line = NULL;
6820 size_t plist_file_size;
6827 plist = talloc_asprintf(talloc_tos(),
6829 handle->conn->connectpath,
6831 if (plist == NULL) {
6836 smb_fname = synthetic_smb_fname(talloc_tos(), plist, NULL, NULL, 0);
6837 if (smb_fname == NULL) {
6842 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
6844 DBG_INFO("Ignoring Sparsebundle without Info.plist [%s]\n", dir);
6849 plist_file_size = smb_fname->st.st_ex_size;
6851 if (plist_file_size > INFO_PLIST_MAX_SIZE) {
6852 DBG_INFO("%s is too large, ignoring\n", plist);
6857 status = SMB_VFS_NEXT_CREATE_FILE(
6860 0, /* root_dir_fid */
6861 smb_fname, /* fname */
6862 FILE_GENERIC_READ, /* access_mask */
6863 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
6864 FILE_OPEN, /* create_disposition */
6865 0, /* create_options */
6866 0, /* file_attributes */
6867 INTERNAL_OPEN_ONLY, /* oplock_request */
6869 0, /* allocation_size */
6870 0, /* private_flags */
6875 NULL, NULL); /* create context */
6876 if (!NT_STATUS_IS_OK(status)) {
6877 DBG_INFO("Opening [%s] failed [%s]\n",
6878 smb_fname_str_dbg(smb_fname), nt_errstr(status));
6883 file_data = talloc_array(talloc_tos(), uint8_t, plist_file_size);
6884 if (file_data == NULL) {
6889 nread = SMB_VFS_NEXT_PREAD(handle, fsp, file_data, plist_file_size, 0);
6890 if (nread != plist_file_size) {
6891 DBG_ERR("Short read on [%s]: %zu/%zd\n",
6892 fsp_str_dbg(fsp), nread, plist_file_size);
6898 status = close_file(NULL, fsp, NORMAL_CLOSE);
6900 if (!NT_STATUS_IS_OK(status)) {
6901 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
6906 lines = file_lines_parse((char *)file_data,
6910 if (lines == NULL) {
6915 band_size_line = fruit_get_bandsize_line(lines, numlines);
6916 if (band_size_line == NULL) {
6917 DBG_ERR("Didn't find band-size key in [%s]\n",
6918 smb_fname_str_dbg(smb_fname));
6923 ok = fruit_get_bandsize_from_line(band_size_line, band_size);
6925 DBG_ERR("fruit_get_bandsize_from_line failed\n");
6929 DBG_DEBUG("Parsed band-size [%zu] for [%s]\n", *band_size, plist);
6933 status = close_file(NULL, fsp, NORMAL_CLOSE);
6934 if (!NT_STATUS_IS_OK(status)) {
6935 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
6940 TALLOC_FREE(smb_fname);
6941 TALLOC_FREE(file_data);
6946 struct fruit_disk_free_state {
6950 static bool fruit_get_num_bands(vfs_handle_struct *handle,
6955 struct smb_filename *bands_dir = NULL;
6957 struct dirent *e = NULL;
6961 path = talloc_asprintf(talloc_tos(),
6963 handle->conn->connectpath,
6969 bands_dir = synthetic_smb_fname(talloc_tos(),
6975 if (bands_dir == NULL) {
6979 d = SMB_VFS_NEXT_OPENDIR(handle, bands_dir, NULL, 0);
6981 TALLOC_FREE(bands_dir);
6987 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
6989 e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
6991 if (ISDOT(e->d_name) || ISDOTDOT(e->d_name)) {
6997 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
6999 TALLOC_FREE(bands_dir);
7003 DBG_DEBUG("%zu bands in [%s]\n", nbands, smb_fname_str_dbg(bands_dir));
7005 TALLOC_FREE(bands_dir);
7011 static bool fruit_tmsize_do_dirent(vfs_handle_struct *handle,
7012 struct fruit_disk_free_state *state,
7017 size_t sparsebundle_strlen = strlen("sparsebundle");
7018 size_t bandsize = 0;
7022 p = strstr(e->d_name, "sparsebundle");
7027 if (p[sparsebundle_strlen] != '\0') {
7031 DBG_DEBUG("Processing sparsebundle [%s]\n", e->d_name);
7033 ok = fruit_get_bandsize(handle, e->d_name, &bandsize);
7036 * Beware of race conditions: this may be an uninitialized
7037 * Info.plist that a client is just creating. We don't want let
7038 * this to trigger complete failure.
7040 DBG_ERR("Processing sparsebundle [%s] failed\n", e->d_name);
7044 ok = fruit_get_num_bands(handle, e->d_name, &nbands);
7047 * Beware of race conditions: this may be a backup sparsebundle
7048 * in an early stage lacking a bands subdirectory. We don't want
7049 * let this to trigger complete failure.
7051 DBG_ERR("Processing sparsebundle [%s] failed\n", e->d_name);
7055 if (bandsize > SIZE_MAX/nbands) {
7056 DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
7060 tm_size = bandsize * nbands;
7062 if (state->total_size + tm_size < state->total_size) {
7063 DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
7068 state->total_size += tm_size;
7070 DBG_DEBUG("[%s] tm_size [%jd] total_size [%jd]\n",
7071 e->d_name, (intmax_t)tm_size, (intmax_t)state->total_size);
7077 * Calculate used size of a TimeMachine volume
7079 * This assumes that the volume is used only for TimeMachine.
7081 * - readdir(basedir of share), then
7082 * - for every element that matches regex "^\(.*\)\.sparsebundle$" :
7083 * - parse "\1.sparsebundle/Info.plist" and read the band-size XML key
7084 * - count band files in "\1.sparsebundle/bands/"
7085 * - calculate used size of all bands: band_count * band_size
7087 static uint64_t fruit_disk_free(vfs_handle_struct *handle,
7088 const struct smb_filename *smb_fname,
7093 struct fruit_config_data *config = NULL;
7094 struct fruit_disk_free_state state = {0};
7096 struct dirent *e = NULL;
7102 SMB_VFS_HANDLE_GET_DATA(handle, config,
7103 struct fruit_config_data,
7106 if (!config->time_machine ||
7107 config->time_machine_max_size == 0)
7109 return SMB_VFS_NEXT_DISK_FREE(handle,
7116 d = SMB_VFS_NEXT_OPENDIR(handle, smb_fname, NULL, 0);
7121 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
7123 e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
7125 ok = fruit_tmsize_do_dirent(handle, &state, e);
7127 SMB_VFS_NEXT_CLOSEDIR(handle, d);
7132 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
7137 dsize = config->time_machine_max_size / 512;
7138 dfree = dsize - (state.total_size / 512);
7139 if (dfree > dsize) {
7149 static struct vfs_fn_pointers vfs_fruit_fns = {
7150 .connect_fn = fruit_connect,
7151 .disk_free_fn = fruit_disk_free,
7153 /* File operations */
7154 .chmod_fn = fruit_chmod,
7155 .chown_fn = fruit_chown,
7156 .unlink_fn = fruit_unlink,
7157 .rename_fn = fruit_rename,
7158 .rmdir_fn = fruit_rmdir,
7159 .open_fn = fruit_open,
7160 .close_fn = fruit_close,
7161 .pread_fn = fruit_pread,
7162 .pwrite_fn = fruit_pwrite,
7163 .pread_send_fn = fruit_pread_send,
7164 .pread_recv_fn = fruit_pread_recv,
7165 .pwrite_send_fn = fruit_pwrite_send,
7166 .pwrite_recv_fn = fruit_pwrite_recv,
7167 .stat_fn = fruit_stat,
7168 .lstat_fn = fruit_lstat,
7169 .fstat_fn = fruit_fstat,
7170 .streaminfo_fn = fruit_streaminfo,
7171 .ntimes_fn = fruit_ntimes,
7172 .ftruncate_fn = fruit_ftruncate,
7173 .fallocate_fn = fruit_fallocate,
7174 .create_file_fn = fruit_create_file,
7175 .readdir_attr_fn = fruit_readdir_attr,
7176 .offload_read_send_fn = fruit_offload_read_send,
7177 .offload_read_recv_fn = fruit_offload_read_recv,
7178 .offload_write_send_fn = fruit_offload_write_send,
7179 .offload_write_recv_fn = fruit_offload_write_recv,
7181 /* NT ACL operations */
7182 .fget_nt_acl_fn = fruit_fget_nt_acl,
7183 .fset_nt_acl_fn = fruit_fset_nt_acl,
7187 NTSTATUS vfs_fruit_init(TALLOC_CTX *ctx)
7189 NTSTATUS ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "fruit",
7191 if (!NT_STATUS_IS_OK(ret)) {
7195 vfs_fruit_debug_level = debug_add_class("fruit");
7196 if (vfs_fruit_debug_level == -1) {
7197 vfs_fruit_debug_level = DBGC_VFS;
7198 DEBUG(0, ("%s: Couldn't register custom debugging class!\n",
7201 DEBUG(10, ("%s: Debug class number of '%s': %d\n",
7202 "vfs_fruit_init","fruit",vfs_fruit_debug_level));