2 * OS X and Netatalk interoperability VFS module for Samba-3.x
4 * Copyright (C) Ralph Boehme, 2013, 2014
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
21 #include "MacExtensions.h"
22 #include "smbd/smbd.h"
23 #include "system/filesys.h"
24 #include "lib/util/time.h"
25 #include "system/shmem.h"
26 #include "locking/proto.h"
27 #include "smbd/globals.h"
29 #include "libcli/security/security.h"
30 #include "../libcli/smb/smb2_create_ctx.h"
31 #include "lib/util/tevent_ntstatus.h"
32 #include "lib/util/tevent_unix.h"
33 #include "offload_token.h"
34 #include "string_replace.h"
35 #include "hash_inode.h"
36 #include "lib/adouble.h"
37 #include "lib/util_macstreams.h"
40 * Enhanced OS X and Netatalk compatibility
41 * ========================================
43 * This modules takes advantage of vfs_streams_xattr and
44 * vfs_catia. VFS modules vfs_fruit and vfs_streams_xattr must be
45 * loaded in the correct order:
47 * vfs modules = catia fruit streams_xattr
49 * The module intercepts the OS X special streams "AFP_AfpInfo" and
50 * "AFP_Resource" and handles them in a special way. All other named
51 * streams are deferred to vfs_streams_xattr.
53 * The OS X client maps all NTFS illegal characters to the Unicode
54 * private range. This module optionally stores the characters using
55 * their native ASCII encoding using vfs_catia. If you're not enabling
56 * this feature, you can skip catia from vfs modules.
58 * Finally, open modes are optionally checked against Netatalk AFP
61 * The "AFP_AfpInfo" named stream is a binary blob containing OS X
62 * extended metadata for files and directories. This module optionally
63 * reads and stores this metadata in a way compatible with Netatalk 3
64 * which stores the metadata in an EA "org.netatalk.metadata". Cf
65 * source3/include/MacExtensions.h for a description of the binary
68 * The "AFP_Resource" named stream may be arbitrarily large, thus it
69 * can't be stored in an xattr on most filesystem. ZFS on Solaris is
70 * the only available filesystem where xattrs can be of any size and
71 * the OS supports using the file APIs for xattrs.
73 * The AFP_Resource stream is stored in an AppleDouble file prepending
74 * "._" to the filename. On Solaris with ZFS the stream is optionally
75 * stored in an EA "org.netatalk.resource".
81 * The OS X SMB client sends xattrs as ADS too. For xattr interop with
82 * other protocols you may want to adjust the xattr names the VFS
83 * module vfs_streams_xattr uses for storing ADS's. This defaults to
84 * user.DosStream.ADS_NAME:$DATA and can be changed by specifying
85 * these module parameters:
87 * streams_xattr:prefix = user.
88 * streams_xattr:store_stream_type = false
94 * - log diagnostic if any needed VFS module is not loaded
95 * (eg with lp_vfs_objects())
99 static int vfs_fruit_debug_level
= DBGC_VFS
;
101 static struct global_fruit_config
{
102 bool nego_aapl
; /* client negotiated AAPL */
104 } global_fruit_config
;
107 #define DBGC_CLASS vfs_fruit_debug_level
109 #define FRUIT_PARAM_TYPE_NAME "fruit"
111 enum apple_fork
{APPLE_FORK_DATA
, APPLE_FORK_RSRC
};
113 enum fruit_rsrc
{FRUIT_RSRC_STREAM
, FRUIT_RSRC_ADFILE
, FRUIT_RSRC_XATTR
};
114 enum fruit_meta
{FRUIT_META_STREAM
, FRUIT_META_NETATALK
};
115 enum fruit_locking
{FRUIT_LOCKING_NETATALK
, FRUIT_LOCKING_NONE
};
116 enum fruit_encoding
{FRUIT_ENC_NATIVE
, FRUIT_ENC_PRIVATE
};
118 struct fruit_config_data
{
119 enum fruit_rsrc rsrc
;
120 enum fruit_meta meta
;
121 enum fruit_locking locking
;
122 enum fruit_encoding encoding
;
123 bool use_aapl
; /* config from smb.conf */
125 bool readdir_attr_enabled
;
126 bool unix_info_enabled
;
127 bool copyfile_enabled
;
128 bool veto_appledouble
;
130 bool aapl_zero_file_id
;
133 off_t time_machine_max_size
;
134 bool wipe_intentionally_left_blank_rfork
;
135 bool delete_empty_adfiles
;
138 * Additional options, all enabled by default,
139 * possibly useful for analyzing performance. The associated
140 * operations with each of them may be expensive, so having
141 * the chance to disable them individually gives a chance
142 * tweaking the setup for the particular usecase.
144 bool readdir_attr_rsize
;
145 bool readdir_attr_finder_info
;
146 bool readdir_attr_max_access
;
149 static const struct enum_list fruit_rsrc
[] = {
150 {FRUIT_RSRC_STREAM
, "stream"}, /* pass on to vfs_streams_xattr */
151 {FRUIT_RSRC_ADFILE
, "file"}, /* ._ AppleDouble file */
152 {FRUIT_RSRC_XATTR
, "xattr"}, /* Netatalk compatible xattr (ZFS only) */
156 static const struct enum_list fruit_meta
[] = {
157 {FRUIT_META_STREAM
, "stream"}, /* pass on to vfs_streams_xattr */
158 {FRUIT_META_NETATALK
, "netatalk"}, /* Netatalk compatible xattr */
162 static const struct enum_list fruit_locking
[] = {
163 {FRUIT_LOCKING_NETATALK
, "netatalk"}, /* synchronize locks with Netatalk */
164 {FRUIT_LOCKING_NONE
, "none"},
168 static const struct enum_list fruit_encoding
[] = {
169 {FRUIT_ENC_NATIVE
, "native"}, /* map unicode private chars to ASCII */
170 {FRUIT_ENC_PRIVATE
, "private"}, /* keep unicode private chars */
175 /* tcon config handle */
176 struct fruit_config_data
*config
;
178 /* Denote stream type, meta or rsrc */
182 * AFP_AfpInfo stream created, but not written yet, thus still a fake
183 * pipe fd. This is set to true in fruit_open_meta if there was no
184 * existing stream but the caller requested O_CREAT. It is later set to
185 * false when we get a write on the stream that then does open and
193 /*****************************************************************************
195 *****************************************************************************/
198 * Initialize config struct from our smb.conf config parameters
200 static int init_fruit_config(vfs_handle_struct
*handle
)
202 struct fruit_config_data
*config
;
204 const char *tm_size_str
= NULL
;
206 config
= talloc_zero(handle
->conn
, struct fruit_config_data
);
208 DEBUG(1, ("talloc_zero() failed\n"));
214 * Versions up to Samba 4.5.x had a spelling bug in the
215 * fruit:resource option calling lp_parm_enum with
216 * "res*s*ource" (ie two s).
218 * In Samba 4.6 we accept both the wrong and the correct
219 * spelling, in Samba 4.7 the bad spelling will be removed.
221 enumval
= lp_parm_enum(SNUM(handle
->conn
), FRUIT_PARAM_TYPE_NAME
,
222 "ressource", fruit_rsrc
, FRUIT_RSRC_ADFILE
);
224 DEBUG(1, ("value for %s: resource type unknown\n",
225 FRUIT_PARAM_TYPE_NAME
));
228 config
->rsrc
= (enum fruit_rsrc
)enumval
;
230 enumval
= lp_parm_enum(SNUM(handle
->conn
), FRUIT_PARAM_TYPE_NAME
,
231 "resource", fruit_rsrc
, enumval
);
233 DEBUG(1, ("value for %s: resource type unknown\n",
234 FRUIT_PARAM_TYPE_NAME
));
237 config
->rsrc
= (enum fruit_rsrc
)enumval
;
239 enumval
= lp_parm_enum(SNUM(handle
->conn
), FRUIT_PARAM_TYPE_NAME
,
240 "metadata", fruit_meta
, FRUIT_META_NETATALK
);
242 DEBUG(1, ("value for %s: metadata type unknown\n",
243 FRUIT_PARAM_TYPE_NAME
));
246 config
->meta
= (enum fruit_meta
)enumval
;
248 enumval
= lp_parm_enum(SNUM(handle
->conn
), FRUIT_PARAM_TYPE_NAME
,
249 "locking", fruit_locking
, FRUIT_LOCKING_NONE
);
251 DEBUG(1, ("value for %s: locking type unknown\n",
252 FRUIT_PARAM_TYPE_NAME
));
255 config
->locking
= (enum fruit_locking
)enumval
;
257 enumval
= lp_parm_enum(SNUM(handle
->conn
), FRUIT_PARAM_TYPE_NAME
,
258 "encoding", fruit_encoding
, FRUIT_ENC_PRIVATE
);
260 DEBUG(1, ("value for %s: encoding type unknown\n",
261 FRUIT_PARAM_TYPE_NAME
));
264 config
->encoding
= (enum fruit_encoding
)enumval
;
266 if (config
->rsrc
== FRUIT_RSRC_ADFILE
) {
267 config
->veto_appledouble
= lp_parm_bool(SNUM(handle
->conn
),
268 FRUIT_PARAM_TYPE_NAME
,
273 config
->use_aapl
= lp_parm_bool(
274 -1, FRUIT_PARAM_TYPE_NAME
, "aapl", true);
276 config
->time_machine
= lp_parm_bool(
277 SNUM(handle
->conn
), FRUIT_PARAM_TYPE_NAME
, "time machine", false);
279 config
->unix_info_enabled
= lp_parm_bool(
280 -1, FRUIT_PARAM_TYPE_NAME
, "nfs_aces", true);
282 config
->use_copyfile
= lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME
,
285 config
->posix_rename
= lp_parm_bool(
286 SNUM(handle
->conn
), FRUIT_PARAM_TYPE_NAME
, "posix_rename", true);
288 config
->aapl_zero_file_id
=
289 lp_parm_bool(SNUM(handle
->conn
), FRUIT_PARAM_TYPE_NAME
,
290 "zero_file_id", false);
292 config
->readdir_attr_rsize
= lp_parm_bool(
293 SNUM(handle
->conn
), "readdir_attr", "aapl_rsize", true);
295 config
->readdir_attr_finder_info
= lp_parm_bool(
296 SNUM(handle
->conn
), "readdir_attr", "aapl_finder_info", true);
298 config
->readdir_attr_max_access
= lp_parm_bool(
299 SNUM(handle
->conn
), "readdir_attr", "aapl_max_access", true);
301 config
->model
= lp_parm_const_string(
302 -1, FRUIT_PARAM_TYPE_NAME
, "model", "MacSamba");
304 tm_size_str
= lp_parm_const_string(
305 SNUM(handle
->conn
), FRUIT_PARAM_TYPE_NAME
,
306 "time machine max size", NULL
);
307 if (tm_size_str
!= NULL
) {
308 config
->time_machine_max_size
= conv_str_size(tm_size_str
);
311 config
->wipe_intentionally_left_blank_rfork
= lp_parm_bool(
312 SNUM(handle
->conn
), FRUIT_PARAM_TYPE_NAME
,
313 "wipe_intentionally_left_blank_rfork", false);
315 config
->delete_empty_adfiles
= lp_parm_bool(
316 SNUM(handle
->conn
), FRUIT_PARAM_TYPE_NAME
,
317 "delete_empty_adfiles", false);
319 SMB_VFS_HANDLE_SET_DATA(handle
, config
,
320 NULL
, struct fruit_config_data
,
326 static bool add_fruit_stream(TALLOC_CTX
*mem_ctx
, unsigned int *num_streams
,
327 struct stream_struct
**streams
,
328 const char *name
, off_t size
,
331 struct stream_struct
*tmp
;
333 tmp
= talloc_realloc(mem_ctx
, *streams
, struct stream_struct
,
339 tmp
[*num_streams
].name
= talloc_asprintf(tmp
, "%s:$DATA", name
);
340 if (tmp
[*num_streams
].name
== NULL
) {
344 tmp
[*num_streams
].size
= size
;
345 tmp
[*num_streams
].alloc_size
= alloc_size
;
352 static bool filter_empty_rsrc_stream(unsigned int *num_streams
,
353 struct stream_struct
**streams
)
355 struct stream_struct
*tmp
= *streams
;
358 if (*num_streams
== 0) {
362 for (i
= 0; i
< *num_streams
; i
++) {
363 if (strequal_m(tmp
[i
].name
, AFPRESOURCE_STREAM
)) {
368 if (i
== *num_streams
) {
372 if (tmp
[i
].size
> 0) {
376 TALLOC_FREE(tmp
[i
].name
);
377 ARRAY_DEL_ELEMENT(tmp
, i
, *num_streams
);
382 static bool del_fruit_stream(TALLOC_CTX
*mem_ctx
, unsigned int *num_streams
,
383 struct stream_struct
**streams
,
386 struct stream_struct
*tmp
= *streams
;
389 if (*num_streams
== 0) {
393 for (i
= 0; i
< *num_streams
; i
++) {
394 if (strequal_m(tmp
[i
].name
, name
)) {
399 if (i
== *num_streams
) {
403 TALLOC_FREE(tmp
[i
].name
);
404 ARRAY_DEL_ELEMENT(tmp
, i
, *num_streams
);
409 static bool ad_empty_finderinfo(const struct adouble
*ad
)
412 char emptybuf
[ADEDLEN_FINDERI
] = {0};
415 fi
= ad_get_entry(ad
, ADEID_FINDERI
);
417 DBG_ERR("Missing FinderInfo in struct adouble [%p]\n", ad
);
421 cmp
= memcmp(emptybuf
, fi
, ADEDLEN_FINDERI
);
425 static bool ai_empty_finderinfo(const AfpInfo
*ai
)
428 char emptybuf
[ADEDLEN_FINDERI
] = {0};
430 cmp
= memcmp(emptybuf
, &ai
->afpi_FinderInfo
[0], ADEDLEN_FINDERI
);
435 * Update btime with btime from Netatalk
437 static void update_btime(vfs_handle_struct
*handle
,
438 struct smb_filename
*smb_fname
)
441 struct timespec creation_time
= {0};
443 struct fruit_config_data
*config
= NULL
;
445 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct fruit_config_data
,
448 switch (config
->meta
) {
449 case FRUIT_META_STREAM
:
451 case FRUIT_META_NETATALK
:
455 DBG_ERR("Unexpected meta config [%d]\n", config
->meta
);
459 ad
= ad_get(talloc_tos(), handle
, smb_fname
, ADOUBLE_META
);
463 if (ad_getdate(ad
, AD_DATE_UNIX
| AD_DATE_CREATE
, &t
) != 0) {
469 creation_time
.tv_sec
= convert_uint32_t_to_time_t(t
);
470 update_stat_ex_create_time(&smb_fname
->st
, creation_time
);
476 * Map an access mask to a Netatalk single byte byte range lock
478 static off_t
access_to_netatalk_brl(enum apple_fork fork_type
,
479 uint32_t access_mask
)
483 switch (access_mask
) {
485 offset
= AD_FILELOCK_OPEN_RD
;
488 case FILE_WRITE_DATA
:
489 case FILE_APPEND_DATA
:
490 offset
= AD_FILELOCK_OPEN_WR
;
494 offset
= AD_FILELOCK_OPEN_NONE
;
498 if (fork_type
== APPLE_FORK_RSRC
) {
499 if (offset
== AD_FILELOCK_OPEN_NONE
) {
500 offset
= AD_FILELOCK_RSRC_OPEN_NONE
;
510 * Map a deny mode to a Netatalk brl
512 static off_t
denymode_to_netatalk_brl(enum apple_fork fork_type
,
519 offset
= AD_FILELOCK_DENY_RD
;
523 offset
= AD_FILELOCK_DENY_WR
;
527 smb_panic("denymode_to_netatalk_brl: bad deny mode\n");
530 if (fork_type
== APPLE_FORK_RSRC
) {
538 * Call fcntl() with an exclusive F_GETLK request in order to
539 * determine if there's an existing shared lock
541 * @return true if the requested lock was found or any error occurred
542 * false if the lock was not found
544 static bool test_netatalk_lock(files_struct
*fsp
, off_t in_offset
)
547 off_t offset
= in_offset
;
552 result
= SMB_VFS_GETLOCK(fsp
, &offset
, &len
, &type
, &pid
);
553 if (result
== false) {
557 if (type
!= F_UNLCK
) {
564 static NTSTATUS
fruit_check_access(vfs_handle_struct
*handle
,
566 uint32_t access_mask
,
569 NTSTATUS status
= NT_STATUS_OK
;
571 bool share_for_read
= (share_mode
& FILE_SHARE_READ
);
572 bool share_for_write
= (share_mode
& FILE_SHARE_WRITE
);
573 bool netatalk_already_open_for_reading
= false;
574 bool netatalk_already_open_for_writing
= false;
575 bool netatalk_already_open_with_deny_read
= false;
576 bool netatalk_already_open_with_deny_write
= false;
577 struct GUID req_guid
= GUID_random();
579 /* FIXME: hardcoded data fork, add resource fork */
580 enum apple_fork fork_type
= APPLE_FORK_DATA
;
582 DBG_DEBUG("fruit_check_access: %s, am: %s/%s, sm: 0x%x\n",
584 access_mask
& FILE_READ_DATA
? "READ" :"-",
585 access_mask
& FILE_WRITE_DATA
? "WRITE" : "-",
588 if (fsp_get_io_fd(fsp
) == -1) {
592 /* Read NetATalk opens and deny modes on the file. */
593 netatalk_already_open_for_reading
= test_netatalk_lock(fsp
,
594 access_to_netatalk_brl(fork_type
,
597 netatalk_already_open_with_deny_read
= test_netatalk_lock(fsp
,
598 denymode_to_netatalk_brl(fork_type
,
601 netatalk_already_open_for_writing
= test_netatalk_lock(fsp
,
602 access_to_netatalk_brl(fork_type
,
605 netatalk_already_open_with_deny_write
= test_netatalk_lock(fsp
,
606 denymode_to_netatalk_brl(fork_type
,
609 /* If there are any conflicts - sharing violation. */
610 if ((access_mask
& FILE_READ_DATA
) &&
611 netatalk_already_open_with_deny_read
) {
612 return NT_STATUS_SHARING_VIOLATION
;
615 if (!share_for_read
&&
616 netatalk_already_open_for_reading
) {
617 return NT_STATUS_SHARING_VIOLATION
;
620 if ((access_mask
& FILE_WRITE_DATA
) &&
621 netatalk_already_open_with_deny_write
) {
622 return NT_STATUS_SHARING_VIOLATION
;
625 if (!share_for_write
&&
626 netatalk_already_open_for_writing
) {
627 return NT_STATUS_SHARING_VIOLATION
;
630 if (!(access_mask
& FILE_READ_DATA
)) {
632 * Nothing we can do here, we need read access
638 /* Set NetAtalk locks matching our access */
639 if (access_mask
& FILE_READ_DATA
) {
640 off
= access_to_netatalk_brl(fork_type
, FILE_READ_DATA
);
641 req_guid
.time_hi_and_version
= __LINE__
;
646 fsp
->op
->global
->open_persistent_id
,
654 if (!NT_STATUS_IS_OK(status
)) {
659 if (!share_for_read
) {
660 off
= denymode_to_netatalk_brl(fork_type
, DENY_READ
);
661 req_guid
.time_hi_and_version
= __LINE__
;
666 fsp
->op
->global
->open_persistent_id
,
674 if (!NT_STATUS_IS_OK(status
)) {
679 if (access_mask
& FILE_WRITE_DATA
) {
680 off
= access_to_netatalk_brl(fork_type
, FILE_WRITE_DATA
);
681 req_guid
.time_hi_and_version
= __LINE__
;
686 fsp
->op
->global
->open_persistent_id
,
694 if (!NT_STATUS_IS_OK(status
)) {
699 if (!share_for_write
) {
700 off
= denymode_to_netatalk_brl(fork_type
, DENY_WRITE
);
701 req_guid
.time_hi_and_version
= __LINE__
;
706 fsp
->op
->global
->open_persistent_id
,
714 if (!NT_STATUS_IS_OK(status
)) {
722 static NTSTATUS
check_aapl(vfs_handle_struct
*handle
,
723 struct smb_request
*req
,
724 const struct smb2_create_blobs
*in_context_blobs
,
725 struct smb2_create_blobs
*out_context_blobs
)
727 struct fruit_config_data
*config
;
729 struct smb2_create_blob
*aapl
= NULL
;
733 DATA_BLOB blob
= data_blob_talloc(req
, NULL
, 0);
734 uint64_t req_bitmap
, client_caps
;
735 uint64_t server_caps
= SMB2_CRTCTX_AAPL_UNIX_BASED
;
739 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct fruit_config_data
,
740 return NT_STATUS_UNSUCCESSFUL
);
742 if (!config
->use_aapl
743 || in_context_blobs
== NULL
744 || out_context_blobs
== NULL
) {
748 aapl
= smb2_create_blob_find(in_context_blobs
,
749 SMB2_CREATE_TAG_AAPL
);
754 if (aapl
->data
.length
!= 24) {
755 DEBUG(1, ("unexpected AAPL ctxt length: %ju\n",
756 (uintmax_t)aapl
->data
.length
));
757 return NT_STATUS_INVALID_PARAMETER
;
760 cmd
= IVAL(aapl
->data
.data
, 0);
761 if (cmd
!= SMB2_CRTCTX_AAPL_SERVER_QUERY
) {
762 DEBUG(1, ("unsupported AAPL cmd: %d\n", cmd
));
763 return NT_STATUS_INVALID_PARAMETER
;
766 req_bitmap
= BVAL(aapl
->data
.data
, 8);
767 client_caps
= BVAL(aapl
->data
.data
, 16);
769 SIVAL(p
, 0, SMB2_CRTCTX_AAPL_SERVER_QUERY
);
771 SBVAL(p
, 8, req_bitmap
);
772 ok
= data_blob_append(req
, &blob
, p
, 16);
774 return NT_STATUS_UNSUCCESSFUL
;
777 if (req_bitmap
& SMB2_CRTCTX_AAPL_SERVER_CAPS
) {
778 if ((client_caps
& SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR
) &&
779 (handle
->conn
->tcon
->compat
->fs_capabilities
& FILE_NAMED_STREAMS
)) {
780 server_caps
|= SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR
;
781 config
->readdir_attr_enabled
= true;
784 if (config
->use_copyfile
) {
785 server_caps
|= SMB2_CRTCTX_AAPL_SUPPORTS_OSX_COPYFILE
;
786 config
->copyfile_enabled
= true;
790 * The client doesn't set the flag, so we can't check
791 * for it and just set it unconditionally
793 if (config
->unix_info_enabled
) {
794 server_caps
|= SMB2_CRTCTX_AAPL_SUPPORTS_NFS_ACE
;
797 SBVAL(p
, 0, server_caps
);
798 ok
= data_blob_append(req
, &blob
, p
, 8);
800 return NT_STATUS_UNSUCCESSFUL
;
804 if (req_bitmap
& SMB2_CRTCTX_AAPL_VOLUME_CAPS
) {
805 int val
= lp_case_sensitive(SNUM(handle
->conn
->tcon
->compat
));
813 caps
|= SMB2_CRTCTX_AAPL_CASE_SENSITIVE
;
820 if (config
->time_machine
) {
821 caps
|= SMB2_CRTCTX_AAPL_FULL_SYNC
;
826 ok
= data_blob_append(req
, &blob
, p
, 8);
828 return NT_STATUS_UNSUCCESSFUL
;
832 if (req_bitmap
& SMB2_CRTCTX_AAPL_MODEL_INFO
) {
833 ok
= convert_string_talloc(req
,
835 config
->model
, strlen(config
->model
),
838 return NT_STATUS_UNSUCCESSFUL
;
842 SIVAL(p
+ 4, 0, modellen
);
843 ok
= data_blob_append(req
, &blob
, p
, 8);
846 return NT_STATUS_UNSUCCESSFUL
;
849 ok
= data_blob_append(req
, &blob
, model
, modellen
);
852 return NT_STATUS_UNSUCCESSFUL
;
856 status
= smb2_create_blob_add(out_context_blobs
,
858 SMB2_CREATE_TAG_AAPL
,
860 if (NT_STATUS_IS_OK(status
)) {
861 global_fruit_config
.nego_aapl
= true;
867 static bool readdir_attr_meta_finderi_stream(
868 struct vfs_handle_struct
*handle
,
869 const struct smb_filename
*smb_fname
,
872 struct smb_filename
*stream_name
= NULL
;
873 files_struct
*fsp
= NULL
;
878 uint8_t buf
[AFP_INFO_SIZE
];
880 stream_name
= synthetic_smb_fname(talloc_tos(),
881 smb_fname
->base_name
,
886 if (stream_name
== NULL
) {
890 ret
= SMB_VFS_STAT(handle
->conn
, stream_name
);
895 status
= openat_pathref_fsp(handle
->conn
->cwd_fsp
, stream_name
);
896 if (!NT_STATUS_IS_OK(status
)) {
900 status
= SMB_VFS_CREATE_FILE(
901 handle
->conn
, /* conn */
903 stream_name
, /* fname */
904 FILE_READ_DATA
, /* access_mask */
905 (FILE_SHARE_READ
| FILE_SHARE_WRITE
| /* share_access */
907 FILE_OPEN
, /* create_disposition*/
908 0, /* create_options */
909 0, /* file_attributes */
910 INTERNAL_OPEN_ONLY
, /* oplock_request */
912 0, /* allocation_size */
913 0, /* private_flags */
918 NULL
, NULL
); /* create context */
920 TALLOC_FREE(stream_name
);
922 if (!NT_STATUS_IS_OK(status
)) {
926 nread
= SMB_VFS_PREAD(fsp
, &buf
[0], AFP_INFO_SIZE
, 0);
927 if (nread
!= AFP_INFO_SIZE
) {
928 DBG_ERR("short read [%s] [%zd/%d]\n",
929 smb_fname_str_dbg(stream_name
), nread
, AFP_INFO_SIZE
);
934 memcpy(&ai
->afpi_FinderInfo
[0], &buf
[AFP_OFF_FinderInfo
],
941 close_file(NULL
, fsp
, NORMAL_CLOSE
);
947 static bool readdir_attr_meta_finderi_netatalk(
948 struct vfs_handle_struct
*handle
,
949 const struct smb_filename
*smb_fname
,
952 struct adouble
*ad
= NULL
;
955 ad
= ad_get(talloc_tos(), handle
, smb_fname
, ADOUBLE_META
);
960 p
= ad_get_entry(ad
, ADEID_FINDERI
);
962 DBG_ERR("No ADEID_FINDERI for [%s]\n", smb_fname
->base_name
);
967 memcpy(&ai
->afpi_FinderInfo
[0], p
, AFP_FinderSize
);
972 static bool readdir_attr_meta_finderi(struct vfs_handle_struct
*handle
,
973 const struct smb_filename
*smb_fname
,
974 struct readdir_attr_data
*attr_data
)
976 struct fruit_config_data
*config
= NULL
;
981 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
982 struct fruit_config_data
,
985 switch (config
->meta
) {
986 case FRUIT_META_NETATALK
:
987 ok
= readdir_attr_meta_finderi_netatalk(
988 handle
, smb_fname
, &ai
);
991 case FRUIT_META_STREAM
:
992 ok
= readdir_attr_meta_finderi_stream(
993 handle
, smb_fname
, &ai
);
997 DBG_ERR("Unexpected meta config [%d]\n", config
->meta
);
1002 /* Don't bother with errors, it's likely ENOENT */
1006 if (S_ISREG(smb_fname
->st
.st_ex_mode
)) {
1008 memcpy(&attr_data
->attr_data
.aapl
.finder_info
[0],
1009 &ai
.afpi_FinderInfo
[0], 4);
1011 /* finder_creator */
1012 memcpy(&attr_data
->attr_data
.aapl
.finder_info
[0] + 4,
1013 &ai
.afpi_FinderInfo
[4], 4);
1017 memcpy(&attr_data
->attr_data
.aapl
.finder_info
[0] + 8,
1018 &ai
.afpi_FinderInfo
[8], 2);
1020 /* finder_ext_flags */
1021 memcpy(&attr_data
->attr_data
.aapl
.finder_info
[0] + 10,
1022 &ai
.afpi_FinderInfo
[24], 2);
1025 date_added
= convert_time_t_to_uint32_t(
1026 smb_fname
->st
.st_ex_btime
.tv_sec
- AD_DATE_DELTA
);
1028 RSIVAL(&attr_data
->attr_data
.aapl
.finder_info
[0], 12, date_added
);
1033 static uint64_t readdir_attr_rfork_size_adouble(
1034 struct vfs_handle_struct
*handle
,
1035 const struct smb_filename
*smb_fname
)
1037 struct adouble
*ad
= NULL
;
1038 uint64_t rfork_size
;
1040 ad
= ad_get(talloc_tos(), handle
, smb_fname
,
1046 rfork_size
= ad_getentrylen(ad
, ADEID_RFORK
);
1052 static uint64_t readdir_attr_rfork_size_stream(
1053 struct vfs_handle_struct
*handle
,
1054 const struct smb_filename
*smb_fname
)
1056 struct smb_filename
*stream_name
= NULL
;
1058 uint64_t rfork_size
;
1060 stream_name
= synthetic_smb_fname(talloc_tos(),
1061 smb_fname
->base_name
,
1062 AFPRESOURCE_STREAM_NAME
,
1066 if (stream_name
== NULL
) {
1070 ret
= SMB_VFS_STAT(handle
->conn
, stream_name
);
1072 TALLOC_FREE(stream_name
);
1076 rfork_size
= stream_name
->st
.st_ex_size
;
1077 TALLOC_FREE(stream_name
);
1082 static uint64_t readdir_attr_rfork_size(struct vfs_handle_struct
*handle
,
1083 const struct smb_filename
*smb_fname
)
1085 struct fruit_config_data
*config
= NULL
;
1086 uint64_t rfork_size
;
1088 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
1089 struct fruit_config_data
,
1092 switch (config
->rsrc
) {
1093 case FRUIT_RSRC_ADFILE
:
1094 rfork_size
= readdir_attr_rfork_size_adouble(handle
,
1098 case FRUIT_RSRC_XATTR
:
1099 case FRUIT_RSRC_STREAM
:
1100 rfork_size
= readdir_attr_rfork_size_stream(handle
,
1105 DBG_ERR("Unexpected rsrc config [%d]\n", config
->rsrc
);
1113 static NTSTATUS
readdir_attr_macmeta(struct vfs_handle_struct
*handle
,
1114 const struct smb_filename
*smb_fname
,
1115 struct readdir_attr_data
*attr_data
)
1117 NTSTATUS status
= NT_STATUS_OK
;
1118 struct fruit_config_data
*config
= NULL
;
1121 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
1122 struct fruit_config_data
,
1123 return NT_STATUS_UNSUCCESSFUL
);
1126 /* Ensure we return a default value in the creation_date field */
1127 RSIVAL(&attr_data
->attr_data
.aapl
.finder_info
, 12, AD_DATE_START
);
1130 * Resource fork length
1133 if (config
->readdir_attr_rsize
) {
1134 uint64_t rfork_size
;
1136 rfork_size
= readdir_attr_rfork_size(handle
, smb_fname
);
1137 attr_data
->attr_data
.aapl
.rfork_size
= rfork_size
;
1144 if (config
->readdir_attr_finder_info
) {
1145 ok
= readdir_attr_meta_finderi(handle
, smb_fname
, attr_data
);
1147 status
= NT_STATUS_INTERNAL_ERROR
;
1154 static NTSTATUS
remove_virtual_nfs_aces(struct security_descriptor
*psd
)
1159 if (psd
->dacl
== NULL
) {
1160 return NT_STATUS_OK
;
1163 for (i
= 0; i
< psd
->dacl
->num_aces
; i
++) {
1164 /* MS NFS style mode/uid/gid */
1165 int cmp
= dom_sid_compare_domain(
1166 &global_sid_Unix_NFS
,
1167 &psd
->dacl
->aces
[i
].trustee
);
1169 /* Normal ACE entry. */
1174 * security_descriptor_dacl_del()
1175 * *must* return NT_STATUS_OK as we know
1176 * we have something to remove.
1179 status
= security_descriptor_dacl_del(psd
,
1180 &psd
->dacl
->aces
[i
].trustee
);
1181 if (!NT_STATUS_IS_OK(status
)) {
1182 DBG_WARNING("failed to remove MS NFS style ACE: %s\n",
1188 * security_descriptor_dacl_del() may delete more
1189 * then one entry subsequent to this one if the
1190 * SID matches, but we only need to ensure that
1191 * we stay looking at the same element in the array.
1195 return NT_STATUS_OK
;
1198 /* Search MS NFS style ACE with UNIX mode */
1199 static NTSTATUS
check_ms_nfs(vfs_handle_struct
*handle
,
1201 struct security_descriptor
*psd
,
1206 struct fruit_config_data
*config
= NULL
;
1210 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
1211 struct fruit_config_data
,
1212 return NT_STATUS_UNSUCCESSFUL
);
1214 if (!global_fruit_config
.nego_aapl
) {
1215 return NT_STATUS_OK
;
1217 if (psd
->dacl
== NULL
|| !config
->unix_info_enabled
) {
1218 return NT_STATUS_OK
;
1221 for (i
= 0; i
< psd
->dacl
->num_aces
; i
++) {
1222 if (dom_sid_compare_domain(
1223 &global_sid_Unix_NFS_Mode
,
1224 &psd
->dacl
->aces
[i
].trustee
) == 0) {
1225 *pmode
= (mode_t
)psd
->dacl
->aces
[i
].trustee
.sub_auths
[2];
1226 *pmode
&= (S_IRWXU
| S_IRWXG
| S_IRWXO
);
1229 DEBUG(10, ("MS NFS chmod request %s, %04o\n",
1230 fsp_str_dbg(fsp
), (unsigned)(*pmode
)));
1236 * Remove any incoming virtual ACE entries generated by
1237 * fruit_fget_nt_acl().
1240 return remove_virtual_nfs_aces(psd
);
1243 /****************************************************************************
1245 ****************************************************************************/
1247 static int fruit_connect(vfs_handle_struct
*handle
,
1248 const char *service
,
1252 char *list
= NULL
, *newlist
= NULL
;
1253 struct fruit_config_data
*config
;
1254 const struct loadparm_substitution
*lp_sub
=
1255 loadparm_s3_global_substitution();
1257 DEBUG(10, ("fruit_connect\n"));
1259 rc
= SMB_VFS_NEXT_CONNECT(handle
, service
, user
);
1264 rc
= init_fruit_config(handle
);
1269 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
1270 struct fruit_config_data
, return -1);
1272 if (config
->veto_appledouble
) {
1273 list
= lp_veto_files(talloc_tos(), lp_sub
, SNUM(handle
->conn
));
1276 if (strstr(list
, "/" ADOUBLE_NAME_PREFIX
"*/") == NULL
) {
1277 newlist
= talloc_asprintf(
1279 "%s/" ADOUBLE_NAME_PREFIX
"*/",
1281 lp_do_parameter(SNUM(handle
->conn
),
1286 lp_do_parameter(SNUM(handle
->conn
),
1288 "/" ADOUBLE_NAME_PREFIX
"*/");
1294 if (config
->encoding
== FRUIT_ENC_NATIVE
) {
1295 lp_do_parameter(SNUM(handle
->conn
),
1297 macos_string_replace_map
);
1300 if (config
->time_machine
) {
1301 DBG_NOTICE("Enabling durable handles for Time Machine "
1302 "support on [%s]\n", service
);
1303 lp_do_parameter(SNUM(handle
->conn
), "durable handles", "yes");
1304 lp_do_parameter(SNUM(handle
->conn
), "kernel oplocks", "no");
1305 lp_do_parameter(SNUM(handle
->conn
), "kernel share modes", "no");
1306 if (!lp_strict_sync(SNUM(handle
->conn
))) {
1307 DBG_WARNING("Time Machine without strict sync is not "
1310 lp_do_parameter(SNUM(handle
->conn
), "posix locking", "no");
1316 static int fruit_open_meta_stream(vfs_handle_struct
*handle
,
1317 const struct files_struct
*dirfsp
,
1318 const struct smb_filename
*smb_fname
,
1323 struct fruit_config_data
*config
= NULL
;
1324 struct fio
*fio
= NULL
;
1325 int open_flags
= flags
& ~O_CREAT
;
1328 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname
));
1330 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
1331 struct fruit_config_data
, return -1);
1333 fio
= VFS_ADD_FSP_EXTENSION(handle
, fsp
, struct fio
, NULL
);
1334 fio
->type
= ADOUBLE_META
;
1335 fio
->config
= config
;
1337 fd
= SMB_VFS_NEXT_OPENAT(handle
,
1347 if (!(flags
& O_CREAT
)) {
1348 VFS_REMOVE_FSP_EXTENSION(handle
, fsp
);
1354 VFS_REMOVE_FSP_EXTENSION(handle
, fsp
);
1358 fio
->fake_fd
= true;
1365 static int fruit_open_meta_netatalk(vfs_handle_struct
*handle
,
1366 const struct files_struct
*dirfsp
,
1367 const struct smb_filename
*smb_fname
,
1372 struct fruit_config_data
*config
= NULL
;
1373 struct fio
*fio
= NULL
;
1374 struct adouble
*ad
= NULL
;
1375 bool meta_exists
= false;
1378 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname
));
1380 ad
= ad_get(talloc_tos(), handle
, smb_fname
, ADOUBLE_META
);
1387 if (!meta_exists
&& !(flags
& O_CREAT
)) {
1397 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
1398 struct fruit_config_data
, return -1);
1400 fio
= VFS_ADD_FSP_EXTENSION(handle
, fsp
, struct fio
, NULL
);
1401 fio
->type
= ADOUBLE_META
;
1402 fio
->config
= config
;
1403 fio
->fake_fd
= true;
1410 static int fruit_open_meta(vfs_handle_struct
*handle
,
1411 const struct files_struct
*dirfsp
,
1412 const struct smb_filename
*smb_fname
,
1413 files_struct
*fsp
, int flags
, mode_t mode
)
1416 struct fruit_config_data
*config
= NULL
;
1418 DBG_DEBUG("path [%s]\n", smb_fname_str_dbg(smb_fname
));
1420 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
1421 struct fruit_config_data
, return -1);
1423 switch (config
->meta
) {
1424 case FRUIT_META_STREAM
:
1425 fd
= fruit_open_meta_stream(handle
, dirfsp
, smb_fname
,
1429 case FRUIT_META_NETATALK
:
1430 fd
= fruit_open_meta_netatalk(handle
, dirfsp
, smb_fname
,
1435 DBG_ERR("Unexpected meta config [%d]\n", config
->meta
);
1439 DBG_DEBUG("path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname
), fd
);
1444 static int fruit_open_rsrc_adouble(vfs_handle_struct
*handle
,
1445 const struct files_struct
*dirfsp
,
1446 const struct smb_filename
*smb_fname
,
1452 struct adouble
*ad
= NULL
;
1453 struct smb_filename
*smb_fname_base
= NULL
;
1454 struct fruit_config_data
*config
= NULL
;
1457 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
1458 struct fruit_config_data
, return -1);
1460 if ((!(flags
& O_CREAT
)) &&
1461 S_ISDIR(fsp
->base_fsp
->fsp_name
->st
.st_ex_mode
))
1463 /* sorry, but directories don't habe a resource fork */
1468 rc
= adouble_path(talloc_tos(), smb_fname
, &smb_fname_base
);
1473 /* We always need read/write access for the metadata header too */
1474 flags
&= ~(O_RDONLY
| O_WRONLY
);
1477 hostfd
= SMB_VFS_NEXT_OPENAT(handle
,
1488 if (flags
& (O_CREAT
| O_TRUNC
)) {
1489 ad
= ad_init(fsp
, ADOUBLE_RSRC
);
1495 fsp_set_fd(fsp
, hostfd
);
1497 rc
= ad_fset(handle
, ad
, fsp
);
1498 fsp_set_fd(fsp
, -1);
1508 TALLOC_FREE(smb_fname_base
);
1510 DEBUG(10, ("fruit_open resource fork: rc=%d, fd=%d\n", rc
, hostfd
));
1512 int saved_errno
= errno
;
1515 * BUGBUGBUG -- we would need to call
1516 * fd_close_posix here, but we don't have a
1519 fsp_set_fd(fsp
, hostfd
);
1523 errno
= saved_errno
;
1528 static int fruit_open_rsrc_xattr(vfs_handle_struct
*handle
,
1529 const struct files_struct
*dirfsp
,
1530 const struct smb_filename
*smb_fname
,
1535 #ifdef HAVE_ATTROPEN
1539 * As there's no attropenat() this is only going to work with AT_FDCWD.
1541 SMB_ASSERT(fsp_get_pathref_fd(dirfsp
) == AT_FDCWD
);
1543 fd
= attropen(smb_fname
->base_name
,
1544 AFPRESOURCE_EA_NETATALK
,
1559 static int fruit_open_rsrc(vfs_handle_struct
*handle
,
1560 const struct files_struct
*dirfsp
,
1561 const struct smb_filename
*smb_fname
,
1562 files_struct
*fsp
, int flags
, mode_t mode
)
1565 struct fruit_config_data
*config
= NULL
;
1566 struct fio
*fio
= NULL
;
1568 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname
));
1570 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
1571 struct fruit_config_data
, return -1);
1573 switch (config
->rsrc
) {
1574 case FRUIT_RSRC_STREAM
:
1575 fd
= SMB_VFS_NEXT_OPENAT(handle
,
1583 case FRUIT_RSRC_ADFILE
:
1584 fd
= fruit_open_rsrc_adouble(handle
, dirfsp
, smb_fname
,
1588 case FRUIT_RSRC_XATTR
:
1589 fd
= fruit_open_rsrc_xattr(handle
, dirfsp
, smb_fname
,
1594 DBG_ERR("Unexpected rsrc config [%d]\n", config
->rsrc
);
1598 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname
), fd
);
1604 fio
= VFS_ADD_FSP_EXTENSION(handle
, fsp
, struct fio
, NULL
);
1605 fio
->type
= ADOUBLE_RSRC
;
1606 fio
->config
= config
;
1611 static int fruit_openat(vfs_handle_struct
*handle
,
1612 const struct files_struct
*dirfsp
,
1613 const struct smb_filename
*smb_fname
,
1620 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname
));
1622 if (!is_named_stream(smb_fname
)) {
1623 return SMB_VFS_NEXT_OPENAT(handle
,
1631 if (is_afpinfo_stream(smb_fname
->stream_name
)) {
1632 fd
= fruit_open_meta(handle
,
1638 } else if (is_afpresource_stream(smb_fname
->stream_name
)) {
1639 fd
= fruit_open_rsrc(handle
,
1646 fd
= SMB_VFS_NEXT_OPENAT(handle
,
1654 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname
), fd
);
1656 /* Prevent reopen optimisation */
1657 fsp
->fsp_flags
.have_proc_fds
= false;
1661 static int fruit_close_meta(vfs_handle_struct
*handle
,
1665 struct fruit_config_data
*config
= NULL
;
1667 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
1668 struct fruit_config_data
, return -1);
1670 switch (config
->meta
) {
1671 case FRUIT_META_STREAM
:
1672 ret
= SMB_VFS_NEXT_CLOSE(handle
, fsp
);
1675 case FRUIT_META_NETATALK
:
1676 ret
= vfs_fake_fd_close(fsp_get_pathref_fd(fsp
));
1677 fsp_set_fd(fsp
, -1);
1681 DBG_ERR("Unexpected meta config [%d]\n", config
->meta
);
1689 static int fruit_close_rsrc(vfs_handle_struct
*handle
,
1693 struct fruit_config_data
*config
= NULL
;
1695 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
1696 struct fruit_config_data
, return -1);
1698 switch (config
->rsrc
) {
1699 case FRUIT_RSRC_STREAM
:
1700 case FRUIT_RSRC_ADFILE
:
1701 ret
= SMB_VFS_NEXT_CLOSE(handle
, fsp
);
1704 case FRUIT_RSRC_XATTR
:
1705 ret
= vfs_fake_fd_close(fsp_get_pathref_fd(fsp
));
1706 fsp_set_fd(fsp
, -1);
1710 DBG_ERR("Unexpected rsrc config [%d]\n", config
->rsrc
);
1717 static int fruit_close(vfs_handle_struct
*handle
,
1723 fd
= fsp_get_pathref_fd(fsp
);
1725 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(fsp
->fsp_name
), fd
);
1727 if (!is_named_stream(fsp
->fsp_name
)) {
1728 return SMB_VFS_NEXT_CLOSE(handle
, fsp
);
1731 if (is_afpinfo_stream(fsp
->fsp_name
->stream_name
)) {
1732 ret
= fruit_close_meta(handle
, fsp
);
1733 } else if (is_afpresource_stream(fsp
->fsp_name
->stream_name
)) {
1734 ret
= fruit_close_rsrc(handle
, fsp
);
1736 ret
= SMB_VFS_NEXT_CLOSE(handle
, fsp
);
1742 static int fruit_renameat(struct vfs_handle_struct
*handle
,
1743 files_struct
*srcfsp
,
1744 const struct smb_filename
*smb_fname_src
,
1745 files_struct
*dstfsp
,
1746 const struct smb_filename
*smb_fname_dst
)
1749 struct fruit_config_data
*config
= NULL
;
1750 struct smb_filename
*src_adp_smb_fname
= NULL
;
1751 struct smb_filename
*dst_adp_smb_fname
= NULL
;
1753 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
1754 struct fruit_config_data
, return -1);
1756 if (!VALID_STAT(smb_fname_src
->st
)) {
1757 DBG_ERR("Need valid stat for [%s]\n",
1758 smb_fname_str_dbg(smb_fname_src
));
1762 rc
= SMB_VFS_NEXT_RENAMEAT(handle
,
1771 if ((config
->rsrc
!= FRUIT_RSRC_ADFILE
) ||
1772 (!S_ISREG(smb_fname_src
->st
.st_ex_mode
)))
1777 rc
= adouble_path(talloc_tos(), smb_fname_src
, &src_adp_smb_fname
);
1782 rc
= adouble_path(talloc_tos(), smb_fname_dst
, &dst_adp_smb_fname
);
1787 DBG_DEBUG("%s -> %s\n",
1788 smb_fname_str_dbg(src_adp_smb_fname
),
1789 smb_fname_str_dbg(dst_adp_smb_fname
));
1791 rc
= SMB_VFS_NEXT_RENAMEAT(handle
,
1796 if (errno
== ENOENT
) {
1801 TALLOC_FREE(src_adp_smb_fname
);
1802 TALLOC_FREE(dst_adp_smb_fname
);
1806 static int fruit_unlink_meta_stream(vfs_handle_struct
*handle
,
1807 struct files_struct
*dirfsp
,
1808 const struct smb_filename
*smb_fname
)
1810 return SMB_VFS_NEXT_UNLINKAT(handle
,
1816 static int fruit_unlink_meta_netatalk(vfs_handle_struct
*handle
,
1817 const struct smb_filename
*smb_fname
)
1819 return SMB_VFS_REMOVEXATTR(handle
->conn
,
1821 AFPINFO_EA_NETATALK
);
1824 static int fruit_unlink_meta(vfs_handle_struct
*handle
,
1825 struct files_struct
*dirfsp
,
1826 const struct smb_filename
*smb_fname
)
1828 struct fruit_config_data
*config
= NULL
;
1831 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
1832 struct fruit_config_data
, return -1);
1834 switch (config
->meta
) {
1835 case FRUIT_META_STREAM
:
1836 rc
= fruit_unlink_meta_stream(handle
,
1841 case FRUIT_META_NETATALK
:
1842 rc
= fruit_unlink_meta_netatalk(handle
, smb_fname
);
1846 DBG_ERR("Unsupported meta config [%d]\n", config
->meta
);
1853 static int fruit_unlink_rsrc_stream(vfs_handle_struct
*handle
,
1854 struct files_struct
*dirfsp
,
1855 const struct smb_filename
*smb_fname
,
1860 if (!force_unlink
) {
1861 struct smb_filename
*smb_fname_cp
= NULL
;
1864 smb_fname_cp
= cp_smb_filename(talloc_tos(), smb_fname
);
1865 if (smb_fname_cp
== NULL
) {
1870 * 0 byte resource fork streams are not listed by
1871 * vfs_streaminfo, as a result stream cleanup/deletion of file
1872 * deletion doesn't remove the resourcefork stream.
1875 ret
= SMB_VFS_NEXT_STAT(handle
, smb_fname_cp
);
1877 TALLOC_FREE(smb_fname_cp
);
1878 DBG_ERR("stat [%s] failed [%s]\n",
1879 smb_fname_str_dbg(smb_fname_cp
), strerror(errno
));
1883 size
= smb_fname_cp
->st
.st_ex_size
;
1884 TALLOC_FREE(smb_fname_cp
);
1887 /* OS X ignores resource fork stream delete requests */
1892 ret
= SMB_VFS_NEXT_UNLINKAT(handle
,
1896 if ((ret
!= 0) && (errno
== ENOENT
) && force_unlink
) {
1903 static int fruit_unlink_rsrc_adouble(vfs_handle_struct
*handle
,
1904 struct files_struct
*dirfsp
,
1905 const struct smb_filename
*smb_fname
,
1909 struct adouble
*ad
= NULL
;
1910 struct smb_filename
*adp_smb_fname
= NULL
;
1912 if (!force_unlink
) {
1913 ad
= ad_get(talloc_tos(), handle
, smb_fname
,
1922 * 0 byte resource fork streams are not listed by
1923 * vfs_streaminfo, as a result stream cleanup/deletion of file
1924 * deletion doesn't remove the resourcefork stream.
1927 if (ad_getentrylen(ad
, ADEID_RFORK
) > 0) {
1928 /* OS X ignores resource fork stream delete requests */
1936 rc
= adouble_path(talloc_tos(), smb_fname
, &adp_smb_fname
);
1941 rc
= SMB_VFS_NEXT_UNLINKAT(handle
,
1945 TALLOC_FREE(adp_smb_fname
);
1946 if ((rc
!= 0) && (errno
== ENOENT
) && force_unlink
) {
1953 static int fruit_unlink_rsrc_xattr(vfs_handle_struct
*handle
,
1954 const struct smb_filename
*smb_fname
,
1958 * OS X ignores resource fork stream delete requests, so nothing to do
1959 * here. Removing the file will remove the xattr anyway, so we don't
1960 * have to take care of removing 0 byte resource forks that could be
1966 static int fruit_unlink_rsrc(vfs_handle_struct
*handle
,
1967 struct files_struct
*dirfsp
,
1968 const struct smb_filename
*smb_fname
,
1971 struct fruit_config_data
*config
= NULL
;
1974 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
1975 struct fruit_config_data
, return -1);
1977 switch (config
->rsrc
) {
1978 case FRUIT_RSRC_STREAM
:
1979 rc
= fruit_unlink_rsrc_stream(handle
,
1985 case FRUIT_RSRC_ADFILE
:
1986 rc
= fruit_unlink_rsrc_adouble(handle
,
1992 case FRUIT_RSRC_XATTR
:
1993 rc
= fruit_unlink_rsrc_xattr(handle
, smb_fname
, force_unlink
);
1997 DBG_ERR("Unsupported rsrc config [%d]\n", config
->rsrc
);
2004 static int fruit_chmod(vfs_handle_struct
*handle
,
2005 const struct smb_filename
*smb_fname
,
2009 struct fruit_config_data
*config
= NULL
;
2010 struct smb_filename
*smb_fname_adp
= NULL
;
2012 rc
= SMB_VFS_NEXT_CHMOD(handle
, smb_fname
, mode
);
2017 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
2018 struct fruit_config_data
, return -1);
2020 if (config
->rsrc
!= FRUIT_RSRC_ADFILE
) {
2024 if (!VALID_STAT(smb_fname
->st
)) {
2028 if (!S_ISREG(smb_fname
->st
.st_ex_mode
)) {
2032 rc
= adouble_path(talloc_tos(), smb_fname
, &smb_fname_adp
);
2037 DEBUG(10, ("fruit_chmod: %s\n", smb_fname_adp
->base_name
));
2039 rc
= SMB_VFS_NEXT_CHMOD(handle
, smb_fname_adp
, mode
);
2040 if (errno
== ENOENT
) {
2044 TALLOC_FREE(smb_fname_adp
);
2048 static int fruit_unlinkat(vfs_handle_struct
*handle
,
2049 struct files_struct
*dirfsp
,
2050 const struct smb_filename
*smb_fname
,
2053 struct fruit_config_data
*config
= NULL
;
2054 struct smb_filename
*rsrc_smb_fname
= NULL
;
2057 SMB_ASSERT(dirfsp
== dirfsp
->conn
->cwd_fsp
);
2059 if (flags
& AT_REMOVEDIR
) {
2060 return SMB_VFS_NEXT_UNLINKAT(handle
,
2066 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
2067 struct fruit_config_data
, return -1);
2069 if (is_afpinfo_stream(smb_fname
->stream_name
)) {
2070 return fruit_unlink_meta(handle
,
2073 } else if (is_afpresource_stream(smb_fname
->stream_name
)) {
2074 return fruit_unlink_rsrc(handle
,
2078 } else if (is_named_stream(smb_fname
)) {
2079 return SMB_VFS_NEXT_UNLINKAT(handle
,
2083 } else if (is_adouble_file(smb_fname
->base_name
)) {
2084 return SMB_VFS_NEXT_UNLINKAT(handle
,
2091 * A request to delete the base file. Because 0 byte resource
2092 * fork streams are not listed by fruit_streaminfo,
2093 * delete_all_streams() can't remove 0 byte resource fork
2094 * streams, so we have to cleanup this here.
2096 rsrc_smb_fname
= synthetic_smb_fname(talloc_tos(),
2097 smb_fname
->base_name
,
2098 AFPRESOURCE_STREAM_NAME
,
2102 if (rsrc_smb_fname
== NULL
) {
2106 ret
= fruit_unlink_rsrc(handle
, dirfsp
, rsrc_smb_fname
, true);
2107 if ((ret
!= 0) && (errno
!= ENOENT
)) {
2108 DBG_ERR("Forced unlink of [%s] failed [%s]\n",
2109 smb_fname_str_dbg(rsrc_smb_fname
), strerror(errno
));
2110 TALLOC_FREE(rsrc_smb_fname
);
2113 TALLOC_FREE(rsrc_smb_fname
);
2115 return SMB_VFS_NEXT_UNLINKAT(handle
,
2121 static ssize_t
fruit_pread_meta_stream(vfs_handle_struct
*handle
,
2122 files_struct
*fsp
, void *data
,
2123 size_t n
, off_t offset
)
2128 nread
= SMB_VFS_NEXT_PREAD(handle
, fsp
, data
, n
, offset
);
2129 if (nread
== -1 || nread
== n
) {
2133 DBG_ERR("Removing [%s] after short read [%zd]\n",
2134 fsp_str_dbg(fsp
), nread
);
2136 ret
= SMB_VFS_NEXT_UNLINKAT(handle
,
2141 DBG_ERR("Removing [%s] failed\n", fsp_str_dbg(fsp
));
2149 static ssize_t
fruit_pread_meta_adouble(vfs_handle_struct
*handle
,
2150 files_struct
*fsp
, void *data
,
2151 size_t n
, off_t offset
)
2154 struct adouble
*ad
= NULL
;
2155 char afpinfo_buf
[AFP_INFO_SIZE
];
2159 ai
= afpinfo_new(talloc_tos());
2164 ad
= ad_fget(talloc_tos(), handle
, fsp
, ADOUBLE_META
);
2170 p
= ad_get_entry(ad
, ADEID_FINDERI
);
2172 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp
));
2177 memcpy(&ai
->afpi_FinderInfo
[0], p
, ADEDLEN_FINDERI
);
2179 nread
= afpinfo_pack(ai
, afpinfo_buf
);
2180 if (nread
!= AFP_INFO_SIZE
) {
2185 memcpy(data
, afpinfo_buf
, n
);
2193 static ssize_t
fruit_pread_meta(vfs_handle_struct
*handle
,
2194 files_struct
*fsp
, void *data
,
2195 size_t n
, off_t offset
)
2197 struct fio
*fio
= (struct fio
*)VFS_FETCH_FSP_EXTENSION(handle
, fsp
);
2202 * OS X has a off-by-1 error in the offset calculation, so we're
2203 * bug compatible here. It won't hurt, as any relevant real
2204 * world read requests from the AFP_AfpInfo stream will be
2205 * offset=0 n=60. offset is ignored anyway, see below.
2207 if ((offset
< 0) || (offset
>= AFP_INFO_SIZE
+ 1)) {
2212 DBG_ERR("Failed to fetch fsp extension");
2216 /* Yes, macOS always reads from offset 0 */
2218 to_return
= MIN(n
, AFP_INFO_SIZE
);
2220 switch (fio
->config
->meta
) {
2221 case FRUIT_META_STREAM
:
2222 nread
= fruit_pread_meta_stream(handle
, fsp
, data
,
2226 case FRUIT_META_NETATALK
:
2227 nread
= fruit_pread_meta_adouble(handle
, fsp
, data
,
2232 DBG_ERR("Unexpected meta config [%d]\n", fio
->config
->meta
);
2236 if (nread
== -1 && fio
->fake_fd
) {
2238 char afpinfo_buf
[AFP_INFO_SIZE
];
2240 ai
= afpinfo_new(talloc_tos());
2245 nread
= afpinfo_pack(ai
, afpinfo_buf
);
2247 if (nread
!= AFP_INFO_SIZE
) {
2251 memcpy(data
, afpinfo_buf
, to_return
);
2258 static ssize_t
fruit_pread_rsrc_stream(vfs_handle_struct
*handle
,
2259 files_struct
*fsp
, void *data
,
2260 size_t n
, off_t offset
)
2262 return SMB_VFS_NEXT_PREAD(handle
, fsp
, data
, n
, offset
);
2265 static ssize_t
fruit_pread_rsrc_xattr(vfs_handle_struct
*handle
,
2266 files_struct
*fsp
, void *data
,
2267 size_t n
, off_t offset
)
2269 return SMB_VFS_NEXT_PREAD(handle
, fsp
, data
, n
, offset
);
2272 static ssize_t
fruit_pread_rsrc_adouble(vfs_handle_struct
*handle
,
2273 files_struct
*fsp
, void *data
,
2274 size_t n
, off_t offset
)
2276 struct adouble
*ad
= NULL
;
2279 ad
= ad_fget(talloc_tos(), handle
, fsp
, ADOUBLE_RSRC
);
2284 nread
= SMB_VFS_NEXT_PREAD(handle
, fsp
, data
, n
,
2285 offset
+ ad_getentryoff(ad
, ADEID_RFORK
));
2291 static ssize_t
fruit_pread_rsrc(vfs_handle_struct
*handle
,
2292 files_struct
*fsp
, void *data
,
2293 size_t n
, off_t offset
)
2295 struct fio
*fio
= (struct fio
*)VFS_FETCH_FSP_EXTENSION(handle
, fsp
);
2303 switch (fio
->config
->rsrc
) {
2304 case FRUIT_RSRC_STREAM
:
2305 nread
= fruit_pread_rsrc_stream(handle
, fsp
, data
, n
, offset
);
2308 case FRUIT_RSRC_ADFILE
:
2309 nread
= fruit_pread_rsrc_adouble(handle
, fsp
, data
, n
, offset
);
2312 case FRUIT_RSRC_XATTR
:
2313 nread
= fruit_pread_rsrc_xattr(handle
, fsp
, data
, n
, offset
);
2317 DBG_ERR("Unexpected rsrc config [%d]\n", fio
->config
->rsrc
);
2324 static ssize_t
fruit_pread(vfs_handle_struct
*handle
,
2325 files_struct
*fsp
, void *data
,
2326 size_t n
, off_t offset
)
2328 struct fio
*fio
= (struct fio
*)VFS_FETCH_FSP_EXTENSION(handle
, fsp
);
2331 DBG_DEBUG("Path [%s] offset=%"PRIdMAX
", size=%zd\n",
2332 fsp_str_dbg(fsp
), (intmax_t)offset
, n
);
2335 return SMB_VFS_NEXT_PREAD(handle
, fsp
, data
, n
, offset
);
2338 if (fio
->type
== ADOUBLE_META
) {
2339 nread
= fruit_pread_meta(handle
, fsp
, data
, n
, offset
);
2341 nread
= fruit_pread_rsrc(handle
, fsp
, data
, n
, offset
);
2344 DBG_DEBUG("Path [%s] nread [%zd]\n", fsp_str_dbg(fsp
), nread
);
2348 static bool fruit_must_handle_aio_stream(struct fio
*fio
)
2354 if (fio
->type
== ADOUBLE_META
) {
2358 if ((fio
->type
== ADOUBLE_RSRC
) &&
2359 (fio
->config
->rsrc
== FRUIT_RSRC_ADFILE
))
2367 struct fruit_pread_state
{
2369 struct vfs_aio_state vfs_aio_state
;
2372 static void fruit_pread_done(struct tevent_req
*subreq
);
2374 static struct tevent_req
*fruit_pread_send(
2375 struct vfs_handle_struct
*handle
,
2376 TALLOC_CTX
*mem_ctx
,
2377 struct tevent_context
*ev
,
2378 struct files_struct
*fsp
,
2380 size_t n
, off_t offset
)
2382 struct tevent_req
*req
= NULL
;
2383 struct tevent_req
*subreq
= NULL
;
2384 struct fruit_pread_state
*state
= NULL
;
2385 struct fio
*fio
= (struct fio
*)VFS_FETCH_FSP_EXTENSION(handle
, fsp
);
2387 req
= tevent_req_create(mem_ctx
, &state
,
2388 struct fruit_pread_state
);
2393 if (fruit_must_handle_aio_stream(fio
)) {
2394 state
->nread
= SMB_VFS_PREAD(fsp
, data
, n
, offset
);
2395 if (state
->nread
!= n
) {
2396 if (state
->nread
!= -1) {
2399 tevent_req_error(req
, errno
);
2400 return tevent_req_post(req
, ev
);
2402 tevent_req_done(req
);
2403 return tevent_req_post(req
, ev
);
2406 subreq
= SMB_VFS_NEXT_PREAD_SEND(state
, ev
, handle
, fsp
,
2408 if (tevent_req_nomem(req
, subreq
)) {
2409 return tevent_req_post(req
, ev
);
2411 tevent_req_set_callback(subreq
, fruit_pread_done
, req
);
2415 static void fruit_pread_done(struct tevent_req
*subreq
)
2417 struct tevent_req
*req
= tevent_req_callback_data(
2418 subreq
, struct tevent_req
);
2419 struct fruit_pread_state
*state
= tevent_req_data(
2420 req
, struct fruit_pread_state
);
2422 state
->nread
= SMB_VFS_PREAD_RECV(subreq
, &state
->vfs_aio_state
);
2423 TALLOC_FREE(subreq
);
2425 if (tevent_req_error(req
, state
->vfs_aio_state
.error
)) {
2428 tevent_req_done(req
);
2431 static ssize_t
fruit_pread_recv(struct tevent_req
*req
,
2432 struct vfs_aio_state
*vfs_aio_state
)
2434 struct fruit_pread_state
*state
= tevent_req_data(
2435 req
, struct fruit_pread_state
);
2437 if (tevent_req_is_unix_error(req
, &vfs_aio_state
->error
)) {
2441 *vfs_aio_state
= state
->vfs_aio_state
;
2442 return state
->nread
;
2445 static ssize_t
fruit_pwrite_meta_stream(vfs_handle_struct
*handle
,
2446 files_struct
*fsp
, const void *data
,
2447 size_t n
, off_t offset
)
2449 struct fio
*fio
= (struct fio
*)VFS_FETCH_FSP_EXTENSION(handle
, fsp
);
2455 DBG_DEBUG("Path [%s] offset=%"PRIdMAX
", size=%zd\n",
2456 fsp_str_dbg(fsp
), (intmax_t)offset
, n
);
2465 ret
= SMB_VFS_NEXT_CLOSE(handle
, fsp
);
2466 fsp_set_fd(fsp
, -1);
2468 DBG_ERR("Close [%s] failed: %s\n",
2469 fsp_str_dbg(fsp
), strerror(errno
));
2473 fd
= SMB_VFS_NEXT_OPENAT(handle
,
2480 DBG_ERR("On-demand create [%s] in write failed: %s\n",
2481 fsp_str_dbg(fsp
), strerror(errno
));
2484 fsp_set_fd(fsp
, fd
);
2485 fio
->fake_fd
= false;
2488 ai
= afpinfo_unpack(talloc_tos(), data
);
2493 if (ai_empty_finderinfo(ai
)) {
2495 * Writing an all 0 blob to the metadata stream results in the
2496 * stream being removed on a macOS server. This ensures we
2497 * behave the same and it verified by the "delete AFP_AfpInfo by
2498 * writing all 0" test.
2500 ret
= SMB_VFS_NEXT_FTRUNCATE(handle
, fsp
, 0);
2502 DBG_ERR("SMB_VFS_NEXT_FTRUNCATE on [%s] failed\n",
2507 ok
= set_delete_on_close(
2510 handle
->conn
->session_info
->security_token
,
2511 handle
->conn
->session_info
->unix_token
);
2513 DBG_ERR("set_delete_on_close on [%s] failed\n",
2520 nwritten
= SMB_VFS_NEXT_PWRITE(handle
, fsp
, data
, n
, offset
);
2521 if (nwritten
!= n
) {
2528 static ssize_t
fruit_pwrite_meta_netatalk(vfs_handle_struct
*handle
,
2529 files_struct
*fsp
, const void *data
,
2530 size_t n
, off_t offset
)
2532 struct adouble
*ad
= NULL
;
2538 ai
= afpinfo_unpack(talloc_tos(), data
);
2543 ad
= ad_fget(talloc_tos(), handle
, fsp
, ADOUBLE_META
);
2545 ad
= ad_init(talloc_tos(), ADOUBLE_META
);
2550 p
= ad_get_entry(ad
, ADEID_FINDERI
);
2552 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp
));
2557 memcpy(p
, &ai
->afpi_FinderInfo
[0], ADEDLEN_FINDERI
);
2559 ret
= ad_fset(handle
, ad
, fsp
);
2561 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp
));
2568 if (!ai_empty_finderinfo(ai
)) {
2573 * Writing an all 0 blob to the metadata stream results in the stream
2574 * being removed on a macOS server. This ensures we behave the same and
2575 * it verified by the "delete AFP_AfpInfo by writing all 0" test.
2578 ok
= set_delete_on_close(
2581 handle
->conn
->session_info
->security_token
,
2582 handle
->conn
->session_info
->unix_token
);
2584 DBG_ERR("set_delete_on_close on [%s] failed\n",
2592 static ssize_t
fruit_pwrite_meta(vfs_handle_struct
*handle
,
2593 files_struct
*fsp
, const void *data
,
2594 size_t n
, off_t offset
)
2596 struct fio
*fio
= (struct fio
*)VFS_FETCH_FSP_EXTENSION(handle
, fsp
);
2598 uint8_t buf
[AFP_INFO_SIZE
];
2604 DBG_ERR("Failed to fetch fsp extension");
2613 if (offset
!= 0 && n
< 60) {
2618 cmp
= memcmp(data
, "AFP", 3);
2624 if (n
<= AFP_OFF_FinderInfo
) {
2626 * Nothing to do here really, just return
2634 if (to_copy
> AFP_INFO_SIZE
) {
2635 to_copy
= AFP_INFO_SIZE
;
2637 memcpy(buf
, data
, to_copy
);
2640 if (to_write
!= AFP_INFO_SIZE
) {
2641 to_write
= AFP_INFO_SIZE
;
2644 switch (fio
->config
->meta
) {
2645 case FRUIT_META_STREAM
:
2646 nwritten
= fruit_pwrite_meta_stream(handle
,
2653 case FRUIT_META_NETATALK
:
2654 nwritten
= fruit_pwrite_meta_netatalk(handle
,
2662 DBG_ERR("Unexpected meta config [%d]\n", fio
->config
->meta
);
2666 if (nwritten
!= to_write
) {
2671 * Return the requested amount, verified against macOS SMB server
2676 static ssize_t
fruit_pwrite_rsrc_stream(vfs_handle_struct
*handle
,
2677 files_struct
*fsp
, const void *data
,
2678 size_t n
, off_t offset
)
2680 return SMB_VFS_NEXT_PWRITE(handle
, fsp
, data
, n
, offset
);
2683 static ssize_t
fruit_pwrite_rsrc_xattr(vfs_handle_struct
*handle
,
2684 files_struct
*fsp
, const void *data
,
2685 size_t n
, off_t offset
)
2687 return SMB_VFS_NEXT_PWRITE(handle
, fsp
, data
, n
, offset
);
2690 static ssize_t
fruit_pwrite_rsrc_adouble(vfs_handle_struct
*handle
,
2691 files_struct
*fsp
, const void *data
,
2692 size_t n
, off_t offset
)
2694 struct adouble
*ad
= NULL
;
2698 ad
= ad_fget(talloc_tos(), handle
, fsp
, ADOUBLE_RSRC
);
2700 DBG_ERR("ad_get [%s] failed\n", fsp_str_dbg(fsp
));
2704 nwritten
= SMB_VFS_NEXT_PWRITE(handle
, fsp
, data
, n
,
2705 offset
+ ad_getentryoff(ad
, ADEID_RFORK
));
2706 if (nwritten
!= n
) {
2707 DBG_ERR("Short write on [%s] [%zd/%zd]\n",
2708 fsp_str_dbg(fsp
), nwritten
, n
);
2713 if ((n
+ offset
) > ad_getentrylen(ad
, ADEID_RFORK
)) {
2714 ad_setentrylen(ad
, ADEID_RFORK
, n
+ offset
);
2715 ret
= ad_fset(handle
, ad
, fsp
);
2717 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp
));
2727 static ssize_t
fruit_pwrite_rsrc(vfs_handle_struct
*handle
,
2728 files_struct
*fsp
, const void *data
,
2729 size_t n
, off_t offset
)
2731 struct fio
*fio
= (struct fio
*)VFS_FETCH_FSP_EXTENSION(handle
, fsp
);
2735 DBG_ERR("Failed to fetch fsp extension");
2739 switch (fio
->config
->rsrc
) {
2740 case FRUIT_RSRC_STREAM
:
2741 nwritten
= fruit_pwrite_rsrc_stream(handle
, fsp
, data
, n
, offset
);
2744 case FRUIT_RSRC_ADFILE
:
2745 nwritten
= fruit_pwrite_rsrc_adouble(handle
, fsp
, data
, n
, offset
);
2748 case FRUIT_RSRC_XATTR
:
2749 nwritten
= fruit_pwrite_rsrc_xattr(handle
, fsp
, data
, n
, offset
);
2753 DBG_ERR("Unexpected rsrc config [%d]\n", fio
->config
->rsrc
);
2760 static ssize_t
fruit_pwrite(vfs_handle_struct
*handle
,
2761 files_struct
*fsp
, const void *data
,
2762 size_t n
, off_t offset
)
2764 struct fio
*fio
= (struct fio
*)VFS_FETCH_FSP_EXTENSION(handle
, fsp
);
2767 DBG_DEBUG("Path [%s] offset=%"PRIdMAX
", size=%zd\n",
2768 fsp_str_dbg(fsp
), (intmax_t)offset
, n
);
2771 return SMB_VFS_NEXT_PWRITE(handle
, fsp
, data
, n
, offset
);
2774 if (fio
->type
== ADOUBLE_META
) {
2775 nwritten
= fruit_pwrite_meta(handle
, fsp
, data
, n
, offset
);
2777 nwritten
= fruit_pwrite_rsrc(handle
, fsp
, data
, n
, offset
);
2780 DBG_DEBUG("Path [%s] nwritten=%zd\n", fsp_str_dbg(fsp
), nwritten
);
2784 struct fruit_pwrite_state
{
2786 struct vfs_aio_state vfs_aio_state
;
2789 static void fruit_pwrite_done(struct tevent_req
*subreq
);
2791 static struct tevent_req
*fruit_pwrite_send(
2792 struct vfs_handle_struct
*handle
,
2793 TALLOC_CTX
*mem_ctx
,
2794 struct tevent_context
*ev
,
2795 struct files_struct
*fsp
,
2797 size_t n
, off_t offset
)
2799 struct tevent_req
*req
= NULL
;
2800 struct tevent_req
*subreq
= NULL
;
2801 struct fruit_pwrite_state
*state
= NULL
;
2802 struct fio
*fio
= (struct fio
*)VFS_FETCH_FSP_EXTENSION(handle
, fsp
);
2804 req
= tevent_req_create(mem_ctx
, &state
,
2805 struct fruit_pwrite_state
);
2810 if (fruit_must_handle_aio_stream(fio
)) {
2811 state
->nwritten
= SMB_VFS_PWRITE(fsp
, data
, n
, offset
);
2812 if (state
->nwritten
!= n
) {
2813 if (state
->nwritten
!= -1) {
2816 tevent_req_error(req
, errno
);
2817 return tevent_req_post(req
, ev
);
2819 tevent_req_done(req
);
2820 return tevent_req_post(req
, ev
);
2823 subreq
= SMB_VFS_NEXT_PWRITE_SEND(state
, ev
, handle
, fsp
,
2825 if (tevent_req_nomem(req
, subreq
)) {
2826 return tevent_req_post(req
, ev
);
2828 tevent_req_set_callback(subreq
, fruit_pwrite_done
, req
);
2832 static void fruit_pwrite_done(struct tevent_req
*subreq
)
2834 struct tevent_req
*req
= tevent_req_callback_data(
2835 subreq
, struct tevent_req
);
2836 struct fruit_pwrite_state
*state
= tevent_req_data(
2837 req
, struct fruit_pwrite_state
);
2839 state
->nwritten
= SMB_VFS_PWRITE_RECV(subreq
, &state
->vfs_aio_state
);
2840 TALLOC_FREE(subreq
);
2842 if (tevent_req_error(req
, state
->vfs_aio_state
.error
)) {
2845 tevent_req_done(req
);
2848 static ssize_t
fruit_pwrite_recv(struct tevent_req
*req
,
2849 struct vfs_aio_state
*vfs_aio_state
)
2851 struct fruit_pwrite_state
*state
= tevent_req_data(
2852 req
, struct fruit_pwrite_state
);
2854 if (tevent_req_is_unix_error(req
, &vfs_aio_state
->error
)) {
2858 *vfs_aio_state
= state
->vfs_aio_state
;
2859 return state
->nwritten
;
2863 * Helper to stat/lstat the base file of an smb_fname.
2865 static int fruit_stat_base(vfs_handle_struct
*handle
,
2866 struct smb_filename
*smb_fname
,
2869 char *tmp_stream_name
;
2872 tmp_stream_name
= smb_fname
->stream_name
;
2873 smb_fname
->stream_name
= NULL
;
2875 rc
= SMB_VFS_NEXT_STAT(handle
, smb_fname
);
2877 rc
= SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
2879 smb_fname
->stream_name
= tmp_stream_name
;
2881 DBG_DEBUG("fruit_stat_base [%s] dev [%ju] ino [%ju]\n",
2882 smb_fname
->base_name
,
2883 (uintmax_t)smb_fname
->st
.st_ex_dev
,
2884 (uintmax_t)smb_fname
->st
.st_ex_ino
);
2888 static int fruit_stat_meta_stream(vfs_handle_struct
*handle
,
2889 struct smb_filename
*smb_fname
,
2895 ret
= fruit_stat_base(handle
, smb_fname
, false);
2900 ino
= hash_inode(&smb_fname
->st
, smb_fname
->stream_name
);
2903 ret
= SMB_VFS_NEXT_STAT(handle
, smb_fname
);
2905 ret
= SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
2908 smb_fname
->st
.st_ex_ino
= ino
;
2913 static int fruit_stat_meta_netatalk(vfs_handle_struct
*handle
,
2914 struct smb_filename
*smb_fname
,
2917 struct adouble
*ad
= NULL
;
2919 ad
= ad_get(talloc_tos(), handle
, smb_fname
, ADOUBLE_META
);
2921 DBG_INFO("fruit_stat_meta %s: %s\n",
2922 smb_fname_str_dbg(smb_fname
), strerror(errno
));
2928 /* Populate the stat struct with info from the base file. */
2929 if (fruit_stat_base(handle
, smb_fname
, follow_links
) == -1) {
2932 smb_fname
->st
.st_ex_size
= AFP_INFO_SIZE
;
2933 smb_fname
->st
.st_ex_ino
= hash_inode(&smb_fname
->st
,
2934 smb_fname
->stream_name
);
2938 static int fruit_stat_meta(vfs_handle_struct
*handle
,
2939 struct smb_filename
*smb_fname
,
2942 struct fruit_config_data
*config
= NULL
;
2945 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
2946 struct fruit_config_data
, return -1);
2948 switch (config
->meta
) {
2949 case FRUIT_META_STREAM
:
2950 ret
= fruit_stat_meta_stream(handle
, smb_fname
, follow_links
);
2953 case FRUIT_META_NETATALK
:
2954 ret
= fruit_stat_meta_netatalk(handle
, smb_fname
, follow_links
);
2958 DBG_ERR("Unexpected meta config [%d]\n", config
->meta
);
2965 static int fruit_stat_rsrc_netatalk(vfs_handle_struct
*handle
,
2966 struct smb_filename
*smb_fname
,
2969 struct adouble
*ad
= NULL
;
2972 ad
= ad_get(talloc_tos(), handle
, smb_fname
, ADOUBLE_RSRC
);
2978 /* Populate the stat struct with info from the base file. */
2979 ret
= fruit_stat_base(handle
, smb_fname
, follow_links
);
2985 smb_fname
->st
.st_ex_size
= ad_getentrylen(ad
, ADEID_RFORK
);
2986 smb_fname
->st
.st_ex_ino
= hash_inode(&smb_fname
->st
,
2987 smb_fname
->stream_name
);
2992 static int fruit_stat_rsrc_stream(vfs_handle_struct
*handle
,
2993 struct smb_filename
*smb_fname
,
2999 ret
= SMB_VFS_NEXT_STAT(handle
, smb_fname
);
3001 ret
= SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
3007 static int fruit_stat_rsrc_xattr(vfs_handle_struct
*handle
,
3008 struct smb_filename
*smb_fname
,
3011 #ifdef HAVE_ATTROPEN
3015 /* Populate the stat struct with info from the base file. */
3016 ret
= fruit_stat_base(handle
, smb_fname
, follow_links
);
3021 fd
= attropen(smb_fname
->base_name
,
3022 AFPRESOURCE_EA_NETATALK
,
3028 ret
= sys_fstat(fd
, &smb_fname
->st
, false);
3031 DBG_ERR("fstat [%s:%s] failed\n", smb_fname
->base_name
,
3032 AFPRESOURCE_EA_NETATALK
);
3038 smb_fname
->st
.st_ex_ino
= hash_inode(&smb_fname
->st
,
3039 smb_fname
->stream_name
);
3049 static int fruit_stat_rsrc(vfs_handle_struct
*handle
,
3050 struct smb_filename
*smb_fname
,
3053 struct fruit_config_data
*config
= NULL
;
3056 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname
));
3058 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
3059 struct fruit_config_data
, return -1);
3061 switch (config
->rsrc
) {
3062 case FRUIT_RSRC_STREAM
:
3063 ret
= fruit_stat_rsrc_stream(handle
, smb_fname
, follow_links
);
3066 case FRUIT_RSRC_XATTR
:
3067 ret
= fruit_stat_rsrc_xattr(handle
, smb_fname
, follow_links
);
3070 case FRUIT_RSRC_ADFILE
:
3071 ret
= fruit_stat_rsrc_netatalk(handle
, smb_fname
, follow_links
);
3075 DBG_ERR("Unexpected rsrc config [%d]\n", config
->rsrc
);
3082 static int fruit_stat(vfs_handle_struct
*handle
,
3083 struct smb_filename
*smb_fname
)
3087 DEBUG(10, ("fruit_stat called for %s\n",
3088 smb_fname_str_dbg(smb_fname
)));
3090 if (!is_named_stream(smb_fname
)) {
3091 rc
= SMB_VFS_NEXT_STAT(handle
, smb_fname
);
3093 update_btime(handle
, smb_fname
);
3099 * Note if lp_posix_paths() is true, we can never
3100 * get here as is_ntfs_stream_smb_fname() is
3101 * always false. So we never need worry about
3102 * not following links here.
3105 if (is_afpinfo_stream(smb_fname
->stream_name
)) {
3106 rc
= fruit_stat_meta(handle
, smb_fname
, true);
3107 } else if (is_afpresource_stream(smb_fname
->stream_name
)) {
3108 rc
= fruit_stat_rsrc(handle
, smb_fname
, true);
3110 return SMB_VFS_NEXT_STAT(handle
, smb_fname
);
3114 update_btime(handle
, smb_fname
);
3115 smb_fname
->st
.st_ex_mode
&= ~S_IFMT
;
3116 smb_fname
->st
.st_ex_mode
|= S_IFREG
;
3117 smb_fname
->st
.st_ex_blocks
=
3118 smb_fname
->st
.st_ex_size
/ STAT_ST_BLOCKSIZE
+ 1;
3123 static int fruit_lstat(vfs_handle_struct
*handle
,
3124 struct smb_filename
*smb_fname
)
3128 DEBUG(10, ("fruit_lstat called for %s\n",
3129 smb_fname_str_dbg(smb_fname
)));
3131 if (!is_named_stream(smb_fname
)) {
3132 rc
= SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
3134 update_btime(handle
, smb_fname
);
3139 if (is_afpinfo_stream(smb_fname
->stream_name
)) {
3140 rc
= fruit_stat_meta(handle
, smb_fname
, false);
3141 } else if (is_afpresource_stream(smb_fname
->stream_name
)) {
3142 rc
= fruit_stat_rsrc(handle
, smb_fname
, false);
3144 return SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
3148 update_btime(handle
, smb_fname
);
3149 smb_fname
->st
.st_ex_mode
&= ~S_IFMT
;
3150 smb_fname
->st
.st_ex_mode
|= S_IFREG
;
3151 smb_fname
->st
.st_ex_blocks
=
3152 smb_fname
->st
.st_ex_size
/ STAT_ST_BLOCKSIZE
+ 1;
3157 static int fruit_fstat_meta_stream(vfs_handle_struct
*handle
,
3159 SMB_STRUCT_STAT
*sbuf
)
3161 struct fio
*fio
= (struct fio
*)VFS_FETCH_FSP_EXTENSION(handle
, fsp
);
3162 struct smb_filename smb_fname
;
3171 ret
= fruit_stat_base(handle
, fsp
->base_fsp
->fsp_name
, false);
3176 *sbuf
= fsp
->base_fsp
->fsp_name
->st
;
3177 sbuf
->st_ex_size
= AFP_INFO_SIZE
;
3178 sbuf
->st_ex_ino
= hash_inode(sbuf
, fsp
->fsp_name
->stream_name
);
3182 smb_fname
= (struct smb_filename
) {
3183 .base_name
= fsp
->fsp_name
->base_name
,
3184 .twrp
= fsp
->fsp_name
->twrp
,
3187 ret
= fruit_stat_base(handle
, &smb_fname
, false);
3191 *sbuf
= smb_fname
.st
;
3193 ino
= hash_inode(sbuf
, fsp
->fsp_name
->stream_name
);
3195 ret
= SMB_VFS_NEXT_FSTAT(handle
, fsp
, sbuf
);
3200 sbuf
->st_ex_ino
= ino
;
3204 static int fruit_fstat_meta_netatalk(vfs_handle_struct
*handle
,
3206 SMB_STRUCT_STAT
*sbuf
)
3210 ret
= fruit_stat_base(handle
, fsp
->base_fsp
->fsp_name
, false);
3215 *sbuf
= fsp
->base_fsp
->fsp_name
->st
;
3216 sbuf
->st_ex_size
= AFP_INFO_SIZE
;
3217 sbuf
->st_ex_ino
= hash_inode(sbuf
, fsp
->fsp_name
->stream_name
);
3222 static int fruit_fstat_meta(vfs_handle_struct
*handle
,
3224 SMB_STRUCT_STAT
*sbuf
,
3229 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp
));
3231 switch (fio
->config
->meta
) {
3232 case FRUIT_META_STREAM
:
3233 ret
= fruit_fstat_meta_stream(handle
, fsp
, sbuf
);
3236 case FRUIT_META_NETATALK
:
3237 ret
= fruit_fstat_meta_netatalk(handle
, fsp
, sbuf
);
3241 DBG_ERR("Unexpected meta config [%d]\n", fio
->config
->meta
);
3245 DBG_DEBUG("Path [%s] ret [%d]\n", fsp_str_dbg(fsp
), ret
);
3249 static int fruit_fstat_rsrc_xattr(vfs_handle_struct
*handle
,
3251 SMB_STRUCT_STAT
*sbuf
)
3253 return SMB_VFS_NEXT_FSTAT(handle
, fsp
, sbuf
);
3256 static int fruit_fstat_rsrc_stream(vfs_handle_struct
*handle
,
3258 SMB_STRUCT_STAT
*sbuf
)
3260 return SMB_VFS_NEXT_FSTAT(handle
, fsp
, sbuf
);
3263 static int fruit_fstat_rsrc_adouble(vfs_handle_struct
*handle
,
3265 SMB_STRUCT_STAT
*sbuf
)
3267 struct adouble
*ad
= NULL
;
3270 /* Populate the stat struct with info from the base file. */
3271 ret
= fruit_stat_base(handle
, fsp
->base_fsp
->fsp_name
, false);
3276 ad
= ad_get(talloc_tos(), handle
,
3277 fsp
->base_fsp
->fsp_name
,
3280 DBG_ERR("ad_get [%s] failed [%s]\n",
3281 fsp_str_dbg(fsp
), strerror(errno
));
3285 *sbuf
= fsp
->base_fsp
->fsp_name
->st
;
3286 sbuf
->st_ex_size
= ad_getentrylen(ad
, ADEID_RFORK
);
3287 sbuf
->st_ex_ino
= hash_inode(sbuf
, fsp
->fsp_name
->stream_name
);
3293 static int fruit_fstat_rsrc(vfs_handle_struct
*handle
, files_struct
*fsp
,
3294 SMB_STRUCT_STAT
*sbuf
, struct fio
*fio
)
3298 switch (fio
->config
->rsrc
) {
3299 case FRUIT_RSRC_STREAM
:
3300 ret
= fruit_fstat_rsrc_stream(handle
, fsp
, sbuf
);
3303 case FRUIT_RSRC_ADFILE
:
3304 ret
= fruit_fstat_rsrc_adouble(handle
, fsp
, sbuf
);
3307 case FRUIT_RSRC_XATTR
:
3308 ret
= fruit_fstat_rsrc_xattr(handle
, fsp
, sbuf
);
3312 DBG_ERR("Unexpected rsrc config [%d]\n", fio
->config
->rsrc
);
3319 static int fruit_fstat(vfs_handle_struct
*handle
, files_struct
*fsp
,
3320 SMB_STRUCT_STAT
*sbuf
)
3322 struct fio
*fio
= (struct fio
*)VFS_FETCH_FSP_EXTENSION(handle
, fsp
);
3326 return SMB_VFS_NEXT_FSTAT(handle
, fsp
, sbuf
);
3329 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp
));
3331 if (fio
->type
== ADOUBLE_META
) {
3332 rc
= fruit_fstat_meta(handle
, fsp
, sbuf
, fio
);
3334 rc
= fruit_fstat_rsrc(handle
, fsp
, sbuf
, fio
);
3338 sbuf
->st_ex_mode
&= ~S_IFMT
;
3339 sbuf
->st_ex_mode
|= S_IFREG
;
3340 sbuf
->st_ex_blocks
= sbuf
->st_ex_size
/ STAT_ST_BLOCKSIZE
+ 1;
3343 DBG_DEBUG("Path [%s] rc [%d] size [%"PRIdMAX
"]\n",
3344 fsp_str_dbg(fsp
), rc
, (intmax_t)sbuf
->st_ex_size
);
3348 static NTSTATUS
delete_invalid_meta_stream(
3349 vfs_handle_struct
*handle
,
3350 const struct smb_filename
*smb_fname
,
3351 TALLOC_CTX
*mem_ctx
,
3352 unsigned int *pnum_streams
,
3353 struct stream_struct
**pstreams
,
3356 struct smb_filename
*sname
= NULL
;
3360 ok
= del_fruit_stream(mem_ctx
, pnum_streams
, pstreams
, AFPINFO_STREAM
);
3362 return NT_STATUS_INTERNAL_ERROR
;
3366 return NT_STATUS_OK
;
3369 sname
= synthetic_smb_fname(talloc_tos(),
3370 smb_fname
->base_name
,
3371 AFPINFO_STREAM_NAME
,
3375 if (sname
== NULL
) {
3376 return NT_STATUS_NO_MEMORY
;
3379 ret
= SMB_VFS_NEXT_UNLINKAT(handle
,
3380 handle
->conn
->cwd_fsp
,
3385 DBG_ERR("Removing [%s] failed\n", smb_fname_str_dbg(sname
));
3386 return map_nt_error_from_unix(errno
);
3389 return NT_STATUS_OK
;
3392 static NTSTATUS
fruit_streaminfo_meta_stream(
3393 vfs_handle_struct
*handle
,
3394 struct files_struct
*fsp
,
3395 const struct smb_filename
*smb_fname
,
3396 TALLOC_CTX
*mem_ctx
,
3397 unsigned int *pnum_streams
,
3398 struct stream_struct
**pstreams
)
3400 struct stream_struct
*stream
= *pstreams
;
3401 unsigned int num_streams
= *pnum_streams
;
3404 for (i
= 0; i
< num_streams
; i
++) {
3405 if (strequal_m(stream
[i
].name
, AFPINFO_STREAM
)) {
3410 if (i
== num_streams
) {
3411 return NT_STATUS_OK
;
3414 if (stream
[i
].size
!= AFP_INFO_SIZE
) {
3415 DBG_ERR("Removing invalid AFPINFO_STREAM size [%jd] from [%s]\n",
3416 (intmax_t)stream
[i
].size
, smb_fname_str_dbg(smb_fname
));
3418 return delete_invalid_meta_stream(handle
,
3427 return NT_STATUS_OK
;
3430 static NTSTATUS
fruit_streaminfo_meta_netatalk(
3431 vfs_handle_struct
*handle
,
3432 struct files_struct
*fsp
,
3433 const struct smb_filename
*smb_fname
,
3434 TALLOC_CTX
*mem_ctx
,
3435 unsigned int *pnum_streams
,
3436 struct stream_struct
**pstreams
)
3438 struct stream_struct
*stream
= *pstreams
;
3439 unsigned int num_streams
= *pnum_streams
;
3440 struct adouble
*ad
= NULL
;
3445 /* Remove the Netatalk xattr from the list */
3446 ok
= del_fruit_stream(mem_ctx
, pnum_streams
, pstreams
,
3447 ":" NETATALK_META_XATTR
":$DATA");
3449 return NT_STATUS_NO_MEMORY
;
3453 * Check if there's a AFPINFO_STREAM from the VFS streams
3454 * backend and if yes, remove it from the list
3456 for (i
= 0; i
< num_streams
; i
++) {
3457 if (strequal_m(stream
[i
].name
, AFPINFO_STREAM
)) {
3462 if (i
< num_streams
) {
3463 DBG_WARNING("Unexpected AFPINFO_STREAM on [%s]\n",
3464 smb_fname_str_dbg(smb_fname
));
3466 ok
= del_fruit_stream(mem_ctx
, pnum_streams
, pstreams
,
3469 return NT_STATUS_INTERNAL_ERROR
;
3473 ad
= ad_get(talloc_tos(), handle
, smb_fname
, ADOUBLE_META
);
3475 return NT_STATUS_OK
;
3478 is_fi_empty
= ad_empty_finderinfo(ad
);
3482 return NT_STATUS_OK
;
3485 ok
= add_fruit_stream(mem_ctx
, pnum_streams
, pstreams
,
3486 AFPINFO_STREAM_NAME
, AFP_INFO_SIZE
,
3487 smb_roundup(handle
->conn
, AFP_INFO_SIZE
));
3489 return NT_STATUS_NO_MEMORY
;
3492 return NT_STATUS_OK
;
3495 static NTSTATUS
fruit_streaminfo_meta(vfs_handle_struct
*handle
,
3496 struct files_struct
*fsp
,
3497 const struct smb_filename
*smb_fname
,
3498 TALLOC_CTX
*mem_ctx
,
3499 unsigned int *pnum_streams
,
3500 struct stream_struct
**pstreams
)
3502 struct fruit_config_data
*config
= NULL
;
3505 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct fruit_config_data
,
3506 return NT_STATUS_INTERNAL_ERROR
);
3508 switch (config
->meta
) {
3509 case FRUIT_META_NETATALK
:
3510 status
= fruit_streaminfo_meta_netatalk(handle
, fsp
, smb_fname
,
3511 mem_ctx
, pnum_streams
,
3515 case FRUIT_META_STREAM
:
3516 status
= fruit_streaminfo_meta_stream(handle
, fsp
, smb_fname
,
3517 mem_ctx
, pnum_streams
,
3522 return NT_STATUS_INTERNAL_ERROR
;
3528 static NTSTATUS
fruit_streaminfo_rsrc_stream(
3529 vfs_handle_struct
*handle
,
3530 struct files_struct
*fsp
,
3531 const struct smb_filename
*smb_fname
,
3532 TALLOC_CTX
*mem_ctx
,
3533 unsigned int *pnum_streams
,
3534 struct stream_struct
**pstreams
)
3538 ok
= filter_empty_rsrc_stream(pnum_streams
, pstreams
);
3540 DBG_ERR("Filtering resource stream failed\n");
3541 return NT_STATUS_INTERNAL_ERROR
;
3543 return NT_STATUS_OK
;
3546 static NTSTATUS
fruit_streaminfo_rsrc_xattr(
3547 vfs_handle_struct
*handle
,
3548 struct files_struct
*fsp
,
3549 const struct smb_filename
*smb_fname
,
3550 TALLOC_CTX
*mem_ctx
,
3551 unsigned int *pnum_streams
,
3552 struct stream_struct
**pstreams
)
3556 ok
= filter_empty_rsrc_stream(pnum_streams
, pstreams
);
3558 DBG_ERR("Filtering resource stream failed\n");
3559 return NT_STATUS_INTERNAL_ERROR
;
3561 return NT_STATUS_OK
;
3564 static NTSTATUS
fruit_streaminfo_rsrc_adouble(
3565 vfs_handle_struct
*handle
,
3566 struct files_struct
*fsp
,
3567 const struct smb_filename
*smb_fname
,
3568 TALLOC_CTX
*mem_ctx
,
3569 unsigned int *pnum_streams
,
3570 struct stream_struct
**pstreams
)
3572 struct stream_struct
*stream
= *pstreams
;
3573 unsigned int num_streams
= *pnum_streams
;
3574 struct adouble
*ad
= NULL
;
3580 * Check if there's a AFPRESOURCE_STREAM from the VFS streams backend
3581 * and if yes, remove it from the list
3583 for (i
= 0; i
< num_streams
; i
++) {
3584 if (strequal_m(stream
[i
].name
, AFPRESOURCE_STREAM
)) {
3589 if (i
< num_streams
) {
3590 DBG_WARNING("Unexpected AFPRESOURCE_STREAM on [%s]\n",
3591 smb_fname_str_dbg(smb_fname
));
3593 ok
= del_fruit_stream(mem_ctx
, pnum_streams
, pstreams
,
3594 AFPRESOURCE_STREAM
);
3596 return NT_STATUS_INTERNAL_ERROR
;
3600 ad
= ad_get(talloc_tos(), handle
, smb_fname
, ADOUBLE_RSRC
);
3602 return NT_STATUS_OK
;
3605 rlen
= ad_getentrylen(ad
, ADEID_RFORK
);
3609 return NT_STATUS_OK
;
3612 ok
= add_fruit_stream(mem_ctx
, pnum_streams
, pstreams
,
3613 AFPRESOURCE_STREAM_NAME
, rlen
,
3614 smb_roundup(handle
->conn
, rlen
));
3616 return NT_STATUS_NO_MEMORY
;
3619 return NT_STATUS_OK
;
3622 static NTSTATUS
fruit_streaminfo_rsrc(vfs_handle_struct
*handle
,
3623 struct files_struct
*fsp
,
3624 const struct smb_filename
*smb_fname
,
3625 TALLOC_CTX
*mem_ctx
,
3626 unsigned int *pnum_streams
,
3627 struct stream_struct
**pstreams
)
3629 struct fruit_config_data
*config
= NULL
;
3632 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct fruit_config_data
,
3633 return NT_STATUS_INTERNAL_ERROR
);
3635 switch (config
->rsrc
) {
3636 case FRUIT_RSRC_STREAM
:
3637 status
= fruit_streaminfo_rsrc_stream(handle
, fsp
, smb_fname
,
3638 mem_ctx
, pnum_streams
,
3642 case FRUIT_RSRC_XATTR
:
3643 status
= fruit_streaminfo_rsrc_xattr(handle
, fsp
, smb_fname
,
3644 mem_ctx
, pnum_streams
,
3648 case FRUIT_RSRC_ADFILE
:
3649 status
= fruit_streaminfo_rsrc_adouble(handle
, fsp
, smb_fname
,
3650 mem_ctx
, pnum_streams
,
3655 return NT_STATUS_INTERNAL_ERROR
;
3661 static void fruit_filter_empty_streams(unsigned int *pnum_streams
,
3662 struct stream_struct
**pstreams
)
3664 unsigned num_streams
= *pnum_streams
;
3665 struct stream_struct
*streams
= *pstreams
;
3668 if (!global_fruit_config
.nego_aapl
) {
3672 while (i
< num_streams
) {
3673 struct smb_filename smb_fname
= (struct smb_filename
) {
3674 .stream_name
= streams
[i
].name
,
3677 if (is_ntfs_default_stream_smb_fname(&smb_fname
)
3678 || streams
[i
].size
> 0)
3684 streams
[i
] = streams
[num_streams
- 1];
3688 *pnum_streams
= num_streams
;
3691 static NTSTATUS
fruit_streaminfo(vfs_handle_struct
*handle
,
3692 struct files_struct
*fsp
,
3693 const struct smb_filename
*smb_fname
,
3694 TALLOC_CTX
*mem_ctx
,
3695 unsigned int *pnum_streams
,
3696 struct stream_struct
**pstreams
)
3698 struct fruit_config_data
*config
= NULL
;
3701 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct fruit_config_data
,
3702 return NT_STATUS_UNSUCCESSFUL
);
3704 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname
));
3706 status
= SMB_VFS_NEXT_STREAMINFO(handle
, fsp
, smb_fname
, mem_ctx
,
3707 pnum_streams
, pstreams
);
3708 if (!NT_STATUS_IS_OK(status
)) {
3712 fruit_filter_empty_streams(pnum_streams
, pstreams
);
3714 status
= fruit_streaminfo_meta(handle
, fsp
, smb_fname
,
3715 mem_ctx
, pnum_streams
, pstreams
);
3716 if (!NT_STATUS_IS_OK(status
)) {
3720 status
= fruit_streaminfo_rsrc(handle
, fsp
, smb_fname
,
3721 mem_ctx
, pnum_streams
, pstreams
);
3722 if (!NT_STATUS_IS_OK(status
)) {
3726 return NT_STATUS_OK
;
3729 static int fruit_ntimes(vfs_handle_struct
*handle
,
3730 const struct smb_filename
*smb_fname
,
3731 struct smb_file_time
*ft
)
3734 struct adouble
*ad
= NULL
;
3735 struct fruit_config_data
*config
= NULL
;
3737 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct fruit_config_data
,
3740 if ((config
->meta
!= FRUIT_META_NETATALK
) ||
3741 is_omit_timespec(&ft
->create_time
))
3743 return SMB_VFS_NEXT_NTIMES(handle
, smb_fname
, ft
);
3746 DEBUG(10,("set btime for %s to %s\n", smb_fname_str_dbg(smb_fname
),
3747 time_to_asc(convert_timespec_to_time_t(ft
->create_time
))));
3749 ad
= ad_get(talloc_tos(), handle
, smb_fname
, ADOUBLE_META
);
3754 ad_setdate(ad
, AD_DATE_CREATE
| AD_DATE_UNIX
,
3755 convert_time_t_to_uint32_t(ft
->create_time
.tv_sec
));
3757 rc
= ad_set(handle
, ad
, smb_fname
);
3763 DEBUG(1, ("fruit_ntimes: %s\n", smb_fname_str_dbg(smb_fname
)));
3766 return SMB_VFS_NEXT_NTIMES(handle
, smb_fname
, ft
);
3769 static int fruit_fallocate(struct vfs_handle_struct
*handle
,
3770 struct files_struct
*fsp
,
3775 struct fio
*fio
= (struct fio
*)VFS_FETCH_FSP_EXTENSION(handle
, fsp
);
3778 return SMB_VFS_NEXT_FALLOCATE(handle
, fsp
, mode
, offset
, len
);
3781 /* Let the pwrite code path handle it. */
3786 static int fruit_ftruncate_rsrc_xattr(struct vfs_handle_struct
*handle
,
3787 struct files_struct
*fsp
,
3790 #ifdef HAVE_ATTROPEN
3791 return SMB_VFS_NEXT_FTRUNCATE(handle
, fsp
, offset
);
3796 static int fruit_ftruncate_rsrc_adouble(struct vfs_handle_struct
*handle
,
3797 struct files_struct
*fsp
,
3801 struct adouble
*ad
= NULL
;
3804 ad
= ad_fget(talloc_tos(), handle
, fsp
, ADOUBLE_RSRC
);
3806 DBG_DEBUG("ad_get [%s] failed [%s]\n",
3807 fsp_str_dbg(fsp
), strerror(errno
));
3811 ad_off
= ad_getentryoff(ad
, ADEID_RFORK
);
3813 rc
= SMB_VFS_NEXT_FTRUNCATE(handle
, fsp
, offset
+ ad_off
);
3819 ad_setentrylen(ad
, ADEID_RFORK
, offset
);
3821 rc
= ad_fset(handle
, ad
, fsp
);
3823 DBG_ERR("ad_fset [%s] failed [%s]\n",
3824 fsp_str_dbg(fsp
), strerror(errno
));
3833 static int fruit_ftruncate_rsrc_stream(struct vfs_handle_struct
*handle
,
3834 struct files_struct
*fsp
,
3837 return SMB_VFS_NEXT_FTRUNCATE(handle
, fsp
, offset
);
3840 static int fruit_ftruncate_rsrc(struct vfs_handle_struct
*handle
,
3841 struct files_struct
*fsp
,
3844 struct fio
*fio
= (struct fio
*)VFS_FETCH_FSP_EXTENSION(handle
, fsp
);
3848 DBG_ERR("Failed to fetch fsp extension");
3852 switch (fio
->config
->rsrc
) {
3853 case FRUIT_RSRC_XATTR
:
3854 ret
= fruit_ftruncate_rsrc_xattr(handle
, fsp
, offset
);
3857 case FRUIT_RSRC_ADFILE
:
3858 ret
= fruit_ftruncate_rsrc_adouble(handle
, fsp
, offset
);
3861 case FRUIT_RSRC_STREAM
:
3862 ret
= fruit_ftruncate_rsrc_stream(handle
, fsp
, offset
);
3866 DBG_ERR("Unexpected rsrc config [%d]\n", fio
->config
->rsrc
);
3874 static int fruit_ftruncate_meta(struct vfs_handle_struct
*handle
,
3875 struct files_struct
*fsp
,
3879 DBG_WARNING("ftruncate %s to %jd",
3880 fsp_str_dbg(fsp
), (intmax_t)offset
);
3881 /* OS X returns NT_STATUS_ALLOTTED_SPACE_EXCEEDED */
3886 /* OS X returns success but does nothing */
3887 DBG_INFO("ignoring ftruncate %s to %jd\n",
3888 fsp_str_dbg(fsp
), (intmax_t)offset
);
3892 static int fruit_ftruncate(struct vfs_handle_struct
*handle
,
3893 struct files_struct
*fsp
,
3896 struct fio
*fio
= (struct fio
*)VFS_FETCH_FSP_EXTENSION(handle
, fsp
);
3899 DBG_DEBUG("Path [%s] offset [%"PRIdMAX
"]\n", fsp_str_dbg(fsp
),
3903 return SMB_VFS_NEXT_FTRUNCATE(handle
, fsp
, offset
);
3906 if (fio
->type
== ADOUBLE_META
) {
3907 ret
= fruit_ftruncate_meta(handle
, fsp
, offset
);
3909 ret
= fruit_ftruncate_rsrc(handle
, fsp
, offset
);
3912 DBG_DEBUG("Path [%s] result [%d]\n", fsp_str_dbg(fsp
), ret
);
3916 static NTSTATUS
fruit_create_file(vfs_handle_struct
*handle
,
3917 struct smb_request
*req
,
3918 struct smb_filename
*smb_fname
,
3919 uint32_t access_mask
,
3920 uint32_t share_access
,
3921 uint32_t create_disposition
,
3922 uint32_t create_options
,
3923 uint32_t file_attributes
,
3924 uint32_t oplock_request
,
3925 const struct smb2_lease
*lease
,
3926 uint64_t allocation_size
,
3927 uint32_t private_flags
,
3928 struct security_descriptor
*sd
,
3929 struct ea_list
*ea_list
,
3930 files_struct
**result
,
3932 const struct smb2_create_blobs
*in_context_blobs
,
3933 struct smb2_create_blobs
*out_context_blobs
)
3936 struct fruit_config_data
*config
= NULL
;
3937 files_struct
*fsp
= NULL
;
3938 bool internal_open
= (oplock_request
& INTERNAL_OPEN_ONLY
);
3941 status
= check_aapl(handle
, req
, in_context_blobs
, out_context_blobs
);
3942 if (!NT_STATUS_IS_OK(status
)) {
3946 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct fruit_config_data
,
3947 return NT_STATUS_UNSUCCESSFUL
);
3949 if (is_apple_stream(smb_fname
->stream_name
) && !internal_open
) {
3950 uint32_t conv_flags
= 0;
3952 if (config
->wipe_intentionally_left_blank_rfork
) {
3953 conv_flags
|= AD_CONV_WIPE_BLANK
;
3955 if (config
->delete_empty_adfiles
) {
3956 conv_flags
|= AD_CONV_DELETE
;
3959 ret
= ad_convert(handle
,
3960 handle
->conn
->cwd_fsp
,
3962 macos_string_replace_map
,
3965 DBG_ERR("ad_convert() failed\n");
3966 return NT_STATUS_UNSUCCESSFUL
;
3970 status
= SMB_VFS_NEXT_CREATE_FILE(
3971 handle
, req
, smb_fname
,
3972 access_mask
, share_access
,
3973 create_disposition
, create_options
,
3974 file_attributes
, oplock_request
,
3976 allocation_size
, private_flags
,
3977 sd
, ea_list
, result
,
3978 pinfo
, in_context_blobs
, out_context_blobs
);
3979 if (!NT_STATUS_IS_OK(status
)) {
3985 if (global_fruit_config
.nego_aapl
) {
3986 if (config
->posix_rename
&& fsp
->fsp_flags
.is_directory
) {
3988 * Enable POSIX directory rename behaviour
3990 fsp
->posix_flags
|= FSP_POSIX_FLAGS_RENAME
;
3995 * If this is a plain open for existing files, opening an 0
3996 * byte size resource fork MUST fail with
3997 * NT_STATUS_OBJECT_NAME_NOT_FOUND.
3999 * Cf the vfs_fruit torture tests in test_rfork_create().
4001 if (global_fruit_config
.nego_aapl
&&
4002 create_disposition
== FILE_OPEN
&&
4003 smb_fname
->st
.st_ex_size
== 0 &&
4004 is_named_stream(smb_fname
))
4006 status
= NT_STATUS_OBJECT_NAME_NOT_FOUND
;
4010 if (is_named_stream(smb_fname
) || fsp
->fsp_flags
.is_directory
) {
4014 if ((config
->locking
== FRUIT_LOCKING_NETATALK
) &&
4015 (fsp
->op
!= NULL
) &&
4016 !fsp
->fsp_flags
.is_pathref
)
4018 status
= fruit_check_access(
4022 if (!NT_STATUS_IS_OK(status
)) {
4030 DEBUG(10, ("fruit_create_file: %s\n", nt_errstr(status
)));
4033 close_file(req
, fsp
, ERROR_CLOSE
);
4034 *result
= fsp
= NULL
;
4040 static NTSTATUS
fruit_readdir_attr(struct vfs_handle_struct
*handle
,
4041 const struct smb_filename
*fname
,
4042 TALLOC_CTX
*mem_ctx
,
4043 struct readdir_attr_data
**pattr_data
)
4045 struct fruit_config_data
*config
= NULL
;
4046 struct readdir_attr_data
*attr_data
;
4047 uint32_t conv_flags
= 0;
4051 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
4052 struct fruit_config_data
,
4053 return NT_STATUS_UNSUCCESSFUL
);
4055 if (!global_fruit_config
.nego_aapl
) {
4056 return SMB_VFS_NEXT_READDIR_ATTR(handle
, fname
, mem_ctx
, pattr_data
);
4059 DEBUG(10, ("fruit_readdir_attr %s\n", fname
->base_name
));
4061 if (config
->wipe_intentionally_left_blank_rfork
) {
4062 conv_flags
|= AD_CONV_WIPE_BLANK
;
4064 if (config
->delete_empty_adfiles
) {
4065 conv_flags
|= AD_CONV_DELETE
;
4068 ret
= ad_convert(handle
,
4069 handle
->conn
->cwd_fsp
,
4071 macos_string_replace_map
,
4074 DBG_ERR("ad_convert() failed\n");
4075 return NT_STATUS_UNSUCCESSFUL
;
4078 *pattr_data
= talloc_zero(mem_ctx
, struct readdir_attr_data
);
4079 if (*pattr_data
== NULL
) {
4080 return NT_STATUS_UNSUCCESSFUL
;
4082 attr_data
= *pattr_data
;
4083 attr_data
->type
= RDATTR_AAPL
;
4086 * Mac metadata: compressed FinderInfo, resource fork length
4089 status
= readdir_attr_macmeta(handle
, fname
, attr_data
);
4090 if (!NT_STATUS_IS_OK(status
)) {
4092 * Error handling is tricky: if we return failure from
4093 * this function, the corresponding directory entry
4094 * will to be passed to the client, so we really just
4095 * want to error out on fatal errors.
4097 if (!NT_STATUS_EQUAL(status
, NT_STATUS_ACCESS_DENIED
)) {
4105 if (config
->unix_info_enabled
) {
4106 attr_data
->attr_data
.aapl
.unix_mode
= fname
->st
.st_ex_mode
;
4112 if (!config
->readdir_attr_max_access
) {
4113 attr_data
->attr_data
.aapl
.max_access
= FILE_GENERIC_ALL
;
4115 status
= smbd_calculate_access_mask(
4117 handle
->conn
->cwd_fsp
,
4120 SEC_FLAG_MAXIMUM_ALLOWED
,
4121 &attr_data
->attr_data
.aapl
.max_access
);
4122 if (!NT_STATUS_IS_OK(status
)) {
4127 return NT_STATUS_OK
;
4130 DEBUG(1, ("fruit_readdir_attr %s, error: %s\n",
4131 fname
->base_name
, nt_errstr(status
)));
4132 TALLOC_FREE(*pattr_data
);
4136 static NTSTATUS
fruit_fget_nt_acl(vfs_handle_struct
*handle
,
4138 uint32_t security_info
,
4139 TALLOC_CTX
*mem_ctx
,
4140 struct security_descriptor
**ppdesc
)
4143 struct security_ace ace
;
4145 struct fruit_config_data
*config
;
4147 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
4148 struct fruit_config_data
,
4149 return NT_STATUS_UNSUCCESSFUL
);
4151 status
= SMB_VFS_NEXT_FGET_NT_ACL(handle
, fsp
, security_info
,
4153 if (!NT_STATUS_IS_OK(status
)) {
4158 * Add MS NFS style ACEs with uid, gid and mode
4160 if (!global_fruit_config
.nego_aapl
) {
4161 return NT_STATUS_OK
;
4163 if (!config
->unix_info_enabled
) {
4164 return NT_STATUS_OK
;
4167 /* First remove any existing ACE's with NFS style mode/uid/gid SIDs. */
4168 status
= remove_virtual_nfs_aces(*ppdesc
);
4169 if (!NT_STATUS_IS_OK(status
)) {
4170 DBG_WARNING("failed to remove MS NFS style ACEs\n");
4174 /* MS NFS style mode */
4175 sid_compose(&sid
, &global_sid_Unix_NFS_Mode
, fsp
->fsp_name
->st
.st_ex_mode
);
4176 init_sec_ace(&ace
, &sid
, SEC_ACE_TYPE_ACCESS_DENIED
, 0, 0);
4177 status
= security_descriptor_dacl_add(*ppdesc
, &ace
);
4178 if (!NT_STATUS_IS_OK(status
)) {
4179 DEBUG(1,("failed to add MS NFS style ACE\n"));
4183 /* MS NFS style uid */
4184 sid_compose(&sid
, &global_sid_Unix_NFS_Users
, fsp
->fsp_name
->st
.st_ex_uid
);
4185 init_sec_ace(&ace
, &sid
, SEC_ACE_TYPE_ACCESS_DENIED
, 0, 0);
4186 status
= security_descriptor_dacl_add(*ppdesc
, &ace
);
4187 if (!NT_STATUS_IS_OK(status
)) {
4188 DEBUG(1,("failed to add MS NFS style ACE\n"));
4192 /* MS NFS style gid */
4193 sid_compose(&sid
, &global_sid_Unix_NFS_Groups
, fsp
->fsp_name
->st
.st_ex_gid
);
4194 init_sec_ace(&ace
, &sid
, SEC_ACE_TYPE_ACCESS_DENIED
, 0, 0);
4195 status
= security_descriptor_dacl_add(*ppdesc
, &ace
);
4196 if (!NT_STATUS_IS_OK(status
)) {
4197 DEBUG(1,("failed to add MS NFS style ACE\n"));
4201 return NT_STATUS_OK
;
4204 static NTSTATUS
fruit_fset_nt_acl(vfs_handle_struct
*handle
,
4206 uint32_t security_info_sent
,
4207 const struct security_descriptor
*orig_psd
)
4211 mode_t ms_nfs_mode
= 0;
4213 struct security_descriptor
*psd
= NULL
;
4214 uint32_t orig_num_aces
= 0;
4216 if (orig_psd
->dacl
!= NULL
) {
4217 orig_num_aces
= orig_psd
->dacl
->num_aces
;
4220 psd
= security_descriptor_copy(talloc_tos(), orig_psd
);
4222 return NT_STATUS_NO_MEMORY
;
4225 DBG_DEBUG("fruit_fset_nt_acl: %s\n", fsp_str_dbg(fsp
));
4227 status
= check_ms_nfs(handle
, fsp
, psd
, &ms_nfs_mode
, &do_chmod
);
4228 if (!NT_STATUS_IS_OK(status
)) {
4229 DEBUG(1, ("fruit_fset_nt_acl: check_ms_nfs failed%s\n", fsp_str_dbg(fsp
)));
4235 * If only ms_nfs ACE entries were sent, ensure we set the DACL
4236 * sent/present flags correctly now we've removed them.
4239 if (orig_num_aces
!= 0) {
4241 * Are there any ACE's left ?
4243 if (psd
->dacl
->num_aces
== 0) {
4244 /* No - clear the DACL sent/present flags. */
4245 security_info_sent
&= ~SECINFO_DACL
;
4246 psd
->type
&= ~SEC_DESC_DACL_PRESENT
;
4250 status
= SMB_VFS_NEXT_FSET_NT_ACL(handle
, fsp
, security_info_sent
, psd
);
4251 if (!NT_STATUS_IS_OK(status
)) {
4252 DEBUG(1, ("fruit_fset_nt_acl: SMB_VFS_NEXT_FSET_NT_ACL failed%s\n", fsp_str_dbg(fsp
)));
4258 result
= SMB_VFS_FCHMOD(fsp
, ms_nfs_mode
);
4260 DBG_WARNING("%s, result: %d, %04o error %s\n",
4263 (unsigned)ms_nfs_mode
,
4265 status
= map_nt_error_from_unix(errno
);
4272 return NT_STATUS_OK
;
4275 static struct vfs_offload_ctx
*fruit_offload_ctx
;
4277 struct fruit_offload_read_state
{
4278 struct vfs_handle_struct
*handle
;
4279 struct tevent_context
*ev
;
4285 static void fruit_offload_read_done(struct tevent_req
*subreq
);
4287 static struct tevent_req
*fruit_offload_read_send(
4288 TALLOC_CTX
*mem_ctx
,
4289 struct tevent_context
*ev
,
4290 struct vfs_handle_struct
*handle
,
4297 struct tevent_req
*req
= NULL
;
4298 struct tevent_req
*subreq
= NULL
;
4299 struct fruit_offload_read_state
*state
= NULL
;
4301 req
= tevent_req_create(mem_ctx
, &state
,
4302 struct fruit_offload_read_state
);
4306 *state
= (struct fruit_offload_read_state
) {
4313 subreq
= SMB_VFS_NEXT_OFFLOAD_READ_SEND(mem_ctx
, ev
, handle
, fsp
,
4314 fsctl
, ttl
, offset
, to_copy
);
4315 if (tevent_req_nomem(subreq
, req
)) {
4316 return tevent_req_post(req
, ev
);
4318 tevent_req_set_callback(subreq
, fruit_offload_read_done
, req
);
4322 static void fruit_offload_read_done(struct tevent_req
*subreq
)
4324 struct tevent_req
*req
= tevent_req_callback_data(
4325 subreq
, struct tevent_req
);
4326 struct fruit_offload_read_state
*state
= tevent_req_data(
4327 req
, struct fruit_offload_read_state
);
4330 status
= SMB_VFS_NEXT_OFFLOAD_READ_RECV(subreq
,
4334 TALLOC_FREE(subreq
);
4335 if (tevent_req_nterror(req
, status
)) {
4339 if (state
->fsctl
!= FSCTL_SRV_REQUEST_RESUME_KEY
) {
4340 tevent_req_done(req
);
4344 status
= vfs_offload_token_ctx_init(state
->fsp
->conn
->sconn
->client
,
4345 &fruit_offload_ctx
);
4346 if (tevent_req_nterror(req
, status
)) {
4350 status
= vfs_offload_token_db_store_fsp(fruit_offload_ctx
,
4353 if (tevent_req_nterror(req
, status
)) {
4357 tevent_req_done(req
);
4361 static NTSTATUS
fruit_offload_read_recv(struct tevent_req
*req
,
4362 struct vfs_handle_struct
*handle
,
4363 TALLOC_CTX
*mem_ctx
,
4366 struct fruit_offload_read_state
*state
= tevent_req_data(
4367 req
, struct fruit_offload_read_state
);
4370 if (tevent_req_is_nterror(req
, &status
)) {
4371 tevent_req_received(req
);
4375 token
->length
= state
->token
.length
;
4376 token
->data
= talloc_move(mem_ctx
, &state
->token
.data
);
4378 tevent_req_received(req
);
4379 return NT_STATUS_OK
;
4382 struct fruit_offload_write_state
{
4383 struct vfs_handle_struct
*handle
;
4385 struct files_struct
*src_fsp
;
4386 struct files_struct
*dst_fsp
;
4390 static void fruit_offload_write_done(struct tevent_req
*subreq
);
4391 static struct tevent_req
*fruit_offload_write_send(struct vfs_handle_struct
*handle
,
4392 TALLOC_CTX
*mem_ctx
,
4393 struct tevent_context
*ev
,
4396 off_t transfer_offset
,
4397 struct files_struct
*dest_fsp
,
4401 struct tevent_req
*req
, *subreq
;
4402 struct fruit_offload_write_state
*state
;
4404 struct fruit_config_data
*config
;
4405 off_t src_off
= transfer_offset
;
4406 files_struct
*src_fsp
= NULL
;
4407 off_t to_copy
= num
;
4408 bool copyfile_enabled
= false;
4410 DEBUG(10,("soff: %ju, doff: %ju, len: %ju\n",
4411 (uintmax_t)src_off
, (uintmax_t)dest_off
, (uintmax_t)num
));
4413 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
4414 struct fruit_config_data
,
4417 req
= tevent_req_create(mem_ctx
, &state
,
4418 struct fruit_offload_write_state
);
4422 state
->handle
= handle
;
4423 state
->dst_fsp
= dest_fsp
;
4426 case FSCTL_SRV_COPYCHUNK
:
4427 case FSCTL_SRV_COPYCHUNK_WRITE
:
4428 copyfile_enabled
= config
->copyfile_enabled
;
4435 * Check if this a OS X copyfile style copychunk request with
4436 * a requested chunk count of 0 that was translated to a
4437 * offload_write_send VFS call overloading the parameters src_off
4438 * = dest_off = num = 0.
4440 if (copyfile_enabled
&& num
== 0 && src_off
== 0 && dest_off
== 0) {
4441 status
= vfs_offload_token_db_fetch_fsp(
4442 fruit_offload_ctx
, token
, &src_fsp
);
4443 if (tevent_req_nterror(req
, status
)) {
4444 return tevent_req_post(req
, ev
);
4446 state
->src_fsp
= src_fsp
;
4448 status
= vfs_stat_fsp(src_fsp
);
4449 if (tevent_req_nterror(req
, status
)) {
4450 return tevent_req_post(req
, ev
);
4453 to_copy
= src_fsp
->fsp_name
->st
.st_ex_size
;
4454 state
->is_copyfile
= true;
4457 subreq
= SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle
,
4466 if (tevent_req_nomem(subreq
, req
)) {
4467 return tevent_req_post(req
, ev
);
4470 tevent_req_set_callback(subreq
, fruit_offload_write_done
, req
);
4474 static void fruit_offload_write_done(struct tevent_req
*subreq
)
4476 struct tevent_req
*req
= tevent_req_callback_data(
4477 subreq
, struct tevent_req
);
4478 struct fruit_offload_write_state
*state
= tevent_req_data(
4479 req
, struct fruit_offload_write_state
);
4481 unsigned int num_streams
= 0;
4482 struct stream_struct
*streams
= NULL
;
4484 struct smb_filename
*src_fname_tmp
= NULL
;
4485 struct smb_filename
*dst_fname_tmp
= NULL
;
4487 status
= SMB_VFS_NEXT_OFFLOAD_WRITE_RECV(state
->handle
,
4490 TALLOC_FREE(subreq
);
4491 if (tevent_req_nterror(req
, status
)) {
4495 if (!state
->is_copyfile
) {
4496 tevent_req_done(req
);
4501 * Now copy all remaining streams. We know the share supports
4502 * streams, because we're in vfs_fruit. We don't do this async
4503 * because streams are few and small.
4505 status
= vfs_streaminfo(state
->handle
->conn
, state
->src_fsp
,
4506 state
->src_fsp
->fsp_name
,
4507 req
, &num_streams
, &streams
);
4508 if (tevent_req_nterror(req
, status
)) {
4512 if (num_streams
== 1) {
4513 /* There is always one stream, ::$DATA. */
4514 tevent_req_done(req
);
4518 for (i
= 0; i
< num_streams
; i
++) {
4519 DEBUG(10, ("%s: stream: '%s'/%zu\n",
4520 __func__
, streams
[i
].name
, (size_t)streams
[i
].size
));
4522 src_fname_tmp
= synthetic_smb_fname(
4524 state
->src_fsp
->fsp_name
->base_name
,
4527 state
->src_fsp
->fsp_name
->twrp
,
4528 state
->src_fsp
->fsp_name
->flags
);
4529 if (tevent_req_nomem(src_fname_tmp
, req
)) {
4533 if (is_ntfs_default_stream_smb_fname(src_fname_tmp
)) {
4534 TALLOC_FREE(src_fname_tmp
);
4538 dst_fname_tmp
= synthetic_smb_fname(
4540 state
->dst_fsp
->fsp_name
->base_name
,
4543 state
->dst_fsp
->fsp_name
->twrp
,
4544 state
->dst_fsp
->fsp_name
->flags
);
4545 if (tevent_req_nomem(dst_fname_tmp
, req
)) {
4546 TALLOC_FREE(src_fname_tmp
);
4550 status
= copy_file(req
,
4551 state
->handle
->conn
,
4554 OPENX_FILE_CREATE_IF_NOT_EXIST
,
4556 if (!NT_STATUS_IS_OK(status
)) {
4557 DEBUG(1, ("%s: copy %s to %s failed: %s\n", __func__
,
4558 smb_fname_str_dbg(src_fname_tmp
),
4559 smb_fname_str_dbg(dst_fname_tmp
),
4560 nt_errstr(status
)));
4561 TALLOC_FREE(src_fname_tmp
);
4562 TALLOC_FREE(dst_fname_tmp
);
4563 tevent_req_nterror(req
, status
);
4567 TALLOC_FREE(src_fname_tmp
);
4568 TALLOC_FREE(dst_fname_tmp
);
4571 TALLOC_FREE(streams
);
4572 TALLOC_FREE(src_fname_tmp
);
4573 TALLOC_FREE(dst_fname_tmp
);
4574 tevent_req_done(req
);
4577 static NTSTATUS
fruit_offload_write_recv(struct vfs_handle_struct
*handle
,
4578 struct tevent_req
*req
,
4581 struct fruit_offload_write_state
*state
= tevent_req_data(
4582 req
, struct fruit_offload_write_state
);
4585 if (tevent_req_is_nterror(req
, &status
)) {
4586 DEBUG(1, ("server side copy chunk failed: %s\n",
4587 nt_errstr(status
)));
4589 tevent_req_received(req
);
4593 *copied
= state
->copied
;
4594 tevent_req_received(req
);
4596 return NT_STATUS_OK
;
4599 static char *fruit_get_bandsize_line(char **lines
, int numlines
)
4602 static bool re_initialized
= false;
4606 if (!re_initialized
) {
4607 ret
= regcomp(&re
, "^[[:blank:]]*<key>band-size</key>$", 0);
4611 re_initialized
= true;
4614 for (i
= 0; i
< numlines
; i
++) {
4615 regmatch_t matches
[1];
4617 ret
= regexec(&re
, lines
[i
], 1, matches
, 0);
4620 * Check if the match was on the last line, sa we want
4621 * the subsequent line.
4623 if (i
+ 1 == numlines
) {
4626 return lines
[i
+ 1];
4628 if (ret
!= REG_NOMATCH
) {
4636 static bool fruit_get_bandsize_from_line(char *line
, size_t *_band_size
)
4639 static bool re_initialized
= false;
4640 regmatch_t matches
[2];
4645 if (!re_initialized
) {
4648 "<integer>\\([[:digit:]]*\\)</integer>$",
4653 re_initialized
= true;
4656 ret
= regexec(&re
, line
, 2, matches
, 0);
4658 DBG_ERR("regex failed [%s]\n", line
);
4662 line
[matches
[1].rm_eo
] = '\0';
4664 ok
= conv_str_u64(&line
[matches
[1].rm_so
], &band_size
);
4668 *_band_size
= (size_t)band_size
;
4673 * This reads and parses an Info.plist from a TM sparsebundle looking for the
4674 * "band-size" key and value.
4676 static bool fruit_get_bandsize(vfs_handle_struct
*handle
,
4680 #define INFO_PLIST_MAX_SIZE 64*1024
4682 struct smb_filename
*smb_fname
= NULL
;
4683 files_struct
*fsp
= NULL
;
4684 uint8_t *file_data
= NULL
;
4685 char **lines
= NULL
;
4686 char *band_size_line
= NULL
;
4687 size_t plist_file_size
;
4694 plist
= talloc_asprintf(talloc_tos(),
4696 handle
->conn
->connectpath
,
4698 if (plist
== NULL
) {
4703 smb_fname
= synthetic_smb_fname(talloc_tos(),
4709 if (smb_fname
== NULL
) {
4714 ret
= SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
4716 DBG_INFO("Ignoring Sparsebundle without Info.plist [%s]\n", dir
);
4721 plist_file_size
= smb_fname
->st
.st_ex_size
;
4723 if (plist_file_size
> INFO_PLIST_MAX_SIZE
) {
4724 DBG_INFO("%s is too large, ignoring\n", plist
);
4729 status
= SMB_VFS_NEXT_CREATE_FILE(
4732 smb_fname
, /* fname */
4733 FILE_GENERIC_READ
, /* access_mask */
4734 FILE_SHARE_READ
| FILE_SHARE_WRITE
, /* share_access */
4735 FILE_OPEN
, /* create_disposition */
4736 0, /* create_options */
4737 0, /* file_attributes */
4738 INTERNAL_OPEN_ONLY
, /* oplock_request */
4740 0, /* allocation_size */
4741 0, /* private_flags */
4746 NULL
, NULL
); /* create context */
4747 if (!NT_STATUS_IS_OK(status
)) {
4748 DBG_INFO("Opening [%s] failed [%s]\n",
4749 smb_fname_str_dbg(smb_fname
), nt_errstr(status
));
4754 file_data
= talloc_zero_array(talloc_tos(),
4756 plist_file_size
+ 1);
4757 if (file_data
== NULL
) {
4762 nread
= SMB_VFS_NEXT_PREAD(handle
, fsp
, file_data
, plist_file_size
, 0);
4763 if (nread
!= plist_file_size
) {
4764 DBG_ERR("Short read on [%s]: %zu/%zd\n",
4765 fsp_str_dbg(fsp
), nread
, plist_file_size
);
4771 status
= close_file(NULL
, fsp
, NORMAL_CLOSE
);
4773 if (!NT_STATUS_IS_OK(status
)) {
4774 DBG_ERR("close_file failed: %s\n", nt_errstr(status
));
4779 lines
= file_lines_parse((char *)file_data
,
4783 if (lines
== NULL
) {
4788 band_size_line
= fruit_get_bandsize_line(lines
, numlines
);
4789 if (band_size_line
== NULL
) {
4790 DBG_ERR("Didn't find band-size key in [%s]\n",
4791 smb_fname_str_dbg(smb_fname
));
4796 ok
= fruit_get_bandsize_from_line(band_size_line
, band_size
);
4798 DBG_ERR("fruit_get_bandsize_from_line failed\n");
4802 DBG_DEBUG("Parsed band-size [%zu] for [%s]\n", *band_size
, plist
);
4806 status
= close_file(NULL
, fsp
, NORMAL_CLOSE
);
4807 if (!NT_STATUS_IS_OK(status
)) {
4808 DBG_ERR("close_file failed: %s\n", nt_errstr(status
));
4813 TALLOC_FREE(smb_fname
);
4814 TALLOC_FREE(file_data
);
4819 struct fruit_disk_free_state
{
4823 static bool fruit_get_num_bands(vfs_handle_struct
*handle
,
4828 struct smb_filename
*bands_dir
= NULL
;
4829 struct smb_Dir
*dir_hnd
= NULL
;
4830 const char *dname
= NULL
;
4831 char *talloced
= NULL
;
4835 path
= talloc_asprintf(talloc_tos(),
4837 handle
->conn
->connectpath
,
4843 bands_dir
= synthetic_smb_fname(talloc_tos(),
4850 if (bands_dir
== NULL
) {
4854 dir_hnd
= OpenDir(talloc_tos(), handle
->conn
, bands_dir
, NULL
, 0);
4855 if (dir_hnd
== NULL
) {
4856 TALLOC_FREE(bands_dir
);
4862 while ((dname
= ReadDirName(dir_hnd
, &offset
, NULL
, &talloced
))
4865 if (ISDOT(dname
) || ISDOTDOT(dname
)) {
4870 TALLOC_FREE(dir_hnd
);
4872 DBG_DEBUG("%zu bands in [%s]\n", nbands
, smb_fname_str_dbg(bands_dir
));
4874 TALLOC_FREE(bands_dir
);
4880 static bool fruit_tmsize_do_dirent(vfs_handle_struct
*handle
,
4881 struct fruit_disk_free_state
*state
,
4886 size_t sparsebundle_strlen
= strlen("sparsebundle");
4887 size_t bandsize
= 0;
4891 p
= strstr(name
, "sparsebundle");
4896 if (p
[sparsebundle_strlen
] != '\0') {
4900 DBG_DEBUG("Processing sparsebundle [%s]\n", name
);
4902 ok
= fruit_get_bandsize(handle
, name
, &bandsize
);
4905 * Beware of race conditions: this may be an uninitialized
4906 * Info.plist that a client is just creating. We don't want let
4907 * this to trigger complete failure.
4909 DBG_ERR("Processing sparsebundle [%s] failed\n", name
);
4913 ok
= fruit_get_num_bands(handle
, name
, &nbands
);
4916 * Beware of race conditions: this may be a backup sparsebundle
4917 * in an early stage lacking a bands subdirectory. We don't want
4918 * let this to trigger complete failure.
4920 DBG_ERR("Processing sparsebundle [%s] failed\n", name
);
4925 * Arithmetic on 32-bit systems may cause overflow, depending on
4926 * size_t precision. First we check its unlikely, then we
4927 * force the precision into target off_t, then we check that
4928 * the total did not overflow either.
4930 if (bandsize
> SIZE_MAX
/nbands
) {
4931 DBG_ERR("tmsize potential overflow: bandsize [%zu] nbands [%zu]\n",
4935 tm_size
= (off_t
)bandsize
* (off_t
)nbands
;
4937 if (state
->total_size
+ tm_size
< state
->total_size
) {
4938 DBG_ERR("tm total size overflow: bandsize [%zu] nbands [%zu]\n",
4943 state
->total_size
+= tm_size
;
4945 DBG_DEBUG("[%s] tm_size [%jd] total_size [%jd]\n",
4946 name
, (intmax_t)tm_size
, (intmax_t)state
->total_size
);
4952 * Calculate used size of a TimeMachine volume
4954 * This assumes that the volume is used only for TimeMachine.
4956 * - readdir(basedir of share), then
4957 * - for every element that matches regex "^\(.*\)\.sparsebundle$" :
4958 * - parse "\1.sparsebundle/Info.plist" and read the band-size XML key
4959 * - count band files in "\1.sparsebundle/bands/"
4960 * - calculate used size of all bands: band_count * band_size
4962 static uint64_t fruit_disk_free(vfs_handle_struct
*handle
,
4963 const struct smb_filename
*smb_fname
,
4968 struct fruit_config_data
*config
= NULL
;
4969 struct fruit_disk_free_state state
= {0};
4970 struct smb_Dir
*dir_hnd
= NULL
;
4971 const char *dname
= NULL
;
4972 char *talloced
= NULL
;
4978 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
4979 struct fruit_config_data
,
4982 if (!config
->time_machine
||
4983 config
->time_machine_max_size
== 0)
4985 return SMB_VFS_NEXT_DISK_FREE(handle
,
4992 dir_hnd
= OpenDir(talloc_tos(), handle
->conn
, smb_fname
, NULL
, 0);
4993 if (dir_hnd
== NULL
) {
4997 while ((dname
= ReadDirName(dir_hnd
, &offset
, NULL
, &talloced
))
5000 ok
= fruit_tmsize_do_dirent(handle
, &state
, dname
);
5002 TALLOC_FREE(talloced
);
5003 TALLOC_FREE(dir_hnd
);
5006 TALLOC_FREE(talloced
);
5009 TALLOC_FREE(dir_hnd
);
5011 dsize
= config
->time_machine_max_size
/ 512;
5012 dfree
= dsize
- (state
.total_size
/ 512);
5013 if (dfree
> dsize
) {
5023 static uint64_t fruit_fs_file_id(struct vfs_handle_struct
*handle
,
5024 const SMB_STRUCT_STAT
*psbuf
)
5026 struct fruit_config_data
*config
= NULL
;
5028 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
5029 struct fruit_config_data
,
5032 if (global_fruit_config
.nego_aapl
&&
5033 config
->aapl_zero_file_id
)
5038 return SMB_VFS_NEXT_FS_FILE_ID(handle
, psbuf
);
5041 static struct vfs_fn_pointers vfs_fruit_fns
= {
5042 .connect_fn
= fruit_connect
,
5043 .disk_free_fn
= fruit_disk_free
,
5045 /* File operations */
5046 .chmod_fn
= fruit_chmod
,
5047 .unlinkat_fn
= fruit_unlinkat
,
5048 .renameat_fn
= fruit_renameat
,
5049 .openat_fn
= fruit_openat
,
5050 .close_fn
= fruit_close
,
5051 .pread_fn
= fruit_pread
,
5052 .pwrite_fn
= fruit_pwrite
,
5053 .pread_send_fn
= fruit_pread_send
,
5054 .pread_recv_fn
= fruit_pread_recv
,
5055 .pwrite_send_fn
= fruit_pwrite_send
,
5056 .pwrite_recv_fn
= fruit_pwrite_recv
,
5057 .stat_fn
= fruit_stat
,
5058 .lstat_fn
= fruit_lstat
,
5059 .fstat_fn
= fruit_fstat
,
5060 .streaminfo_fn
= fruit_streaminfo
,
5061 .ntimes_fn
= fruit_ntimes
,
5062 .ftruncate_fn
= fruit_ftruncate
,
5063 .fallocate_fn
= fruit_fallocate
,
5064 .create_file_fn
= fruit_create_file
,
5065 .readdir_attr_fn
= fruit_readdir_attr
,
5066 .offload_read_send_fn
= fruit_offload_read_send
,
5067 .offload_read_recv_fn
= fruit_offload_read_recv
,
5068 .offload_write_send_fn
= fruit_offload_write_send
,
5069 .offload_write_recv_fn
= fruit_offload_write_recv
,
5070 .fs_file_id_fn
= fruit_fs_file_id
,
5072 /* NT ACL operations */
5073 .fget_nt_acl_fn
= fruit_fget_nt_acl
,
5074 .fset_nt_acl_fn
= fruit_fset_nt_acl
,
5078 NTSTATUS
vfs_fruit_init(TALLOC_CTX
*ctx
)
5080 NTSTATUS ret
= smb_register_vfs(SMB_VFS_INTERFACE_VERSION
, "fruit",
5082 if (!NT_STATUS_IS_OK(ret
)) {
5086 vfs_fruit_debug_level
= debug_add_class("fruit");
5087 if (vfs_fruit_debug_level
== -1) {
5088 vfs_fruit_debug_level
= DBGC_VFS
;
5089 DEBUG(0, ("%s: Couldn't register custom debugging class!\n",
5092 DEBUG(10, ("%s: Debug class number of '%s': %d\n",
5093 "vfs_fruit_init","fruit",vfs_fruit_debug_level
));