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 "../lib/crypto/md5.h"
26 #include "system/shmem.h"
27 #include "locking/proto.h"
28 #include "smbd/globals.h"
30 #include "libcli/security/security.h"
31 #include "../libcli/smb/smb2_create_ctx.h"
32 #include "lib/util/sys_rw.h"
33 #include "lib/util/tevent_ntstatus.h"
34 #include "lib/util/tevent_unix.h"
35 #include "offload_token.h"
36 #include "string_replace.h"
39 * Enhanced OS X and Netatalk compatibility
40 * ========================================
42 * This modules takes advantage of vfs_streams_xattr and
43 * vfs_catia. VFS modules vfs_fruit and vfs_streams_xattr must be
44 * loaded in the correct order:
46 * vfs modules = catia fruit streams_xattr
48 * The module intercepts the OS X special streams "AFP_AfpInfo" and
49 * "AFP_Resource" and handles them in a special way. All other named
50 * streams are deferred to vfs_streams_xattr.
52 * The OS X client maps all NTFS illegal characters to the Unicode
53 * private range. This module optionally stores the charcters using
54 * their native ASCII encoding using vfs_catia. If you're not enabling
55 * this feature, you can skip catia from vfs modules.
57 * Finally, open modes are optionally checked against Netatalk AFP
60 * The "AFP_AfpInfo" named stream is a binary blob containing OS X
61 * extended metadata for files and directories. This module optionally
62 * reads and stores this metadata in a way compatible with Netatalk 3
63 * which stores the metadata in an EA "org.netatalk.metadata". Cf
64 * source3/include/MacExtensions.h for a description of the binary
67 * The "AFP_Resource" named stream may be arbitrarily large, thus it
68 * can't be stored in an xattr on most filesystem. ZFS on Solaris is
69 * the only available filesystem where xattrs can be of any size and
70 * the OS supports using the file APIs for xattrs.
72 * The AFP_Resource stream is stored in an AppleDouble file prepending
73 * "._" to the filename. On Solaris with ZFS the stream is optionally
74 * stored in an EA "org.netatalk.resource".
80 * The OS X SMB client sends xattrs as ADS too. For xattr interop with
81 * other protocols you may want to adjust the xattr names the VFS
82 * module vfs_streams_xattr uses for storing ADS's. This defaults to
83 * user.DosStream.ADS_NAME:$DATA and can be changed by specifying
84 * these module parameters:
86 * streams_xattr:prefix = user.
87 * streams_xattr:store_stream_type = false
93 * - log diagnostic if any needed VFS module is not loaded
94 * (eg with lp_vfs_objects())
98 static int vfs_fruit_debug_level
= DBGC_VFS
;
100 static struct global_fruit_config
{
101 bool nego_aapl
; /* client negotiated AAPL */
103 } global_fruit_config
;
106 #define DBGC_CLASS vfs_fruit_debug_level
108 #define FRUIT_PARAM_TYPE_NAME "fruit"
109 #define ADOUBLE_NAME_PREFIX "._"
111 #define NETATALK_META_XATTR "org.netatalk.Metadata"
112 #define NETATALK_RSRC_XATTR "org.netatalk.ResourceFork"
114 #if defined(HAVE_ATTROPEN)
115 #define AFPINFO_EA_NETATALK NETATALK_META_XATTR
116 #define AFPRESOURCE_EA_NETATALK NETATALK_RSRC_XATTR
118 #define AFPINFO_EA_NETATALK "user." NETATALK_META_XATTR
119 #define AFPRESOURCE_EA_NETATALK "user." NETATALK_RSRC_XATTR
122 enum apple_fork
{APPLE_FORK_DATA
, APPLE_FORK_RSRC
};
124 enum fruit_rsrc
{FRUIT_RSRC_STREAM
, FRUIT_RSRC_ADFILE
, FRUIT_RSRC_XATTR
};
125 enum fruit_meta
{FRUIT_META_STREAM
, FRUIT_META_NETATALK
};
126 enum fruit_locking
{FRUIT_LOCKING_NETATALK
, FRUIT_LOCKING_NONE
};
127 enum fruit_encoding
{FRUIT_ENC_NATIVE
, FRUIT_ENC_PRIVATE
};
129 struct fruit_config_data
{
130 enum fruit_rsrc rsrc
;
131 enum fruit_meta meta
;
132 enum fruit_locking locking
;
133 enum fruit_encoding encoding
;
134 bool use_aapl
; /* config from smb.conf */
136 bool readdir_attr_enabled
;
137 bool unix_info_enabled
;
138 bool copyfile_enabled
;
139 bool veto_appledouble
;
141 bool aapl_zero_file_id
;
144 off_t time_machine_max_size
;
145 bool wipe_intentionally_left_blank_rfork
;
146 bool delete_empty_adfiles
;
149 * Additional options, all enabled by default,
150 * possibly useful for analyzing performance. The associated
151 * operations with each of them may be expensive, so having
152 * the chance to disable them individually gives a chance
153 * tweaking the setup for the particular usecase.
155 bool readdir_attr_rsize
;
156 bool readdir_attr_finder_info
;
157 bool readdir_attr_max_access
;
160 static const struct enum_list fruit_rsrc
[] = {
161 {FRUIT_RSRC_STREAM
, "stream"}, /* pass on to vfs_streams_xattr */
162 {FRUIT_RSRC_ADFILE
, "file"}, /* ._ AppleDouble file */
163 {FRUIT_RSRC_XATTR
, "xattr"}, /* Netatalk compatible xattr (ZFS only) */
167 static const struct enum_list fruit_meta
[] = {
168 {FRUIT_META_STREAM
, "stream"}, /* pass on to vfs_streams_xattr */
169 {FRUIT_META_NETATALK
, "netatalk"}, /* Netatalk compatible xattr */
173 static const struct enum_list fruit_locking
[] = {
174 {FRUIT_LOCKING_NETATALK
, "netatalk"}, /* synchronize locks with Netatalk */
175 {FRUIT_LOCKING_NONE
, "none"},
179 static const struct enum_list fruit_encoding
[] = {
180 {FRUIT_ENC_NATIVE
, "native"}, /* map unicode private chars to ASCII */
181 {FRUIT_ENC_PRIVATE
, "private"}, /* keep unicode private chars */
185 static const char *fruit_catia_maps
=
186 "0x01:0xf001,0x02:0xf002,0x03:0xf003,0x04:0xf004,"
187 "0x05:0xf005,0x06:0xf006,0x07:0xf007,0x08:0xf008,"
188 "0x09:0xf009,0x0a:0xf00a,0x0b:0xf00b,0x0c:0xf00c,"
189 "0x0d:0xf00d,0x0e:0xf00e,0x0f:0xf00f,0x10:0xf010,"
190 "0x11:0xf011,0x12:0xf012,0x13:0xf013,0x14:0xf014,"
191 "0x15:0xf015,0x16:0xf016,0x17:0xf017,0x18:0xf018,"
192 "0x19:0xf019,0x1a:0xf01a,0x1b:0xf01b,0x1c:0xf01c,"
193 "0x1d:0xf01d,0x1e:0xf01e,0x1f:0xf01f,"
194 "0x22:0xf020,0x2a:0xf021,0x3a:0xf022,0x3c:0xf023,"
195 "0x3e:0xf024,0x3f:0xf025,0x5c:0xf026,0x7c:0xf027,"
198 /*****************************************************************************
199 * Defines, functions and data structures that deal with AppleDouble
200 *****************************************************************************/
203 * There are two AppleDouble blobs we deal with:
205 * - ADOUBLE_META - AppleDouble blob used by Netatalk for storing
206 * metadata in an xattr
208 * - ADOUBLE_RSRC - AppleDouble blob used by OS X and Netatalk in
211 typedef enum {ADOUBLE_META
, ADOUBLE_RSRC
} adouble_type_t
;
214 #define AD_VERSION2 0x00020000
215 #define AD_VERSION AD_VERSION2
218 * AppleDouble entry IDs.
220 #define ADEID_DFORK 1
221 #define ADEID_RFORK 2
223 #define ADEID_COMMENT 4
224 #define ADEID_ICONBW 5
225 #define ADEID_ICONCOL 6
226 #define ADEID_FILEI 7
227 #define ADEID_FILEDATESI 8
228 #define ADEID_FINDERI 9
229 #define ADEID_MACFILEI 10
230 #define ADEID_PRODOSFILEI 11
231 #define ADEID_MSDOSFILEI 12
232 #define ADEID_SHORTNAME 13
233 #define ADEID_AFPFILEI 14
236 /* Private Netatalk entries */
237 #define ADEID_PRIVDEV 16
238 #define ADEID_PRIVINO 17
239 #define ADEID_PRIVSYN 18
240 #define ADEID_PRIVID 19
241 #define ADEID_MAX (ADEID_PRIVID + 1)
244 * These are the real ids for the private entries,
245 * as stored in the adouble file
247 #define AD_DEV 0x80444556
248 #define AD_INO 0x80494E4F
249 #define AD_SYN 0x8053594E
250 #define AD_ID 0x8053567E
252 /* Number of actually used entries */
253 #define ADEID_NUM_XATTR 8
254 #define ADEID_NUM_DOT_UND 2
255 #define ADEID_NUM_RSRC_XATTR 1
257 /* AppleDouble magic */
258 #define AD_APPLESINGLE_MAGIC 0x00051600
259 #define AD_APPLEDOUBLE_MAGIC 0x00051607
260 #define AD_MAGIC AD_APPLEDOUBLE_MAGIC
262 /* Sizes of relevant entry bits */
263 #define ADEDLEN_MAGIC 4
264 #define ADEDLEN_VERSION 4
265 #define ADEDLEN_FILLER 16
266 #define AD_FILLER_TAG "Netatalk " /* should be 16 bytes */
267 #define AD_FILLER_TAG_OSX "Mac OS X " /* should be 16 bytes */
268 #define ADEDLEN_NENTRIES 2
269 #define AD_HEADER_LEN (ADEDLEN_MAGIC + ADEDLEN_VERSION + \
270 ADEDLEN_FILLER + ADEDLEN_NENTRIES) /* 26 */
271 #define AD_ENTRY_LEN_EID 4
272 #define AD_ENTRY_LEN_OFF 4
273 #define AD_ENTRY_LEN_LEN 4
274 #define AD_ENTRY_LEN (AD_ENTRY_LEN_EID + AD_ENTRY_LEN_OFF + AD_ENTRY_LEN_LEN)
277 #define ADEDLEN_NAME 255
278 #define ADEDLEN_COMMENT 200
279 #define ADEDLEN_FILEI 16
280 #define ADEDLEN_FINDERI 32
281 #define ADEDLEN_FILEDATESI 16
282 #define ADEDLEN_SHORTNAME 12 /* length up to 8.3 */
283 #define ADEDLEN_AFPFILEI 4
284 #define ADEDLEN_MACFILEI 4
285 #define ADEDLEN_PRODOSFILEI 8
286 #define ADEDLEN_MSDOSFILEI 2
287 #define ADEDLEN_DID 4
288 #define ADEDLEN_PRIVDEV 8
289 #define ADEDLEN_PRIVINO 8
290 #define ADEDLEN_PRIVSYN 8
291 #define ADEDLEN_PRIVID 4
294 #define ADEDOFF_MAGIC 0
295 #define ADEDOFF_VERSION (ADEDOFF_MAGIC + ADEDLEN_MAGIC)
296 #define ADEDOFF_FILLER (ADEDOFF_VERSION + ADEDLEN_VERSION)
297 #define ADEDOFF_NENTRIES (ADEDOFF_FILLER + ADEDLEN_FILLER)
299 #define ADEDOFF_FINDERI_XATTR (AD_HEADER_LEN + \
300 (ADEID_NUM_XATTR * AD_ENTRY_LEN))
301 #define ADEDOFF_COMMENT_XATTR (ADEDOFF_FINDERI_XATTR + ADEDLEN_FINDERI)
302 #define ADEDOFF_FILEDATESI_XATTR (ADEDOFF_COMMENT_XATTR + ADEDLEN_COMMENT)
303 #define ADEDOFF_AFPFILEI_XATTR (ADEDOFF_FILEDATESI_XATTR + \
305 #define ADEDOFF_PRIVDEV_XATTR (ADEDOFF_AFPFILEI_XATTR + ADEDLEN_AFPFILEI)
306 #define ADEDOFF_PRIVINO_XATTR (ADEDOFF_PRIVDEV_XATTR + ADEDLEN_PRIVDEV)
307 #define ADEDOFF_PRIVSYN_XATTR (ADEDOFF_PRIVINO_XATTR + ADEDLEN_PRIVINO)
308 #define ADEDOFF_PRIVID_XATTR (ADEDOFF_PRIVSYN_XATTR + ADEDLEN_PRIVSYN)
310 #define ADEDOFF_FINDERI_DOT_UND (AD_HEADER_LEN + \
311 (ADEID_NUM_DOT_UND * AD_ENTRY_LEN))
312 #define ADEDOFF_RFORK_DOT_UND (ADEDOFF_FINDERI_DOT_UND + ADEDLEN_FINDERI)
314 #define AD_DATASZ_XATTR (AD_HEADER_LEN + \
315 (ADEID_NUM_XATTR * AD_ENTRY_LEN) + \
316 ADEDLEN_FINDERI + ADEDLEN_COMMENT + \
317 ADEDLEN_FILEDATESI + ADEDLEN_AFPFILEI + \
318 ADEDLEN_PRIVDEV + ADEDLEN_PRIVINO + \
319 ADEDLEN_PRIVSYN + ADEDLEN_PRIVID)
321 #if AD_DATASZ_XATTR != 402
322 #error bad size for AD_DATASZ_XATTR
325 #define AD_DATASZ_DOT_UND (AD_HEADER_LEN + \
326 (ADEID_NUM_DOT_UND * AD_ENTRY_LEN) + \
328 #if AD_DATASZ_DOT_UND != 82
329 #error bad size for AD_DATASZ_DOT_UND
333 * Sharemode locks fcntl() offsets
335 #if _FILE_OFFSET_BITS == 64 || defined(HAVE_LARGEFILE)
336 #define AD_FILELOCK_BASE (UINT64_C(0x7FFFFFFFFFFFFFFF) - 9)
338 #define AD_FILELOCK_BASE (UINT32_C(0x7FFFFFFF) - 9)
340 #define BYTELOCK_MAX (AD_FILELOCK_BASE - 1)
342 #define AD_FILELOCK_OPEN_WR (AD_FILELOCK_BASE + 0)
343 #define AD_FILELOCK_OPEN_RD (AD_FILELOCK_BASE + 1)
344 #define AD_FILELOCK_RSRC_OPEN_WR (AD_FILELOCK_BASE + 2)
345 #define AD_FILELOCK_RSRC_OPEN_RD (AD_FILELOCK_BASE + 3)
346 #define AD_FILELOCK_DENY_WR (AD_FILELOCK_BASE + 4)
347 #define AD_FILELOCK_DENY_RD (AD_FILELOCK_BASE + 5)
348 #define AD_FILELOCK_RSRC_DENY_WR (AD_FILELOCK_BASE + 6)
349 #define AD_FILELOCK_RSRC_DENY_RD (AD_FILELOCK_BASE + 7)
350 #define AD_FILELOCK_OPEN_NONE (AD_FILELOCK_BASE + 8)
351 #define AD_FILELOCK_RSRC_OPEN_NONE (AD_FILELOCK_BASE + 9)
353 /* Time stuff we overload the bits a little */
354 #define AD_DATE_CREATE 0
355 #define AD_DATE_MODIFY 4
356 #define AD_DATE_BACKUP 8
357 #define AD_DATE_ACCESS 12
358 #define AD_DATE_MASK (AD_DATE_CREATE | AD_DATE_MODIFY | \
359 AD_DATE_BACKUP | AD_DATE_ACCESS)
360 #define AD_DATE_UNIX (1 << 10)
361 #define AD_DATE_START 0x80000000
362 #define AD_DATE_DELTA 946684800
363 #define AD_DATE_FROM_UNIX(x) (htonl((x) - AD_DATE_DELTA))
364 #define AD_DATE_TO_UNIX(x) (ntohl(x) + AD_DATE_DELTA)
366 #define AD_XATTR_HDR_MAGIC 0x41545452 /* 'ATTR' */
367 #define AD_XATTR_MAX_ENTRIES 1024 /* Some arbitrarily enforced limit */
368 #define AD_XATTR_HDR_SIZE 36
369 #define AD_XATTR_MAX_HDR_SIZE 65536
371 /* Accessor macros */
372 #define ad_getentrylen(ad,eid) ((ad)->ad_eid[(eid)].ade_len)
373 #define ad_getentryoff(ad,eid) ((ad)->ad_eid[(eid)].ade_off)
374 #define ad_setentrylen(ad,eid,len) ((ad)->ad_eid[(eid)].ade_len = (len))
375 #define ad_setentryoff(ad,eid,off) ((ad)->ad_eid[(eid)].ade_off = (off))
378 * Both struct ad_xattr_header and struct ad_xattr_entry describe the in memory
379 * representation as well as the on-disk format.
381 * The ad_xattr_header follows the FinderInfo data in the FinderInfo entry if
382 * the length of the FinderInfo entry is larger then 32 bytes. It is then
383 * preceeded with 2 bytes padding.
385 * Cf: https://opensource.apple.com/source/xnu/xnu-4570.1.46/bsd/vfs/vfs_xattr.c
388 struct ad_xattr_header
{
389 uint32_t adx_magic
; /* ATTR_HDR_MAGIC */
390 uint32_t adx_debug_tag
; /* for debugging == file id of owning file */
391 uint32_t adx_total_size
; /* file offset of end of attribute header + entries + data */
392 uint32_t adx_data_start
; /* file offset to attribute data area */
393 uint32_t adx_data_length
; /* length of attribute data area */
394 uint32_t adx_reserved
[3];
396 uint16_t adx_num_attrs
;
399 /* On-disk entries are aligned on 4 byte boundaries */
400 struct ad_xattr_entry
{
401 uint32_t adx_offset
; /* file offset to data */
402 uint32_t adx_length
; /* size of attribute data */
404 uint8_t adx_namelen
; /* included the NULL terminator */
405 char *adx_name
; /* NULL-terminated UTF-8 name */
414 vfs_handle_struct
*ad_handle
;
417 adouble_type_t ad_type
;
420 uint8_t ad_filler
[ADEDLEN_FILLER
];
421 struct ad_entry ad_eid
[ADEID_MAX
];
423 struct ad_xattr_header adx_header
;
424 struct ad_xattr_entry
*adx_entries
;
427 struct ad_entry_order
{
428 uint32_t id
, offset
, len
;
431 /* Netatalk AppleDouble metadata xattr */
433 struct ad_entry_order entry_order_meta_xattr
[ADEID_NUM_XATTR
+ 1] = {
434 {ADEID_FINDERI
, ADEDOFF_FINDERI_XATTR
, ADEDLEN_FINDERI
},
435 {ADEID_COMMENT
, ADEDOFF_COMMENT_XATTR
, 0},
436 {ADEID_FILEDATESI
, ADEDOFF_FILEDATESI_XATTR
, ADEDLEN_FILEDATESI
},
437 {ADEID_AFPFILEI
, ADEDOFF_AFPFILEI_XATTR
, ADEDLEN_AFPFILEI
},
438 {ADEID_PRIVDEV
, ADEDOFF_PRIVDEV_XATTR
, 0},
439 {ADEID_PRIVINO
, ADEDOFF_PRIVINO_XATTR
, 0},
440 {ADEID_PRIVSYN
, ADEDOFF_PRIVSYN_XATTR
, 0},
441 {ADEID_PRIVID
, ADEDOFF_PRIVID_XATTR
, 0},
445 /* AppleDouble resource fork file (the ones prefixed by "._") */
447 struct ad_entry_order entry_order_dot_und
[ADEID_NUM_DOT_UND
+ 1] = {
448 {ADEID_FINDERI
, ADEDOFF_FINDERI_DOT_UND
, ADEDLEN_FINDERI
},
449 {ADEID_RFORK
, ADEDOFF_RFORK_DOT_UND
, 0},
454 * Fake AppleDouble entry oder for resource fork xattr. The xattr
455 * isn't an AppleDouble file, it simply contains the resource data,
456 * but in order to be able to use some API calls like ad_getentryoff()
457 * we build a fake/helper struct adouble with this entry order struct.
460 struct ad_entry_order entry_order_rsrc_xattr
[ADEID_NUM_RSRC_XATTR
+ 1] = {
465 /* Conversion from enumerated id to on-disk AppleDouble id */
466 #define AD_EID_DISK(a) (set_eid[a])
467 static const uint32_t set_eid
[] = {
468 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
469 AD_DEV
, AD_INO
, AD_SYN
, AD_ID
472 static char empty_resourcefork
[] = {
473 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
474 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
475 0x54, 0x68, 0x69, 0x73, 0x20, 0x72, 0x65, 0x73,
476 0x6F, 0x75, 0x72, 0x63, 0x65, 0x20, 0x66, 0x6F,
477 0x72, 0x6B, 0x20, 0x69, 0x6E, 0x74, 0x65, 0x6E,
478 0x74, 0x69, 0x6F, 0x6E, 0x61, 0x6C, 0x6C, 0x79,
479 0x20, 0x6C, 0x65, 0x66, 0x74, 0x20, 0x62, 0x6C,
480 0x61, 0x6E, 0x6B, 0x20, 0x20, 0x20, 0x00, 0x00,
481 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
482 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
483 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
484 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
485 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
486 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
487 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
488 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
489 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
490 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
491 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
492 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
493 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
494 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
495 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
496 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
497 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
498 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
499 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
500 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
501 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
502 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
503 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
504 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
505 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
506 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
507 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
508 0x00, 0x1C, 0x00, 0x1E, 0xFF, 0xFF
512 /* tcon config handle */
513 struct fruit_config_data
*config
;
515 /* Denote stream type, meta or rsrc */
520 * Forward declarations
522 static struct adouble
*ad_init(TALLOC_CTX
*ctx
, vfs_handle_struct
*handle
,
523 adouble_type_t type
);
524 static int ad_set(struct adouble
*ad
, const struct smb_filename
*smb_fname
);
525 static int ad_fset(struct adouble
*ad
, files_struct
*fsp
);
526 static int adouble_path(TALLOC_CTX
*ctx
,
527 const struct smb_filename
*smb_fname__in
,
528 struct smb_filename
**ppsmb_fname_out
);
529 static AfpInfo
*afpinfo_new(TALLOC_CTX
*ctx
);
530 static ssize_t
afpinfo_pack(const AfpInfo
*ai
, char *buf
);
531 static AfpInfo
*afpinfo_unpack(TALLOC_CTX
*ctx
, const void *data
);
535 * Return a pointer to an AppleDouble entry
537 * Returns NULL if the entry is not present
539 static char *ad_get_entry(const struct adouble
*ad
, int eid
)
541 off_t off
= ad_getentryoff(ad
, eid
);
542 size_t len
= ad_getentrylen(ad
, eid
);
544 if (off
== 0 || len
== 0) {
548 return ad
->ad_data
+ off
;
554 static int ad_getdate(const struct adouble
*ad
,
555 unsigned int dateoff
,
558 bool xlate
= (dateoff
& AD_DATE_UNIX
);
561 dateoff
&= AD_DATE_MASK
;
562 p
= ad_get_entry(ad
, ADEID_FILEDATESI
);
567 if (dateoff
> AD_DATE_ACCESS
) {
571 memcpy(date
, p
+ dateoff
, sizeof(uint32_t));
574 *date
= AD_DATE_TO_UNIX(*date
);
582 static int ad_setdate(struct adouble
*ad
, unsigned int dateoff
, uint32_t date
)
584 bool xlate
= (dateoff
& AD_DATE_UNIX
);
587 p
= ad_get_entry(ad
, ADEID_FILEDATESI
);
592 dateoff
&= AD_DATE_MASK
;
594 date
= AD_DATE_FROM_UNIX(date
);
597 if (dateoff
> AD_DATE_ACCESS
) {
601 memcpy(p
+ dateoff
, &date
, sizeof(date
));
608 * Map on-disk AppleDouble id to enumerated id
610 static uint32_t get_eid(uint32_t eid
)
618 return ADEID_PRIVDEV
;
620 return ADEID_PRIVINO
;
622 return ADEID_PRIVSYN
;
633 * Pack AppleDouble structure into data buffer
635 static bool ad_pack(struct adouble
*ad
)
642 bufsize
= talloc_get_size(ad
->ad_data
);
643 if (bufsize
< AD_DATASZ_DOT_UND
) {
644 DBG_ERR("bad buffer size [0x%" PRIx32
"]\n", bufsize
);
648 if (offset
+ ADEDLEN_MAGIC
< offset
||
649 offset
+ ADEDLEN_MAGIC
>= bufsize
) {
652 RSIVAL(ad
->ad_data
, offset
, ad
->ad_magic
);
653 offset
+= ADEDLEN_MAGIC
;
655 if (offset
+ ADEDLEN_VERSION
< offset
||
656 offset
+ ADEDLEN_VERSION
>= bufsize
) {
659 RSIVAL(ad
->ad_data
, offset
, ad
->ad_version
);
660 offset
+= ADEDLEN_VERSION
;
662 if (offset
+ ADEDLEN_FILLER
< offset
||
663 offset
+ ADEDLEN_FILLER
>= bufsize
) {
666 if (ad
->ad_type
== ADOUBLE_RSRC
) {
667 memcpy(ad
->ad_data
+ offset
, AD_FILLER_TAG
, ADEDLEN_FILLER
);
669 offset
+= ADEDLEN_FILLER
;
671 if (offset
+ ADEDLEN_NENTRIES
< offset
||
672 offset
+ ADEDLEN_NENTRIES
>= bufsize
) {
675 offset
+= ADEDLEN_NENTRIES
;
677 for (eid
= 0, nent
= 0; eid
< ADEID_MAX
; eid
++) {
678 if (ad
->ad_eid
[eid
].ade_off
== 0) {
680 * ade_off is also used as indicator whether a
681 * specific entry is used or not
686 if (offset
+ AD_ENTRY_LEN_EID
< offset
||
687 offset
+ AD_ENTRY_LEN_EID
>= bufsize
) {
690 RSIVAL(ad
->ad_data
, offset
, AD_EID_DISK(eid
));
691 offset
+= AD_ENTRY_LEN_EID
;
693 if (offset
+ AD_ENTRY_LEN_OFF
< offset
||
694 offset
+ AD_ENTRY_LEN_OFF
>= bufsize
) {
697 RSIVAL(ad
->ad_data
, offset
, ad
->ad_eid
[eid
].ade_off
);
698 offset
+= AD_ENTRY_LEN_OFF
;
700 if (offset
+ AD_ENTRY_LEN_LEN
< offset
||
701 offset
+ AD_ENTRY_LEN_LEN
>= bufsize
) {
704 RSIVAL(ad
->ad_data
, offset
, ad
->ad_eid
[eid
].ade_len
);
705 offset
+= AD_ENTRY_LEN_LEN
;
710 if (ADEDOFF_NENTRIES
+ 2 >= bufsize
) {
713 RSSVAL(ad
->ad_data
, ADEDOFF_NENTRIES
, nent
);
718 static bool ad_unpack_xattrs(struct adouble
*ad
)
720 struct ad_xattr_header
*h
= &ad
->adx_header
;
721 const char *p
= ad
->ad_data
;
725 if (ad_getentrylen(ad
, ADEID_FINDERI
) <= ADEDLEN_FINDERI
) {
729 /* 2 bytes padding */
730 hoff
= ad_getentryoff(ad
, ADEID_FINDERI
) + ADEDLEN_FINDERI
+ 2;
732 h
->adx_magic
= RIVAL(p
, hoff
+ 0);
733 h
->adx_debug_tag
= RIVAL(p
, hoff
+ 4); /* Not used -> not checked */
734 h
->adx_total_size
= RIVAL(p
, hoff
+ 8);
735 h
->adx_data_start
= RIVAL(p
, hoff
+ 12);
736 h
->adx_data_length
= RIVAL(p
, hoff
+ 16);
737 h
->adx_flags
= RSVAL(p
, hoff
+ 32); /* Not used -> not checked */
738 h
->adx_num_attrs
= RSVAL(p
, hoff
+ 34);
740 if (h
->adx_magic
!= AD_XATTR_HDR_MAGIC
) {
741 DBG_ERR("Bad magic: 0x%" PRIx32
"\n", h
->adx_magic
);
745 if (h
->adx_total_size
> ad_getentryoff(ad
, ADEID_RFORK
)) {
746 DBG_ERR("Bad total size: 0x%" PRIx32
"\n", h
->adx_total_size
);
749 if (h
->adx_total_size
> AD_XATTR_MAX_HDR_SIZE
) {
750 DBG_ERR("Bad total size: 0x%" PRIx32
"\n", h
->adx_total_size
);
754 if (h
->adx_data_start
< (hoff
+ AD_XATTR_HDR_SIZE
)) {
755 DBG_ERR("Bad start: 0x%" PRIx32
"\n", h
->adx_data_start
);
759 if ((h
->adx_data_start
+ h
->adx_data_length
) < h
->adx_data_start
) {
760 DBG_ERR("Bad length: %" PRIu32
"\n", h
->adx_data_length
);
763 if ((h
->adx_data_start
+ h
->adx_data_length
) >
764 ad
->adx_header
.adx_total_size
)
766 DBG_ERR("Bad length: %" PRIu32
"\n", h
->adx_data_length
);
770 if (h
->adx_num_attrs
> AD_XATTR_MAX_ENTRIES
) {
771 DBG_ERR("Bad num xattrs: %" PRIu16
"\n", h
->adx_num_attrs
);
775 if (h
->adx_num_attrs
== 0) {
779 ad
->adx_entries
= talloc_zero_array(
780 ad
, struct ad_xattr_entry
, h
->adx_num_attrs
);
781 if (ad
->adx_entries
== NULL
) {
785 hoff
+= AD_XATTR_HDR_SIZE
;
787 for (i
= 0; i
< h
->adx_num_attrs
; i
++) {
788 struct ad_xattr_entry
*e
= &ad
->adx_entries
[i
];
790 hoff
= (hoff
+ 3) & ~3;
792 e
->adx_offset
= RIVAL(p
, hoff
+ 0);
793 e
->adx_length
= RIVAL(p
, hoff
+ 4);
794 e
->adx_flags
= RSVAL(p
, hoff
+ 8);
795 e
->adx_namelen
= *(p
+ hoff
+ 10);
797 if (e
->adx_offset
>= ad
->adx_header
.adx_total_size
) {
798 DBG_ERR("Bad adx_offset: %" PRIx32
"\n",
803 if ((e
->adx_offset
+ e
->adx_length
) < e
->adx_offset
) {
804 DBG_ERR("Bad adx_length: %" PRIx32
"\n",
809 if ((e
->adx_offset
+ e
->adx_length
) >
810 ad
->adx_header
.adx_total_size
)
812 DBG_ERR("Bad adx_length: %" PRIx32
"\n",
817 if (e
->adx_namelen
== 0) {
818 DBG_ERR("Bad adx_namelen: %" PRIx32
"\n",
822 if ((hoff
+ 11 + e
->adx_namelen
) < hoff
+ 11) {
823 DBG_ERR("Bad adx_namelen: %" PRIx32
"\n",
827 if ((hoff
+ 11 + e
->adx_namelen
) >
828 ad
->adx_header
.adx_data_start
)
830 DBG_ERR("Bad adx_namelen: %" PRIx32
"\n",
835 e
->adx_name
= talloc_strndup(ad
->adx_entries
,
838 if (e
->adx_name
== NULL
) {
842 DBG_DEBUG("xattr [%s] offset [0x%x] size [0x%x]\n",
843 e
->adx_name
, e
->adx_offset
, e
->adx_length
);
844 dump_data(10, (uint8_t *)(ad
->ad_data
+ e
->adx_offset
),
847 hoff
+= 11 + e
->adx_namelen
;
854 * Unpack an AppleDouble blob into a struct adoble
856 static bool ad_unpack(struct adouble
*ad
, const size_t nentries
,
859 size_t bufsize
= talloc_get_size(ad
->ad_data
);
861 uint32_t eid
, len
, off
;
865 * The size of the buffer ad->ad_data is checked when read, so
866 * we wouldn't have to check our own offsets, a few extra
867 * checks won't hurt though. We have to check the offsets we
868 * read from the buffer anyway.
871 if (bufsize
< (AD_HEADER_LEN
+ (AD_ENTRY_LEN
* nentries
))) {
872 DEBUG(1, ("bad size\n"));
876 ad
->ad_magic
= RIVAL(ad
->ad_data
, 0);
877 ad
->ad_version
= RIVAL(ad
->ad_data
, ADEDOFF_VERSION
);
878 if ((ad
->ad_magic
!= AD_MAGIC
) || (ad
->ad_version
!= AD_VERSION
)) {
879 DEBUG(1, ("wrong magic or version\n"));
883 memcpy(ad
->ad_filler
, ad
->ad_data
+ ADEDOFF_FILLER
, ADEDLEN_FILLER
);
885 adentries
= RSVAL(ad
->ad_data
, ADEDOFF_NENTRIES
);
886 if (adentries
!= nentries
) {
887 DEBUG(1, ("invalid number of entries: %zu\n",
892 /* now, read in the entry bits */
893 for (i
= 0; i
< adentries
; i
++) {
894 eid
= RIVAL(ad
->ad_data
, AD_HEADER_LEN
+ (i
* AD_ENTRY_LEN
));
896 off
= RIVAL(ad
->ad_data
, AD_HEADER_LEN
+ (i
* AD_ENTRY_LEN
) + 4);
897 len
= RIVAL(ad
->ad_data
, AD_HEADER_LEN
+ (i
* AD_ENTRY_LEN
) + 8);
899 if (!eid
|| eid
>= ADEID_MAX
) {
900 DEBUG(1, ("bogus eid %d\n", eid
));
905 * All entries other than the resource fork are
906 * expected to be read into the ad_data buffer, so
907 * ensure the specified offset is within that bound
909 if ((off
> bufsize
) && (eid
!= ADEID_RFORK
)) {
910 DEBUG(1, ("bogus eid %d: off: %" PRIu32
", len: %" PRIu32
"\n",
916 * All entries besides FinderInfo and resource fork
917 * must fit into the buffer. FinderInfo is special as
918 * it may be larger then the default 32 bytes (if it
919 * contains marshalled xattrs), but we will fixup that
920 * in ad_convert(). And the resource fork is never
921 * accessed directly by the ad_data buf (also see
922 * comment above) anyway.
924 if ((eid
!= ADEID_RFORK
) &&
925 (eid
!= ADEID_FINDERI
) &&
926 ((off
+ len
) > bufsize
)) {
927 DEBUG(1, ("bogus eid %d: off: %" PRIu32
", len: %" PRIu32
"\n",
933 * That would be obviously broken
935 if (off
> filesize
) {
936 DEBUG(1, ("bogus eid %d: off: %" PRIu32
", len: %" PRIu32
"\n",
942 * Check for any entry that has its end beyond the
945 if (off
+ len
< off
) {
946 DEBUG(1, ("offset wrap in eid %d: off: %" PRIu32
947 ", len: %" PRIu32
"\n",
952 if (off
+ len
> filesize
) {
954 * If this is the resource fork entry, we fix
955 * up the length, for any other entry we bail
958 if (eid
!= ADEID_RFORK
) {
959 DEBUG(1, ("bogus eid %d: off: %" PRIu32
960 ", len: %" PRIu32
"\n",
966 * Fixup the resource fork entry by limiting
967 * the size to entryoffset - filesize.
969 len
= filesize
- off
;
970 DEBUG(1, ("Limiting ADEID_RFORK: off: %" PRIu32
971 ", len: %" PRIu32
"\n", off
, len
));
974 ad
->ad_eid
[eid
].ade_off
= off
;
975 ad
->ad_eid
[eid
].ade_len
= len
;
978 ok
= ad_unpack_xattrs(ad
);
986 static bool ad_convert_move_reso(struct adouble
*ad
,
987 const struct smb_filename
*smb_fname
)
989 char *map
= MAP_FAILED
;
995 if (ad_getentrylen(ad
, ADEID_RFORK
) == 0) {
999 maplen
= ad_getentryoff(ad
, ADEID_RFORK
) +
1000 ad_getentrylen(ad
, ADEID_RFORK
);
1002 /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
1003 map
= mmap(NULL
, maplen
, PROT_READ
|PROT_WRITE
, MAP_SHARED
,
1005 if (map
== MAP_FAILED
) {
1006 DBG_ERR("mmap AppleDouble: %s\n", strerror(errno
));
1011 memmove(map
+ ADEDOFF_RFORK_DOT_UND
,
1012 map
+ ad_getentryoff(ad
, ADEID_RFORK
),
1013 ad_getentrylen(ad
, ADEID_RFORK
));
1015 rc
= munmap(map
, maplen
);
1017 DBG_ERR("munmap failed: %s\n", strerror(errno
));
1021 ad_setentryoff(ad
, ADEID_RFORK
, ADEDOFF_RFORK_DOT_UND
);
1025 DBG_WARNING("ad_pack [%s] failed\n", smb_fname
->base_name
);
1029 len
= sys_pwrite(ad
->ad_fd
, ad
->ad_data
, AD_DATASZ_DOT_UND
, 0);
1030 if (len
!= AD_DATASZ_DOT_UND
) {
1031 DBG_ERR("%s: bad size: %zd\n", smb_fname
->base_name
, len
);
1038 static bool ad_convert_xattr(struct adouble
*ad
,
1039 const struct smb_filename
*smb_fname
,
1040 bool *converted_xattr
)
1042 static struct char_mappings
**string_replace_cmaps
= NULL
;
1043 char *map
= MAP_FAILED
;
1047 int saved_errno
= 0;
1052 *converted_xattr
= false;
1054 if (ad_getentrylen(ad
, ADEID_FINDERI
) == ADEDLEN_FINDERI
) {
1058 if (string_replace_cmaps
== NULL
) {
1059 const char **mappings
= NULL
;
1061 mappings
= str_list_make_v3_const(
1062 talloc_tos(), fruit_catia_maps
, NULL
);
1063 if (mappings
== NULL
) {
1066 string_replace_cmaps
= string_replace_init_map(mappings
);
1067 TALLOC_FREE(mappings
);
1070 maplen
= ad_getentryoff(ad
, ADEID_RFORK
) +
1071 ad_getentrylen(ad
, ADEID_RFORK
);
1073 /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
1074 map
= mmap(NULL
, maplen
, PROT_READ
|PROT_WRITE
, MAP_SHARED
,
1076 if (map
== MAP_FAILED
) {
1077 DBG_ERR("mmap AppleDouble: %s\n", strerror(errno
));
1081 for (i
= 0; i
< ad
->adx_header
.adx_num_attrs
; i
++) {
1082 struct ad_xattr_entry
*e
= &ad
->adx_entries
[i
];
1083 char *mapped_name
= NULL
;
1085 struct smb_filename
*stream_name
= NULL
;
1086 files_struct
*fsp
= NULL
;
1089 status
= string_replace_allocate(ad
->ad_handle
->conn
,
1091 string_replace_cmaps
,
1094 vfs_translate_to_windows
);
1095 if (!NT_STATUS_IS_OK(status
) &&
1096 !NT_STATUS_EQUAL(status
, NT_STATUS_NONE_MAPPED
))
1098 DBG_ERR("string_replace_allocate failed\n");
1104 mapped_name
= talloc_asprintf(talloc_tos(), ":%s", tmp
);
1106 if (mapped_name
== NULL
) {
1111 stream_name
= synthetic_smb_fname(talloc_tos(),
1112 smb_fname
->base_name
,
1116 TALLOC_FREE(mapped_name
);
1117 if (stream_name
== NULL
) {
1118 DBG_ERR("synthetic_smb_fname failed\n");
1123 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name
));
1125 status
= SMB_VFS_CREATE_FILE(
1126 ad
->ad_handle
->conn
, /* conn */
1128 0, /* root_dir_fid */
1129 stream_name
, /* fname */
1130 FILE_GENERIC_WRITE
, /* access_mask */
1131 FILE_SHARE_READ
| FILE_SHARE_WRITE
, /* share_access */
1132 FILE_OPEN_IF
, /* create_disposition */
1133 0, /* create_options */
1134 0, /* file_attributes */
1135 INTERNAL_OPEN_ONLY
, /* oplock_request */
1137 0, /* allocation_size */
1138 0, /* private_flags */
1143 NULL
, NULL
); /* create context */
1144 TALLOC_FREE(stream_name
);
1145 if (!NT_STATUS_IS_OK(status
)) {
1146 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1151 nwritten
= SMB_VFS_PWRITE(fsp
,
1152 map
+ e
->adx_offset
,
1155 if (nwritten
== -1) {
1156 DBG_ERR("SMB_VFS_PWRITE failed\n");
1157 saved_errno
= errno
;
1158 close_file(NULL
, fsp
, ERROR_CLOSE
);
1159 errno
= saved_errno
;
1164 status
= close_file(NULL
, fsp
, NORMAL_CLOSE
);
1165 if (!NT_STATUS_IS_OK(status
)) {
1172 ad_setentrylen(ad
, ADEID_FINDERI
, ADEDLEN_FINDERI
);
1176 DBG_WARNING("ad_pack [%s] failed\n", smb_fname
->base_name
);
1180 len
= sys_pwrite(ad
->ad_fd
, ad
->ad_data
, AD_DATASZ_DOT_UND
, 0);
1181 if (len
!= AD_DATASZ_DOT_UND
) {
1182 DBG_ERR("%s: bad size: %zd\n", smb_fname
->base_name
, len
);
1187 ok
= ad_convert_move_reso(ad
, smb_fname
);
1192 *converted_xattr
= true;
1196 rc
= munmap(map
, maplen
);
1198 DBG_ERR("munmap failed: %s\n", strerror(errno
));
1205 static bool ad_convert_finderinfo(struct adouble
*ad
,
1206 const struct smb_filename
*smb_fname
)
1211 struct smb_filename
*stream_name
= NULL
;
1212 files_struct
*fsp
= NULL
;
1216 int saved_errno
= 0;
1219 cmp
= memcmp(ad
->ad_filler
, AD_FILLER_TAG_OSX
, ADEDLEN_FILLER
);
1224 p_ad
= ad_get_entry(ad
, ADEID_FINDERI
);
1229 ai
= afpinfo_new(talloc_tos());
1234 memcpy(ai
->afpi_FinderInfo
, p_ad
, ADEDLEN_FINDERI
);
1236 aiblob
= data_blob_talloc(talloc_tos(), NULL
, AFP_INFO_SIZE
);
1237 if (aiblob
.data
== NULL
) {
1242 size
= afpinfo_pack(ai
, (char *)aiblob
.data
);
1244 if (size
!= AFP_INFO_SIZE
) {
1248 stream_name
= synthetic_smb_fname(talloc_tos(),
1249 smb_fname
->base_name
,
1253 if (stream_name
== NULL
) {
1254 data_blob_free(&aiblob
);
1255 DBG_ERR("synthetic_smb_fname failed\n");
1259 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name
));
1261 status
= SMB_VFS_CREATE_FILE(
1262 ad
->ad_handle
->conn
, /* conn */
1264 0, /* root_dir_fid */
1265 stream_name
, /* fname */
1266 FILE_GENERIC_WRITE
, /* access_mask */
1267 FILE_SHARE_READ
| FILE_SHARE_WRITE
, /* share_access */
1268 FILE_OPEN_IF
, /* create_disposition */
1269 0, /* create_options */
1270 0, /* file_attributes */
1271 INTERNAL_OPEN_ONLY
, /* oplock_request */
1273 0, /* allocation_size */
1274 0, /* private_flags */
1279 NULL
, NULL
); /* create context */
1280 TALLOC_FREE(stream_name
);
1281 if (!NT_STATUS_IS_OK(status
)) {
1282 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1286 nwritten
= SMB_VFS_PWRITE(fsp
,
1290 if (nwritten
== -1) {
1291 DBG_ERR("SMB_VFS_PWRITE failed\n");
1292 saved_errno
= errno
;
1293 close_file(NULL
, fsp
, ERROR_CLOSE
);
1294 errno
= saved_errno
;
1298 status
= close_file(NULL
, fsp
, NORMAL_CLOSE
);
1299 if (!NT_STATUS_IS_OK(status
)) {
1307 static bool ad_convert_truncate(struct adouble
*ad
,
1308 const struct smb_filename
*smb_fname
)
1313 * FIXME: direct ftruncate(), but we don't have a fsp for the
1316 rc
= ftruncate(ad
->ad_fd
, ADEDOFF_RFORK_DOT_UND
+
1317 ad_getentrylen(ad
, ADEID_RFORK
));
1325 static bool ad_convert_blank_rfork(struct adouble
*ad
,
1328 struct fruit_config_data
*config
= NULL
;
1329 uint8_t *map
= MAP_FAILED
;
1338 SMB_VFS_HANDLE_GET_DATA(ad
->ad_handle
, config
,
1339 struct fruit_config_data
, return false);
1341 if (!config
->wipe_intentionally_left_blank_rfork
) {
1345 if (ad_getentrylen(ad
, ADEID_RFORK
) != sizeof(empty_resourcefork
)) {
1349 maplen
= ad_getentryoff(ad
, ADEID_RFORK
) +
1350 ad_getentrylen(ad
, ADEID_RFORK
);
1352 /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
1353 map
= mmap(NULL
, maplen
, PROT_READ
|PROT_WRITE
, MAP_SHARED
,
1355 if (map
== MAP_FAILED
) {
1356 DBG_ERR("mmap AppleDouble: %s\n", strerror(errno
));
1360 cmp
= memcmp(map
+ ADEDOFF_RFORK_DOT_UND
,
1362 sizeof(empty_resourcefork
));
1363 rc
= munmap(map
, maplen
);
1365 DBG_ERR("munmap failed: %s\n", strerror(errno
));
1373 ad_setentrylen(ad
, ADEID_RFORK
, 0);
1380 len
= sys_pwrite(ad
->ad_fd
, ad
->ad_data
, AD_DATASZ_DOT_UND
, 0);
1381 if (len
!= AD_DATASZ_DOT_UND
) {
1389 static bool ad_convert_delete_adfile(struct adouble
*ad
,
1390 const struct smb_filename
*smb_fname
)
1392 struct fruit_config_data
*config
= NULL
;
1393 struct smb_filename
*ad_name
= NULL
;
1396 if (ad_getentrylen(ad
, ADEID_RFORK
) > 0) {
1400 SMB_VFS_HANDLE_GET_DATA(ad
->ad_handle
, config
,
1401 struct fruit_config_data
, return false);
1403 if (!config
->delete_empty_adfiles
) {
1407 rc
= adouble_path(talloc_tos(), smb_fname
, &ad_name
);
1412 rc
= SMB_VFS_NEXT_UNLINK(ad
->ad_handle
, ad_name
);
1414 DBG_ERR("Unlinking [%s] failed: %s\n",
1415 smb_fname_str_dbg(ad_name
), strerror(errno
));
1416 TALLOC_FREE(ad_name
);
1420 DBG_WARNING("Unlinked [%s] after conversion\n", smb_fname_str_dbg(ad_name
));
1421 TALLOC_FREE(ad_name
);
1427 * Convert from Apple's ._ file to Netatalk
1429 * Apple's AppleDouble may contain a FinderInfo entry longer then 32
1430 * bytes containing packed xattrs.
1432 * @return -1 in case an error occurred, 0 if no conversion was done, 1
1435 static int ad_convert(struct adouble
*ad
,
1436 const struct smb_filename
*smb_fname
)
1439 bool converted_xattr
= false;
1442 ok
= ad_convert_xattr(ad
, smb_fname
, &converted_xattr
);
1447 ok
= ad_convert_blank_rfork(ad
, &blank
);
1452 if (converted_xattr
|| blank
) {
1453 ok
= ad_convert_truncate(ad
, smb_fname
);
1459 ok
= ad_convert_finderinfo(ad
, smb_fname
);
1461 DBG_ERR("Failed to convert [%s]\n",
1462 smb_fname_str_dbg(smb_fname
));
1466 ok
= ad_convert_delete_adfile(ad
, smb_fname
);
1475 * Read and parse Netatalk AppleDouble metadata xattr
1477 static ssize_t
ad_read_meta(struct adouble
*ad
,
1478 const struct smb_filename
*smb_fname
)
1484 DEBUG(10, ("reading meta xattr for %s\n", smb_fname
->base_name
));
1486 ealen
= SMB_VFS_GETXATTR(ad
->ad_handle
->conn
, smb_fname
,
1487 AFPINFO_EA_NETATALK
, ad
->ad_data
,
1493 if (errno
== ENOATTR
) {
1499 DEBUG(2, ("error reading meta xattr: %s\n",
1505 if (ealen
!= AD_DATASZ_XATTR
) {
1506 DEBUG(2, ("bad size %zd\n", ealen
));
1512 /* Now parse entries */
1513 ok
= ad_unpack(ad
, ADEID_NUM_XATTR
, AD_DATASZ_XATTR
);
1515 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1521 if (!ad_getentryoff(ad
, ADEID_FINDERI
)
1522 || !ad_getentryoff(ad
, ADEID_COMMENT
)
1523 || !ad_getentryoff(ad
, ADEID_FILEDATESI
)
1524 || !ad_getentryoff(ad
, ADEID_AFPFILEI
)
1525 || !ad_getentryoff(ad
, ADEID_PRIVDEV
)
1526 || !ad_getentryoff(ad
, ADEID_PRIVINO
)
1527 || !ad_getentryoff(ad
, ADEID_PRIVSYN
)
1528 || !ad_getentryoff(ad
, ADEID_PRIVID
)) {
1529 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1536 DEBUG(10, ("reading meta xattr for %s, rc: %d\n",
1537 smb_fname
->base_name
, rc
));
1541 if (errno
== EINVAL
) {
1543 removexattr(smb_fname
->base_name
, AFPINFO_EA_NETATALK
);
1551 static int ad_open_rsrc_xattr(const struct smb_filename
*smb_fname
,
1555 #ifdef HAVE_ATTROPEN
1556 /* FIXME: direct Solaris xattr syscall */
1557 return attropen(smb_fname
->base_name
,
1558 AFPRESOURCE_EA_NETATALK
, flags
, mode
);
1565 static int ad_open_rsrc_adouble(const struct smb_filename
*smb_fname
,
1571 struct smb_filename
*adp_smb_fname
= NULL
;
1573 ret
= adouble_path(talloc_tos(), smb_fname
, &adp_smb_fname
);
1578 fd
= open(adp_smb_fname
->base_name
, flags
, mode
);
1579 TALLOC_FREE(adp_smb_fname
);
1584 static int ad_open_rsrc(vfs_handle_struct
*handle
,
1585 const struct smb_filename
*smb_fname
,
1589 struct fruit_config_data
*config
= NULL
;
1592 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
1593 struct fruit_config_data
, return -1);
1595 if (config
->rsrc
== FRUIT_RSRC_XATTR
) {
1596 fd
= ad_open_rsrc_xattr(smb_fname
, flags
, mode
);
1598 fd
= ad_open_rsrc_adouble(smb_fname
, flags
, mode
);
1605 * Here's the deal: for ADOUBLE_META we can do without an fd as we can issue
1606 * path based xattr calls. For ADOUBLE_RSRC however we need a full-fledged fd
1607 * for file IO on the ._ file.
1609 static int ad_open(vfs_handle_struct
*handle
,
1612 const struct smb_filename
*smb_fname
,
1618 DBG_DEBUG("Path [%s] type [%s]\n", smb_fname
->base_name
,
1619 ad
->ad_type
== ADOUBLE_META
? "meta" : "rsrc");
1621 if (ad
->ad_type
== ADOUBLE_META
) {
1625 if ((fsp
!= NULL
) && (fsp
->fh
!= NULL
) && (fsp
->fh
->fd
!= -1)) {
1626 ad
->ad_fd
= fsp
->fh
->fd
;
1627 ad
->ad_opened
= false;
1631 fd
= ad_open_rsrc(handle
, smb_fname
, flags
, mode
);
1635 ad
->ad_opened
= true;
1638 DBG_DEBUG("Path [%s] type [%s] fd [%d]\n",
1639 smb_fname
->base_name
,
1640 ad
->ad_type
== ADOUBLE_META
? "meta" : "rsrc", fd
);
1645 static ssize_t
ad_read_rsrc_xattr(struct adouble
*ad
)
1650 /* FIXME: direct sys_fstat(), don't have an fsp */
1651 ret
= sys_fstat(ad
->ad_fd
, &st
,
1652 lp_fake_directory_create_times(
1653 SNUM(ad
->ad_handle
->conn
)));
1658 ad_setentrylen(ad
, ADEID_RFORK
, st
.st_ex_size
);
1659 return st
.st_ex_size
;
1662 static ssize_t
ad_read_rsrc_adouble(struct adouble
*ad
,
1663 const struct smb_filename
*smb_fname
)
1665 SMB_STRUCT_STAT sbuf
;
1672 ret
= sys_fstat(ad
->ad_fd
, &sbuf
, lp_fake_directory_create_times(
1673 SNUM(ad
->ad_handle
->conn
)));
1679 * AppleDouble file header content and size, two cases:
1681 * - without xattrs it is exactly AD_DATASZ_DOT_UND (82) bytes large
1682 * - with embedded xattrs it can be larger, up to AD_XATTR_MAX_HDR_SIZE
1684 * Read as much as we can up to AD_XATTR_MAX_HDR_SIZE.
1686 size
= sbuf
.st_ex_size
;
1687 if (size
> talloc_array_length(ad
->ad_data
)) {
1688 if (size
> AD_XATTR_MAX_HDR_SIZE
) {
1689 size
= AD_XATTR_MAX_HDR_SIZE
;
1691 p_ad
= talloc_realloc(ad
, ad
->ad_data
, char, size
);
1698 len
= sys_pread(ad
->ad_fd
, ad
->ad_data
,
1699 talloc_array_length(ad
->ad_data
), 0);
1700 if (len
!= talloc_array_length(ad
->ad_data
)) {
1701 DBG_NOTICE("%s %s: bad size: %zd\n",
1702 smb_fname
->base_name
, strerror(errno
), len
);
1706 /* Now parse entries */
1707 ok
= ad_unpack(ad
, ADEID_NUM_DOT_UND
, sbuf
.st_ex_size
);
1709 DBG_ERR("invalid AppleDouble resource %s\n",
1710 smb_fname
->base_name
);
1715 if ((ad_getentryoff(ad
, ADEID_FINDERI
) != ADEDOFF_FINDERI_DOT_UND
)
1716 || (ad_getentrylen(ad
, ADEID_FINDERI
) < ADEDLEN_FINDERI
)
1717 || (ad_getentryoff(ad
, ADEID_RFORK
) < ADEDOFF_RFORK_DOT_UND
)) {
1718 DBG_ERR("invalid AppleDouble resource %s\n",
1719 smb_fname
->base_name
);
1725 * Try to fixup AppleDouble files created by OS X with xattrs
1726 * appended to the ADEID_FINDERI entry.
1729 ret
= ad_convert(ad
, smb_fname
);
1731 DBG_WARNING("Failed to convert [%s]\n", smb_fname
->base_name
);
1739 * Read and parse resource fork, either ._ AppleDouble file or xattr
1741 static ssize_t
ad_read_rsrc(struct adouble
*ad
,
1742 const struct smb_filename
*smb_fname
)
1744 struct fruit_config_data
*config
= NULL
;
1747 SMB_VFS_HANDLE_GET_DATA(ad
->ad_handle
, config
,
1748 struct fruit_config_data
, return -1);
1750 if (config
->rsrc
== FRUIT_RSRC_XATTR
) {
1751 len
= ad_read_rsrc_xattr(ad
);
1753 len
= ad_read_rsrc_adouble(ad
, smb_fname
);
1760 * Read and unpack an AppleDouble metadata xattr or resource
1762 static ssize_t
ad_read(struct adouble
*ad
, const struct smb_filename
*smb_fname
)
1764 switch (ad
->ad_type
) {
1766 return ad_read_meta(ad
, smb_fname
);
1768 return ad_read_rsrc(ad
, smb_fname
);
1774 static int adouble_destructor(struct adouble
*ad
)
1776 if ((ad
->ad_fd
!= -1) && ad
->ad_opened
) {
1784 * Allocate a struct adouble without initialiing it
1786 * The struct is either hang of the fsp extension context or if fsp is
1789 * @param[in] ctx talloc context
1790 * @param[in] handle vfs handle
1791 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1793 * @return adouble handle
1795 static struct adouble
*ad_alloc(TALLOC_CTX
*ctx
, vfs_handle_struct
*handle
,
1796 adouble_type_t type
)
1801 struct fruit_config_data
*config
;
1803 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
1804 struct fruit_config_data
, return NULL
);
1808 adsize
= AD_DATASZ_XATTR
;
1811 if (config
->rsrc
== FRUIT_RSRC_ADFILE
) {
1812 adsize
= AD_DATASZ_DOT_UND
;
1819 ad
= talloc_zero(ctx
, struct adouble
);
1826 ad
->ad_data
= talloc_zero_array(ad
, char, adsize
);
1827 if (ad
->ad_data
== NULL
) {
1833 ad
->ad_handle
= handle
;
1835 ad
->ad_magic
= AD_MAGIC
;
1836 ad
->ad_version
= AD_VERSION
;
1839 talloc_set_destructor(ad
, adouble_destructor
);
1849 * Allocate and initialize a new struct adouble
1851 * @param[in] ctx talloc context
1852 * @param[in] handle vfs handle
1853 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1855 * @return adouble handle, initialized
1857 static struct adouble
*ad_init(TALLOC_CTX
*ctx
, vfs_handle_struct
*handle
,
1858 adouble_type_t type
)
1861 const struct ad_entry_order
*eid
;
1862 struct adouble
*ad
= NULL
;
1863 struct fruit_config_data
*config
;
1864 time_t t
= time(NULL
);
1866 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
1867 struct fruit_config_data
, return NULL
);
1871 eid
= entry_order_meta_xattr
;
1874 if (config
->rsrc
== FRUIT_RSRC_ADFILE
) {
1875 eid
= entry_order_dot_und
;
1877 eid
= entry_order_rsrc_xattr
;
1884 ad
= ad_alloc(ctx
, handle
, type
);
1890 ad
->ad_eid
[eid
->id
].ade_off
= eid
->offset
;
1891 ad
->ad_eid
[eid
->id
].ade_len
= eid
->len
;
1895 /* put something sane in the date fields */
1896 ad_setdate(ad
, AD_DATE_CREATE
| AD_DATE_UNIX
, t
);
1897 ad_setdate(ad
, AD_DATE_MODIFY
| AD_DATE_UNIX
, t
);
1898 ad_setdate(ad
, AD_DATE_ACCESS
| AD_DATE_UNIX
, t
);
1899 ad_setdate(ad
, AD_DATE_BACKUP
, htonl(AD_DATE_START
));
1907 static struct adouble
*ad_get_internal(TALLOC_CTX
*ctx
,
1908 vfs_handle_struct
*handle
,
1910 const struct smb_filename
*smb_fname
,
1911 adouble_type_t type
)
1915 struct adouble
*ad
= NULL
;
1919 smb_fname
= fsp
->base_fsp
->fsp_name
;
1922 DEBUG(10, ("ad_get(%s) called for %s\n",
1923 type
== ADOUBLE_META
? "meta" : "rsrc",
1924 smb_fname
->base_name
));
1926 ad
= ad_alloc(ctx
, handle
, type
);
1932 /* Try rw first so we can use the fd in ad_convert() */
1935 rc
= ad_open(handle
, ad
, fsp
, smb_fname
, mode
, 0);
1936 if (rc
== -1 && ((errno
== EROFS
) || (errno
== EACCES
))) {
1938 rc
= ad_open(handle
, ad
, fsp
, smb_fname
, mode
, 0);
1941 DBG_DEBUG("ad_open [%s] error [%s]\n",
1942 smb_fname
->base_name
, strerror(errno
));
1947 len
= ad_read(ad
, smb_fname
);
1949 DEBUG(10, ("error reading AppleDouble for %s\n",
1950 smb_fname
->base_name
));
1956 DEBUG(10, ("ad_get(%s) for %s returning %d\n",
1957 type
== ADOUBLE_META
? "meta" : "rsrc",
1958 smb_fname
->base_name
, rc
));
1967 * Return AppleDouble data for a file
1969 * @param[in] ctx talloc context
1970 * @param[in] handle vfs handle
1971 * @param[in] smb_fname pathname to file or directory
1972 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1974 * @return talloced struct adouble or NULL on error
1976 static struct adouble
*ad_get(TALLOC_CTX
*ctx
,
1977 vfs_handle_struct
*handle
,
1978 const struct smb_filename
*smb_fname
,
1979 adouble_type_t type
)
1981 return ad_get_internal(ctx
, handle
, NULL
, smb_fname
, type
);
1985 * Return AppleDouble data for a file
1987 * @param[in] ctx talloc context
1988 * @param[in] handle vfs handle
1989 * @param[in] fsp fsp to use for IO
1990 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1992 * @return talloced struct adouble or NULL on error
1994 static struct adouble
*ad_fget(TALLOC_CTX
*ctx
, vfs_handle_struct
*handle
,
1995 files_struct
*fsp
, adouble_type_t type
)
1997 return ad_get_internal(ctx
, handle
, fsp
, NULL
, type
);
2001 * Set AppleDouble metadata on a file or directory
2003 * @param[in] ad adouble handle
2005 * @param[in] smb_fname pathname to file or directory
2007 * @return status code, 0 means success
2009 static int ad_set(struct adouble
*ad
, const struct smb_filename
*smb_fname
)
2014 DBG_DEBUG("Path [%s]\n", smb_fname
->base_name
);
2016 if (ad
->ad_type
!= ADOUBLE_META
) {
2017 DBG_ERR("ad_set on [%s] used with ADOUBLE_RSRC\n",
2018 smb_fname
->base_name
);
2027 ret
= SMB_VFS_SETXATTR(ad
->ad_handle
->conn
,
2029 AFPINFO_EA_NETATALK
,
2031 AD_DATASZ_XATTR
, 0);
2033 DBG_DEBUG("Path [%s] ret [%d]\n", smb_fname
->base_name
, ret
);
2039 * Set AppleDouble metadata on a file or directory
2041 * @param[in] ad adouble handle
2042 * @param[in] fsp file handle
2044 * @return status code, 0 means success
2046 static int ad_fset(struct adouble
*ad
, files_struct
*fsp
)
2052 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp
));
2055 || (fsp
->fh
== NULL
)
2056 || (fsp
->fh
->fd
== -1))
2058 smb_panic("bad fsp");
2066 switch (ad
->ad_type
) {
2068 rc
= SMB_VFS_NEXT_SETXATTR(ad
->ad_handle
,
2070 AFPINFO_EA_NETATALK
,
2072 AD_DATASZ_XATTR
, 0);
2076 len
= SMB_VFS_NEXT_PWRITE(ad
->ad_handle
,
2081 if (len
!= AD_DATASZ_DOT_UND
) {
2082 DBG_ERR("short write on %s: %zd", fsp_str_dbg(fsp
), len
);
2092 DBG_DEBUG("Path [%s] rc [%d]\n", fsp_str_dbg(fsp
), rc
);
2097 /*****************************************************************************
2099 *****************************************************************************/
2101 static bool is_afpinfo_stream(const struct smb_filename
*smb_fname
)
2103 if (strncasecmp_m(smb_fname
->stream_name
,
2104 AFPINFO_STREAM_NAME
,
2105 strlen(AFPINFO_STREAM_NAME
)) == 0) {
2111 static bool is_afpresource_stream(const struct smb_filename
*smb_fname
)
2113 if (strncasecmp_m(smb_fname
->stream_name
,
2114 AFPRESOURCE_STREAM_NAME
,
2115 strlen(AFPRESOURCE_STREAM_NAME
)) == 0) {
2122 * Test whether stream is an Apple stream, not used atm
2125 static bool is_apple_stream(const struct smb_filename
*smb_fname
)
2127 if (is_afpinfo_stream(smb_fname
)) {
2130 if (is_afpresource_stream(smb_fname
)) {
2138 * Initialize config struct from our smb.conf config parameters
2140 static int init_fruit_config(vfs_handle_struct
*handle
)
2142 struct fruit_config_data
*config
;
2144 const char *tm_size_str
= NULL
;
2146 config
= talloc_zero(handle
->conn
, struct fruit_config_data
);
2148 DEBUG(1, ("talloc_zero() failed\n"));
2154 * Versions up to Samba 4.5.x had a spelling bug in the
2155 * fruit:resource option calling lp_parm_enum with
2156 * "res*s*ource" (ie two s).
2158 * In Samba 4.6 we accept both the wrong and the correct
2159 * spelling, in Samba 4.7 the bad spelling will be removed.
2161 enumval
= lp_parm_enum(SNUM(handle
->conn
), FRUIT_PARAM_TYPE_NAME
,
2162 "ressource", fruit_rsrc
, FRUIT_RSRC_ADFILE
);
2163 if (enumval
== -1) {
2164 DEBUG(1, ("value for %s: resource type unknown\n",
2165 FRUIT_PARAM_TYPE_NAME
));
2168 config
->rsrc
= (enum fruit_rsrc
)enumval
;
2170 enumval
= lp_parm_enum(SNUM(handle
->conn
), FRUIT_PARAM_TYPE_NAME
,
2171 "resource", fruit_rsrc
, enumval
);
2172 if (enumval
== -1) {
2173 DEBUG(1, ("value for %s: resource type unknown\n",
2174 FRUIT_PARAM_TYPE_NAME
));
2177 config
->rsrc
= (enum fruit_rsrc
)enumval
;
2179 enumval
= lp_parm_enum(SNUM(handle
->conn
), FRUIT_PARAM_TYPE_NAME
,
2180 "metadata", fruit_meta
, FRUIT_META_NETATALK
);
2181 if (enumval
== -1) {
2182 DEBUG(1, ("value for %s: metadata type unknown\n",
2183 FRUIT_PARAM_TYPE_NAME
));
2186 config
->meta
= (enum fruit_meta
)enumval
;
2188 enumval
= lp_parm_enum(SNUM(handle
->conn
), FRUIT_PARAM_TYPE_NAME
,
2189 "locking", fruit_locking
, FRUIT_LOCKING_NONE
);
2190 if (enumval
== -1) {
2191 DEBUG(1, ("value for %s: locking type unknown\n",
2192 FRUIT_PARAM_TYPE_NAME
));
2195 config
->locking
= (enum fruit_locking
)enumval
;
2197 enumval
= lp_parm_enum(SNUM(handle
->conn
), FRUIT_PARAM_TYPE_NAME
,
2198 "encoding", fruit_encoding
, FRUIT_ENC_PRIVATE
);
2199 if (enumval
== -1) {
2200 DEBUG(1, ("value for %s: encoding type unknown\n",
2201 FRUIT_PARAM_TYPE_NAME
));
2204 config
->encoding
= (enum fruit_encoding
)enumval
;
2206 if (config
->rsrc
== FRUIT_RSRC_ADFILE
) {
2207 config
->veto_appledouble
= lp_parm_bool(SNUM(handle
->conn
),
2208 FRUIT_PARAM_TYPE_NAME
,
2213 config
->use_aapl
= lp_parm_bool(
2214 -1, FRUIT_PARAM_TYPE_NAME
, "aapl", true);
2216 config
->time_machine
= lp_parm_bool(
2217 SNUM(handle
->conn
), FRUIT_PARAM_TYPE_NAME
, "time machine", false);
2219 config
->unix_info_enabled
= lp_parm_bool(
2220 -1, FRUIT_PARAM_TYPE_NAME
, "nfs_aces", true);
2222 config
->use_copyfile
= lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME
,
2225 config
->posix_rename
= lp_parm_bool(
2226 SNUM(handle
->conn
), FRUIT_PARAM_TYPE_NAME
, "posix_rename", true);
2228 config
->aapl_zero_file_id
=
2229 lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME
, "zero_file_id", true);
2231 config
->readdir_attr_rsize
= lp_parm_bool(
2232 SNUM(handle
->conn
), "readdir_attr", "aapl_rsize", true);
2234 config
->readdir_attr_finder_info
= lp_parm_bool(
2235 SNUM(handle
->conn
), "readdir_attr", "aapl_finder_info", true);
2237 config
->readdir_attr_max_access
= lp_parm_bool(
2238 SNUM(handle
->conn
), "readdir_attr", "aapl_max_access", true);
2240 config
->model
= lp_parm_const_string(
2241 -1, FRUIT_PARAM_TYPE_NAME
, "model", "MacSamba");
2243 tm_size_str
= lp_parm_const_string(
2244 SNUM(handle
->conn
), FRUIT_PARAM_TYPE_NAME
,
2245 "time machine max size", NULL
);
2246 if (tm_size_str
!= NULL
) {
2247 config
->time_machine_max_size
= conv_str_size(tm_size_str
);
2250 config
->wipe_intentionally_left_blank_rfork
= lp_parm_bool(
2251 SNUM(handle
->conn
), FRUIT_PARAM_TYPE_NAME
,
2252 "wipe_intentionally_left_blank_rfork", false);
2254 config
->delete_empty_adfiles
= lp_parm_bool(
2255 SNUM(handle
->conn
), FRUIT_PARAM_TYPE_NAME
,
2256 "delete_empty_adfiles", false);
2258 SMB_VFS_HANDLE_SET_DATA(handle
, config
,
2259 NULL
, struct fruit_config_data
,
2266 * Prepend "._" to a basename
2267 * Return a new struct smb_filename with stream_name == NULL.
2269 static int adouble_path(TALLOC_CTX
*ctx
,
2270 const struct smb_filename
*smb_fname_in
,
2271 struct smb_filename
**pp_smb_fname_out
)
2275 struct smb_filename
*smb_fname
= cp_smb_filename(ctx
,
2278 if (smb_fname
== NULL
) {
2282 /* We need streamname to be NULL */
2283 TALLOC_FREE(smb_fname
->stream_name
);
2285 /* And we're replacing base_name. */
2286 TALLOC_FREE(smb_fname
->base_name
);
2288 if (!parent_dirname(smb_fname
, smb_fname_in
->base_name
,
2290 TALLOC_FREE(smb_fname
);
2294 smb_fname
->base_name
= talloc_asprintf(smb_fname
,
2295 "%s/._%s", parent
, base
);
2296 if (smb_fname
->base_name
== NULL
) {
2297 TALLOC_FREE(smb_fname
);
2301 *pp_smb_fname_out
= smb_fname
;
2307 * Allocate and initialize an AfpInfo struct
2309 static AfpInfo
*afpinfo_new(TALLOC_CTX
*ctx
)
2311 AfpInfo
*ai
= talloc_zero(ctx
, AfpInfo
);
2315 ai
->afpi_Signature
= AFP_Signature
;
2316 ai
->afpi_Version
= AFP_Version
;
2317 ai
->afpi_BackupTime
= AD_DATE_START
;
2322 * Pack an AfpInfo struct into a buffer
2324 * Buffer size must be at least AFP_INFO_SIZE
2325 * Returns size of packed buffer
2327 static ssize_t
afpinfo_pack(const AfpInfo
*ai
, char *buf
)
2329 memset(buf
, 0, AFP_INFO_SIZE
);
2331 RSIVAL(buf
, 0, ai
->afpi_Signature
);
2332 RSIVAL(buf
, 4, ai
->afpi_Version
);
2333 RSIVAL(buf
, 12, ai
->afpi_BackupTime
);
2334 memcpy(buf
+ 16, ai
->afpi_FinderInfo
, sizeof(ai
->afpi_FinderInfo
));
2336 return AFP_INFO_SIZE
;
2340 * Unpack a buffer into a AfpInfo structure
2342 * Buffer size must be at least AFP_INFO_SIZE
2343 * Returns allocated AfpInfo struct
2345 static AfpInfo
*afpinfo_unpack(TALLOC_CTX
*ctx
, const void *data
)
2347 AfpInfo
*ai
= talloc_zero(ctx
, AfpInfo
);
2352 ai
->afpi_Signature
= RIVAL(data
, 0);
2353 ai
->afpi_Version
= RIVAL(data
, 4);
2354 ai
->afpi_BackupTime
= RIVAL(data
, 12);
2355 memcpy(ai
->afpi_FinderInfo
, (const char *)data
+ 16,
2356 sizeof(ai
->afpi_FinderInfo
));
2358 if (ai
->afpi_Signature
!= AFP_Signature
2359 || ai
->afpi_Version
!= AFP_Version
) {
2360 DEBUG(1, ("Bad AfpInfo signature or version\n"));
2368 * Fake an inode number from the md5 hash of the (xattr) name
2370 static SMB_INO_T
fruit_inode(const SMB_STRUCT_STAT
*sbuf
, const char *sname
)
2373 unsigned char hash
[16];
2377 upper_sname
= talloc_strdup_upper(talloc_tos(), sname
);
2378 SMB_ASSERT(upper_sname
!= NULL
);
2381 MD5Update(&ctx
, (const unsigned char *)&(sbuf
->st_ex_dev
),
2382 sizeof(sbuf
->st_ex_dev
));
2383 MD5Update(&ctx
, (const unsigned char *)&(sbuf
->st_ex_ino
),
2384 sizeof(sbuf
->st_ex_ino
));
2385 MD5Update(&ctx
, (unsigned char *)upper_sname
,
2386 talloc_get_size(upper_sname
)-1);
2387 MD5Final(hash
, &ctx
);
2389 TALLOC_FREE(upper_sname
);
2391 /* Hopefully all the variation is in the lower 4 (or 8) bytes! */
2392 memcpy(&result
, hash
, sizeof(result
));
2394 DEBUG(10, ("fruit_inode \"%s\": ino=0x%llu\n",
2395 sname
, (unsigned long long)result
));
2400 static bool add_fruit_stream(TALLOC_CTX
*mem_ctx
, unsigned int *num_streams
,
2401 struct stream_struct
**streams
,
2402 const char *name
, off_t size
,
2405 struct stream_struct
*tmp
;
2407 tmp
= talloc_realloc(mem_ctx
, *streams
, struct stream_struct
,
2413 tmp
[*num_streams
].name
= talloc_asprintf(tmp
, "%s:$DATA", name
);
2414 if (tmp
[*num_streams
].name
== NULL
) {
2418 tmp
[*num_streams
].size
= size
;
2419 tmp
[*num_streams
].alloc_size
= alloc_size
;
2426 static bool filter_empty_rsrc_stream(unsigned int *num_streams
,
2427 struct stream_struct
**streams
)
2429 struct stream_struct
*tmp
= *streams
;
2432 if (*num_streams
== 0) {
2436 for (i
= 0; i
< *num_streams
; i
++) {
2437 if (strequal_m(tmp
[i
].name
, AFPRESOURCE_STREAM
)) {
2442 if (i
== *num_streams
) {
2446 if (tmp
[i
].size
> 0) {
2450 TALLOC_FREE(tmp
[i
].name
);
2451 if (*num_streams
- 1 > i
) {
2452 memmove(&tmp
[i
], &tmp
[i
+1],
2453 (*num_streams
- i
- 1) * sizeof(struct stream_struct
));
2460 static bool del_fruit_stream(TALLOC_CTX
*mem_ctx
, unsigned int *num_streams
,
2461 struct stream_struct
**streams
,
2464 struct stream_struct
*tmp
= *streams
;
2467 if (*num_streams
== 0) {
2471 for (i
= 0; i
< *num_streams
; i
++) {
2472 if (strequal_m(tmp
[i
].name
, name
)) {
2477 if (i
== *num_streams
) {
2481 TALLOC_FREE(tmp
[i
].name
);
2482 if (*num_streams
- 1 > i
) {
2483 memmove(&tmp
[i
], &tmp
[i
+1],
2484 (*num_streams
- i
- 1) * sizeof(struct stream_struct
));
2491 static bool ad_empty_finderinfo(const struct adouble
*ad
)
2494 char emptybuf
[ADEDLEN_FINDERI
] = {0};
2497 fi
= ad_get_entry(ad
, ADEID_FINDERI
);
2499 DBG_ERR("Missing FinderInfo in struct adouble [%p]\n", ad
);
2503 cmp
= memcmp(emptybuf
, fi
, ADEDLEN_FINDERI
);
2507 static bool ai_empty_finderinfo(const AfpInfo
*ai
)
2510 char emptybuf
[ADEDLEN_FINDERI
] = {0};
2512 cmp
= memcmp(emptybuf
, &ai
->afpi_FinderInfo
[0], ADEDLEN_FINDERI
);
2517 * Update btime with btime from Netatalk
2519 static void update_btime(vfs_handle_struct
*handle
,
2520 struct smb_filename
*smb_fname
)
2523 struct timespec creation_time
= {0};
2525 struct fruit_config_data
*config
= NULL
;
2527 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct fruit_config_data
,
2530 switch (config
->meta
) {
2531 case FRUIT_META_STREAM
:
2533 case FRUIT_META_NETATALK
:
2537 DBG_ERR("Unexpected meta config [%d]\n", config
->meta
);
2541 ad
= ad_get(talloc_tos(), handle
, smb_fname
, ADOUBLE_META
);
2545 if (ad_getdate(ad
, AD_DATE_UNIX
| AD_DATE_CREATE
, &t
) != 0) {
2551 creation_time
.tv_sec
= convert_uint32_t_to_time_t(t
);
2552 update_stat_ex_create_time(&smb_fname
->st
, creation_time
);
2558 * Map an access mask to a Netatalk single byte byte range lock
2560 static off_t
access_to_netatalk_brl(enum apple_fork fork_type
,
2561 uint32_t access_mask
)
2565 switch (access_mask
) {
2566 case FILE_READ_DATA
:
2567 offset
= AD_FILELOCK_OPEN_RD
;
2570 case FILE_WRITE_DATA
:
2571 case FILE_APPEND_DATA
:
2572 offset
= AD_FILELOCK_OPEN_WR
;
2576 offset
= AD_FILELOCK_OPEN_NONE
;
2580 if (fork_type
== APPLE_FORK_RSRC
) {
2581 if (offset
== AD_FILELOCK_OPEN_NONE
) {
2582 offset
= AD_FILELOCK_RSRC_OPEN_NONE
;
2592 * Map a deny mode to a Netatalk brl
2594 static off_t
denymode_to_netatalk_brl(enum apple_fork fork_type
,
2599 switch (deny_mode
) {
2601 offset
= AD_FILELOCK_DENY_RD
;
2605 offset
= AD_FILELOCK_DENY_WR
;
2609 smb_panic("denymode_to_netatalk_brl: bad deny mode\n");
2612 if (fork_type
== APPLE_FORK_RSRC
) {
2620 * Call fcntl() with an exclusive F_GETLK request in order to
2621 * determine if there's an exisiting shared lock
2623 * @return true if the requested lock was found or any error occurred
2624 * false if the lock was not found
2626 static bool test_netatalk_lock(files_struct
*fsp
, off_t in_offset
)
2629 off_t offset
= in_offset
;
2634 result
= SMB_VFS_GETLOCK(fsp
, &offset
, &len
, &type
, &pid
);
2635 if (result
== false) {
2639 if (type
!= F_UNLCK
) {
2646 static NTSTATUS
fruit_check_access(vfs_handle_struct
*handle
,
2648 uint32_t access_mask
,
2651 NTSTATUS status
= NT_STATUS_OK
;
2652 bool open_for_reading
, open_for_writing
, deny_read
, deny_write
;
2654 bool have_read
= false;
2657 /* FIXME: hardcoded data fork, add resource fork */
2658 enum apple_fork fork_type
= APPLE_FORK_DATA
;
2660 DEBUG(10, ("fruit_check_access: %s, am: %s/%s, dm: %s/%s\n",
2662 access_mask
& FILE_READ_DATA
? "READ" :"-",
2663 access_mask
& FILE_WRITE_DATA
? "WRITE" : "-",
2664 deny_mode
& DENY_READ
? "DENY_READ" : "-",
2665 deny_mode
& DENY_WRITE
? "DENY_WRITE" : "-"));
2667 if (fsp
->fh
->fd
== -1) {
2668 return NT_STATUS_OK
;
2671 flags
= fcntl(fsp
->fh
->fd
, F_GETFL
);
2673 DBG_ERR("fcntl get flags [%s] fd [%d] failed [%s]\n",
2674 fsp_str_dbg(fsp
), fsp
->fh
->fd
, strerror(errno
));
2675 return map_nt_error_from_unix(errno
);
2678 if (flags
& (O_RDONLY
|O_RDWR
)) {
2680 * Applying fcntl read locks requires an fd opened for
2681 * reading. This means we won't be applying locks for
2682 * files openend write-only, but what can we do...
2688 * Check read access and deny read mode
2690 if ((access_mask
& FILE_READ_DATA
) || (deny_mode
& DENY_READ
)) {
2692 open_for_reading
= test_netatalk_lock(
2693 fsp
, access_to_netatalk_brl(fork_type
, FILE_READ_DATA
));
2695 deny_read
= test_netatalk_lock(
2696 fsp
, denymode_to_netatalk_brl(fork_type
, DENY_READ
));
2698 DEBUG(10, ("read: %s, deny_write: %s\n",
2699 open_for_reading
== true ? "yes" : "no",
2700 deny_read
== true ? "yes" : "no"));
2702 if (((access_mask
& FILE_READ_DATA
) && deny_read
)
2703 || ((deny_mode
& DENY_READ
) && open_for_reading
)) {
2704 return NT_STATUS_SHARING_VIOLATION
;
2708 if ((access_mask
& FILE_READ_DATA
) && have_read
) {
2709 struct byte_range_lock
*br_lck
= NULL
;
2711 off
= access_to_netatalk_brl(fork_type
, FILE_READ_DATA
);
2713 handle
->conn
->sconn
->msg_ctx
, fsp
,
2714 fsp
->op
->global
->open_persistent_id
, 1, off
,
2715 READ_LOCK
, POSIX_LOCK
, false,
2718 TALLOC_FREE(br_lck
);
2720 if (!NT_STATUS_IS_OK(status
)) {
2725 if ((deny_mode
& DENY_READ
) && have_read
) {
2726 struct byte_range_lock
*br_lck
= NULL
;
2728 off
= denymode_to_netatalk_brl(fork_type
, DENY_READ
);
2730 handle
->conn
->sconn
->msg_ctx
, fsp
,
2731 fsp
->op
->global
->open_persistent_id
, 1, off
,
2732 READ_LOCK
, POSIX_LOCK
, false,
2735 TALLOC_FREE(br_lck
);
2737 if (!NT_STATUS_IS_OK(status
)) {
2744 * Check write access and deny write mode
2746 if ((access_mask
& FILE_WRITE_DATA
) || (deny_mode
& DENY_WRITE
)) {
2748 open_for_writing
= test_netatalk_lock(
2749 fsp
, access_to_netatalk_brl(fork_type
, FILE_WRITE_DATA
));
2751 deny_write
= test_netatalk_lock(
2752 fsp
, denymode_to_netatalk_brl(fork_type
, DENY_WRITE
));
2754 DEBUG(10, ("write: %s, deny_write: %s\n",
2755 open_for_writing
== true ? "yes" : "no",
2756 deny_write
== true ? "yes" : "no"));
2758 if (((access_mask
& FILE_WRITE_DATA
) && deny_write
)
2759 || ((deny_mode
& DENY_WRITE
) && open_for_writing
)) {
2760 return NT_STATUS_SHARING_VIOLATION
;
2764 if ((access_mask
& FILE_WRITE_DATA
) && have_read
) {
2765 struct byte_range_lock
*br_lck
= NULL
;
2767 off
= access_to_netatalk_brl(fork_type
, FILE_WRITE_DATA
);
2769 handle
->conn
->sconn
->msg_ctx
, fsp
,
2770 fsp
->op
->global
->open_persistent_id
, 1, off
,
2771 READ_LOCK
, POSIX_LOCK
, false,
2774 TALLOC_FREE(br_lck
);
2776 if (!NT_STATUS_IS_OK(status
)) {
2780 if ((deny_mode
& DENY_WRITE
) && have_read
) {
2781 struct byte_range_lock
*br_lck
= NULL
;
2783 off
= denymode_to_netatalk_brl(fork_type
, DENY_WRITE
);
2785 handle
->conn
->sconn
->msg_ctx
, fsp
,
2786 fsp
->op
->global
->open_persistent_id
, 1, off
,
2787 READ_LOCK
, POSIX_LOCK
, false,
2790 TALLOC_FREE(br_lck
);
2792 if (!NT_STATUS_IS_OK(status
)) {
2801 static NTSTATUS
check_aapl(vfs_handle_struct
*handle
,
2802 struct smb_request
*req
,
2803 const struct smb2_create_blobs
*in_context_blobs
,
2804 struct smb2_create_blobs
*out_context_blobs
)
2806 struct fruit_config_data
*config
;
2808 struct smb2_create_blob
*aapl
= NULL
;
2812 DATA_BLOB blob
= data_blob_talloc(req
, NULL
, 0);
2813 uint64_t req_bitmap
, client_caps
;
2814 uint64_t server_caps
= SMB2_CRTCTX_AAPL_UNIX_BASED
;
2818 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct fruit_config_data
,
2819 return NT_STATUS_UNSUCCESSFUL
);
2821 if (!config
->use_aapl
2822 || in_context_blobs
== NULL
2823 || out_context_blobs
== NULL
) {
2824 return NT_STATUS_OK
;
2827 aapl
= smb2_create_blob_find(in_context_blobs
,
2828 SMB2_CREATE_TAG_AAPL
);
2830 return NT_STATUS_OK
;
2833 if (aapl
->data
.length
!= 24) {
2834 DEBUG(1, ("unexpected AAPL ctxt length: %ju\n",
2835 (uintmax_t)aapl
->data
.length
));
2836 return NT_STATUS_INVALID_PARAMETER
;
2839 cmd
= IVAL(aapl
->data
.data
, 0);
2840 if (cmd
!= SMB2_CRTCTX_AAPL_SERVER_QUERY
) {
2841 DEBUG(1, ("unsupported AAPL cmd: %d\n", cmd
));
2842 return NT_STATUS_INVALID_PARAMETER
;
2845 req_bitmap
= BVAL(aapl
->data
.data
, 8);
2846 client_caps
= BVAL(aapl
->data
.data
, 16);
2848 SIVAL(p
, 0, SMB2_CRTCTX_AAPL_SERVER_QUERY
);
2850 SBVAL(p
, 8, req_bitmap
);
2851 ok
= data_blob_append(req
, &blob
, p
, 16);
2853 return NT_STATUS_UNSUCCESSFUL
;
2856 if (req_bitmap
& SMB2_CRTCTX_AAPL_SERVER_CAPS
) {
2857 if ((client_caps
& SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR
) &&
2858 (handle
->conn
->tcon
->compat
->fs_capabilities
& FILE_NAMED_STREAMS
)) {
2859 server_caps
|= SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR
;
2860 config
->readdir_attr_enabled
= true;
2863 if (config
->use_copyfile
) {
2864 server_caps
|= SMB2_CRTCTX_AAPL_SUPPORTS_OSX_COPYFILE
;
2865 config
->copyfile_enabled
= true;
2869 * The client doesn't set the flag, so we can't check
2870 * for it and just set it unconditionally
2872 if (config
->unix_info_enabled
) {
2873 server_caps
|= SMB2_CRTCTX_AAPL_SUPPORTS_NFS_ACE
;
2876 SBVAL(p
, 0, server_caps
);
2877 ok
= data_blob_append(req
, &blob
, p
, 8);
2879 return NT_STATUS_UNSUCCESSFUL
;
2883 if (req_bitmap
& SMB2_CRTCTX_AAPL_VOLUME_CAPS
) {
2884 int val
= lp_case_sensitive(SNUM(handle
->conn
->tcon
->compat
));
2892 caps
|= SMB2_CRTCTX_AAPL_CASE_SENSITIVE
;
2899 if (config
->time_machine
) {
2900 caps
|= SMB2_CRTCTX_AAPL_FULL_SYNC
;
2905 ok
= data_blob_append(req
, &blob
, p
, 8);
2907 return NT_STATUS_UNSUCCESSFUL
;
2911 if (req_bitmap
& SMB2_CRTCTX_AAPL_MODEL_INFO
) {
2912 ok
= convert_string_talloc(req
,
2913 CH_UNIX
, CH_UTF16LE
,
2914 config
->model
, strlen(config
->model
),
2917 return NT_STATUS_UNSUCCESSFUL
;
2921 SIVAL(p
+ 4, 0, modellen
);
2922 ok
= data_blob_append(req
, &blob
, p
, 8);
2925 return NT_STATUS_UNSUCCESSFUL
;
2928 ok
= data_blob_append(req
, &blob
, model
, modellen
);
2931 return NT_STATUS_UNSUCCESSFUL
;
2935 status
= smb2_create_blob_add(out_context_blobs
,
2937 SMB2_CREATE_TAG_AAPL
,
2939 if (NT_STATUS_IS_OK(status
)) {
2940 global_fruit_config
.nego_aapl
= true;
2941 if (config
->aapl_zero_file_id
) {
2942 aapl_force_zero_file_id(handle
->conn
->sconn
);
2949 static bool readdir_attr_meta_finderi_stream(
2950 struct vfs_handle_struct
*handle
,
2951 const struct smb_filename
*smb_fname
,
2954 struct smb_filename
*stream_name
= NULL
;
2955 files_struct
*fsp
= NULL
;
2960 uint8_t buf
[AFP_INFO_SIZE
];
2962 stream_name
= synthetic_smb_fname(talloc_tos(),
2963 smb_fname
->base_name
,
2964 AFPINFO_STREAM_NAME
,
2965 NULL
, smb_fname
->flags
);
2966 if (stream_name
== NULL
) {
2970 ret
= SMB_VFS_STAT(handle
->conn
, stream_name
);
2975 status
= SMB_VFS_CREATE_FILE(
2976 handle
->conn
, /* conn */
2978 0, /* root_dir_fid */
2979 stream_name
, /* fname */
2980 FILE_READ_DATA
, /* access_mask */
2981 (FILE_SHARE_READ
| FILE_SHARE_WRITE
| /* share_access */
2983 FILE_OPEN
, /* create_disposition*/
2984 0, /* create_options */
2985 0, /* file_attributes */
2986 INTERNAL_OPEN_ONLY
, /* oplock_request */
2988 0, /* allocation_size */
2989 0, /* private_flags */
2994 NULL
, NULL
); /* create context */
2996 TALLOC_FREE(stream_name
);
2998 if (!NT_STATUS_IS_OK(status
)) {
3002 nread
= SMB_VFS_PREAD(fsp
, &buf
[0], AFP_INFO_SIZE
, 0);
3003 if (nread
!= AFP_INFO_SIZE
) {
3004 DBG_ERR("short read [%s] [%zd/%d]\n",
3005 smb_fname_str_dbg(stream_name
), nread
, AFP_INFO_SIZE
);
3010 memcpy(&ai
->afpi_FinderInfo
[0], &buf
[AFP_OFF_FinderInfo
],
3017 close_file(NULL
, fsp
, NORMAL_CLOSE
);
3023 static bool readdir_attr_meta_finderi_netatalk(
3024 struct vfs_handle_struct
*handle
,
3025 const struct smb_filename
*smb_fname
,
3028 struct adouble
*ad
= NULL
;
3031 ad
= ad_get(talloc_tos(), handle
, smb_fname
, ADOUBLE_META
);
3036 p
= ad_get_entry(ad
, ADEID_FINDERI
);
3038 DBG_ERR("No ADEID_FINDERI for [%s]\n", smb_fname
->base_name
);
3043 memcpy(&ai
->afpi_FinderInfo
[0], p
, AFP_FinderSize
);
3048 static bool readdir_attr_meta_finderi(struct vfs_handle_struct
*handle
,
3049 const struct smb_filename
*smb_fname
,
3050 struct readdir_attr_data
*attr_data
)
3052 struct fruit_config_data
*config
= NULL
;
3053 uint32_t date_added
;
3057 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
3058 struct fruit_config_data
,
3061 switch (config
->meta
) {
3062 case FRUIT_META_NETATALK
:
3063 ok
= readdir_attr_meta_finderi_netatalk(
3064 handle
, smb_fname
, &ai
);
3067 case FRUIT_META_STREAM
:
3068 ok
= readdir_attr_meta_finderi_stream(
3069 handle
, smb_fname
, &ai
);
3073 DBG_ERR("Unexpected meta config [%d]\n", config
->meta
);
3078 /* Don't bother with errors, it's likely ENOENT */
3082 if (S_ISREG(smb_fname
->st
.st_ex_mode
)) {
3084 memcpy(&attr_data
->attr_data
.aapl
.finder_info
[0],
3085 &ai
.afpi_FinderInfo
[0], 4);
3087 /* finder_creator */
3088 memcpy(&attr_data
->attr_data
.aapl
.finder_info
[0] + 4,
3089 &ai
.afpi_FinderInfo
[4], 4);
3093 memcpy(&attr_data
->attr_data
.aapl
.finder_info
[0] + 8,
3094 &ai
.afpi_FinderInfo
[8], 2);
3096 /* finder_ext_flags */
3097 memcpy(&attr_data
->attr_data
.aapl
.finder_info
[0] + 10,
3098 &ai
.afpi_FinderInfo
[24], 2);
3101 date_added
= convert_time_t_to_uint32_t(
3102 smb_fname
->st
.st_ex_btime
.tv_sec
- AD_DATE_DELTA
);
3104 RSIVAL(&attr_data
->attr_data
.aapl
.finder_info
[0], 12, date_added
);
3109 static uint64_t readdir_attr_rfork_size_adouble(
3110 struct vfs_handle_struct
*handle
,
3111 const struct smb_filename
*smb_fname
)
3113 struct adouble
*ad
= NULL
;
3114 uint64_t rfork_size
;
3116 ad
= ad_get(talloc_tos(), handle
, smb_fname
,
3122 rfork_size
= ad_getentrylen(ad
, ADEID_RFORK
);
3128 static uint64_t readdir_attr_rfork_size_stream(
3129 struct vfs_handle_struct
*handle
,
3130 const struct smb_filename
*smb_fname
)
3132 struct smb_filename
*stream_name
= NULL
;
3134 uint64_t rfork_size
;
3136 stream_name
= synthetic_smb_fname(talloc_tos(),
3137 smb_fname
->base_name
,
3138 AFPRESOURCE_STREAM_NAME
,
3140 if (stream_name
== NULL
) {
3144 ret
= SMB_VFS_STAT(handle
->conn
, stream_name
);
3146 TALLOC_FREE(stream_name
);
3150 rfork_size
= stream_name
->st
.st_ex_size
;
3151 TALLOC_FREE(stream_name
);
3156 static uint64_t readdir_attr_rfork_size(struct vfs_handle_struct
*handle
,
3157 const struct smb_filename
*smb_fname
)
3159 struct fruit_config_data
*config
= NULL
;
3160 uint64_t rfork_size
;
3162 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
3163 struct fruit_config_data
,
3166 switch (config
->rsrc
) {
3167 case FRUIT_RSRC_ADFILE
:
3168 case FRUIT_RSRC_XATTR
:
3169 rfork_size
= readdir_attr_rfork_size_adouble(handle
,
3173 case FRUIT_META_STREAM
:
3174 rfork_size
= readdir_attr_rfork_size_stream(handle
,
3179 DBG_ERR("Unexpected rsrc config [%d]\n", config
->rsrc
);
3187 static NTSTATUS
readdir_attr_macmeta(struct vfs_handle_struct
*handle
,
3188 const struct smb_filename
*smb_fname
,
3189 struct readdir_attr_data
*attr_data
)
3191 NTSTATUS status
= NT_STATUS_OK
;
3192 struct fruit_config_data
*config
= NULL
;
3195 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
3196 struct fruit_config_data
,
3197 return NT_STATUS_UNSUCCESSFUL
);
3200 /* Ensure we return a default value in the creation_date field */
3201 RSIVAL(&attr_data
->attr_data
.aapl
.finder_info
, 12, AD_DATE_START
);
3204 * Resource fork length
3207 if (config
->readdir_attr_rsize
) {
3208 uint64_t rfork_size
;
3210 rfork_size
= readdir_attr_rfork_size(handle
, smb_fname
);
3211 attr_data
->attr_data
.aapl
.rfork_size
= rfork_size
;
3218 if (config
->readdir_attr_finder_info
) {
3219 ok
= readdir_attr_meta_finderi(handle
, smb_fname
, attr_data
);
3221 status
= NT_STATUS_INTERNAL_ERROR
;
3228 static NTSTATUS
remove_virtual_nfs_aces(struct security_descriptor
*psd
)
3233 if (psd
->dacl
== NULL
) {
3234 return NT_STATUS_OK
;
3237 for (i
= 0; i
< psd
->dacl
->num_aces
; i
++) {
3238 /* MS NFS style mode/uid/gid */
3239 int cmp
= dom_sid_compare_domain(
3240 &global_sid_Unix_NFS
,
3241 &psd
->dacl
->aces
[i
].trustee
);
3243 /* Normal ACE entry. */
3248 * security_descriptor_dacl_del()
3249 * *must* return NT_STATUS_OK as we know
3250 * we have something to remove.
3253 status
= security_descriptor_dacl_del(psd
,
3254 &psd
->dacl
->aces
[i
].trustee
);
3255 if (!NT_STATUS_IS_OK(status
)) {
3256 DBG_WARNING("failed to remove MS NFS style ACE: %s\n",
3262 * security_descriptor_dacl_del() may delete more
3263 * then one entry subsequent to this one if the
3264 * SID matches, but we only need to ensure that
3265 * we stay looking at the same element in the array.
3269 return NT_STATUS_OK
;
3272 /* Search MS NFS style ACE with UNIX mode */
3273 static NTSTATUS
check_ms_nfs(vfs_handle_struct
*handle
,
3275 struct security_descriptor
*psd
,
3280 struct fruit_config_data
*config
= NULL
;
3284 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
3285 struct fruit_config_data
,
3286 return NT_STATUS_UNSUCCESSFUL
);
3288 if (!global_fruit_config
.nego_aapl
) {
3289 return NT_STATUS_OK
;
3291 if (psd
->dacl
== NULL
|| !config
->unix_info_enabled
) {
3292 return NT_STATUS_OK
;
3295 for (i
= 0; i
< psd
->dacl
->num_aces
; i
++) {
3296 if (dom_sid_compare_domain(
3297 &global_sid_Unix_NFS_Mode
,
3298 &psd
->dacl
->aces
[i
].trustee
) == 0) {
3299 *pmode
= (mode_t
)psd
->dacl
->aces
[i
].trustee
.sub_auths
[2];
3300 *pmode
&= (S_IRWXU
| S_IRWXG
| S_IRWXO
);
3303 DEBUG(10, ("MS NFS chmod request %s, %04o\n",
3304 fsp_str_dbg(fsp
), (unsigned)(*pmode
)));
3310 * Remove any incoming virtual ACE entries generated by
3311 * fruit_fget_nt_acl().
3314 return remove_virtual_nfs_aces(psd
);
3317 /****************************************************************************
3319 ****************************************************************************/
3321 static int fruit_connect(vfs_handle_struct
*handle
,
3322 const char *service
,
3326 char *list
= NULL
, *newlist
= NULL
;
3327 struct fruit_config_data
*config
;
3329 DEBUG(10, ("fruit_connect\n"));
3331 rc
= SMB_VFS_NEXT_CONNECT(handle
, service
, user
);
3336 rc
= init_fruit_config(handle
);
3341 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
3342 struct fruit_config_data
, return -1);
3344 if (config
->veto_appledouble
) {
3345 list
= lp_veto_files(talloc_tos(), SNUM(handle
->conn
));
3348 if (strstr(list
, "/" ADOUBLE_NAME_PREFIX
"*/") == NULL
) {
3349 newlist
= talloc_asprintf(
3351 "%s/" ADOUBLE_NAME_PREFIX
"*/",
3353 lp_do_parameter(SNUM(handle
->conn
),
3358 lp_do_parameter(SNUM(handle
->conn
),
3360 "/" ADOUBLE_NAME_PREFIX
"*/");
3366 if (config
->encoding
== FRUIT_ENC_NATIVE
) {
3367 lp_do_parameter(SNUM(handle
->conn
),
3372 if (config
->time_machine
) {
3373 DBG_NOTICE("Enabling durable handles for Time Machine "
3374 "support on [%s]\n", service
);
3375 lp_do_parameter(SNUM(handle
->conn
), "durable handles", "yes");
3376 lp_do_parameter(SNUM(handle
->conn
), "kernel oplocks", "no");
3377 lp_do_parameter(SNUM(handle
->conn
), "kernel share modes", "no");
3378 if (!lp_strict_sync(SNUM(handle
->conn
))) {
3379 DBG_WARNING("Time Machine without strict sync is not "
3382 lp_do_parameter(SNUM(handle
->conn
), "posix locking", "no");
3388 static int fruit_open_meta_stream(vfs_handle_struct
*handle
,
3389 struct smb_filename
*smb_fname
,
3395 char afpinfo_buf
[AFP_INFO_SIZE
];
3396 ssize_t len
, written
;
3400 hostfd
= SMB_VFS_NEXT_OPEN(handle
, smb_fname
, fsp
, flags
, mode
);
3405 if (!(flags
& (O_CREAT
| O_TRUNC
))) {
3409 ai
= afpinfo_new(talloc_tos());
3415 len
= afpinfo_pack(ai
, afpinfo_buf
);
3416 if (len
!= AFP_INFO_SIZE
) {
3421 /* Set fd, needed in SMB_VFS_NEXT_PWRITE() */
3422 fsp
->fh
->fd
= hostfd
;
3424 written
= SMB_VFS_NEXT_PWRITE(handle
, fsp
, afpinfo_buf
,
3427 if (written
!= AFP_INFO_SIZE
) {
3428 DBG_ERR("bad write [%zd/%d]\n", written
, AFP_INFO_SIZE
);
3436 DBG_DEBUG("rc=%d, fd=%d\n", rc
, hostfd
);
3439 int saved_errno
= errno
;
3441 fsp
->fh
->fd
= hostfd
;
3442 SMB_VFS_NEXT_CLOSE(handle
, fsp
);
3445 errno
= saved_errno
;
3450 static int fruit_open_meta_netatalk(vfs_handle_struct
*handle
,
3451 struct smb_filename
*smb_fname
,
3458 struct adouble
*ad
= NULL
;
3461 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname
));
3464 * Return a valid fd, but ensure any attempt to use it returns an error
3465 * (EPIPE). All operations on the smb_fname or the fsp will use path
3475 if (flags
& (O_CREAT
| O_TRUNC
)) {
3477 * The attribute does not exist or needs to be truncated,
3478 * create an AppleDouble EA
3480 ad
= ad_init(fsp
, handle
, ADOUBLE_META
);
3486 rc
= ad_set(ad
, fsp
->fsp_name
);
3496 DEBUG(10, ("fruit_open meta rc=%d, fd=%d\n", rc
, fakefd
));
3498 int saved_errno
= errno
;
3503 errno
= saved_errno
;
3508 static int fruit_open_meta(vfs_handle_struct
*handle
,
3509 struct smb_filename
*smb_fname
,
3510 files_struct
*fsp
, int flags
, mode_t mode
)
3513 struct fruit_config_data
*config
= NULL
;
3514 struct fio
*fio
= NULL
;
3516 DBG_DEBUG("path [%s]\n", smb_fname_str_dbg(smb_fname
));
3518 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
3519 struct fruit_config_data
, return -1);
3521 switch (config
->meta
) {
3522 case FRUIT_META_STREAM
:
3523 fd
= fruit_open_meta_stream(handle
, smb_fname
,
3527 case FRUIT_META_NETATALK
:
3528 fd
= fruit_open_meta_netatalk(handle
, smb_fname
,
3533 DBG_ERR("Unexpected meta config [%d]\n", config
->meta
);
3537 DBG_DEBUG("path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname
), fd
);
3543 fio
= VFS_ADD_FSP_EXTENSION(handle
, fsp
, struct fio
, NULL
);
3544 fio
->type
= ADOUBLE_META
;
3545 fio
->config
= config
;
3550 static int fruit_open_rsrc_adouble(vfs_handle_struct
*handle
,
3551 struct smb_filename
*smb_fname
,
3557 struct adouble
*ad
= NULL
;
3558 struct smb_filename
*smb_fname_base
= NULL
;
3559 struct fruit_config_data
*config
= NULL
;
3562 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
3563 struct fruit_config_data
, return -1);
3565 if ((!(flags
& O_CREAT
)) &&
3566 S_ISDIR(fsp
->base_fsp
->fsp_name
->st
.st_ex_mode
))
3568 /* sorry, but directories don't habe a resource fork */
3573 rc
= adouble_path(talloc_tos(), smb_fname
, &smb_fname_base
);
3578 /* We always need read/write access for the metadata header too */
3579 flags
&= ~(O_RDONLY
| O_WRONLY
);
3582 hostfd
= SMB_VFS_NEXT_OPEN(handle
, smb_fname_base
, fsp
,
3589 if (flags
& (O_CREAT
| O_TRUNC
)) {
3590 ad
= ad_init(fsp
, handle
, ADOUBLE_RSRC
);
3596 fsp
->fh
->fd
= hostfd
;
3598 rc
= ad_fset(ad
, fsp
);
3609 TALLOC_FREE(smb_fname_base
);
3611 DEBUG(10, ("fruit_open resource fork: rc=%d, fd=%d\n", rc
, hostfd
));
3613 int saved_errno
= errno
;
3616 * BUGBUGBUG -- we would need to call
3617 * fd_close_posix here, but we don't have a
3620 fsp
->fh
->fd
= hostfd
;
3624 errno
= saved_errno
;
3629 static int fruit_open_rsrc_xattr(vfs_handle_struct
*handle
,
3630 struct smb_filename
*smb_fname
,
3635 #ifdef HAVE_ATTROPEN
3638 fd
= attropen(smb_fname
->base_name
,
3639 AFPRESOURCE_EA_NETATALK
,
3654 static int fruit_open_rsrc(vfs_handle_struct
*handle
,
3655 struct smb_filename
*smb_fname
,
3656 files_struct
*fsp
, int flags
, mode_t mode
)
3659 struct fruit_config_data
*config
= NULL
;
3660 struct fio
*fio
= NULL
;
3662 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname
));
3664 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
3665 struct fruit_config_data
, return -1);
3667 switch (config
->rsrc
) {
3668 case FRUIT_RSRC_STREAM
:
3669 fd
= SMB_VFS_NEXT_OPEN(handle
, smb_fname
, fsp
, flags
, mode
);
3672 case FRUIT_RSRC_ADFILE
:
3673 fd
= fruit_open_rsrc_adouble(handle
, smb_fname
,
3677 case FRUIT_RSRC_XATTR
:
3678 fd
= fruit_open_rsrc_xattr(handle
, smb_fname
,
3683 DBG_ERR("Unexpected rsrc config [%d]\n", config
->rsrc
);
3687 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname
), fd
);
3693 fio
= VFS_ADD_FSP_EXTENSION(handle
, fsp
, struct fio
, NULL
);
3694 fio
->type
= ADOUBLE_RSRC
;
3695 fio
->config
= config
;
3700 static int fruit_open(vfs_handle_struct
*handle
,
3701 struct smb_filename
*smb_fname
,
3702 files_struct
*fsp
, int flags
, mode_t mode
)
3706 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname
));
3708 if (!is_ntfs_stream_smb_fname(smb_fname
)) {
3709 return SMB_VFS_NEXT_OPEN(handle
, smb_fname
, fsp
, flags
, mode
);
3712 if (is_afpinfo_stream(smb_fname
)) {
3713 fd
= fruit_open_meta(handle
, smb_fname
, fsp
, flags
, mode
);
3714 } else if (is_afpresource_stream(smb_fname
)) {
3715 fd
= fruit_open_rsrc(handle
, smb_fname
, fsp
, flags
, mode
);
3717 fd
= SMB_VFS_NEXT_OPEN(handle
, smb_fname
, fsp
, flags
, mode
);
3720 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname
), fd
);
3725 static int fruit_rename(struct vfs_handle_struct
*handle
,
3726 const struct smb_filename
*smb_fname_src
,
3727 const struct smb_filename
*smb_fname_dst
)
3730 struct fruit_config_data
*config
= NULL
;
3731 struct smb_filename
*src_adp_smb_fname
= NULL
;
3732 struct smb_filename
*dst_adp_smb_fname
= NULL
;
3734 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
3735 struct fruit_config_data
, return -1);
3737 if (!VALID_STAT(smb_fname_src
->st
)) {
3738 DBG_ERR("Need valid stat for [%s]\n",
3739 smb_fname_str_dbg(smb_fname_src
));
3743 rc
= SMB_VFS_NEXT_RENAME(handle
, smb_fname_src
, smb_fname_dst
);
3748 if ((config
->rsrc
!= FRUIT_RSRC_ADFILE
) ||
3749 (!S_ISREG(smb_fname_src
->st
.st_ex_mode
)))
3754 rc
= adouble_path(talloc_tos(), smb_fname_src
, &src_adp_smb_fname
);
3759 rc
= adouble_path(talloc_tos(), smb_fname_dst
, &dst_adp_smb_fname
);
3764 DBG_DEBUG("%s -> %s\n",
3765 smb_fname_str_dbg(src_adp_smb_fname
),
3766 smb_fname_str_dbg(dst_adp_smb_fname
));
3768 rc
= SMB_VFS_NEXT_RENAME(handle
, src_adp_smb_fname
, dst_adp_smb_fname
);
3769 if (errno
== ENOENT
) {
3774 TALLOC_FREE(src_adp_smb_fname
);
3775 TALLOC_FREE(dst_adp_smb_fname
);
3779 static int fruit_unlink_meta_stream(vfs_handle_struct
*handle
,
3780 const struct smb_filename
*smb_fname
)
3782 return SMB_VFS_NEXT_UNLINK(handle
, smb_fname
);
3785 static int fruit_unlink_meta_netatalk(vfs_handle_struct
*handle
,
3786 const struct smb_filename
*smb_fname
)
3788 return SMB_VFS_REMOVEXATTR(handle
->conn
,
3790 AFPINFO_EA_NETATALK
);
3793 static int fruit_unlink_meta(vfs_handle_struct
*handle
,
3794 const struct smb_filename
*smb_fname
)
3796 struct fruit_config_data
*config
= NULL
;
3799 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
3800 struct fruit_config_data
, return -1);
3802 switch (config
->meta
) {
3803 case FRUIT_META_STREAM
:
3804 rc
= fruit_unlink_meta_stream(handle
, smb_fname
);
3807 case FRUIT_META_NETATALK
:
3808 rc
= fruit_unlink_meta_netatalk(handle
, smb_fname
);
3812 DBG_ERR("Unsupported meta config [%d]\n", config
->meta
);
3819 static int fruit_unlink_rsrc_stream(vfs_handle_struct
*handle
,
3820 const struct smb_filename
*smb_fname
,
3825 if (!force_unlink
) {
3826 struct smb_filename
*smb_fname_cp
= NULL
;
3829 smb_fname_cp
= cp_smb_filename(talloc_tos(), smb_fname
);
3830 if (smb_fname_cp
== NULL
) {
3835 * 0 byte resource fork streams are not listed by
3836 * vfs_streaminfo, as a result stream cleanup/deletion of file
3837 * deletion doesn't remove the resourcefork stream.
3840 ret
= SMB_VFS_NEXT_STAT(handle
, smb_fname_cp
);
3842 TALLOC_FREE(smb_fname_cp
);
3843 DBG_ERR("stat [%s] failed [%s]\n",
3844 smb_fname_str_dbg(smb_fname_cp
), strerror(errno
));
3848 size
= smb_fname_cp
->st
.st_ex_size
;
3849 TALLOC_FREE(smb_fname_cp
);
3852 /* OS X ignores resource fork stream delete requests */
3857 ret
= SMB_VFS_NEXT_UNLINK(handle
, smb_fname
);
3858 if ((ret
!= 0) && (errno
== ENOENT
) && force_unlink
) {
3865 static int fruit_unlink_rsrc_adouble(vfs_handle_struct
*handle
,
3866 const struct smb_filename
*smb_fname
,
3870 struct adouble
*ad
= NULL
;
3871 struct smb_filename
*adp_smb_fname
= NULL
;
3873 if (!force_unlink
) {
3874 ad
= ad_get(talloc_tos(), handle
, smb_fname
,
3883 * 0 byte resource fork streams are not listed by
3884 * vfs_streaminfo, as a result stream cleanup/deletion of file
3885 * deletion doesn't remove the resourcefork stream.
3888 if (ad_getentrylen(ad
, ADEID_RFORK
) > 0) {
3889 /* OS X ignores resource fork stream delete requests */
3897 rc
= adouble_path(talloc_tos(), smb_fname
, &adp_smb_fname
);
3902 rc
= SMB_VFS_NEXT_UNLINK(handle
, adp_smb_fname
);
3903 TALLOC_FREE(adp_smb_fname
);
3904 if ((rc
!= 0) && (errno
== ENOENT
) && force_unlink
) {
3911 static int fruit_unlink_rsrc_xattr(vfs_handle_struct
*handle
,
3912 const struct smb_filename
*smb_fname
,
3916 * OS X ignores resource fork stream delete requests, so nothing to do
3917 * here. Removing the file will remove the xattr anyway, so we don't
3918 * have to take care of removing 0 byte resource forks that could be
3924 static int fruit_unlink_rsrc(vfs_handle_struct
*handle
,
3925 const struct smb_filename
*smb_fname
,
3928 struct fruit_config_data
*config
= NULL
;
3931 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
3932 struct fruit_config_data
, return -1);
3934 switch (config
->rsrc
) {
3935 case FRUIT_RSRC_STREAM
:
3936 rc
= fruit_unlink_rsrc_stream(handle
, smb_fname
, force_unlink
);
3939 case FRUIT_RSRC_ADFILE
:
3940 rc
= fruit_unlink_rsrc_adouble(handle
, smb_fname
, force_unlink
);
3943 case FRUIT_RSRC_XATTR
:
3944 rc
= fruit_unlink_rsrc_xattr(handle
, smb_fname
, force_unlink
);
3948 DBG_ERR("Unsupported rsrc config [%d]\n", config
->rsrc
);
3955 static int fruit_unlink(vfs_handle_struct
*handle
,
3956 const struct smb_filename
*smb_fname
)
3959 struct fruit_config_data
*config
= NULL
;
3960 struct smb_filename
*rsrc_smb_fname
= NULL
;
3962 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
3963 struct fruit_config_data
, return -1);
3965 if (is_afpinfo_stream(smb_fname
)) {
3966 return fruit_unlink_meta(handle
, smb_fname
);
3967 } else if (is_afpresource_stream(smb_fname
)) {
3968 return fruit_unlink_rsrc(handle
, smb_fname
, false);
3969 } if (is_ntfs_stream_smb_fname(smb_fname
)) {
3970 return SMB_VFS_NEXT_UNLINK(handle
, smb_fname
);
3974 * A request to delete the base file. Because 0 byte resource
3975 * fork streams are not listed by fruit_streaminfo,
3976 * delete_all_streams() can't remove 0 byte resource fork
3977 * streams, so we have to cleanup this here.
3979 rsrc_smb_fname
= synthetic_smb_fname(talloc_tos(),
3980 smb_fname
->base_name
,
3981 AFPRESOURCE_STREAM_NAME
,
3984 if (rsrc_smb_fname
== NULL
) {
3988 rc
= fruit_unlink_rsrc(handle
, rsrc_smb_fname
, true);
3989 if ((rc
!= 0) && (errno
!= ENOENT
)) {
3990 DBG_ERR("Forced unlink of [%s] failed [%s]\n",
3991 smb_fname_str_dbg(rsrc_smb_fname
), strerror(errno
));
3992 TALLOC_FREE(rsrc_smb_fname
);
3995 TALLOC_FREE(rsrc_smb_fname
);
3997 return SMB_VFS_NEXT_UNLINK(handle
, smb_fname
);
4000 static int fruit_chmod(vfs_handle_struct
*handle
,
4001 const struct smb_filename
*smb_fname
,
4005 struct fruit_config_data
*config
= NULL
;
4006 struct smb_filename
*smb_fname_adp
= NULL
;
4008 rc
= SMB_VFS_NEXT_CHMOD(handle
, smb_fname
, mode
);
4013 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
4014 struct fruit_config_data
, return -1);
4016 if (config
->rsrc
!= FRUIT_RSRC_ADFILE
) {
4020 if (!VALID_STAT(smb_fname
->st
)) {
4024 if (!S_ISREG(smb_fname
->st
.st_ex_mode
)) {
4028 rc
= adouble_path(talloc_tos(), smb_fname
, &smb_fname_adp
);
4033 DEBUG(10, ("fruit_chmod: %s\n", smb_fname_adp
->base_name
));
4035 rc
= SMB_VFS_NEXT_CHMOD(handle
, smb_fname_adp
, mode
);
4036 if (errno
== ENOENT
) {
4040 TALLOC_FREE(smb_fname_adp
);
4044 static int fruit_chown(vfs_handle_struct
*handle
,
4045 const struct smb_filename
*smb_fname
,
4050 struct fruit_config_data
*config
= NULL
;
4051 struct smb_filename
*adp_smb_fname
= NULL
;
4053 rc
= SMB_VFS_NEXT_CHOWN(handle
, smb_fname
, uid
, gid
);
4058 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
4059 struct fruit_config_data
, return -1);
4061 if (config
->rsrc
!= FRUIT_RSRC_ADFILE
) {
4065 if (!VALID_STAT(smb_fname
->st
)) {
4069 if (!S_ISREG(smb_fname
->st
.st_ex_mode
)) {
4073 rc
= adouble_path(talloc_tos(), smb_fname
, &adp_smb_fname
);
4078 DEBUG(10, ("fruit_chown: %s\n", adp_smb_fname
->base_name
));
4080 rc
= SMB_VFS_NEXT_CHOWN(handle
, adp_smb_fname
, uid
, gid
);
4081 if (errno
== ENOENT
) {
4086 TALLOC_FREE(adp_smb_fname
);
4090 static int fruit_rmdir(struct vfs_handle_struct
*handle
,
4091 const struct smb_filename
*smb_fname
)
4095 struct fruit_config_data
*config
;
4097 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
4098 struct fruit_config_data
, return -1);
4100 if (config
->rsrc
!= FRUIT_RSRC_ADFILE
) {
4105 * Due to there is no way to change bDeleteVetoFiles variable
4106 * from this module, need to clean up ourselves
4109 dh
= SMB_VFS_OPENDIR(handle
->conn
, smb_fname
, NULL
, 0);
4114 while ((de
= SMB_VFS_READDIR(handle
->conn
, dh
, NULL
)) != NULL
) {
4116 struct adouble
*ad
= NULL
;
4118 struct smb_filename
*ad_smb_fname
= NULL
;
4121 match
= strncmp(de
->d_name
,
4122 ADOUBLE_NAME_PREFIX
,
4123 strlen(ADOUBLE_NAME_PREFIX
));
4128 p
= talloc_asprintf(talloc_tos(), "%s/%s",
4129 smb_fname
->base_name
, de
->d_name
);
4131 DBG_ERR("talloc_asprintf failed\n");
4135 ad_smb_fname
= synthetic_smb_fname(talloc_tos(), p
,
4139 if (ad_smb_fname
== NULL
) {
4140 DBG_ERR("synthetic_smb_fname failed\n");
4145 * Check whether it's a valid AppleDouble file, if
4146 * yes, delete it, ignore it otherwise.
4148 ad
= ad_get(talloc_tos(), handle
, ad_smb_fname
, ADOUBLE_RSRC
);
4150 TALLOC_FREE(ad_smb_fname
);
4156 ret
= SMB_VFS_NEXT_UNLINK(handle
, ad_smb_fname
);
4158 DBG_ERR("Deleting [%s] failed\n",
4159 smb_fname_str_dbg(ad_smb_fname
));
4161 TALLOC_FREE(ad_smb_fname
);
4166 SMB_VFS_CLOSEDIR(handle
->conn
, dh
);
4168 return SMB_VFS_NEXT_RMDIR(handle
, smb_fname
);
4171 static ssize_t
fruit_pread_meta_stream(vfs_handle_struct
*handle
,
4172 files_struct
*fsp
, void *data
,
4173 size_t n
, off_t offset
)
4178 nread
= SMB_VFS_NEXT_PREAD(handle
, fsp
, data
, n
, offset
);
4184 DBG_ERR("Removing [%s] after short read [%zd]\n",
4185 fsp_str_dbg(fsp
), nread
);
4187 ret
= SMB_VFS_NEXT_UNLINK(handle
, fsp
->fsp_name
);
4189 DBG_ERR("Removing [%s] failed\n", fsp_str_dbg(fsp
));
4197 static ssize_t
fruit_pread_meta_adouble(vfs_handle_struct
*handle
,
4198 files_struct
*fsp
, void *data
,
4199 size_t n
, off_t offset
)
4202 struct adouble
*ad
= NULL
;
4203 char afpinfo_buf
[AFP_INFO_SIZE
];
4207 ai
= afpinfo_new(talloc_tos());
4212 ad
= ad_fget(talloc_tos(), handle
, fsp
, ADOUBLE_META
);
4218 p
= ad_get_entry(ad
, ADEID_FINDERI
);
4220 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp
));
4225 memcpy(&ai
->afpi_FinderInfo
[0], p
, ADEDLEN_FINDERI
);
4227 nread
= afpinfo_pack(ai
, afpinfo_buf
);
4228 if (nread
!= AFP_INFO_SIZE
) {
4233 memcpy(data
, afpinfo_buf
, n
);
4241 static ssize_t
fruit_pread_meta(vfs_handle_struct
*handle
,
4242 files_struct
*fsp
, void *data
,
4243 size_t n
, off_t offset
)
4245 struct fio
*fio
= (struct fio
*)VFS_FETCH_FSP_EXTENSION(handle
, fsp
);
4250 * OS X has a off-by-1 error in the offset calculation, so we're
4251 * bug compatible here. It won't hurt, as any relevant real
4252 * world read requests from the AFP_AfpInfo stream will be
4253 * offset=0 n=60. offset is ignored anyway, see below.
4255 if ((offset
< 0) || (offset
>= AFP_INFO_SIZE
+ 1)) {
4260 DBG_ERR("Failed to fetch fsp extension");
4264 /* Yes, macOS always reads from offset 0 */
4266 to_return
= MIN(n
, AFP_INFO_SIZE
);
4268 switch (fio
->config
->meta
) {
4269 case FRUIT_META_STREAM
:
4270 nread
= fruit_pread_meta_stream(handle
, fsp
, data
,
4274 case FRUIT_META_NETATALK
:
4275 nread
= fruit_pread_meta_adouble(handle
, fsp
, data
,
4280 DBG_ERR("Unexpected meta config [%d]\n", fio
->config
->meta
);
4287 static ssize_t
fruit_pread_rsrc_stream(vfs_handle_struct
*handle
,
4288 files_struct
*fsp
, void *data
,
4289 size_t n
, off_t offset
)
4291 return SMB_VFS_NEXT_PREAD(handle
, fsp
, data
, n
, offset
);
4294 static ssize_t
fruit_pread_rsrc_xattr(vfs_handle_struct
*handle
,
4295 files_struct
*fsp
, void *data
,
4296 size_t n
, off_t offset
)
4298 return SMB_VFS_NEXT_PREAD(handle
, fsp
, data
, n
, offset
);
4301 static ssize_t
fruit_pread_rsrc_adouble(vfs_handle_struct
*handle
,
4302 files_struct
*fsp
, void *data
,
4303 size_t n
, off_t offset
)
4305 struct adouble
*ad
= NULL
;
4308 ad
= ad_fget(talloc_tos(), handle
, fsp
, ADOUBLE_RSRC
);
4313 nread
= SMB_VFS_NEXT_PREAD(handle
, fsp
, data
, n
,
4314 offset
+ ad_getentryoff(ad
, ADEID_RFORK
));
4320 static ssize_t
fruit_pread_rsrc(vfs_handle_struct
*handle
,
4321 files_struct
*fsp
, void *data
,
4322 size_t n
, off_t offset
)
4324 struct fio
*fio
= (struct fio
*)VFS_FETCH_FSP_EXTENSION(handle
, fsp
);
4332 switch (fio
->config
->rsrc
) {
4333 case FRUIT_RSRC_STREAM
:
4334 nread
= fruit_pread_rsrc_stream(handle
, fsp
, data
, n
, offset
);
4337 case FRUIT_RSRC_ADFILE
:
4338 nread
= fruit_pread_rsrc_adouble(handle
, fsp
, data
, n
, offset
);
4341 case FRUIT_RSRC_XATTR
:
4342 nread
= fruit_pread_rsrc_xattr(handle
, fsp
, data
, n
, offset
);
4346 DBG_ERR("Unexpected rsrc config [%d]\n", fio
->config
->rsrc
);
4353 static ssize_t
fruit_pread(vfs_handle_struct
*handle
,
4354 files_struct
*fsp
, void *data
,
4355 size_t n
, off_t offset
)
4357 struct fio
*fio
= (struct fio
*)VFS_FETCH_FSP_EXTENSION(handle
, fsp
);
4360 DBG_DEBUG("Path [%s] offset=%"PRIdMAX
", size=%zd\n",
4361 fsp_str_dbg(fsp
), (intmax_t)offset
, n
);
4364 return SMB_VFS_NEXT_PREAD(handle
, fsp
, data
, n
, offset
);
4367 if (fio
->type
== ADOUBLE_META
) {
4368 nread
= fruit_pread_meta(handle
, fsp
, data
, n
, offset
);
4370 nread
= fruit_pread_rsrc(handle
, fsp
, data
, n
, offset
);
4373 DBG_DEBUG("Path [%s] nread [%zd]\n", fsp_str_dbg(fsp
), nread
);
4377 static bool fruit_must_handle_aio_stream(struct fio
*fio
)
4383 if ((fio
->type
== ADOUBLE_META
) &&
4384 (fio
->config
->meta
== FRUIT_META_NETATALK
))
4389 if ((fio
->type
== ADOUBLE_RSRC
) &&
4390 (fio
->config
->rsrc
== FRUIT_RSRC_ADFILE
))
4398 struct fruit_pread_state
{
4400 struct vfs_aio_state vfs_aio_state
;
4403 static void fruit_pread_done(struct tevent_req
*subreq
);
4405 static struct tevent_req
*fruit_pread_send(
4406 struct vfs_handle_struct
*handle
,
4407 TALLOC_CTX
*mem_ctx
,
4408 struct tevent_context
*ev
,
4409 struct files_struct
*fsp
,
4411 size_t n
, off_t offset
)
4413 struct tevent_req
*req
= NULL
;
4414 struct tevent_req
*subreq
= NULL
;
4415 struct fruit_pread_state
*state
= NULL
;
4416 struct fio
*fio
= (struct fio
*)VFS_FETCH_FSP_EXTENSION(handle
, fsp
);
4418 req
= tevent_req_create(mem_ctx
, &state
,
4419 struct fruit_pread_state
);
4424 if (fruit_must_handle_aio_stream(fio
)) {
4425 state
->nread
= SMB_VFS_PREAD(fsp
, data
, n
, offset
);
4426 if (state
->nread
!= n
) {
4427 if (state
->nread
!= -1) {
4430 tevent_req_error(req
, errno
);
4431 return tevent_req_post(req
, ev
);
4433 tevent_req_done(req
);
4434 return tevent_req_post(req
, ev
);
4437 subreq
= SMB_VFS_NEXT_PREAD_SEND(state
, ev
, handle
, fsp
,
4439 if (tevent_req_nomem(req
, subreq
)) {
4440 return tevent_req_post(req
, ev
);
4442 tevent_req_set_callback(subreq
, fruit_pread_done
, req
);
4446 static void fruit_pread_done(struct tevent_req
*subreq
)
4448 struct tevent_req
*req
= tevent_req_callback_data(
4449 subreq
, struct tevent_req
);
4450 struct fruit_pread_state
*state
= tevent_req_data(
4451 req
, struct fruit_pread_state
);
4453 state
->nread
= SMB_VFS_PREAD_RECV(subreq
, &state
->vfs_aio_state
);
4454 TALLOC_FREE(subreq
);
4456 if (tevent_req_error(req
, state
->vfs_aio_state
.error
)) {
4459 tevent_req_done(req
);
4462 static ssize_t
fruit_pread_recv(struct tevent_req
*req
,
4463 struct vfs_aio_state
*vfs_aio_state
)
4465 struct fruit_pread_state
*state
= tevent_req_data(
4466 req
, struct fruit_pread_state
);
4468 if (tevent_req_is_unix_error(req
, &vfs_aio_state
->error
)) {
4472 *vfs_aio_state
= state
->vfs_aio_state
;
4473 return state
->nread
;
4476 static ssize_t
fruit_pwrite_meta_stream(vfs_handle_struct
*handle
,
4477 files_struct
*fsp
, const void *data
,
4478 size_t n
, off_t offset
)
4484 ai
= afpinfo_unpack(talloc_tos(), data
);
4489 nwritten
= SMB_VFS_NEXT_PWRITE(handle
, fsp
, data
, n
, offset
);
4490 if (nwritten
!= n
) {
4494 if (!ai_empty_finderinfo(ai
)) {
4498 ok
= set_delete_on_close(
4501 handle
->conn
->session_info
->security_token
,
4502 handle
->conn
->session_info
->unix_token
);
4504 DBG_ERR("set_delete_on_close on [%s] failed\n",
4512 static ssize_t
fruit_pwrite_meta_netatalk(vfs_handle_struct
*handle
,
4513 files_struct
*fsp
, const void *data
,
4514 size_t n
, off_t offset
)
4516 struct adouble
*ad
= NULL
;
4522 ai
= afpinfo_unpack(talloc_tos(), data
);
4527 ad
= ad_fget(talloc_tos(), handle
, fsp
, ADOUBLE_META
);
4529 ad
= ad_init(talloc_tos(), handle
, ADOUBLE_META
);
4534 p
= ad_get_entry(ad
, ADEID_FINDERI
);
4536 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp
));
4541 memcpy(p
, &ai
->afpi_FinderInfo
[0], ADEDLEN_FINDERI
);
4543 ret
= ad_fset(ad
, fsp
);
4545 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp
));
4552 if (!ai_empty_finderinfo(ai
)) {
4556 ok
= set_delete_on_close(
4559 handle
->conn
->session_info
->security_token
,
4560 handle
->conn
->session_info
->unix_token
);
4562 DBG_ERR("set_delete_on_close on [%s] failed\n",
4570 static ssize_t
fruit_pwrite_meta(vfs_handle_struct
*handle
,
4571 files_struct
*fsp
, const void *data
,
4572 size_t n
, off_t offset
)
4574 struct fio
*fio
= (struct fio
*)VFS_FETCH_FSP_EXTENSION(handle
, fsp
);
4578 * Writing an all 0 blob to the metadata stream
4579 * results in the stream being removed on a macOS
4580 * server. This ensures we behave the same and it
4581 * verified by the "delete AFP_AfpInfo by writing all
4584 if (n
!= AFP_INFO_SIZE
|| offset
!= 0) {
4585 DBG_ERR("unexpected offset=%jd or size=%jd\n",
4586 (intmax_t)offset
, (intmax_t)n
);
4591 DBG_ERR("Failed to fetch fsp extension");
4595 switch (fio
->config
->meta
) {
4596 case FRUIT_META_STREAM
:
4597 nwritten
= fruit_pwrite_meta_stream(handle
, fsp
, data
,
4601 case FRUIT_META_NETATALK
:
4602 nwritten
= fruit_pwrite_meta_netatalk(handle
, fsp
, data
,
4607 DBG_ERR("Unexpected meta config [%d]\n", fio
->config
->meta
);
4614 static ssize_t
fruit_pwrite_rsrc_stream(vfs_handle_struct
*handle
,
4615 files_struct
*fsp
, const void *data
,
4616 size_t n
, off_t offset
)
4618 return SMB_VFS_NEXT_PWRITE(handle
, fsp
, data
, n
, offset
);
4621 static ssize_t
fruit_pwrite_rsrc_xattr(vfs_handle_struct
*handle
,
4622 files_struct
*fsp
, const void *data
,
4623 size_t n
, off_t offset
)
4625 return SMB_VFS_NEXT_PWRITE(handle
, fsp
, data
, n
, offset
);
4628 static ssize_t
fruit_pwrite_rsrc_adouble(vfs_handle_struct
*handle
,
4629 files_struct
*fsp
, const void *data
,
4630 size_t n
, off_t offset
)
4632 struct adouble
*ad
= NULL
;
4636 ad
= ad_fget(talloc_tos(), handle
, fsp
, ADOUBLE_RSRC
);
4638 DBG_ERR("ad_get [%s] failed\n", fsp_str_dbg(fsp
));
4642 nwritten
= SMB_VFS_NEXT_PWRITE(handle
, fsp
, data
, n
,
4643 offset
+ ad_getentryoff(ad
, ADEID_RFORK
));
4644 if (nwritten
!= n
) {
4645 DBG_ERR("Short write on [%s] [%zd/%zd]\n",
4646 fsp_str_dbg(fsp
), nwritten
, n
);
4651 if ((n
+ offset
) > ad_getentrylen(ad
, ADEID_RFORK
)) {
4652 ad_setentrylen(ad
, ADEID_RFORK
, n
+ offset
);
4653 ret
= ad_fset(ad
, fsp
);
4655 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp
));
4665 static ssize_t
fruit_pwrite_rsrc(vfs_handle_struct
*handle
,
4666 files_struct
*fsp
, const void *data
,
4667 size_t n
, off_t offset
)
4669 struct fio
*fio
= (struct fio
*)VFS_FETCH_FSP_EXTENSION(handle
, fsp
);
4673 DBG_ERR("Failed to fetch fsp extension");
4677 switch (fio
->config
->rsrc
) {
4678 case FRUIT_RSRC_STREAM
:
4679 nwritten
= fruit_pwrite_rsrc_stream(handle
, fsp
, data
, n
, offset
);
4682 case FRUIT_RSRC_ADFILE
:
4683 nwritten
= fruit_pwrite_rsrc_adouble(handle
, fsp
, data
, n
, offset
);
4686 case FRUIT_RSRC_XATTR
:
4687 nwritten
= fruit_pwrite_rsrc_xattr(handle
, fsp
, data
, n
, offset
);
4691 DBG_ERR("Unexpected rsrc config [%d]\n", fio
->config
->rsrc
);
4698 static ssize_t
fruit_pwrite(vfs_handle_struct
*handle
,
4699 files_struct
*fsp
, const void *data
,
4700 size_t n
, off_t offset
)
4702 struct fio
*fio
= (struct fio
*)VFS_FETCH_FSP_EXTENSION(handle
, fsp
);
4705 DBG_DEBUG("Path [%s] offset=%"PRIdMAX
", size=%zd\n",
4706 fsp_str_dbg(fsp
), (intmax_t)offset
, n
);
4709 return SMB_VFS_NEXT_PWRITE(handle
, fsp
, data
, n
, offset
);
4712 if (fio
->type
== ADOUBLE_META
) {
4713 nwritten
= fruit_pwrite_meta(handle
, fsp
, data
, n
, offset
);
4715 nwritten
= fruit_pwrite_rsrc(handle
, fsp
, data
, n
, offset
);
4718 DBG_DEBUG("Path [%s] nwritten=%zd\n", fsp_str_dbg(fsp
), nwritten
);
4722 struct fruit_pwrite_state
{
4724 struct vfs_aio_state vfs_aio_state
;
4727 static void fruit_pwrite_done(struct tevent_req
*subreq
);
4729 static struct tevent_req
*fruit_pwrite_send(
4730 struct vfs_handle_struct
*handle
,
4731 TALLOC_CTX
*mem_ctx
,
4732 struct tevent_context
*ev
,
4733 struct files_struct
*fsp
,
4735 size_t n
, off_t offset
)
4737 struct tevent_req
*req
= NULL
;
4738 struct tevent_req
*subreq
= NULL
;
4739 struct fruit_pwrite_state
*state
= NULL
;
4740 struct fio
*fio
= (struct fio
*)VFS_FETCH_FSP_EXTENSION(handle
, fsp
);
4742 req
= tevent_req_create(mem_ctx
, &state
,
4743 struct fruit_pwrite_state
);
4748 if (fruit_must_handle_aio_stream(fio
)) {
4749 state
->nwritten
= SMB_VFS_PWRITE(fsp
, data
, n
, offset
);
4750 if (state
->nwritten
!= n
) {
4751 if (state
->nwritten
!= -1) {
4754 tevent_req_error(req
, errno
);
4755 return tevent_req_post(req
, ev
);
4757 tevent_req_done(req
);
4758 return tevent_req_post(req
, ev
);
4761 subreq
= SMB_VFS_NEXT_PWRITE_SEND(state
, ev
, handle
, fsp
,
4763 if (tevent_req_nomem(req
, subreq
)) {
4764 return tevent_req_post(req
, ev
);
4766 tevent_req_set_callback(subreq
, fruit_pwrite_done
, req
);
4770 static void fruit_pwrite_done(struct tevent_req
*subreq
)
4772 struct tevent_req
*req
= tevent_req_callback_data(
4773 subreq
, struct tevent_req
);
4774 struct fruit_pwrite_state
*state
= tevent_req_data(
4775 req
, struct fruit_pwrite_state
);
4777 state
->nwritten
= SMB_VFS_PWRITE_RECV(subreq
, &state
->vfs_aio_state
);
4778 TALLOC_FREE(subreq
);
4780 if (tevent_req_error(req
, state
->vfs_aio_state
.error
)) {
4783 tevent_req_done(req
);
4786 static ssize_t
fruit_pwrite_recv(struct tevent_req
*req
,
4787 struct vfs_aio_state
*vfs_aio_state
)
4789 struct fruit_pwrite_state
*state
= tevent_req_data(
4790 req
, struct fruit_pwrite_state
);
4792 if (tevent_req_is_unix_error(req
, &vfs_aio_state
->error
)) {
4796 *vfs_aio_state
= state
->vfs_aio_state
;
4797 return state
->nwritten
;
4801 * Helper to stat/lstat the base file of an smb_fname.
4803 static int fruit_stat_base(vfs_handle_struct
*handle
,
4804 struct smb_filename
*smb_fname
,
4807 char *tmp_stream_name
;
4810 tmp_stream_name
= smb_fname
->stream_name
;
4811 smb_fname
->stream_name
= NULL
;
4813 rc
= SMB_VFS_NEXT_STAT(handle
, smb_fname
);
4815 rc
= SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
4817 smb_fname
->stream_name
= tmp_stream_name
;
4821 static int fruit_stat_meta_stream(vfs_handle_struct
*handle
,
4822 struct smb_filename
*smb_fname
,
4828 ret
= SMB_VFS_NEXT_STAT(handle
, smb_fname
);
4830 ret
= SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
4836 static int fruit_stat_meta_netatalk(vfs_handle_struct
*handle
,
4837 struct smb_filename
*smb_fname
,
4840 struct adouble
*ad
= NULL
;
4842 ad
= ad_get(talloc_tos(), handle
, smb_fname
, ADOUBLE_META
);
4844 DBG_INFO("fruit_stat_meta %s: %s\n",
4845 smb_fname_str_dbg(smb_fname
), strerror(errno
));
4851 /* Populate the stat struct with info from the base file. */
4852 if (fruit_stat_base(handle
, smb_fname
, follow_links
) == -1) {
4855 smb_fname
->st
.st_ex_size
= AFP_INFO_SIZE
;
4856 smb_fname
->st
.st_ex_ino
= fruit_inode(&smb_fname
->st
,
4857 smb_fname
->stream_name
);
4861 static int fruit_stat_meta(vfs_handle_struct
*handle
,
4862 struct smb_filename
*smb_fname
,
4865 struct fruit_config_data
*config
= NULL
;
4868 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
4869 struct fruit_config_data
, return -1);
4871 switch (config
->meta
) {
4872 case FRUIT_META_STREAM
:
4873 ret
= fruit_stat_meta_stream(handle
, smb_fname
, follow_links
);
4876 case FRUIT_META_NETATALK
:
4877 ret
= fruit_stat_meta_netatalk(handle
, smb_fname
, follow_links
);
4881 DBG_ERR("Unexpected meta config [%d]\n", config
->meta
);
4888 static int fruit_stat_rsrc_netatalk(vfs_handle_struct
*handle
,
4889 struct smb_filename
*smb_fname
,
4892 struct adouble
*ad
= NULL
;
4895 ad
= ad_get(talloc_tos(), handle
, smb_fname
, ADOUBLE_RSRC
);
4901 /* Populate the stat struct with info from the base file. */
4902 ret
= fruit_stat_base(handle
, smb_fname
, follow_links
);
4908 smb_fname
->st
.st_ex_size
= ad_getentrylen(ad
, ADEID_RFORK
);
4909 smb_fname
->st
.st_ex_ino
= fruit_inode(&smb_fname
->st
,
4910 smb_fname
->stream_name
);
4915 static int fruit_stat_rsrc_stream(vfs_handle_struct
*handle
,
4916 struct smb_filename
*smb_fname
,
4922 ret
= SMB_VFS_NEXT_STAT(handle
, smb_fname
);
4924 ret
= SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
4930 static int fruit_stat_rsrc_xattr(vfs_handle_struct
*handle
,
4931 struct smb_filename
*smb_fname
,
4934 #ifdef HAVE_ATTROPEN
4938 /* Populate the stat struct with info from the base file. */
4939 ret
= fruit_stat_base(handle
, smb_fname
, follow_links
);
4944 fd
= attropen(smb_fname
->base_name
,
4945 AFPRESOURCE_EA_NETATALK
,
4951 ret
= sys_fstat(fd
, &smb_fname
->st
, false);
4954 DBG_ERR("fstat [%s:%s] failed\n", smb_fname
->base_name
,
4955 AFPRESOURCE_EA_NETATALK
);
4961 smb_fname
->st
.st_ex_ino
= fruit_inode(&smb_fname
->st
,
4962 smb_fname
->stream_name
);
4972 static int fruit_stat_rsrc(vfs_handle_struct
*handle
,
4973 struct smb_filename
*smb_fname
,
4976 struct fruit_config_data
*config
= NULL
;
4979 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname
));
4981 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
4982 struct fruit_config_data
, return -1);
4984 switch (config
->rsrc
) {
4985 case FRUIT_RSRC_STREAM
:
4986 ret
= fruit_stat_rsrc_stream(handle
, smb_fname
, follow_links
);
4989 case FRUIT_RSRC_XATTR
:
4990 ret
= fruit_stat_rsrc_xattr(handle
, smb_fname
, follow_links
);
4993 case FRUIT_RSRC_ADFILE
:
4994 ret
= fruit_stat_rsrc_netatalk(handle
, smb_fname
, follow_links
);
4998 DBG_ERR("Unexpected rsrc config [%d]\n", config
->rsrc
);
5005 static int fruit_stat(vfs_handle_struct
*handle
,
5006 struct smb_filename
*smb_fname
)
5010 DEBUG(10, ("fruit_stat called for %s\n",
5011 smb_fname_str_dbg(smb_fname
)));
5013 if (!is_ntfs_stream_smb_fname(smb_fname
)
5014 || is_ntfs_default_stream_smb_fname(smb_fname
)) {
5015 rc
= SMB_VFS_NEXT_STAT(handle
, smb_fname
);
5017 update_btime(handle
, smb_fname
);
5023 * Note if lp_posix_paths() is true, we can never
5024 * get here as is_ntfs_stream_smb_fname() is
5025 * always false. So we never need worry about
5026 * not following links here.
5029 if (is_afpinfo_stream(smb_fname
)) {
5030 rc
= fruit_stat_meta(handle
, smb_fname
, true);
5031 } else if (is_afpresource_stream(smb_fname
)) {
5032 rc
= fruit_stat_rsrc(handle
, smb_fname
, true);
5034 return SMB_VFS_NEXT_STAT(handle
, smb_fname
);
5038 update_btime(handle
, smb_fname
);
5039 smb_fname
->st
.st_ex_mode
&= ~S_IFMT
;
5040 smb_fname
->st
.st_ex_mode
|= S_IFREG
;
5041 smb_fname
->st
.st_ex_blocks
=
5042 smb_fname
->st
.st_ex_size
/ STAT_ST_BLOCKSIZE
+ 1;
5047 static int fruit_lstat(vfs_handle_struct
*handle
,
5048 struct smb_filename
*smb_fname
)
5052 DEBUG(10, ("fruit_lstat called for %s\n",
5053 smb_fname_str_dbg(smb_fname
)));
5055 if (!is_ntfs_stream_smb_fname(smb_fname
)
5056 || is_ntfs_default_stream_smb_fname(smb_fname
)) {
5057 rc
= SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
5059 update_btime(handle
, smb_fname
);
5064 if (is_afpinfo_stream(smb_fname
)) {
5065 rc
= fruit_stat_meta(handle
, smb_fname
, false);
5066 } else if (is_afpresource_stream(smb_fname
)) {
5067 rc
= fruit_stat_rsrc(handle
, smb_fname
, false);
5069 return SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
5073 update_btime(handle
, smb_fname
);
5074 smb_fname
->st
.st_ex_mode
&= ~S_IFMT
;
5075 smb_fname
->st
.st_ex_mode
|= S_IFREG
;
5076 smb_fname
->st
.st_ex_blocks
=
5077 smb_fname
->st
.st_ex_size
/ STAT_ST_BLOCKSIZE
+ 1;
5082 static int fruit_fstat_meta_stream(vfs_handle_struct
*handle
,
5084 SMB_STRUCT_STAT
*sbuf
)
5086 return SMB_VFS_NEXT_FSTAT(handle
, fsp
, sbuf
);
5089 static int fruit_fstat_meta_netatalk(vfs_handle_struct
*handle
,
5091 SMB_STRUCT_STAT
*sbuf
)
5095 ret
= fruit_stat_base(handle
, fsp
->base_fsp
->fsp_name
, false);
5100 *sbuf
= fsp
->base_fsp
->fsp_name
->st
;
5101 sbuf
->st_ex_size
= AFP_INFO_SIZE
;
5102 sbuf
->st_ex_ino
= fruit_inode(sbuf
, fsp
->fsp_name
->stream_name
);
5107 static int fruit_fstat_meta(vfs_handle_struct
*handle
,
5109 SMB_STRUCT_STAT
*sbuf
,
5114 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp
));
5116 switch (fio
->config
->meta
) {
5117 case FRUIT_META_STREAM
:
5118 ret
= fruit_fstat_meta_stream(handle
, fsp
, sbuf
);
5121 case FRUIT_META_NETATALK
:
5122 ret
= fruit_fstat_meta_netatalk(handle
, fsp
, sbuf
);
5126 DBG_ERR("Unexpected meta config [%d]\n", fio
->config
->meta
);
5130 DBG_DEBUG("Path [%s] ret [%d]\n", fsp_str_dbg(fsp
), ret
);
5134 static int fruit_fstat_rsrc_xattr(vfs_handle_struct
*handle
,
5136 SMB_STRUCT_STAT
*sbuf
)
5138 return SMB_VFS_NEXT_FSTAT(handle
, fsp
, sbuf
);
5141 static int fruit_fstat_rsrc_stream(vfs_handle_struct
*handle
,
5143 SMB_STRUCT_STAT
*sbuf
)
5145 return SMB_VFS_NEXT_FSTAT(handle
, fsp
, sbuf
);
5148 static int fruit_fstat_rsrc_adouble(vfs_handle_struct
*handle
,
5150 SMB_STRUCT_STAT
*sbuf
)
5152 struct adouble
*ad
= NULL
;
5155 /* Populate the stat struct with info from the base file. */
5156 ret
= fruit_stat_base(handle
, fsp
->base_fsp
->fsp_name
, false);
5161 ad
= ad_get(talloc_tos(), handle
,
5162 fsp
->base_fsp
->fsp_name
,
5165 DBG_ERR("ad_get [%s] failed [%s]\n",
5166 fsp_str_dbg(fsp
), strerror(errno
));
5170 *sbuf
= fsp
->base_fsp
->fsp_name
->st
;
5171 sbuf
->st_ex_size
= ad_getentrylen(ad
, ADEID_RFORK
);
5172 sbuf
->st_ex_ino
= fruit_inode(sbuf
, fsp
->fsp_name
->stream_name
);
5178 static int fruit_fstat_rsrc(vfs_handle_struct
*handle
, files_struct
*fsp
,
5179 SMB_STRUCT_STAT
*sbuf
, struct fio
*fio
)
5183 switch (fio
->config
->rsrc
) {
5184 case FRUIT_RSRC_STREAM
:
5185 ret
= fruit_fstat_rsrc_stream(handle
, fsp
, sbuf
);
5188 case FRUIT_RSRC_ADFILE
:
5189 ret
= fruit_fstat_rsrc_adouble(handle
, fsp
, sbuf
);
5192 case FRUIT_RSRC_XATTR
:
5193 ret
= fruit_fstat_rsrc_xattr(handle
, fsp
, sbuf
);
5197 DBG_ERR("Unexpected rsrc config [%d]\n", fio
->config
->rsrc
);
5204 static int fruit_fstat(vfs_handle_struct
*handle
, files_struct
*fsp
,
5205 SMB_STRUCT_STAT
*sbuf
)
5207 struct fio
*fio
= (struct fio
*)VFS_FETCH_FSP_EXTENSION(handle
, fsp
);
5211 return SMB_VFS_NEXT_FSTAT(handle
, fsp
, sbuf
);
5214 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp
));
5216 if (fio
->type
== ADOUBLE_META
) {
5217 rc
= fruit_fstat_meta(handle
, fsp
, sbuf
, fio
);
5219 rc
= fruit_fstat_rsrc(handle
, fsp
, sbuf
, fio
);
5223 sbuf
->st_ex_mode
&= ~S_IFMT
;
5224 sbuf
->st_ex_mode
|= S_IFREG
;
5225 sbuf
->st_ex_blocks
= sbuf
->st_ex_size
/ STAT_ST_BLOCKSIZE
+ 1;
5228 DBG_DEBUG("Path [%s] rc [%d] size [%"PRIdMAX
"]\n",
5229 fsp_str_dbg(fsp
), rc
, (intmax_t)sbuf
->st_ex_size
);
5233 static NTSTATUS
delete_invalid_meta_stream(
5234 vfs_handle_struct
*handle
,
5235 const struct smb_filename
*smb_fname
,
5236 TALLOC_CTX
*mem_ctx
,
5237 unsigned int *pnum_streams
,
5238 struct stream_struct
**pstreams
)
5240 struct smb_filename
*sname
= NULL
;
5244 ok
= del_fruit_stream(mem_ctx
, pnum_streams
, pstreams
, AFPINFO_STREAM
);
5246 return NT_STATUS_INTERNAL_ERROR
;
5249 sname
= synthetic_smb_fname(talloc_tos(),
5250 smb_fname
->base_name
,
5251 AFPINFO_STREAM_NAME
,
5253 if (sname
== NULL
) {
5254 return NT_STATUS_NO_MEMORY
;
5257 ret
= SMB_VFS_NEXT_UNLINK(handle
, sname
);
5260 DBG_ERR("Removing [%s] failed\n", smb_fname_str_dbg(sname
));
5261 return map_nt_error_from_unix(errno
);
5264 return NT_STATUS_OK
;
5267 static NTSTATUS
fruit_streaminfo_meta_stream(
5268 vfs_handle_struct
*handle
,
5269 struct files_struct
*fsp
,
5270 const struct smb_filename
*smb_fname
,
5271 TALLOC_CTX
*mem_ctx
,
5272 unsigned int *pnum_streams
,
5273 struct stream_struct
**pstreams
)
5275 struct stream_struct
*stream
= *pstreams
;
5276 unsigned int num_streams
= *pnum_streams
;
5277 struct smb_filename
*sname
= NULL
;
5278 char *full_name
= NULL
;
5280 struct share_mode_lock
*lck
= NULL
;
5281 struct file_id id
= {0};
5282 bool delete_on_close_set
;
5288 for (i
= 0; i
< num_streams
; i
++) {
5289 if (strequal_m(stream
[i
].name
, AFPINFO_STREAM
)) {
5294 if (i
== num_streams
) {
5295 return NT_STATUS_OK
;
5298 if (stream
[i
].size
!= AFP_INFO_SIZE
) {
5299 DBG_ERR("Removing invalid AFPINFO_STREAM size [%jd] from [%s]\n",
5300 (intmax_t)stream
[i
].size
, smb_fname_str_dbg(smb_fname
));
5302 return delete_invalid_meta_stream(handle
, smb_fname
, mem_ctx
,
5303 pnum_streams
, pstreams
);
5307 * Now check if there's a delete-on-close pending on the stream. If so,
5308 * hide the stream. This behaviour was verified against a macOS 10.12
5312 sname
= synthetic_smb_fname(talloc_tos(),
5313 smb_fname
->base_name
,
5314 AFPINFO_STREAM_NAME
,
5316 if (sname
== NULL
) {
5317 status
= NT_STATUS_NO_MEMORY
;
5321 ret
= SMB_VFS_NEXT_STAT(handle
, sname
);
5323 status
= map_nt_error_from_unix(errno
);
5327 id
= SMB_VFS_NEXT_FILE_ID_CREATE(handle
, &sname
->st
);
5329 lck
= get_existing_share_mode_lock(talloc_tos(), id
);
5331 status
= NT_STATUS_OK
;
5335 full_name
= talloc_asprintf(talloc_tos(),
5339 if (full_name
== NULL
) {
5340 status
= NT_STATUS_NO_MEMORY
;
5344 status
= file_name_hash(handle
->conn
, full_name
, &name_hash
);
5345 if (!NT_STATUS_IS_OK(status
)) {
5349 delete_on_close_set
= is_delete_on_close_set(lck
, name_hash
);
5350 if (delete_on_close_set
) {
5351 ok
= del_fruit_stream(mem_ctx
,
5356 status
= NT_STATUS_INTERNAL_ERROR
;
5361 status
= NT_STATUS_OK
;
5366 TALLOC_FREE(full_name
);
5370 static NTSTATUS
fruit_streaminfo_meta_netatalk(
5371 vfs_handle_struct
*handle
,
5372 struct files_struct
*fsp
,
5373 const struct smb_filename
*smb_fname
,
5374 TALLOC_CTX
*mem_ctx
,
5375 unsigned int *pnum_streams
,
5376 struct stream_struct
**pstreams
)
5378 struct stream_struct
*stream
= *pstreams
;
5379 unsigned int num_streams
= *pnum_streams
;
5380 struct adouble
*ad
= NULL
;
5385 /* Remove the Netatalk xattr from the list */
5386 ok
= del_fruit_stream(mem_ctx
, pnum_streams
, pstreams
,
5387 ":" NETATALK_META_XATTR
":$DATA");
5389 return NT_STATUS_NO_MEMORY
;
5393 * Check if there's a AFPINFO_STREAM from the VFS streams
5394 * backend and if yes, remove it from the list
5396 for (i
= 0; i
< num_streams
; i
++) {
5397 if (strequal_m(stream
[i
].name
, AFPINFO_STREAM
)) {
5402 if (i
< num_streams
) {
5403 DBG_WARNING("Unexpected AFPINFO_STREAM on [%s]\n",
5404 smb_fname_str_dbg(smb_fname
));
5406 ok
= del_fruit_stream(mem_ctx
, pnum_streams
, pstreams
,
5409 return NT_STATUS_INTERNAL_ERROR
;
5413 ad
= ad_get(talloc_tos(), handle
, smb_fname
, ADOUBLE_META
);
5415 return NT_STATUS_OK
;
5418 is_fi_empty
= ad_empty_finderinfo(ad
);
5422 return NT_STATUS_OK
;
5425 ok
= add_fruit_stream(mem_ctx
, pnum_streams
, pstreams
,
5426 AFPINFO_STREAM_NAME
, AFP_INFO_SIZE
,
5427 smb_roundup(handle
->conn
, AFP_INFO_SIZE
));
5429 return NT_STATUS_NO_MEMORY
;
5432 return NT_STATUS_OK
;
5435 static NTSTATUS
fruit_streaminfo_meta(vfs_handle_struct
*handle
,
5436 struct files_struct
*fsp
,
5437 const struct smb_filename
*smb_fname
,
5438 TALLOC_CTX
*mem_ctx
,
5439 unsigned int *pnum_streams
,
5440 struct stream_struct
**pstreams
)
5442 struct fruit_config_data
*config
= NULL
;
5445 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct fruit_config_data
,
5446 return NT_STATUS_INTERNAL_ERROR
);
5448 switch (config
->meta
) {
5449 case FRUIT_META_NETATALK
:
5450 status
= fruit_streaminfo_meta_netatalk(handle
, fsp
, smb_fname
,
5451 mem_ctx
, pnum_streams
,
5455 case FRUIT_META_STREAM
:
5456 status
= fruit_streaminfo_meta_stream(handle
, fsp
, smb_fname
,
5457 mem_ctx
, pnum_streams
,
5462 return NT_STATUS_INTERNAL_ERROR
;
5468 static NTSTATUS
fruit_streaminfo_rsrc_stream(
5469 vfs_handle_struct
*handle
,
5470 struct files_struct
*fsp
,
5471 const struct smb_filename
*smb_fname
,
5472 TALLOC_CTX
*mem_ctx
,
5473 unsigned int *pnum_streams
,
5474 struct stream_struct
**pstreams
)
5478 ok
= filter_empty_rsrc_stream(pnum_streams
, pstreams
);
5480 DBG_ERR("Filtering resource stream failed\n");
5481 return NT_STATUS_INTERNAL_ERROR
;
5483 return NT_STATUS_OK
;
5486 static NTSTATUS
fruit_streaminfo_rsrc_xattr(
5487 vfs_handle_struct
*handle
,
5488 struct files_struct
*fsp
,
5489 const struct smb_filename
*smb_fname
,
5490 TALLOC_CTX
*mem_ctx
,
5491 unsigned int *pnum_streams
,
5492 struct stream_struct
**pstreams
)
5496 ok
= filter_empty_rsrc_stream(pnum_streams
, pstreams
);
5498 DBG_ERR("Filtering resource stream failed\n");
5499 return NT_STATUS_INTERNAL_ERROR
;
5501 return NT_STATUS_OK
;
5504 static NTSTATUS
fruit_streaminfo_rsrc_adouble(
5505 vfs_handle_struct
*handle
,
5506 struct files_struct
*fsp
,
5507 const struct smb_filename
*smb_fname
,
5508 TALLOC_CTX
*mem_ctx
,
5509 unsigned int *pnum_streams
,
5510 struct stream_struct
**pstreams
)
5512 struct stream_struct
*stream
= *pstreams
;
5513 unsigned int num_streams
= *pnum_streams
;
5514 struct adouble
*ad
= NULL
;
5520 * Check if there's a AFPRESOURCE_STREAM from the VFS streams backend
5521 * and if yes, remove it from the list
5523 for (i
= 0; i
< num_streams
; i
++) {
5524 if (strequal_m(stream
[i
].name
, AFPRESOURCE_STREAM
)) {
5529 if (i
< num_streams
) {
5530 DBG_WARNING("Unexpected AFPRESOURCE_STREAM on [%s]\n",
5531 smb_fname_str_dbg(smb_fname
));
5533 ok
= del_fruit_stream(mem_ctx
, pnum_streams
, pstreams
,
5534 AFPRESOURCE_STREAM
);
5536 return NT_STATUS_INTERNAL_ERROR
;
5540 ad
= ad_get(talloc_tos(), handle
, smb_fname
, ADOUBLE_RSRC
);
5542 return NT_STATUS_OK
;
5545 rlen
= ad_getentrylen(ad
, ADEID_RFORK
);
5549 return NT_STATUS_OK
;
5552 ok
= add_fruit_stream(mem_ctx
, pnum_streams
, pstreams
,
5553 AFPRESOURCE_STREAM_NAME
, rlen
,
5554 smb_roundup(handle
->conn
, rlen
));
5556 return NT_STATUS_NO_MEMORY
;
5559 return NT_STATUS_OK
;
5562 static NTSTATUS
fruit_streaminfo_rsrc(vfs_handle_struct
*handle
,
5563 struct files_struct
*fsp
,
5564 const struct smb_filename
*smb_fname
,
5565 TALLOC_CTX
*mem_ctx
,
5566 unsigned int *pnum_streams
,
5567 struct stream_struct
**pstreams
)
5569 struct fruit_config_data
*config
= NULL
;
5572 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct fruit_config_data
,
5573 return NT_STATUS_INTERNAL_ERROR
);
5575 switch (config
->rsrc
) {
5576 case FRUIT_RSRC_STREAM
:
5577 status
= fruit_streaminfo_rsrc_stream(handle
, fsp
, smb_fname
,
5578 mem_ctx
, pnum_streams
,
5582 case FRUIT_RSRC_XATTR
:
5583 status
= fruit_streaminfo_rsrc_xattr(handle
, fsp
, smb_fname
,
5584 mem_ctx
, pnum_streams
,
5588 case FRUIT_RSRC_ADFILE
:
5589 status
= fruit_streaminfo_rsrc_adouble(handle
, fsp
, smb_fname
,
5590 mem_ctx
, pnum_streams
,
5595 return NT_STATUS_INTERNAL_ERROR
;
5601 static NTSTATUS
fruit_streaminfo(vfs_handle_struct
*handle
,
5602 struct files_struct
*fsp
,
5603 const struct smb_filename
*smb_fname
,
5604 TALLOC_CTX
*mem_ctx
,
5605 unsigned int *pnum_streams
,
5606 struct stream_struct
**pstreams
)
5608 struct fruit_config_data
*config
= NULL
;
5611 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct fruit_config_data
,
5612 return NT_STATUS_UNSUCCESSFUL
);
5614 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname
));
5616 status
= SMB_VFS_NEXT_STREAMINFO(handle
, fsp
, smb_fname
, mem_ctx
,
5617 pnum_streams
, pstreams
);
5618 if (!NT_STATUS_IS_OK(status
)) {
5622 status
= fruit_streaminfo_meta(handle
, fsp
, smb_fname
,
5623 mem_ctx
, pnum_streams
, pstreams
);
5624 if (!NT_STATUS_IS_OK(status
)) {
5628 status
= fruit_streaminfo_rsrc(handle
, fsp
, smb_fname
,
5629 mem_ctx
, pnum_streams
, pstreams
);
5630 if (!NT_STATUS_IS_OK(status
)) {
5634 return NT_STATUS_OK
;
5637 static int fruit_ntimes(vfs_handle_struct
*handle
,
5638 const struct smb_filename
*smb_fname
,
5639 struct smb_file_time
*ft
)
5642 struct adouble
*ad
= NULL
;
5643 struct fruit_config_data
*config
= NULL
;
5645 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct fruit_config_data
,
5648 if ((config
->meta
!= FRUIT_META_NETATALK
) ||
5649 null_timespec(ft
->create_time
))
5651 return SMB_VFS_NEXT_NTIMES(handle
, smb_fname
, ft
);
5654 DEBUG(10,("set btime for %s to %s\n", smb_fname_str_dbg(smb_fname
),
5655 time_to_asc(convert_timespec_to_time_t(ft
->create_time
))));
5657 ad
= ad_get(talloc_tos(), handle
, smb_fname
, ADOUBLE_META
);
5662 ad_setdate(ad
, AD_DATE_CREATE
| AD_DATE_UNIX
,
5663 convert_time_t_to_uint32_t(ft
->create_time
.tv_sec
));
5665 rc
= ad_set(ad
, smb_fname
);
5671 DEBUG(1, ("fruit_ntimes: %s\n", smb_fname_str_dbg(smb_fname
)));
5674 return SMB_VFS_NEXT_NTIMES(handle
, smb_fname
, ft
);
5677 static int fruit_fallocate(struct vfs_handle_struct
*handle
,
5678 struct files_struct
*fsp
,
5683 struct fio
*fio
= (struct fio
*)VFS_FETCH_FSP_EXTENSION(handle
, fsp
);
5686 return SMB_VFS_NEXT_FALLOCATE(handle
, fsp
, mode
, offset
, len
);
5689 /* Let the pwrite code path handle it. */
5694 static int fruit_ftruncate_rsrc_xattr(struct vfs_handle_struct
*handle
,
5695 struct files_struct
*fsp
,
5699 return SMB_VFS_FREMOVEXATTR(fsp
, AFPRESOURCE_EA_NETATALK
);
5702 #ifdef HAVE_ATTROPEN
5703 return SMB_VFS_NEXT_FTRUNCATE(handle
, fsp
, offset
);
5708 static int fruit_ftruncate_rsrc_adouble(struct vfs_handle_struct
*handle
,
5709 struct files_struct
*fsp
,
5713 struct adouble
*ad
= NULL
;
5716 ad
= ad_fget(talloc_tos(), handle
, fsp
, ADOUBLE_RSRC
);
5718 DBG_DEBUG("ad_get [%s] failed [%s]\n",
5719 fsp_str_dbg(fsp
), strerror(errno
));
5723 ad_off
= ad_getentryoff(ad
, ADEID_RFORK
);
5725 rc
= ftruncate(fsp
->fh
->fd
, offset
+ ad_off
);
5731 ad_setentrylen(ad
, ADEID_RFORK
, offset
);
5733 rc
= ad_fset(ad
, fsp
);
5735 DBG_ERR("ad_fset [%s] failed [%s]\n",
5736 fsp_str_dbg(fsp
), strerror(errno
));
5745 static int fruit_ftruncate_rsrc_stream(struct vfs_handle_struct
*handle
,
5746 struct files_struct
*fsp
,
5750 return SMB_VFS_NEXT_UNLINK(handle
, fsp
->fsp_name
);
5753 return SMB_VFS_NEXT_FTRUNCATE(handle
, fsp
, offset
);
5756 static int fruit_ftruncate_rsrc(struct vfs_handle_struct
*handle
,
5757 struct files_struct
*fsp
,
5760 struct fio
*fio
= (struct fio
*)VFS_FETCH_FSP_EXTENSION(handle
, fsp
);
5764 DBG_ERR("Failed to fetch fsp extension");
5768 switch (fio
->config
->rsrc
) {
5769 case FRUIT_RSRC_XATTR
:
5770 ret
= fruit_ftruncate_rsrc_xattr(handle
, fsp
, offset
);
5773 case FRUIT_RSRC_ADFILE
:
5774 ret
= fruit_ftruncate_rsrc_adouble(handle
, fsp
, offset
);
5777 case FRUIT_RSRC_STREAM
:
5778 ret
= fruit_ftruncate_rsrc_stream(handle
, fsp
, offset
);
5782 DBG_ERR("Unexpected rsrc config [%d]\n", fio
->config
->rsrc
);
5790 static int fruit_ftruncate_meta(struct vfs_handle_struct
*handle
,
5791 struct files_struct
*fsp
,
5795 DBG_WARNING("ftruncate %s to %jd",
5796 fsp_str_dbg(fsp
), (intmax_t)offset
);
5797 /* OS X returns NT_STATUS_ALLOTTED_SPACE_EXCEEDED */
5802 /* OS X returns success but does nothing */
5803 DBG_INFO("ignoring ftruncate %s to %jd\n",
5804 fsp_str_dbg(fsp
), (intmax_t)offset
);
5808 static int fruit_ftruncate(struct vfs_handle_struct
*handle
,
5809 struct files_struct
*fsp
,
5812 struct fio
*fio
= (struct fio
*)VFS_FETCH_FSP_EXTENSION(handle
, fsp
);
5815 DBG_DEBUG("Path [%s] offset [%"PRIdMAX
"]\n", fsp_str_dbg(fsp
),
5820 global_fruit_config
.nego_aapl
&&
5821 is_ntfs_stream_smb_fname(fsp
->fsp_name
) &&
5822 !is_ntfs_default_stream_smb_fname(fsp
->fsp_name
))
5824 return SMB_VFS_NEXT_UNLINK(handle
, fsp
->fsp_name
);
5826 return SMB_VFS_NEXT_FTRUNCATE(handle
, fsp
, offset
);
5829 if (fio
->type
== ADOUBLE_META
) {
5830 ret
= fruit_ftruncate_meta(handle
, fsp
, offset
);
5832 ret
= fruit_ftruncate_rsrc(handle
, fsp
, offset
);
5835 DBG_DEBUG("Path [%s] result [%d]\n", fsp_str_dbg(fsp
), ret
);
5839 static NTSTATUS
fruit_create_file(vfs_handle_struct
*handle
,
5840 struct smb_request
*req
,
5841 uint16_t root_dir_fid
,
5842 struct smb_filename
*smb_fname
,
5843 uint32_t access_mask
,
5844 uint32_t share_access
,
5845 uint32_t create_disposition
,
5846 uint32_t create_options
,
5847 uint32_t file_attributes
,
5848 uint32_t oplock_request
,
5849 struct smb2_lease
*lease
,
5850 uint64_t allocation_size
,
5851 uint32_t private_flags
,
5852 struct security_descriptor
*sd
,
5853 struct ea_list
*ea_list
,
5854 files_struct
**result
,
5856 const struct smb2_create_blobs
*in_context_blobs
,
5857 struct smb2_create_blobs
*out_context_blobs
)
5860 struct fruit_config_data
*config
= NULL
;
5861 files_struct
*fsp
= NULL
;
5863 status
= check_aapl(handle
, req
, in_context_blobs
, out_context_blobs
);
5864 if (!NT_STATUS_IS_OK(status
)) {
5868 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct fruit_config_data
,
5869 return NT_STATUS_UNSUCCESSFUL
);
5871 status
= SMB_VFS_NEXT_CREATE_FILE(
5872 handle
, req
, root_dir_fid
, smb_fname
,
5873 access_mask
, share_access
,
5874 create_disposition
, create_options
,
5875 file_attributes
, oplock_request
,
5877 allocation_size
, private_flags
,
5878 sd
, ea_list
, result
,
5879 pinfo
, in_context_blobs
, out_context_blobs
);
5880 if (!NT_STATUS_IS_OK(status
)) {
5886 if (global_fruit_config
.nego_aapl
) {
5887 if (config
->posix_rename
&& fsp
->is_directory
) {
5889 * Enable POSIX directory rename behaviour
5891 fsp
->posix_flags
|= FSP_POSIX_FLAGS_RENAME
;
5896 * If this is a plain open for existing files, opening an 0
5897 * byte size resource fork MUST fail with
5898 * NT_STATUS_OBJECT_NAME_NOT_FOUND.
5900 * Cf the vfs_fruit torture tests in test_rfork_create().
5902 if (is_afpresource_stream(fsp
->fsp_name
) &&
5903 create_disposition
== FILE_OPEN
)
5905 if (fsp
->fsp_name
->st
.st_ex_size
== 0) {
5906 status
= NT_STATUS_OBJECT_NAME_NOT_FOUND
;
5911 if (is_ntfs_stream_smb_fname(smb_fname
)
5912 || fsp
->is_directory
) {
5916 if (config
->locking
== FRUIT_LOCKING_NETATALK
) {
5917 status
= fruit_check_access(
5920 map_share_mode_to_deny_mode(share_access
, 0));
5921 if (!NT_STATUS_IS_OK(status
)) {
5929 DEBUG(10, ("fruit_create_file: %s\n", nt_errstr(status
)));
5932 close_file(req
, fsp
, ERROR_CLOSE
);
5933 *result
= fsp
= NULL
;
5939 static NTSTATUS
fruit_readdir_attr(struct vfs_handle_struct
*handle
,
5940 const struct smb_filename
*fname
,
5941 TALLOC_CTX
*mem_ctx
,
5942 struct readdir_attr_data
**pattr_data
)
5944 struct fruit_config_data
*config
= NULL
;
5945 struct readdir_attr_data
*attr_data
;
5948 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
5949 struct fruit_config_data
,
5950 return NT_STATUS_UNSUCCESSFUL
);
5952 if (!global_fruit_config
.nego_aapl
) {
5953 return SMB_VFS_NEXT_READDIR_ATTR(handle
, fname
, mem_ctx
, pattr_data
);
5956 DEBUG(10, ("fruit_readdir_attr %s\n", fname
->base_name
));
5958 *pattr_data
= talloc_zero(mem_ctx
, struct readdir_attr_data
);
5959 if (*pattr_data
== NULL
) {
5960 return NT_STATUS_UNSUCCESSFUL
;
5962 attr_data
= *pattr_data
;
5963 attr_data
->type
= RDATTR_AAPL
;
5966 * Mac metadata: compressed FinderInfo, resource fork length
5969 status
= readdir_attr_macmeta(handle
, fname
, attr_data
);
5970 if (!NT_STATUS_IS_OK(status
)) {
5972 * Error handling is tricky: if we return failure from
5973 * this function, the corresponding directory entry
5974 * will to be passed to the client, so we really just
5975 * want to error out on fatal errors.
5977 if (!NT_STATUS_EQUAL(status
, NT_STATUS_ACCESS_DENIED
)) {
5985 if (config
->unix_info_enabled
) {
5986 attr_data
->attr_data
.aapl
.unix_mode
= fname
->st
.st_ex_mode
;
5992 if (!config
->readdir_attr_max_access
) {
5993 attr_data
->attr_data
.aapl
.max_access
= FILE_GENERIC_ALL
;
5995 status
= smbd_calculate_access_mask(
5999 SEC_FLAG_MAXIMUM_ALLOWED
,
6000 &attr_data
->attr_data
.aapl
.max_access
);
6001 if (!NT_STATUS_IS_OK(status
)) {
6006 return NT_STATUS_OK
;
6009 DEBUG(1, ("fruit_readdir_attr %s, error: %s\n",
6010 fname
->base_name
, nt_errstr(status
)));
6011 TALLOC_FREE(*pattr_data
);
6015 static NTSTATUS
fruit_fget_nt_acl(vfs_handle_struct
*handle
,
6017 uint32_t security_info
,
6018 TALLOC_CTX
*mem_ctx
,
6019 struct security_descriptor
**ppdesc
)
6022 struct security_ace ace
;
6024 struct fruit_config_data
*config
;
6026 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
6027 struct fruit_config_data
,
6028 return NT_STATUS_UNSUCCESSFUL
);
6030 status
= SMB_VFS_NEXT_FGET_NT_ACL(handle
, fsp
, security_info
,
6032 if (!NT_STATUS_IS_OK(status
)) {
6037 * Add MS NFS style ACEs with uid, gid and mode
6039 if (!global_fruit_config
.nego_aapl
) {
6040 return NT_STATUS_OK
;
6042 if (!config
->unix_info_enabled
) {
6043 return NT_STATUS_OK
;
6046 /* First remove any existing ACE's with NFS style mode/uid/gid SIDs. */
6047 status
= remove_virtual_nfs_aces(*ppdesc
);
6048 if (!NT_STATUS_IS_OK(status
)) {
6049 DBG_WARNING("failed to remove MS NFS style ACEs\n");
6053 /* MS NFS style mode */
6054 sid_compose(&sid
, &global_sid_Unix_NFS_Mode
, fsp
->fsp_name
->st
.st_ex_mode
);
6055 init_sec_ace(&ace
, &sid
, SEC_ACE_TYPE_ACCESS_DENIED
, 0, 0);
6056 status
= security_descriptor_dacl_add(*ppdesc
, &ace
);
6057 if (!NT_STATUS_IS_OK(status
)) {
6058 DEBUG(1,("failed to add MS NFS style ACE\n"));
6062 /* MS NFS style uid */
6063 sid_compose(&sid
, &global_sid_Unix_NFS_Users
, fsp
->fsp_name
->st
.st_ex_uid
);
6064 init_sec_ace(&ace
, &sid
, SEC_ACE_TYPE_ACCESS_DENIED
, 0, 0);
6065 status
= security_descriptor_dacl_add(*ppdesc
, &ace
);
6066 if (!NT_STATUS_IS_OK(status
)) {
6067 DEBUG(1,("failed to add MS NFS style ACE\n"));
6071 /* MS NFS style gid */
6072 sid_compose(&sid
, &global_sid_Unix_NFS_Groups
, fsp
->fsp_name
->st
.st_ex_gid
);
6073 init_sec_ace(&ace
, &sid
, SEC_ACE_TYPE_ACCESS_DENIED
, 0, 0);
6074 status
= security_descriptor_dacl_add(*ppdesc
, &ace
);
6075 if (!NT_STATUS_IS_OK(status
)) {
6076 DEBUG(1,("failed to add MS NFS style ACE\n"));
6080 return NT_STATUS_OK
;
6083 static NTSTATUS
fruit_fset_nt_acl(vfs_handle_struct
*handle
,
6085 uint32_t security_info_sent
,
6086 const struct security_descriptor
*orig_psd
)
6090 mode_t ms_nfs_mode
= 0;
6092 struct security_descriptor
*psd
= NULL
;
6093 uint32_t orig_num_aces
= 0;
6095 if (orig_psd
->dacl
!= NULL
) {
6096 orig_num_aces
= orig_psd
->dacl
->num_aces
;
6099 psd
= security_descriptor_copy(talloc_tos(), orig_psd
);
6101 return NT_STATUS_NO_MEMORY
;
6104 DBG_DEBUG("fruit_fset_nt_acl: %s\n", fsp_str_dbg(fsp
));
6106 status
= check_ms_nfs(handle
, fsp
, psd
, &ms_nfs_mode
, &do_chmod
);
6107 if (!NT_STATUS_IS_OK(status
)) {
6108 DEBUG(1, ("fruit_fset_nt_acl: check_ms_nfs failed%s\n", fsp_str_dbg(fsp
)));
6114 * If only ms_nfs ACE entries were sent, ensure we set the DACL
6115 * sent/present flags correctly now we've removed them.
6118 if (orig_num_aces
!= 0) {
6120 * Are there any ACE's left ?
6122 if (psd
->dacl
->num_aces
== 0) {
6123 /* No - clear the DACL sent/present flags. */
6124 security_info_sent
&= ~SECINFO_DACL
;
6125 psd
->type
&= ~SEC_DESC_DACL_PRESENT
;
6129 status
= SMB_VFS_NEXT_FSET_NT_ACL(handle
, fsp
, security_info_sent
, psd
);
6130 if (!NT_STATUS_IS_OK(status
)) {
6131 DEBUG(1, ("fruit_fset_nt_acl: SMB_VFS_NEXT_FSET_NT_ACL failed%s\n", fsp_str_dbg(fsp
)));
6137 if (fsp
->fh
->fd
!= -1) {
6138 result
= SMB_VFS_FCHMOD(fsp
, ms_nfs_mode
);
6140 result
= SMB_VFS_CHMOD(fsp
->conn
,
6146 DEBUG(1, ("chmod: %s, result: %d, %04o error %s\n", fsp_str_dbg(fsp
),
6147 result
, (unsigned)ms_nfs_mode
,
6149 status
= map_nt_error_from_unix(errno
);
6156 return NT_STATUS_OK
;
6159 static struct vfs_offload_ctx
*fruit_offload_ctx
;
6161 struct fruit_offload_read_state
{
6162 struct vfs_handle_struct
*handle
;
6163 struct tevent_context
*ev
;
6169 static void fruit_offload_read_done(struct tevent_req
*subreq
);
6171 static struct tevent_req
*fruit_offload_read_send(
6172 TALLOC_CTX
*mem_ctx
,
6173 struct tevent_context
*ev
,
6174 struct vfs_handle_struct
*handle
,
6181 struct tevent_req
*req
= NULL
;
6182 struct tevent_req
*subreq
= NULL
;
6183 struct fruit_offload_read_state
*state
= NULL
;
6185 req
= tevent_req_create(mem_ctx
, &state
,
6186 struct fruit_offload_read_state
);
6190 *state
= (struct fruit_offload_read_state
) {
6197 subreq
= SMB_VFS_NEXT_OFFLOAD_READ_SEND(mem_ctx
, ev
, handle
, fsp
,
6198 fsctl
, ttl
, offset
, to_copy
);
6199 if (tevent_req_nomem(subreq
, req
)) {
6200 return tevent_req_post(req
, ev
);
6202 tevent_req_set_callback(subreq
, fruit_offload_read_done
, req
);
6206 static void fruit_offload_read_done(struct tevent_req
*subreq
)
6208 struct tevent_req
*req
= tevent_req_callback_data(
6209 subreq
, struct tevent_req
);
6210 struct fruit_offload_read_state
*state
= tevent_req_data(
6211 req
, struct fruit_offload_read_state
);
6214 status
= SMB_VFS_NEXT_OFFLOAD_READ_RECV(subreq
,
6218 TALLOC_FREE(subreq
);
6219 if (tevent_req_nterror(req
, status
)) {
6223 if (state
->fsctl
!= FSCTL_SRV_REQUEST_RESUME_KEY
) {
6224 tevent_req_done(req
);
6228 status
= vfs_offload_token_ctx_init(state
->fsp
->conn
->sconn
->client
,
6229 &fruit_offload_ctx
);
6230 if (tevent_req_nterror(req
, status
)) {
6234 status
= vfs_offload_token_db_store_fsp(fruit_offload_ctx
,
6237 if (tevent_req_nterror(req
, status
)) {
6241 tevent_req_done(req
);
6245 static NTSTATUS
fruit_offload_read_recv(struct tevent_req
*req
,
6246 struct vfs_handle_struct
*handle
,
6247 TALLOC_CTX
*mem_ctx
,
6250 struct fruit_offload_read_state
*state
= tevent_req_data(
6251 req
, struct fruit_offload_read_state
);
6254 if (tevent_req_is_nterror(req
, &status
)) {
6255 tevent_req_received(req
);
6259 token
->length
= state
->token
.length
;
6260 token
->data
= talloc_move(mem_ctx
, &state
->token
.data
);
6262 tevent_req_received(req
);
6263 return NT_STATUS_OK
;
6266 struct fruit_offload_write_state
{
6267 struct vfs_handle_struct
*handle
;
6269 struct files_struct
*src_fsp
;
6270 struct files_struct
*dst_fsp
;
6274 static void fruit_offload_write_done(struct tevent_req
*subreq
);
6275 static struct tevent_req
*fruit_offload_write_send(struct vfs_handle_struct
*handle
,
6276 TALLOC_CTX
*mem_ctx
,
6277 struct tevent_context
*ev
,
6280 off_t transfer_offset
,
6281 struct files_struct
*dest_fsp
,
6285 struct tevent_req
*req
, *subreq
;
6286 struct fruit_offload_write_state
*state
;
6288 struct fruit_config_data
*config
;
6289 off_t src_off
= transfer_offset
;
6290 files_struct
*src_fsp
= NULL
;
6291 off_t to_copy
= num
;
6292 bool copyfile_enabled
= false;
6294 DEBUG(10,("soff: %ju, doff: %ju, len: %ju\n",
6295 (uintmax_t)src_off
, (uintmax_t)dest_off
, (uintmax_t)num
));
6297 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
6298 struct fruit_config_data
,
6301 req
= tevent_req_create(mem_ctx
, &state
,
6302 struct fruit_offload_write_state
);
6306 state
->handle
= handle
;
6307 state
->dst_fsp
= dest_fsp
;
6310 case FSCTL_SRV_COPYCHUNK
:
6311 case FSCTL_SRV_COPYCHUNK_WRITE
:
6312 copyfile_enabled
= config
->copyfile_enabled
;
6319 * Check if this a OS X copyfile style copychunk request with
6320 * a requested chunk count of 0 that was translated to a
6321 * offload_write_send VFS call overloading the parameters src_off
6322 * = dest_off = num = 0.
6324 if (copyfile_enabled
&& num
== 0 && src_off
== 0 && dest_off
== 0) {
6325 status
= vfs_offload_token_db_fetch_fsp(
6326 fruit_offload_ctx
, token
, &src_fsp
);
6327 if (tevent_req_nterror(req
, status
)) {
6328 return tevent_req_post(req
, ev
);
6330 state
->src_fsp
= src_fsp
;
6332 status
= vfs_stat_fsp(src_fsp
);
6333 if (tevent_req_nterror(req
, status
)) {
6334 return tevent_req_post(req
, ev
);
6337 to_copy
= src_fsp
->fsp_name
->st
.st_ex_size
;
6338 state
->is_copyfile
= true;
6341 subreq
= SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle
,
6350 if (tevent_req_nomem(subreq
, req
)) {
6351 return tevent_req_post(req
, ev
);
6354 tevent_req_set_callback(subreq
, fruit_offload_write_done
, req
);
6358 static void fruit_offload_write_done(struct tevent_req
*subreq
)
6360 struct tevent_req
*req
= tevent_req_callback_data(
6361 subreq
, struct tevent_req
);
6362 struct fruit_offload_write_state
*state
= tevent_req_data(
6363 req
, struct fruit_offload_write_state
);
6365 unsigned int num_streams
= 0;
6366 struct stream_struct
*streams
= NULL
;
6368 struct smb_filename
*src_fname_tmp
= NULL
;
6369 struct smb_filename
*dst_fname_tmp
= NULL
;
6371 status
= SMB_VFS_NEXT_OFFLOAD_WRITE_RECV(state
->handle
,
6374 TALLOC_FREE(subreq
);
6375 if (tevent_req_nterror(req
, status
)) {
6379 if (!state
->is_copyfile
) {
6380 tevent_req_done(req
);
6385 * Now copy all remaining streams. We know the share supports
6386 * streams, because we're in vfs_fruit. We don't do this async
6387 * because streams are few and small.
6389 status
= vfs_streaminfo(state
->handle
->conn
, state
->src_fsp
,
6390 state
->src_fsp
->fsp_name
,
6391 req
, &num_streams
, &streams
);
6392 if (tevent_req_nterror(req
, status
)) {
6396 if (num_streams
== 1) {
6397 /* There is always one stream, ::$DATA. */
6398 tevent_req_done(req
);
6402 for (i
= 0; i
< num_streams
; i
++) {
6403 DEBUG(10, ("%s: stream: '%s'/%zu\n",
6404 __func__
, streams
[i
].name
, (size_t)streams
[i
].size
));
6406 src_fname_tmp
= synthetic_smb_fname(
6408 state
->src_fsp
->fsp_name
->base_name
,
6411 state
->src_fsp
->fsp_name
->flags
);
6412 if (tevent_req_nomem(src_fname_tmp
, req
)) {
6416 if (is_ntfs_default_stream_smb_fname(src_fname_tmp
)) {
6417 TALLOC_FREE(src_fname_tmp
);
6421 dst_fname_tmp
= synthetic_smb_fname(
6423 state
->dst_fsp
->fsp_name
->base_name
,
6426 state
->dst_fsp
->fsp_name
->flags
);
6427 if (tevent_req_nomem(dst_fname_tmp
, req
)) {
6428 TALLOC_FREE(src_fname_tmp
);
6432 status
= copy_file(req
,
6433 state
->handle
->conn
,
6436 OPENX_FILE_CREATE_IF_NOT_EXIST
,
6438 if (!NT_STATUS_IS_OK(status
)) {
6439 DEBUG(1, ("%s: copy %s to %s failed: %s\n", __func__
,
6440 smb_fname_str_dbg(src_fname_tmp
),
6441 smb_fname_str_dbg(dst_fname_tmp
),
6442 nt_errstr(status
)));
6443 TALLOC_FREE(src_fname_tmp
);
6444 TALLOC_FREE(dst_fname_tmp
);
6445 tevent_req_nterror(req
, status
);
6449 TALLOC_FREE(src_fname_tmp
);
6450 TALLOC_FREE(dst_fname_tmp
);
6453 TALLOC_FREE(streams
);
6454 TALLOC_FREE(src_fname_tmp
);
6455 TALLOC_FREE(dst_fname_tmp
);
6456 tevent_req_done(req
);
6459 static NTSTATUS
fruit_offload_write_recv(struct vfs_handle_struct
*handle
,
6460 struct tevent_req
*req
,
6463 struct fruit_offload_write_state
*state
= tevent_req_data(
6464 req
, struct fruit_offload_write_state
);
6467 if (tevent_req_is_nterror(req
, &status
)) {
6468 DEBUG(1, ("server side copy chunk failed: %s\n",
6469 nt_errstr(status
)));
6471 tevent_req_received(req
);
6475 *copied
= state
->copied
;
6476 tevent_req_received(req
);
6478 return NT_STATUS_OK
;
6481 static char *fruit_get_bandsize_line(char **lines
, int numlines
)
6484 static bool re_initialized
= false;
6488 if (!re_initialized
) {
6489 ret
= regcomp(&re
, "^[[:blank:]]*<key>band-size</key>$", 0);
6493 re_initialized
= true;
6496 for (i
= 0; i
< numlines
; i
++) {
6497 regmatch_t matches
[1];
6499 ret
= regexec(&re
, lines
[i
], 1, matches
, 0);
6502 * Check if the match was on the last line, sa we want
6503 * the subsequent line.
6505 if (i
+ 1 == numlines
) {
6508 return lines
[i
+ 1];
6510 if (ret
!= REG_NOMATCH
) {
6518 static bool fruit_get_bandsize_from_line(char *line
, size_t *_band_size
)
6521 static bool re_initialized
= false;
6522 regmatch_t matches
[2];
6527 if (!re_initialized
) {
6530 "<integer>\\([[:digit:]]*\\)</integer>$",
6535 re_initialized
= true;
6538 ret
= regexec(&re
, line
, 2, matches
, 0);
6540 DBG_ERR("regex failed [%s]\n", line
);
6544 line
[matches
[1].rm_eo
] = '\0';
6546 ok
= conv_str_u64(&line
[matches
[1].rm_so
], &band_size
);
6550 *_band_size
= (size_t)band_size
;
6555 * This reads and parses an Info.plist from a TM sparsebundle looking for the
6556 * "band-size" key and value.
6558 static bool fruit_get_bandsize(vfs_handle_struct
*handle
,
6562 #define INFO_PLIST_MAX_SIZE 64*1024
6564 struct smb_filename
*smb_fname
= NULL
;
6565 files_struct
*fsp
= NULL
;
6566 uint8_t *file_data
= NULL
;
6567 char **lines
= NULL
;
6568 char *band_size_line
= NULL
;
6569 size_t plist_file_size
;
6576 plist
= talloc_asprintf(talloc_tos(),
6578 handle
->conn
->connectpath
,
6580 if (plist
== NULL
) {
6585 smb_fname
= synthetic_smb_fname(talloc_tos(), plist
, NULL
, NULL
, 0);
6586 if (smb_fname
== NULL
) {
6591 ret
= SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
6593 DBG_INFO("Ignoring Sparsebundle without Info.plist [%s]\n", dir
);
6598 plist_file_size
= smb_fname
->st
.st_ex_size
;
6600 if (plist_file_size
> INFO_PLIST_MAX_SIZE
) {
6601 DBG_INFO("%s is too large, ignoring\n", plist
);
6606 status
= SMB_VFS_NEXT_CREATE_FILE(
6609 0, /* root_dir_fid */
6610 smb_fname
, /* fname */
6611 FILE_GENERIC_READ
, /* access_mask */
6612 FILE_SHARE_READ
| FILE_SHARE_WRITE
, /* share_access */
6613 FILE_OPEN
, /* create_disposition */
6614 0, /* create_options */
6615 0, /* file_attributes */
6616 INTERNAL_OPEN_ONLY
, /* oplock_request */
6618 0, /* allocation_size */
6619 0, /* private_flags */
6624 NULL
, NULL
); /* create context */
6625 if (!NT_STATUS_IS_OK(status
)) {
6626 DBG_INFO("Opening [%s] failed [%s]\n",
6627 smb_fname_str_dbg(smb_fname
), nt_errstr(status
));
6632 file_data
= talloc_array(talloc_tos(), uint8_t, plist_file_size
);
6633 if (file_data
== NULL
) {
6638 nread
= SMB_VFS_NEXT_PREAD(handle
, fsp
, file_data
, plist_file_size
, 0);
6639 if (nread
!= plist_file_size
) {
6640 DBG_ERR("Short read on [%s]: %zu/%zd\n",
6641 fsp_str_dbg(fsp
), nread
, plist_file_size
);
6647 status
= close_file(NULL
, fsp
, NORMAL_CLOSE
);
6649 if (!NT_STATUS_IS_OK(status
)) {
6650 DBG_ERR("close_file failed: %s\n", nt_errstr(status
));
6655 lines
= file_lines_parse((char *)file_data
,
6659 if (lines
== NULL
) {
6664 band_size_line
= fruit_get_bandsize_line(lines
, numlines
);
6665 if (band_size_line
== NULL
) {
6666 DBG_ERR("Didn't find band-size key in [%s]\n",
6667 smb_fname_str_dbg(smb_fname
));
6672 ok
= fruit_get_bandsize_from_line(band_size_line
, band_size
);
6674 DBG_ERR("fruit_get_bandsize_from_line failed\n");
6678 DBG_DEBUG("Parsed band-size [%zu] for [%s]\n", *band_size
, plist
);
6682 status
= close_file(NULL
, fsp
, NORMAL_CLOSE
);
6683 if (!NT_STATUS_IS_OK(status
)) {
6684 DBG_ERR("close_file failed: %s\n", nt_errstr(status
));
6689 TALLOC_FREE(smb_fname
);
6690 TALLOC_FREE(file_data
);
6695 struct fruit_disk_free_state
{
6699 static bool fruit_get_num_bands(vfs_handle_struct
*handle
,
6704 struct smb_filename
*bands_dir
= NULL
;
6706 struct dirent
*e
= NULL
;
6710 path
= talloc_asprintf(talloc_tos(),
6712 handle
->conn
->connectpath
,
6718 bands_dir
= synthetic_smb_fname(talloc_tos(),
6724 if (bands_dir
== NULL
) {
6728 d
= SMB_VFS_NEXT_OPENDIR(handle
, bands_dir
, NULL
, 0);
6730 TALLOC_FREE(bands_dir
);
6736 for (e
= SMB_VFS_NEXT_READDIR(handle
, d
, NULL
);
6738 e
= SMB_VFS_NEXT_READDIR(handle
, d
, NULL
))
6740 if (ISDOT(e
->d_name
) || ISDOTDOT(e
->d_name
)) {
6746 ret
= SMB_VFS_NEXT_CLOSEDIR(handle
, d
);
6748 TALLOC_FREE(bands_dir
);
6752 DBG_DEBUG("%zu bands in [%s]\n", nbands
, smb_fname_str_dbg(bands_dir
));
6754 TALLOC_FREE(bands_dir
);
6760 static bool fruit_tmsize_do_dirent(vfs_handle_struct
*handle
,
6761 struct fruit_disk_free_state
*state
,
6766 size_t sparsebundle_strlen
= strlen("sparsebundle");
6767 size_t bandsize
= 0;
6771 p
= strstr(e
->d_name
, "sparsebundle");
6776 if (p
[sparsebundle_strlen
] != '\0') {
6780 DBG_DEBUG("Processing sparsebundle [%s]\n", e
->d_name
);
6782 ok
= fruit_get_bandsize(handle
, e
->d_name
, &bandsize
);
6785 * Beware of race conditions: this may be an uninitialized
6786 * Info.plist that a client is just creating. We don't want let
6787 * this to trigger complete failure.
6789 DBG_ERR("Processing sparsebundle [%s] failed\n", e
->d_name
);
6793 ok
= fruit_get_num_bands(handle
, e
->d_name
, &nbands
);
6796 * Beware of race conditions: this may be a backup sparsebundle
6797 * in an early stage lacking a bands subdirectory. We don't want
6798 * let this to trigger complete failure.
6800 DBG_ERR("Processing sparsebundle [%s] failed\n", e
->d_name
);
6804 if (bandsize
> SIZE_MAX
/nbands
) {
6805 DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
6809 tm_size
= bandsize
* nbands
;
6811 if (state
->total_size
+ tm_size
< state
->total_size
) {
6812 DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
6817 state
->total_size
+= tm_size
;
6819 DBG_DEBUG("[%s] tm_size [%jd] total_size [%jd]\n",
6820 e
->d_name
, (intmax_t)tm_size
, (intmax_t)state
->total_size
);
6826 * Calculate used size of a TimeMachine volume
6828 * This assumes that the volume is used only for TimeMachine.
6830 * - readdir(basedir of share), then
6831 * - for every element that matches regex "^\(.*\)\.sparsebundle$" :
6832 * - parse "\1.sparsebundle/Info.plist" and read the band-size XML key
6833 * - count band files in "\1.sparsebundle/bands/"
6834 * - calculate used size of all bands: band_count * band_size
6836 static uint64_t fruit_disk_free(vfs_handle_struct
*handle
,
6837 const struct smb_filename
*smb_fname
,
6842 struct fruit_config_data
*config
= NULL
;
6843 struct fruit_disk_free_state state
= {0};
6845 struct dirent
*e
= NULL
;
6851 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
6852 struct fruit_config_data
,
6855 if (!config
->time_machine
||
6856 config
->time_machine_max_size
== 0)
6858 return SMB_VFS_NEXT_DISK_FREE(handle
,
6865 d
= SMB_VFS_NEXT_OPENDIR(handle
, smb_fname
, NULL
, 0);
6870 for (e
= SMB_VFS_NEXT_READDIR(handle
, d
, NULL
);
6872 e
= SMB_VFS_NEXT_READDIR(handle
, d
, NULL
))
6874 ok
= fruit_tmsize_do_dirent(handle
, &state
, e
);
6876 SMB_VFS_NEXT_CLOSEDIR(handle
, d
);
6881 ret
= SMB_VFS_NEXT_CLOSEDIR(handle
, d
);
6886 dsize
= config
->time_machine_max_size
/ 512;
6887 dfree
= dsize
- (state
.total_size
/ 512);
6888 if (dfree
> dsize
) {
6898 static struct vfs_fn_pointers vfs_fruit_fns
= {
6899 .connect_fn
= fruit_connect
,
6900 .disk_free_fn
= fruit_disk_free
,
6902 /* File operations */
6903 .chmod_fn
= fruit_chmod
,
6904 .chown_fn
= fruit_chown
,
6905 .unlink_fn
= fruit_unlink
,
6906 .rename_fn
= fruit_rename
,
6907 .rmdir_fn
= fruit_rmdir
,
6908 .open_fn
= fruit_open
,
6909 .pread_fn
= fruit_pread
,
6910 .pwrite_fn
= fruit_pwrite
,
6911 .pread_send_fn
= fruit_pread_send
,
6912 .pread_recv_fn
= fruit_pread_recv
,
6913 .pwrite_send_fn
= fruit_pwrite_send
,
6914 .pwrite_recv_fn
= fruit_pwrite_recv
,
6915 .stat_fn
= fruit_stat
,
6916 .lstat_fn
= fruit_lstat
,
6917 .fstat_fn
= fruit_fstat
,
6918 .streaminfo_fn
= fruit_streaminfo
,
6919 .ntimes_fn
= fruit_ntimes
,
6920 .ftruncate_fn
= fruit_ftruncate
,
6921 .fallocate_fn
= fruit_fallocate
,
6922 .create_file_fn
= fruit_create_file
,
6923 .readdir_attr_fn
= fruit_readdir_attr
,
6924 .offload_read_send_fn
= fruit_offload_read_send
,
6925 .offload_read_recv_fn
= fruit_offload_read_recv
,
6926 .offload_write_send_fn
= fruit_offload_write_send
,
6927 .offload_write_recv_fn
= fruit_offload_write_recv
,
6929 /* NT ACL operations */
6930 .fget_nt_acl_fn
= fruit_fget_nt_acl
,
6931 .fset_nt_acl_fn
= fruit_fset_nt_acl
,
6935 NTSTATUS
vfs_fruit_init(TALLOC_CTX
*ctx
)
6937 NTSTATUS ret
= smb_register_vfs(SMB_VFS_INTERFACE_VERSION
, "fruit",
6939 if (!NT_STATUS_IS_OK(ret
)) {
6943 vfs_fruit_debug_level
= debug_add_class("fruit");
6944 if (vfs_fruit_debug_level
== -1) {
6945 vfs_fruit_debug_level
= DBGC_VFS
;
6946 DEBUG(0, ("%s: Couldn't register custom debugging class!\n",
6949 DEBUG(10, ("%s: Debug class number of '%s': %d\n",
6950 "vfs_fruit_init","fruit",vfs_fruit_debug_level
));