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/tevent_ntstatus.h"
33 #include "lib/util/tevent_unix.h"
34 #include "offload_token.h"
35 #include "string_replace.h"
38 * Enhanced OS X and Netatalk compatibility
39 * ========================================
41 * This modules takes advantage of vfs_streams_xattr and
42 * vfs_catia. VFS modules vfs_fruit and vfs_streams_xattr must be
43 * loaded in the correct order:
45 * vfs modules = catia fruit streams_xattr
47 * The module intercepts the OS X special streams "AFP_AfpInfo" and
48 * "AFP_Resource" and handles them in a special way. All other named
49 * streams are deferred to vfs_streams_xattr.
51 * The OS X client maps all NTFS illegal characters to the Unicode
52 * private range. This module optionally stores the charcters using
53 * their native ASCII encoding using vfs_catia. If you're not enabling
54 * this feature, you can skip catia from vfs modules.
56 * Finally, open modes are optionally checked against Netatalk AFP
59 * The "AFP_AfpInfo" named stream is a binary blob containing OS X
60 * extended metadata for files and directories. This module optionally
61 * reads and stores this metadata in a way compatible with Netatalk 3
62 * which stores the metadata in an EA "org.netatalk.metadata". Cf
63 * source3/include/MacExtensions.h for a description of the binary
66 * The "AFP_Resource" named stream may be arbitrarily large, thus it
67 * can't be stored in an xattr on most filesystem. ZFS on Solaris is
68 * the only available filesystem where xattrs can be of any size and
69 * the OS supports using the file APIs for xattrs.
71 * The AFP_Resource stream is stored in an AppleDouble file prepending
72 * "._" to the filename. On Solaris with ZFS the stream is optionally
73 * stored in an EA "org.netatalk.resource".
79 * The OS X SMB client sends xattrs as ADS too. For xattr interop with
80 * other protocols you may want to adjust the xattr names the VFS
81 * module vfs_streams_xattr uses for storing ADS's. This defaults to
82 * user.DosStream.ADS_NAME:$DATA and can be changed by specifying
83 * these module parameters:
85 * streams_xattr:prefix = user.
86 * streams_xattr:store_stream_type = false
92 * - log diagnostic if any needed VFS module is not loaded
93 * (eg with lp_vfs_objects())
97 static int vfs_fruit_debug_level
= DBGC_VFS
;
99 static struct global_fruit_config
{
100 bool nego_aapl
; /* client negotiated AAPL */
102 } global_fruit_config
;
105 #define DBGC_CLASS vfs_fruit_debug_level
107 #define FRUIT_PARAM_TYPE_NAME "fruit"
108 #define ADOUBLE_NAME_PREFIX "._"
110 #define NETATALK_META_XATTR "org.netatalk.Metadata"
111 #define NETATALK_RSRC_XATTR "org.netatalk.ResourceFork"
113 #if defined(HAVE_ATTROPEN)
114 #define AFPINFO_EA_NETATALK NETATALK_META_XATTR
115 #define AFPRESOURCE_EA_NETATALK NETATALK_RSRC_XATTR
117 #define AFPINFO_EA_NETATALK "user." NETATALK_META_XATTR
118 #define AFPRESOURCE_EA_NETATALK "user." NETATALK_RSRC_XATTR
121 enum apple_fork
{APPLE_FORK_DATA
, APPLE_FORK_RSRC
};
123 enum fruit_rsrc
{FRUIT_RSRC_STREAM
, FRUIT_RSRC_ADFILE
, FRUIT_RSRC_XATTR
};
124 enum fruit_meta
{FRUIT_META_STREAM
, FRUIT_META_NETATALK
};
125 enum fruit_locking
{FRUIT_LOCKING_NETATALK
, FRUIT_LOCKING_NONE
};
126 enum fruit_encoding
{FRUIT_ENC_NATIVE
, FRUIT_ENC_PRIVATE
};
128 struct fruit_config_data
{
129 enum fruit_rsrc rsrc
;
130 enum fruit_meta meta
;
131 enum fruit_locking locking
;
132 enum fruit_encoding encoding
;
133 bool use_aapl
; /* config from smb.conf */
135 bool readdir_attr_enabled
;
136 bool unix_info_enabled
;
137 bool copyfile_enabled
;
138 bool veto_appledouble
;
140 bool aapl_zero_file_id
;
143 off_t time_machine_max_size
;
144 bool wipe_intentionally_left_blank_rfork
;
145 bool delete_empty_adfiles
;
148 * Additional options, all enabled by default,
149 * possibly useful for analyzing performance. The associated
150 * operations with each of them may be expensive, so having
151 * the chance to disable them individually gives a chance
152 * tweaking the setup for the particular usecase.
154 bool readdir_attr_rsize
;
155 bool readdir_attr_finder_info
;
156 bool readdir_attr_max_access
;
159 static const struct enum_list fruit_rsrc
[] = {
160 {FRUIT_RSRC_STREAM
, "stream"}, /* pass on to vfs_streams_xattr */
161 {FRUIT_RSRC_ADFILE
, "file"}, /* ._ AppleDouble file */
162 {FRUIT_RSRC_XATTR
, "xattr"}, /* Netatalk compatible xattr (ZFS only) */
166 static const struct enum_list fruit_meta
[] = {
167 {FRUIT_META_STREAM
, "stream"}, /* pass on to vfs_streams_xattr */
168 {FRUIT_META_NETATALK
, "netatalk"}, /* Netatalk compatible xattr */
172 static const struct enum_list fruit_locking
[] = {
173 {FRUIT_LOCKING_NETATALK
, "netatalk"}, /* synchronize locks with Netatalk */
174 {FRUIT_LOCKING_NONE
, "none"},
178 static const struct enum_list fruit_encoding
[] = {
179 {FRUIT_ENC_NATIVE
, "native"}, /* map unicode private chars to ASCII */
180 {FRUIT_ENC_PRIVATE
, "private"}, /* keep unicode private chars */
184 static const char *fruit_catia_maps
=
185 "0x01:0xf001,0x02:0xf002,0x03:0xf003,0x04:0xf004,"
186 "0x05:0xf005,0x06:0xf006,0x07:0xf007,0x08:0xf008,"
187 "0x09:0xf009,0x0a:0xf00a,0x0b:0xf00b,0x0c:0xf00c,"
188 "0x0d:0xf00d,0x0e:0xf00e,0x0f:0xf00f,0x10:0xf010,"
189 "0x11:0xf011,0x12:0xf012,0x13:0xf013,0x14:0xf014,"
190 "0x15:0xf015,0x16:0xf016,0x17:0xf017,0x18:0xf018,"
191 "0x19:0xf019,0x1a:0xf01a,0x1b:0xf01b,0x1c:0xf01c,"
192 "0x1d:0xf01d,0x1e:0xf01e,0x1f:0xf01f,"
193 "0x22:0xf020,0x2a:0xf021,0x3a:0xf022,0x3c:0xf023,"
194 "0x3e:0xf024,0x3f:0xf025,0x5c:0xf026,0x7c:0xf027,"
197 /*****************************************************************************
198 * Defines, functions and data structures that deal with AppleDouble
199 *****************************************************************************/
202 * There are two AppleDouble blobs we deal with:
204 * - ADOUBLE_META - AppleDouble blob used by Netatalk for storing
205 * metadata in an xattr
207 * - ADOUBLE_RSRC - AppleDouble blob used by OS X and Netatalk in
210 typedef enum {ADOUBLE_META
, ADOUBLE_RSRC
} adouble_type_t
;
213 #define AD_VERSION2 0x00020000
214 #define AD_VERSION AD_VERSION2
217 * AppleDouble entry IDs.
219 #define ADEID_DFORK 1
220 #define ADEID_RFORK 2
222 #define ADEID_COMMENT 4
223 #define ADEID_ICONBW 5
224 #define ADEID_ICONCOL 6
225 #define ADEID_FILEI 7
226 #define ADEID_FILEDATESI 8
227 #define ADEID_FINDERI 9
228 #define ADEID_MACFILEI 10
229 #define ADEID_PRODOSFILEI 11
230 #define ADEID_MSDOSFILEI 12
231 #define ADEID_SHORTNAME 13
232 #define ADEID_AFPFILEI 14
235 /* Private Netatalk entries */
236 #define ADEID_PRIVDEV 16
237 #define ADEID_PRIVINO 17
238 #define ADEID_PRIVSYN 18
239 #define ADEID_PRIVID 19
240 #define ADEID_MAX (ADEID_PRIVID + 1)
243 * These are the real ids for the private entries,
244 * as stored in the adouble file
246 #define AD_DEV 0x80444556
247 #define AD_INO 0x80494E4F
248 #define AD_SYN 0x8053594E
249 #define AD_ID 0x8053567E
251 /* Number of actually used entries */
252 #define ADEID_NUM_XATTR 8
253 #define ADEID_NUM_DOT_UND 2
254 #define ADEID_NUM_RSRC_XATTR 1
256 /* AppleDouble magic */
257 #define AD_APPLESINGLE_MAGIC 0x00051600
258 #define AD_APPLEDOUBLE_MAGIC 0x00051607
259 #define AD_MAGIC AD_APPLEDOUBLE_MAGIC
261 /* Sizes of relevant entry bits */
262 #define ADEDLEN_MAGIC 4
263 #define ADEDLEN_VERSION 4
264 #define ADEDLEN_FILLER 16
265 #define AD_FILLER_TAG "Netatalk " /* should be 16 bytes */
266 #define AD_FILLER_TAG_OSX "Mac OS X " /* should be 16 bytes */
267 #define ADEDLEN_NENTRIES 2
268 #define AD_HEADER_LEN (ADEDLEN_MAGIC + ADEDLEN_VERSION + \
269 ADEDLEN_FILLER + ADEDLEN_NENTRIES) /* 26 */
270 #define AD_ENTRY_LEN_EID 4
271 #define AD_ENTRY_LEN_OFF 4
272 #define AD_ENTRY_LEN_LEN 4
273 #define AD_ENTRY_LEN (AD_ENTRY_LEN_EID + AD_ENTRY_LEN_OFF + AD_ENTRY_LEN_LEN)
276 #define ADEDLEN_NAME 255
277 #define ADEDLEN_COMMENT 200
278 #define ADEDLEN_FILEI 16
279 #define ADEDLEN_FINDERI 32
280 #define ADEDLEN_FILEDATESI 16
281 #define ADEDLEN_SHORTNAME 12 /* length up to 8.3 */
282 #define ADEDLEN_AFPFILEI 4
283 #define ADEDLEN_MACFILEI 4
284 #define ADEDLEN_PRODOSFILEI 8
285 #define ADEDLEN_MSDOSFILEI 2
286 #define ADEDLEN_DID 4
287 #define ADEDLEN_PRIVDEV 8
288 #define ADEDLEN_PRIVINO 8
289 #define ADEDLEN_PRIVSYN 8
290 #define ADEDLEN_PRIVID 4
293 #define ADEDOFF_MAGIC 0
294 #define ADEDOFF_VERSION (ADEDOFF_MAGIC + ADEDLEN_MAGIC)
295 #define ADEDOFF_FILLER (ADEDOFF_VERSION + ADEDLEN_VERSION)
296 #define ADEDOFF_NENTRIES (ADEDOFF_FILLER + ADEDLEN_FILLER)
298 #define ADEDOFF_FINDERI_XATTR (AD_HEADER_LEN + \
299 (ADEID_NUM_XATTR * AD_ENTRY_LEN))
300 #define ADEDOFF_COMMENT_XATTR (ADEDOFF_FINDERI_XATTR + ADEDLEN_FINDERI)
301 #define ADEDOFF_FILEDATESI_XATTR (ADEDOFF_COMMENT_XATTR + ADEDLEN_COMMENT)
302 #define ADEDOFF_AFPFILEI_XATTR (ADEDOFF_FILEDATESI_XATTR + \
304 #define ADEDOFF_PRIVDEV_XATTR (ADEDOFF_AFPFILEI_XATTR + ADEDLEN_AFPFILEI)
305 #define ADEDOFF_PRIVINO_XATTR (ADEDOFF_PRIVDEV_XATTR + ADEDLEN_PRIVDEV)
306 #define ADEDOFF_PRIVSYN_XATTR (ADEDOFF_PRIVINO_XATTR + ADEDLEN_PRIVINO)
307 #define ADEDOFF_PRIVID_XATTR (ADEDOFF_PRIVSYN_XATTR + ADEDLEN_PRIVSYN)
309 #define ADEDOFF_FINDERI_DOT_UND (AD_HEADER_LEN + \
310 (ADEID_NUM_DOT_UND * AD_ENTRY_LEN))
311 #define ADEDOFF_RFORK_DOT_UND (ADEDOFF_FINDERI_DOT_UND + ADEDLEN_FINDERI)
313 #define AD_DATASZ_XATTR (AD_HEADER_LEN + \
314 (ADEID_NUM_XATTR * AD_ENTRY_LEN) + \
315 ADEDLEN_FINDERI + ADEDLEN_COMMENT + \
316 ADEDLEN_FILEDATESI + ADEDLEN_AFPFILEI + \
317 ADEDLEN_PRIVDEV + ADEDLEN_PRIVINO + \
318 ADEDLEN_PRIVSYN + ADEDLEN_PRIVID)
320 #if AD_DATASZ_XATTR != 402
321 #error bad size for AD_DATASZ_XATTR
324 #define AD_DATASZ_DOT_UND (AD_HEADER_LEN + \
325 (ADEID_NUM_DOT_UND * AD_ENTRY_LEN) + \
327 #if AD_DATASZ_DOT_UND != 82
328 #error bad size for AD_DATASZ_DOT_UND
332 * Sharemode locks fcntl() offsets
334 #if _FILE_OFFSET_BITS == 64 || defined(HAVE_LARGEFILE)
335 #define AD_FILELOCK_BASE (UINT64_C(0x7FFFFFFFFFFFFFFF) - 9)
337 #define AD_FILELOCK_BASE (UINT32_C(0x7FFFFFFF) - 9)
339 #define BYTELOCK_MAX (AD_FILELOCK_BASE - 1)
341 #define AD_FILELOCK_OPEN_WR (AD_FILELOCK_BASE + 0)
342 #define AD_FILELOCK_OPEN_RD (AD_FILELOCK_BASE + 1)
343 #define AD_FILELOCK_RSRC_OPEN_WR (AD_FILELOCK_BASE + 2)
344 #define AD_FILELOCK_RSRC_OPEN_RD (AD_FILELOCK_BASE + 3)
345 #define AD_FILELOCK_DENY_WR (AD_FILELOCK_BASE + 4)
346 #define AD_FILELOCK_DENY_RD (AD_FILELOCK_BASE + 5)
347 #define AD_FILELOCK_RSRC_DENY_WR (AD_FILELOCK_BASE + 6)
348 #define AD_FILELOCK_RSRC_DENY_RD (AD_FILELOCK_BASE + 7)
349 #define AD_FILELOCK_OPEN_NONE (AD_FILELOCK_BASE + 8)
350 #define AD_FILELOCK_RSRC_OPEN_NONE (AD_FILELOCK_BASE + 9)
352 /* Time stuff we overload the bits a little */
353 #define AD_DATE_CREATE 0
354 #define AD_DATE_MODIFY 4
355 #define AD_DATE_BACKUP 8
356 #define AD_DATE_ACCESS 12
357 #define AD_DATE_MASK (AD_DATE_CREATE | AD_DATE_MODIFY | \
358 AD_DATE_BACKUP | AD_DATE_ACCESS)
359 #define AD_DATE_UNIX (1 << 10)
360 #define AD_DATE_START 0x80000000
361 #define AD_DATE_DELTA 946684800
362 #define AD_DATE_FROM_UNIX(x) (htonl((x) - AD_DATE_DELTA))
363 #define AD_DATE_TO_UNIX(x) (ntohl(x) + AD_DATE_DELTA)
365 #define AD_XATTR_HDR_MAGIC 0x41545452 /* 'ATTR' */
366 #define AD_XATTR_MAX_ENTRIES 1024 /* Some arbitrarily enforced limit */
367 #define AD_XATTR_HDR_SIZE 36
368 #define AD_XATTR_MAX_HDR_SIZE 65536
370 /* Accessor macros */
371 #define ad_getentrylen(ad,eid) ((ad)->ad_eid[(eid)].ade_len)
372 #define ad_getentryoff(ad,eid) ((ad)->ad_eid[(eid)].ade_off)
373 #define ad_setentrylen(ad,eid,len) ((ad)->ad_eid[(eid)].ade_len = (len))
374 #define ad_setentryoff(ad,eid,off) ((ad)->ad_eid[(eid)].ade_off = (off))
377 * Both struct ad_xattr_header and struct ad_xattr_entry describe the in memory
378 * representation as well as the on-disk format.
380 * The ad_xattr_header follows the FinderInfo data in the FinderInfo entry if
381 * the length of the FinderInfo entry is larger then 32 bytes. It is then
382 * preceeded with 2 bytes padding.
384 * Cf: https://opensource.apple.com/source/xnu/xnu-4570.1.46/bsd/vfs/vfs_xattr.c
387 struct ad_xattr_header
{
388 uint32_t adx_magic
; /* ATTR_HDR_MAGIC */
389 uint32_t adx_debug_tag
; /* for debugging == file id of owning file */
390 uint32_t adx_total_size
; /* file offset of end of attribute header + entries + data */
391 uint32_t adx_data_start
; /* file offset to attribute data area */
392 uint32_t adx_data_length
; /* length of attribute data area */
393 uint32_t adx_reserved
[3];
395 uint16_t adx_num_attrs
;
398 /* On-disk entries are aligned on 4 byte boundaries */
399 struct ad_xattr_entry
{
400 uint32_t adx_offset
; /* file offset to data */
401 uint32_t adx_length
; /* size of attribute data */
403 uint8_t adx_namelen
; /* included the NULL terminator */
404 char *adx_name
; /* NULL-terminated UTF-8 name */
413 files_struct
*ad_fsp
;
415 adouble_type_t ad_type
;
418 uint8_t ad_filler
[ADEDLEN_FILLER
];
419 struct ad_entry ad_eid
[ADEID_MAX
];
421 struct ad_xattr_header adx_header
;
422 struct ad_xattr_entry
*adx_entries
;
425 struct ad_entry_order
{
426 uint32_t id
, offset
, len
;
429 /* Netatalk AppleDouble metadata xattr */
431 struct ad_entry_order entry_order_meta_xattr
[ADEID_NUM_XATTR
+ 1] = {
432 {ADEID_FINDERI
, ADEDOFF_FINDERI_XATTR
, ADEDLEN_FINDERI
},
433 {ADEID_COMMENT
, ADEDOFF_COMMENT_XATTR
, 0},
434 {ADEID_FILEDATESI
, ADEDOFF_FILEDATESI_XATTR
, ADEDLEN_FILEDATESI
},
435 {ADEID_AFPFILEI
, ADEDOFF_AFPFILEI_XATTR
, ADEDLEN_AFPFILEI
},
436 {ADEID_PRIVDEV
, ADEDOFF_PRIVDEV_XATTR
, 0},
437 {ADEID_PRIVINO
, ADEDOFF_PRIVINO_XATTR
, 0},
438 {ADEID_PRIVSYN
, ADEDOFF_PRIVSYN_XATTR
, 0},
439 {ADEID_PRIVID
, ADEDOFF_PRIVID_XATTR
, 0},
443 /* AppleDouble resource fork file (the ones prefixed by "._") */
445 struct ad_entry_order entry_order_dot_und
[ADEID_NUM_DOT_UND
+ 1] = {
446 {ADEID_FINDERI
, ADEDOFF_FINDERI_DOT_UND
, ADEDLEN_FINDERI
},
447 {ADEID_RFORK
, ADEDOFF_RFORK_DOT_UND
, 0},
451 /* Conversion from enumerated id to on-disk AppleDouble id */
452 #define AD_EID_DISK(a) (set_eid[a])
453 static const uint32_t set_eid
[] = {
454 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
455 AD_DEV
, AD_INO
, AD_SYN
, AD_ID
458 static char empty_resourcefork
[] = {
459 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
460 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
461 0x54, 0x68, 0x69, 0x73, 0x20, 0x72, 0x65, 0x73,
462 0x6F, 0x75, 0x72, 0x63, 0x65, 0x20, 0x66, 0x6F,
463 0x72, 0x6B, 0x20, 0x69, 0x6E, 0x74, 0x65, 0x6E,
464 0x74, 0x69, 0x6F, 0x6E, 0x61, 0x6C, 0x6C, 0x79,
465 0x20, 0x6C, 0x65, 0x66, 0x74, 0x20, 0x62, 0x6C,
466 0x61, 0x6E, 0x6B, 0x20, 0x20, 0x20, 0x00, 0x00,
467 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
468 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
469 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
470 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
471 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
472 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
473 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
474 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
475 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
476 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
477 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
478 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
479 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
480 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
481 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
482 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
483 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
484 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
485 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
486 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
487 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
488 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
489 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
490 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
491 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
492 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
493 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
494 0x00, 0x1C, 0x00, 0x1E, 0xFF, 0xFF
498 /* tcon config handle */
499 struct fruit_config_data
*config
;
501 /* Denote stream type, meta or rsrc */
504 /* Whether the create created the stream */
508 * AFP_AfpInfo stream created, but not written yet, thus still a fake
509 * pipe fd. This is set to true in fruit_open_meta if there was no
510 * exisiting stream but the caller requested O_CREAT. It is later set to
511 * false when we get a write on the stream that then does open and
520 * Forward declarations
522 static struct adouble
*ad_init(TALLOC_CTX
*ctx
,
523 adouble_type_t type
);
524 static struct adouble
*ad_get(TALLOC_CTX
*ctx
,
525 vfs_handle_struct
*handle
,
526 const struct smb_filename
*smb_fname
,
527 adouble_type_t type
);
528 static int ad_set(vfs_handle_struct
*handle
,
530 const struct smb_filename
*smb_fname
);
531 static int ad_fset(struct vfs_handle_struct
*handle
,
534 static int adouble_path(TALLOC_CTX
*ctx
,
535 const struct smb_filename
*smb_fname__in
,
536 struct smb_filename
**ppsmb_fname_out
);
537 static AfpInfo
*afpinfo_new(TALLOC_CTX
*ctx
);
538 static ssize_t
afpinfo_pack(const AfpInfo
*ai
, char *buf
);
539 static AfpInfo
*afpinfo_unpack(TALLOC_CTX
*ctx
, const void *data
);
543 * Return a pointer to an AppleDouble entry
545 * Returns NULL if the entry is not present
547 static char *ad_get_entry(const struct adouble
*ad
, int eid
)
549 off_t off
= ad_getentryoff(ad
, eid
);
550 size_t len
= ad_getentrylen(ad
, eid
);
552 if (off
== 0 || len
== 0) {
556 return ad
->ad_data
+ off
;
562 static int ad_getdate(const struct adouble
*ad
,
563 unsigned int dateoff
,
566 bool xlate
= (dateoff
& AD_DATE_UNIX
);
569 dateoff
&= AD_DATE_MASK
;
570 p
= ad_get_entry(ad
, ADEID_FILEDATESI
);
575 if (dateoff
> AD_DATE_ACCESS
) {
579 memcpy(date
, p
+ dateoff
, sizeof(uint32_t));
582 *date
= AD_DATE_TO_UNIX(*date
);
590 static int ad_setdate(struct adouble
*ad
, unsigned int dateoff
, uint32_t date
)
592 bool xlate
= (dateoff
& AD_DATE_UNIX
);
595 p
= ad_get_entry(ad
, ADEID_FILEDATESI
);
600 dateoff
&= AD_DATE_MASK
;
602 date
= AD_DATE_FROM_UNIX(date
);
605 if (dateoff
> AD_DATE_ACCESS
) {
609 memcpy(p
+ dateoff
, &date
, sizeof(date
));
616 * Map on-disk AppleDouble id to enumerated id
618 static uint32_t get_eid(uint32_t eid
)
626 return ADEID_PRIVDEV
;
628 return ADEID_PRIVINO
;
630 return ADEID_PRIVSYN
;
641 * Pack AppleDouble structure into data buffer
643 static bool ad_pack(struct adouble
*ad
)
650 bufsize
= talloc_get_size(ad
->ad_data
);
651 if (bufsize
< AD_DATASZ_DOT_UND
) {
652 DBG_ERR("bad buffer size [0x%" PRIx32
"]\n", bufsize
);
656 if (offset
+ ADEDLEN_MAGIC
< offset
||
657 offset
+ ADEDLEN_MAGIC
>= bufsize
) {
660 RSIVAL(ad
->ad_data
, offset
, ad
->ad_magic
);
661 offset
+= ADEDLEN_MAGIC
;
663 if (offset
+ ADEDLEN_VERSION
< offset
||
664 offset
+ ADEDLEN_VERSION
>= bufsize
) {
667 RSIVAL(ad
->ad_data
, offset
, ad
->ad_version
);
668 offset
+= ADEDLEN_VERSION
;
670 if (offset
+ ADEDLEN_FILLER
< offset
||
671 offset
+ ADEDLEN_FILLER
>= bufsize
) {
674 if (ad
->ad_type
== ADOUBLE_RSRC
) {
675 memcpy(ad
->ad_data
+ offset
, AD_FILLER_TAG
, ADEDLEN_FILLER
);
677 offset
+= ADEDLEN_FILLER
;
679 if (offset
+ ADEDLEN_NENTRIES
< offset
||
680 offset
+ ADEDLEN_NENTRIES
>= bufsize
) {
683 offset
+= ADEDLEN_NENTRIES
;
685 for (eid
= 0, nent
= 0; eid
< ADEID_MAX
; eid
++) {
686 if (ad
->ad_eid
[eid
].ade_off
== 0) {
688 * ade_off is also used as indicator whether a
689 * specific entry is used or not
694 if (offset
+ AD_ENTRY_LEN_EID
< offset
||
695 offset
+ AD_ENTRY_LEN_EID
>= bufsize
) {
698 RSIVAL(ad
->ad_data
, offset
, AD_EID_DISK(eid
));
699 offset
+= AD_ENTRY_LEN_EID
;
701 if (offset
+ AD_ENTRY_LEN_OFF
< offset
||
702 offset
+ AD_ENTRY_LEN_OFF
>= bufsize
) {
705 RSIVAL(ad
->ad_data
, offset
, ad
->ad_eid
[eid
].ade_off
);
706 offset
+= AD_ENTRY_LEN_OFF
;
708 if (offset
+ AD_ENTRY_LEN_LEN
< offset
||
709 offset
+ AD_ENTRY_LEN_LEN
>= bufsize
) {
712 RSIVAL(ad
->ad_data
, offset
, ad
->ad_eid
[eid
].ade_len
);
713 offset
+= AD_ENTRY_LEN_LEN
;
718 if (ADEDOFF_NENTRIES
+ 2 >= bufsize
) {
721 RSSVAL(ad
->ad_data
, ADEDOFF_NENTRIES
, nent
);
726 static bool ad_unpack_xattrs(struct adouble
*ad
)
728 struct ad_xattr_header
*h
= &ad
->adx_header
;
729 const char *p
= ad
->ad_data
;
733 if (ad_getentrylen(ad
, ADEID_FINDERI
) <= ADEDLEN_FINDERI
) {
737 /* 2 bytes padding */
738 hoff
= ad_getentryoff(ad
, ADEID_FINDERI
) + ADEDLEN_FINDERI
+ 2;
740 h
->adx_magic
= RIVAL(p
, hoff
+ 0);
741 h
->adx_debug_tag
= RIVAL(p
, hoff
+ 4); /* Not used -> not checked */
742 h
->adx_total_size
= RIVAL(p
, hoff
+ 8);
743 h
->adx_data_start
= RIVAL(p
, hoff
+ 12);
744 h
->adx_data_length
= RIVAL(p
, hoff
+ 16);
745 h
->adx_flags
= RSVAL(p
, hoff
+ 32); /* Not used -> not checked */
746 h
->adx_num_attrs
= RSVAL(p
, hoff
+ 34);
748 if (h
->adx_magic
!= AD_XATTR_HDR_MAGIC
) {
749 DBG_ERR("Bad magic: 0x%" PRIx32
"\n", h
->adx_magic
);
753 if (h
->adx_total_size
> ad_getentryoff(ad
, ADEID_RFORK
)) {
754 DBG_ERR("Bad total size: 0x%" PRIx32
"\n", h
->adx_total_size
);
757 if (h
->adx_total_size
> AD_XATTR_MAX_HDR_SIZE
) {
758 DBG_ERR("Bad total size: 0x%" PRIx32
"\n", h
->adx_total_size
);
762 if (h
->adx_data_start
< (hoff
+ AD_XATTR_HDR_SIZE
)) {
763 DBG_ERR("Bad start: 0x%" PRIx32
"\n", h
->adx_data_start
);
767 if ((h
->adx_data_start
+ h
->adx_data_length
) < h
->adx_data_start
) {
768 DBG_ERR("Bad length: %" PRIu32
"\n", h
->adx_data_length
);
771 if ((h
->adx_data_start
+ h
->adx_data_length
) >
772 ad
->adx_header
.adx_total_size
)
774 DBG_ERR("Bad length: %" PRIu32
"\n", h
->adx_data_length
);
778 if (h
->adx_num_attrs
> AD_XATTR_MAX_ENTRIES
) {
779 DBG_ERR("Bad num xattrs: %" PRIu16
"\n", h
->adx_num_attrs
);
783 if (h
->adx_num_attrs
== 0) {
787 ad
->adx_entries
= talloc_zero_array(
788 ad
, struct ad_xattr_entry
, h
->adx_num_attrs
);
789 if (ad
->adx_entries
== NULL
) {
793 hoff
+= AD_XATTR_HDR_SIZE
;
795 for (i
= 0; i
< h
->adx_num_attrs
; i
++) {
796 struct ad_xattr_entry
*e
= &ad
->adx_entries
[i
];
798 hoff
= (hoff
+ 3) & ~3;
800 e
->adx_offset
= RIVAL(p
, hoff
+ 0);
801 e
->adx_length
= RIVAL(p
, hoff
+ 4);
802 e
->adx_flags
= RSVAL(p
, hoff
+ 8);
803 e
->adx_namelen
= *(p
+ hoff
+ 10);
805 if (e
->adx_offset
>= ad
->adx_header
.adx_total_size
) {
806 DBG_ERR("Bad adx_offset: %" PRIx32
"\n",
811 if ((e
->adx_offset
+ e
->adx_length
) < e
->adx_offset
) {
812 DBG_ERR("Bad adx_length: %" PRIx32
"\n",
817 if ((e
->adx_offset
+ e
->adx_length
) >
818 ad
->adx_header
.adx_total_size
)
820 DBG_ERR("Bad adx_length: %" PRIx32
"\n",
825 if (e
->adx_namelen
== 0) {
826 DBG_ERR("Bad adx_namelen: %" PRIx32
"\n",
830 if ((hoff
+ 11 + e
->adx_namelen
) < hoff
+ 11) {
831 DBG_ERR("Bad adx_namelen: %" PRIx32
"\n",
835 if ((hoff
+ 11 + e
->adx_namelen
) >
836 ad
->adx_header
.adx_data_start
)
838 DBG_ERR("Bad adx_namelen: %" PRIx32
"\n",
843 e
->adx_name
= talloc_strndup(ad
->adx_entries
,
846 if (e
->adx_name
== NULL
) {
850 DBG_DEBUG("xattr [%s] offset [0x%x] size [0x%x]\n",
851 e
->adx_name
, e
->adx_offset
, e
->adx_length
);
852 dump_data(10, (uint8_t *)(ad
->ad_data
+ e
->adx_offset
),
855 hoff
+= 11 + e
->adx_namelen
;
862 * Unpack an AppleDouble blob into a struct adoble
864 static bool ad_unpack(struct adouble
*ad
, const size_t nentries
,
867 size_t bufsize
= talloc_get_size(ad
->ad_data
);
869 uint32_t eid
, len
, off
;
873 * The size of the buffer ad->ad_data is checked when read, so
874 * we wouldn't have to check our own offsets, a few extra
875 * checks won't hurt though. We have to check the offsets we
876 * read from the buffer anyway.
879 if (bufsize
< (AD_HEADER_LEN
+ (AD_ENTRY_LEN
* nentries
))) {
880 DEBUG(1, ("bad size\n"));
884 ad
->ad_magic
= RIVAL(ad
->ad_data
, 0);
885 ad
->ad_version
= RIVAL(ad
->ad_data
, ADEDOFF_VERSION
);
886 if ((ad
->ad_magic
!= AD_MAGIC
) || (ad
->ad_version
!= AD_VERSION
)) {
887 DEBUG(1, ("wrong magic or version\n"));
891 memcpy(ad
->ad_filler
, ad
->ad_data
+ ADEDOFF_FILLER
, ADEDLEN_FILLER
);
893 adentries
= RSVAL(ad
->ad_data
, ADEDOFF_NENTRIES
);
894 if (adentries
!= nentries
) {
895 DEBUG(1, ("invalid number of entries: %zu\n",
900 /* now, read in the entry bits */
901 for (i
= 0; i
< adentries
; i
++) {
902 eid
= RIVAL(ad
->ad_data
, AD_HEADER_LEN
+ (i
* AD_ENTRY_LEN
));
904 off
= RIVAL(ad
->ad_data
, AD_HEADER_LEN
+ (i
* AD_ENTRY_LEN
) + 4);
905 len
= RIVAL(ad
->ad_data
, AD_HEADER_LEN
+ (i
* AD_ENTRY_LEN
) + 8);
907 if (!eid
|| eid
>= ADEID_MAX
) {
908 DEBUG(1, ("bogus eid %d\n", eid
));
913 * All entries other than the resource fork are
914 * expected to be read into the ad_data buffer, so
915 * ensure the specified offset is within that bound
917 if ((off
> bufsize
) && (eid
!= ADEID_RFORK
)) {
918 DEBUG(1, ("bogus eid %d: off: %" PRIu32
", len: %" PRIu32
"\n",
924 * All entries besides FinderInfo and resource fork
925 * must fit into the buffer. FinderInfo is special as
926 * it may be larger then the default 32 bytes (if it
927 * contains marshalled xattrs), but we will fixup that
928 * in ad_convert(). And the resource fork is never
929 * accessed directly by the ad_data buf (also see
930 * comment above) anyway.
932 if ((eid
!= ADEID_RFORK
) &&
933 (eid
!= ADEID_FINDERI
) &&
934 ((off
+ len
) > bufsize
)) {
935 DEBUG(1, ("bogus eid %d: off: %" PRIu32
", len: %" PRIu32
"\n",
941 * That would be obviously broken
943 if (off
> filesize
) {
944 DEBUG(1, ("bogus eid %d: off: %" PRIu32
", len: %" PRIu32
"\n",
950 * Check for any entry that has its end beyond the
953 if (off
+ len
< off
) {
954 DEBUG(1, ("offset wrap in eid %d: off: %" PRIu32
955 ", len: %" PRIu32
"\n",
960 if (off
+ len
> filesize
) {
962 * If this is the resource fork entry, we fix
963 * up the length, for any other entry we bail
966 if (eid
!= ADEID_RFORK
) {
967 DEBUG(1, ("bogus eid %d: off: %" PRIu32
968 ", len: %" PRIu32
"\n",
974 * Fixup the resource fork entry by limiting
975 * the size to entryoffset - filesize.
977 len
= filesize
- off
;
978 DEBUG(1, ("Limiting ADEID_RFORK: off: %" PRIu32
979 ", len: %" PRIu32
"\n", off
, len
));
982 ad
->ad_eid
[eid
].ade_off
= off
;
983 ad
->ad_eid
[eid
].ade_len
= len
;
986 ok
= ad_unpack_xattrs(ad
);
994 static bool ad_convert_move_reso(vfs_handle_struct
*handle
,
996 const struct smb_filename
*smb_fname
)
1005 rforklen
= ad_getentrylen(ad
, ADEID_RFORK
);
1006 if (rforklen
== 0) {
1010 buf
= talloc_size(ad
, rforklen
);
1013 * This allocates a buffer for reading the resource fork data in
1014 * one big swoop. Resource forks won't be larger then, say, 64
1015 * MB, I swear, so just doing the allocation with the talloc
1016 * limit as safeguard seems safe.
1018 DBG_ERR("Failed to allocate %zu bytes for rfork\n",
1023 rforkoff
= ad_getentryoff(ad
, ADEID_RFORK
);
1025 n
= SMB_VFS_PREAD(ad
->ad_fsp
, buf
, rforklen
, rforkoff
);
1026 if (n
!= rforklen
) {
1027 DBG_ERR("Reading %zu bytes from rfork [%s] failed: %s\n",
1028 rforklen
, fsp_str_dbg(ad
->ad_fsp
), strerror(errno
));
1032 rforkoff
= ADEDOFF_RFORK_DOT_UND
;
1034 n
= SMB_VFS_PWRITE(ad
->ad_fsp
, buf
, rforklen
, rforkoff
);
1035 if (n
!= rforklen
) {
1036 DBG_ERR("Writing %zu bytes to rfork [%s] failed: %s\n",
1037 rforklen
, fsp_str_dbg(ad
->ad_fsp
), strerror(errno
));
1041 ad_setentryoff(ad
, ADEID_RFORK
, ADEDOFF_RFORK_DOT_UND
);
1044 DBG_WARNING("ad_pack [%s] failed\n", smb_fname
->base_name
);
1048 ret
= ad_fset(handle
, ad
, ad
->ad_fsp
);
1050 DBG_ERR("ad_fset on [%s] failed\n", fsp_str_dbg(ad
->ad_fsp
));
1057 static bool ad_convert_xattr(vfs_handle_struct
*handle
,
1059 const struct smb_filename
*smb_fname
,
1060 bool *converted_xattr
)
1062 static struct char_mappings
**string_replace_cmaps
= NULL
;
1064 int saved_errno
= 0;
1069 *converted_xattr
= false;
1071 if (ad_getentrylen(ad
, ADEID_FINDERI
) == ADEDLEN_FINDERI
) {
1075 if (string_replace_cmaps
== NULL
) {
1076 const char **mappings
= NULL
;
1078 mappings
= str_list_make_v3_const(
1079 talloc_tos(), fruit_catia_maps
, NULL
);
1080 if (mappings
== NULL
) {
1083 string_replace_cmaps
= string_replace_init_map(mappings
);
1084 TALLOC_FREE(mappings
);
1087 for (i
= 0; i
< ad
->adx_header
.adx_num_attrs
; i
++) {
1088 struct ad_xattr_entry
*e
= &ad
->adx_entries
[i
];
1089 char *mapped_name
= NULL
;
1091 struct smb_filename
*stream_name
= NULL
;
1092 files_struct
*fsp
= NULL
;
1095 status
= string_replace_allocate(handle
->conn
,
1097 string_replace_cmaps
,
1100 vfs_translate_to_windows
);
1101 if (!NT_STATUS_IS_OK(status
) &&
1102 !NT_STATUS_EQUAL(status
, NT_STATUS_NONE_MAPPED
))
1104 DBG_ERR("string_replace_allocate failed\n");
1110 mapped_name
= talloc_asprintf(talloc_tos(), ":%s", tmp
);
1112 if (mapped_name
== NULL
) {
1117 stream_name
= synthetic_smb_fname(talloc_tos(),
1118 smb_fname
->base_name
,
1122 TALLOC_FREE(mapped_name
);
1123 if (stream_name
== NULL
) {
1124 DBG_ERR("synthetic_smb_fname failed\n");
1129 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name
));
1131 status
= SMB_VFS_CREATE_FILE(
1132 handle
->conn
, /* conn */
1134 0, /* root_dir_fid */
1135 stream_name
, /* fname */
1136 FILE_GENERIC_WRITE
, /* access_mask */
1137 FILE_SHARE_READ
| FILE_SHARE_WRITE
, /* share_access */
1138 FILE_OPEN_IF
, /* create_disposition */
1139 0, /* create_options */
1140 0, /* file_attributes */
1141 INTERNAL_OPEN_ONLY
, /* oplock_request */
1143 0, /* allocation_size */
1144 0, /* private_flags */
1149 NULL
, NULL
); /* create context */
1150 TALLOC_FREE(stream_name
);
1151 if (!NT_STATUS_IS_OK(status
)) {
1152 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1157 nwritten
= SMB_VFS_PWRITE(fsp
,
1158 ad
->ad_data
+ e
->adx_offset
,
1161 if (nwritten
== -1) {
1162 DBG_ERR("SMB_VFS_PWRITE failed\n");
1163 saved_errno
= errno
;
1164 close_file(NULL
, fsp
, ERROR_CLOSE
);
1165 errno
= saved_errno
;
1170 status
= close_file(NULL
, fsp
, NORMAL_CLOSE
);
1171 if (!NT_STATUS_IS_OK(status
)) {
1178 ad_setentrylen(ad
, ADEID_FINDERI
, ADEDLEN_FINDERI
);
1182 DBG_WARNING("ad_pack [%s] failed\n", smb_fname
->base_name
);
1186 rc
= ad_fset(handle
, ad
, ad
->ad_fsp
);
1188 DBG_ERR("ad_fset on [%s] failed: %s\n",
1189 fsp_str_dbg(ad
->ad_fsp
), strerror(errno
));
1194 ok
= ad_convert_move_reso(handle
, ad
, smb_fname
);
1199 *converted_xattr
= true;
1206 static bool ad_convert_finderinfo(vfs_handle_struct
*handle
,
1208 const struct smb_filename
*smb_fname
)
1213 struct smb_filename
*stream_name
= NULL
;
1214 files_struct
*fsp
= NULL
;
1218 int saved_errno
= 0;
1221 cmp
= memcmp(ad
->ad_filler
, AD_FILLER_TAG_OSX
, ADEDLEN_FILLER
);
1226 p_ad
= ad_get_entry(ad
, ADEID_FINDERI
);
1231 ai
= afpinfo_new(talloc_tos());
1236 memcpy(ai
->afpi_FinderInfo
, p_ad
, ADEDLEN_FINDERI
);
1238 aiblob
= data_blob_talloc(talloc_tos(), NULL
, AFP_INFO_SIZE
);
1239 if (aiblob
.data
== NULL
) {
1244 size
= afpinfo_pack(ai
, (char *)aiblob
.data
);
1246 if (size
!= AFP_INFO_SIZE
) {
1250 stream_name
= synthetic_smb_fname(talloc_tos(),
1251 smb_fname
->base_name
,
1255 if (stream_name
== NULL
) {
1256 data_blob_free(&aiblob
);
1257 DBG_ERR("synthetic_smb_fname failed\n");
1261 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name
));
1263 status
= SMB_VFS_CREATE_FILE(
1264 handle
->conn
, /* conn */
1266 0, /* root_dir_fid */
1267 stream_name
, /* fname */
1268 FILE_GENERIC_WRITE
, /* access_mask */
1269 FILE_SHARE_READ
| FILE_SHARE_WRITE
, /* share_access */
1270 FILE_OPEN_IF
, /* create_disposition */
1271 0, /* create_options */
1272 0, /* file_attributes */
1273 INTERNAL_OPEN_ONLY
, /* oplock_request */
1275 0, /* allocation_size */
1276 0, /* private_flags */
1281 NULL
, NULL
); /* create context */
1282 TALLOC_FREE(stream_name
);
1283 if (!NT_STATUS_IS_OK(status
)) {
1284 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1288 nwritten
= SMB_VFS_PWRITE(fsp
,
1292 if (nwritten
== -1) {
1293 DBG_ERR("SMB_VFS_PWRITE failed\n");
1294 saved_errno
= errno
;
1295 close_file(NULL
, fsp
, ERROR_CLOSE
);
1296 errno
= saved_errno
;
1300 status
= close_file(NULL
, fsp
, NORMAL_CLOSE
);
1301 if (!NT_STATUS_IS_OK(status
)) {
1309 static bool ad_convert_truncate(vfs_handle_struct
*handle
,
1311 const struct smb_filename
*smb_fname
)
1316 newlen
= ADEDOFF_RFORK_DOT_UND
+ ad_getentrylen(ad
, ADEID_RFORK
);
1318 rc
= SMB_VFS_FTRUNCATE(ad
->ad_fsp
, newlen
);
1326 static bool ad_convert_blank_rfork(vfs_handle_struct
*handle
,
1330 struct fruit_config_data
*config
= NULL
;
1331 size_t rforklen
= sizeof(empty_resourcefork
);
1340 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
1341 struct fruit_config_data
, return false);
1343 if (!config
->wipe_intentionally_left_blank_rfork
) {
1347 if (ad_getentrylen(ad
, ADEID_RFORK
) != rforklen
) {
1351 nread
= SMB_VFS_PREAD(ad
->ad_fsp
, buf
, rforklen
, ADEDOFF_RFORK_DOT_UND
);
1352 if (nread
!= rforklen
) {
1353 DBG_ERR("Reading %zu bytes from rfork [%s] failed: %s\n",
1354 rforklen
, fsp_str_dbg(ad
->ad_fsp
), strerror(errno
));
1358 cmp
= memcmp(buf
, empty_resourcefork
, rforklen
);
1363 ad_setentrylen(ad
, ADEID_RFORK
, 0);
1369 rc
= ad_fset(handle
, ad
, ad
->ad_fsp
);
1371 DBG_ERR("ad_fset on [%s] failed\n", fsp_str_dbg(ad
->ad_fsp
));
1379 static bool ad_convert_delete_adfile(vfs_handle_struct
*handle
,
1381 const struct smb_filename
*smb_fname
)
1383 struct fruit_config_data
*config
= NULL
;
1384 struct smb_filename
*ad_name
= NULL
;
1387 if (ad_getentrylen(ad
, ADEID_RFORK
) > 0) {
1391 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
1392 struct fruit_config_data
, return false);
1394 if (!config
->delete_empty_adfiles
) {
1398 rc
= adouble_path(talloc_tos(), smb_fname
, &ad_name
);
1403 rc
= SMB_VFS_NEXT_UNLINK(handle
, ad_name
);
1405 DBG_ERR("Unlinking [%s] failed: %s\n",
1406 smb_fname_str_dbg(ad_name
), strerror(errno
));
1407 TALLOC_FREE(ad_name
);
1411 DBG_WARNING("Unlinked [%s] after conversion\n", smb_fname_str_dbg(ad_name
));
1412 TALLOC_FREE(ad_name
);
1418 * Convert from Apple's ._ file to Netatalk
1420 * Apple's AppleDouble may contain a FinderInfo entry longer then 32
1421 * bytes containing packed xattrs.
1423 * @return -1 in case an error occurred, 0 if no conversion was done, 1
1426 static int ad_convert(struct vfs_handle_struct
*handle
,
1427 const struct smb_filename
*smb_fname
)
1429 struct adouble
*ad
= NULL
;
1431 bool converted_xattr
= false;
1435 ad
= ad_get(talloc_tos(), handle
, smb_fname
, ADOUBLE_RSRC
);
1440 ok
= ad_convert_xattr(handle
, ad
, smb_fname
, &converted_xattr
);
1446 ok
= ad_convert_blank_rfork(handle
, ad
, &blank
);
1452 if (converted_xattr
|| blank
) {
1453 ok
= ad_convert_truncate(handle
, ad
, smb_fname
);
1460 ok
= ad_convert_finderinfo(handle
, ad
, smb_fname
);
1462 DBG_ERR("Failed to convert [%s]\n",
1463 smb_fname_str_dbg(smb_fname
));
1468 ok
= ad_convert_delete_adfile(handle
, ad
, smb_fname
);
1481 * Read and parse Netatalk AppleDouble metadata xattr
1483 static ssize_t
ad_read_meta(vfs_handle_struct
*handle
,
1485 const struct smb_filename
*smb_fname
)
1491 DEBUG(10, ("reading meta xattr for %s\n", smb_fname
->base_name
));
1493 ealen
= SMB_VFS_GETXATTR(handle
->conn
, smb_fname
,
1494 AFPINFO_EA_NETATALK
, ad
->ad_data
,
1500 if (errno
== ENOATTR
) {
1506 DEBUG(2, ("error reading meta xattr: %s\n",
1512 if (ealen
!= AD_DATASZ_XATTR
) {
1513 DEBUG(2, ("bad size %zd\n", ealen
));
1519 /* Now parse entries */
1520 ok
= ad_unpack(ad
, ADEID_NUM_XATTR
, AD_DATASZ_XATTR
);
1522 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1528 if (!ad_getentryoff(ad
, ADEID_FINDERI
)
1529 || !ad_getentryoff(ad
, ADEID_COMMENT
)
1530 || !ad_getentryoff(ad
, ADEID_FILEDATESI
)
1531 || !ad_getentryoff(ad
, ADEID_AFPFILEI
)
1532 || !ad_getentryoff(ad
, ADEID_PRIVDEV
)
1533 || !ad_getentryoff(ad
, ADEID_PRIVINO
)
1534 || !ad_getentryoff(ad
, ADEID_PRIVSYN
)
1535 || !ad_getentryoff(ad
, ADEID_PRIVID
)) {
1536 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1543 DEBUG(10, ("reading meta xattr for %s, rc: %d\n",
1544 smb_fname
->base_name
, rc
));
1548 if (errno
== EINVAL
) {
1550 (void)SMB_VFS_REMOVEXATTR(handle
->conn
,
1552 AFPINFO_EA_NETATALK
);
1560 static int ad_open_rsrc(vfs_handle_struct
*handle
,
1561 const struct smb_filename
*smb_fname
,
1564 files_struct
**_fsp
)
1567 struct smb_filename
*adp_smb_fname
= NULL
;
1568 files_struct
*fsp
= NULL
;
1569 uint32_t access_mask
;
1570 uint32_t share_access
;
1571 uint32_t create_disposition
;
1574 ret
= adouble_path(talloc_tos(), smb_fname
, &adp_smb_fname
);
1579 ret
= SMB_VFS_STAT(handle
->conn
, adp_smb_fname
);
1581 TALLOC_FREE(adp_smb_fname
);
1585 access_mask
= FILE_GENERIC_READ
;
1586 share_access
= FILE_SHARE_READ
| FILE_SHARE_WRITE
;
1587 create_disposition
= FILE_OPEN
;
1589 if (flags
& O_RDWR
) {
1590 access_mask
|= FILE_GENERIC_WRITE
;
1591 share_access
&= ~FILE_SHARE_WRITE
;
1594 status
= SMB_VFS_CREATE_FILE(
1595 handle
->conn
, /* conn */
1597 0, /* root_dir_fid */
1602 0, /* create_options */
1603 0, /* file_attributes */
1604 INTERNAL_OPEN_ONLY
, /* oplock_request */
1606 0, /* allocation_size */
1607 0, /* private_flags */
1612 NULL
, NULL
); /* create context */
1613 TALLOC_FREE(adp_smb_fname
);
1614 if (!NT_STATUS_IS_OK(status
)) {
1615 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1624 * Here's the deal: for ADOUBLE_META we can do without an fd as we can issue
1625 * path based xattr calls. For ADOUBLE_RSRC however we need a full-fledged fd
1626 * for file IO on the ._ file.
1628 static int ad_open(vfs_handle_struct
*handle
,
1631 const struct smb_filename
*smb_fname
,
1637 DBG_DEBUG("Path [%s] type [%s]\n", smb_fname
->base_name
,
1638 ad
->ad_type
== ADOUBLE_META
? "meta" : "rsrc");
1640 if (ad
->ad_type
== ADOUBLE_META
) {
1646 ad
->ad_opened
= false;
1650 ret
= ad_open_rsrc(handle
, smb_fname
, flags
, mode
, &ad
->ad_fsp
);
1654 ad
->ad_opened
= true;
1656 DBG_DEBUG("Path [%s] type [%s]\n",
1657 smb_fname
->base_name
,
1658 ad
->ad_type
== ADOUBLE_META
? "meta" : "rsrc");
1663 static ssize_t
ad_read_rsrc_adouble(vfs_handle_struct
*handle
,
1665 const struct smb_filename
*smb_fname
)
1673 ret
= SMB_VFS_NEXT_FSTAT(handle
, ad
->ad_fsp
, &ad
->ad_fsp
->fsp_name
->st
);
1675 DBG_ERR("fstat [%s] failed: %s\n",
1676 fsp_str_dbg(ad
->ad_fsp
), strerror(errno
));
1681 * AppleDouble file header content and size, two cases:
1683 * - without xattrs it is exactly AD_DATASZ_DOT_UND (82) bytes large
1684 * - with embedded xattrs it can be larger, up to AD_XATTR_MAX_HDR_SIZE
1686 * Read as much as we can up to AD_XATTR_MAX_HDR_SIZE.
1688 size
= ad
->ad_fsp
->fsp_name
->st
.st_ex_size
;
1689 if (size
> talloc_array_length(ad
->ad_data
)) {
1690 if (size
> AD_XATTR_MAX_HDR_SIZE
) {
1691 size
= AD_XATTR_MAX_HDR_SIZE
;
1693 p_ad
= talloc_realloc(ad
, ad
->ad_data
, char, size
);
1700 len
= SMB_VFS_NEXT_PREAD(handle
, ad
->ad_fsp
, ad
->ad_data
, talloc_array_length(ad
->ad_data
), 0);
1701 if (len
!= talloc_array_length(ad
->ad_data
)) {
1702 DBG_NOTICE("%s %s: bad size: %zd\n",
1703 smb_fname
->base_name
, strerror(errno
), len
);
1707 /* Now parse entries */
1708 ok
= ad_unpack(ad
, ADEID_NUM_DOT_UND
, size
);
1710 DBG_ERR("invalid AppleDouble resource %s\n",
1711 smb_fname
->base_name
);
1716 if ((ad_getentryoff(ad
, ADEID_FINDERI
) != ADEDOFF_FINDERI_DOT_UND
)
1717 || (ad_getentrylen(ad
, ADEID_FINDERI
) < ADEDLEN_FINDERI
)
1718 || (ad_getentryoff(ad
, ADEID_RFORK
) < ADEDOFF_RFORK_DOT_UND
)) {
1719 DBG_ERR("invalid AppleDouble resource %s\n",
1720 smb_fname
->base_name
);
1729 * Read and parse resource fork, either ._ AppleDouble file or xattr
1731 static ssize_t
ad_read_rsrc(vfs_handle_struct
*handle
,
1733 const struct smb_filename
*smb_fname
)
1735 return ad_read_rsrc_adouble(handle
, ad
, smb_fname
);
1739 * Read and unpack an AppleDouble metadata xattr or resource
1741 static ssize_t
ad_read(vfs_handle_struct
*handle
,
1743 const struct smb_filename
*smb_fname
)
1745 switch (ad
->ad_type
) {
1747 return ad_read_meta(handle
, ad
, smb_fname
);
1749 return ad_read_rsrc(handle
, ad
, smb_fname
);
1755 static int adouble_destructor(struct adouble
*ad
)
1759 if (!ad
->ad_opened
) {
1763 SMB_ASSERT(ad
->ad_fsp
!= NULL
);
1765 status
= close_file(NULL
, ad
->ad_fsp
, NORMAL_CLOSE
);
1766 if (!NT_STATUS_IS_OK(status
)) {
1767 DBG_ERR("Closing [%s] failed: %s\n",
1768 fsp_str_dbg(ad
->ad_fsp
), nt_errstr(status
));
1775 * Allocate a struct adouble without initialiing it
1777 * The struct is either hang of the fsp extension context or if fsp is
1780 * @param[in] ctx talloc context
1781 * @param[in] handle vfs handle
1782 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1784 * @return adouble handle
1786 static struct adouble
*ad_alloc(TALLOC_CTX
*ctx
,
1787 adouble_type_t type
)
1795 adsize
= AD_DATASZ_XATTR
;
1798 adsize
= AD_DATASZ_DOT_UND
;
1804 ad
= talloc_zero(ctx
, struct adouble
);
1811 ad
->ad_data
= talloc_zero_array(ad
, char, adsize
);
1812 if (ad
->ad_data
== NULL
) {
1819 ad
->ad_magic
= AD_MAGIC
;
1820 ad
->ad_version
= AD_VERSION
;
1822 talloc_set_destructor(ad
, adouble_destructor
);
1832 * Allocate and initialize a new struct adouble
1834 * @param[in] ctx talloc context
1835 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1837 * @return adouble handle, initialized
1839 static struct adouble
*ad_init(TALLOC_CTX
*ctx
,
1840 adouble_type_t type
)
1843 const struct ad_entry_order
*eid
;
1844 struct adouble
*ad
= NULL
;
1845 time_t t
= time(NULL
);
1849 eid
= entry_order_meta_xattr
;
1852 eid
= entry_order_dot_und
;
1858 ad
= ad_alloc(ctx
, type
);
1864 ad
->ad_eid
[eid
->id
].ade_off
= eid
->offset
;
1865 ad
->ad_eid
[eid
->id
].ade_len
= eid
->len
;
1869 /* put something sane in the date fields */
1870 ad_setdate(ad
, AD_DATE_CREATE
| AD_DATE_UNIX
, t
);
1871 ad_setdate(ad
, AD_DATE_MODIFY
| AD_DATE_UNIX
, t
);
1872 ad_setdate(ad
, AD_DATE_ACCESS
| AD_DATE_UNIX
, t
);
1873 ad_setdate(ad
, AD_DATE_BACKUP
, htonl(AD_DATE_START
));
1881 static struct adouble
*ad_get_internal(TALLOC_CTX
*ctx
,
1882 vfs_handle_struct
*handle
,
1884 const struct smb_filename
*smb_fname
,
1885 adouble_type_t type
)
1889 struct adouble
*ad
= NULL
;
1893 smb_fname
= fsp
->base_fsp
->fsp_name
;
1896 DEBUG(10, ("ad_get(%s) called for %s\n",
1897 type
== ADOUBLE_META
? "meta" : "rsrc",
1898 smb_fname
->base_name
));
1900 ad
= ad_alloc(ctx
, type
);
1906 /* Try rw first so we can use the fd in ad_convert() */
1909 rc
= ad_open(handle
, ad
, fsp
, smb_fname
, mode
, 0);
1910 if (rc
== -1 && ((errno
== EROFS
) || (errno
== EACCES
))) {
1912 rc
= ad_open(handle
, ad
, fsp
, smb_fname
, mode
, 0);
1915 DBG_DEBUG("ad_open [%s] error [%s]\n",
1916 smb_fname
->base_name
, strerror(errno
));
1921 len
= ad_read(handle
, ad
, smb_fname
);
1923 DEBUG(10, ("error reading AppleDouble for %s\n",
1924 smb_fname
->base_name
));
1930 DEBUG(10, ("ad_get(%s) for %s returning %d\n",
1931 type
== ADOUBLE_META
? "meta" : "rsrc",
1932 smb_fname
->base_name
, rc
));
1941 * Return AppleDouble data for a file
1943 * @param[in] ctx talloc context
1944 * @param[in] handle vfs handle
1945 * @param[in] smb_fname pathname to file or directory
1946 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1948 * @return talloced struct adouble or NULL on error
1950 static struct adouble
*ad_get(TALLOC_CTX
*ctx
,
1951 vfs_handle_struct
*handle
,
1952 const struct smb_filename
*smb_fname
,
1953 adouble_type_t type
)
1955 return ad_get_internal(ctx
, handle
, NULL
, smb_fname
, type
);
1959 * Return AppleDouble data for a file
1961 * @param[in] ctx talloc context
1962 * @param[in] handle vfs handle
1963 * @param[in] fsp fsp to use for IO
1964 * @param[in] type type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1966 * @return talloced struct adouble or NULL on error
1968 static struct adouble
*ad_fget(TALLOC_CTX
*ctx
, vfs_handle_struct
*handle
,
1969 files_struct
*fsp
, adouble_type_t type
)
1971 return ad_get_internal(ctx
, handle
, fsp
, NULL
, type
);
1975 * Set AppleDouble metadata on a file or directory
1977 * @param[in] ad adouble handle
1979 * @param[in] smb_fname pathname to file or directory
1981 * @return status code, 0 means success
1983 static int ad_set(vfs_handle_struct
*handle
,
1985 const struct smb_filename
*smb_fname
)
1990 DBG_DEBUG("Path [%s]\n", smb_fname
->base_name
);
1992 if (ad
->ad_type
!= ADOUBLE_META
) {
1993 DBG_ERR("ad_set on [%s] used with ADOUBLE_RSRC\n",
1994 smb_fname
->base_name
);
2003 ret
= SMB_VFS_SETXATTR(handle
->conn
,
2005 AFPINFO_EA_NETATALK
,
2007 AD_DATASZ_XATTR
, 0);
2009 DBG_DEBUG("Path [%s] ret [%d]\n", smb_fname
->base_name
, ret
);
2015 * Set AppleDouble metadata on a file or directory
2017 * @param[in] ad adouble handle
2018 * @param[in] fsp file handle
2020 * @return status code, 0 means success
2022 static int ad_fset(struct vfs_handle_struct
*handle
,
2030 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp
));
2033 || (fsp
->fh
== NULL
)
2034 || (fsp
->fh
->fd
== -1))
2036 smb_panic("bad fsp");
2044 switch (ad
->ad_type
) {
2046 rc
= SMB_VFS_NEXT_SETXATTR(handle
,
2048 AFPINFO_EA_NETATALK
,
2050 AD_DATASZ_XATTR
, 0);
2054 len
= SMB_VFS_NEXT_PWRITE(handle
,
2059 if (len
!= AD_DATASZ_DOT_UND
) {
2060 DBG_ERR("short write on %s: %zd", fsp_str_dbg(fsp
), len
);
2070 DBG_DEBUG("Path [%s] rc [%d]\n", fsp_str_dbg(fsp
), rc
);
2075 /*****************************************************************************
2077 *****************************************************************************/
2079 static bool is_afpinfo_stream(const struct smb_filename
*smb_fname
)
2081 if (strncasecmp_m(smb_fname
->stream_name
,
2082 AFPINFO_STREAM_NAME
,
2083 strlen(AFPINFO_STREAM_NAME
)) == 0) {
2089 static bool is_afpresource_stream(const struct smb_filename
*smb_fname
)
2091 if (strncasecmp_m(smb_fname
->stream_name
,
2092 AFPRESOURCE_STREAM_NAME
,
2093 strlen(AFPRESOURCE_STREAM_NAME
)) == 0) {
2100 * Test whether stream is an Apple stream.
2102 static bool is_apple_stream(const struct smb_filename
*smb_fname
)
2104 if (is_afpinfo_stream(smb_fname
)) {
2107 if (is_afpresource_stream(smb_fname
)) {
2113 static bool is_adouble_file(const char *path
)
2115 const char *p
= NULL
;
2118 p
= strrchr(path
, '/');
2126 ADOUBLE_NAME_PREFIX
,
2127 strlen(ADOUBLE_NAME_PREFIX
));
2135 * Initialize config struct from our smb.conf config parameters
2137 static int init_fruit_config(vfs_handle_struct
*handle
)
2139 struct fruit_config_data
*config
;
2141 const char *tm_size_str
= NULL
;
2143 config
= talloc_zero(handle
->conn
, struct fruit_config_data
);
2145 DEBUG(1, ("talloc_zero() failed\n"));
2151 * Versions up to Samba 4.5.x had a spelling bug in the
2152 * fruit:resource option calling lp_parm_enum with
2153 * "res*s*ource" (ie two s).
2155 * In Samba 4.6 we accept both the wrong and the correct
2156 * spelling, in Samba 4.7 the bad spelling will be removed.
2158 enumval
= lp_parm_enum(SNUM(handle
->conn
), FRUIT_PARAM_TYPE_NAME
,
2159 "ressource", fruit_rsrc
, FRUIT_RSRC_ADFILE
);
2160 if (enumval
== -1) {
2161 DEBUG(1, ("value for %s: resource type unknown\n",
2162 FRUIT_PARAM_TYPE_NAME
));
2165 config
->rsrc
= (enum fruit_rsrc
)enumval
;
2167 enumval
= lp_parm_enum(SNUM(handle
->conn
), FRUIT_PARAM_TYPE_NAME
,
2168 "resource", fruit_rsrc
, enumval
);
2169 if (enumval
== -1) {
2170 DEBUG(1, ("value for %s: resource type unknown\n",
2171 FRUIT_PARAM_TYPE_NAME
));
2174 config
->rsrc
= (enum fruit_rsrc
)enumval
;
2176 enumval
= lp_parm_enum(SNUM(handle
->conn
), FRUIT_PARAM_TYPE_NAME
,
2177 "metadata", fruit_meta
, FRUIT_META_NETATALK
);
2178 if (enumval
== -1) {
2179 DEBUG(1, ("value for %s: metadata type unknown\n",
2180 FRUIT_PARAM_TYPE_NAME
));
2183 config
->meta
= (enum fruit_meta
)enumval
;
2185 enumval
= lp_parm_enum(SNUM(handle
->conn
), FRUIT_PARAM_TYPE_NAME
,
2186 "locking", fruit_locking
, FRUIT_LOCKING_NONE
);
2187 if (enumval
== -1) {
2188 DEBUG(1, ("value for %s: locking type unknown\n",
2189 FRUIT_PARAM_TYPE_NAME
));
2192 config
->locking
= (enum fruit_locking
)enumval
;
2194 enumval
= lp_parm_enum(SNUM(handle
->conn
), FRUIT_PARAM_TYPE_NAME
,
2195 "encoding", fruit_encoding
, FRUIT_ENC_PRIVATE
);
2196 if (enumval
== -1) {
2197 DEBUG(1, ("value for %s: encoding type unknown\n",
2198 FRUIT_PARAM_TYPE_NAME
));
2201 config
->encoding
= (enum fruit_encoding
)enumval
;
2203 if (config
->rsrc
== FRUIT_RSRC_ADFILE
) {
2204 config
->veto_appledouble
= lp_parm_bool(SNUM(handle
->conn
),
2205 FRUIT_PARAM_TYPE_NAME
,
2210 config
->use_aapl
= lp_parm_bool(
2211 -1, FRUIT_PARAM_TYPE_NAME
, "aapl", true);
2213 config
->time_machine
= lp_parm_bool(
2214 SNUM(handle
->conn
), FRUIT_PARAM_TYPE_NAME
, "time machine", false);
2216 config
->unix_info_enabled
= lp_parm_bool(
2217 -1, FRUIT_PARAM_TYPE_NAME
, "nfs_aces", true);
2219 config
->use_copyfile
= lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME
,
2222 config
->posix_rename
= lp_parm_bool(
2223 SNUM(handle
->conn
), FRUIT_PARAM_TYPE_NAME
, "posix_rename", true);
2225 config
->aapl_zero_file_id
=
2226 lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME
, "zero_file_id", true);
2228 config
->readdir_attr_rsize
= lp_parm_bool(
2229 SNUM(handle
->conn
), "readdir_attr", "aapl_rsize", true);
2231 config
->readdir_attr_finder_info
= lp_parm_bool(
2232 SNUM(handle
->conn
), "readdir_attr", "aapl_finder_info", true);
2234 config
->readdir_attr_max_access
= lp_parm_bool(
2235 SNUM(handle
->conn
), "readdir_attr", "aapl_max_access", true);
2237 config
->model
= lp_parm_const_string(
2238 -1, FRUIT_PARAM_TYPE_NAME
, "model", "MacSamba");
2240 tm_size_str
= lp_parm_const_string(
2241 SNUM(handle
->conn
), FRUIT_PARAM_TYPE_NAME
,
2242 "time machine max size", NULL
);
2243 if (tm_size_str
!= NULL
) {
2244 config
->time_machine_max_size
= conv_str_size(tm_size_str
);
2247 config
->wipe_intentionally_left_blank_rfork
= lp_parm_bool(
2248 SNUM(handle
->conn
), FRUIT_PARAM_TYPE_NAME
,
2249 "wipe_intentionally_left_blank_rfork", false);
2251 config
->delete_empty_adfiles
= lp_parm_bool(
2252 SNUM(handle
->conn
), FRUIT_PARAM_TYPE_NAME
,
2253 "delete_empty_adfiles", false);
2255 SMB_VFS_HANDLE_SET_DATA(handle
, config
,
2256 NULL
, struct fruit_config_data
,
2263 * Prepend "._" to a basename
2264 * Return a new struct smb_filename with stream_name == NULL.
2266 static int adouble_path(TALLOC_CTX
*ctx
,
2267 const struct smb_filename
*smb_fname_in
,
2268 struct smb_filename
**pp_smb_fname_out
)
2272 struct smb_filename
*smb_fname
= cp_smb_filename(ctx
,
2275 if (smb_fname
== NULL
) {
2279 /* We need streamname to be NULL */
2280 TALLOC_FREE(smb_fname
->stream_name
);
2282 /* And we're replacing base_name. */
2283 TALLOC_FREE(smb_fname
->base_name
);
2285 if (!parent_dirname(smb_fname
, smb_fname_in
->base_name
,
2287 TALLOC_FREE(smb_fname
);
2291 smb_fname
->base_name
= talloc_asprintf(smb_fname
,
2292 "%s/._%s", parent
, base
);
2293 if (smb_fname
->base_name
== NULL
) {
2294 TALLOC_FREE(smb_fname
);
2298 *pp_smb_fname_out
= smb_fname
;
2304 * Allocate and initialize an AfpInfo struct
2306 static AfpInfo
*afpinfo_new(TALLOC_CTX
*ctx
)
2308 AfpInfo
*ai
= talloc_zero(ctx
, AfpInfo
);
2312 ai
->afpi_Signature
= AFP_Signature
;
2313 ai
->afpi_Version
= AFP_Version
;
2314 ai
->afpi_BackupTime
= AD_DATE_START
;
2319 * Pack an AfpInfo struct into a buffer
2321 * Buffer size must be at least AFP_INFO_SIZE
2322 * Returns size of packed buffer
2324 static ssize_t
afpinfo_pack(const AfpInfo
*ai
, char *buf
)
2326 memset(buf
, 0, AFP_INFO_SIZE
);
2328 RSIVAL(buf
, 0, ai
->afpi_Signature
);
2329 RSIVAL(buf
, 4, ai
->afpi_Version
);
2330 RSIVAL(buf
, 12, ai
->afpi_BackupTime
);
2331 memcpy(buf
+ 16, ai
->afpi_FinderInfo
, sizeof(ai
->afpi_FinderInfo
));
2333 return AFP_INFO_SIZE
;
2337 * Unpack a buffer into a AfpInfo structure
2339 * Buffer size must be at least AFP_INFO_SIZE
2340 * Returns allocated AfpInfo struct
2342 static AfpInfo
*afpinfo_unpack(TALLOC_CTX
*ctx
, const void *data
)
2344 AfpInfo
*ai
= talloc_zero(ctx
, AfpInfo
);
2349 ai
->afpi_Signature
= RIVAL(data
, 0);
2350 ai
->afpi_Version
= RIVAL(data
, 4);
2351 ai
->afpi_BackupTime
= RIVAL(data
, 12);
2352 memcpy(ai
->afpi_FinderInfo
, (const char *)data
+ 16,
2353 sizeof(ai
->afpi_FinderInfo
));
2355 if (ai
->afpi_Signature
!= AFP_Signature
2356 || ai
->afpi_Version
!= AFP_Version
) {
2357 DEBUG(1, ("Bad AfpInfo signature or version\n"));
2365 * Fake an inode number from the md5 hash of the (xattr) name
2367 static SMB_INO_T
fruit_inode(const SMB_STRUCT_STAT
*sbuf
, const char *sname
)
2370 unsigned char hash
[16];
2374 DBG_DEBUG("fruit_inode called for %ju/%ju [%s]\n",
2375 (uintmax_t)sbuf
->st_ex_dev
,
2376 (uintmax_t)sbuf
->st_ex_ino
, sname
);
2378 upper_sname
= talloc_strdup_upper(talloc_tos(), sname
);
2379 SMB_ASSERT(upper_sname
!= NULL
);
2382 MD5Update(&ctx
, (const unsigned char *)&(sbuf
->st_ex_dev
),
2383 sizeof(sbuf
->st_ex_dev
));
2384 MD5Update(&ctx
, (const unsigned char *)&(sbuf
->st_ex_ino
),
2385 sizeof(sbuf
->st_ex_ino
));
2386 MD5Update(&ctx
, (unsigned char *)upper_sname
,
2387 talloc_get_size(upper_sname
)-1);
2388 MD5Final(hash
, &ctx
);
2390 TALLOC_FREE(upper_sname
);
2392 /* Hopefully all the variation is in the lower 4 (or 8) bytes! */
2393 memcpy(&result
, hash
, sizeof(result
));
2395 DBG_DEBUG("fruit_inode \"%s\": ino=%ju\n",
2396 sname
, (uintmax_t)result
);
2401 static bool add_fruit_stream(TALLOC_CTX
*mem_ctx
, unsigned int *num_streams
,
2402 struct stream_struct
**streams
,
2403 const char *name
, off_t size
,
2406 struct stream_struct
*tmp
;
2408 tmp
= talloc_realloc(mem_ctx
, *streams
, struct stream_struct
,
2414 tmp
[*num_streams
].name
= talloc_asprintf(tmp
, "%s:$DATA", name
);
2415 if (tmp
[*num_streams
].name
== NULL
) {
2419 tmp
[*num_streams
].size
= size
;
2420 tmp
[*num_streams
].alloc_size
= alloc_size
;
2427 static bool filter_empty_rsrc_stream(unsigned int *num_streams
,
2428 struct stream_struct
**streams
)
2430 struct stream_struct
*tmp
= *streams
;
2433 if (*num_streams
== 0) {
2437 for (i
= 0; i
< *num_streams
; i
++) {
2438 if (strequal_m(tmp
[i
].name
, AFPRESOURCE_STREAM
)) {
2443 if (i
== *num_streams
) {
2447 if (tmp
[i
].size
> 0) {
2451 TALLOC_FREE(tmp
[i
].name
);
2452 if (*num_streams
- 1 > i
) {
2453 memmove(&tmp
[i
], &tmp
[i
+1],
2454 (*num_streams
- i
- 1) * sizeof(struct stream_struct
));
2461 static bool del_fruit_stream(TALLOC_CTX
*mem_ctx
, unsigned int *num_streams
,
2462 struct stream_struct
**streams
,
2465 struct stream_struct
*tmp
= *streams
;
2468 if (*num_streams
== 0) {
2472 for (i
= 0; i
< *num_streams
; i
++) {
2473 if (strequal_m(tmp
[i
].name
, name
)) {
2478 if (i
== *num_streams
) {
2482 TALLOC_FREE(tmp
[i
].name
);
2483 if (*num_streams
- 1 > i
) {
2484 memmove(&tmp
[i
], &tmp
[i
+1],
2485 (*num_streams
- i
- 1) * sizeof(struct stream_struct
));
2492 static bool ad_empty_finderinfo(const struct adouble
*ad
)
2495 char emptybuf
[ADEDLEN_FINDERI
] = {0};
2498 fi
= ad_get_entry(ad
, ADEID_FINDERI
);
2500 DBG_ERR("Missing FinderInfo in struct adouble [%p]\n", ad
);
2504 cmp
= memcmp(emptybuf
, fi
, ADEDLEN_FINDERI
);
2508 static bool ai_empty_finderinfo(const AfpInfo
*ai
)
2511 char emptybuf
[ADEDLEN_FINDERI
] = {0};
2513 cmp
= memcmp(emptybuf
, &ai
->afpi_FinderInfo
[0], ADEDLEN_FINDERI
);
2518 * Update btime with btime from Netatalk
2520 static void update_btime(vfs_handle_struct
*handle
,
2521 struct smb_filename
*smb_fname
)
2524 struct timespec creation_time
= {0};
2526 struct fruit_config_data
*config
= NULL
;
2528 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct fruit_config_data
,
2531 switch (config
->meta
) {
2532 case FRUIT_META_STREAM
:
2534 case FRUIT_META_NETATALK
:
2538 DBG_ERR("Unexpected meta config [%d]\n", config
->meta
);
2542 ad
= ad_get(talloc_tos(), handle
, smb_fname
, ADOUBLE_META
);
2546 if (ad_getdate(ad
, AD_DATE_UNIX
| AD_DATE_CREATE
, &t
) != 0) {
2552 creation_time
.tv_sec
= convert_uint32_t_to_time_t(t
);
2553 update_stat_ex_create_time(&smb_fname
->st
, creation_time
);
2559 * Map an access mask to a Netatalk single byte byte range lock
2561 static off_t
access_to_netatalk_brl(enum apple_fork fork_type
,
2562 uint32_t access_mask
)
2566 switch (access_mask
) {
2567 case FILE_READ_DATA
:
2568 offset
= AD_FILELOCK_OPEN_RD
;
2571 case FILE_WRITE_DATA
:
2572 case FILE_APPEND_DATA
:
2573 offset
= AD_FILELOCK_OPEN_WR
;
2577 offset
= AD_FILELOCK_OPEN_NONE
;
2581 if (fork_type
== APPLE_FORK_RSRC
) {
2582 if (offset
== AD_FILELOCK_OPEN_NONE
) {
2583 offset
= AD_FILELOCK_RSRC_OPEN_NONE
;
2593 * Map a deny mode to a Netatalk brl
2595 static off_t
denymode_to_netatalk_brl(enum apple_fork fork_type
,
2600 switch (deny_mode
) {
2602 offset
= AD_FILELOCK_DENY_RD
;
2606 offset
= AD_FILELOCK_DENY_WR
;
2610 smb_panic("denymode_to_netatalk_brl: bad deny mode\n");
2613 if (fork_type
== APPLE_FORK_RSRC
) {
2621 * Call fcntl() with an exclusive F_GETLK request in order to
2622 * determine if there's an exisiting shared lock
2624 * @return true if the requested lock was found or any error occurred
2625 * false if the lock was not found
2627 static bool test_netatalk_lock(files_struct
*fsp
, off_t in_offset
)
2630 off_t offset
= in_offset
;
2635 result
= SMB_VFS_GETLOCK(fsp
, &offset
, &len
, &type
, &pid
);
2636 if (result
== false) {
2640 if (type
!= F_UNLCK
) {
2647 static NTSTATUS
fruit_check_access(vfs_handle_struct
*handle
,
2649 uint32_t access_mask
,
2650 uint32_t share_mode
)
2652 NTSTATUS status
= NT_STATUS_OK
;
2654 bool share_for_read
= (share_mode
& FILE_SHARE_READ
);
2655 bool share_for_write
= (share_mode
& FILE_SHARE_WRITE
);
2656 bool netatalk_already_open_for_reading
= false;
2657 bool netatalk_already_open_for_writing
= false;
2658 bool netatalk_already_open_with_deny_read
= false;
2659 bool netatalk_already_open_with_deny_write
= false;
2661 /* FIXME: hardcoded data fork, add resource fork */
2662 enum apple_fork fork_type
= APPLE_FORK_DATA
;
2664 DBG_DEBUG("fruit_check_access: %s, am: %s/%s, sm: 0x%x\n",
2666 access_mask
& FILE_READ_DATA
? "READ" :"-",
2667 access_mask
& FILE_WRITE_DATA
? "WRITE" : "-",
2670 if (fsp
->fh
->fd
== -1) {
2671 return NT_STATUS_OK
;
2674 /* Read NetATalk opens and deny modes on the file. */
2675 netatalk_already_open_for_reading
= test_netatalk_lock(fsp
,
2676 access_to_netatalk_brl(fork_type
,
2679 netatalk_already_open_with_deny_read
= test_netatalk_lock(fsp
,
2680 denymode_to_netatalk_brl(fork_type
,
2683 netatalk_already_open_for_writing
= test_netatalk_lock(fsp
,
2684 access_to_netatalk_brl(fork_type
,
2687 netatalk_already_open_with_deny_write
= test_netatalk_lock(fsp
,
2688 denymode_to_netatalk_brl(fork_type
,
2691 /* If there are any conflicts - sharing violation. */
2692 if ((access_mask
& FILE_READ_DATA
) &&
2693 netatalk_already_open_with_deny_read
) {
2694 return NT_STATUS_SHARING_VIOLATION
;
2697 if (!share_for_read
&&
2698 netatalk_already_open_for_reading
) {
2699 return NT_STATUS_SHARING_VIOLATION
;
2702 if ((access_mask
& FILE_WRITE_DATA
) &&
2703 netatalk_already_open_with_deny_write
) {
2704 return NT_STATUS_SHARING_VIOLATION
;
2707 if (!share_for_write
&&
2708 netatalk_already_open_for_writing
) {
2709 return NT_STATUS_SHARING_VIOLATION
;
2712 if (!(access_mask
& FILE_READ_DATA
)) {
2714 * Nothing we can do here, we need read access
2717 return NT_STATUS_OK
;
2720 /* Set NetAtalk locks matching our access */
2721 if (access_mask
& FILE_READ_DATA
) {
2722 struct byte_range_lock
*br_lck
= NULL
;
2724 off
= access_to_netatalk_brl(fork_type
, FILE_READ_DATA
);
2726 handle
->conn
->sconn
->msg_ctx
, fsp
,
2727 fsp
->op
->global
->open_persistent_id
, 1, off
,
2728 READ_LOCK
, POSIX_LOCK
, false,
2731 TALLOC_FREE(br_lck
);
2733 if (!NT_STATUS_IS_OK(status
)) {
2738 if (!share_for_read
) {
2739 struct byte_range_lock
*br_lck
= NULL
;
2741 off
= denymode_to_netatalk_brl(fork_type
, DENY_READ
);
2743 handle
->conn
->sconn
->msg_ctx
, fsp
,
2744 fsp
->op
->global
->open_persistent_id
, 1, off
,
2745 READ_LOCK
, POSIX_LOCK
, false,
2748 TALLOC_FREE(br_lck
);
2750 if (!NT_STATUS_IS_OK(status
)) {
2755 if (access_mask
& FILE_WRITE_DATA
) {
2756 struct byte_range_lock
*br_lck
= NULL
;
2758 off
= access_to_netatalk_brl(fork_type
, FILE_WRITE_DATA
);
2760 handle
->conn
->sconn
->msg_ctx
, fsp
,
2761 fsp
->op
->global
->open_persistent_id
, 1, off
,
2762 READ_LOCK
, POSIX_LOCK
, false,
2765 TALLOC_FREE(br_lck
);
2767 if (!NT_STATUS_IS_OK(status
)) {
2772 if (!share_for_write
) {
2773 struct byte_range_lock
*br_lck
= NULL
;
2775 off
= denymode_to_netatalk_brl(fork_type
, DENY_WRITE
);
2777 handle
->conn
->sconn
->msg_ctx
, fsp
,
2778 fsp
->op
->global
->open_persistent_id
, 1, off
,
2779 READ_LOCK
, POSIX_LOCK
, false,
2782 TALLOC_FREE(br_lck
);
2784 if (!NT_STATUS_IS_OK(status
)) {
2789 return NT_STATUS_OK
;
2792 static NTSTATUS
check_aapl(vfs_handle_struct
*handle
,
2793 struct smb_request
*req
,
2794 const struct smb2_create_blobs
*in_context_blobs
,
2795 struct smb2_create_blobs
*out_context_blobs
)
2797 struct fruit_config_data
*config
;
2799 struct smb2_create_blob
*aapl
= NULL
;
2803 DATA_BLOB blob
= data_blob_talloc(req
, NULL
, 0);
2804 uint64_t req_bitmap
, client_caps
;
2805 uint64_t server_caps
= SMB2_CRTCTX_AAPL_UNIX_BASED
;
2809 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct fruit_config_data
,
2810 return NT_STATUS_UNSUCCESSFUL
);
2812 if (!config
->use_aapl
2813 || in_context_blobs
== NULL
2814 || out_context_blobs
== NULL
) {
2815 return NT_STATUS_OK
;
2818 aapl
= smb2_create_blob_find(in_context_blobs
,
2819 SMB2_CREATE_TAG_AAPL
);
2821 return NT_STATUS_OK
;
2824 if (aapl
->data
.length
!= 24) {
2825 DEBUG(1, ("unexpected AAPL ctxt length: %ju\n",
2826 (uintmax_t)aapl
->data
.length
));
2827 return NT_STATUS_INVALID_PARAMETER
;
2830 cmd
= IVAL(aapl
->data
.data
, 0);
2831 if (cmd
!= SMB2_CRTCTX_AAPL_SERVER_QUERY
) {
2832 DEBUG(1, ("unsupported AAPL cmd: %d\n", cmd
));
2833 return NT_STATUS_INVALID_PARAMETER
;
2836 req_bitmap
= BVAL(aapl
->data
.data
, 8);
2837 client_caps
= BVAL(aapl
->data
.data
, 16);
2839 SIVAL(p
, 0, SMB2_CRTCTX_AAPL_SERVER_QUERY
);
2841 SBVAL(p
, 8, req_bitmap
);
2842 ok
= data_blob_append(req
, &blob
, p
, 16);
2844 return NT_STATUS_UNSUCCESSFUL
;
2847 if (req_bitmap
& SMB2_CRTCTX_AAPL_SERVER_CAPS
) {
2848 if ((client_caps
& SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR
) &&
2849 (handle
->conn
->tcon
->compat
->fs_capabilities
& FILE_NAMED_STREAMS
)) {
2850 server_caps
|= SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR
;
2851 config
->readdir_attr_enabled
= true;
2854 if (config
->use_copyfile
) {
2855 server_caps
|= SMB2_CRTCTX_AAPL_SUPPORTS_OSX_COPYFILE
;
2856 config
->copyfile_enabled
= true;
2860 * The client doesn't set the flag, so we can't check
2861 * for it and just set it unconditionally
2863 if (config
->unix_info_enabled
) {
2864 server_caps
|= SMB2_CRTCTX_AAPL_SUPPORTS_NFS_ACE
;
2867 SBVAL(p
, 0, server_caps
);
2868 ok
= data_blob_append(req
, &blob
, p
, 8);
2870 return NT_STATUS_UNSUCCESSFUL
;
2874 if (req_bitmap
& SMB2_CRTCTX_AAPL_VOLUME_CAPS
) {
2875 int val
= lp_case_sensitive(SNUM(handle
->conn
->tcon
->compat
));
2883 caps
|= SMB2_CRTCTX_AAPL_CASE_SENSITIVE
;
2890 if (config
->time_machine
) {
2891 caps
|= SMB2_CRTCTX_AAPL_FULL_SYNC
;
2896 ok
= data_blob_append(req
, &blob
, p
, 8);
2898 return NT_STATUS_UNSUCCESSFUL
;
2902 if (req_bitmap
& SMB2_CRTCTX_AAPL_MODEL_INFO
) {
2903 ok
= convert_string_talloc(req
,
2904 CH_UNIX
, CH_UTF16LE
,
2905 config
->model
, strlen(config
->model
),
2908 return NT_STATUS_UNSUCCESSFUL
;
2912 SIVAL(p
+ 4, 0, modellen
);
2913 ok
= data_blob_append(req
, &blob
, p
, 8);
2916 return NT_STATUS_UNSUCCESSFUL
;
2919 ok
= data_blob_append(req
, &blob
, model
, modellen
);
2922 return NT_STATUS_UNSUCCESSFUL
;
2926 status
= smb2_create_blob_add(out_context_blobs
,
2928 SMB2_CREATE_TAG_AAPL
,
2930 if (NT_STATUS_IS_OK(status
)) {
2931 global_fruit_config
.nego_aapl
= true;
2932 if (config
->aapl_zero_file_id
) {
2933 aapl_force_zero_file_id(handle
->conn
->sconn
);
2940 static bool readdir_attr_meta_finderi_stream(
2941 struct vfs_handle_struct
*handle
,
2942 const struct smb_filename
*smb_fname
,
2945 struct smb_filename
*stream_name
= NULL
;
2946 files_struct
*fsp
= NULL
;
2951 uint8_t buf
[AFP_INFO_SIZE
];
2953 stream_name
= synthetic_smb_fname(talloc_tos(),
2954 smb_fname
->base_name
,
2955 AFPINFO_STREAM_NAME
,
2956 NULL
, smb_fname
->flags
);
2957 if (stream_name
== NULL
) {
2961 ret
= SMB_VFS_STAT(handle
->conn
, stream_name
);
2966 status
= SMB_VFS_CREATE_FILE(
2967 handle
->conn
, /* conn */
2969 0, /* root_dir_fid */
2970 stream_name
, /* fname */
2971 FILE_READ_DATA
, /* access_mask */
2972 (FILE_SHARE_READ
| FILE_SHARE_WRITE
| /* share_access */
2974 FILE_OPEN
, /* create_disposition*/
2975 0, /* create_options */
2976 0, /* file_attributes */
2977 INTERNAL_OPEN_ONLY
, /* oplock_request */
2979 0, /* allocation_size */
2980 0, /* private_flags */
2985 NULL
, NULL
); /* create context */
2987 TALLOC_FREE(stream_name
);
2989 if (!NT_STATUS_IS_OK(status
)) {
2993 nread
= SMB_VFS_PREAD(fsp
, &buf
[0], AFP_INFO_SIZE
, 0);
2994 if (nread
!= AFP_INFO_SIZE
) {
2995 DBG_ERR("short read [%s] [%zd/%d]\n",
2996 smb_fname_str_dbg(stream_name
), nread
, AFP_INFO_SIZE
);
3001 memcpy(&ai
->afpi_FinderInfo
[0], &buf
[AFP_OFF_FinderInfo
],
3008 close_file(NULL
, fsp
, NORMAL_CLOSE
);
3014 static bool readdir_attr_meta_finderi_netatalk(
3015 struct vfs_handle_struct
*handle
,
3016 const struct smb_filename
*smb_fname
,
3019 struct adouble
*ad
= NULL
;
3022 ad
= ad_get(talloc_tos(), handle
, smb_fname
, ADOUBLE_META
);
3027 p
= ad_get_entry(ad
, ADEID_FINDERI
);
3029 DBG_ERR("No ADEID_FINDERI for [%s]\n", smb_fname
->base_name
);
3034 memcpy(&ai
->afpi_FinderInfo
[0], p
, AFP_FinderSize
);
3039 static bool readdir_attr_meta_finderi(struct vfs_handle_struct
*handle
,
3040 const struct smb_filename
*smb_fname
,
3041 struct readdir_attr_data
*attr_data
)
3043 struct fruit_config_data
*config
= NULL
;
3044 uint32_t date_added
;
3048 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
3049 struct fruit_config_data
,
3052 switch (config
->meta
) {
3053 case FRUIT_META_NETATALK
:
3054 ok
= readdir_attr_meta_finderi_netatalk(
3055 handle
, smb_fname
, &ai
);
3058 case FRUIT_META_STREAM
:
3059 ok
= readdir_attr_meta_finderi_stream(
3060 handle
, smb_fname
, &ai
);
3064 DBG_ERR("Unexpected meta config [%d]\n", config
->meta
);
3069 /* Don't bother with errors, it's likely ENOENT */
3073 if (S_ISREG(smb_fname
->st
.st_ex_mode
)) {
3075 memcpy(&attr_data
->attr_data
.aapl
.finder_info
[0],
3076 &ai
.afpi_FinderInfo
[0], 4);
3078 /* finder_creator */
3079 memcpy(&attr_data
->attr_data
.aapl
.finder_info
[0] + 4,
3080 &ai
.afpi_FinderInfo
[4], 4);
3084 memcpy(&attr_data
->attr_data
.aapl
.finder_info
[0] + 8,
3085 &ai
.afpi_FinderInfo
[8], 2);
3087 /* finder_ext_flags */
3088 memcpy(&attr_data
->attr_data
.aapl
.finder_info
[0] + 10,
3089 &ai
.afpi_FinderInfo
[24], 2);
3092 date_added
= convert_time_t_to_uint32_t(
3093 smb_fname
->st
.st_ex_btime
.tv_sec
- AD_DATE_DELTA
);
3095 RSIVAL(&attr_data
->attr_data
.aapl
.finder_info
[0], 12, date_added
);
3100 static uint64_t readdir_attr_rfork_size_adouble(
3101 struct vfs_handle_struct
*handle
,
3102 const struct smb_filename
*smb_fname
)
3104 struct adouble
*ad
= NULL
;
3105 uint64_t rfork_size
;
3107 ad
= ad_get(talloc_tos(), handle
, smb_fname
,
3113 rfork_size
= ad_getentrylen(ad
, ADEID_RFORK
);
3119 static uint64_t readdir_attr_rfork_size_stream(
3120 struct vfs_handle_struct
*handle
,
3121 const struct smb_filename
*smb_fname
)
3123 struct smb_filename
*stream_name
= NULL
;
3125 uint64_t rfork_size
;
3127 stream_name
= synthetic_smb_fname(talloc_tos(),
3128 smb_fname
->base_name
,
3129 AFPRESOURCE_STREAM_NAME
,
3131 if (stream_name
== NULL
) {
3135 ret
= SMB_VFS_STAT(handle
->conn
, stream_name
);
3137 TALLOC_FREE(stream_name
);
3141 rfork_size
= stream_name
->st
.st_ex_size
;
3142 TALLOC_FREE(stream_name
);
3147 static uint64_t readdir_attr_rfork_size(struct vfs_handle_struct
*handle
,
3148 const struct smb_filename
*smb_fname
)
3150 struct fruit_config_data
*config
= NULL
;
3151 uint64_t rfork_size
;
3153 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
3154 struct fruit_config_data
,
3157 switch (config
->rsrc
) {
3158 case FRUIT_RSRC_ADFILE
:
3159 rfork_size
= readdir_attr_rfork_size_adouble(handle
,
3163 case FRUIT_RSRC_XATTR
:
3164 case FRUIT_RSRC_STREAM
:
3165 rfork_size
= readdir_attr_rfork_size_stream(handle
,
3170 DBG_ERR("Unexpected rsrc config [%d]\n", config
->rsrc
);
3178 static NTSTATUS
readdir_attr_macmeta(struct vfs_handle_struct
*handle
,
3179 const struct smb_filename
*smb_fname
,
3180 struct readdir_attr_data
*attr_data
)
3182 NTSTATUS status
= NT_STATUS_OK
;
3183 struct fruit_config_data
*config
= NULL
;
3186 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
3187 struct fruit_config_data
,
3188 return NT_STATUS_UNSUCCESSFUL
);
3191 /* Ensure we return a default value in the creation_date field */
3192 RSIVAL(&attr_data
->attr_data
.aapl
.finder_info
, 12, AD_DATE_START
);
3195 * Resource fork length
3198 if (config
->readdir_attr_rsize
) {
3199 uint64_t rfork_size
;
3201 rfork_size
= readdir_attr_rfork_size(handle
, smb_fname
);
3202 attr_data
->attr_data
.aapl
.rfork_size
= rfork_size
;
3209 if (config
->readdir_attr_finder_info
) {
3210 ok
= readdir_attr_meta_finderi(handle
, smb_fname
, attr_data
);
3212 status
= NT_STATUS_INTERNAL_ERROR
;
3219 static NTSTATUS
remove_virtual_nfs_aces(struct security_descriptor
*psd
)
3224 if (psd
->dacl
== NULL
) {
3225 return NT_STATUS_OK
;
3228 for (i
= 0; i
< psd
->dacl
->num_aces
; i
++) {
3229 /* MS NFS style mode/uid/gid */
3230 int cmp
= dom_sid_compare_domain(
3231 &global_sid_Unix_NFS
,
3232 &psd
->dacl
->aces
[i
].trustee
);
3234 /* Normal ACE entry. */
3239 * security_descriptor_dacl_del()
3240 * *must* return NT_STATUS_OK as we know
3241 * we have something to remove.
3244 status
= security_descriptor_dacl_del(psd
,
3245 &psd
->dacl
->aces
[i
].trustee
);
3246 if (!NT_STATUS_IS_OK(status
)) {
3247 DBG_WARNING("failed to remove MS NFS style ACE: %s\n",
3253 * security_descriptor_dacl_del() may delete more
3254 * then one entry subsequent to this one if the
3255 * SID matches, but we only need to ensure that
3256 * we stay looking at the same element in the array.
3260 return NT_STATUS_OK
;
3263 /* Search MS NFS style ACE with UNIX mode */
3264 static NTSTATUS
check_ms_nfs(vfs_handle_struct
*handle
,
3266 struct security_descriptor
*psd
,
3271 struct fruit_config_data
*config
= NULL
;
3275 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
3276 struct fruit_config_data
,
3277 return NT_STATUS_UNSUCCESSFUL
);
3279 if (!global_fruit_config
.nego_aapl
) {
3280 return NT_STATUS_OK
;
3282 if (psd
->dacl
== NULL
|| !config
->unix_info_enabled
) {
3283 return NT_STATUS_OK
;
3286 for (i
= 0; i
< psd
->dacl
->num_aces
; i
++) {
3287 if (dom_sid_compare_domain(
3288 &global_sid_Unix_NFS_Mode
,
3289 &psd
->dacl
->aces
[i
].trustee
) == 0) {
3290 *pmode
= (mode_t
)psd
->dacl
->aces
[i
].trustee
.sub_auths
[2];
3291 *pmode
&= (S_IRWXU
| S_IRWXG
| S_IRWXO
);
3294 DEBUG(10, ("MS NFS chmod request %s, %04o\n",
3295 fsp_str_dbg(fsp
), (unsigned)(*pmode
)));
3301 * Remove any incoming virtual ACE entries generated by
3302 * fruit_fget_nt_acl().
3305 return remove_virtual_nfs_aces(psd
);
3308 /****************************************************************************
3310 ****************************************************************************/
3312 static int fruit_connect(vfs_handle_struct
*handle
,
3313 const char *service
,
3317 char *list
= NULL
, *newlist
= NULL
;
3318 struct fruit_config_data
*config
;
3320 DEBUG(10, ("fruit_connect\n"));
3322 rc
= SMB_VFS_NEXT_CONNECT(handle
, service
, user
);
3327 rc
= init_fruit_config(handle
);
3332 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
3333 struct fruit_config_data
, return -1);
3335 if (config
->veto_appledouble
) {
3336 list
= lp_veto_files(talloc_tos(), SNUM(handle
->conn
));
3339 if (strstr(list
, "/" ADOUBLE_NAME_PREFIX
"*/") == NULL
) {
3340 newlist
= talloc_asprintf(
3342 "%s/" ADOUBLE_NAME_PREFIX
"*/",
3344 lp_do_parameter(SNUM(handle
->conn
),
3349 lp_do_parameter(SNUM(handle
->conn
),
3351 "/" ADOUBLE_NAME_PREFIX
"*/");
3357 if (config
->encoding
== FRUIT_ENC_NATIVE
) {
3358 lp_do_parameter(SNUM(handle
->conn
),
3363 if (config
->time_machine
) {
3364 DBG_NOTICE("Enabling durable handles for Time Machine "
3365 "support on [%s]\n", service
);
3366 lp_do_parameter(SNUM(handle
->conn
), "durable handles", "yes");
3367 lp_do_parameter(SNUM(handle
->conn
), "kernel oplocks", "no");
3368 lp_do_parameter(SNUM(handle
->conn
), "kernel share modes", "no");
3369 if (!lp_strict_sync(SNUM(handle
->conn
))) {
3370 DBG_WARNING("Time Machine without strict sync is not "
3373 lp_do_parameter(SNUM(handle
->conn
), "posix locking", "no");
3379 static int fruit_fake_fd(void)
3386 * Return a valid fd, but ensure any attempt to use it returns
3387 * an error (EPIPE). Once we get a write on the handle, we open
3390 ret
= pipe(pipe_fds
);
3400 static int fruit_open_meta_stream(vfs_handle_struct
*handle
,
3401 struct smb_filename
*smb_fname
,
3406 struct fruit_config_data
*config
= NULL
;
3407 struct fio
*fio
= NULL
;
3408 int open_flags
= flags
& ~O_CREAT
;
3411 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname
));
3413 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
3414 struct fruit_config_data
, return -1);
3416 fio
= VFS_ADD_FSP_EXTENSION(handle
, fsp
, struct fio
, NULL
);
3417 fio
->type
= ADOUBLE_META
;
3418 fio
->config
= config
;
3420 fd
= SMB_VFS_NEXT_OPEN(handle
, smb_fname
, fsp
, open_flags
, mode
);
3425 if (!(flags
& O_CREAT
)) {
3426 VFS_REMOVE_FSP_EXTENSION(handle
, fsp
);
3430 fd
= fruit_fake_fd();
3432 VFS_REMOVE_FSP_EXTENSION(handle
, fsp
);
3436 fio
->fake_fd
= true;
3443 static int fruit_open_meta_netatalk(vfs_handle_struct
*handle
,
3444 struct smb_filename
*smb_fname
,
3449 struct fruit_config_data
*config
= NULL
;
3450 struct fio
*fio
= NULL
;
3451 struct adouble
*ad
= NULL
;
3452 bool meta_exists
= false;
3455 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname
));
3457 ad
= ad_get(talloc_tos(), handle
, smb_fname
, ADOUBLE_META
);
3464 if (!meta_exists
&& !(flags
& O_CREAT
)) {
3469 fd
= fruit_fake_fd();
3474 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
3475 struct fruit_config_data
, return -1);
3477 fio
= VFS_ADD_FSP_EXTENSION(handle
, fsp
, struct fio
, NULL
);
3478 fio
->type
= ADOUBLE_META
;
3479 fio
->config
= config
;
3480 fio
->fake_fd
= true;
3487 static int fruit_open_meta(vfs_handle_struct
*handle
,
3488 struct smb_filename
*smb_fname
,
3489 files_struct
*fsp
, int flags
, mode_t mode
)
3492 struct fruit_config_data
*config
= NULL
;
3494 DBG_DEBUG("path [%s]\n", smb_fname_str_dbg(smb_fname
));
3496 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
3497 struct fruit_config_data
, return -1);
3499 switch (config
->meta
) {
3500 case FRUIT_META_STREAM
:
3501 fd
= fruit_open_meta_stream(handle
, smb_fname
,
3505 case FRUIT_META_NETATALK
:
3506 fd
= fruit_open_meta_netatalk(handle
, smb_fname
,
3511 DBG_ERR("Unexpected meta config [%d]\n", config
->meta
);
3515 DBG_DEBUG("path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname
), fd
);
3520 static int fruit_open_rsrc_adouble(vfs_handle_struct
*handle
,
3521 struct smb_filename
*smb_fname
,
3527 struct adouble
*ad
= NULL
;
3528 struct smb_filename
*smb_fname_base
= NULL
;
3529 struct fruit_config_data
*config
= NULL
;
3532 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
3533 struct fruit_config_data
, return -1);
3535 if ((!(flags
& O_CREAT
)) &&
3536 S_ISDIR(fsp
->base_fsp
->fsp_name
->st
.st_ex_mode
))
3538 /* sorry, but directories don't habe a resource fork */
3543 rc
= adouble_path(talloc_tos(), smb_fname
, &smb_fname_base
);
3548 /* We always need read/write access for the metadata header too */
3549 flags
&= ~(O_RDONLY
| O_WRONLY
);
3552 hostfd
= SMB_VFS_NEXT_OPEN(handle
, smb_fname_base
, fsp
,
3559 if (flags
& (O_CREAT
| O_TRUNC
)) {
3560 ad
= ad_init(fsp
, ADOUBLE_RSRC
);
3566 fsp
->fh
->fd
= hostfd
;
3568 rc
= ad_fset(handle
, ad
, fsp
);
3579 TALLOC_FREE(smb_fname_base
);
3581 DEBUG(10, ("fruit_open resource fork: rc=%d, fd=%d\n", rc
, hostfd
));
3583 int saved_errno
= errno
;
3586 * BUGBUGBUG -- we would need to call
3587 * fd_close_posix here, but we don't have a
3590 fsp
->fh
->fd
= hostfd
;
3594 errno
= saved_errno
;
3599 static int fruit_open_rsrc_xattr(vfs_handle_struct
*handle
,
3600 struct smb_filename
*smb_fname
,
3605 #ifdef HAVE_ATTROPEN
3608 fd
= attropen(smb_fname
->base_name
,
3609 AFPRESOURCE_EA_NETATALK
,
3624 static int fruit_open_rsrc(vfs_handle_struct
*handle
,
3625 struct smb_filename
*smb_fname
,
3626 files_struct
*fsp
, int flags
, mode_t mode
)
3629 struct fruit_config_data
*config
= NULL
;
3630 struct fio
*fio
= NULL
;
3632 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname
));
3634 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
3635 struct fruit_config_data
, return -1);
3637 switch (config
->rsrc
) {
3638 case FRUIT_RSRC_STREAM
:
3639 fd
= SMB_VFS_NEXT_OPEN(handle
, smb_fname
, fsp
, flags
, mode
);
3642 case FRUIT_RSRC_ADFILE
:
3643 fd
= fruit_open_rsrc_adouble(handle
, smb_fname
,
3647 case FRUIT_RSRC_XATTR
:
3648 fd
= fruit_open_rsrc_xattr(handle
, smb_fname
,
3653 DBG_ERR("Unexpected rsrc config [%d]\n", config
->rsrc
);
3657 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname
), fd
);
3663 fio
= VFS_ADD_FSP_EXTENSION(handle
, fsp
, struct fio
, NULL
);
3664 fio
->type
= ADOUBLE_RSRC
;
3665 fio
->config
= config
;
3670 static int fruit_open(vfs_handle_struct
*handle
,
3671 struct smb_filename
*smb_fname
,
3672 files_struct
*fsp
, int flags
, mode_t mode
)
3676 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname
));
3678 if (!is_ntfs_stream_smb_fname(smb_fname
)) {
3679 return SMB_VFS_NEXT_OPEN(handle
, smb_fname
, fsp
, flags
, mode
);
3682 if (is_afpinfo_stream(smb_fname
)) {
3683 fd
= fruit_open_meta(handle
, smb_fname
, fsp
, flags
, mode
);
3684 } else if (is_afpresource_stream(smb_fname
)) {
3685 fd
= fruit_open_rsrc(handle
, smb_fname
, fsp
, flags
, mode
);
3687 fd
= SMB_VFS_NEXT_OPEN(handle
, smb_fname
, fsp
, flags
, mode
);
3690 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname
), fd
);
3695 static int fruit_close_meta(vfs_handle_struct
*handle
,
3699 struct fruit_config_data
*config
= NULL
;
3701 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
3702 struct fruit_config_data
, return -1);
3704 switch (config
->meta
) {
3705 case FRUIT_META_STREAM
:
3706 ret
= SMB_VFS_NEXT_CLOSE(handle
, fsp
);
3709 case FRUIT_META_NETATALK
:
3710 ret
= close(fsp
->fh
->fd
);
3715 DBG_ERR("Unexpected meta config [%d]\n", config
->meta
);
3723 static int fruit_close_rsrc(vfs_handle_struct
*handle
,
3727 struct fruit_config_data
*config
= NULL
;
3729 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
3730 struct fruit_config_data
, return -1);
3732 switch (config
->rsrc
) {
3733 case FRUIT_RSRC_STREAM
:
3734 case FRUIT_RSRC_ADFILE
:
3735 ret
= SMB_VFS_NEXT_CLOSE(handle
, fsp
);
3738 case FRUIT_RSRC_XATTR
:
3739 ret
= close(fsp
->fh
->fd
);
3744 DBG_ERR("Unexpected rsrc config [%d]\n", config
->rsrc
);
3751 static int fruit_close(vfs_handle_struct
*handle
,
3759 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(fsp
->fsp_name
), fd
);
3761 if (!is_ntfs_stream_smb_fname(fsp
->fsp_name
)) {
3762 return SMB_VFS_NEXT_CLOSE(handle
, fsp
);
3765 if (is_afpinfo_stream(fsp
->fsp_name
)) {
3766 ret
= fruit_close_meta(handle
, fsp
);
3767 } else if (is_afpresource_stream(fsp
->fsp_name
)) {
3768 ret
= fruit_close_rsrc(handle
, fsp
);
3770 ret
= SMB_VFS_NEXT_CLOSE(handle
, fsp
);
3776 static int fruit_rename(struct vfs_handle_struct
*handle
,
3777 const struct smb_filename
*smb_fname_src
,
3778 const struct smb_filename
*smb_fname_dst
)
3781 struct fruit_config_data
*config
= NULL
;
3782 struct smb_filename
*src_adp_smb_fname
= NULL
;
3783 struct smb_filename
*dst_adp_smb_fname
= NULL
;
3785 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
3786 struct fruit_config_data
, return -1);
3788 if (!VALID_STAT(smb_fname_src
->st
)) {
3789 DBG_ERR("Need valid stat for [%s]\n",
3790 smb_fname_str_dbg(smb_fname_src
));
3794 rc
= SMB_VFS_NEXT_RENAME(handle
, smb_fname_src
, smb_fname_dst
);
3799 if ((config
->rsrc
!= FRUIT_RSRC_ADFILE
) ||
3800 (!S_ISREG(smb_fname_src
->st
.st_ex_mode
)))
3805 rc
= adouble_path(talloc_tos(), smb_fname_src
, &src_adp_smb_fname
);
3810 rc
= adouble_path(talloc_tos(), smb_fname_dst
, &dst_adp_smb_fname
);
3815 DBG_DEBUG("%s -> %s\n",
3816 smb_fname_str_dbg(src_adp_smb_fname
),
3817 smb_fname_str_dbg(dst_adp_smb_fname
));
3819 rc
= SMB_VFS_NEXT_RENAME(handle
, src_adp_smb_fname
, dst_adp_smb_fname
);
3820 if (errno
== ENOENT
) {
3825 TALLOC_FREE(src_adp_smb_fname
);
3826 TALLOC_FREE(dst_adp_smb_fname
);
3830 static int fruit_unlink_meta_stream(vfs_handle_struct
*handle
,
3831 const struct smb_filename
*smb_fname
)
3833 return SMB_VFS_NEXT_UNLINK(handle
, smb_fname
);
3836 static int fruit_unlink_meta_netatalk(vfs_handle_struct
*handle
,
3837 const struct smb_filename
*smb_fname
)
3839 return SMB_VFS_REMOVEXATTR(handle
->conn
,
3841 AFPINFO_EA_NETATALK
);
3844 static int fruit_unlink_meta(vfs_handle_struct
*handle
,
3845 const struct smb_filename
*smb_fname
)
3847 struct fruit_config_data
*config
= NULL
;
3850 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
3851 struct fruit_config_data
, return -1);
3853 switch (config
->meta
) {
3854 case FRUIT_META_STREAM
:
3855 rc
= fruit_unlink_meta_stream(handle
, smb_fname
);
3858 case FRUIT_META_NETATALK
:
3859 rc
= fruit_unlink_meta_netatalk(handle
, smb_fname
);
3863 DBG_ERR("Unsupported meta config [%d]\n", config
->meta
);
3870 static int fruit_unlink_rsrc_stream(vfs_handle_struct
*handle
,
3871 const struct smb_filename
*smb_fname
,
3876 if (!force_unlink
) {
3877 struct smb_filename
*smb_fname_cp
= NULL
;
3880 smb_fname_cp
= cp_smb_filename(talloc_tos(), smb_fname
);
3881 if (smb_fname_cp
== NULL
) {
3886 * 0 byte resource fork streams are not listed by
3887 * vfs_streaminfo, as a result stream cleanup/deletion of file
3888 * deletion doesn't remove the resourcefork stream.
3891 ret
= SMB_VFS_NEXT_STAT(handle
, smb_fname_cp
);
3893 TALLOC_FREE(smb_fname_cp
);
3894 DBG_ERR("stat [%s] failed [%s]\n",
3895 smb_fname_str_dbg(smb_fname_cp
), strerror(errno
));
3899 size
= smb_fname_cp
->st
.st_ex_size
;
3900 TALLOC_FREE(smb_fname_cp
);
3903 /* OS X ignores resource fork stream delete requests */
3908 ret
= SMB_VFS_NEXT_UNLINK(handle
, smb_fname
);
3909 if ((ret
!= 0) && (errno
== ENOENT
) && force_unlink
) {
3916 static int fruit_unlink_rsrc_adouble(vfs_handle_struct
*handle
,
3917 const struct smb_filename
*smb_fname
,
3921 struct adouble
*ad
= NULL
;
3922 struct smb_filename
*adp_smb_fname
= NULL
;
3924 if (!force_unlink
) {
3925 ad
= ad_get(talloc_tos(), handle
, smb_fname
,
3934 * 0 byte resource fork streams are not listed by
3935 * vfs_streaminfo, as a result stream cleanup/deletion of file
3936 * deletion doesn't remove the resourcefork stream.
3939 if (ad_getentrylen(ad
, ADEID_RFORK
) > 0) {
3940 /* OS X ignores resource fork stream delete requests */
3948 rc
= adouble_path(talloc_tos(), smb_fname
, &adp_smb_fname
);
3953 rc
= SMB_VFS_NEXT_UNLINK(handle
, adp_smb_fname
);
3954 TALLOC_FREE(adp_smb_fname
);
3955 if ((rc
!= 0) && (errno
== ENOENT
) && force_unlink
) {
3962 static int fruit_unlink_rsrc_xattr(vfs_handle_struct
*handle
,
3963 const struct smb_filename
*smb_fname
,
3967 * OS X ignores resource fork stream delete requests, so nothing to do
3968 * here. Removing the file will remove the xattr anyway, so we don't
3969 * have to take care of removing 0 byte resource forks that could be
3975 static int fruit_unlink_rsrc(vfs_handle_struct
*handle
,
3976 const struct smb_filename
*smb_fname
,
3979 struct fruit_config_data
*config
= NULL
;
3982 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
3983 struct fruit_config_data
, return -1);
3985 switch (config
->rsrc
) {
3986 case FRUIT_RSRC_STREAM
:
3987 rc
= fruit_unlink_rsrc_stream(handle
, smb_fname
, force_unlink
);
3990 case FRUIT_RSRC_ADFILE
:
3991 rc
= fruit_unlink_rsrc_adouble(handle
, smb_fname
, force_unlink
);
3994 case FRUIT_RSRC_XATTR
:
3995 rc
= fruit_unlink_rsrc_xattr(handle
, smb_fname
, force_unlink
);
3999 DBG_ERR("Unsupported rsrc config [%d]\n", config
->rsrc
);
4006 static int fruit_unlink(vfs_handle_struct
*handle
,
4007 const struct smb_filename
*smb_fname
)
4010 struct fruit_config_data
*config
= NULL
;
4011 struct smb_filename
*rsrc_smb_fname
= NULL
;
4013 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
4014 struct fruit_config_data
, return -1);
4016 if (is_afpinfo_stream(smb_fname
)) {
4017 return fruit_unlink_meta(handle
, smb_fname
);
4018 } else if (is_afpresource_stream(smb_fname
)) {
4019 return fruit_unlink_rsrc(handle
, smb_fname
, false);
4020 } else if (is_ntfs_stream_smb_fname(smb_fname
)) {
4021 return SMB_VFS_NEXT_UNLINK(handle
, smb_fname
);
4022 } else if (is_adouble_file(smb_fname
->base_name
)) {
4023 return SMB_VFS_NEXT_UNLINK(handle
, smb_fname
);
4027 * A request to delete the base file. Because 0 byte resource
4028 * fork streams are not listed by fruit_streaminfo,
4029 * delete_all_streams() can't remove 0 byte resource fork
4030 * streams, so we have to cleanup this here.
4032 rsrc_smb_fname
= synthetic_smb_fname(talloc_tos(),
4033 smb_fname
->base_name
,
4034 AFPRESOURCE_STREAM_NAME
,
4037 if (rsrc_smb_fname
== NULL
) {
4041 rc
= fruit_unlink_rsrc(handle
, rsrc_smb_fname
, true);
4042 if ((rc
!= 0) && (errno
!= ENOENT
)) {
4043 DBG_ERR("Forced unlink of [%s] failed [%s]\n",
4044 smb_fname_str_dbg(rsrc_smb_fname
), strerror(errno
));
4045 TALLOC_FREE(rsrc_smb_fname
);
4048 TALLOC_FREE(rsrc_smb_fname
);
4050 return SMB_VFS_NEXT_UNLINK(handle
, smb_fname
);
4053 static int fruit_chmod(vfs_handle_struct
*handle
,
4054 const struct smb_filename
*smb_fname
,
4058 struct fruit_config_data
*config
= NULL
;
4059 struct smb_filename
*smb_fname_adp
= NULL
;
4061 rc
= SMB_VFS_NEXT_CHMOD(handle
, smb_fname
, mode
);
4066 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
4067 struct fruit_config_data
, return -1);
4069 if (config
->rsrc
!= FRUIT_RSRC_ADFILE
) {
4073 if (!VALID_STAT(smb_fname
->st
)) {
4077 if (!S_ISREG(smb_fname
->st
.st_ex_mode
)) {
4081 rc
= adouble_path(talloc_tos(), smb_fname
, &smb_fname_adp
);
4086 DEBUG(10, ("fruit_chmod: %s\n", smb_fname_adp
->base_name
));
4088 rc
= SMB_VFS_NEXT_CHMOD(handle
, smb_fname_adp
, mode
);
4089 if (errno
== ENOENT
) {
4093 TALLOC_FREE(smb_fname_adp
);
4097 static int fruit_chown(vfs_handle_struct
*handle
,
4098 const struct smb_filename
*smb_fname
,
4103 struct fruit_config_data
*config
= NULL
;
4104 struct smb_filename
*adp_smb_fname
= NULL
;
4106 rc
= SMB_VFS_NEXT_CHOWN(handle
, smb_fname
, uid
, gid
);
4111 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
4112 struct fruit_config_data
, return -1);
4114 if (config
->rsrc
!= FRUIT_RSRC_ADFILE
) {
4118 if (!VALID_STAT(smb_fname
->st
)) {
4122 if (!S_ISREG(smb_fname
->st
.st_ex_mode
)) {
4126 rc
= adouble_path(talloc_tos(), smb_fname
, &adp_smb_fname
);
4131 DEBUG(10, ("fruit_chown: %s\n", adp_smb_fname
->base_name
));
4133 rc
= SMB_VFS_NEXT_CHOWN(handle
, adp_smb_fname
, uid
, gid
);
4134 if (errno
== ENOENT
) {
4139 TALLOC_FREE(adp_smb_fname
);
4143 static int fruit_rmdir(struct vfs_handle_struct
*handle
,
4144 const struct smb_filename
*smb_fname
)
4148 struct fruit_config_data
*config
;
4150 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
4151 struct fruit_config_data
, return -1);
4153 if (config
->rsrc
!= FRUIT_RSRC_ADFILE
) {
4158 * Due to there is no way to change bDeleteVetoFiles variable
4159 * from this module, need to clean up ourselves
4162 dh
= SMB_VFS_OPENDIR(handle
->conn
, smb_fname
, NULL
, 0);
4167 while ((de
= SMB_VFS_READDIR(handle
->conn
, dh
, NULL
)) != NULL
) {
4168 struct adouble
*ad
= NULL
;
4170 struct smb_filename
*ad_smb_fname
= NULL
;
4173 if (!is_adouble_file(de
->d_name
)) {
4177 p
= talloc_asprintf(talloc_tos(), "%s/%s",
4178 smb_fname
->base_name
, de
->d_name
);
4180 DBG_ERR("talloc_asprintf failed\n");
4184 ad_smb_fname
= synthetic_smb_fname(talloc_tos(), p
,
4188 if (ad_smb_fname
== NULL
) {
4189 DBG_ERR("synthetic_smb_fname failed\n");
4194 * Check whether it's a valid AppleDouble file, if
4195 * yes, delete it, ignore it otherwise.
4197 ad
= ad_get(talloc_tos(), handle
, ad_smb_fname
, ADOUBLE_RSRC
);
4199 TALLOC_FREE(ad_smb_fname
);
4205 ret
= SMB_VFS_NEXT_UNLINK(handle
, ad_smb_fname
);
4207 DBG_ERR("Deleting [%s] failed\n",
4208 smb_fname_str_dbg(ad_smb_fname
));
4210 TALLOC_FREE(ad_smb_fname
);
4215 SMB_VFS_CLOSEDIR(handle
->conn
, dh
);
4217 return SMB_VFS_NEXT_RMDIR(handle
, smb_fname
);
4220 static ssize_t
fruit_pread_meta_stream(vfs_handle_struct
*handle
,
4221 files_struct
*fsp
, void *data
,
4222 size_t n
, off_t offset
)
4227 nread
= SMB_VFS_NEXT_PREAD(handle
, fsp
, data
, n
, offset
);
4228 if (nread
== -1 || nread
== n
) {
4232 DBG_ERR("Removing [%s] after short read [%zd]\n",
4233 fsp_str_dbg(fsp
), nread
);
4235 ret
= SMB_VFS_NEXT_UNLINK(handle
, fsp
->fsp_name
);
4237 DBG_ERR("Removing [%s] failed\n", fsp_str_dbg(fsp
));
4245 static ssize_t
fruit_pread_meta_adouble(vfs_handle_struct
*handle
,
4246 files_struct
*fsp
, void *data
,
4247 size_t n
, off_t offset
)
4250 struct adouble
*ad
= NULL
;
4251 char afpinfo_buf
[AFP_INFO_SIZE
];
4255 ai
= afpinfo_new(talloc_tos());
4260 ad
= ad_fget(talloc_tos(), handle
, fsp
, ADOUBLE_META
);
4266 p
= ad_get_entry(ad
, ADEID_FINDERI
);
4268 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp
));
4273 memcpy(&ai
->afpi_FinderInfo
[0], p
, ADEDLEN_FINDERI
);
4275 nread
= afpinfo_pack(ai
, afpinfo_buf
);
4276 if (nread
!= AFP_INFO_SIZE
) {
4281 memcpy(data
, afpinfo_buf
, n
);
4289 static ssize_t
fruit_pread_meta(vfs_handle_struct
*handle
,
4290 files_struct
*fsp
, void *data
,
4291 size_t n
, off_t offset
)
4293 struct fio
*fio
= (struct fio
*)VFS_FETCH_FSP_EXTENSION(handle
, fsp
);
4298 * OS X has a off-by-1 error in the offset calculation, so we're
4299 * bug compatible here. It won't hurt, as any relevant real
4300 * world read requests from the AFP_AfpInfo stream will be
4301 * offset=0 n=60. offset is ignored anyway, see below.
4303 if ((offset
< 0) || (offset
>= AFP_INFO_SIZE
+ 1)) {
4308 DBG_ERR("Failed to fetch fsp extension");
4312 /* Yes, macOS always reads from offset 0 */
4314 to_return
= MIN(n
, AFP_INFO_SIZE
);
4316 switch (fio
->config
->meta
) {
4317 case FRUIT_META_STREAM
:
4318 nread
= fruit_pread_meta_stream(handle
, fsp
, data
,
4322 case FRUIT_META_NETATALK
:
4323 nread
= fruit_pread_meta_adouble(handle
, fsp
, data
,
4328 DBG_ERR("Unexpected meta config [%d]\n", fio
->config
->meta
);
4332 if (nread
== -1 && fio
->created
) {
4334 char afpinfo_buf
[AFP_INFO_SIZE
];
4336 ai
= afpinfo_new(talloc_tos());
4341 nread
= afpinfo_pack(ai
, afpinfo_buf
);
4343 if (nread
!= AFP_INFO_SIZE
) {
4347 memcpy(data
, afpinfo_buf
, to_return
);
4354 static ssize_t
fruit_pread_rsrc_stream(vfs_handle_struct
*handle
,
4355 files_struct
*fsp
, void *data
,
4356 size_t n
, off_t offset
)
4358 return SMB_VFS_NEXT_PREAD(handle
, fsp
, data
, n
, offset
);
4361 static ssize_t
fruit_pread_rsrc_xattr(vfs_handle_struct
*handle
,
4362 files_struct
*fsp
, void *data
,
4363 size_t n
, off_t offset
)
4365 return SMB_VFS_NEXT_PREAD(handle
, fsp
, data
, n
, offset
);
4368 static ssize_t
fruit_pread_rsrc_adouble(vfs_handle_struct
*handle
,
4369 files_struct
*fsp
, void *data
,
4370 size_t n
, off_t offset
)
4372 struct adouble
*ad
= NULL
;
4375 ad
= ad_fget(talloc_tos(), handle
, fsp
, ADOUBLE_RSRC
);
4380 nread
= SMB_VFS_NEXT_PREAD(handle
, fsp
, data
, n
,
4381 offset
+ ad_getentryoff(ad
, ADEID_RFORK
));
4387 static ssize_t
fruit_pread_rsrc(vfs_handle_struct
*handle
,
4388 files_struct
*fsp
, void *data
,
4389 size_t n
, off_t offset
)
4391 struct fio
*fio
= (struct fio
*)VFS_FETCH_FSP_EXTENSION(handle
, fsp
);
4399 switch (fio
->config
->rsrc
) {
4400 case FRUIT_RSRC_STREAM
:
4401 nread
= fruit_pread_rsrc_stream(handle
, fsp
, data
, n
, offset
);
4404 case FRUIT_RSRC_ADFILE
:
4405 nread
= fruit_pread_rsrc_adouble(handle
, fsp
, data
, n
, offset
);
4408 case FRUIT_RSRC_XATTR
:
4409 nread
= fruit_pread_rsrc_xattr(handle
, fsp
, data
, n
, offset
);
4413 DBG_ERR("Unexpected rsrc config [%d]\n", fio
->config
->rsrc
);
4420 static ssize_t
fruit_pread(vfs_handle_struct
*handle
,
4421 files_struct
*fsp
, void *data
,
4422 size_t n
, off_t offset
)
4424 struct fio
*fio
= (struct fio
*)VFS_FETCH_FSP_EXTENSION(handle
, fsp
);
4427 DBG_DEBUG("Path [%s] offset=%"PRIdMAX
", size=%zd\n",
4428 fsp_str_dbg(fsp
), (intmax_t)offset
, n
);
4431 return SMB_VFS_NEXT_PREAD(handle
, fsp
, data
, n
, offset
);
4434 if (fio
->type
== ADOUBLE_META
) {
4435 nread
= fruit_pread_meta(handle
, fsp
, data
, n
, offset
);
4437 nread
= fruit_pread_rsrc(handle
, fsp
, data
, n
, offset
);
4440 DBG_DEBUG("Path [%s] nread [%zd]\n", fsp_str_dbg(fsp
), nread
);
4444 static bool fruit_must_handle_aio_stream(struct fio
*fio
)
4450 if (fio
->type
== ADOUBLE_META
) {
4454 if ((fio
->type
== ADOUBLE_RSRC
) &&
4455 (fio
->config
->rsrc
== FRUIT_RSRC_ADFILE
))
4463 struct fruit_pread_state
{
4465 struct vfs_aio_state vfs_aio_state
;
4468 static void fruit_pread_done(struct tevent_req
*subreq
);
4470 static struct tevent_req
*fruit_pread_send(
4471 struct vfs_handle_struct
*handle
,
4472 TALLOC_CTX
*mem_ctx
,
4473 struct tevent_context
*ev
,
4474 struct files_struct
*fsp
,
4476 size_t n
, off_t offset
)
4478 struct tevent_req
*req
= NULL
;
4479 struct tevent_req
*subreq
= NULL
;
4480 struct fruit_pread_state
*state
= NULL
;
4481 struct fio
*fio
= (struct fio
*)VFS_FETCH_FSP_EXTENSION(handle
, fsp
);
4483 req
= tevent_req_create(mem_ctx
, &state
,
4484 struct fruit_pread_state
);
4489 if (fruit_must_handle_aio_stream(fio
)) {
4490 state
->nread
= SMB_VFS_PREAD(fsp
, data
, n
, offset
);
4491 if (state
->nread
!= n
) {
4492 if (state
->nread
!= -1) {
4495 tevent_req_error(req
, errno
);
4496 return tevent_req_post(req
, ev
);
4498 tevent_req_done(req
);
4499 return tevent_req_post(req
, ev
);
4502 subreq
= SMB_VFS_NEXT_PREAD_SEND(state
, ev
, handle
, fsp
,
4504 if (tevent_req_nomem(req
, subreq
)) {
4505 return tevent_req_post(req
, ev
);
4507 tevent_req_set_callback(subreq
, fruit_pread_done
, req
);
4511 static void fruit_pread_done(struct tevent_req
*subreq
)
4513 struct tevent_req
*req
= tevent_req_callback_data(
4514 subreq
, struct tevent_req
);
4515 struct fruit_pread_state
*state
= tevent_req_data(
4516 req
, struct fruit_pread_state
);
4518 state
->nread
= SMB_VFS_PREAD_RECV(subreq
, &state
->vfs_aio_state
);
4519 TALLOC_FREE(subreq
);
4521 if (tevent_req_error(req
, state
->vfs_aio_state
.error
)) {
4524 tevent_req_done(req
);
4527 static ssize_t
fruit_pread_recv(struct tevent_req
*req
,
4528 struct vfs_aio_state
*vfs_aio_state
)
4530 struct fruit_pread_state
*state
= tevent_req_data(
4531 req
, struct fruit_pread_state
);
4533 if (tevent_req_is_unix_error(req
, &vfs_aio_state
->error
)) {
4537 *vfs_aio_state
= state
->vfs_aio_state
;
4538 return state
->nread
;
4541 static ssize_t
fruit_pwrite_meta_stream(vfs_handle_struct
*handle
,
4542 files_struct
*fsp
, const void *data
,
4543 size_t n
, off_t offset
)
4545 struct fio
*fio
= (struct fio
*)VFS_FETCH_FSP_EXTENSION(handle
, fsp
);
4551 DBG_DEBUG("Path [%s] offset=%"PRIdMAX
", size=%zd\n",
4552 fsp_str_dbg(fsp
), (intmax_t)offset
, n
);
4561 ret
= SMB_VFS_NEXT_CLOSE(handle
, fsp
);
4563 DBG_ERR("Close [%s] failed: %s\n",
4564 fsp_str_dbg(fsp
), strerror(errno
));
4569 fd
= SMB_VFS_NEXT_OPEN(handle
,
4575 DBG_ERR("On-demand create [%s] in write failed: %s\n",
4576 fsp_str_dbg(fsp
), strerror(errno
));
4580 fio
->fake_fd
= false;
4583 ai
= afpinfo_unpack(talloc_tos(), data
);
4588 if (ai_empty_finderinfo(ai
)) {
4590 * Writing an all 0 blob to the metadata stream results in the
4591 * stream being removed on a macOS server. This ensures we
4592 * behave the same and it verified by the "delete AFP_AfpInfo by
4593 * writing all 0" test.
4595 ret
= SMB_VFS_NEXT_FTRUNCATE(handle
, fsp
, 0);
4597 DBG_ERR("SMB_VFS_NEXT_FTRUNCATE on [%s] failed\n",
4602 ok
= set_delete_on_close(
4605 handle
->conn
->session_info
->security_token
,
4606 handle
->conn
->session_info
->unix_token
);
4608 DBG_ERR("set_delete_on_close on [%s] failed\n",
4615 nwritten
= SMB_VFS_NEXT_PWRITE(handle
, fsp
, data
, n
, offset
);
4616 if (nwritten
!= n
) {
4623 static ssize_t
fruit_pwrite_meta_netatalk(vfs_handle_struct
*handle
,
4624 files_struct
*fsp
, const void *data
,
4625 size_t n
, off_t offset
)
4627 struct adouble
*ad
= NULL
;
4633 ai
= afpinfo_unpack(talloc_tos(), data
);
4638 ad
= ad_fget(talloc_tos(), handle
, fsp
, ADOUBLE_META
);
4640 ad
= ad_init(talloc_tos(), ADOUBLE_META
);
4645 p
= ad_get_entry(ad
, ADEID_FINDERI
);
4647 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp
));
4652 memcpy(p
, &ai
->afpi_FinderInfo
[0], ADEDLEN_FINDERI
);
4654 ret
= ad_fset(handle
, ad
, fsp
);
4656 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp
));
4663 if (!ai_empty_finderinfo(ai
)) {
4668 * Writing an all 0 blob to the metadata stream results in the stream
4669 * being removed on a macOS server. This ensures we behave the same and
4670 * it verified by the "delete AFP_AfpInfo by writing all 0" test.
4673 ok
= set_delete_on_close(
4676 handle
->conn
->session_info
->security_token
,
4677 handle
->conn
->session_info
->unix_token
);
4679 DBG_ERR("set_delete_on_close on [%s] failed\n",
4687 static ssize_t
fruit_pwrite_meta(vfs_handle_struct
*handle
,
4688 files_struct
*fsp
, const void *data
,
4689 size_t n
, off_t offset
)
4691 struct fio
*fio
= (struct fio
*)VFS_FETCH_FSP_EXTENSION(handle
, fsp
);
4693 uint8_t buf
[AFP_INFO_SIZE
];
4699 DBG_ERR("Failed to fetch fsp extension");
4708 if (offset
!= 0 && n
< 60) {
4713 cmp
= memcmp(data
, "AFP", 3);
4719 if (n
<= AFP_OFF_FinderInfo
) {
4721 * Nothing to do here really, just return
4729 if (to_copy
> AFP_INFO_SIZE
) {
4730 to_copy
= AFP_INFO_SIZE
;
4732 memcpy(buf
, data
, to_copy
);
4735 if (to_write
!= AFP_INFO_SIZE
) {
4736 to_write
= AFP_INFO_SIZE
;
4739 switch (fio
->config
->meta
) {
4740 case FRUIT_META_STREAM
:
4741 nwritten
= fruit_pwrite_meta_stream(handle
,
4748 case FRUIT_META_NETATALK
:
4749 nwritten
= fruit_pwrite_meta_netatalk(handle
,
4757 DBG_ERR("Unexpected meta config [%d]\n", fio
->config
->meta
);
4761 if (nwritten
!= to_write
) {
4766 * Return the requested amount, verified against macOS SMB server
4771 static ssize_t
fruit_pwrite_rsrc_stream(vfs_handle_struct
*handle
,
4772 files_struct
*fsp
, const void *data
,
4773 size_t n
, off_t offset
)
4775 return SMB_VFS_NEXT_PWRITE(handle
, fsp
, data
, n
, offset
);
4778 static ssize_t
fruit_pwrite_rsrc_xattr(vfs_handle_struct
*handle
,
4779 files_struct
*fsp
, const void *data
,
4780 size_t n
, off_t offset
)
4782 return SMB_VFS_NEXT_PWRITE(handle
, fsp
, data
, n
, offset
);
4785 static ssize_t
fruit_pwrite_rsrc_adouble(vfs_handle_struct
*handle
,
4786 files_struct
*fsp
, const void *data
,
4787 size_t n
, off_t offset
)
4789 struct adouble
*ad
= NULL
;
4793 ad
= ad_fget(talloc_tos(), handle
, fsp
, ADOUBLE_RSRC
);
4795 DBG_ERR("ad_get [%s] failed\n", fsp_str_dbg(fsp
));
4799 nwritten
= SMB_VFS_NEXT_PWRITE(handle
, fsp
, data
, n
,
4800 offset
+ ad_getentryoff(ad
, ADEID_RFORK
));
4801 if (nwritten
!= n
) {
4802 DBG_ERR("Short write on [%s] [%zd/%zd]\n",
4803 fsp_str_dbg(fsp
), nwritten
, n
);
4808 if ((n
+ offset
) > ad_getentrylen(ad
, ADEID_RFORK
)) {
4809 ad_setentrylen(ad
, ADEID_RFORK
, n
+ offset
);
4810 ret
= ad_fset(handle
, ad
, fsp
);
4812 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp
));
4822 static ssize_t
fruit_pwrite_rsrc(vfs_handle_struct
*handle
,
4823 files_struct
*fsp
, const void *data
,
4824 size_t n
, off_t offset
)
4826 struct fio
*fio
= (struct fio
*)VFS_FETCH_FSP_EXTENSION(handle
, fsp
);
4830 DBG_ERR("Failed to fetch fsp extension");
4834 switch (fio
->config
->rsrc
) {
4835 case FRUIT_RSRC_STREAM
:
4836 nwritten
= fruit_pwrite_rsrc_stream(handle
, fsp
, data
, n
, offset
);
4839 case FRUIT_RSRC_ADFILE
:
4840 nwritten
= fruit_pwrite_rsrc_adouble(handle
, fsp
, data
, n
, offset
);
4843 case FRUIT_RSRC_XATTR
:
4844 nwritten
= fruit_pwrite_rsrc_xattr(handle
, fsp
, data
, n
, offset
);
4848 DBG_ERR("Unexpected rsrc config [%d]\n", fio
->config
->rsrc
);
4855 static ssize_t
fruit_pwrite(vfs_handle_struct
*handle
,
4856 files_struct
*fsp
, const void *data
,
4857 size_t n
, off_t offset
)
4859 struct fio
*fio
= (struct fio
*)VFS_FETCH_FSP_EXTENSION(handle
, fsp
);
4862 DBG_DEBUG("Path [%s] offset=%"PRIdMAX
", size=%zd\n",
4863 fsp_str_dbg(fsp
), (intmax_t)offset
, n
);
4866 return SMB_VFS_NEXT_PWRITE(handle
, fsp
, data
, n
, offset
);
4869 if (fio
->type
== ADOUBLE_META
) {
4870 nwritten
= fruit_pwrite_meta(handle
, fsp
, data
, n
, offset
);
4872 nwritten
= fruit_pwrite_rsrc(handle
, fsp
, data
, n
, offset
);
4875 DBG_DEBUG("Path [%s] nwritten=%zd\n", fsp_str_dbg(fsp
), nwritten
);
4879 struct fruit_pwrite_state
{
4881 struct vfs_aio_state vfs_aio_state
;
4884 static void fruit_pwrite_done(struct tevent_req
*subreq
);
4886 static struct tevent_req
*fruit_pwrite_send(
4887 struct vfs_handle_struct
*handle
,
4888 TALLOC_CTX
*mem_ctx
,
4889 struct tevent_context
*ev
,
4890 struct files_struct
*fsp
,
4892 size_t n
, off_t offset
)
4894 struct tevent_req
*req
= NULL
;
4895 struct tevent_req
*subreq
= NULL
;
4896 struct fruit_pwrite_state
*state
= NULL
;
4897 struct fio
*fio
= (struct fio
*)VFS_FETCH_FSP_EXTENSION(handle
, fsp
);
4899 req
= tevent_req_create(mem_ctx
, &state
,
4900 struct fruit_pwrite_state
);
4905 if (fruit_must_handle_aio_stream(fio
)) {
4906 state
->nwritten
= SMB_VFS_PWRITE(fsp
, data
, n
, offset
);
4907 if (state
->nwritten
!= n
) {
4908 if (state
->nwritten
!= -1) {
4911 tevent_req_error(req
, errno
);
4912 return tevent_req_post(req
, ev
);
4914 tevent_req_done(req
);
4915 return tevent_req_post(req
, ev
);
4918 subreq
= SMB_VFS_NEXT_PWRITE_SEND(state
, ev
, handle
, fsp
,
4920 if (tevent_req_nomem(req
, subreq
)) {
4921 return tevent_req_post(req
, ev
);
4923 tevent_req_set_callback(subreq
, fruit_pwrite_done
, req
);
4927 static void fruit_pwrite_done(struct tevent_req
*subreq
)
4929 struct tevent_req
*req
= tevent_req_callback_data(
4930 subreq
, struct tevent_req
);
4931 struct fruit_pwrite_state
*state
= tevent_req_data(
4932 req
, struct fruit_pwrite_state
);
4934 state
->nwritten
= SMB_VFS_PWRITE_RECV(subreq
, &state
->vfs_aio_state
);
4935 TALLOC_FREE(subreq
);
4937 if (tevent_req_error(req
, state
->vfs_aio_state
.error
)) {
4940 tevent_req_done(req
);
4943 static ssize_t
fruit_pwrite_recv(struct tevent_req
*req
,
4944 struct vfs_aio_state
*vfs_aio_state
)
4946 struct fruit_pwrite_state
*state
= tevent_req_data(
4947 req
, struct fruit_pwrite_state
);
4949 if (tevent_req_is_unix_error(req
, &vfs_aio_state
->error
)) {
4953 *vfs_aio_state
= state
->vfs_aio_state
;
4954 return state
->nwritten
;
4958 * Helper to stat/lstat the base file of an smb_fname.
4960 static int fruit_stat_base(vfs_handle_struct
*handle
,
4961 struct smb_filename
*smb_fname
,
4964 char *tmp_stream_name
;
4967 tmp_stream_name
= smb_fname
->stream_name
;
4968 smb_fname
->stream_name
= NULL
;
4970 rc
= SMB_VFS_NEXT_STAT(handle
, smb_fname
);
4972 rc
= SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
4974 smb_fname
->stream_name
= tmp_stream_name
;
4976 DBG_DEBUG("fruit_stat_base [%s] dev [%ju] ino [%ju]\n",
4977 smb_fname
->base_name
,
4978 (uintmax_t)smb_fname
->st
.st_ex_dev
,
4979 (uintmax_t)smb_fname
->st
.st_ex_ino
);
4983 static int fruit_stat_meta_stream(vfs_handle_struct
*handle
,
4984 struct smb_filename
*smb_fname
,
4990 ret
= fruit_stat_base(handle
, smb_fname
, false);
4995 ino
= fruit_inode(&smb_fname
->st
, smb_fname
->stream_name
);
4998 ret
= SMB_VFS_NEXT_STAT(handle
, smb_fname
);
5000 ret
= SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
5003 smb_fname
->st
.st_ex_ino
= ino
;
5008 static int fruit_stat_meta_netatalk(vfs_handle_struct
*handle
,
5009 struct smb_filename
*smb_fname
,
5012 struct adouble
*ad
= NULL
;
5014 ad
= ad_get(talloc_tos(), handle
, smb_fname
, ADOUBLE_META
);
5016 DBG_INFO("fruit_stat_meta %s: %s\n",
5017 smb_fname_str_dbg(smb_fname
), strerror(errno
));
5023 /* Populate the stat struct with info from the base file. */
5024 if (fruit_stat_base(handle
, smb_fname
, follow_links
) == -1) {
5027 smb_fname
->st
.st_ex_size
= AFP_INFO_SIZE
;
5028 smb_fname
->st
.st_ex_ino
= fruit_inode(&smb_fname
->st
,
5029 smb_fname
->stream_name
);
5033 static int fruit_stat_meta(vfs_handle_struct
*handle
,
5034 struct smb_filename
*smb_fname
,
5037 struct fruit_config_data
*config
= NULL
;
5040 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
5041 struct fruit_config_data
, return -1);
5043 switch (config
->meta
) {
5044 case FRUIT_META_STREAM
:
5045 ret
= fruit_stat_meta_stream(handle
, smb_fname
, follow_links
);
5048 case FRUIT_META_NETATALK
:
5049 ret
= fruit_stat_meta_netatalk(handle
, smb_fname
, follow_links
);
5053 DBG_ERR("Unexpected meta config [%d]\n", config
->meta
);
5060 static int fruit_stat_rsrc_netatalk(vfs_handle_struct
*handle
,
5061 struct smb_filename
*smb_fname
,
5064 struct adouble
*ad
= NULL
;
5067 ad
= ad_get(talloc_tos(), handle
, smb_fname
, ADOUBLE_RSRC
);
5073 /* Populate the stat struct with info from the base file. */
5074 ret
= fruit_stat_base(handle
, smb_fname
, follow_links
);
5080 smb_fname
->st
.st_ex_size
= ad_getentrylen(ad
, ADEID_RFORK
);
5081 smb_fname
->st
.st_ex_ino
= fruit_inode(&smb_fname
->st
,
5082 smb_fname
->stream_name
);
5087 static int fruit_stat_rsrc_stream(vfs_handle_struct
*handle
,
5088 struct smb_filename
*smb_fname
,
5094 ret
= SMB_VFS_NEXT_STAT(handle
, smb_fname
);
5096 ret
= SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
5102 static int fruit_stat_rsrc_xattr(vfs_handle_struct
*handle
,
5103 struct smb_filename
*smb_fname
,
5106 #ifdef HAVE_ATTROPEN
5110 /* Populate the stat struct with info from the base file. */
5111 ret
= fruit_stat_base(handle
, smb_fname
, follow_links
);
5116 fd
= attropen(smb_fname
->base_name
,
5117 AFPRESOURCE_EA_NETATALK
,
5123 ret
= sys_fstat(fd
, &smb_fname
->st
, false);
5126 DBG_ERR("fstat [%s:%s] failed\n", smb_fname
->base_name
,
5127 AFPRESOURCE_EA_NETATALK
);
5133 smb_fname
->st
.st_ex_ino
= fruit_inode(&smb_fname
->st
,
5134 smb_fname
->stream_name
);
5144 static int fruit_stat_rsrc(vfs_handle_struct
*handle
,
5145 struct smb_filename
*smb_fname
,
5148 struct fruit_config_data
*config
= NULL
;
5151 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname
));
5153 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
5154 struct fruit_config_data
, return -1);
5156 switch (config
->rsrc
) {
5157 case FRUIT_RSRC_STREAM
:
5158 ret
= fruit_stat_rsrc_stream(handle
, smb_fname
, follow_links
);
5161 case FRUIT_RSRC_XATTR
:
5162 ret
= fruit_stat_rsrc_xattr(handle
, smb_fname
, follow_links
);
5165 case FRUIT_RSRC_ADFILE
:
5166 ret
= fruit_stat_rsrc_netatalk(handle
, smb_fname
, follow_links
);
5170 DBG_ERR("Unexpected rsrc config [%d]\n", config
->rsrc
);
5177 static int fruit_stat(vfs_handle_struct
*handle
,
5178 struct smb_filename
*smb_fname
)
5182 DEBUG(10, ("fruit_stat called for %s\n",
5183 smb_fname_str_dbg(smb_fname
)));
5185 if (!is_ntfs_stream_smb_fname(smb_fname
)
5186 || is_ntfs_default_stream_smb_fname(smb_fname
)) {
5187 rc
= SMB_VFS_NEXT_STAT(handle
, smb_fname
);
5189 update_btime(handle
, smb_fname
);
5195 * Note if lp_posix_paths() is true, we can never
5196 * get here as is_ntfs_stream_smb_fname() is
5197 * always false. So we never need worry about
5198 * not following links here.
5201 if (is_afpinfo_stream(smb_fname
)) {
5202 rc
= fruit_stat_meta(handle
, smb_fname
, true);
5203 } else if (is_afpresource_stream(smb_fname
)) {
5204 rc
= fruit_stat_rsrc(handle
, smb_fname
, true);
5206 return SMB_VFS_NEXT_STAT(handle
, smb_fname
);
5210 update_btime(handle
, smb_fname
);
5211 smb_fname
->st
.st_ex_mode
&= ~S_IFMT
;
5212 smb_fname
->st
.st_ex_mode
|= S_IFREG
;
5213 smb_fname
->st
.st_ex_blocks
=
5214 smb_fname
->st
.st_ex_size
/ STAT_ST_BLOCKSIZE
+ 1;
5219 static int fruit_lstat(vfs_handle_struct
*handle
,
5220 struct smb_filename
*smb_fname
)
5224 DEBUG(10, ("fruit_lstat called for %s\n",
5225 smb_fname_str_dbg(smb_fname
)));
5227 if (!is_ntfs_stream_smb_fname(smb_fname
)
5228 || is_ntfs_default_stream_smb_fname(smb_fname
)) {
5229 rc
= SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
5231 update_btime(handle
, smb_fname
);
5236 if (is_afpinfo_stream(smb_fname
)) {
5237 rc
= fruit_stat_meta(handle
, smb_fname
, false);
5238 } else if (is_afpresource_stream(smb_fname
)) {
5239 rc
= fruit_stat_rsrc(handle
, smb_fname
, false);
5241 return SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
5245 update_btime(handle
, smb_fname
);
5246 smb_fname
->st
.st_ex_mode
&= ~S_IFMT
;
5247 smb_fname
->st
.st_ex_mode
|= S_IFREG
;
5248 smb_fname
->st
.st_ex_blocks
=
5249 smb_fname
->st
.st_ex_size
/ STAT_ST_BLOCKSIZE
+ 1;
5254 static int fruit_fstat_meta_stream(vfs_handle_struct
*handle
,
5256 SMB_STRUCT_STAT
*sbuf
)
5258 struct fio
*fio
= (struct fio
*)VFS_FETCH_FSP_EXTENSION(handle
, fsp
);
5267 ret
= fruit_stat_base(handle
, fsp
->base_fsp
->fsp_name
, false);
5272 *sbuf
= fsp
->base_fsp
->fsp_name
->st
;
5273 sbuf
->st_ex_size
= AFP_INFO_SIZE
;
5274 sbuf
->st_ex_ino
= fruit_inode(sbuf
, fsp
->fsp_name
->stream_name
);
5278 ret
= fruit_stat_base(handle
, fsp
->base_fsp
->fsp_name
, false);
5282 *sbuf
= fsp
->base_fsp
->fsp_name
->st
;
5284 ino
= fruit_inode(sbuf
, fsp
->fsp_name
->stream_name
);
5286 ret
= SMB_VFS_NEXT_FSTAT(handle
, fsp
, sbuf
);
5291 sbuf
->st_ex_ino
= ino
;
5295 static int fruit_fstat_meta_netatalk(vfs_handle_struct
*handle
,
5297 SMB_STRUCT_STAT
*sbuf
)
5301 ret
= fruit_stat_base(handle
, fsp
->base_fsp
->fsp_name
, false);
5306 *sbuf
= fsp
->base_fsp
->fsp_name
->st
;
5307 sbuf
->st_ex_size
= AFP_INFO_SIZE
;
5308 sbuf
->st_ex_ino
= fruit_inode(sbuf
, fsp
->fsp_name
->stream_name
);
5313 static int fruit_fstat_meta(vfs_handle_struct
*handle
,
5315 SMB_STRUCT_STAT
*sbuf
,
5320 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp
));
5322 switch (fio
->config
->meta
) {
5323 case FRUIT_META_STREAM
:
5324 ret
= fruit_fstat_meta_stream(handle
, fsp
, sbuf
);
5327 case FRUIT_META_NETATALK
:
5328 ret
= fruit_fstat_meta_netatalk(handle
, fsp
, sbuf
);
5332 DBG_ERR("Unexpected meta config [%d]\n", fio
->config
->meta
);
5336 DBG_DEBUG("Path [%s] ret [%d]\n", fsp_str_dbg(fsp
), ret
);
5340 static int fruit_fstat_rsrc_xattr(vfs_handle_struct
*handle
,
5342 SMB_STRUCT_STAT
*sbuf
)
5344 return SMB_VFS_NEXT_FSTAT(handle
, fsp
, sbuf
);
5347 static int fruit_fstat_rsrc_stream(vfs_handle_struct
*handle
,
5349 SMB_STRUCT_STAT
*sbuf
)
5351 return SMB_VFS_NEXT_FSTAT(handle
, fsp
, sbuf
);
5354 static int fruit_fstat_rsrc_adouble(vfs_handle_struct
*handle
,
5356 SMB_STRUCT_STAT
*sbuf
)
5358 struct adouble
*ad
= NULL
;
5361 /* Populate the stat struct with info from the base file. */
5362 ret
= fruit_stat_base(handle
, fsp
->base_fsp
->fsp_name
, false);
5367 ad
= ad_get(talloc_tos(), handle
,
5368 fsp
->base_fsp
->fsp_name
,
5371 DBG_ERR("ad_get [%s] failed [%s]\n",
5372 fsp_str_dbg(fsp
), strerror(errno
));
5376 *sbuf
= fsp
->base_fsp
->fsp_name
->st
;
5377 sbuf
->st_ex_size
= ad_getentrylen(ad
, ADEID_RFORK
);
5378 sbuf
->st_ex_ino
= fruit_inode(sbuf
, fsp
->fsp_name
->stream_name
);
5384 static int fruit_fstat_rsrc(vfs_handle_struct
*handle
, files_struct
*fsp
,
5385 SMB_STRUCT_STAT
*sbuf
, struct fio
*fio
)
5389 switch (fio
->config
->rsrc
) {
5390 case FRUIT_RSRC_STREAM
:
5391 ret
= fruit_fstat_rsrc_stream(handle
, fsp
, sbuf
);
5394 case FRUIT_RSRC_ADFILE
:
5395 ret
= fruit_fstat_rsrc_adouble(handle
, fsp
, sbuf
);
5398 case FRUIT_RSRC_XATTR
:
5399 ret
= fruit_fstat_rsrc_xattr(handle
, fsp
, sbuf
);
5403 DBG_ERR("Unexpected rsrc config [%d]\n", fio
->config
->rsrc
);
5410 static int fruit_fstat(vfs_handle_struct
*handle
, files_struct
*fsp
,
5411 SMB_STRUCT_STAT
*sbuf
)
5413 struct fio
*fio
= (struct fio
*)VFS_FETCH_FSP_EXTENSION(handle
, fsp
);
5417 return SMB_VFS_NEXT_FSTAT(handle
, fsp
, sbuf
);
5420 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp
));
5422 if (fio
->type
== ADOUBLE_META
) {
5423 rc
= fruit_fstat_meta(handle
, fsp
, sbuf
, fio
);
5425 rc
= fruit_fstat_rsrc(handle
, fsp
, sbuf
, fio
);
5429 sbuf
->st_ex_mode
&= ~S_IFMT
;
5430 sbuf
->st_ex_mode
|= S_IFREG
;
5431 sbuf
->st_ex_blocks
= sbuf
->st_ex_size
/ STAT_ST_BLOCKSIZE
+ 1;
5434 DBG_DEBUG("Path [%s] rc [%d] size [%"PRIdMAX
"]\n",
5435 fsp_str_dbg(fsp
), rc
, (intmax_t)sbuf
->st_ex_size
);
5439 static NTSTATUS
delete_invalid_meta_stream(
5440 vfs_handle_struct
*handle
,
5441 const struct smb_filename
*smb_fname
,
5442 TALLOC_CTX
*mem_ctx
,
5443 unsigned int *pnum_streams
,
5444 struct stream_struct
**pstreams
,
5447 struct smb_filename
*sname
= NULL
;
5451 ok
= del_fruit_stream(mem_ctx
, pnum_streams
, pstreams
, AFPINFO_STREAM
);
5453 return NT_STATUS_INTERNAL_ERROR
;
5457 return NT_STATUS_OK
;
5460 sname
= synthetic_smb_fname(talloc_tos(),
5461 smb_fname
->base_name
,
5462 AFPINFO_STREAM_NAME
,
5464 if (sname
== NULL
) {
5465 return NT_STATUS_NO_MEMORY
;
5468 ret
= SMB_VFS_NEXT_UNLINK(handle
, sname
);
5471 DBG_ERR("Removing [%s] failed\n", smb_fname_str_dbg(sname
));
5472 return map_nt_error_from_unix(errno
);
5475 return NT_STATUS_OK
;
5478 static NTSTATUS
fruit_streaminfo_meta_stream(
5479 vfs_handle_struct
*handle
,
5480 struct files_struct
*fsp
,
5481 const struct smb_filename
*smb_fname
,
5482 TALLOC_CTX
*mem_ctx
,
5483 unsigned int *pnum_streams
,
5484 struct stream_struct
**pstreams
)
5486 struct stream_struct
*stream
= *pstreams
;
5487 unsigned int num_streams
= *pnum_streams
;
5490 for (i
= 0; i
< num_streams
; i
++) {
5491 if (strequal_m(stream
[i
].name
, AFPINFO_STREAM
)) {
5496 if (i
== num_streams
) {
5497 return NT_STATUS_OK
;
5500 if (stream
[i
].size
!= AFP_INFO_SIZE
) {
5501 DBG_ERR("Removing invalid AFPINFO_STREAM size [%jd] from [%s]\n",
5502 (intmax_t)stream
[i
].size
, smb_fname_str_dbg(smb_fname
));
5504 return delete_invalid_meta_stream(handle
,
5513 return NT_STATUS_OK
;
5516 static NTSTATUS
fruit_streaminfo_meta_netatalk(
5517 vfs_handle_struct
*handle
,
5518 struct files_struct
*fsp
,
5519 const struct smb_filename
*smb_fname
,
5520 TALLOC_CTX
*mem_ctx
,
5521 unsigned int *pnum_streams
,
5522 struct stream_struct
**pstreams
)
5524 struct stream_struct
*stream
= *pstreams
;
5525 unsigned int num_streams
= *pnum_streams
;
5526 struct adouble
*ad
= NULL
;
5531 /* Remove the Netatalk xattr from the list */
5532 ok
= del_fruit_stream(mem_ctx
, pnum_streams
, pstreams
,
5533 ":" NETATALK_META_XATTR
":$DATA");
5535 return NT_STATUS_NO_MEMORY
;
5539 * Check if there's a AFPINFO_STREAM from the VFS streams
5540 * backend and if yes, remove it from the list
5542 for (i
= 0; i
< num_streams
; i
++) {
5543 if (strequal_m(stream
[i
].name
, AFPINFO_STREAM
)) {
5548 if (i
< num_streams
) {
5549 DBG_WARNING("Unexpected AFPINFO_STREAM on [%s]\n",
5550 smb_fname_str_dbg(smb_fname
));
5552 ok
= del_fruit_stream(mem_ctx
, pnum_streams
, pstreams
,
5555 return NT_STATUS_INTERNAL_ERROR
;
5559 ad
= ad_get(talloc_tos(), handle
, smb_fname
, ADOUBLE_META
);
5561 return NT_STATUS_OK
;
5564 is_fi_empty
= ad_empty_finderinfo(ad
);
5568 return NT_STATUS_OK
;
5571 ok
= add_fruit_stream(mem_ctx
, pnum_streams
, pstreams
,
5572 AFPINFO_STREAM_NAME
, AFP_INFO_SIZE
,
5573 smb_roundup(handle
->conn
, AFP_INFO_SIZE
));
5575 return NT_STATUS_NO_MEMORY
;
5578 return NT_STATUS_OK
;
5581 static NTSTATUS
fruit_streaminfo_meta(vfs_handle_struct
*handle
,
5582 struct files_struct
*fsp
,
5583 const struct smb_filename
*smb_fname
,
5584 TALLOC_CTX
*mem_ctx
,
5585 unsigned int *pnum_streams
,
5586 struct stream_struct
**pstreams
)
5588 struct fruit_config_data
*config
= NULL
;
5591 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct fruit_config_data
,
5592 return NT_STATUS_INTERNAL_ERROR
);
5594 switch (config
->meta
) {
5595 case FRUIT_META_NETATALK
:
5596 status
= fruit_streaminfo_meta_netatalk(handle
, fsp
, smb_fname
,
5597 mem_ctx
, pnum_streams
,
5601 case FRUIT_META_STREAM
:
5602 status
= fruit_streaminfo_meta_stream(handle
, fsp
, smb_fname
,
5603 mem_ctx
, pnum_streams
,
5608 return NT_STATUS_INTERNAL_ERROR
;
5614 static NTSTATUS
fruit_streaminfo_rsrc_stream(
5615 vfs_handle_struct
*handle
,
5616 struct files_struct
*fsp
,
5617 const struct smb_filename
*smb_fname
,
5618 TALLOC_CTX
*mem_ctx
,
5619 unsigned int *pnum_streams
,
5620 struct stream_struct
**pstreams
)
5624 ok
= filter_empty_rsrc_stream(pnum_streams
, pstreams
);
5626 DBG_ERR("Filtering resource stream failed\n");
5627 return NT_STATUS_INTERNAL_ERROR
;
5629 return NT_STATUS_OK
;
5632 static NTSTATUS
fruit_streaminfo_rsrc_xattr(
5633 vfs_handle_struct
*handle
,
5634 struct files_struct
*fsp
,
5635 const struct smb_filename
*smb_fname
,
5636 TALLOC_CTX
*mem_ctx
,
5637 unsigned int *pnum_streams
,
5638 struct stream_struct
**pstreams
)
5642 ok
= filter_empty_rsrc_stream(pnum_streams
, pstreams
);
5644 DBG_ERR("Filtering resource stream failed\n");
5645 return NT_STATUS_INTERNAL_ERROR
;
5647 return NT_STATUS_OK
;
5650 static NTSTATUS
fruit_streaminfo_rsrc_adouble(
5651 vfs_handle_struct
*handle
,
5652 struct files_struct
*fsp
,
5653 const struct smb_filename
*smb_fname
,
5654 TALLOC_CTX
*mem_ctx
,
5655 unsigned int *pnum_streams
,
5656 struct stream_struct
**pstreams
)
5658 struct stream_struct
*stream
= *pstreams
;
5659 unsigned int num_streams
= *pnum_streams
;
5660 struct adouble
*ad
= NULL
;
5666 * Check if there's a AFPRESOURCE_STREAM from the VFS streams backend
5667 * and if yes, remove it from the list
5669 for (i
= 0; i
< num_streams
; i
++) {
5670 if (strequal_m(stream
[i
].name
, AFPRESOURCE_STREAM
)) {
5675 if (i
< num_streams
) {
5676 DBG_WARNING("Unexpected AFPRESOURCE_STREAM on [%s]\n",
5677 smb_fname_str_dbg(smb_fname
));
5679 ok
= del_fruit_stream(mem_ctx
, pnum_streams
, pstreams
,
5680 AFPRESOURCE_STREAM
);
5682 return NT_STATUS_INTERNAL_ERROR
;
5686 ad
= ad_get(talloc_tos(), handle
, smb_fname
, ADOUBLE_RSRC
);
5688 return NT_STATUS_OK
;
5691 rlen
= ad_getentrylen(ad
, ADEID_RFORK
);
5695 return NT_STATUS_OK
;
5698 ok
= add_fruit_stream(mem_ctx
, pnum_streams
, pstreams
,
5699 AFPRESOURCE_STREAM_NAME
, rlen
,
5700 smb_roundup(handle
->conn
, rlen
));
5702 return NT_STATUS_NO_MEMORY
;
5705 return NT_STATUS_OK
;
5708 static NTSTATUS
fruit_streaminfo_rsrc(vfs_handle_struct
*handle
,
5709 struct files_struct
*fsp
,
5710 const struct smb_filename
*smb_fname
,
5711 TALLOC_CTX
*mem_ctx
,
5712 unsigned int *pnum_streams
,
5713 struct stream_struct
**pstreams
)
5715 struct fruit_config_data
*config
= NULL
;
5718 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct fruit_config_data
,
5719 return NT_STATUS_INTERNAL_ERROR
);
5721 switch (config
->rsrc
) {
5722 case FRUIT_RSRC_STREAM
:
5723 status
= fruit_streaminfo_rsrc_stream(handle
, fsp
, smb_fname
,
5724 mem_ctx
, pnum_streams
,
5728 case FRUIT_RSRC_XATTR
:
5729 status
= fruit_streaminfo_rsrc_xattr(handle
, fsp
, smb_fname
,
5730 mem_ctx
, pnum_streams
,
5734 case FRUIT_RSRC_ADFILE
:
5735 status
= fruit_streaminfo_rsrc_adouble(handle
, fsp
, smb_fname
,
5736 mem_ctx
, pnum_streams
,
5741 return NT_STATUS_INTERNAL_ERROR
;
5747 static void fruit_filter_empty_streams(unsigned int *pnum_streams
,
5748 struct stream_struct
**pstreams
)
5750 unsigned num_streams
= *pnum_streams
;
5751 struct stream_struct
*streams
= *pstreams
;
5754 if (!global_fruit_config
.nego_aapl
) {
5758 while (i
< num_streams
) {
5759 struct smb_filename smb_fname
= (struct smb_filename
) {
5760 .stream_name
= streams
[i
].name
,
5763 if (is_ntfs_default_stream_smb_fname(&smb_fname
)
5764 || streams
[i
].size
> 0)
5770 streams
[i
] = streams
[num_streams
- 1];
5774 *pnum_streams
= num_streams
;
5777 static NTSTATUS
fruit_streaminfo(vfs_handle_struct
*handle
,
5778 struct files_struct
*fsp
,
5779 const struct smb_filename
*smb_fname
,
5780 TALLOC_CTX
*mem_ctx
,
5781 unsigned int *pnum_streams
,
5782 struct stream_struct
**pstreams
)
5784 struct fruit_config_data
*config
= NULL
;
5787 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct fruit_config_data
,
5788 return NT_STATUS_UNSUCCESSFUL
);
5790 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname
));
5792 status
= SMB_VFS_NEXT_STREAMINFO(handle
, fsp
, smb_fname
, mem_ctx
,
5793 pnum_streams
, pstreams
);
5794 if (!NT_STATUS_IS_OK(status
)) {
5798 fruit_filter_empty_streams(pnum_streams
, pstreams
);
5800 status
= fruit_streaminfo_meta(handle
, fsp
, smb_fname
,
5801 mem_ctx
, pnum_streams
, pstreams
);
5802 if (!NT_STATUS_IS_OK(status
)) {
5806 status
= fruit_streaminfo_rsrc(handle
, fsp
, smb_fname
,
5807 mem_ctx
, pnum_streams
, pstreams
);
5808 if (!NT_STATUS_IS_OK(status
)) {
5812 return NT_STATUS_OK
;
5815 static int fruit_ntimes(vfs_handle_struct
*handle
,
5816 const struct smb_filename
*smb_fname
,
5817 struct smb_file_time
*ft
)
5820 struct adouble
*ad
= NULL
;
5821 struct fruit_config_data
*config
= NULL
;
5823 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct fruit_config_data
,
5826 if ((config
->meta
!= FRUIT_META_NETATALK
) ||
5827 null_timespec(ft
->create_time
))
5829 return SMB_VFS_NEXT_NTIMES(handle
, smb_fname
, ft
);
5832 DEBUG(10,("set btime for %s to %s\n", smb_fname_str_dbg(smb_fname
),
5833 time_to_asc(convert_timespec_to_time_t(ft
->create_time
))));
5835 ad
= ad_get(talloc_tos(), handle
, smb_fname
, ADOUBLE_META
);
5840 ad_setdate(ad
, AD_DATE_CREATE
| AD_DATE_UNIX
,
5841 convert_time_t_to_uint32_t(ft
->create_time
.tv_sec
));
5843 rc
= ad_set(handle
, ad
, smb_fname
);
5849 DEBUG(1, ("fruit_ntimes: %s\n", smb_fname_str_dbg(smb_fname
)));
5852 return SMB_VFS_NEXT_NTIMES(handle
, smb_fname
, ft
);
5855 static int fruit_fallocate(struct vfs_handle_struct
*handle
,
5856 struct files_struct
*fsp
,
5861 struct fio
*fio
= (struct fio
*)VFS_FETCH_FSP_EXTENSION(handle
, fsp
);
5864 return SMB_VFS_NEXT_FALLOCATE(handle
, fsp
, mode
, offset
, len
);
5867 /* Let the pwrite code path handle it. */
5872 static int fruit_ftruncate_rsrc_xattr(struct vfs_handle_struct
*handle
,
5873 struct files_struct
*fsp
,
5876 #ifdef HAVE_ATTROPEN
5877 return SMB_VFS_NEXT_FTRUNCATE(handle
, fsp
, offset
);
5882 static int fruit_ftruncate_rsrc_adouble(struct vfs_handle_struct
*handle
,
5883 struct files_struct
*fsp
,
5887 struct adouble
*ad
= NULL
;
5890 ad
= ad_fget(talloc_tos(), handle
, fsp
, ADOUBLE_RSRC
);
5892 DBG_DEBUG("ad_get [%s] failed [%s]\n",
5893 fsp_str_dbg(fsp
), strerror(errno
));
5897 ad_off
= ad_getentryoff(ad
, ADEID_RFORK
);
5899 rc
= ftruncate(fsp
->fh
->fd
, offset
+ ad_off
);
5905 ad_setentrylen(ad
, ADEID_RFORK
, offset
);
5907 rc
= ad_fset(handle
, ad
, fsp
);
5909 DBG_ERR("ad_fset [%s] failed [%s]\n",
5910 fsp_str_dbg(fsp
), strerror(errno
));
5919 static int fruit_ftruncate_rsrc_stream(struct vfs_handle_struct
*handle
,
5920 struct files_struct
*fsp
,
5923 return SMB_VFS_NEXT_FTRUNCATE(handle
, fsp
, offset
);
5926 static int fruit_ftruncate_rsrc(struct vfs_handle_struct
*handle
,
5927 struct files_struct
*fsp
,
5930 struct fio
*fio
= (struct fio
*)VFS_FETCH_FSP_EXTENSION(handle
, fsp
);
5934 DBG_ERR("Failed to fetch fsp extension");
5938 switch (fio
->config
->rsrc
) {
5939 case FRUIT_RSRC_XATTR
:
5940 ret
= fruit_ftruncate_rsrc_xattr(handle
, fsp
, offset
);
5943 case FRUIT_RSRC_ADFILE
:
5944 ret
= fruit_ftruncate_rsrc_adouble(handle
, fsp
, offset
);
5947 case FRUIT_RSRC_STREAM
:
5948 ret
= fruit_ftruncate_rsrc_stream(handle
, fsp
, offset
);
5952 DBG_ERR("Unexpected rsrc config [%d]\n", fio
->config
->rsrc
);
5960 static int fruit_ftruncate_meta(struct vfs_handle_struct
*handle
,
5961 struct files_struct
*fsp
,
5965 DBG_WARNING("ftruncate %s to %jd",
5966 fsp_str_dbg(fsp
), (intmax_t)offset
);
5967 /* OS X returns NT_STATUS_ALLOTTED_SPACE_EXCEEDED */
5972 /* OS X returns success but does nothing */
5973 DBG_INFO("ignoring ftruncate %s to %jd\n",
5974 fsp_str_dbg(fsp
), (intmax_t)offset
);
5978 static int fruit_ftruncate(struct vfs_handle_struct
*handle
,
5979 struct files_struct
*fsp
,
5982 struct fio
*fio
= (struct fio
*)VFS_FETCH_FSP_EXTENSION(handle
, fsp
);
5985 DBG_DEBUG("Path [%s] offset [%"PRIdMAX
"]\n", fsp_str_dbg(fsp
),
5989 return SMB_VFS_NEXT_FTRUNCATE(handle
, fsp
, offset
);
5992 if (fio
->type
== ADOUBLE_META
) {
5993 ret
= fruit_ftruncate_meta(handle
, fsp
, offset
);
5995 ret
= fruit_ftruncate_rsrc(handle
, fsp
, offset
);
5998 DBG_DEBUG("Path [%s] result [%d]\n", fsp_str_dbg(fsp
), ret
);
6002 static NTSTATUS
fruit_create_file(vfs_handle_struct
*handle
,
6003 struct smb_request
*req
,
6004 uint16_t root_dir_fid
,
6005 struct smb_filename
*smb_fname
,
6006 uint32_t access_mask
,
6007 uint32_t share_access
,
6008 uint32_t create_disposition
,
6009 uint32_t create_options
,
6010 uint32_t file_attributes
,
6011 uint32_t oplock_request
,
6012 struct smb2_lease
*lease
,
6013 uint64_t allocation_size
,
6014 uint32_t private_flags
,
6015 struct security_descriptor
*sd
,
6016 struct ea_list
*ea_list
,
6017 files_struct
**result
,
6019 const struct smb2_create_blobs
*in_context_blobs
,
6020 struct smb2_create_blobs
*out_context_blobs
)
6023 struct fruit_config_data
*config
= NULL
;
6024 files_struct
*fsp
= NULL
;
6025 struct fio
*fio
= NULL
;
6026 bool internal_open
= (oplock_request
& INTERNAL_OPEN_ONLY
);
6029 status
= check_aapl(handle
, req
, in_context_blobs
, out_context_blobs
);
6030 if (!NT_STATUS_IS_OK(status
)) {
6034 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct fruit_config_data
,
6035 return NT_STATUS_UNSUCCESSFUL
);
6037 if (is_apple_stream(smb_fname
) && !internal_open
) {
6038 ret
= ad_convert(handle
, smb_fname
);
6040 DBG_ERR("ad_convert() failed\n");
6041 return NT_STATUS_UNSUCCESSFUL
;
6045 status
= SMB_VFS_NEXT_CREATE_FILE(
6046 handle
, req
, root_dir_fid
, smb_fname
,
6047 access_mask
, share_access
,
6048 create_disposition
, create_options
,
6049 file_attributes
, oplock_request
,
6051 allocation_size
, private_flags
,
6052 sd
, ea_list
, result
,
6053 pinfo
, in_context_blobs
, out_context_blobs
);
6054 if (!NT_STATUS_IS_OK(status
)) {
6060 if (global_fruit_config
.nego_aapl
) {
6061 if (config
->posix_rename
&& fsp
->is_directory
) {
6063 * Enable POSIX directory rename behaviour
6065 fsp
->posix_flags
|= FSP_POSIX_FLAGS_RENAME
;
6070 * If this is a plain open for existing files, opening an 0
6071 * byte size resource fork MUST fail with
6072 * NT_STATUS_OBJECT_NAME_NOT_FOUND.
6074 * Cf the vfs_fruit torture tests in test_rfork_create().
6076 if (global_fruit_config
.nego_aapl
&&
6077 create_disposition
== FILE_OPEN
&&
6078 smb_fname
->st
.st_ex_size
== 0 &&
6079 is_ntfs_stream_smb_fname(smb_fname
) &&
6080 !(is_ntfs_default_stream_smb_fname(smb_fname
)))
6082 status
= NT_STATUS_OBJECT_NAME_NOT_FOUND
;
6086 fio
= (struct fio
*)VFS_FETCH_FSP_EXTENSION(handle
, fsp
);
6087 if (fio
!= NULL
&& pinfo
!= NULL
&& *pinfo
== FILE_WAS_CREATED
) {
6088 fio
->created
= true;
6091 if (is_ntfs_stream_smb_fname(smb_fname
)
6092 || fsp
->is_directory
) {
6096 if ((config
->locking
== FRUIT_LOCKING_NETATALK
) &&
6099 status
= fruit_check_access(
6103 if (!NT_STATUS_IS_OK(status
)) {
6111 DEBUG(10, ("fruit_create_file: %s\n", nt_errstr(status
)));
6114 close_file(req
, fsp
, ERROR_CLOSE
);
6115 *result
= fsp
= NULL
;
6121 static NTSTATUS
fruit_readdir_attr(struct vfs_handle_struct
*handle
,
6122 const struct smb_filename
*fname
,
6123 TALLOC_CTX
*mem_ctx
,
6124 struct readdir_attr_data
**pattr_data
)
6126 struct fruit_config_data
*config
= NULL
;
6127 struct readdir_attr_data
*attr_data
;
6131 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
6132 struct fruit_config_data
,
6133 return NT_STATUS_UNSUCCESSFUL
);
6135 if (!global_fruit_config
.nego_aapl
) {
6136 return SMB_VFS_NEXT_READDIR_ATTR(handle
, fname
, mem_ctx
, pattr_data
);
6139 DEBUG(10, ("fruit_readdir_attr %s\n", fname
->base_name
));
6141 ret
= ad_convert(handle
, fname
);
6143 DBG_ERR("ad_convert() failed\n");
6144 return NT_STATUS_UNSUCCESSFUL
;
6147 *pattr_data
= talloc_zero(mem_ctx
, struct readdir_attr_data
);
6148 if (*pattr_data
== NULL
) {
6149 return NT_STATUS_UNSUCCESSFUL
;
6151 attr_data
= *pattr_data
;
6152 attr_data
->type
= RDATTR_AAPL
;
6155 * Mac metadata: compressed FinderInfo, resource fork length
6158 status
= readdir_attr_macmeta(handle
, fname
, attr_data
);
6159 if (!NT_STATUS_IS_OK(status
)) {
6161 * Error handling is tricky: if we return failure from
6162 * this function, the corresponding directory entry
6163 * will to be passed to the client, so we really just
6164 * want to error out on fatal errors.
6166 if (!NT_STATUS_EQUAL(status
, NT_STATUS_ACCESS_DENIED
)) {
6174 if (config
->unix_info_enabled
) {
6175 attr_data
->attr_data
.aapl
.unix_mode
= fname
->st
.st_ex_mode
;
6181 if (!config
->readdir_attr_max_access
) {
6182 attr_data
->attr_data
.aapl
.max_access
= FILE_GENERIC_ALL
;
6184 status
= smbd_calculate_access_mask(
6188 SEC_FLAG_MAXIMUM_ALLOWED
,
6189 &attr_data
->attr_data
.aapl
.max_access
);
6190 if (!NT_STATUS_IS_OK(status
)) {
6195 return NT_STATUS_OK
;
6198 DEBUG(1, ("fruit_readdir_attr %s, error: %s\n",
6199 fname
->base_name
, nt_errstr(status
)));
6200 TALLOC_FREE(*pattr_data
);
6204 static NTSTATUS
fruit_fget_nt_acl(vfs_handle_struct
*handle
,
6206 uint32_t security_info
,
6207 TALLOC_CTX
*mem_ctx
,
6208 struct security_descriptor
**ppdesc
)
6211 struct security_ace ace
;
6213 struct fruit_config_data
*config
;
6215 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
6216 struct fruit_config_data
,
6217 return NT_STATUS_UNSUCCESSFUL
);
6219 status
= SMB_VFS_NEXT_FGET_NT_ACL(handle
, fsp
, security_info
,
6221 if (!NT_STATUS_IS_OK(status
)) {
6226 * Add MS NFS style ACEs with uid, gid and mode
6228 if (!global_fruit_config
.nego_aapl
) {
6229 return NT_STATUS_OK
;
6231 if (!config
->unix_info_enabled
) {
6232 return NT_STATUS_OK
;
6235 /* First remove any existing ACE's with NFS style mode/uid/gid SIDs. */
6236 status
= remove_virtual_nfs_aces(*ppdesc
);
6237 if (!NT_STATUS_IS_OK(status
)) {
6238 DBG_WARNING("failed to remove MS NFS style ACEs\n");
6242 /* MS NFS style mode */
6243 sid_compose(&sid
, &global_sid_Unix_NFS_Mode
, fsp
->fsp_name
->st
.st_ex_mode
);
6244 init_sec_ace(&ace
, &sid
, SEC_ACE_TYPE_ACCESS_DENIED
, 0, 0);
6245 status
= security_descriptor_dacl_add(*ppdesc
, &ace
);
6246 if (!NT_STATUS_IS_OK(status
)) {
6247 DEBUG(1,("failed to add MS NFS style ACE\n"));
6251 /* MS NFS style uid */
6252 sid_compose(&sid
, &global_sid_Unix_NFS_Users
, fsp
->fsp_name
->st
.st_ex_uid
);
6253 init_sec_ace(&ace
, &sid
, SEC_ACE_TYPE_ACCESS_DENIED
, 0, 0);
6254 status
= security_descriptor_dacl_add(*ppdesc
, &ace
);
6255 if (!NT_STATUS_IS_OK(status
)) {
6256 DEBUG(1,("failed to add MS NFS style ACE\n"));
6260 /* MS NFS style gid */
6261 sid_compose(&sid
, &global_sid_Unix_NFS_Groups
, fsp
->fsp_name
->st
.st_ex_gid
);
6262 init_sec_ace(&ace
, &sid
, SEC_ACE_TYPE_ACCESS_DENIED
, 0, 0);
6263 status
= security_descriptor_dacl_add(*ppdesc
, &ace
);
6264 if (!NT_STATUS_IS_OK(status
)) {
6265 DEBUG(1,("failed to add MS NFS style ACE\n"));
6269 return NT_STATUS_OK
;
6272 static NTSTATUS
fruit_fset_nt_acl(vfs_handle_struct
*handle
,
6274 uint32_t security_info_sent
,
6275 const struct security_descriptor
*orig_psd
)
6279 mode_t ms_nfs_mode
= 0;
6281 struct security_descriptor
*psd
= NULL
;
6282 uint32_t orig_num_aces
= 0;
6284 if (orig_psd
->dacl
!= NULL
) {
6285 orig_num_aces
= orig_psd
->dacl
->num_aces
;
6288 psd
= security_descriptor_copy(talloc_tos(), orig_psd
);
6290 return NT_STATUS_NO_MEMORY
;
6293 DBG_DEBUG("fruit_fset_nt_acl: %s\n", fsp_str_dbg(fsp
));
6295 status
= check_ms_nfs(handle
, fsp
, psd
, &ms_nfs_mode
, &do_chmod
);
6296 if (!NT_STATUS_IS_OK(status
)) {
6297 DEBUG(1, ("fruit_fset_nt_acl: check_ms_nfs failed%s\n", fsp_str_dbg(fsp
)));
6303 * If only ms_nfs ACE entries were sent, ensure we set the DACL
6304 * sent/present flags correctly now we've removed them.
6307 if (orig_num_aces
!= 0) {
6309 * Are there any ACE's left ?
6311 if (psd
->dacl
->num_aces
== 0) {
6312 /* No - clear the DACL sent/present flags. */
6313 security_info_sent
&= ~SECINFO_DACL
;
6314 psd
->type
&= ~SEC_DESC_DACL_PRESENT
;
6318 status
= SMB_VFS_NEXT_FSET_NT_ACL(handle
, fsp
, security_info_sent
, psd
);
6319 if (!NT_STATUS_IS_OK(status
)) {
6320 DEBUG(1, ("fruit_fset_nt_acl: SMB_VFS_NEXT_FSET_NT_ACL failed%s\n", fsp_str_dbg(fsp
)));
6326 if (fsp
->fh
->fd
!= -1) {
6327 result
= SMB_VFS_FCHMOD(fsp
, ms_nfs_mode
);
6329 result
= SMB_VFS_CHMOD(fsp
->conn
,
6335 DEBUG(1, ("chmod: %s, result: %d, %04o error %s\n", fsp_str_dbg(fsp
),
6336 result
, (unsigned)ms_nfs_mode
,
6338 status
= map_nt_error_from_unix(errno
);
6345 return NT_STATUS_OK
;
6348 static struct vfs_offload_ctx
*fruit_offload_ctx
;
6350 struct fruit_offload_read_state
{
6351 struct vfs_handle_struct
*handle
;
6352 struct tevent_context
*ev
;
6358 static void fruit_offload_read_done(struct tevent_req
*subreq
);
6360 static struct tevent_req
*fruit_offload_read_send(
6361 TALLOC_CTX
*mem_ctx
,
6362 struct tevent_context
*ev
,
6363 struct vfs_handle_struct
*handle
,
6370 struct tevent_req
*req
= NULL
;
6371 struct tevent_req
*subreq
= NULL
;
6372 struct fruit_offload_read_state
*state
= NULL
;
6374 req
= tevent_req_create(mem_ctx
, &state
,
6375 struct fruit_offload_read_state
);
6379 *state
= (struct fruit_offload_read_state
) {
6386 subreq
= SMB_VFS_NEXT_OFFLOAD_READ_SEND(mem_ctx
, ev
, handle
, fsp
,
6387 fsctl
, ttl
, offset
, to_copy
);
6388 if (tevent_req_nomem(subreq
, req
)) {
6389 return tevent_req_post(req
, ev
);
6391 tevent_req_set_callback(subreq
, fruit_offload_read_done
, req
);
6395 static void fruit_offload_read_done(struct tevent_req
*subreq
)
6397 struct tevent_req
*req
= tevent_req_callback_data(
6398 subreq
, struct tevent_req
);
6399 struct fruit_offload_read_state
*state
= tevent_req_data(
6400 req
, struct fruit_offload_read_state
);
6403 status
= SMB_VFS_NEXT_OFFLOAD_READ_RECV(subreq
,
6407 TALLOC_FREE(subreq
);
6408 if (tevent_req_nterror(req
, status
)) {
6412 if (state
->fsctl
!= FSCTL_SRV_REQUEST_RESUME_KEY
) {
6413 tevent_req_done(req
);
6417 status
= vfs_offload_token_ctx_init(state
->fsp
->conn
->sconn
->client
,
6418 &fruit_offload_ctx
);
6419 if (tevent_req_nterror(req
, status
)) {
6423 status
= vfs_offload_token_db_store_fsp(fruit_offload_ctx
,
6426 if (tevent_req_nterror(req
, status
)) {
6430 tevent_req_done(req
);
6434 static NTSTATUS
fruit_offload_read_recv(struct tevent_req
*req
,
6435 struct vfs_handle_struct
*handle
,
6436 TALLOC_CTX
*mem_ctx
,
6439 struct fruit_offload_read_state
*state
= tevent_req_data(
6440 req
, struct fruit_offload_read_state
);
6443 if (tevent_req_is_nterror(req
, &status
)) {
6444 tevent_req_received(req
);
6448 token
->length
= state
->token
.length
;
6449 token
->data
= talloc_move(mem_ctx
, &state
->token
.data
);
6451 tevent_req_received(req
);
6452 return NT_STATUS_OK
;
6455 struct fruit_offload_write_state
{
6456 struct vfs_handle_struct
*handle
;
6458 struct files_struct
*src_fsp
;
6459 struct files_struct
*dst_fsp
;
6463 static void fruit_offload_write_done(struct tevent_req
*subreq
);
6464 static struct tevent_req
*fruit_offload_write_send(struct vfs_handle_struct
*handle
,
6465 TALLOC_CTX
*mem_ctx
,
6466 struct tevent_context
*ev
,
6469 off_t transfer_offset
,
6470 struct files_struct
*dest_fsp
,
6474 struct tevent_req
*req
, *subreq
;
6475 struct fruit_offload_write_state
*state
;
6477 struct fruit_config_data
*config
;
6478 off_t src_off
= transfer_offset
;
6479 files_struct
*src_fsp
= NULL
;
6480 off_t to_copy
= num
;
6481 bool copyfile_enabled
= false;
6483 DEBUG(10,("soff: %ju, doff: %ju, len: %ju\n",
6484 (uintmax_t)src_off
, (uintmax_t)dest_off
, (uintmax_t)num
));
6486 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
6487 struct fruit_config_data
,
6490 req
= tevent_req_create(mem_ctx
, &state
,
6491 struct fruit_offload_write_state
);
6495 state
->handle
= handle
;
6496 state
->dst_fsp
= dest_fsp
;
6499 case FSCTL_SRV_COPYCHUNK
:
6500 case FSCTL_SRV_COPYCHUNK_WRITE
:
6501 copyfile_enabled
= config
->copyfile_enabled
;
6508 * Check if this a OS X copyfile style copychunk request with
6509 * a requested chunk count of 0 that was translated to a
6510 * offload_write_send VFS call overloading the parameters src_off
6511 * = dest_off = num = 0.
6513 if (copyfile_enabled
&& num
== 0 && src_off
== 0 && dest_off
== 0) {
6514 status
= vfs_offload_token_db_fetch_fsp(
6515 fruit_offload_ctx
, token
, &src_fsp
);
6516 if (tevent_req_nterror(req
, status
)) {
6517 return tevent_req_post(req
, ev
);
6519 state
->src_fsp
= src_fsp
;
6521 status
= vfs_stat_fsp(src_fsp
);
6522 if (tevent_req_nterror(req
, status
)) {
6523 return tevent_req_post(req
, ev
);
6526 to_copy
= src_fsp
->fsp_name
->st
.st_ex_size
;
6527 state
->is_copyfile
= true;
6530 subreq
= SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle
,
6539 if (tevent_req_nomem(subreq
, req
)) {
6540 return tevent_req_post(req
, ev
);
6543 tevent_req_set_callback(subreq
, fruit_offload_write_done
, req
);
6547 static void fruit_offload_write_done(struct tevent_req
*subreq
)
6549 struct tevent_req
*req
= tevent_req_callback_data(
6550 subreq
, struct tevent_req
);
6551 struct fruit_offload_write_state
*state
= tevent_req_data(
6552 req
, struct fruit_offload_write_state
);
6554 unsigned int num_streams
= 0;
6555 struct stream_struct
*streams
= NULL
;
6557 struct smb_filename
*src_fname_tmp
= NULL
;
6558 struct smb_filename
*dst_fname_tmp
= NULL
;
6560 status
= SMB_VFS_NEXT_OFFLOAD_WRITE_RECV(state
->handle
,
6563 TALLOC_FREE(subreq
);
6564 if (tevent_req_nterror(req
, status
)) {
6568 if (!state
->is_copyfile
) {
6569 tevent_req_done(req
);
6574 * Now copy all remaining streams. We know the share supports
6575 * streams, because we're in vfs_fruit. We don't do this async
6576 * because streams are few and small.
6578 status
= vfs_streaminfo(state
->handle
->conn
, state
->src_fsp
,
6579 state
->src_fsp
->fsp_name
,
6580 req
, &num_streams
, &streams
);
6581 if (tevent_req_nterror(req
, status
)) {
6585 if (num_streams
== 1) {
6586 /* There is always one stream, ::$DATA. */
6587 tevent_req_done(req
);
6591 for (i
= 0; i
< num_streams
; i
++) {
6592 DEBUG(10, ("%s: stream: '%s'/%zu\n",
6593 __func__
, streams
[i
].name
, (size_t)streams
[i
].size
));
6595 src_fname_tmp
= synthetic_smb_fname(
6597 state
->src_fsp
->fsp_name
->base_name
,
6600 state
->src_fsp
->fsp_name
->flags
);
6601 if (tevent_req_nomem(src_fname_tmp
, req
)) {
6605 if (is_ntfs_default_stream_smb_fname(src_fname_tmp
)) {
6606 TALLOC_FREE(src_fname_tmp
);
6610 dst_fname_tmp
= synthetic_smb_fname(
6612 state
->dst_fsp
->fsp_name
->base_name
,
6615 state
->dst_fsp
->fsp_name
->flags
);
6616 if (tevent_req_nomem(dst_fname_tmp
, req
)) {
6617 TALLOC_FREE(src_fname_tmp
);
6621 status
= copy_file(req
,
6622 state
->handle
->conn
,
6625 OPENX_FILE_CREATE_IF_NOT_EXIST
,
6627 if (!NT_STATUS_IS_OK(status
)) {
6628 DEBUG(1, ("%s: copy %s to %s failed: %s\n", __func__
,
6629 smb_fname_str_dbg(src_fname_tmp
),
6630 smb_fname_str_dbg(dst_fname_tmp
),
6631 nt_errstr(status
)));
6632 TALLOC_FREE(src_fname_tmp
);
6633 TALLOC_FREE(dst_fname_tmp
);
6634 tevent_req_nterror(req
, status
);
6638 TALLOC_FREE(src_fname_tmp
);
6639 TALLOC_FREE(dst_fname_tmp
);
6642 TALLOC_FREE(streams
);
6643 TALLOC_FREE(src_fname_tmp
);
6644 TALLOC_FREE(dst_fname_tmp
);
6645 tevent_req_done(req
);
6648 static NTSTATUS
fruit_offload_write_recv(struct vfs_handle_struct
*handle
,
6649 struct tevent_req
*req
,
6652 struct fruit_offload_write_state
*state
= tevent_req_data(
6653 req
, struct fruit_offload_write_state
);
6656 if (tevent_req_is_nterror(req
, &status
)) {
6657 DEBUG(1, ("server side copy chunk failed: %s\n",
6658 nt_errstr(status
)));
6660 tevent_req_received(req
);
6664 *copied
= state
->copied
;
6665 tevent_req_received(req
);
6667 return NT_STATUS_OK
;
6670 static char *fruit_get_bandsize_line(char **lines
, int numlines
)
6673 static bool re_initialized
= false;
6677 if (!re_initialized
) {
6678 ret
= regcomp(&re
, "^[[:blank:]]*<key>band-size</key>$", 0);
6682 re_initialized
= true;
6685 for (i
= 0; i
< numlines
; i
++) {
6686 regmatch_t matches
[1];
6688 ret
= regexec(&re
, lines
[i
], 1, matches
, 0);
6691 * Check if the match was on the last line, sa we want
6692 * the subsequent line.
6694 if (i
+ 1 == numlines
) {
6697 return lines
[i
+ 1];
6699 if (ret
!= REG_NOMATCH
) {
6707 static bool fruit_get_bandsize_from_line(char *line
, size_t *_band_size
)
6710 static bool re_initialized
= false;
6711 regmatch_t matches
[2];
6716 if (!re_initialized
) {
6719 "<integer>\\([[:digit:]]*\\)</integer>$",
6724 re_initialized
= true;
6727 ret
= regexec(&re
, line
, 2, matches
, 0);
6729 DBG_ERR("regex failed [%s]\n", line
);
6733 line
[matches
[1].rm_eo
] = '\0';
6735 ok
= conv_str_u64(&line
[matches
[1].rm_so
], &band_size
);
6739 *_band_size
= (size_t)band_size
;
6744 * This reads and parses an Info.plist from a TM sparsebundle looking for the
6745 * "band-size" key and value.
6747 static bool fruit_get_bandsize(vfs_handle_struct
*handle
,
6751 #define INFO_PLIST_MAX_SIZE 64*1024
6753 struct smb_filename
*smb_fname
= NULL
;
6754 files_struct
*fsp
= NULL
;
6755 uint8_t *file_data
= NULL
;
6756 char **lines
= NULL
;
6757 char *band_size_line
= NULL
;
6758 size_t plist_file_size
;
6765 plist
= talloc_asprintf(talloc_tos(),
6767 handle
->conn
->connectpath
,
6769 if (plist
== NULL
) {
6774 smb_fname
= synthetic_smb_fname(talloc_tos(), plist
, NULL
, NULL
, 0);
6775 if (smb_fname
== NULL
) {
6780 ret
= SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
6782 DBG_INFO("Ignoring Sparsebundle without Info.plist [%s]\n", dir
);
6787 plist_file_size
= smb_fname
->st
.st_ex_size
;
6789 if (plist_file_size
> INFO_PLIST_MAX_SIZE
) {
6790 DBG_INFO("%s is too large, ignoring\n", plist
);
6795 status
= SMB_VFS_NEXT_CREATE_FILE(
6798 0, /* root_dir_fid */
6799 smb_fname
, /* fname */
6800 FILE_GENERIC_READ
, /* access_mask */
6801 FILE_SHARE_READ
| FILE_SHARE_WRITE
, /* share_access */
6802 FILE_OPEN
, /* create_disposition */
6803 0, /* create_options */
6804 0, /* file_attributes */
6805 INTERNAL_OPEN_ONLY
, /* oplock_request */
6807 0, /* allocation_size */
6808 0, /* private_flags */
6813 NULL
, NULL
); /* create context */
6814 if (!NT_STATUS_IS_OK(status
)) {
6815 DBG_INFO("Opening [%s] failed [%s]\n",
6816 smb_fname_str_dbg(smb_fname
), nt_errstr(status
));
6821 file_data
= talloc_array(talloc_tos(), uint8_t, plist_file_size
);
6822 if (file_data
== NULL
) {
6827 nread
= SMB_VFS_NEXT_PREAD(handle
, fsp
, file_data
, plist_file_size
, 0);
6828 if (nread
!= plist_file_size
) {
6829 DBG_ERR("Short read on [%s]: %zu/%zd\n",
6830 fsp_str_dbg(fsp
), nread
, plist_file_size
);
6836 status
= close_file(NULL
, fsp
, NORMAL_CLOSE
);
6838 if (!NT_STATUS_IS_OK(status
)) {
6839 DBG_ERR("close_file failed: %s\n", nt_errstr(status
));
6844 lines
= file_lines_parse((char *)file_data
,
6848 if (lines
== NULL
) {
6853 band_size_line
= fruit_get_bandsize_line(lines
, numlines
);
6854 if (band_size_line
== NULL
) {
6855 DBG_ERR("Didn't find band-size key in [%s]\n",
6856 smb_fname_str_dbg(smb_fname
));
6861 ok
= fruit_get_bandsize_from_line(band_size_line
, band_size
);
6863 DBG_ERR("fruit_get_bandsize_from_line failed\n");
6867 DBG_DEBUG("Parsed band-size [%zu] for [%s]\n", *band_size
, plist
);
6871 status
= close_file(NULL
, fsp
, NORMAL_CLOSE
);
6872 if (!NT_STATUS_IS_OK(status
)) {
6873 DBG_ERR("close_file failed: %s\n", nt_errstr(status
));
6878 TALLOC_FREE(smb_fname
);
6879 TALLOC_FREE(file_data
);
6884 struct fruit_disk_free_state
{
6888 static bool fruit_get_num_bands(vfs_handle_struct
*handle
,
6893 struct smb_filename
*bands_dir
= NULL
;
6895 struct dirent
*e
= NULL
;
6899 path
= talloc_asprintf(talloc_tos(),
6901 handle
->conn
->connectpath
,
6907 bands_dir
= synthetic_smb_fname(talloc_tos(),
6913 if (bands_dir
== NULL
) {
6917 d
= SMB_VFS_NEXT_OPENDIR(handle
, bands_dir
, NULL
, 0);
6919 TALLOC_FREE(bands_dir
);
6925 for (e
= SMB_VFS_NEXT_READDIR(handle
, d
, NULL
);
6927 e
= SMB_VFS_NEXT_READDIR(handle
, d
, NULL
))
6929 if (ISDOT(e
->d_name
) || ISDOTDOT(e
->d_name
)) {
6935 ret
= SMB_VFS_NEXT_CLOSEDIR(handle
, d
);
6937 TALLOC_FREE(bands_dir
);
6941 DBG_DEBUG("%zu bands in [%s]\n", nbands
, smb_fname_str_dbg(bands_dir
));
6943 TALLOC_FREE(bands_dir
);
6949 static bool fruit_tmsize_do_dirent(vfs_handle_struct
*handle
,
6950 struct fruit_disk_free_state
*state
,
6955 size_t sparsebundle_strlen
= strlen("sparsebundle");
6956 size_t bandsize
= 0;
6960 p
= strstr(e
->d_name
, "sparsebundle");
6965 if (p
[sparsebundle_strlen
] != '\0') {
6969 DBG_DEBUG("Processing sparsebundle [%s]\n", e
->d_name
);
6971 ok
= fruit_get_bandsize(handle
, e
->d_name
, &bandsize
);
6974 * Beware of race conditions: this may be an uninitialized
6975 * Info.plist that a client is just creating. We don't want let
6976 * this to trigger complete failure.
6978 DBG_ERR("Processing sparsebundle [%s] failed\n", e
->d_name
);
6982 ok
= fruit_get_num_bands(handle
, e
->d_name
, &nbands
);
6985 * Beware of race conditions: this may be a backup sparsebundle
6986 * in an early stage lacking a bands subdirectory. We don't want
6987 * let this to trigger complete failure.
6989 DBG_ERR("Processing sparsebundle [%s] failed\n", e
->d_name
);
6993 if (bandsize
> SIZE_MAX
/nbands
) {
6994 DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
6998 tm_size
= bandsize
* nbands
;
7000 if (state
->total_size
+ tm_size
< state
->total_size
) {
7001 DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
7006 state
->total_size
+= tm_size
;
7008 DBG_DEBUG("[%s] tm_size [%jd] total_size [%jd]\n",
7009 e
->d_name
, (intmax_t)tm_size
, (intmax_t)state
->total_size
);
7015 * Calculate used size of a TimeMachine volume
7017 * This assumes that the volume is used only for TimeMachine.
7019 * - readdir(basedir of share), then
7020 * - for every element that matches regex "^\(.*\)\.sparsebundle$" :
7021 * - parse "\1.sparsebundle/Info.plist" and read the band-size XML key
7022 * - count band files in "\1.sparsebundle/bands/"
7023 * - calculate used size of all bands: band_count * band_size
7025 static uint64_t fruit_disk_free(vfs_handle_struct
*handle
,
7026 const struct smb_filename
*smb_fname
,
7031 struct fruit_config_data
*config
= NULL
;
7032 struct fruit_disk_free_state state
= {0};
7034 struct dirent
*e
= NULL
;
7040 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
7041 struct fruit_config_data
,
7044 if (!config
->time_machine
||
7045 config
->time_machine_max_size
== 0)
7047 return SMB_VFS_NEXT_DISK_FREE(handle
,
7054 d
= SMB_VFS_NEXT_OPENDIR(handle
, smb_fname
, NULL
, 0);
7059 for (e
= SMB_VFS_NEXT_READDIR(handle
, d
, NULL
);
7061 e
= SMB_VFS_NEXT_READDIR(handle
, d
, NULL
))
7063 ok
= fruit_tmsize_do_dirent(handle
, &state
, e
);
7065 SMB_VFS_NEXT_CLOSEDIR(handle
, d
);
7070 ret
= SMB_VFS_NEXT_CLOSEDIR(handle
, d
);
7075 dsize
= config
->time_machine_max_size
/ 512;
7076 dfree
= dsize
- (state
.total_size
/ 512);
7077 if (dfree
> dsize
) {
7087 static struct vfs_fn_pointers vfs_fruit_fns
= {
7088 .connect_fn
= fruit_connect
,
7089 .disk_free_fn
= fruit_disk_free
,
7091 /* File operations */
7092 .chmod_fn
= fruit_chmod
,
7093 .chown_fn
= fruit_chown
,
7094 .unlink_fn
= fruit_unlink
,
7095 .rename_fn
= fruit_rename
,
7096 .rmdir_fn
= fruit_rmdir
,
7097 .open_fn
= fruit_open
,
7098 .close_fn
= fruit_close
,
7099 .pread_fn
= fruit_pread
,
7100 .pwrite_fn
= fruit_pwrite
,
7101 .pread_send_fn
= fruit_pread_send
,
7102 .pread_recv_fn
= fruit_pread_recv
,
7103 .pwrite_send_fn
= fruit_pwrite_send
,
7104 .pwrite_recv_fn
= fruit_pwrite_recv
,
7105 .stat_fn
= fruit_stat
,
7106 .lstat_fn
= fruit_lstat
,
7107 .fstat_fn
= fruit_fstat
,
7108 .streaminfo_fn
= fruit_streaminfo
,
7109 .ntimes_fn
= fruit_ntimes
,
7110 .ftruncate_fn
= fruit_ftruncate
,
7111 .fallocate_fn
= fruit_fallocate
,
7112 .create_file_fn
= fruit_create_file
,
7113 .readdir_attr_fn
= fruit_readdir_attr
,
7114 .offload_read_send_fn
= fruit_offload_read_send
,
7115 .offload_read_recv_fn
= fruit_offload_read_recv
,
7116 .offload_write_send_fn
= fruit_offload_write_send
,
7117 .offload_write_recv_fn
= fruit_offload_write_recv
,
7119 /* NT ACL operations */
7120 .fget_nt_acl_fn
= fruit_fget_nt_acl
,
7121 .fset_nt_acl_fn
= fruit_fset_nt_acl
,
7125 NTSTATUS
vfs_fruit_init(TALLOC_CTX
*ctx
)
7127 NTSTATUS ret
= smb_register_vfs(SMB_VFS_INTERFACE_VERSION
, "fruit",
7129 if (!NT_STATUS_IS_OK(ret
)) {
7133 vfs_fruit_debug_level
= debug_add_class("fruit");
7134 if (vfs_fruit_debug_level
== -1) {
7135 vfs_fruit_debug_level
= DBGC_VFS
;
7136 DEBUG(0, ("%s: Couldn't register custom debugging class!\n",
7139 DEBUG(10, ("%s: Debug class number of '%s': %d\n",
7140 "vfs_fruit_init","fruit",vfs_fruit_debug_level
));