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 convert_adouble
;
135 bool wipe_intentionally_left_blank_rfork
;
136 bool delete_empty_adfiles
;
139 * Additional options, all enabled by default,
140 * possibly useful for analyzing performance. The associated
141 * operations with each of them may be expensive, so having
142 * the chance to disable them individually gives a chance
143 * tweaking the setup for the particular usecase.
145 bool readdir_attr_rsize
;
146 bool readdir_attr_finder_info
;
147 bool readdir_attr_max_access
;
148 /* Recursion guard. Will go away when we have STATX. */
149 bool in_openat_pathref_fsp
;
152 static const struct enum_list fruit_rsrc
[] = {
153 {FRUIT_RSRC_STREAM
, "stream"}, /* pass on to vfs_streams_xattr */
154 {FRUIT_RSRC_ADFILE
, "file"}, /* ._ AppleDouble file */
155 {FRUIT_RSRC_XATTR
, "xattr"}, /* Netatalk compatible xattr (ZFS only) */
159 static const struct enum_list fruit_meta
[] = {
160 {FRUIT_META_STREAM
, "stream"}, /* pass on to vfs_streams_xattr */
161 {FRUIT_META_NETATALK
, "netatalk"}, /* Netatalk compatible xattr */
165 static const struct enum_list fruit_locking
[] = {
166 {FRUIT_LOCKING_NETATALK
, "netatalk"}, /* synchronize locks with Netatalk */
167 {FRUIT_LOCKING_NONE
, "none"},
171 static const struct enum_list fruit_encoding
[] = {
172 {FRUIT_ENC_NATIVE
, "native"}, /* map unicode private chars to ASCII */
173 {FRUIT_ENC_PRIVATE
, "private"}, /* keep unicode private chars */
178 vfs_handle_struct
*handle
;
179 files_struct
*fsp
; /* backlink to itself */
181 /* tcon config handle */
182 struct fruit_config_data
*config
;
184 /* Backend fsp for AppleDouble file, can be NULL */
185 files_struct
*ad_fsp
;
186 /* link from adouble_open_from_base_fsp() to fio */
187 struct fio
*real_fio
;
189 /* Denote stream type, meta or rsrc */
193 * AFP_AfpInfo stream created, but not written yet, thus still a fake
194 * pipe fd. This is set to true in fruit_open_meta if there was no
195 * existing stream but the caller requested O_CREAT. It is later set to
196 * false when we get a write on the stream that then does open and
204 /*****************************************************************************
206 *****************************************************************************/
208 static struct adouble
*ad_get_meta_fsp(TALLOC_CTX
*ctx
,
209 vfs_handle_struct
*handle
,
210 const struct smb_filename
*smb_fname
)
213 struct adouble
*ad
= NULL
;
214 struct smb_filename
*smb_fname_cp
= NULL
;
215 struct fruit_config_data
*config
= NULL
;
217 if (smb_fname
->fsp
!= NULL
) {
218 return ad_get(ctx
, handle
, smb_fname
, ADOUBLE_META
);
221 SMB_VFS_HANDLE_GET_DATA(handle
,
223 struct fruit_config_data
,
226 if (config
->in_openat_pathref_fsp
) {
230 smb_fname_cp
= cp_smb_filename(ctx
,
232 if (smb_fname_cp
== NULL
) {
235 TALLOC_FREE(smb_fname_cp
->stream_name
);
236 config
->in_openat_pathref_fsp
= true;
237 status
= openat_pathref_fsp(handle
->conn
->cwd_fsp
,
239 config
->in_openat_pathref_fsp
= false;
240 if (!NT_STATUS_IS_OK(status
)) {
241 TALLOC_FREE(smb_fname_cp
);
245 ad
= ad_get(ctx
, handle
, smb_fname_cp
, ADOUBLE_META
);
246 TALLOC_FREE(smb_fname_cp
);
250 static struct fio
*fruit_get_complete_fio(vfs_handle_struct
*handle
,
253 struct fio
*fio
= (struct fio
*)VFS_FETCH_FSP_EXTENSION(handle
, fsp
);
259 if (fio
->real_fio
!= NULL
) {
261 * This is an fsp from adouble_open_from_base_fsp()
262 * we should just pass this to the next
272 * Initialize config struct from our smb.conf config parameters
274 static int init_fruit_config(vfs_handle_struct
*handle
)
276 struct fruit_config_data
*config
;
278 const char *tm_size_str
= NULL
;
280 config
= talloc_zero(handle
->conn
, struct fruit_config_data
);
282 DEBUG(1, ("talloc_zero() failed\n"));
287 enumval
= lp_parm_enum(SNUM(handle
->conn
), FRUIT_PARAM_TYPE_NAME
,
288 "resource", fruit_rsrc
, FRUIT_RSRC_ADFILE
);
290 DEBUG(1, ("value for %s: resource type unknown\n",
291 FRUIT_PARAM_TYPE_NAME
));
294 config
->rsrc
= (enum fruit_rsrc
)enumval
;
296 enumval
= lp_parm_enum(SNUM(handle
->conn
), FRUIT_PARAM_TYPE_NAME
,
297 "metadata", fruit_meta
, FRUIT_META_NETATALK
);
299 DEBUG(1, ("value for %s: metadata type unknown\n",
300 FRUIT_PARAM_TYPE_NAME
));
303 config
->meta
= (enum fruit_meta
)enumval
;
305 enumval
= lp_parm_enum(SNUM(handle
->conn
), FRUIT_PARAM_TYPE_NAME
,
306 "locking", fruit_locking
, FRUIT_LOCKING_NONE
);
308 DEBUG(1, ("value for %s: locking type unknown\n",
309 FRUIT_PARAM_TYPE_NAME
));
312 config
->locking
= (enum fruit_locking
)enumval
;
314 enumval
= lp_parm_enum(SNUM(handle
->conn
), FRUIT_PARAM_TYPE_NAME
,
315 "encoding", fruit_encoding
, FRUIT_ENC_PRIVATE
);
317 DEBUG(1, ("value for %s: encoding type unknown\n",
318 FRUIT_PARAM_TYPE_NAME
));
321 config
->encoding
= (enum fruit_encoding
)enumval
;
323 if (config
->rsrc
== FRUIT_RSRC_ADFILE
) {
324 config
->veto_appledouble
= lp_parm_bool(SNUM(handle
->conn
),
325 FRUIT_PARAM_TYPE_NAME
,
330 config
->use_aapl
= lp_parm_bool(
331 -1, FRUIT_PARAM_TYPE_NAME
, "aapl", true);
333 config
->time_machine
= lp_parm_bool(
334 SNUM(handle
->conn
), FRUIT_PARAM_TYPE_NAME
, "time machine", false);
336 config
->unix_info_enabled
= lp_parm_bool(
337 -1, FRUIT_PARAM_TYPE_NAME
, "nfs_aces", true);
339 config
->use_copyfile
= lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME
,
342 config
->posix_rename
= lp_parm_bool(
343 SNUM(handle
->conn
), FRUIT_PARAM_TYPE_NAME
, "posix_rename", true);
345 config
->aapl_zero_file_id
=
346 lp_parm_bool(SNUM(handle
->conn
), FRUIT_PARAM_TYPE_NAME
,
347 "zero_file_id", true);
349 config
->readdir_attr_rsize
= lp_parm_bool(
350 SNUM(handle
->conn
), "readdir_attr", "aapl_rsize", true);
352 config
->readdir_attr_finder_info
= lp_parm_bool(
353 SNUM(handle
->conn
), "readdir_attr", "aapl_finder_info", true);
355 config
->readdir_attr_max_access
= lp_parm_bool(
356 SNUM(handle
->conn
), "readdir_attr", "aapl_max_access", true);
358 config
->model
= lp_parm_const_string(
359 -1, FRUIT_PARAM_TYPE_NAME
, "model", "MacSamba");
361 tm_size_str
= lp_parm_const_string(
362 SNUM(handle
->conn
), FRUIT_PARAM_TYPE_NAME
,
363 "time machine max size", NULL
);
364 if (tm_size_str
!= NULL
) {
365 config
->time_machine_max_size
= conv_str_size(tm_size_str
);
368 config
->convert_adouble
= lp_parm_bool(
369 SNUM(handle
->conn
), FRUIT_PARAM_TYPE_NAME
,
370 "convert_adouble", true);
372 config
->wipe_intentionally_left_blank_rfork
= lp_parm_bool(
373 SNUM(handle
->conn
), FRUIT_PARAM_TYPE_NAME
,
374 "wipe_intentionally_left_blank_rfork", false);
376 config
->delete_empty_adfiles
= lp_parm_bool(
377 SNUM(handle
->conn
), FRUIT_PARAM_TYPE_NAME
,
378 "delete_empty_adfiles", false);
380 SMB_VFS_HANDLE_SET_DATA(handle
, config
,
381 NULL
, struct fruit_config_data
,
387 static bool add_fruit_stream(TALLOC_CTX
*mem_ctx
, unsigned int *num_streams
,
388 struct stream_struct
**streams
,
389 const char *name
, off_t size
,
392 struct stream_struct
*tmp
;
394 tmp
= talloc_realloc(mem_ctx
, *streams
, struct stream_struct
,
400 tmp
[*num_streams
].name
= talloc_asprintf(tmp
, "%s:$DATA", name
);
401 if (tmp
[*num_streams
].name
== NULL
) {
405 tmp
[*num_streams
].size
= size
;
406 tmp
[*num_streams
].alloc_size
= alloc_size
;
413 static bool filter_empty_rsrc_stream(unsigned int *num_streams
,
414 struct stream_struct
**streams
)
416 struct stream_struct
*tmp
= *streams
;
419 if (*num_streams
== 0) {
423 for (i
= 0; i
< *num_streams
; i
++) {
424 if (strequal_m(tmp
[i
].name
, AFPRESOURCE_STREAM
)) {
429 if (i
== *num_streams
) {
433 if (tmp
[i
].size
> 0) {
437 TALLOC_FREE(tmp
[i
].name
);
438 ARRAY_DEL_ELEMENT(tmp
, i
, *num_streams
);
443 static bool del_fruit_stream(TALLOC_CTX
*mem_ctx
, unsigned int *num_streams
,
444 struct stream_struct
**streams
,
447 struct stream_struct
*tmp
= *streams
;
450 if (*num_streams
== 0) {
454 for (i
= 0; i
< *num_streams
; i
++) {
455 if (strequal_m(tmp
[i
].name
, name
)) {
460 if (i
== *num_streams
) {
464 TALLOC_FREE(tmp
[i
].name
);
465 ARRAY_DEL_ELEMENT(tmp
, i
, *num_streams
);
470 static bool ad_empty_finderinfo(const struct adouble
*ad
)
473 char emptybuf
[ADEDLEN_FINDERI
] = {0};
476 fi
= ad_get_entry(ad
, ADEID_FINDERI
);
478 DBG_ERR("Missing FinderInfo in struct adouble [%p]\n", ad
);
482 cmp
= memcmp(emptybuf
, fi
, ADEDLEN_FINDERI
);
486 static bool ai_empty_finderinfo(const AfpInfo
*ai
)
489 char emptybuf
[ADEDLEN_FINDERI
] = {0};
491 cmp
= memcmp(emptybuf
, &ai
->afpi_FinderInfo
[0], ADEDLEN_FINDERI
);
496 * Update btime with btime from Netatalk
498 static void update_btime(vfs_handle_struct
*handle
,
499 struct smb_filename
*smb_fname
)
502 struct timespec creation_time
= {0};
504 struct fruit_config_data
*config
= NULL
;
506 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct fruit_config_data
,
509 switch (config
->meta
) {
510 case FRUIT_META_STREAM
:
512 case FRUIT_META_NETATALK
:
516 DBG_ERR("Unexpected meta config [%d]\n", config
->meta
);
520 ad
= ad_get_meta_fsp(talloc_tos(), handle
, smb_fname
);
524 if (ad_getdate(ad
, AD_DATE_UNIX
| AD_DATE_CREATE
, &t
) != 0) {
530 creation_time
.tv_sec
= convert_uint32_t_to_time_t(t
);
531 update_stat_ex_create_time(&smb_fname
->st
, creation_time
);
537 * Map an access mask to a Netatalk single byte byte range lock
539 static off_t
access_to_netatalk_brl(enum apple_fork fork_type
,
540 uint32_t access_mask
)
544 switch (access_mask
) {
546 offset
= AD_FILELOCK_OPEN_RD
;
549 case FILE_WRITE_DATA
:
550 case FILE_APPEND_DATA
:
551 offset
= AD_FILELOCK_OPEN_WR
;
555 offset
= AD_FILELOCK_OPEN_NONE
;
559 if (fork_type
== APPLE_FORK_RSRC
) {
560 if (offset
== AD_FILELOCK_OPEN_NONE
) {
561 offset
= AD_FILELOCK_RSRC_OPEN_NONE
;
571 * Map a deny mode to a Netatalk brl
573 static off_t
denymode_to_netatalk_brl(enum apple_fork fork_type
,
580 offset
= AD_FILELOCK_DENY_RD
;
584 offset
= AD_FILELOCK_DENY_WR
;
588 smb_panic("denymode_to_netatalk_brl: bad deny mode\n");
591 if (fork_type
== APPLE_FORK_RSRC
) {
599 * Call fcntl() with an exclusive F_GETLK request in order to
600 * determine if there's an existing shared lock
602 * @return true if the requested lock was found or any error occurred
603 * false if the lock was not found
605 static bool test_netatalk_lock(files_struct
*fsp
, off_t in_offset
)
608 off_t offset
= in_offset
;
613 result
= SMB_VFS_GETLOCK(fsp
, &offset
, &len
, &type
, &pid
);
614 if (result
== false) {
618 if (type
!= F_UNLCK
) {
625 static NTSTATUS
fruit_check_access(vfs_handle_struct
*handle
,
627 uint32_t access_mask
,
630 NTSTATUS status
= NT_STATUS_OK
;
632 bool share_for_read
= (share_mode
& FILE_SHARE_READ
);
633 bool share_for_write
= (share_mode
& FILE_SHARE_WRITE
);
634 bool netatalk_already_open_for_reading
= false;
635 bool netatalk_already_open_for_writing
= false;
636 bool netatalk_already_open_with_deny_read
= false;
637 bool netatalk_already_open_with_deny_write
= false;
638 struct GUID req_guid
= GUID_random();
640 /* FIXME: hardcoded data fork, add resource fork */
641 enum apple_fork fork_type
= APPLE_FORK_DATA
;
643 DBG_DEBUG("fruit_check_access: %s, am: %s/%s, sm: 0x%x\n",
645 access_mask
& FILE_READ_DATA
? "READ" :"-",
646 access_mask
& FILE_WRITE_DATA
? "WRITE" : "-",
649 if (fsp_get_io_fd(fsp
) == -1) {
653 /* Read NetATalk opens and deny modes on the file. */
654 netatalk_already_open_for_reading
= test_netatalk_lock(fsp
,
655 access_to_netatalk_brl(fork_type
,
658 netatalk_already_open_with_deny_read
= test_netatalk_lock(fsp
,
659 denymode_to_netatalk_brl(fork_type
,
662 netatalk_already_open_for_writing
= test_netatalk_lock(fsp
,
663 access_to_netatalk_brl(fork_type
,
666 netatalk_already_open_with_deny_write
= test_netatalk_lock(fsp
,
667 denymode_to_netatalk_brl(fork_type
,
670 /* If there are any conflicts - sharing violation. */
671 if ((access_mask
& FILE_READ_DATA
) &&
672 netatalk_already_open_with_deny_read
) {
673 return NT_STATUS_SHARING_VIOLATION
;
676 if (!share_for_read
&&
677 netatalk_already_open_for_reading
) {
678 return NT_STATUS_SHARING_VIOLATION
;
681 if ((access_mask
& FILE_WRITE_DATA
) &&
682 netatalk_already_open_with_deny_write
) {
683 return NT_STATUS_SHARING_VIOLATION
;
686 if (!share_for_write
&&
687 netatalk_already_open_for_writing
) {
688 return NT_STATUS_SHARING_VIOLATION
;
691 if (!(access_mask
& FILE_READ_DATA
)) {
693 * Nothing we can do here, we need read access
699 /* Set NetAtalk locks matching our access */
700 if (access_mask
& FILE_READ_DATA
) {
701 off
= access_to_netatalk_brl(fork_type
, FILE_READ_DATA
);
702 req_guid
.time_hi_and_version
= __LINE__
;
707 fsp
->op
->global
->open_persistent_id
,
715 if (!NT_STATUS_IS_OK(status
)) {
720 if (!share_for_read
) {
721 off
= denymode_to_netatalk_brl(fork_type
, DENY_READ
);
722 req_guid
.time_hi_and_version
= __LINE__
;
727 fsp
->op
->global
->open_persistent_id
,
735 if (!NT_STATUS_IS_OK(status
)) {
740 if (access_mask
& FILE_WRITE_DATA
) {
741 off
= access_to_netatalk_brl(fork_type
, FILE_WRITE_DATA
);
742 req_guid
.time_hi_and_version
= __LINE__
;
747 fsp
->op
->global
->open_persistent_id
,
755 if (!NT_STATUS_IS_OK(status
)) {
760 if (!share_for_write
) {
761 off
= denymode_to_netatalk_brl(fork_type
, DENY_WRITE
);
762 req_guid
.time_hi_and_version
= __LINE__
;
767 fsp
->op
->global
->open_persistent_id
,
775 if (!NT_STATUS_IS_OK(status
)) {
783 static NTSTATUS
check_aapl(vfs_handle_struct
*handle
,
784 struct smb_request
*req
,
785 const struct smb2_create_blobs
*in_context_blobs
,
786 struct smb2_create_blobs
*out_context_blobs
)
788 struct fruit_config_data
*config
;
790 struct smb2_create_blob
*aapl
= NULL
;
794 DATA_BLOB blob
= data_blob_talloc(req
, NULL
, 0);
795 uint64_t req_bitmap
, client_caps
;
796 uint64_t server_caps
= SMB2_CRTCTX_AAPL_UNIX_BASED
;
800 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct fruit_config_data
,
801 return NT_STATUS_UNSUCCESSFUL
);
803 if (!config
->use_aapl
804 || in_context_blobs
== NULL
805 || out_context_blobs
== NULL
) {
809 aapl
= smb2_create_blob_find(in_context_blobs
,
810 SMB2_CREATE_TAG_AAPL
);
815 if (aapl
->data
.length
!= 24) {
816 DEBUG(1, ("unexpected AAPL ctxt length: %ju\n",
817 (uintmax_t)aapl
->data
.length
));
818 return NT_STATUS_INVALID_PARAMETER
;
821 cmd
= IVAL(aapl
->data
.data
, 0);
822 if (cmd
!= SMB2_CRTCTX_AAPL_SERVER_QUERY
) {
823 DEBUG(1, ("unsupported AAPL cmd: %d\n", cmd
));
824 return NT_STATUS_INVALID_PARAMETER
;
827 req_bitmap
= BVAL(aapl
->data
.data
, 8);
828 client_caps
= BVAL(aapl
->data
.data
, 16);
830 SIVAL(p
, 0, SMB2_CRTCTX_AAPL_SERVER_QUERY
);
832 SBVAL(p
, 8, req_bitmap
);
833 ok
= data_blob_append(req
, &blob
, p
, 16);
835 return NT_STATUS_UNSUCCESSFUL
;
838 if (req_bitmap
& SMB2_CRTCTX_AAPL_SERVER_CAPS
) {
839 if ((client_caps
& SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR
) &&
840 (handle
->conn
->fs_capabilities
& FILE_NAMED_STREAMS
)) {
841 server_caps
|= SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR
;
842 config
->readdir_attr_enabled
= true;
845 if (config
->use_copyfile
) {
846 server_caps
|= SMB2_CRTCTX_AAPL_SUPPORTS_OSX_COPYFILE
;
847 config
->copyfile_enabled
= true;
851 * The client doesn't set the flag, so we can't check
852 * for it and just set it unconditionally
854 if (config
->unix_info_enabled
) {
855 server_caps
|= SMB2_CRTCTX_AAPL_SUPPORTS_NFS_ACE
;
858 SBVAL(p
, 0, server_caps
);
859 ok
= data_blob_append(req
, &blob
, p
, 8);
861 return NT_STATUS_UNSUCCESSFUL
;
865 if (req_bitmap
& SMB2_CRTCTX_AAPL_VOLUME_CAPS
) {
866 int val
= lp_case_sensitive(SNUM(handle
->conn
));
874 caps
|= SMB2_CRTCTX_AAPL_CASE_SENSITIVE
;
881 if (config
->time_machine
) {
882 caps
|= SMB2_CRTCTX_AAPL_FULL_SYNC
;
887 ok
= data_blob_append(req
, &blob
, p
, 8);
889 return NT_STATUS_UNSUCCESSFUL
;
893 if (req_bitmap
& SMB2_CRTCTX_AAPL_MODEL_INFO
) {
894 ok
= convert_string_talloc(req
,
896 config
->model
, strlen(config
->model
),
899 return NT_STATUS_UNSUCCESSFUL
;
903 SIVAL(p
+ 4, 0, modellen
);
904 ok
= data_blob_append(req
, &blob
, p
, 8);
907 return NT_STATUS_UNSUCCESSFUL
;
910 ok
= data_blob_append(req
, &blob
, model
, modellen
);
913 return NT_STATUS_UNSUCCESSFUL
;
917 status
= smb2_create_blob_add(out_context_blobs
,
919 SMB2_CREATE_TAG_AAPL
,
921 if (NT_STATUS_IS_OK(status
)) {
922 global_fruit_config
.nego_aapl
= true;
928 static bool readdir_attr_meta_finderi_stream(
929 struct vfs_handle_struct
*handle
,
930 const struct smb_filename
*smb_fname
,
933 struct smb_filename
*stream_name
= NULL
;
934 files_struct
*fsp
= NULL
;
938 uint8_t buf
[AFP_INFO_SIZE
];
940 status
= synthetic_pathref(talloc_tos(),
941 handle
->conn
->cwd_fsp
,
942 smb_fname
->base_name
,
948 if (!NT_STATUS_IS_OK(status
)) {
952 status
= SMB_VFS_CREATE_FILE(
953 handle
->conn
, /* conn */
956 stream_name
, /* fname */
957 FILE_READ_DATA
, /* access_mask */
958 (FILE_SHARE_READ
| FILE_SHARE_WRITE
| /* share_access */
960 FILE_OPEN
, /* create_disposition*/
961 0, /* create_options */
962 0, /* file_attributes */
963 INTERNAL_OPEN_ONLY
, /* oplock_request */
965 0, /* allocation_size */
966 0, /* private_flags */
971 NULL
, NULL
); /* create context */
973 TALLOC_FREE(stream_name
);
975 if (!NT_STATUS_IS_OK(status
)) {
979 nread
= SMB_VFS_PREAD(fsp
, &buf
[0], AFP_INFO_SIZE
, 0);
980 if (nread
!= AFP_INFO_SIZE
) {
981 DBG_ERR("short read [%s] [%zd/%d]\n",
982 smb_fname_str_dbg(stream_name
), nread
, AFP_INFO_SIZE
);
987 memcpy(&ai
->afpi_FinderInfo
[0], &buf
[AFP_OFF_FinderInfo
],
994 close_file_free(NULL
, &fsp
, NORMAL_CLOSE
);
1000 static bool readdir_attr_meta_finderi_netatalk(
1001 struct vfs_handle_struct
*handle
,
1002 const struct smb_filename
*smb_fname
,
1005 struct adouble
*ad
= NULL
;
1008 ad
= ad_get_meta_fsp(talloc_tos(), handle
, smb_fname
);
1013 p
= ad_get_entry(ad
, ADEID_FINDERI
);
1015 DBG_ERR("No ADEID_FINDERI for [%s]\n", smb_fname
->base_name
);
1020 memcpy(&ai
->afpi_FinderInfo
[0], p
, AFP_FinderSize
);
1025 static bool readdir_attr_meta_finderi(struct vfs_handle_struct
*handle
,
1026 const struct smb_filename
*smb_fname
,
1027 struct readdir_attr_data
*attr_data
)
1029 struct fruit_config_data
*config
= NULL
;
1030 uint32_t date_added
;
1034 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
1035 struct fruit_config_data
,
1038 switch (config
->meta
) {
1039 case FRUIT_META_NETATALK
:
1040 ok
= readdir_attr_meta_finderi_netatalk(
1041 handle
, smb_fname
, &ai
);
1044 case FRUIT_META_STREAM
:
1045 ok
= readdir_attr_meta_finderi_stream(
1046 handle
, smb_fname
, &ai
);
1050 DBG_ERR("Unexpected meta config [%d]\n", config
->meta
);
1055 /* Don't bother with errors, it's likely ENOENT */
1059 if (S_ISREG(smb_fname
->st
.st_ex_mode
)) {
1061 memcpy(&attr_data
->attr_data
.aapl
.finder_info
[0],
1062 &ai
.afpi_FinderInfo
[0], 4);
1064 /* finder_creator */
1065 memcpy(&attr_data
->attr_data
.aapl
.finder_info
[0] + 4,
1066 &ai
.afpi_FinderInfo
[4], 4);
1070 memcpy(&attr_data
->attr_data
.aapl
.finder_info
[0] + 8,
1071 &ai
.afpi_FinderInfo
[8], 2);
1073 /* finder_ext_flags */
1074 memcpy(&attr_data
->attr_data
.aapl
.finder_info
[0] + 10,
1075 &ai
.afpi_FinderInfo
[24], 2);
1078 date_added
= convert_time_t_to_uint32_t(
1079 smb_fname
->st
.st_ex_btime
.tv_sec
- AD_DATE_DELTA
);
1081 RSIVAL(&attr_data
->attr_data
.aapl
.finder_info
[0], 12, date_added
);
1086 static uint64_t readdir_attr_rfork_size_adouble(
1087 struct vfs_handle_struct
*handle
,
1088 const struct smb_filename
*smb_fname
)
1090 struct adouble
*ad
= NULL
;
1091 uint64_t rfork_size
;
1093 ad
= ad_get(talloc_tos(), handle
, smb_fname
,
1099 rfork_size
= ad_getentrylen(ad
, ADEID_RFORK
);
1105 static uint64_t readdir_attr_rfork_size_stream(
1106 struct vfs_handle_struct
*handle
,
1107 const struct smb_filename
*smb_fname
)
1109 struct smb_filename
*stream_name
= NULL
;
1111 uint64_t rfork_size
;
1113 stream_name
= synthetic_smb_fname(talloc_tos(),
1114 smb_fname
->base_name
,
1115 AFPRESOURCE_STREAM_NAME
,
1119 if (stream_name
== NULL
) {
1123 ret
= SMB_VFS_STAT(handle
->conn
, stream_name
);
1125 TALLOC_FREE(stream_name
);
1129 rfork_size
= stream_name
->st
.st_ex_size
;
1130 TALLOC_FREE(stream_name
);
1135 static uint64_t readdir_attr_rfork_size(struct vfs_handle_struct
*handle
,
1136 const struct smb_filename
*smb_fname
)
1138 struct fruit_config_data
*config
= NULL
;
1139 uint64_t rfork_size
;
1141 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
1142 struct fruit_config_data
,
1145 switch (config
->rsrc
) {
1146 case FRUIT_RSRC_ADFILE
:
1147 rfork_size
= readdir_attr_rfork_size_adouble(handle
,
1151 case FRUIT_RSRC_XATTR
:
1152 case FRUIT_RSRC_STREAM
:
1153 rfork_size
= readdir_attr_rfork_size_stream(handle
,
1158 DBG_ERR("Unexpected rsrc config [%d]\n", config
->rsrc
);
1166 static NTSTATUS
readdir_attr_macmeta(struct vfs_handle_struct
*handle
,
1167 const struct smb_filename
*smb_fname
,
1168 struct readdir_attr_data
*attr_data
)
1170 NTSTATUS status
= NT_STATUS_OK
;
1171 struct fruit_config_data
*config
= NULL
;
1174 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
1175 struct fruit_config_data
,
1176 return NT_STATUS_UNSUCCESSFUL
);
1179 /* Ensure we return a default value in the creation_date field */
1180 RSIVAL(&attr_data
->attr_data
.aapl
.finder_info
, 12, AD_DATE_START
);
1183 * Resource fork length
1186 if (config
->readdir_attr_rsize
) {
1187 uint64_t rfork_size
;
1189 rfork_size
= readdir_attr_rfork_size(handle
, smb_fname
);
1190 attr_data
->attr_data
.aapl
.rfork_size
= rfork_size
;
1197 if (config
->readdir_attr_finder_info
) {
1198 ok
= readdir_attr_meta_finderi(handle
, smb_fname
, attr_data
);
1200 status
= NT_STATUS_INTERNAL_ERROR
;
1207 static NTSTATUS
remove_virtual_nfs_aces(struct security_descriptor
*psd
)
1212 if (psd
->dacl
== NULL
) {
1213 return NT_STATUS_OK
;
1216 for (i
= 0; i
< psd
->dacl
->num_aces
; i
++) {
1217 /* MS NFS style mode/uid/gid */
1218 int cmp
= dom_sid_compare_domain(
1219 &global_sid_Unix_NFS
,
1220 &psd
->dacl
->aces
[i
].trustee
);
1222 /* Normal ACE entry. */
1227 * security_descriptor_dacl_del()
1228 * *must* return NT_STATUS_OK as we know
1229 * we have something to remove.
1232 status
= security_descriptor_dacl_del(psd
,
1233 &psd
->dacl
->aces
[i
].trustee
);
1234 if (!NT_STATUS_IS_OK(status
)) {
1235 DBG_WARNING("failed to remove MS NFS style ACE: %s\n",
1241 * security_descriptor_dacl_del() may delete more
1242 * then one entry subsequent to this one if the
1243 * SID matches, but we only need to ensure that
1244 * we stay looking at the same element in the array.
1248 return NT_STATUS_OK
;
1251 /* Search MS NFS style ACE with UNIX mode */
1252 static NTSTATUS
check_ms_nfs(vfs_handle_struct
*handle
,
1254 struct security_descriptor
*psd
,
1259 struct fruit_config_data
*config
= NULL
;
1263 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
1264 struct fruit_config_data
,
1265 return NT_STATUS_UNSUCCESSFUL
);
1267 if (!global_fruit_config
.nego_aapl
) {
1268 return NT_STATUS_OK
;
1270 if (psd
->dacl
== NULL
|| !config
->unix_info_enabled
) {
1271 return NT_STATUS_OK
;
1274 for (i
= 0; i
< psd
->dacl
->num_aces
; i
++) {
1275 if (dom_sid_compare_domain(
1276 &global_sid_Unix_NFS_Mode
,
1277 &psd
->dacl
->aces
[i
].trustee
) == 0) {
1278 *pmode
= (mode_t
)psd
->dacl
->aces
[i
].trustee
.sub_auths
[2];
1279 *pmode
&= (S_IRWXU
| S_IRWXG
| S_IRWXO
);
1282 DEBUG(10, ("MS NFS chmod request %s, %04o\n",
1283 fsp_str_dbg(fsp
), (unsigned)(*pmode
)));
1289 * Remove any incoming virtual ACE entries generated by
1290 * fruit_fget_nt_acl().
1293 return remove_virtual_nfs_aces(psd
);
1296 /****************************************************************************
1298 ****************************************************************************/
1300 static int fruit_connect(vfs_handle_struct
*handle
,
1301 const char *service
,
1305 char *list
= NULL
, *newlist
= NULL
;
1306 struct fruit_config_data
*config
;
1307 const struct loadparm_substitution
*lp_sub
=
1308 loadparm_s3_global_substitution();
1310 DEBUG(10, ("fruit_connect\n"));
1312 rc
= SMB_VFS_NEXT_CONNECT(handle
, service
, user
);
1317 rc
= init_fruit_config(handle
);
1322 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
1323 struct fruit_config_data
, return -1);
1325 if (config
->veto_appledouble
) {
1326 list
= lp_veto_files(talloc_tos(), lp_sub
, SNUM(handle
->conn
));
1329 if (strstr(list
, "/" ADOUBLE_NAME_PREFIX
"*/") == NULL
) {
1330 newlist
= talloc_asprintf(
1332 "%s/" ADOUBLE_NAME_PREFIX
"*/",
1334 lp_do_parameter(SNUM(handle
->conn
),
1339 lp_do_parameter(SNUM(handle
->conn
),
1341 "/" ADOUBLE_NAME_PREFIX
"*/");
1347 if (config
->encoding
== FRUIT_ENC_NATIVE
) {
1348 lp_do_parameter(SNUM(handle
->conn
),
1350 macos_string_replace_map
);
1353 if (config
->time_machine
) {
1354 DBG_NOTICE("Enabling durable handles for Time Machine "
1355 "support on [%s]\n", service
);
1356 lp_do_parameter(SNUM(handle
->conn
), "durable handles", "yes");
1357 lp_do_parameter(SNUM(handle
->conn
), "kernel oplocks", "no");
1358 lp_do_parameter(SNUM(handle
->conn
), "kernel share modes", "no");
1359 if (!lp_strict_sync(SNUM(handle
->conn
))) {
1360 DBG_WARNING("Time Machine without strict sync is not "
1363 lp_do_parameter(SNUM(handle
->conn
), "posix locking", "no");
1369 static void fio_ref_destroy_fn(void *p_data
)
1371 struct fio
*ref_fio
= (struct fio
*)p_data
;
1372 if (ref_fio
->real_fio
!= NULL
) {
1373 SMB_ASSERT(ref_fio
->real_fio
->ad_fsp
== ref_fio
->fsp
);
1374 ref_fio
->real_fio
->ad_fsp
= NULL
;
1375 ref_fio
->real_fio
= NULL
;
1379 static void fio_close_ad_fsp(struct fio
*fio
)
1381 if (fio
->ad_fsp
!= NULL
) {
1382 fd_close(fio
->ad_fsp
);
1383 file_free(NULL
, fio
->ad_fsp
);
1384 /* fio_ref_destroy_fn() should have cleared this */
1385 SMB_ASSERT(fio
->ad_fsp
== NULL
);
1389 static void fio_destroy_fn(void *p_data
)
1391 struct fio
*fio
= (struct fio
*)p_data
;
1392 fio_close_ad_fsp(fio
);
1395 static int fruit_open_meta_stream(vfs_handle_struct
*handle
,
1396 const struct files_struct
*dirfsp
,
1397 const struct smb_filename
*smb_fname
,
1402 struct fruit_config_data
*config
= NULL
;
1403 struct fio
*fio
= NULL
;
1404 struct vfs_open_how how
= {
1405 .flags
= flags
& ~O_CREAT
,
1410 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname
));
1412 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
1413 struct fruit_config_data
, return -1);
1415 fio
= VFS_ADD_FSP_EXTENSION(handle
, fsp
, struct fio
, fio_destroy_fn
);
1416 fio
->handle
= handle
;
1418 fio
->type
= ADOUBLE_META
;
1419 fio
->config
= config
;
1421 fd
= SMB_VFS_NEXT_OPENAT(handle
,
1430 if (!(flags
& O_CREAT
)) {
1431 VFS_REMOVE_FSP_EXTENSION(handle
, fsp
);
1437 VFS_REMOVE_FSP_EXTENSION(handle
, fsp
);
1441 fio
->fake_fd
= true;
1448 static int fruit_open_meta_netatalk(vfs_handle_struct
*handle
,
1449 const struct files_struct
*dirfsp
,
1450 const struct smb_filename
*smb_fname
,
1455 struct fruit_config_data
*config
= NULL
;
1456 struct fio
*fio
= NULL
;
1457 struct adouble
*ad
= NULL
;
1458 bool meta_exists
= false;
1461 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname
));
1464 * We know this is a stream open, so fsp->base_fsp must
1467 SMB_ASSERT(fsp_is_alternate_stream(fsp
));
1468 SMB_ASSERT(fsp
->base_fsp
->fsp_name
->fsp
== fsp
->base_fsp
);
1470 ad
= ad_get(talloc_tos(), handle
, fsp
->base_fsp
->fsp_name
, ADOUBLE_META
);
1477 if (!meta_exists
&& !(flags
& O_CREAT
)) {
1487 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
1488 struct fruit_config_data
, return -1);
1490 fio
= VFS_ADD_FSP_EXTENSION(handle
, fsp
, struct fio
, fio_destroy_fn
);
1491 fio
->handle
= handle
;
1493 fio
->type
= ADOUBLE_META
;
1494 fio
->config
= config
;
1495 fio
->fake_fd
= true;
1502 static int fruit_open_meta(vfs_handle_struct
*handle
,
1503 const struct files_struct
*dirfsp
,
1504 const struct smb_filename
*smb_fname
,
1505 files_struct
*fsp
, int flags
, mode_t mode
)
1508 struct fruit_config_data
*config
= NULL
;
1510 DBG_DEBUG("path [%s]\n", smb_fname_str_dbg(smb_fname
));
1512 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
1513 struct fruit_config_data
, return -1);
1515 switch (config
->meta
) {
1516 case FRUIT_META_STREAM
:
1517 fd
= fruit_open_meta_stream(handle
, dirfsp
, smb_fname
,
1521 case FRUIT_META_NETATALK
:
1522 fd
= fruit_open_meta_netatalk(handle
, dirfsp
, smb_fname
,
1527 DBG_ERR("Unexpected meta config [%d]\n", config
->meta
);
1531 DBG_DEBUG("path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname
), fd
);
1536 static int fruit_open_rsrc_adouble(vfs_handle_struct
*handle
,
1537 const struct files_struct
*dirfsp
,
1538 const struct smb_filename
*smb_fname
,
1544 struct fruit_config_data
*config
= NULL
;
1545 struct files_struct
*ad_fsp
= NULL
;
1546 struct fio
*fio
= NULL
;
1547 struct fio
*ref_fio
= NULL
;
1551 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
1552 struct fruit_config_data
, return -1);
1554 if ((!(flags
& O_CREAT
)) &&
1555 S_ISDIR(fsp
->base_fsp
->fsp_name
->st
.st_ex_mode
))
1557 /* sorry, but directories don't have a resource fork */
1564 * We return a fake_fd to the vfs modules above,
1565 * while we open an internal backend fsp for the
1566 * '._' file for the next vfs modules.
1568 * Note that adouble_open_from_base_fsp() recurses
1569 * into fruit_openat(), but it'll just pass to
1570 * the next module as just opens a flat file on
1580 status
= adouble_open_from_base_fsp(fsp
->conn
->cwd_fsp
,
1586 if (!NT_STATUS_IS_OK(status
)) {
1587 errno
= map_errno_from_nt_status(status
);
1593 * Now we need to glue both handles together,
1594 * so that they automatically detach each other
1597 fio
= fruit_get_complete_fio(handle
, fsp
);
1599 DBG_ERR("fio=NULL for [%s]\n", fsp_str_dbg(fsp
));
1605 ref_fio
= VFS_ADD_FSP_EXTENSION(handle
, ad_fsp
,
1607 fio_ref_destroy_fn
);
1608 if (ref_fio
== NULL
) {
1609 int saved_errno
= errno
;
1611 file_free(NULL
, ad_fsp
);
1613 errno
= saved_errno
;
1618 SMB_ASSERT(ref_fio
->fsp
== NULL
);
1619 ref_fio
->handle
= handle
;
1620 ref_fio
->fsp
= ad_fsp
;
1621 ref_fio
->type
= ADOUBLE_RSRC
;
1622 ref_fio
->config
= config
;
1623 ref_fio
->real_fio
= fio
;
1624 SMB_ASSERT(fio
->ad_fsp
== NULL
);
1625 fio
->ad_fsp
= ad_fsp
;
1626 fio
->fake_fd
= true;
1630 DEBUG(10, ("fruit_open resource fork: rc=%d\n", rc
));
1632 int saved_errno
= errno
;
1634 vfs_fake_fd_close(fd
);
1636 errno
= saved_errno
;
1642 static int fruit_open_rsrc_xattr(vfs_handle_struct
*handle
,
1643 const struct files_struct
*dirfsp
,
1644 const struct smb_filename
*smb_fname
,
1649 #ifdef HAVE_ATTROPEN
1653 * As there's no attropenat() this is only going to work with AT_FDCWD.
1655 SMB_ASSERT(fsp_get_pathref_fd(dirfsp
) == AT_FDCWD
);
1657 fd
= attropen(smb_fname
->base_name
,
1658 AFPRESOURCE_EA_NETATALK
,
1673 static int fruit_open_rsrc(vfs_handle_struct
*handle
,
1674 const struct files_struct
*dirfsp
,
1675 const struct smb_filename
*smb_fname
,
1676 files_struct
*fsp
, int flags
, mode_t mode
)
1679 struct fruit_config_data
*config
= NULL
;
1680 struct fio
*fio
= NULL
;
1682 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname
));
1684 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
1685 struct fruit_config_data
, return -1);
1687 fio
= VFS_ADD_FSP_EXTENSION(handle
, fsp
, struct fio
, fio_destroy_fn
);
1688 fio
->handle
= handle
;
1690 fio
->type
= ADOUBLE_RSRC
;
1691 fio
->config
= config
;
1693 switch (config
->rsrc
) {
1694 case FRUIT_RSRC_STREAM
: {
1695 struct vfs_open_how how
= {
1696 .flags
= flags
, .mode
= mode
,
1698 fd
= SMB_VFS_NEXT_OPENAT(handle
,
1706 case FRUIT_RSRC_ADFILE
:
1707 fd
= fruit_open_rsrc_adouble(handle
, dirfsp
, smb_fname
,
1711 case FRUIT_RSRC_XATTR
:
1712 fd
= fruit_open_rsrc_xattr(handle
, dirfsp
, smb_fname
,
1717 DBG_ERR("Unexpected rsrc config [%d]\n", config
->rsrc
);
1722 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname
), fd
);
1731 static int fruit_openat(vfs_handle_struct
*handle
,
1732 const struct files_struct
*dirfsp
,
1733 const struct smb_filename
*smb_fname
,
1735 const struct vfs_open_how
*how
)
1739 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname
));
1741 if (!is_named_stream(smb_fname
)) {
1742 return SMB_VFS_NEXT_OPENAT(handle
,
1749 if (how
->resolve
!= 0) {
1754 SMB_ASSERT(fsp_is_alternate_stream(fsp
));
1756 if (is_afpinfo_stream(smb_fname
->stream_name
)) {
1757 fd
= fruit_open_meta(handle
,
1763 } else if (is_afpresource_stream(smb_fname
->stream_name
)) {
1764 fd
= fruit_open_rsrc(handle
,
1771 fd
= SMB_VFS_NEXT_OPENAT(handle
,
1778 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname
), fd
);
1780 /* Prevent reopen optimisation */
1781 fsp
->fsp_flags
.have_proc_fds
= false;
1785 static int fruit_close_meta(vfs_handle_struct
*handle
,
1789 struct fruit_config_data
*config
= NULL
;
1791 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
1792 struct fruit_config_data
, return -1);
1794 switch (config
->meta
) {
1795 case FRUIT_META_STREAM
:
1797 struct fio
*fio
= fruit_get_complete_fio(handle
, fsp
);
1802 ret
= vfs_fake_fd_close(fsp_get_pathref_fd(fsp
));
1803 fsp_set_fd(fsp
, -1);
1805 ret
= SMB_VFS_NEXT_CLOSE(handle
, fsp
);
1809 case FRUIT_META_NETATALK
:
1810 ret
= vfs_fake_fd_close(fsp_get_pathref_fd(fsp
));
1811 fsp_set_fd(fsp
, -1);
1815 DBG_ERR("Unexpected meta config [%d]\n", config
->meta
);
1823 static int fruit_close_rsrc(vfs_handle_struct
*handle
,
1827 struct fruit_config_data
*config
= NULL
;
1829 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
1830 struct fruit_config_data
, return -1);
1832 switch (config
->rsrc
) {
1833 case FRUIT_RSRC_STREAM
:
1834 ret
= SMB_VFS_NEXT_CLOSE(handle
, fsp
);
1837 case FRUIT_RSRC_ADFILE
:
1839 struct fio
*fio
= fruit_get_complete_fio(handle
, fsp
);
1843 fio_close_ad_fsp(fio
);
1844 ret
= vfs_fake_fd_close(fsp_get_pathref_fd(fsp
));
1845 fsp_set_fd(fsp
, -1);
1849 case FRUIT_RSRC_XATTR
:
1850 ret
= vfs_fake_fd_close(fsp_get_pathref_fd(fsp
));
1851 fsp_set_fd(fsp
, -1);
1855 DBG_ERR("Unexpected rsrc config [%d]\n", config
->rsrc
);
1862 static int fruit_close(vfs_handle_struct
*handle
,
1868 fd
= fsp_get_pathref_fd(fsp
);
1870 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(fsp
->fsp_name
), fd
);
1872 if (!fsp_is_alternate_stream(fsp
)) {
1873 return SMB_VFS_NEXT_CLOSE(handle
, fsp
);
1876 if (is_afpinfo_stream(fsp
->fsp_name
->stream_name
)) {
1877 ret
= fruit_close_meta(handle
, fsp
);
1878 } else if (is_afpresource_stream(fsp
->fsp_name
->stream_name
)) {
1879 ret
= fruit_close_rsrc(handle
, fsp
);
1881 ret
= SMB_VFS_NEXT_CLOSE(handle
, fsp
);
1887 static int fruit_renameat(struct vfs_handle_struct
*handle
,
1888 files_struct
*srcfsp
,
1889 const struct smb_filename
*smb_fname_src
,
1890 files_struct
*dstfsp
,
1891 const struct smb_filename
*smb_fname_dst
)
1894 struct fruit_config_data
*config
= NULL
;
1895 struct smb_filename
*src_adp_smb_fname
= NULL
;
1896 struct smb_filename
*dst_adp_smb_fname
= NULL
;
1898 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
1899 struct fruit_config_data
, return -1);
1901 if (!VALID_STAT(smb_fname_src
->st
)) {
1902 DBG_ERR("Need valid stat for [%s]\n",
1903 smb_fname_str_dbg(smb_fname_src
));
1907 rc
= SMB_VFS_NEXT_RENAMEAT(handle
,
1916 if ((config
->rsrc
!= FRUIT_RSRC_ADFILE
) ||
1917 (!S_ISREG(smb_fname_src
->st
.st_ex_mode
)))
1922 rc
= adouble_path(talloc_tos(), smb_fname_src
, &src_adp_smb_fname
);
1927 rc
= adouble_path(talloc_tos(), smb_fname_dst
, &dst_adp_smb_fname
);
1932 DBG_DEBUG("%s -> %s\n",
1933 smb_fname_str_dbg(src_adp_smb_fname
),
1934 smb_fname_str_dbg(dst_adp_smb_fname
));
1936 rc
= SMB_VFS_NEXT_RENAMEAT(handle
,
1941 if (errno
== ENOENT
) {
1946 TALLOC_FREE(src_adp_smb_fname
);
1947 TALLOC_FREE(dst_adp_smb_fname
);
1951 static int fruit_unlink_meta_stream(vfs_handle_struct
*handle
,
1952 struct files_struct
*dirfsp
,
1953 const struct smb_filename
*smb_fname
)
1955 return SMB_VFS_NEXT_UNLINKAT(handle
,
1961 static int fruit_unlink_meta_netatalk(vfs_handle_struct
*handle
,
1962 const struct smb_filename
*smb_fname
)
1964 SMB_ASSERT(smb_fname
->fsp
!= NULL
);
1965 SMB_ASSERT(fsp_is_alternate_stream(smb_fname
->fsp
));
1966 return SMB_VFS_FREMOVEXATTR(smb_fname
->fsp
->base_fsp
,
1967 AFPINFO_EA_NETATALK
);
1970 static int fruit_unlink_meta(vfs_handle_struct
*handle
,
1971 struct files_struct
*dirfsp
,
1972 const struct smb_filename
*smb_fname
)
1974 struct fruit_config_data
*config
= NULL
;
1977 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
1978 struct fruit_config_data
, return -1);
1980 switch (config
->meta
) {
1981 case FRUIT_META_STREAM
:
1982 rc
= fruit_unlink_meta_stream(handle
,
1987 case FRUIT_META_NETATALK
:
1988 rc
= fruit_unlink_meta_netatalk(handle
, smb_fname
);
1992 DBG_ERR("Unsupported meta config [%d]\n", config
->meta
);
1999 static int fruit_unlink_rsrc_stream(vfs_handle_struct
*handle
,
2000 struct files_struct
*dirfsp
,
2001 const struct smb_filename
*smb_fname
,
2006 if (!force_unlink
) {
2007 struct smb_filename
*full_fname
= NULL
;
2011 * TODO: use SMB_VFS_STATX() once we have it.
2014 full_fname
= full_path_from_dirfsp_atname(talloc_tos(),
2017 if (full_fname
== NULL
) {
2022 * 0 byte resource fork streams are not listed by
2023 * vfs_streaminfo, as a result stream cleanup/deletion of file
2024 * deletion doesn't remove the resourcefork stream.
2027 ret
= SMB_VFS_NEXT_STAT(handle
, full_fname
);
2029 TALLOC_FREE(full_fname
);
2030 DBG_ERR("stat [%s] failed [%s]\n",
2031 smb_fname_str_dbg(full_fname
), strerror(errno
));
2035 size
= full_fname
->st
.st_ex_size
;
2036 TALLOC_FREE(full_fname
);
2039 /* OS X ignores resource fork stream delete requests */
2044 ret
= SMB_VFS_NEXT_UNLINKAT(handle
,
2048 if ((ret
!= 0) && (errno
== ENOENT
) && force_unlink
) {
2055 static int fruit_unlink_rsrc_adouble(vfs_handle_struct
*handle
,
2056 struct files_struct
*dirfsp
,
2057 const struct smb_filename
*smb_fname
,
2061 struct adouble
*ad
= NULL
;
2062 struct smb_filename
*adp_smb_fname
= NULL
;
2064 if (!force_unlink
) {
2065 struct smb_filename
*full_fname
= NULL
;
2067 full_fname
= full_path_from_dirfsp_atname(talloc_tos(),
2070 if (full_fname
== NULL
) {
2074 ad
= ad_get(talloc_tos(), handle
, full_fname
,
2076 TALLOC_FREE(full_fname
);
2084 * 0 byte resource fork streams are not listed by
2085 * vfs_streaminfo, as a result stream cleanup/deletion of file
2086 * deletion doesn't remove the resourcefork stream.
2089 if (ad_getentrylen(ad
, ADEID_RFORK
) > 0) {
2090 /* OS X ignores resource fork stream delete requests */
2098 rc
= adouble_path(talloc_tos(), smb_fname
, &adp_smb_fname
);
2103 rc
= SMB_VFS_NEXT_UNLINKAT(handle
,
2107 TALLOC_FREE(adp_smb_fname
);
2108 if ((rc
!= 0) && (errno
== ENOENT
) && force_unlink
) {
2115 static int fruit_unlink_rsrc_xattr(vfs_handle_struct
*handle
,
2116 const struct smb_filename
*smb_fname
,
2120 * OS X ignores resource fork stream delete requests, so nothing to do
2121 * here. Removing the file will remove the xattr anyway, so we don't
2122 * have to take care of removing 0 byte resource forks that could be
2128 static int fruit_unlink_rsrc(vfs_handle_struct
*handle
,
2129 struct files_struct
*dirfsp
,
2130 const struct smb_filename
*smb_fname
,
2133 struct fruit_config_data
*config
= NULL
;
2136 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
2137 struct fruit_config_data
, return -1);
2139 switch (config
->rsrc
) {
2140 case FRUIT_RSRC_STREAM
:
2141 rc
= fruit_unlink_rsrc_stream(handle
,
2147 case FRUIT_RSRC_ADFILE
:
2148 rc
= fruit_unlink_rsrc_adouble(handle
,
2154 case FRUIT_RSRC_XATTR
:
2155 rc
= fruit_unlink_rsrc_xattr(handle
, smb_fname
, force_unlink
);
2159 DBG_ERR("Unsupported rsrc config [%d]\n", config
->rsrc
);
2166 static int fruit_fchmod(vfs_handle_struct
*handle
,
2167 struct files_struct
*fsp
,
2171 struct fruit_config_data
*config
= NULL
;
2172 struct smb_filename
*smb_fname_adp
= NULL
;
2173 const struct smb_filename
*smb_fname
= NULL
;
2176 rc
= SMB_VFS_NEXT_FCHMOD(handle
, fsp
, mode
);
2181 smb_fname
= fsp
->fsp_name
;
2182 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
2183 struct fruit_config_data
, return -1);
2185 if (config
->rsrc
!= FRUIT_RSRC_ADFILE
) {
2189 if (!VALID_STAT(smb_fname
->st
)) {
2193 if (!S_ISREG(smb_fname
->st
.st_ex_mode
)) {
2197 rc
= adouble_path(talloc_tos(), smb_fname
, &smb_fname_adp
);
2202 status
= openat_pathref_fsp(handle
->conn
->cwd_fsp
,
2204 if (!NT_STATUS_IS_OK(status
)) {
2205 /* detect ENOENT (mapped to OBJECT_NAME_NOT_FOUND) */
2206 if (NT_STATUS_EQUAL(status
,
2207 NT_STATUS_OBJECT_NAME_NOT_FOUND
)){
2215 DBG_DEBUG("%s\n", smb_fname_adp
->base_name
);
2217 rc
= SMB_VFS_NEXT_FCHMOD(handle
, smb_fname_adp
->fsp
, mode
);
2218 if (errno
== ENOENT
) {
2222 TALLOC_FREE(smb_fname_adp
);
2226 static int fruit_unlinkat(vfs_handle_struct
*handle
,
2227 struct files_struct
*dirfsp
,
2228 const struct smb_filename
*smb_fname
,
2231 struct fruit_config_data
*config
= NULL
;
2232 struct smb_filename
*rsrc_smb_fname
= NULL
;
2235 if (flags
& AT_REMOVEDIR
) {
2236 return SMB_VFS_NEXT_UNLINKAT(handle
,
2242 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
2243 struct fruit_config_data
, return -1);
2245 if (is_afpinfo_stream(smb_fname
->stream_name
)) {
2246 return fruit_unlink_meta(handle
,
2249 } else if (is_afpresource_stream(smb_fname
->stream_name
)) {
2250 return fruit_unlink_rsrc(handle
,
2254 } else if (is_named_stream(smb_fname
)) {
2255 return SMB_VFS_NEXT_UNLINKAT(handle
,
2259 } else if (is_adouble_file(smb_fname
->base_name
)) {
2260 return SMB_VFS_NEXT_UNLINKAT(handle
,
2267 * A request to delete the base file. Because 0 byte resource
2268 * fork streams are not listed by fruit_streaminfo,
2269 * delete_all_streams() can't remove 0 byte resource fork
2270 * streams, so we have to cleanup this here.
2272 rsrc_smb_fname
= synthetic_smb_fname(talloc_tos(),
2273 smb_fname
->base_name
,
2274 AFPRESOURCE_STREAM_NAME
,
2278 if (rsrc_smb_fname
== NULL
) {
2282 ret
= fruit_unlink_rsrc(handle
, dirfsp
, rsrc_smb_fname
, true);
2283 if ((ret
!= 0) && (errno
!= ENOENT
)) {
2284 DBG_ERR("Forced unlink of [%s] failed [%s]\n",
2285 smb_fname_str_dbg(rsrc_smb_fname
), strerror(errno
));
2286 TALLOC_FREE(rsrc_smb_fname
);
2289 TALLOC_FREE(rsrc_smb_fname
);
2291 return SMB_VFS_NEXT_UNLINKAT(handle
,
2297 static ssize_t
fruit_pread_meta_stream(vfs_handle_struct
*handle
,
2298 files_struct
*fsp
, void *data
,
2299 size_t n
, off_t offset
)
2301 struct fio
*fio
= fruit_get_complete_fio(handle
, fsp
);
2305 if ((fio
== NULL
) || fio
->fake_fd
) {
2309 nread
= SMB_VFS_NEXT_PREAD(handle
, fsp
, data
, n
, offset
);
2310 if (nread
== -1 || nread
== n
) {
2314 DBG_ERR("Removing [%s] after short read [%zd]\n",
2315 fsp_str_dbg(fsp
), nread
);
2317 ret
= SMB_VFS_NEXT_UNLINKAT(handle
,
2322 DBG_ERR("Removing [%s] failed\n", fsp_str_dbg(fsp
));
2330 static ssize_t
fruit_pread_meta_adouble(vfs_handle_struct
*handle
,
2331 files_struct
*fsp
, void *data
,
2332 size_t n
, off_t offset
)
2335 struct adouble
*ad
= NULL
;
2336 char afpinfo_buf
[AFP_INFO_SIZE
];
2340 ai
= afpinfo_new(talloc_tos());
2345 ad
= ad_fget(talloc_tos(), handle
, fsp
, ADOUBLE_META
);
2351 p
= ad_get_entry(ad
, ADEID_FINDERI
);
2353 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp
));
2358 memcpy(&ai
->afpi_FinderInfo
[0], p
, ADEDLEN_FINDERI
);
2360 nread
= afpinfo_pack(ai
, afpinfo_buf
);
2361 if (nread
!= AFP_INFO_SIZE
) {
2366 memcpy(data
, afpinfo_buf
, n
);
2374 static ssize_t
fruit_pread_meta(vfs_handle_struct
*handle
,
2375 files_struct
*fsp
, void *data
,
2376 size_t n
, off_t offset
)
2378 struct fio
*fio
= fruit_get_complete_fio(handle
, fsp
);
2383 * OS X has a off-by-1 error in the offset calculation, so we're
2384 * bug compatible here. It won't hurt, as any relevant real
2385 * world read requests from the AFP_AfpInfo stream will be
2386 * offset=0 n=60. offset is ignored anyway, see below.
2388 if ((offset
< 0) || (offset
>= AFP_INFO_SIZE
+ 1)) {
2393 DBG_ERR("Failed to fetch fsp extension");
2397 /* Yes, macOS always reads from offset 0 */
2399 to_return
= MIN(n
, AFP_INFO_SIZE
);
2401 switch (fio
->config
->meta
) {
2402 case FRUIT_META_STREAM
:
2403 nread
= fruit_pread_meta_stream(handle
, fsp
, data
,
2407 case FRUIT_META_NETATALK
:
2408 nread
= fruit_pread_meta_adouble(handle
, fsp
, data
,
2413 DBG_ERR("Unexpected meta config [%d]\n", fio
->config
->meta
);
2417 if (nread
== -1 && fio
->fake_fd
) {
2419 char afpinfo_buf
[AFP_INFO_SIZE
];
2421 ai
= afpinfo_new(talloc_tos());
2426 nread
= afpinfo_pack(ai
, afpinfo_buf
);
2428 if (nread
!= AFP_INFO_SIZE
) {
2432 memcpy(data
, afpinfo_buf
, to_return
);
2439 static ssize_t
fruit_pread_rsrc_stream(vfs_handle_struct
*handle
,
2440 files_struct
*fsp
, void *data
,
2441 size_t n
, off_t offset
)
2443 return SMB_VFS_NEXT_PREAD(handle
, fsp
, data
, n
, offset
);
2446 static ssize_t
fruit_pread_rsrc_xattr(vfs_handle_struct
*handle
,
2447 files_struct
*fsp
, void *data
,
2448 size_t n
, off_t offset
)
2450 return SMB_VFS_NEXT_PREAD(handle
, fsp
, data
, n
, offset
);
2453 static ssize_t
fruit_pread_rsrc_adouble(vfs_handle_struct
*handle
,
2454 files_struct
*fsp
, void *data
,
2455 size_t n
, off_t offset
)
2457 struct fio
*fio
= fruit_get_complete_fio(handle
, fsp
);
2458 struct adouble
*ad
= NULL
;
2461 if (fio
== NULL
|| fio
->ad_fsp
== NULL
) {
2462 DBG_ERR("fio/ad_fsp=NULL for [%s]\n", fsp_str_dbg(fsp
));
2467 ad
= ad_fget(talloc_tos(), handle
, fio
->ad_fsp
, ADOUBLE_RSRC
);
2469 DBG_ERR("ad_fget [%s] failed [%s]\n",
2470 fsp_str_dbg(fio
->ad_fsp
), strerror(errno
));
2474 nread
= SMB_VFS_NEXT_PREAD(handle
, fio
->ad_fsp
, data
, n
,
2475 offset
+ ad_getentryoff(ad
, ADEID_RFORK
));
2481 static ssize_t
fruit_pread_rsrc(vfs_handle_struct
*handle
,
2482 files_struct
*fsp
, void *data
,
2483 size_t n
, off_t offset
)
2485 struct fio
*fio
= fruit_get_complete_fio(handle
, fsp
);
2493 switch (fio
->config
->rsrc
) {
2494 case FRUIT_RSRC_STREAM
:
2495 nread
= fruit_pread_rsrc_stream(handle
, fsp
, data
, n
, offset
);
2498 case FRUIT_RSRC_ADFILE
:
2499 nread
= fruit_pread_rsrc_adouble(handle
, fsp
, data
, n
, offset
);
2502 case FRUIT_RSRC_XATTR
:
2503 nread
= fruit_pread_rsrc_xattr(handle
, fsp
, data
, n
, offset
);
2507 DBG_ERR("Unexpected rsrc config [%d]\n", fio
->config
->rsrc
);
2514 static ssize_t
fruit_pread(vfs_handle_struct
*handle
,
2515 files_struct
*fsp
, void *data
,
2516 size_t n
, off_t offset
)
2518 struct fio
*fio
= fruit_get_complete_fio(handle
, fsp
);
2521 DBG_DEBUG("Path [%s] offset=%"PRIdMAX
", size=%zd\n",
2522 fsp_str_dbg(fsp
), (intmax_t)offset
, n
);
2525 return SMB_VFS_NEXT_PREAD(handle
, fsp
, data
, n
, offset
);
2528 if (fio
->type
== ADOUBLE_META
) {
2529 nread
= fruit_pread_meta(handle
, fsp
, data
, n
, offset
);
2531 nread
= fruit_pread_rsrc(handle
, fsp
, data
, n
, offset
);
2534 DBG_DEBUG("Path [%s] nread [%zd]\n", fsp_str_dbg(fsp
), nread
);
2538 static bool fruit_must_handle_aio_stream(struct fio
*fio
)
2544 if (fio
->type
== ADOUBLE_META
) {
2548 if ((fio
->type
== ADOUBLE_RSRC
) &&
2549 (fio
->config
->rsrc
== FRUIT_RSRC_ADFILE
))
2557 struct fruit_pread_state
{
2559 struct vfs_aio_state vfs_aio_state
;
2562 static void fruit_pread_done(struct tevent_req
*subreq
);
2564 static struct tevent_req
*fruit_pread_send(
2565 struct vfs_handle_struct
*handle
,
2566 TALLOC_CTX
*mem_ctx
,
2567 struct tevent_context
*ev
,
2568 struct files_struct
*fsp
,
2570 size_t n
, off_t offset
)
2572 struct tevent_req
*req
= NULL
;
2573 struct tevent_req
*subreq
= NULL
;
2574 struct fruit_pread_state
*state
= NULL
;
2575 struct fio
*fio
= fruit_get_complete_fio(handle
, fsp
);
2577 req
= tevent_req_create(mem_ctx
, &state
,
2578 struct fruit_pread_state
);
2583 if (fruit_must_handle_aio_stream(fio
)) {
2584 state
->nread
= SMB_VFS_PREAD(fsp
, data
, n
, offset
);
2585 if (state
->nread
!= n
) {
2586 if (state
->nread
!= -1) {
2589 tevent_req_error(req
, errno
);
2590 return tevent_req_post(req
, ev
);
2592 tevent_req_done(req
);
2593 return tevent_req_post(req
, ev
);
2596 subreq
= SMB_VFS_NEXT_PREAD_SEND(state
, ev
, handle
, fsp
,
2598 if (tevent_req_nomem(req
, subreq
)) {
2599 return tevent_req_post(req
, ev
);
2601 tevent_req_set_callback(subreq
, fruit_pread_done
, req
);
2605 static void fruit_pread_done(struct tevent_req
*subreq
)
2607 struct tevent_req
*req
= tevent_req_callback_data(
2608 subreq
, struct tevent_req
);
2609 struct fruit_pread_state
*state
= tevent_req_data(
2610 req
, struct fruit_pread_state
);
2612 state
->nread
= SMB_VFS_PREAD_RECV(subreq
, &state
->vfs_aio_state
);
2613 TALLOC_FREE(subreq
);
2615 if (tevent_req_error(req
, state
->vfs_aio_state
.error
)) {
2618 tevent_req_done(req
);
2621 static ssize_t
fruit_pread_recv(struct tevent_req
*req
,
2622 struct vfs_aio_state
*vfs_aio_state
)
2624 struct fruit_pread_state
*state
= tevent_req_data(
2625 req
, struct fruit_pread_state
);
2626 ssize_t retval
= -1;
2628 if (tevent_req_is_unix_error(req
, &vfs_aio_state
->error
)) {
2629 tevent_req_received(req
);
2633 *vfs_aio_state
= state
->vfs_aio_state
;
2634 retval
= state
->nread
;
2635 tevent_req_received(req
);
2639 static ssize_t
fruit_pwrite_meta_stream(vfs_handle_struct
*handle
,
2640 files_struct
*fsp
, const void *data
,
2641 size_t n
, off_t offset
)
2643 struct fio
*fio
= fruit_get_complete_fio(handle
, fsp
);
2649 DBG_DEBUG("Path [%s] offset=%"PRIdMAX
", size=%zd\n",
2650 fsp_str_dbg(fsp
), (intmax_t)offset
, n
);
2657 struct vfs_open_how how
= {
2658 .flags
= fio
->flags
, .mode
= fio
->mode
,
2660 int fd
= fsp_get_pathref_fd(fsp
);
2662 ret
= vfs_fake_fd_close(fd
);
2663 fsp_set_fd(fsp
, -1);
2665 DBG_ERR("Close [%s] failed: %s\n",
2666 fsp_str_dbg(fsp
), strerror(errno
));
2670 fd
= SMB_VFS_NEXT_OPENAT(handle
,
2671 NULL
, /* opening a stream */
2676 DBG_ERR("On-demand create [%s] in write failed: %s\n",
2677 fsp_str_dbg(fsp
), strerror(errno
));
2680 fsp_set_fd(fsp
, fd
);
2681 fio
->fake_fd
= false;
2684 ai
= afpinfo_unpack(talloc_tos(), data
);
2689 if (ai_empty_finderinfo(ai
)) {
2691 * Writing an all 0 blob to the metadata stream results in the
2692 * stream being removed on a macOS server. This ensures we
2693 * behave the same and it verified by the "delete AFP_AfpInfo by
2694 * writing all 0" test.
2696 ret
= SMB_VFS_NEXT_FTRUNCATE(handle
, fsp
, 0);
2698 DBG_ERR("SMB_VFS_NEXT_FTRUNCATE on [%s] failed\n",
2703 ok
= set_delete_on_close(
2706 handle
->conn
->session_info
->security_token
,
2707 handle
->conn
->session_info
->unix_token
);
2709 DBG_ERR("set_delete_on_close on [%s] failed\n",
2716 nwritten
= SMB_VFS_NEXT_PWRITE(handle
, fsp
, data
, n
, offset
);
2717 if (nwritten
!= n
) {
2724 static ssize_t
fruit_pwrite_meta_netatalk(vfs_handle_struct
*handle
,
2725 files_struct
*fsp
, const void *data
,
2726 size_t n
, off_t offset
)
2728 struct adouble
*ad
= NULL
;
2734 ai
= afpinfo_unpack(talloc_tos(), data
);
2739 ad
= ad_fget(talloc_tos(), handle
, fsp
, ADOUBLE_META
);
2741 ad
= ad_init(talloc_tos(), ADOUBLE_META
);
2746 p
= ad_get_entry(ad
, ADEID_FINDERI
);
2748 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp
));
2753 memcpy(p
, &ai
->afpi_FinderInfo
[0], ADEDLEN_FINDERI
);
2755 ret
= ad_fset(handle
, ad
, fsp
);
2757 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp
));
2764 if (!ai_empty_finderinfo(ai
)) {
2769 * Writing an all 0 blob to the metadata stream results in the stream
2770 * being removed on a macOS server. This ensures we behave the same and
2771 * it verified by the "delete AFP_AfpInfo by writing all 0" test.
2774 ok
= set_delete_on_close(
2777 handle
->conn
->session_info
->security_token
,
2778 handle
->conn
->session_info
->unix_token
);
2780 DBG_ERR("set_delete_on_close on [%s] failed\n",
2788 static ssize_t
fruit_pwrite_meta(vfs_handle_struct
*handle
,
2789 files_struct
*fsp
, const void *data
,
2790 size_t n
, off_t offset
)
2792 struct fio
*fio
= fruit_get_complete_fio(handle
, fsp
);
2794 uint8_t buf
[AFP_INFO_SIZE
];
2800 DBG_ERR("Failed to fetch fsp extension");
2809 if (offset
!= 0 && n
< 60) {
2814 cmp
= memcmp(data
, "AFP", 3);
2820 if (n
<= AFP_OFF_FinderInfo
) {
2822 * Nothing to do here really, just return
2830 if (to_copy
> AFP_INFO_SIZE
) {
2831 to_copy
= AFP_INFO_SIZE
;
2833 memcpy(buf
, data
, to_copy
);
2836 if (to_write
!= AFP_INFO_SIZE
) {
2837 to_write
= AFP_INFO_SIZE
;
2840 switch (fio
->config
->meta
) {
2841 case FRUIT_META_STREAM
:
2842 nwritten
= fruit_pwrite_meta_stream(handle
,
2849 case FRUIT_META_NETATALK
:
2850 nwritten
= fruit_pwrite_meta_netatalk(handle
,
2858 DBG_ERR("Unexpected meta config [%d]\n", fio
->config
->meta
);
2862 if (nwritten
!= to_write
) {
2867 * Return the requested amount, verified against macOS SMB server
2872 static ssize_t
fruit_pwrite_rsrc_stream(vfs_handle_struct
*handle
,
2873 files_struct
*fsp
, const void *data
,
2874 size_t n
, off_t offset
)
2876 return SMB_VFS_NEXT_PWRITE(handle
, fsp
, data
, n
, offset
);
2879 static ssize_t
fruit_pwrite_rsrc_xattr(vfs_handle_struct
*handle
,
2880 files_struct
*fsp
, const void *data
,
2881 size_t n
, off_t offset
)
2883 return SMB_VFS_NEXT_PWRITE(handle
, fsp
, data
, n
, offset
);
2886 static ssize_t
fruit_pwrite_rsrc_adouble(vfs_handle_struct
*handle
,
2887 files_struct
*fsp
, const void *data
,
2888 size_t n
, off_t offset
)
2890 struct fio
*fio
= fruit_get_complete_fio(handle
, fsp
);
2891 struct adouble
*ad
= NULL
;
2895 if (fio
== NULL
|| fio
->ad_fsp
== NULL
) {
2896 DBG_ERR("fio/ad_fsp=NULL for [%s]\n", fsp_str_dbg(fsp
));
2901 ad
= ad_fget(talloc_tos(), handle
, fio
->ad_fsp
, ADOUBLE_RSRC
);
2903 DBG_ERR("ad_fget [%s] failed [%s]\n",
2904 fsp_str_dbg(fio
->ad_fsp
), strerror(errno
));
2908 nwritten
= SMB_VFS_NEXT_PWRITE(handle
, fio
->ad_fsp
, data
, n
,
2909 offset
+ ad_getentryoff(ad
, ADEID_RFORK
));
2910 if (nwritten
!= n
) {
2911 DBG_ERR("Short write on [%s] [%zd/%zd]\n",
2912 fsp_str_dbg(fio
->ad_fsp
), nwritten
, n
);
2917 if ((n
+ offset
) > ad_getentrylen(ad
, ADEID_RFORK
)) {
2918 ad_setentrylen(ad
, ADEID_RFORK
, n
+ offset
);
2919 ret
= ad_fset(handle
, ad
, fio
->ad_fsp
);
2921 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fio
->ad_fsp
));
2931 static ssize_t
fruit_pwrite_rsrc(vfs_handle_struct
*handle
,
2932 files_struct
*fsp
, const void *data
,
2933 size_t n
, off_t offset
)
2935 struct fio
*fio
= fruit_get_complete_fio(handle
, fsp
);
2939 DBG_ERR("Failed to fetch fsp extension");
2943 switch (fio
->config
->rsrc
) {
2944 case FRUIT_RSRC_STREAM
:
2945 nwritten
= fruit_pwrite_rsrc_stream(handle
, fsp
, data
, n
, offset
);
2948 case FRUIT_RSRC_ADFILE
:
2949 nwritten
= fruit_pwrite_rsrc_adouble(handle
, fsp
, data
, n
, offset
);
2952 case FRUIT_RSRC_XATTR
:
2953 nwritten
= fruit_pwrite_rsrc_xattr(handle
, fsp
, data
, n
, offset
);
2957 DBG_ERR("Unexpected rsrc config [%d]\n", fio
->config
->rsrc
);
2964 static ssize_t
fruit_pwrite(vfs_handle_struct
*handle
,
2965 files_struct
*fsp
, const void *data
,
2966 size_t n
, off_t offset
)
2968 struct fio
*fio
= fruit_get_complete_fio(handle
, fsp
);
2971 DBG_DEBUG("Path [%s] offset=%"PRIdMAX
", size=%zd\n",
2972 fsp_str_dbg(fsp
), (intmax_t)offset
, n
);
2975 return SMB_VFS_NEXT_PWRITE(handle
, fsp
, data
, n
, offset
);
2978 if (fio
->type
== ADOUBLE_META
) {
2979 nwritten
= fruit_pwrite_meta(handle
, fsp
, data
, n
, offset
);
2981 nwritten
= fruit_pwrite_rsrc(handle
, fsp
, data
, n
, offset
);
2984 DBG_DEBUG("Path [%s] nwritten=%zd\n", fsp_str_dbg(fsp
), nwritten
);
2988 struct fruit_pwrite_state
{
2990 struct vfs_aio_state vfs_aio_state
;
2993 static void fruit_pwrite_done(struct tevent_req
*subreq
);
2995 static struct tevent_req
*fruit_pwrite_send(
2996 struct vfs_handle_struct
*handle
,
2997 TALLOC_CTX
*mem_ctx
,
2998 struct tevent_context
*ev
,
2999 struct files_struct
*fsp
,
3001 size_t n
, off_t offset
)
3003 struct tevent_req
*req
= NULL
;
3004 struct tevent_req
*subreq
= NULL
;
3005 struct fruit_pwrite_state
*state
= NULL
;
3006 struct fio
*fio
= fruit_get_complete_fio(handle
, fsp
);
3008 req
= tevent_req_create(mem_ctx
, &state
,
3009 struct fruit_pwrite_state
);
3014 if (fruit_must_handle_aio_stream(fio
)) {
3015 state
->nwritten
= SMB_VFS_PWRITE(fsp
, data
, n
, offset
);
3016 if (state
->nwritten
!= n
) {
3017 if (state
->nwritten
!= -1) {
3020 tevent_req_error(req
, errno
);
3021 return tevent_req_post(req
, ev
);
3023 tevent_req_done(req
);
3024 return tevent_req_post(req
, ev
);
3027 subreq
= SMB_VFS_NEXT_PWRITE_SEND(state
, ev
, handle
, fsp
,
3029 if (tevent_req_nomem(req
, subreq
)) {
3030 return tevent_req_post(req
, ev
);
3032 tevent_req_set_callback(subreq
, fruit_pwrite_done
, req
);
3036 static void fruit_pwrite_done(struct tevent_req
*subreq
)
3038 struct tevent_req
*req
= tevent_req_callback_data(
3039 subreq
, struct tevent_req
);
3040 struct fruit_pwrite_state
*state
= tevent_req_data(
3041 req
, struct fruit_pwrite_state
);
3043 state
->nwritten
= SMB_VFS_PWRITE_RECV(subreq
, &state
->vfs_aio_state
);
3044 TALLOC_FREE(subreq
);
3046 if (tevent_req_error(req
, state
->vfs_aio_state
.error
)) {
3049 tevent_req_done(req
);
3052 static ssize_t
fruit_pwrite_recv(struct tevent_req
*req
,
3053 struct vfs_aio_state
*vfs_aio_state
)
3055 struct fruit_pwrite_state
*state
= tevent_req_data(
3056 req
, struct fruit_pwrite_state
);
3057 ssize_t retval
= -1;
3059 if (tevent_req_is_unix_error(req
, &vfs_aio_state
->error
)) {
3060 tevent_req_received(req
);
3064 *vfs_aio_state
= state
->vfs_aio_state
;
3065 retval
= state
->nwritten
;
3066 tevent_req_received(req
);
3070 struct fruit_fsync_state
{
3072 struct vfs_aio_state vfs_aio_state
;
3075 static void fruit_fsync_done(struct tevent_req
*subreq
);
3077 static struct tevent_req
*fruit_fsync_send(
3078 struct vfs_handle_struct
*handle
,
3079 TALLOC_CTX
*mem_ctx
,
3080 struct tevent_context
*ev
,
3081 struct files_struct
*fsp
)
3083 struct tevent_req
*req
= NULL
;
3084 struct tevent_req
*subreq
= NULL
;
3085 struct fruit_fsync_state
*state
= NULL
;
3086 struct fio
*fio
= fruit_get_complete_fio(handle
, fsp
);
3088 req
= tevent_req_create(mem_ctx
, &state
,
3089 struct fruit_fsync_state
);
3094 if (fruit_must_handle_aio_stream(fio
)) {
3095 struct adouble
*ad
= NULL
;
3097 if (fio
->type
== ADOUBLE_META
) {
3099 * We must never pass a fake_fd
3100 * to lower level fsync calls.
3101 * Everything is already done
3102 * synchronously, so just return
3105 SMB_ASSERT(fio
->fake_fd
);
3106 tevent_req_done(req
);
3107 return tevent_req_post(req
, ev
);
3111 * We know the following must be true,
3112 * as it's the condition for fruit_must_handle_aio_stream()
3113 * to return true if fio->type == ADOUBLE_RSRC.
3115 SMB_ASSERT(fio
->config
->rsrc
== FRUIT_RSRC_ADFILE
);
3116 if (fio
->ad_fsp
== NULL
) {
3117 tevent_req_error(req
, EBADF
);
3118 return tevent_req_post(req
, ev
);
3120 ad
= ad_fget(talloc_tos(), handle
, fio
->ad_fsp
, ADOUBLE_RSRC
);
3122 tevent_req_error(req
, ENOMEM
);
3123 return tevent_req_post(req
, ev
);
3128 subreq
= SMB_VFS_NEXT_FSYNC_SEND(state
, ev
, handle
, fsp
);
3129 if (tevent_req_nomem(req
, subreq
)) {
3130 return tevent_req_post(req
, ev
);
3132 tevent_req_set_callback(subreq
, fruit_fsync_done
, req
);
3136 static void fruit_fsync_done(struct tevent_req
*subreq
)
3138 struct tevent_req
*req
= tevent_req_callback_data(
3139 subreq
, struct tevent_req
);
3140 struct fruit_fsync_state
*state
= tevent_req_data(
3141 req
, struct fruit_fsync_state
);
3143 state
->ret
= SMB_VFS_FSYNC_RECV(subreq
, &state
->vfs_aio_state
);
3144 TALLOC_FREE(subreq
);
3145 if (state
->ret
!= 0) {
3146 tevent_req_error(req
, errno
);
3149 tevent_req_done(req
);
3152 static int fruit_fsync_recv(struct tevent_req
*req
,
3153 struct vfs_aio_state
*vfs_aio_state
)
3155 struct fruit_fsync_state
*state
= tevent_req_data(
3156 req
, struct fruit_fsync_state
);
3159 if (tevent_req_is_unix_error(req
, &vfs_aio_state
->error
)) {
3160 tevent_req_received(req
);
3164 *vfs_aio_state
= state
->vfs_aio_state
;
3165 retval
= state
->ret
;
3166 tevent_req_received(req
);
3171 * Helper to stat/lstat the base file of an smb_fname.
3173 static int fruit_stat_base(vfs_handle_struct
*handle
,
3174 struct smb_filename
*smb_fname
,
3177 char *tmp_stream_name
;
3180 tmp_stream_name
= smb_fname
->stream_name
;
3181 smb_fname
->stream_name
= NULL
;
3183 rc
= SMB_VFS_NEXT_STAT(handle
, smb_fname
);
3185 rc
= SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
3187 smb_fname
->stream_name
= tmp_stream_name
;
3189 DBG_DEBUG("fruit_stat_base [%s] dev [%ju] ino [%ju]\n",
3190 smb_fname
->base_name
,
3191 (uintmax_t)smb_fname
->st
.st_ex_dev
,
3192 (uintmax_t)smb_fname
->st
.st_ex_ino
);
3196 static int fruit_stat_meta_stream(vfs_handle_struct
*handle
,
3197 struct smb_filename
*smb_fname
,
3203 ret
= fruit_stat_base(handle
, smb_fname
, false);
3208 ino
= hash_inode(&smb_fname
->st
, smb_fname
->stream_name
);
3211 ret
= SMB_VFS_NEXT_STAT(handle
, smb_fname
);
3213 ret
= SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
3216 smb_fname
->st
.st_ex_ino
= ino
;
3221 static int fruit_stat_meta_netatalk(vfs_handle_struct
*handle
,
3222 struct smb_filename
*smb_fname
,
3225 struct adouble
*ad
= NULL
;
3227 /* Populate the stat struct with info from the base file. */
3228 if (fruit_stat_base(handle
, smb_fname
, follow_links
) == -1) {
3232 ad
= ad_get_meta_fsp(talloc_tos(), handle
, smb_fname
);
3234 DBG_INFO("fruit_stat_meta %s: %s\n",
3235 smb_fname_str_dbg(smb_fname
), strerror(errno
));
3241 smb_fname
->st
.st_ex_size
= AFP_INFO_SIZE
;
3242 smb_fname
->st
.st_ex_ino
= hash_inode(&smb_fname
->st
,
3243 smb_fname
->stream_name
);
3247 static int fruit_stat_meta(vfs_handle_struct
*handle
,
3248 struct smb_filename
*smb_fname
,
3251 struct fruit_config_data
*config
= NULL
;
3254 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
3255 struct fruit_config_data
, return -1);
3257 switch (config
->meta
) {
3258 case FRUIT_META_STREAM
:
3259 ret
= fruit_stat_meta_stream(handle
, smb_fname
, follow_links
);
3262 case FRUIT_META_NETATALK
:
3263 ret
= fruit_stat_meta_netatalk(handle
, smb_fname
, follow_links
);
3267 DBG_ERR("Unexpected meta config [%d]\n", config
->meta
);
3274 static int fruit_stat_rsrc_netatalk(vfs_handle_struct
*handle
,
3275 struct smb_filename
*smb_fname
,
3278 struct adouble
*ad
= NULL
;
3281 ad
= ad_get(talloc_tos(), handle
, smb_fname
, ADOUBLE_RSRC
);
3287 /* Populate the stat struct with info from the base file. */
3288 ret
= fruit_stat_base(handle
, smb_fname
, follow_links
);
3294 smb_fname
->st
.st_ex_size
= ad_getentrylen(ad
, ADEID_RFORK
);
3295 smb_fname
->st
.st_ex_ino
= hash_inode(&smb_fname
->st
,
3296 smb_fname
->stream_name
);
3301 static int fruit_stat_rsrc_stream(vfs_handle_struct
*handle
,
3302 struct smb_filename
*smb_fname
,
3308 ret
= SMB_VFS_NEXT_STAT(handle
, smb_fname
);
3310 ret
= SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
3316 static int fruit_stat_rsrc_xattr(vfs_handle_struct
*handle
,
3317 struct smb_filename
*smb_fname
,
3320 #ifdef HAVE_ATTROPEN
3324 /* Populate the stat struct with info from the base file. */
3325 ret
= fruit_stat_base(handle
, smb_fname
, follow_links
);
3330 fd
= attropen(smb_fname
->base_name
,
3331 AFPRESOURCE_EA_NETATALK
,
3337 ret
= sys_fstat(fd
, &smb_fname
->st
, false);
3340 DBG_ERR("fstat [%s:%s] failed\n", smb_fname
->base_name
,
3341 AFPRESOURCE_EA_NETATALK
);
3347 smb_fname
->st
.st_ex_ino
= hash_inode(&smb_fname
->st
,
3348 smb_fname
->stream_name
);
3358 static int fruit_stat_rsrc(vfs_handle_struct
*handle
,
3359 struct smb_filename
*smb_fname
,
3362 struct fruit_config_data
*config
= NULL
;
3365 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname
));
3367 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
3368 struct fruit_config_data
, return -1);
3370 switch (config
->rsrc
) {
3371 case FRUIT_RSRC_STREAM
:
3372 ret
= fruit_stat_rsrc_stream(handle
, smb_fname
, follow_links
);
3375 case FRUIT_RSRC_XATTR
:
3376 ret
= fruit_stat_rsrc_xattr(handle
, smb_fname
, follow_links
);
3379 case FRUIT_RSRC_ADFILE
:
3380 ret
= fruit_stat_rsrc_netatalk(handle
, smb_fname
, follow_links
);
3384 DBG_ERR("Unexpected rsrc config [%d]\n", config
->rsrc
);
3391 static int fruit_stat(vfs_handle_struct
*handle
,
3392 struct smb_filename
*smb_fname
)
3396 DEBUG(10, ("fruit_stat called for %s\n",
3397 smb_fname_str_dbg(smb_fname
)));
3399 if (!is_named_stream(smb_fname
)) {
3400 rc
= SMB_VFS_NEXT_STAT(handle
, smb_fname
);
3402 update_btime(handle
, smb_fname
);
3408 * Note if lp_posix_paths() is true, we can never
3409 * get here as is_ntfs_stream_smb_fname() is
3410 * always false. So we never need worry about
3411 * not following links here.
3414 if (is_afpinfo_stream(smb_fname
->stream_name
)) {
3415 rc
= fruit_stat_meta(handle
, smb_fname
, true);
3416 } else if (is_afpresource_stream(smb_fname
->stream_name
)) {
3417 rc
= fruit_stat_rsrc(handle
, smb_fname
, true);
3419 return SMB_VFS_NEXT_STAT(handle
, smb_fname
);
3423 update_btime(handle
, smb_fname
);
3424 smb_fname
->st
.st_ex_mode
&= ~S_IFMT
;
3425 smb_fname
->st
.st_ex_mode
|= S_IFREG
;
3426 smb_fname
->st
.st_ex_blocks
=
3427 smb_fname
->st
.st_ex_size
/ STAT_ST_BLOCKSIZE
+ 1;
3432 static int fruit_lstat(vfs_handle_struct
*handle
,
3433 struct smb_filename
*smb_fname
)
3437 DEBUG(10, ("fruit_lstat called for %s\n",
3438 smb_fname_str_dbg(smb_fname
)));
3440 if (!is_named_stream(smb_fname
)) {
3441 rc
= SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
3443 update_btime(handle
, smb_fname
);
3448 if (is_afpinfo_stream(smb_fname
->stream_name
)) {
3449 rc
= fruit_stat_meta(handle
, smb_fname
, false);
3450 } else if (is_afpresource_stream(smb_fname
->stream_name
)) {
3451 rc
= fruit_stat_rsrc(handle
, smb_fname
, false);
3453 return SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
3457 update_btime(handle
, smb_fname
);
3458 smb_fname
->st
.st_ex_mode
&= ~S_IFMT
;
3459 smb_fname
->st
.st_ex_mode
|= S_IFREG
;
3460 smb_fname
->st
.st_ex_blocks
=
3461 smb_fname
->st
.st_ex_size
/ STAT_ST_BLOCKSIZE
+ 1;
3466 static int fruit_fstat_meta_stream(vfs_handle_struct
*handle
,
3468 SMB_STRUCT_STAT
*sbuf
)
3470 struct fio
*fio
= fruit_get_complete_fio(handle
, fsp
);
3471 struct smb_filename smb_fname
;
3480 ret
= fruit_stat_base(handle
, fsp
->base_fsp
->fsp_name
, false);
3485 *sbuf
= fsp
->base_fsp
->fsp_name
->st
;
3486 sbuf
->st_ex_size
= AFP_INFO_SIZE
;
3487 sbuf
->st_ex_ino
= hash_inode(sbuf
, fsp
->fsp_name
->stream_name
);
3491 smb_fname
= (struct smb_filename
) {
3492 .base_name
= fsp
->fsp_name
->base_name
,
3493 .twrp
= fsp
->fsp_name
->twrp
,
3496 ret
= fruit_stat_base(handle
, &smb_fname
, false);
3500 *sbuf
= smb_fname
.st
;
3502 ino
= hash_inode(sbuf
, fsp
->fsp_name
->stream_name
);
3504 ret
= SMB_VFS_NEXT_FSTAT(handle
, fsp
, sbuf
);
3509 sbuf
->st_ex_ino
= ino
;
3513 static int fruit_fstat_meta_netatalk(vfs_handle_struct
*handle
,
3515 SMB_STRUCT_STAT
*sbuf
)
3519 ret
= fruit_stat_base(handle
, fsp
->base_fsp
->fsp_name
, false);
3524 *sbuf
= fsp
->base_fsp
->fsp_name
->st
;
3525 sbuf
->st_ex_size
= AFP_INFO_SIZE
;
3526 sbuf
->st_ex_ino
= hash_inode(sbuf
, fsp
->fsp_name
->stream_name
);
3531 static int fruit_fstat_meta(vfs_handle_struct
*handle
,
3533 SMB_STRUCT_STAT
*sbuf
,
3538 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp
));
3540 switch (fio
->config
->meta
) {
3541 case FRUIT_META_STREAM
:
3542 ret
= fruit_fstat_meta_stream(handle
, fsp
, sbuf
);
3545 case FRUIT_META_NETATALK
:
3546 ret
= fruit_fstat_meta_netatalk(handle
, fsp
, sbuf
);
3550 DBG_ERR("Unexpected meta config [%d]\n", fio
->config
->meta
);
3554 DBG_DEBUG("Path [%s] ret [%d]\n", fsp_str_dbg(fsp
), ret
);
3558 static int fruit_fstat_rsrc_xattr(vfs_handle_struct
*handle
,
3560 SMB_STRUCT_STAT
*sbuf
)
3562 return SMB_VFS_NEXT_FSTAT(handle
, fsp
, sbuf
);
3565 static int fruit_fstat_rsrc_stream(vfs_handle_struct
*handle
,
3567 SMB_STRUCT_STAT
*sbuf
)
3569 return SMB_VFS_NEXT_FSTAT(handle
, fsp
, sbuf
);
3572 static int fruit_fstat_rsrc_adouble(vfs_handle_struct
*handle
,
3574 SMB_STRUCT_STAT
*sbuf
)
3576 struct fio
*fio
= fruit_get_complete_fio(handle
, fsp
);
3577 struct adouble
*ad
= NULL
;
3580 if (fio
== NULL
|| fio
->ad_fsp
== NULL
) {
3581 DBG_ERR("fio/ad_fsp=NULL for [%s]\n", fsp_str_dbg(fsp
));
3586 /* Populate the stat struct with info from the base file. */
3587 ret
= fruit_stat_base(handle
, fsp
->base_fsp
->fsp_name
, false);
3592 ad
= ad_fget(talloc_tos(), handle
, fio
->ad_fsp
, ADOUBLE_RSRC
);
3594 DBG_ERR("ad_fget [%s] failed [%s]\n",
3595 fsp_str_dbg(fio
->ad_fsp
), strerror(errno
));
3599 *sbuf
= fsp
->base_fsp
->fsp_name
->st
;
3600 sbuf
->st_ex_size
= ad_getentrylen(ad
, ADEID_RFORK
);
3601 sbuf
->st_ex_ino
= hash_inode(sbuf
, fsp
->fsp_name
->stream_name
);
3607 static int fruit_fstat_rsrc(vfs_handle_struct
*handle
, files_struct
*fsp
,
3608 SMB_STRUCT_STAT
*sbuf
, struct fio
*fio
)
3612 switch (fio
->config
->rsrc
) {
3613 case FRUIT_RSRC_STREAM
:
3614 ret
= fruit_fstat_rsrc_stream(handle
, fsp
, sbuf
);
3617 case FRUIT_RSRC_ADFILE
:
3618 ret
= fruit_fstat_rsrc_adouble(handle
, fsp
, sbuf
);
3621 case FRUIT_RSRC_XATTR
:
3622 ret
= fruit_fstat_rsrc_xattr(handle
, fsp
, sbuf
);
3626 DBG_ERR("Unexpected rsrc config [%d]\n", fio
->config
->rsrc
);
3633 static int fruit_fstat(vfs_handle_struct
*handle
, files_struct
*fsp
,
3634 SMB_STRUCT_STAT
*sbuf
)
3636 struct fio
*fio
= fruit_get_complete_fio(handle
, fsp
);
3640 return SMB_VFS_NEXT_FSTAT(handle
, fsp
, sbuf
);
3643 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp
));
3645 if (fio
->type
== ADOUBLE_META
) {
3646 rc
= fruit_fstat_meta(handle
, fsp
, sbuf
, fio
);
3648 rc
= fruit_fstat_rsrc(handle
, fsp
, sbuf
, fio
);
3652 sbuf
->st_ex_mode
&= ~S_IFMT
;
3653 sbuf
->st_ex_mode
|= S_IFREG
;
3654 sbuf
->st_ex_blocks
= sbuf
->st_ex_size
/ STAT_ST_BLOCKSIZE
+ 1;
3657 DBG_DEBUG("Path [%s] rc [%d] size [%"PRIdMAX
"]\n",
3658 fsp_str_dbg(fsp
), rc
, (intmax_t)sbuf
->st_ex_size
);
3662 static NTSTATUS
delete_invalid_meta_stream(
3663 vfs_handle_struct
*handle
,
3664 const struct smb_filename
*smb_fname
,
3665 TALLOC_CTX
*mem_ctx
,
3666 unsigned int *pnum_streams
,
3667 struct stream_struct
**pstreams
,
3670 struct smb_filename
*sname
= NULL
;
3675 ok
= del_fruit_stream(mem_ctx
, pnum_streams
, pstreams
, AFPINFO_STREAM
);
3677 return NT_STATUS_INTERNAL_ERROR
;
3681 return NT_STATUS_OK
;
3684 status
= synthetic_pathref(talloc_tos(),
3685 handle
->conn
->cwd_fsp
,
3686 smb_fname
->base_name
,
3687 AFPINFO_STREAM_NAME
,
3692 if (!NT_STATUS_IS_OK(status
)) {
3693 return NT_STATUS_NO_MEMORY
;
3696 ret
= SMB_VFS_NEXT_UNLINKAT(handle
,
3697 handle
->conn
->cwd_fsp
,
3701 DBG_ERR("Removing [%s] failed\n", smb_fname_str_dbg(sname
));
3703 return map_nt_error_from_unix(errno
);
3707 return NT_STATUS_OK
;
3710 static NTSTATUS
fruit_streaminfo_meta_stream(
3711 vfs_handle_struct
*handle
,
3712 struct files_struct
*fsp
,
3713 const struct smb_filename
*smb_fname
,
3714 TALLOC_CTX
*mem_ctx
,
3715 unsigned int *pnum_streams
,
3716 struct stream_struct
**pstreams
)
3718 struct stream_struct
*stream
= *pstreams
;
3719 unsigned int num_streams
= *pnum_streams
;
3722 for (i
= 0; i
< num_streams
; i
++) {
3723 if (strequal_m(stream
[i
].name
, AFPINFO_STREAM
)) {
3728 if (i
== num_streams
) {
3729 return NT_STATUS_OK
;
3732 if (stream
[i
].size
!= AFP_INFO_SIZE
) {
3733 DBG_ERR("Removing invalid AFPINFO_STREAM size [%jd] from [%s]\n",
3734 (intmax_t)stream
[i
].size
, smb_fname_str_dbg(smb_fname
));
3736 return delete_invalid_meta_stream(handle
,
3745 return NT_STATUS_OK
;
3748 static NTSTATUS
fruit_streaminfo_meta_netatalk(
3749 vfs_handle_struct
*handle
,
3750 struct files_struct
*fsp
,
3751 const struct smb_filename
*smb_fname
,
3752 TALLOC_CTX
*mem_ctx
,
3753 unsigned int *pnum_streams
,
3754 struct stream_struct
**pstreams
)
3756 struct stream_struct
*stream
= *pstreams
;
3757 unsigned int num_streams
= *pnum_streams
;
3758 struct adouble
*ad
= NULL
;
3763 /* Remove the Netatalk xattr from the list */
3764 ok
= del_fruit_stream(mem_ctx
, pnum_streams
, pstreams
,
3765 ":" NETATALK_META_XATTR
":$DATA");
3767 return NT_STATUS_NO_MEMORY
;
3771 * Check if there's a AFPINFO_STREAM from the VFS streams
3772 * backend and if yes, remove it from the list
3774 for (i
= 0; i
< num_streams
; i
++) {
3775 if (strequal_m(stream
[i
].name
, AFPINFO_STREAM
)) {
3780 if (i
< num_streams
) {
3781 DBG_WARNING("Unexpected AFPINFO_STREAM on [%s]\n",
3782 smb_fname_str_dbg(smb_fname
));
3784 ok
= del_fruit_stream(mem_ctx
, pnum_streams
, pstreams
,
3787 return NT_STATUS_INTERNAL_ERROR
;
3791 ad
= ad_get_meta_fsp(talloc_tos(), handle
, smb_fname
);
3793 return NT_STATUS_OK
;
3796 is_fi_empty
= ad_empty_finderinfo(ad
);
3800 return NT_STATUS_OK
;
3803 ok
= add_fruit_stream(mem_ctx
, pnum_streams
, pstreams
,
3804 AFPINFO_STREAM_NAME
, AFP_INFO_SIZE
,
3805 smb_roundup(handle
->conn
, AFP_INFO_SIZE
));
3807 return NT_STATUS_NO_MEMORY
;
3810 return NT_STATUS_OK
;
3813 static NTSTATUS
fruit_streaminfo_meta(vfs_handle_struct
*handle
,
3814 struct files_struct
*fsp
,
3815 const struct smb_filename
*smb_fname
,
3816 TALLOC_CTX
*mem_ctx
,
3817 unsigned int *pnum_streams
,
3818 struct stream_struct
**pstreams
)
3820 struct fruit_config_data
*config
= NULL
;
3823 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct fruit_config_data
,
3824 return NT_STATUS_INTERNAL_ERROR
);
3826 switch (config
->meta
) {
3827 case FRUIT_META_NETATALK
:
3828 status
= fruit_streaminfo_meta_netatalk(handle
, fsp
, smb_fname
,
3829 mem_ctx
, pnum_streams
,
3833 case FRUIT_META_STREAM
:
3834 status
= fruit_streaminfo_meta_stream(handle
, fsp
, smb_fname
,
3835 mem_ctx
, pnum_streams
,
3840 return NT_STATUS_INTERNAL_ERROR
;
3846 static NTSTATUS
fruit_streaminfo_rsrc_stream(
3847 vfs_handle_struct
*handle
,
3848 struct files_struct
*fsp
,
3849 const struct smb_filename
*smb_fname
,
3850 TALLOC_CTX
*mem_ctx
,
3851 unsigned int *pnum_streams
,
3852 struct stream_struct
**pstreams
)
3856 ok
= filter_empty_rsrc_stream(pnum_streams
, pstreams
);
3858 DBG_ERR("Filtering resource stream failed\n");
3859 return NT_STATUS_INTERNAL_ERROR
;
3861 return NT_STATUS_OK
;
3864 static NTSTATUS
fruit_streaminfo_rsrc_xattr(
3865 vfs_handle_struct
*handle
,
3866 struct files_struct
*fsp
,
3867 const struct smb_filename
*smb_fname
,
3868 TALLOC_CTX
*mem_ctx
,
3869 unsigned int *pnum_streams
,
3870 struct stream_struct
**pstreams
)
3874 ok
= filter_empty_rsrc_stream(pnum_streams
, pstreams
);
3876 DBG_ERR("Filtering resource stream failed\n");
3877 return NT_STATUS_INTERNAL_ERROR
;
3879 return NT_STATUS_OK
;
3882 static NTSTATUS
fruit_streaminfo_rsrc_adouble(
3883 vfs_handle_struct
*handle
,
3884 struct files_struct
*fsp
,
3885 const struct smb_filename
*smb_fname
,
3886 TALLOC_CTX
*mem_ctx
,
3887 unsigned int *pnum_streams
,
3888 struct stream_struct
**pstreams
)
3890 struct stream_struct
*stream
= *pstreams
;
3891 unsigned int num_streams
= *pnum_streams
;
3892 struct adouble
*ad
= NULL
;
3898 * Check if there's a AFPRESOURCE_STREAM from the VFS streams backend
3899 * and if yes, remove it from the list
3901 for (i
= 0; i
< num_streams
; i
++) {
3902 if (strequal_m(stream
[i
].name
, AFPRESOURCE_STREAM
)) {
3907 if (i
< num_streams
) {
3908 DBG_WARNING("Unexpected AFPRESOURCE_STREAM on [%s]\n",
3909 smb_fname_str_dbg(smb_fname
));
3911 ok
= del_fruit_stream(mem_ctx
, pnum_streams
, pstreams
,
3912 AFPRESOURCE_STREAM
);
3914 return NT_STATUS_INTERNAL_ERROR
;
3918 ad
= ad_get(talloc_tos(), handle
, smb_fname
, ADOUBLE_RSRC
);
3920 return NT_STATUS_OK
;
3923 rlen
= ad_getentrylen(ad
, ADEID_RFORK
);
3927 return NT_STATUS_OK
;
3930 ok
= add_fruit_stream(mem_ctx
, pnum_streams
, pstreams
,
3931 AFPRESOURCE_STREAM_NAME
, rlen
,
3932 smb_roundup(handle
->conn
, rlen
));
3934 return NT_STATUS_NO_MEMORY
;
3937 return NT_STATUS_OK
;
3940 static NTSTATUS
fruit_streaminfo_rsrc(vfs_handle_struct
*handle
,
3941 struct files_struct
*fsp
,
3942 const struct smb_filename
*smb_fname
,
3943 TALLOC_CTX
*mem_ctx
,
3944 unsigned int *pnum_streams
,
3945 struct stream_struct
**pstreams
)
3947 struct fruit_config_data
*config
= NULL
;
3950 if (S_ISDIR(smb_fname
->st
.st_ex_mode
)) {
3951 return NT_STATUS_OK
;
3954 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct fruit_config_data
,
3955 return NT_STATUS_INTERNAL_ERROR
);
3957 switch (config
->rsrc
) {
3958 case FRUIT_RSRC_STREAM
:
3959 status
= fruit_streaminfo_rsrc_stream(handle
, fsp
, smb_fname
,
3960 mem_ctx
, pnum_streams
,
3964 case FRUIT_RSRC_XATTR
:
3965 status
= fruit_streaminfo_rsrc_xattr(handle
, fsp
, smb_fname
,
3966 mem_ctx
, pnum_streams
,
3970 case FRUIT_RSRC_ADFILE
:
3971 status
= fruit_streaminfo_rsrc_adouble(handle
, fsp
, smb_fname
,
3972 mem_ctx
, pnum_streams
,
3977 return NT_STATUS_INTERNAL_ERROR
;
3983 static void fruit_filter_empty_streams(unsigned int *pnum_streams
,
3984 struct stream_struct
**pstreams
)
3986 unsigned num_streams
= *pnum_streams
;
3987 struct stream_struct
*streams
= *pstreams
;
3990 if (!global_fruit_config
.nego_aapl
) {
3994 while (i
< num_streams
) {
3995 struct smb_filename smb_fname
= (struct smb_filename
) {
3996 .stream_name
= streams
[i
].name
,
3999 if (is_ntfs_default_stream_smb_fname(&smb_fname
)
4000 || streams
[i
].size
> 0)
4006 streams
[i
] = streams
[num_streams
- 1];
4010 *pnum_streams
= num_streams
;
4013 static NTSTATUS
fruit_fstreaminfo(vfs_handle_struct
*handle
,
4014 struct files_struct
*fsp
,
4015 TALLOC_CTX
*mem_ctx
,
4016 unsigned int *pnum_streams
,
4017 struct stream_struct
**pstreams
)
4019 struct fruit_config_data
*config
= NULL
;
4020 const struct smb_filename
*smb_fname
= NULL
;
4023 smb_fname
= fsp
->fsp_name
;
4025 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct fruit_config_data
,
4026 return NT_STATUS_UNSUCCESSFUL
);
4028 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname
));
4030 status
= SMB_VFS_NEXT_FSTREAMINFO(handle
, fsp
, mem_ctx
,
4031 pnum_streams
, pstreams
);
4032 if (!NT_STATUS_IS_OK(status
)) {
4036 fruit_filter_empty_streams(pnum_streams
, pstreams
);
4038 status
= fruit_streaminfo_meta(handle
, fsp
, smb_fname
,
4039 mem_ctx
, pnum_streams
, pstreams
);
4040 if (!NT_STATUS_IS_OK(status
)) {
4044 status
= fruit_streaminfo_rsrc(handle
, fsp
, smb_fname
,
4045 mem_ctx
, pnum_streams
, pstreams
);
4046 if (!NT_STATUS_IS_OK(status
)) {
4050 return NT_STATUS_OK
;
4053 static int fruit_fntimes(vfs_handle_struct
*handle
,
4055 struct smb_file_time
*ft
)
4058 struct adouble
*ad
= NULL
;
4059 struct fruit_config_data
*config
= NULL
;
4061 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct fruit_config_data
,
4064 if ((config
->meta
!= FRUIT_META_NETATALK
) ||
4065 is_omit_timespec(&ft
->create_time
))
4067 return SMB_VFS_NEXT_FNTIMES(handle
, fsp
, ft
);
4070 DBG_DEBUG("set btime for %s to %s\n", fsp_str_dbg(fsp
),
4071 time_to_asc(convert_timespec_to_time_t(ft
->create_time
)));
4073 ad
= ad_fget(talloc_tos(), handle
, fsp
, ADOUBLE_META
);
4078 ad_setdate(ad
, AD_DATE_CREATE
| AD_DATE_UNIX
,
4079 convert_time_t_to_uint32_t(ft
->create_time
.tv_sec
));
4081 rc
= ad_fset(handle
, ad
, fsp
);
4087 DBG_WARNING("%s\n", fsp_str_dbg(fsp
));
4090 return SMB_VFS_NEXT_FNTIMES(handle
, fsp
, ft
);
4093 static int fruit_fallocate(struct vfs_handle_struct
*handle
,
4094 struct files_struct
*fsp
,
4099 struct fio
*fio
= fruit_get_complete_fio(handle
, fsp
);
4102 return SMB_VFS_NEXT_FALLOCATE(handle
, fsp
, mode
, offset
, len
);
4105 /* Let the pwrite code path handle it. */
4110 static int fruit_ftruncate_rsrc_xattr(struct vfs_handle_struct
*handle
,
4111 struct files_struct
*fsp
,
4114 #ifdef HAVE_ATTROPEN
4115 return SMB_VFS_NEXT_FTRUNCATE(handle
, fsp
, offset
);
4120 static int fruit_ftruncate_rsrc_adouble(struct vfs_handle_struct
*handle
,
4121 struct files_struct
*fsp
,
4124 struct fio
*fio
= fruit_get_complete_fio(handle
, fsp
);
4126 struct adouble
*ad
= NULL
;
4129 if (fio
== NULL
|| fio
->ad_fsp
== NULL
) {
4130 DBG_ERR("fio/ad_fsp=NULL for [%s]\n", fsp_str_dbg(fsp
));
4135 ad
= ad_fget(talloc_tos(), handle
, fio
->ad_fsp
, ADOUBLE_RSRC
);
4137 DBG_ERR("ad_fget [%s] failed [%s]\n",
4138 fsp_str_dbg(fio
->ad_fsp
), strerror(errno
));
4142 ad_off
= ad_getentryoff(ad
, ADEID_RFORK
);
4144 rc
= SMB_VFS_NEXT_FTRUNCATE(handle
, fio
->ad_fsp
, offset
+ ad_off
);
4150 ad_setentrylen(ad
, ADEID_RFORK
, offset
);
4152 rc
= ad_fset(handle
, ad
, fio
->ad_fsp
);
4154 DBG_ERR("ad_fset [%s] failed [%s]\n",
4155 fsp_str_dbg(fio
->ad_fsp
), strerror(errno
));
4164 static int fruit_ftruncate_rsrc_stream(struct vfs_handle_struct
*handle
,
4165 struct files_struct
*fsp
,
4168 return SMB_VFS_NEXT_FTRUNCATE(handle
, fsp
, offset
);
4171 static int fruit_ftruncate_rsrc(struct vfs_handle_struct
*handle
,
4172 struct files_struct
*fsp
,
4175 struct fio
*fio
= fruit_get_complete_fio(handle
, fsp
);
4179 DBG_ERR("Failed to fetch fsp extension");
4183 switch (fio
->config
->rsrc
) {
4184 case FRUIT_RSRC_XATTR
:
4185 ret
= fruit_ftruncate_rsrc_xattr(handle
, fsp
, offset
);
4188 case FRUIT_RSRC_ADFILE
:
4189 ret
= fruit_ftruncate_rsrc_adouble(handle
, fsp
, offset
);
4192 case FRUIT_RSRC_STREAM
:
4193 ret
= fruit_ftruncate_rsrc_stream(handle
, fsp
, offset
);
4197 DBG_ERR("Unexpected rsrc config [%d]\n", fio
->config
->rsrc
);
4205 static int fruit_ftruncate_meta(struct vfs_handle_struct
*handle
,
4206 struct files_struct
*fsp
,
4210 DBG_WARNING("ftruncate %s to %jd",
4211 fsp_str_dbg(fsp
), (intmax_t)offset
);
4212 /* OS X returns NT_STATUS_ALLOTTED_SPACE_EXCEEDED */
4217 /* OS X returns success but does nothing */
4218 DBG_INFO("ignoring ftruncate %s to %jd\n",
4219 fsp_str_dbg(fsp
), (intmax_t)offset
);
4223 static int fruit_ftruncate(struct vfs_handle_struct
*handle
,
4224 struct files_struct
*fsp
,
4227 struct fio
*fio
= fruit_get_complete_fio(handle
, fsp
);
4230 DBG_DEBUG("Path [%s] offset [%"PRIdMAX
"]\n", fsp_str_dbg(fsp
),
4234 return SMB_VFS_NEXT_FTRUNCATE(handle
, fsp
, offset
);
4237 if (fio
->type
== ADOUBLE_META
) {
4238 ret
= fruit_ftruncate_meta(handle
, fsp
, offset
);
4240 ret
= fruit_ftruncate_rsrc(handle
, fsp
, offset
);
4243 DBG_DEBUG("Path [%s] result [%d]\n", fsp_str_dbg(fsp
), ret
);
4247 static NTSTATUS
fruit_create_file(vfs_handle_struct
*handle
,
4248 struct smb_request
*req
,
4249 struct files_struct
*dirfsp
,
4250 struct smb_filename
*smb_fname
,
4251 uint32_t access_mask
,
4252 uint32_t share_access
,
4253 uint32_t create_disposition
,
4254 uint32_t create_options
,
4255 uint32_t file_attributes
,
4256 uint32_t oplock_request
,
4257 const struct smb2_lease
*lease
,
4258 uint64_t allocation_size
,
4259 uint32_t private_flags
,
4260 struct security_descriptor
*sd
,
4261 struct ea_list
*ea_list
,
4262 files_struct
**result
,
4264 const struct smb2_create_blobs
*in_context_blobs
,
4265 struct smb2_create_blobs
*out_context_blobs
)
4268 struct fruit_config_data
*config
= NULL
;
4269 files_struct
*fsp
= NULL
;
4270 bool internal_open
= (oplock_request
& INTERNAL_OPEN_ONLY
);
4273 status
= check_aapl(handle
, req
, in_context_blobs
, out_context_blobs
);
4274 if (!NT_STATUS_IS_OK(status
)) {
4278 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct fruit_config_data
,
4279 return NT_STATUS_UNSUCCESSFUL
);
4281 if (is_apple_stream(smb_fname
->stream_name
) &&
4283 config
->convert_adouble
)
4285 uint32_t conv_flags
= 0;
4287 if (config
->wipe_intentionally_left_blank_rfork
) {
4288 conv_flags
|= AD_CONV_WIPE_BLANK
;
4290 if (config
->delete_empty_adfiles
) {
4291 conv_flags
|= AD_CONV_DELETE
;
4294 ret
= ad_convert(handle
,
4296 macos_string_replace_map
,
4299 DBG_ERR("ad_convert(\"%s\") failed\n",
4300 smb_fname_str_dbg(smb_fname
));
4304 status
= SMB_VFS_NEXT_CREATE_FILE(
4305 handle
, req
, dirfsp
, smb_fname
,
4306 access_mask
, share_access
,
4307 create_disposition
, create_options
,
4308 file_attributes
, oplock_request
,
4310 allocation_size
, private_flags
,
4311 sd
, ea_list
, result
,
4312 pinfo
, in_context_blobs
, out_context_blobs
);
4313 if (!NT_STATUS_IS_OK(status
)) {
4319 if (global_fruit_config
.nego_aapl
) {
4320 if (config
->posix_rename
&& fsp
->fsp_flags
.is_directory
) {
4322 * Enable POSIX directory rename behaviour
4324 fsp
->posix_flags
|= FSP_POSIX_FLAGS_RENAME
;
4329 * If this is a plain open for existing files, opening an 0
4330 * byte size resource fork MUST fail with
4331 * NT_STATUS_OBJECT_NAME_NOT_FOUND.
4333 * Cf the vfs_fruit torture tests in test_rfork_create().
4335 if (global_fruit_config
.nego_aapl
&&
4336 create_disposition
== FILE_OPEN
&&
4337 smb_fname
->st
.st_ex_size
== 0 &&
4338 is_named_stream(smb_fname
))
4340 status
= NT_STATUS_OBJECT_NAME_NOT_FOUND
;
4344 if (is_named_stream(smb_fname
) || fsp
->fsp_flags
.is_directory
) {
4348 if ((config
->locking
== FRUIT_LOCKING_NETATALK
) &&
4349 (fsp
->op
!= NULL
) &&
4350 !fsp
->fsp_flags
.is_pathref
)
4352 status
= fruit_check_access(
4356 if (!NT_STATUS_IS_OK(status
)) {
4364 DEBUG(10, ("fruit_create_file: %s\n", nt_errstr(status
)));
4367 close_file_free(req
, &fsp
, ERROR_CLOSE
);
4374 static NTSTATUS
fruit_freaddir_attr(struct vfs_handle_struct
*handle
,
4375 struct files_struct
*fsp
,
4376 TALLOC_CTX
*mem_ctx
,
4377 struct readdir_attr_data
**pattr_data
)
4379 struct fruit_config_data
*config
= NULL
;
4380 struct readdir_attr_data
*attr_data
;
4381 uint32_t conv_flags
= 0;
4385 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
4386 struct fruit_config_data
,
4387 return NT_STATUS_UNSUCCESSFUL
);
4389 if (!global_fruit_config
.nego_aapl
) {
4390 return SMB_VFS_NEXT_FREADDIR_ATTR(handle
,
4396 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp
));
4398 if (config
->convert_adouble
) {
4399 if (config
->wipe_intentionally_left_blank_rfork
) {
4400 conv_flags
|= AD_CONV_WIPE_BLANK
;
4402 if (config
->delete_empty_adfiles
) {
4403 conv_flags
|= AD_CONV_DELETE
;
4406 ret
= ad_convert(handle
,
4408 macos_string_replace_map
,
4411 DBG_ERR("ad_convert(\"%s\") failed\n",
4416 *pattr_data
= talloc_zero(mem_ctx
, struct readdir_attr_data
);
4417 if (*pattr_data
== NULL
) {
4418 return NT_STATUS_NO_MEMORY
;
4420 attr_data
= *pattr_data
;
4421 attr_data
->type
= RDATTR_AAPL
;
4424 * Mac metadata: compressed FinderInfo, resource fork length
4427 status
= readdir_attr_macmeta(handle
, fsp
->fsp_name
, attr_data
);
4428 if (!NT_STATUS_IS_OK(status
)) {
4430 * Error handling is tricky: if we return failure from
4431 * this function, the corresponding directory entry
4432 * will to be passed to the client, so we really just
4433 * want to error out on fatal errors.
4435 if (!NT_STATUS_EQUAL(status
, NT_STATUS_ACCESS_DENIED
)) {
4443 if (config
->unix_info_enabled
) {
4444 attr_data
->attr_data
.aapl
.unix_mode
=
4445 fsp
->fsp_name
->st
.st_ex_mode
;
4451 if (!config
->readdir_attr_max_access
) {
4452 attr_data
->attr_data
.aapl
.max_access
= FILE_GENERIC_ALL
;
4454 status
= smbd_calculate_access_mask_fsp(fsp
->conn
->cwd_fsp
,
4457 SEC_FLAG_MAXIMUM_ALLOWED
,
4458 &attr_data
->attr_data
.aapl
.max_access
);
4459 if (!NT_STATUS_IS_OK(status
)) {
4464 return NT_STATUS_OK
;
4467 DBG_WARNING("Path [%s], error: %s\n", fsp_str_dbg(fsp
),
4469 TALLOC_FREE(*pattr_data
);
4473 static NTSTATUS
fruit_fget_nt_acl(vfs_handle_struct
*handle
,
4475 uint32_t security_info
,
4476 TALLOC_CTX
*mem_ctx
,
4477 struct security_descriptor
**ppdesc
)
4480 struct security_ace ace
;
4482 struct fruit_config_data
*config
;
4484 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
4485 struct fruit_config_data
,
4486 return NT_STATUS_UNSUCCESSFUL
);
4488 status
= SMB_VFS_NEXT_FGET_NT_ACL(handle
, fsp
, security_info
,
4490 if (!NT_STATUS_IS_OK(status
)) {
4495 * Add MS NFS style ACEs with uid, gid and mode
4497 if (!global_fruit_config
.nego_aapl
) {
4498 return NT_STATUS_OK
;
4500 if (!config
->unix_info_enabled
) {
4501 return NT_STATUS_OK
;
4504 /* First remove any existing ACE's with NFS style mode/uid/gid SIDs. */
4505 status
= remove_virtual_nfs_aces(*ppdesc
);
4506 if (!NT_STATUS_IS_OK(status
)) {
4507 DBG_WARNING("failed to remove MS NFS style ACEs\n");
4511 /* MS NFS style mode */
4512 sid_compose(&sid
, &global_sid_Unix_NFS_Mode
, fsp
->fsp_name
->st
.st_ex_mode
);
4513 init_sec_ace(&ace
, &sid
, SEC_ACE_TYPE_ACCESS_DENIED
, 0, 0);
4514 status
= security_descriptor_dacl_add(*ppdesc
, &ace
);
4515 if (!NT_STATUS_IS_OK(status
)) {
4516 DEBUG(1,("failed to add MS NFS style ACE\n"));
4520 /* MS NFS style uid */
4521 sid_compose(&sid
, &global_sid_Unix_NFS_Users
, fsp
->fsp_name
->st
.st_ex_uid
);
4522 init_sec_ace(&ace
, &sid
, SEC_ACE_TYPE_ACCESS_DENIED
, 0, 0);
4523 status
= security_descriptor_dacl_add(*ppdesc
, &ace
);
4524 if (!NT_STATUS_IS_OK(status
)) {
4525 DEBUG(1,("failed to add MS NFS style ACE\n"));
4529 /* MS NFS style gid */
4530 sid_compose(&sid
, &global_sid_Unix_NFS_Groups
, fsp
->fsp_name
->st
.st_ex_gid
);
4531 init_sec_ace(&ace
, &sid
, SEC_ACE_TYPE_ACCESS_DENIED
, 0, 0);
4532 status
= security_descriptor_dacl_add(*ppdesc
, &ace
);
4533 if (!NT_STATUS_IS_OK(status
)) {
4534 DEBUG(1,("failed to add MS NFS style ACE\n"));
4538 return NT_STATUS_OK
;
4541 static NTSTATUS
fruit_fset_nt_acl(vfs_handle_struct
*handle
,
4543 uint32_t security_info_sent
,
4544 const struct security_descriptor
*orig_psd
)
4548 mode_t ms_nfs_mode
= 0;
4550 struct security_descriptor
*psd
= NULL
;
4551 uint32_t orig_num_aces
= 0;
4553 if (orig_psd
->dacl
!= NULL
) {
4554 orig_num_aces
= orig_psd
->dacl
->num_aces
;
4557 psd
= security_descriptor_copy(talloc_tos(), orig_psd
);
4559 return NT_STATUS_NO_MEMORY
;
4562 DBG_DEBUG("fruit_fset_nt_acl: %s\n", fsp_str_dbg(fsp
));
4564 status
= check_ms_nfs(handle
, fsp
, psd
, &ms_nfs_mode
, &do_chmod
);
4565 if (!NT_STATUS_IS_OK(status
)) {
4566 DEBUG(1, ("fruit_fset_nt_acl: check_ms_nfs failed%s\n", fsp_str_dbg(fsp
)));
4572 * If only ms_nfs ACE entries were sent, ensure we set the DACL
4573 * sent/present flags correctly now we've removed them.
4576 if (orig_num_aces
!= 0) {
4578 * Are there any ACE's left ?
4580 if (psd
->dacl
->num_aces
== 0) {
4581 /* No - clear the DACL sent/present flags. */
4582 security_info_sent
&= ~SECINFO_DACL
;
4583 psd
->type
&= ~SEC_DESC_DACL_PRESENT
;
4587 status
= SMB_VFS_NEXT_FSET_NT_ACL(handle
, fsp
, security_info_sent
, psd
);
4588 if (!NT_STATUS_IS_OK(status
)) {
4589 DEBUG(1, ("fruit_fset_nt_acl: SMB_VFS_NEXT_FSET_NT_ACL failed%s\n", fsp_str_dbg(fsp
)));
4595 result
= SMB_VFS_FCHMOD(fsp
, ms_nfs_mode
);
4597 DBG_WARNING("%s, result: %d, %04o error %s\n",
4600 (unsigned)ms_nfs_mode
,
4602 status
= map_nt_error_from_unix(errno
);
4609 return NT_STATUS_OK
;
4612 static struct vfs_offload_ctx
*fruit_offload_ctx
;
4614 struct fruit_offload_read_state
{
4615 struct vfs_handle_struct
*handle
;
4616 struct tevent_context
*ev
;
4624 static void fruit_offload_read_done(struct tevent_req
*subreq
);
4626 static struct tevent_req
*fruit_offload_read_send(
4627 TALLOC_CTX
*mem_ctx
,
4628 struct tevent_context
*ev
,
4629 struct vfs_handle_struct
*handle
,
4636 struct tevent_req
*req
= NULL
;
4637 struct tevent_req
*subreq
= NULL
;
4638 struct fruit_offload_read_state
*state
= NULL
;
4640 req
= tevent_req_create(mem_ctx
, &state
,
4641 struct fruit_offload_read_state
);
4645 *state
= (struct fruit_offload_read_state
) {
4652 subreq
= SMB_VFS_NEXT_OFFLOAD_READ_SEND(mem_ctx
, ev
, handle
, fsp
,
4653 fsctl
, ttl
, offset
, to_copy
);
4654 if (tevent_req_nomem(subreq
, req
)) {
4655 return tevent_req_post(req
, ev
);
4657 tevent_req_set_callback(subreq
, fruit_offload_read_done
, req
);
4661 static void fruit_offload_read_done(struct tevent_req
*subreq
)
4663 struct tevent_req
*req
= tevent_req_callback_data(
4664 subreq
, struct tevent_req
);
4665 struct fruit_offload_read_state
*state
= tevent_req_data(
4666 req
, struct fruit_offload_read_state
);
4669 status
= SMB_VFS_NEXT_OFFLOAD_READ_RECV(subreq
,
4675 TALLOC_FREE(subreq
);
4676 if (tevent_req_nterror(req
, status
)) {
4680 if (state
->fsctl
!= FSCTL_SRV_REQUEST_RESUME_KEY
) {
4681 tevent_req_done(req
);
4685 status
= vfs_offload_token_ctx_init(state
->fsp
->conn
->sconn
->client
,
4686 &fruit_offload_ctx
);
4687 if (tevent_req_nterror(req
, status
)) {
4691 status
= vfs_offload_token_db_store_fsp(fruit_offload_ctx
,
4694 if (tevent_req_nterror(req
, status
)) {
4698 tevent_req_done(req
);
4702 static NTSTATUS
fruit_offload_read_recv(struct tevent_req
*req
,
4703 struct vfs_handle_struct
*handle
,
4704 TALLOC_CTX
*mem_ctx
,
4709 struct fruit_offload_read_state
*state
= tevent_req_data(
4710 req
, struct fruit_offload_read_state
);
4713 if (tevent_req_is_nterror(req
, &status
)) {
4714 tevent_req_received(req
);
4718 *flags
= state
->flags
;
4719 *xferlen
= state
->xferlen
;
4720 token
->length
= state
->token
.length
;
4721 token
->data
= talloc_move(mem_ctx
, &state
->token
.data
);
4723 tevent_req_received(req
);
4724 return NT_STATUS_OK
;
4727 struct fruit_offload_write_state
{
4728 struct vfs_handle_struct
*handle
;
4730 struct files_struct
*src_fsp
;
4731 struct files_struct
*dst_fsp
;
4735 static void fruit_offload_write_done(struct tevent_req
*subreq
);
4736 static struct tevent_req
*fruit_offload_write_send(struct vfs_handle_struct
*handle
,
4737 TALLOC_CTX
*mem_ctx
,
4738 struct tevent_context
*ev
,
4741 off_t transfer_offset
,
4742 struct files_struct
*dest_fsp
,
4746 struct tevent_req
*req
, *subreq
;
4747 struct fruit_offload_write_state
*state
;
4749 struct fruit_config_data
*config
;
4750 off_t src_off
= transfer_offset
;
4751 files_struct
*src_fsp
= NULL
;
4752 off_t to_copy
= num
;
4753 bool copyfile_enabled
= false;
4755 DEBUG(10,("soff: %ju, doff: %ju, len: %ju\n",
4756 (uintmax_t)src_off
, (uintmax_t)dest_off
, (uintmax_t)num
));
4758 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
4759 struct fruit_config_data
,
4762 req
= tevent_req_create(mem_ctx
, &state
,
4763 struct fruit_offload_write_state
);
4767 state
->handle
= handle
;
4768 state
->dst_fsp
= dest_fsp
;
4771 case FSCTL_SRV_COPYCHUNK
:
4772 case FSCTL_SRV_COPYCHUNK_WRITE
:
4773 copyfile_enabled
= config
->copyfile_enabled
;
4780 * Check if this a OS X copyfile style copychunk request with
4781 * a requested chunk count of 0 that was translated to a
4782 * offload_write_send VFS call overloading the parameters src_off
4783 * = dest_off = num = 0.
4785 if (copyfile_enabled
&& num
== 0 && src_off
== 0 && dest_off
== 0) {
4786 status
= vfs_offload_token_db_fetch_fsp(
4787 fruit_offload_ctx
, token
, &src_fsp
);
4788 if (tevent_req_nterror(req
, status
)) {
4789 return tevent_req_post(req
, ev
);
4791 state
->src_fsp
= src_fsp
;
4793 status
= vfs_stat_fsp(src_fsp
);
4794 if (tevent_req_nterror(req
, status
)) {
4795 return tevent_req_post(req
, ev
);
4798 to_copy
= src_fsp
->fsp_name
->st
.st_ex_size
;
4799 state
->is_copyfile
= true;
4802 subreq
= SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle
,
4811 if (tevent_req_nomem(subreq
, req
)) {
4812 return tevent_req_post(req
, ev
);
4815 tevent_req_set_callback(subreq
, fruit_offload_write_done
, req
);
4819 static void fruit_offload_write_done(struct tevent_req
*subreq
)
4821 struct tevent_req
*req
= tevent_req_callback_data(
4822 subreq
, struct tevent_req
);
4823 struct fruit_offload_write_state
*state
= tevent_req_data(
4824 req
, struct fruit_offload_write_state
);
4826 unsigned int num_streams
= 0;
4827 struct stream_struct
*streams
= NULL
;
4829 struct smb_filename
*src_fname_tmp
= NULL
;
4830 struct smb_filename
*dst_fname_tmp
= NULL
;
4832 status
= SMB_VFS_NEXT_OFFLOAD_WRITE_RECV(state
->handle
,
4835 TALLOC_FREE(subreq
);
4836 if (tevent_req_nterror(req
, status
)) {
4840 if (!state
->is_copyfile
) {
4841 tevent_req_done(req
);
4846 * Now copy all remaining streams. We know the share supports
4847 * streams, because we're in vfs_fruit. We don't do this async
4848 * because streams are few and small.
4850 status
= vfs_fstreaminfo(state
->src_fsp
,
4851 req
, &num_streams
, &streams
);
4852 if (tevent_req_nterror(req
, status
)) {
4856 if (num_streams
== 1) {
4857 /* There is always one stream, ::$DATA. */
4858 tevent_req_done(req
);
4862 for (i
= 0; i
< num_streams
; i
++) {
4863 DEBUG(10, ("%s: stream: '%s'/%zu\n",
4864 __func__
, streams
[i
].name
, (size_t)streams
[i
].size
));
4866 src_fname_tmp
= synthetic_smb_fname(
4868 state
->src_fsp
->fsp_name
->base_name
,
4871 state
->src_fsp
->fsp_name
->twrp
,
4872 state
->src_fsp
->fsp_name
->flags
);
4873 if (tevent_req_nomem(src_fname_tmp
, req
)) {
4877 if (is_ntfs_default_stream_smb_fname(src_fname_tmp
)) {
4878 TALLOC_FREE(src_fname_tmp
);
4882 dst_fname_tmp
= synthetic_smb_fname(
4884 state
->dst_fsp
->fsp_name
->base_name
,
4887 state
->dst_fsp
->fsp_name
->twrp
,
4888 state
->dst_fsp
->fsp_name
->flags
);
4889 if (tevent_req_nomem(dst_fname_tmp
, req
)) {
4890 TALLOC_FREE(src_fname_tmp
);
4894 status
= copy_file(req
,
4895 state
->handle
->conn
,
4899 if (!NT_STATUS_IS_OK(status
)) {
4900 DEBUG(1, ("%s: copy %s to %s failed: %s\n", __func__
,
4901 smb_fname_str_dbg(src_fname_tmp
),
4902 smb_fname_str_dbg(dst_fname_tmp
),
4903 nt_errstr(status
)));
4904 TALLOC_FREE(src_fname_tmp
);
4905 TALLOC_FREE(dst_fname_tmp
);
4906 tevent_req_nterror(req
, status
);
4910 TALLOC_FREE(src_fname_tmp
);
4911 TALLOC_FREE(dst_fname_tmp
);
4914 TALLOC_FREE(streams
);
4915 TALLOC_FREE(src_fname_tmp
);
4916 TALLOC_FREE(dst_fname_tmp
);
4917 tevent_req_done(req
);
4920 static NTSTATUS
fruit_offload_write_recv(struct vfs_handle_struct
*handle
,
4921 struct tevent_req
*req
,
4924 struct fruit_offload_write_state
*state
= tevent_req_data(
4925 req
, struct fruit_offload_write_state
);
4928 if (tevent_req_is_nterror(req
, &status
)) {
4929 DEBUG(1, ("server side copy chunk failed: %s\n",
4930 nt_errstr(status
)));
4932 tevent_req_received(req
);
4936 *copied
= state
->copied
;
4937 tevent_req_received(req
);
4939 return NT_STATUS_OK
;
4942 static char *fruit_get_bandsize_line(char **lines
, int numlines
)
4945 static bool re_initialized
= false;
4949 if (!re_initialized
) {
4950 ret
= regcomp(&re
, "^[[:blank:]]*<key>band-size</key>$", 0);
4954 re_initialized
= true;
4957 for (i
= 0; i
< numlines
; i
++) {
4958 regmatch_t matches
[1];
4960 ret
= regexec(&re
, lines
[i
], 1, matches
, 0);
4963 * Check if the match was on the last line, sa we want
4964 * the subsequent line.
4966 if (i
+ 1 == numlines
) {
4969 return lines
[i
+ 1];
4971 if (ret
!= REG_NOMATCH
) {
4979 static bool fruit_get_bandsize_from_line(char *line
, size_t *_band_size
)
4982 static bool re_initialized
= false;
4983 regmatch_t matches
[2];
4988 if (!re_initialized
) {
4991 "<integer>\\([[:digit:]]*\\)</integer>$",
4996 re_initialized
= true;
4999 ret
= regexec(&re
, line
, 2, matches
, 0);
5001 DBG_ERR("regex failed [%s]\n", line
);
5005 line
[matches
[1].rm_eo
] = '\0';
5007 ok
= conv_str_u64(&line
[matches
[1].rm_so
], &band_size
);
5011 *_band_size
= (size_t)band_size
;
5016 * This reads and parses an Info.plist from a TM sparsebundle looking for the
5017 * "band-size" key and value.
5019 static bool fruit_get_bandsize(vfs_handle_struct
*handle
,
5023 #define INFO_PLIST_MAX_SIZE 64*1024
5025 struct smb_filename
*smb_fname
= NULL
;
5026 files_struct
*fsp
= NULL
;
5027 uint8_t *file_data
= NULL
;
5028 char **lines
= NULL
;
5029 char *band_size_line
= NULL
;
5030 size_t plist_file_size
;
5037 plist
= talloc_asprintf(talloc_tos(),
5039 handle
->conn
->connectpath
,
5041 if (plist
== NULL
) {
5046 smb_fname
= synthetic_smb_fname(talloc_tos(),
5052 if (smb_fname
== NULL
) {
5057 ret
= SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
5059 DBG_INFO("Ignoring Sparsebundle without Info.plist [%s]\n", dir
);
5064 plist_file_size
= smb_fname
->st
.st_ex_size
;
5066 if (plist_file_size
> INFO_PLIST_MAX_SIZE
) {
5067 DBG_INFO("%s is too large, ignoring\n", plist
);
5072 status
= SMB_VFS_NEXT_CREATE_FILE(
5076 smb_fname
, /* fname */
5077 FILE_GENERIC_READ
, /* access_mask */
5078 FILE_SHARE_READ
| FILE_SHARE_WRITE
, /* share_access */
5079 FILE_OPEN
, /* create_disposition */
5080 0, /* create_options */
5081 0, /* file_attributes */
5082 INTERNAL_OPEN_ONLY
, /* oplock_request */
5084 0, /* allocation_size */
5085 0, /* private_flags */
5090 NULL
, NULL
); /* create context */
5091 if (!NT_STATUS_IS_OK(status
)) {
5092 DBG_INFO("Opening [%s] failed [%s]\n",
5093 smb_fname_str_dbg(smb_fname
), nt_errstr(status
));
5098 file_data
= talloc_zero_array(talloc_tos(),
5100 plist_file_size
+ 1);
5101 if (file_data
== NULL
) {
5106 nread
= SMB_VFS_NEXT_PREAD(handle
, fsp
, file_data
, plist_file_size
, 0);
5107 if (nread
!= plist_file_size
) {
5108 DBG_ERR("Short read on [%s]: %zu/%zd\n",
5109 fsp_str_dbg(fsp
), nread
, plist_file_size
);
5115 status
= close_file_free(NULL
, &fsp
, NORMAL_CLOSE
);
5116 if (!NT_STATUS_IS_OK(status
)) {
5117 DBG_ERR("close_file failed: %s\n", nt_errstr(status
));
5122 lines
= file_lines_parse((char *)file_data
,
5126 if (lines
== NULL
) {
5131 band_size_line
= fruit_get_bandsize_line(lines
, numlines
);
5132 if (band_size_line
== NULL
) {
5133 DBG_ERR("Didn't find band-size key in [%s]\n",
5134 smb_fname_str_dbg(smb_fname
));
5139 ok
= fruit_get_bandsize_from_line(band_size_line
, band_size
);
5141 DBG_ERR("fruit_get_bandsize_from_line failed\n");
5145 DBG_DEBUG("Parsed band-size [%zu] for [%s]\n", *band_size
, plist
);
5149 status
= close_file_free(NULL
, &fsp
, NORMAL_CLOSE
);
5150 if (!NT_STATUS_IS_OK(status
)) {
5151 DBG_ERR("close_file failed: %s\n", nt_errstr(status
));
5155 TALLOC_FREE(smb_fname
);
5156 TALLOC_FREE(file_data
);
5161 struct fruit_disk_free_state
{
5165 static bool fruit_get_num_bands(vfs_handle_struct
*handle
,
5170 struct smb_filename
*bands_dir
= NULL
;
5171 struct smb_Dir
*dir_hnd
= NULL
;
5172 const char *dname
= NULL
;
5173 char *talloced
= NULL
;
5177 path
= talloc_asprintf(talloc_tos(),
5179 handle
->conn
->connectpath
,
5185 bands_dir
= synthetic_smb_fname(talloc_tos(),
5192 if (bands_dir
== NULL
) {
5196 status
= OpenDir(talloc_tos(),
5202 if (!NT_STATUS_IS_OK(status
)) {
5203 TALLOC_FREE(bands_dir
);
5204 errno
= map_errno_from_nt_status(status
);
5210 while ((dname
= ReadDirName(dir_hnd
, &talloced
)) != NULL
) {
5211 if (ISDOT(dname
) || ISDOTDOT(dname
)) {
5216 TALLOC_FREE(dir_hnd
);
5218 DBG_DEBUG("%zu bands in [%s]\n", nbands
, smb_fname_str_dbg(bands_dir
));
5220 TALLOC_FREE(bands_dir
);
5226 static bool fruit_tmsize_do_dirent(vfs_handle_struct
*handle
,
5227 struct fruit_disk_free_state
*state
,
5232 size_t sparsebundle_strlen
= strlen("sparsebundle");
5233 size_t bandsize
= 0;
5237 p
= strstr(name
, "sparsebundle");
5242 if (p
[sparsebundle_strlen
] != '\0') {
5246 DBG_DEBUG("Processing sparsebundle [%s]\n", name
);
5248 ok
= fruit_get_bandsize(handle
, name
, &bandsize
);
5251 * Beware of race conditions: this may be an uninitialized
5252 * Info.plist that a client is just creating. We don't want let
5253 * this to trigger complete failure.
5255 DBG_ERR("Processing sparsebundle [%s] failed\n", name
);
5259 ok
= fruit_get_num_bands(handle
, name
, &nbands
);
5262 * Beware of race conditions: this may be a backup sparsebundle
5263 * in an early stage lacking a bands subdirectory. We don't want
5264 * let this to trigger complete failure.
5266 DBG_ERR("Processing sparsebundle [%s] failed\n", name
);
5271 * Arithmetic on 32-bit systems may cause overflow, depending on
5272 * size_t precision. First we check its unlikely, then we
5273 * force the precision into target off_t, then we check that
5274 * the total did not overflow either.
5276 if (bandsize
> SIZE_MAX
/nbands
) {
5277 DBG_ERR("tmsize potential overflow: bandsize [%zu] nbands [%zu]\n",
5281 tm_size
= (off_t
)bandsize
* (off_t
)nbands
;
5283 if (state
->total_size
+ tm_size
< state
->total_size
) {
5284 DBG_ERR("tm total size overflow: bandsize [%zu] nbands [%zu]\n",
5289 state
->total_size
+= tm_size
;
5291 DBG_DEBUG("[%s] tm_size [%jd] total_size [%jd]\n",
5292 name
, (intmax_t)tm_size
, (intmax_t)state
->total_size
);
5298 * Calculate used size of a TimeMachine volume
5300 * This assumes that the volume is used only for TimeMachine.
5302 * - readdir(basedir of share), then
5303 * - for every element that matches regex "^\(.*\)\.sparsebundle$" :
5304 * - parse "\1.sparsebundle/Info.plist" and read the band-size XML key
5305 * - count band files in "\1.sparsebundle/bands/"
5306 * - calculate used size of all bands: band_count * band_size
5308 static uint64_t fruit_disk_free(vfs_handle_struct
*handle
,
5309 const struct smb_filename
*smb_fname
,
5314 struct fruit_config_data
*config
= NULL
;
5315 struct fruit_disk_free_state state
= {0};
5316 struct smb_Dir
*dir_hnd
= NULL
;
5317 const char *dname
= NULL
;
5318 char *talloced
= NULL
;
5324 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
5325 struct fruit_config_data
,
5328 if (!config
->time_machine
||
5329 config
->time_machine_max_size
== 0)
5331 return SMB_VFS_NEXT_DISK_FREE(handle
,
5338 status
= OpenDir(talloc_tos(),
5344 if (!NT_STATUS_IS_OK(status
)) {
5345 errno
= map_errno_from_nt_status(status
);
5349 while ((dname
= ReadDirName(dir_hnd
, &talloced
)) != NULL
) {
5350 ok
= fruit_tmsize_do_dirent(handle
, &state
, dname
);
5352 TALLOC_FREE(talloced
);
5353 TALLOC_FREE(dir_hnd
);
5356 TALLOC_FREE(talloced
);
5359 TALLOC_FREE(dir_hnd
);
5361 dsize
= config
->time_machine_max_size
/ 512;
5362 dfree
= dsize
- (state
.total_size
/ 512);
5363 if (dfree
> dsize
) {
5373 static uint64_t fruit_fs_file_id(struct vfs_handle_struct
*handle
,
5374 const SMB_STRUCT_STAT
*psbuf
)
5376 struct fruit_config_data
*config
= NULL
;
5378 SMB_VFS_HANDLE_GET_DATA(handle
, config
,
5379 struct fruit_config_data
,
5382 if (global_fruit_config
.nego_aapl
&&
5383 config
->aapl_zero_file_id
)
5388 return SMB_VFS_NEXT_FS_FILE_ID(handle
, psbuf
);
5391 static struct vfs_fn_pointers vfs_fruit_fns
= {
5392 .connect_fn
= fruit_connect
,
5393 .disk_free_fn
= fruit_disk_free
,
5395 /* File operations */
5396 .fchmod_fn
= fruit_fchmod
,
5397 .unlinkat_fn
= fruit_unlinkat
,
5398 .renameat_fn
= fruit_renameat
,
5399 .openat_fn
= fruit_openat
,
5400 .close_fn
= fruit_close
,
5401 .pread_fn
= fruit_pread
,
5402 .pwrite_fn
= fruit_pwrite
,
5403 .pread_send_fn
= fruit_pread_send
,
5404 .pread_recv_fn
= fruit_pread_recv
,
5405 .pwrite_send_fn
= fruit_pwrite_send
,
5406 .pwrite_recv_fn
= fruit_pwrite_recv
,
5407 .fsync_send_fn
= fruit_fsync_send
,
5408 .fsync_recv_fn
= fruit_fsync_recv
,
5409 .stat_fn
= fruit_stat
,
5410 .lstat_fn
= fruit_lstat
,
5411 .fstat_fn
= fruit_fstat
,
5412 .fstreaminfo_fn
= fruit_fstreaminfo
,
5413 .fntimes_fn
= fruit_fntimes
,
5414 .ftruncate_fn
= fruit_ftruncate
,
5415 .fallocate_fn
= fruit_fallocate
,
5416 .create_file_fn
= fruit_create_file
,
5417 .freaddir_attr_fn
= fruit_freaddir_attr
,
5418 .offload_read_send_fn
= fruit_offload_read_send
,
5419 .offload_read_recv_fn
= fruit_offload_read_recv
,
5420 .offload_write_send_fn
= fruit_offload_write_send
,
5421 .offload_write_recv_fn
= fruit_offload_write_recv
,
5422 .fs_file_id_fn
= fruit_fs_file_id
,
5424 /* NT ACL operations */
5425 .fget_nt_acl_fn
= fruit_fget_nt_acl
,
5426 .fset_nt_acl_fn
= fruit_fset_nt_acl
,
5430 NTSTATUS
vfs_fruit_init(TALLOC_CTX
*ctx
)
5432 NTSTATUS ret
= smb_register_vfs(SMB_VFS_INTERFACE_VERSION
, "fruit",
5434 if (!NT_STATUS_IS_OK(ret
)) {
5438 vfs_fruit_debug_level
= debug_add_class("fruit");
5439 if (vfs_fruit_debug_level
== -1) {
5440 vfs_fruit_debug_level
= DBGC_VFS
;
5441 DEBUG(0, ("%s: Couldn't register custom debugging class!\n",
5444 DEBUG(10, ("%s: Debug class number of '%s': %d\n",
5445 "vfs_fruit_init","fruit",vfs_fruit_debug_level
));