smbd: replace CHECK_WRITE() macro with calls to check_any_access_fsp()
[Samba.git] / source3 / modules / vfs_fruit.c
blob49a1723864e18c6530d6eff987672e7d7e4652ce
1 /*
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/>.
20 #include "includes.h"
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"
28 #include "messages.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
59 * share modes.
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
66 * blobs content.
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".
78 * Extended Attributes
79 * ===================
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
91 * TODO
92 * ====
94 * - log diagnostic if any needed VFS module is not loaded
95 * (eg with lp_vfs_objects())
96 * - add tests
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;
106 #undef DBGC_CLASS
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 */
124 bool use_copyfile;
125 bool readdir_attr_enabled;
126 bool unix_info_enabled;
127 bool copyfile_enabled;
128 bool veto_appledouble;
129 bool posix_rename;
130 bool aapl_zero_file_id;
131 const char *model;
132 bool time_machine;
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) */
156 { -1, NULL}
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 */
162 { -1, NULL}
165 static const struct enum_list fruit_locking[] = {
166 {FRUIT_LOCKING_NETATALK, "netatalk"}, /* synchronize locks with Netatalk */
167 {FRUIT_LOCKING_NONE, "none"},
168 { -1, NULL}
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 */
174 { -1, NULL}
177 struct fio {
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 */
190 adouble_type_t type;
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
197 * create the stream.
199 bool fake_fd;
200 int flags;
201 int mode;
204 /*****************************************************************************
205 * Helper functions
206 *****************************************************************************/
208 static struct adouble *ad_get_meta_fsp(TALLOC_CTX *ctx,
209 vfs_handle_struct *handle,
210 const struct smb_filename *smb_fname)
212 NTSTATUS status;
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,
222 config,
223 struct fruit_config_data,
224 return NULL);
226 if (config->in_openat_pathref_fsp) {
227 return NULL;
230 smb_fname_cp = cp_smb_filename(ctx,
231 smb_fname);
232 if (smb_fname_cp == NULL) {
233 return 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,
238 smb_fname_cp);
239 config->in_openat_pathref_fsp = false;
240 if (!NT_STATUS_IS_OK(status)) {
241 TALLOC_FREE(smb_fname_cp);
242 return NULL;
245 ad = ad_get(ctx, handle, smb_fname_cp, ADOUBLE_META);
246 TALLOC_FREE(smb_fname_cp);
247 return ad;
250 static struct fio *fruit_get_complete_fio(vfs_handle_struct *handle,
251 files_struct *fsp)
253 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
255 if (fio == NULL) {
256 return NULL;
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
263 * module.
265 return NULL;
268 return fio;
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;
277 int enumval = -1;
278 const char *tm_size_str = NULL;
280 config = talloc_zero(handle->conn, struct fruit_config_data);
281 if (!config) {
282 DEBUG(1, ("talloc_zero() failed\n"));
283 errno = ENOMEM;
284 return -1;
287 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
288 "resource", fruit_rsrc, FRUIT_RSRC_ADFILE);
289 if (enumval == -1) {
290 DEBUG(1, ("value for %s: resource type unknown\n",
291 FRUIT_PARAM_TYPE_NAME));
292 return -1;
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);
298 if (enumval == -1) {
299 DEBUG(1, ("value for %s: metadata type unknown\n",
300 FRUIT_PARAM_TYPE_NAME));
301 return -1;
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);
307 if (enumval == -1) {
308 DEBUG(1, ("value for %s: locking type unknown\n",
309 FRUIT_PARAM_TYPE_NAME));
310 return -1;
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);
316 if (enumval == -1) {
317 DEBUG(1, ("value for %s: encoding type unknown\n",
318 FRUIT_PARAM_TYPE_NAME));
319 return -1;
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,
326 "veto_appledouble",
327 true);
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,
340 "copyfile", false);
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,
382 return -1);
384 return 0;
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,
390 off_t alloc_size)
392 struct stream_struct *tmp;
394 tmp = talloc_realloc(mem_ctx, *streams, struct stream_struct,
395 (*num_streams)+1);
396 if (tmp == NULL) {
397 return false;
400 tmp[*num_streams].name = talloc_asprintf(tmp, "%s:$DATA", name);
401 if (tmp[*num_streams].name == NULL) {
402 return false;
405 tmp[*num_streams].size = size;
406 tmp[*num_streams].alloc_size = alloc_size;
408 *streams = tmp;
409 *num_streams += 1;
410 return true;
413 static bool filter_empty_rsrc_stream(unsigned int *num_streams,
414 struct stream_struct **streams)
416 struct stream_struct *tmp = *streams;
417 unsigned int i;
419 if (*num_streams == 0) {
420 return true;
423 for (i = 0; i < *num_streams; i++) {
424 if (strequal_m(tmp[i].name, AFPRESOURCE_STREAM)) {
425 break;
429 if (i == *num_streams) {
430 return true;
433 if (tmp[i].size > 0) {
434 return true;
437 TALLOC_FREE(tmp[i].name);
438 ARRAY_DEL_ELEMENT(tmp, i, *num_streams);
439 *num_streams -= 1;
440 return true;
443 static bool del_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
444 struct stream_struct **streams,
445 const char *name)
447 struct stream_struct *tmp = *streams;
448 unsigned int i;
450 if (*num_streams == 0) {
451 return true;
454 for (i = 0; i < *num_streams; i++) {
455 if (strequal_m(tmp[i].name, name)) {
456 break;
460 if (i == *num_streams) {
461 return true;
464 TALLOC_FREE(tmp[i].name);
465 ARRAY_DEL_ELEMENT(tmp, i, *num_streams);
466 *num_streams -= 1;
467 return true;
470 static bool ad_empty_finderinfo(const struct adouble *ad)
472 int cmp;
473 char emptybuf[ADEDLEN_FINDERI] = {0};
474 char *fi = NULL;
476 fi = ad_get_entry(ad, ADEID_FINDERI);
477 if (fi == NULL) {
478 DBG_ERR("Missing FinderInfo in struct adouble [%p]\n", ad);
479 return false;
482 cmp = memcmp(emptybuf, fi, ADEDLEN_FINDERI);
483 return (cmp == 0);
486 static bool ai_empty_finderinfo(const AfpInfo *ai)
488 int cmp;
489 char emptybuf[ADEDLEN_FINDERI] = {0};
491 cmp = memcmp(emptybuf, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
492 return (cmp == 0);
496 * Update btime with btime from Netatalk
498 static void update_btime(vfs_handle_struct *handle,
499 struct smb_filename *smb_fname)
501 uint32_t t;
502 struct timespec creation_time = {0};
503 struct adouble *ad;
504 struct fruit_config_data *config = NULL;
506 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
507 return);
509 switch (config->meta) {
510 case FRUIT_META_STREAM:
511 return;
512 case FRUIT_META_NETATALK:
513 /* Handled below */
514 break;
515 default:
516 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
517 return;
520 ad = ad_get_meta_fsp(talloc_tos(), handle, smb_fname);
521 if (ad == NULL) {
522 return;
524 if (ad_getdate(ad, AD_DATE_UNIX | AD_DATE_CREATE, &t) != 0) {
525 TALLOC_FREE(ad);
526 return;
528 TALLOC_FREE(ad);
530 creation_time.tv_sec = convert_uint32_t_to_time_t(t);
531 update_stat_ex_create_time(&smb_fname->st, creation_time);
533 return;
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)
542 off_t offset;
544 switch (access_mask) {
545 case FILE_READ_DATA:
546 offset = AD_FILELOCK_OPEN_RD;
547 break;
549 case FILE_WRITE_DATA:
550 case FILE_APPEND_DATA:
551 offset = AD_FILELOCK_OPEN_WR;
552 break;
554 default:
555 offset = AD_FILELOCK_OPEN_NONE;
556 break;
559 if (fork_type == APPLE_FORK_RSRC) {
560 if (offset == AD_FILELOCK_OPEN_NONE) {
561 offset = AD_FILELOCK_RSRC_OPEN_NONE;
562 } else {
563 offset += 2;
567 return offset;
571 * Map a deny mode to a Netatalk brl
573 static off_t denymode_to_netatalk_brl(enum apple_fork fork_type,
574 uint32_t deny_mode)
576 off_t offset = 0;
578 switch (deny_mode) {
579 case DENY_READ:
580 offset = AD_FILELOCK_DENY_RD;
581 break;
583 case DENY_WRITE:
584 offset = AD_FILELOCK_DENY_WR;
585 break;
587 default:
588 smb_panic("denymode_to_netatalk_brl: bad deny mode\n");
591 if (fork_type == APPLE_FORK_RSRC) {
592 offset += 2;
595 return offset;
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)
607 bool result;
608 off_t offset = in_offset;
609 off_t len = 1;
610 int type = F_WRLCK;
611 pid_t pid = 0;
613 result = SMB_VFS_GETLOCK(fsp, &offset, &len, &type, &pid);
614 if (result == false) {
615 return true;
618 if (type != F_UNLCK) {
619 return true;
622 return false;
625 static NTSTATUS fruit_check_access(vfs_handle_struct *handle,
626 files_struct *fsp,
627 uint32_t access_mask,
628 uint32_t share_mode)
630 NTSTATUS status = NT_STATUS_OK;
631 off_t off;
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",
644 fsp_str_dbg(fsp),
645 access_mask & FILE_READ_DATA ? "READ" :"-",
646 access_mask & FILE_WRITE_DATA ? "WRITE" : "-",
647 share_mode);
649 if (fsp_get_io_fd(fsp) == -1) {
650 return NT_STATUS_OK;
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,
656 FILE_READ_DATA));
658 netatalk_already_open_with_deny_read = test_netatalk_lock(fsp,
659 denymode_to_netatalk_brl(fork_type,
660 DENY_READ));
662 netatalk_already_open_for_writing = test_netatalk_lock(fsp,
663 access_to_netatalk_brl(fork_type,
664 FILE_WRITE_DATA));
666 netatalk_already_open_with_deny_write = test_netatalk_lock(fsp,
667 denymode_to_netatalk_brl(fork_type,
668 DENY_WRITE));
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
694 * to set locks.
696 return NT_STATUS_OK;
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__;
703 status = do_lock(
704 fsp,
705 talloc_tos(),
706 &req_guid,
707 fsp->op->global->open_persistent_id,
709 off,
710 READ_LOCK,
711 POSIX_LOCK,
712 NULL,
713 NULL);
715 if (!NT_STATUS_IS_OK(status)) {
716 return status;
720 if (!share_for_read) {
721 off = denymode_to_netatalk_brl(fork_type, DENY_READ);
722 req_guid.time_hi_and_version = __LINE__;
723 status = do_lock(
724 fsp,
725 talloc_tos(),
726 &req_guid,
727 fsp->op->global->open_persistent_id,
729 off,
730 READ_LOCK,
731 POSIX_LOCK,
732 NULL,
733 NULL);
735 if (!NT_STATUS_IS_OK(status)) {
736 return 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__;
743 status = do_lock(
744 fsp,
745 talloc_tos(),
746 &req_guid,
747 fsp->op->global->open_persistent_id,
749 off,
750 READ_LOCK,
751 POSIX_LOCK,
752 NULL,
753 NULL);
755 if (!NT_STATUS_IS_OK(status)) {
756 return status;
760 if (!share_for_write) {
761 off = denymode_to_netatalk_brl(fork_type, DENY_WRITE);
762 req_guid.time_hi_and_version = __LINE__;
763 status = do_lock(
764 fsp,
765 talloc_tos(),
766 &req_guid,
767 fsp->op->global->open_persistent_id,
769 off,
770 READ_LOCK,
771 POSIX_LOCK,
772 NULL,
773 NULL);
775 if (!NT_STATUS_IS_OK(status)) {
776 return status;
780 return NT_STATUS_OK;
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;
789 NTSTATUS status;
790 struct smb2_create_blob *aapl = NULL;
791 uint32_t cmd;
792 bool ok;
793 uint8_t p[16];
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;
797 smb_ucs2_t *model;
798 size_t modellen;
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) {
806 return NT_STATUS_OK;
809 aapl = smb2_create_blob_find(in_context_blobs,
810 SMB2_CREATE_TAG_AAPL);
811 if (aapl == NULL) {
812 return NT_STATUS_OK;
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);
831 SIVAL(p, 4, 0);
832 SBVAL(p, 8, req_bitmap);
833 ok = data_blob_append(req, &blob, p, 16);
834 if (!ok) {
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);
860 if (!ok) {
861 return NT_STATUS_UNSUCCESSFUL;
865 if (req_bitmap & SMB2_CRTCTX_AAPL_VOLUME_CAPS) {
866 int val = lp_case_sensitive(SNUM(handle->conn));
867 uint64_t caps = 0;
869 switch (val) {
870 case Auto:
871 break;
873 case True:
874 caps |= SMB2_CRTCTX_AAPL_CASE_SENSITIVE;
875 break;
877 default:
878 break;
881 if (config->time_machine) {
882 caps |= SMB2_CRTCTX_AAPL_FULL_SYNC;
885 SBVAL(p, 0, caps);
887 ok = data_blob_append(req, &blob, p, 8);
888 if (!ok) {
889 return NT_STATUS_UNSUCCESSFUL;
893 if (req_bitmap & SMB2_CRTCTX_AAPL_MODEL_INFO) {
894 ok = convert_string_talloc(req,
895 CH_UNIX, CH_UTF16LE,
896 config->model, strlen(config->model),
897 &model, &modellen);
898 if (!ok) {
899 return NT_STATUS_UNSUCCESSFUL;
902 SIVAL(p, 0, 0);
903 SIVAL(p + 4, 0, modellen);
904 ok = data_blob_append(req, &blob, p, 8);
905 if (!ok) {
906 talloc_free(model);
907 return NT_STATUS_UNSUCCESSFUL;
910 ok = data_blob_append(req, &blob, model, modellen);
911 talloc_free(model);
912 if (!ok) {
913 return NT_STATUS_UNSUCCESSFUL;
917 status = smb2_create_blob_add(out_context_blobs,
918 out_context_blobs,
919 SMB2_CREATE_TAG_AAPL,
920 blob);
921 if (NT_STATUS_IS_OK(status)) {
922 global_fruit_config.nego_aapl = true;
925 return status;
928 static bool readdir_attr_meta_finderi_stream(
929 struct vfs_handle_struct *handle,
930 const struct smb_filename *smb_fname,
931 AfpInfo *ai)
933 struct smb_filename *stream_name = NULL;
934 files_struct *fsp = NULL;
935 ssize_t nread;
936 NTSTATUS status;
937 bool ok;
938 uint8_t buf[AFP_INFO_SIZE];
940 status = synthetic_pathref(talloc_tos(),
941 handle->conn->cwd_fsp,
942 smb_fname->base_name,
943 AFPINFO_STREAM_NAME,
944 NULL,
945 smb_fname->twrp,
946 smb_fname->flags,
947 &stream_name);
948 if (!NT_STATUS_IS_OK(status)) {
949 return false;
952 status = SMB_VFS_CREATE_FILE(
953 handle->conn, /* conn */
954 NULL, /* req */
955 NULL, /* dirfsp */
956 stream_name, /* fname */
957 FILE_READ_DATA, /* access_mask */
958 (FILE_SHARE_READ | FILE_SHARE_WRITE | /* share_access */
959 FILE_SHARE_DELETE),
960 FILE_OPEN, /* create_disposition*/
961 0, /* create_options */
962 0, /* file_attributes */
963 INTERNAL_OPEN_ONLY, /* oplock_request */
964 NULL, /* lease */
965 0, /* allocation_size */
966 0, /* private_flags */
967 NULL, /* sd */
968 NULL, /* ea_list */
969 &fsp, /* result */
970 NULL, /* pinfo */
971 NULL, NULL); /* create context */
973 TALLOC_FREE(stream_name);
975 if (!NT_STATUS_IS_OK(status)) {
976 return false;
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);
983 ok = false;
984 goto fail;
987 memcpy(&ai->afpi_FinderInfo[0], &buf[AFP_OFF_FinderInfo],
988 AFP_FinderSize);
990 ok = true;
992 fail:
993 if (fsp != NULL) {
994 close_file_free(NULL, &fsp, NORMAL_CLOSE);
997 return ok;
1000 static bool readdir_attr_meta_finderi_netatalk(
1001 struct vfs_handle_struct *handle,
1002 const struct smb_filename *smb_fname,
1003 AfpInfo *ai)
1005 struct adouble *ad = NULL;
1006 char *p = NULL;
1008 ad = ad_get_meta_fsp(talloc_tos(), handle, smb_fname);
1009 if (ad == NULL) {
1010 return false;
1013 p = ad_get_entry(ad, ADEID_FINDERI);
1014 if (p == NULL) {
1015 DBG_ERR("No ADEID_FINDERI for [%s]\n", smb_fname->base_name);
1016 TALLOC_FREE(ad);
1017 return false;
1020 memcpy(&ai->afpi_FinderInfo[0], p, AFP_FinderSize);
1021 TALLOC_FREE(ad);
1022 return true;
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;
1031 AfpInfo ai = {0};
1032 bool ok;
1034 SMB_VFS_HANDLE_GET_DATA(handle, config,
1035 struct fruit_config_data,
1036 return false);
1038 switch (config->meta) {
1039 case FRUIT_META_NETATALK:
1040 ok = readdir_attr_meta_finderi_netatalk(
1041 handle, smb_fname, &ai);
1042 break;
1044 case FRUIT_META_STREAM:
1045 ok = readdir_attr_meta_finderi_stream(
1046 handle, smb_fname, &ai);
1047 break;
1049 default:
1050 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
1051 return false;
1054 if (!ok) {
1055 /* Don't bother with errors, it's likely ENOENT */
1056 return true;
1059 if (S_ISREG(smb_fname->st.st_ex_mode)) {
1060 /* finder_type */
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);
1069 /* finder_flags */
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);
1077 /* creation date */
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);
1083 return true;
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,
1094 ADOUBLE_RSRC);
1095 if (ad == NULL) {
1096 return 0;
1099 rfork_size = ad_getentrylen(ad, ADEID_RFORK);
1100 TALLOC_FREE(ad);
1102 return rfork_size;
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;
1110 int ret;
1111 uint64_t rfork_size;
1113 stream_name = synthetic_smb_fname(talloc_tos(),
1114 smb_fname->base_name,
1115 AFPRESOURCE_STREAM_NAME,
1116 NULL,
1117 smb_fname->twrp,
1119 if (stream_name == NULL) {
1120 return 0;
1123 ret = SMB_VFS_STAT(handle->conn, stream_name);
1124 if (ret != 0) {
1125 TALLOC_FREE(stream_name);
1126 return 0;
1129 rfork_size = stream_name->st.st_ex_size;
1130 TALLOC_FREE(stream_name);
1132 return rfork_size;
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,
1143 return 0);
1145 switch (config->rsrc) {
1146 case FRUIT_RSRC_ADFILE:
1147 rfork_size = readdir_attr_rfork_size_adouble(handle,
1148 smb_fname);
1149 break;
1151 case FRUIT_RSRC_XATTR:
1152 case FRUIT_RSRC_STREAM:
1153 rfork_size = readdir_attr_rfork_size_stream(handle,
1154 smb_fname);
1155 break;
1157 default:
1158 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
1159 rfork_size = 0;
1160 break;
1163 return rfork_size;
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;
1172 bool ok;
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;
1194 * FinderInfo
1197 if (config->readdir_attr_finder_info) {
1198 ok = readdir_attr_meta_finderi(handle, smb_fname, attr_data);
1199 if (!ok) {
1200 status = NT_STATUS_INTERNAL_ERROR;
1204 return status;
1207 static NTSTATUS remove_virtual_nfs_aces(struct security_descriptor *psd)
1209 NTSTATUS status;
1210 uint32_t i;
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);
1221 if (cmp != 0) {
1222 /* Normal ACE entry. */
1223 continue;
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",
1236 nt_errstr(status));
1237 return status;
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.
1246 i--;
1248 return NT_STATUS_OK;
1251 /* Search MS NFS style ACE with UNIX mode */
1252 static NTSTATUS check_ms_nfs(vfs_handle_struct *handle,
1253 files_struct *fsp,
1254 struct security_descriptor *psd,
1255 mode_t *pmode,
1256 bool *pdo_chmod)
1258 uint32_t i;
1259 struct fruit_config_data *config = NULL;
1261 *pdo_chmod = false;
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);
1280 *pdo_chmod = true;
1282 DEBUG(10, ("MS NFS chmod request %s, %04o\n",
1283 fsp_str_dbg(fsp), (unsigned)(*pmode)));
1284 break;
1289 * Remove any incoming virtual ACE entries generated by
1290 * fruit_fget_nt_acl().
1293 return remove_virtual_nfs_aces(psd);
1296 /****************************************************************************
1297 * VFS ops
1298 ****************************************************************************/
1300 static int fruit_connect(vfs_handle_struct *handle,
1301 const char *service,
1302 const char *user)
1304 int rc;
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);
1313 if (rc < 0) {
1314 return rc;
1317 rc = init_fruit_config(handle);
1318 if (rc != 0) {
1319 return rc;
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));
1328 if (list) {
1329 if (strstr(list, "/" ADOUBLE_NAME_PREFIX "*/") == NULL) {
1330 newlist = talloc_asprintf(
1331 list,
1332 "%s/" ADOUBLE_NAME_PREFIX "*/",
1333 list);
1334 lp_do_parameter(SNUM(handle->conn),
1335 "veto files",
1336 newlist);
1338 } else {
1339 lp_do_parameter(SNUM(handle->conn),
1340 "veto files",
1341 "/" ADOUBLE_NAME_PREFIX "*/");
1344 TALLOC_FREE(list);
1347 if (config->encoding == FRUIT_ENC_NATIVE) {
1348 lp_do_parameter(SNUM(handle->conn),
1349 "catia:mappings",
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 "
1361 "recommended!\n");
1363 lp_do_parameter(SNUM(handle->conn), "posix locking", "no");
1366 return rc;
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,
1398 files_struct *fsp,
1399 int flags,
1400 mode_t mode)
1402 struct fruit_config_data *config = NULL;
1403 struct fio *fio = NULL;
1404 struct vfs_open_how how = {
1405 .flags = flags & ~O_CREAT,
1406 .mode = mode,
1408 int fd;
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;
1417 fio->fsp = fsp;
1418 fio->type = ADOUBLE_META;
1419 fio->config = config;
1421 fd = SMB_VFS_NEXT_OPENAT(handle,
1422 dirfsp,
1423 smb_fname,
1424 fsp,
1425 &how);
1426 if (fd != -1) {
1427 return fd;
1430 if (!(flags & O_CREAT)) {
1431 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
1432 return -1;
1435 fd = vfs_fake_fd();
1436 if (fd == -1) {
1437 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
1438 return -1;
1441 fio->fake_fd = true;
1442 fio->flags = flags;
1443 fio->mode = mode;
1445 return fd;
1448 static int fruit_open_meta_netatalk(vfs_handle_struct *handle,
1449 const struct files_struct *dirfsp,
1450 const struct smb_filename *smb_fname,
1451 files_struct *fsp,
1452 int flags,
1453 mode_t mode)
1455 struct fruit_config_data *config = NULL;
1456 struct fio *fio = NULL;
1457 struct adouble *ad = NULL;
1458 bool meta_exists = false;
1459 int fd;
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
1465 * already be open.
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);
1471 if (ad != NULL) {
1472 meta_exists = true;
1475 TALLOC_FREE(ad);
1477 if (!meta_exists && !(flags & O_CREAT)) {
1478 errno = ENOENT;
1479 return -1;
1482 fd = vfs_fake_fd();
1483 if (fd == -1) {
1484 return -1;
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;
1492 fio->fsp = fsp;
1493 fio->type = ADOUBLE_META;
1494 fio->config = config;
1495 fio->fake_fd = true;
1496 fio->flags = flags;
1497 fio->mode = mode;
1499 return fd;
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)
1507 int fd;
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,
1518 fsp, flags, mode);
1519 break;
1521 case FRUIT_META_NETATALK:
1522 fd = fruit_open_meta_netatalk(handle, dirfsp, smb_fname,
1523 fsp, flags, mode);
1524 break;
1526 default:
1527 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
1528 return -1;
1531 DBG_DEBUG("path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
1533 return 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,
1539 files_struct *fsp,
1540 int flags,
1541 mode_t mode)
1543 int rc = 0;
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;
1548 NTSTATUS status;
1549 int fd = -1;
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 */
1558 errno = ENOENT;
1559 rc = -1;
1560 goto exit;
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
1571 * disk.
1574 fd = vfs_fake_fd();
1575 if (fd == -1) {
1576 rc = fd;
1577 goto exit;
1580 status = adouble_open_from_base_fsp(fsp->conn->cwd_fsp,
1581 fsp->base_fsp,
1582 ADOUBLE_RSRC,
1583 flags,
1584 mode,
1585 &ad_fsp);
1586 if (!NT_STATUS_IS_OK(status)) {
1587 errno = map_errno_from_nt_status(status);
1588 rc = -1;
1589 goto exit;
1593 * Now we need to glue both handles together,
1594 * so that they automatically detach each other
1595 * on close.
1597 fio = fruit_get_complete_fio(handle, fsp);
1598 if (fio == NULL) {
1599 DBG_ERR("fio=NULL for [%s]\n", fsp_str_dbg(fsp));
1600 errno = EBADF;
1601 rc = -1;
1602 goto exit;
1605 ref_fio = VFS_ADD_FSP_EXTENSION(handle, ad_fsp,
1606 struct fio,
1607 fio_ref_destroy_fn);
1608 if (ref_fio == NULL) {
1609 int saved_errno = errno;
1610 fd_close(ad_fsp);
1611 file_free(NULL, ad_fsp);
1612 ad_fsp = NULL;
1613 errno = saved_errno;
1614 rc = -1;
1615 goto exit;
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;
1628 exit:
1630 DEBUG(10, ("fruit_open resource fork: rc=%d\n", rc));
1631 if (rc != 0) {
1632 int saved_errno = errno;
1633 if (fd != -1) {
1634 vfs_fake_fd_close(fd);
1636 errno = saved_errno;
1637 return rc;
1639 return fd;
1642 static int fruit_open_rsrc_xattr(vfs_handle_struct *handle,
1643 const struct files_struct *dirfsp,
1644 const struct smb_filename *smb_fname,
1645 files_struct *fsp,
1646 int flags,
1647 mode_t mode)
1649 #ifdef HAVE_ATTROPEN
1650 int fd = -1;
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,
1659 flags,
1660 mode);
1661 if (fd == -1) {
1662 return -1;
1665 return fd;
1667 #else
1668 errno = ENOSYS;
1669 return -1;
1670 #endif
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)
1678 int fd;
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;
1689 fio->fsp = fsp;
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,
1699 dirfsp,
1700 smb_fname,
1701 fsp,
1702 &how);
1703 break;
1706 case FRUIT_RSRC_ADFILE:
1707 fd = fruit_open_rsrc_adouble(handle, dirfsp, smb_fname,
1708 fsp, flags, mode);
1709 break;
1711 case FRUIT_RSRC_XATTR:
1712 fd = fruit_open_rsrc_xattr(handle, dirfsp, smb_fname,
1713 fsp, flags, mode);
1714 break;
1716 default:
1717 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
1718 errno = EINVAL;
1719 return -1;
1722 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
1724 if (fd == -1) {
1725 return -1;
1728 return fd;
1731 static int fruit_openat(vfs_handle_struct *handle,
1732 const struct files_struct *dirfsp,
1733 const struct smb_filename *smb_fname,
1734 files_struct *fsp,
1735 const struct vfs_open_how *how)
1737 int fd;
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,
1743 dirfsp,
1744 smb_fname,
1745 fsp,
1746 how);
1749 if (how->resolve != 0) {
1750 errno = ENOSYS;
1751 return -1;
1754 SMB_ASSERT(fsp_is_alternate_stream(fsp));
1756 if (is_afpinfo_stream(smb_fname->stream_name)) {
1757 fd = fruit_open_meta(handle,
1758 dirfsp,
1759 smb_fname,
1760 fsp,
1761 how->flags,
1762 how->mode);
1763 } else if (is_afpresource_stream(smb_fname->stream_name)) {
1764 fd = fruit_open_rsrc(handle,
1765 dirfsp,
1766 smb_fname,
1767 fsp,
1768 how->flags,
1769 how->mode);
1770 } else {
1771 fd = SMB_VFS_NEXT_OPENAT(handle,
1772 dirfsp,
1773 smb_fname,
1774 fsp,
1775 how);
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;
1782 return fd;
1785 static int fruit_close_meta(vfs_handle_struct *handle,
1786 files_struct *fsp)
1788 int ret;
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);
1798 if (fio == NULL) {
1799 return -1;
1801 if (fio->fake_fd) {
1802 ret = vfs_fake_fd_close(fsp_get_pathref_fd(fsp));
1803 fsp_set_fd(fsp, -1);
1804 } else {
1805 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
1807 break;
1809 case FRUIT_META_NETATALK:
1810 ret = vfs_fake_fd_close(fsp_get_pathref_fd(fsp));
1811 fsp_set_fd(fsp, -1);
1812 break;
1814 default:
1815 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
1816 return -1;
1819 return ret;
1823 static int fruit_close_rsrc(vfs_handle_struct *handle,
1824 files_struct *fsp)
1826 int ret;
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);
1835 break;
1837 case FRUIT_RSRC_ADFILE:
1839 struct fio *fio = fruit_get_complete_fio(handle, fsp);
1840 if (fio == NULL) {
1841 return -1;
1843 fio_close_ad_fsp(fio);
1844 ret = vfs_fake_fd_close(fsp_get_pathref_fd(fsp));
1845 fsp_set_fd(fsp, -1);
1846 break;
1849 case FRUIT_RSRC_XATTR:
1850 ret = vfs_fake_fd_close(fsp_get_pathref_fd(fsp));
1851 fsp_set_fd(fsp, -1);
1852 break;
1854 default:
1855 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
1856 return -1;
1859 return ret;
1862 static int fruit_close(vfs_handle_struct *handle,
1863 files_struct *fsp)
1865 int ret;
1866 int fd;
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);
1880 } else {
1881 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
1884 return ret;
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)
1893 int rc = -1;
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));
1904 return -1;
1907 rc = SMB_VFS_NEXT_RENAMEAT(handle,
1908 srcfsp,
1909 smb_fname_src,
1910 dstfsp,
1911 smb_fname_dst);
1912 if (rc != 0) {
1913 return -1;
1916 if ((config->rsrc != FRUIT_RSRC_ADFILE) ||
1917 (!S_ISREG(smb_fname_src->st.st_ex_mode)))
1919 return 0;
1922 rc = adouble_path(talloc_tos(), smb_fname_src, &src_adp_smb_fname);
1923 if (rc != 0) {
1924 goto done;
1927 rc = adouble_path(talloc_tos(), smb_fname_dst, &dst_adp_smb_fname);
1928 if (rc != 0) {
1929 goto done;
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,
1937 srcfsp,
1938 src_adp_smb_fname,
1939 dstfsp,
1940 dst_adp_smb_fname);
1941 if (errno == ENOENT) {
1942 rc = 0;
1945 done:
1946 TALLOC_FREE(src_adp_smb_fname);
1947 TALLOC_FREE(dst_adp_smb_fname);
1948 return rc;
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,
1956 dirfsp,
1957 smb_fname,
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;
1975 int rc;
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,
1983 dirfsp,
1984 smb_fname);
1985 break;
1987 case FRUIT_META_NETATALK:
1988 rc = fruit_unlink_meta_netatalk(handle, smb_fname);
1989 break;
1991 default:
1992 DBG_ERR("Unsupported meta config [%d]\n", config->meta);
1993 return -1;
1996 return rc;
1999 static int fruit_unlink_rsrc_stream(vfs_handle_struct *handle,
2000 struct files_struct *dirfsp,
2001 const struct smb_filename *smb_fname,
2002 bool force_unlink)
2004 int ret;
2006 if (!force_unlink) {
2007 struct smb_filename *full_fname = NULL;
2008 off_t size;
2011 * TODO: use SMB_VFS_STATX() once we have it.
2014 full_fname = full_path_from_dirfsp_atname(talloc_tos(),
2015 dirfsp,
2016 smb_fname);
2017 if (full_fname == NULL) {
2018 return -1;
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);
2028 if (ret != 0) {
2029 TALLOC_FREE(full_fname);
2030 DBG_ERR("stat [%s] failed [%s]\n",
2031 smb_fname_str_dbg(full_fname), strerror(errno));
2032 return -1;
2035 size = full_fname->st.st_ex_size;
2036 TALLOC_FREE(full_fname);
2038 if (size > 0) {
2039 /* OS X ignores resource fork stream delete requests */
2040 return 0;
2044 ret = SMB_VFS_NEXT_UNLINKAT(handle,
2045 dirfsp,
2046 smb_fname,
2048 if ((ret != 0) && (errno == ENOENT) && force_unlink) {
2049 ret = 0;
2052 return ret;
2055 static int fruit_unlink_rsrc_adouble(vfs_handle_struct *handle,
2056 struct files_struct *dirfsp,
2057 const struct smb_filename *smb_fname,
2058 bool force_unlink)
2060 int rc;
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(),
2068 dirfsp,
2069 smb_fname);
2070 if (full_fname == NULL) {
2071 return -1;
2074 ad = ad_get(talloc_tos(), handle, full_fname,
2075 ADOUBLE_RSRC);
2076 TALLOC_FREE(full_fname);
2077 if (ad == NULL) {
2078 errno = ENOENT;
2079 return -1;
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 */
2091 TALLOC_FREE(ad);
2092 return 0;
2095 TALLOC_FREE(ad);
2098 rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
2099 if (rc != 0) {
2100 return -1;
2103 rc = SMB_VFS_NEXT_UNLINKAT(handle,
2104 dirfsp,
2105 adp_smb_fname,
2107 TALLOC_FREE(adp_smb_fname);
2108 if ((rc != 0) && (errno == ENOENT) && force_unlink) {
2109 rc = 0;
2112 return rc;
2115 static int fruit_unlink_rsrc_xattr(vfs_handle_struct *handle,
2116 const struct smb_filename *smb_fname,
2117 bool force_unlink)
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
2123 * left behind.
2125 return 0;
2128 static int fruit_unlink_rsrc(vfs_handle_struct *handle,
2129 struct files_struct *dirfsp,
2130 const struct smb_filename *smb_fname,
2131 bool force_unlink)
2133 struct fruit_config_data *config = NULL;
2134 int rc;
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,
2142 dirfsp,
2143 smb_fname,
2144 force_unlink);
2145 break;
2147 case FRUIT_RSRC_ADFILE:
2148 rc = fruit_unlink_rsrc_adouble(handle,
2149 dirfsp,
2150 smb_fname,
2151 force_unlink);
2152 break;
2154 case FRUIT_RSRC_XATTR:
2155 rc = fruit_unlink_rsrc_xattr(handle, smb_fname, force_unlink);
2156 break;
2158 default:
2159 DBG_ERR("Unsupported rsrc config [%d]\n", config->rsrc);
2160 return -1;
2163 return rc;
2166 static int fruit_fchmod(vfs_handle_struct *handle,
2167 struct files_struct *fsp,
2168 mode_t mode)
2170 int rc = -1;
2171 struct fruit_config_data *config = NULL;
2172 struct smb_filename *smb_fname_adp = NULL;
2173 const struct smb_filename *smb_fname = NULL;
2174 NTSTATUS status;
2176 rc = SMB_VFS_NEXT_FCHMOD(handle, fsp, mode);
2177 if (rc != 0) {
2178 return rc;
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) {
2186 return 0;
2189 if (!VALID_STAT(smb_fname->st)) {
2190 return 0;
2193 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
2194 return 0;
2197 rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_adp);
2198 if (rc != 0) {
2199 return -1;
2202 status = openat_pathref_fsp(handle->conn->cwd_fsp,
2203 smb_fname_adp);
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)){
2208 rc = 0;
2209 goto out;
2211 rc = -1;
2212 goto out;
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) {
2219 rc = 0;
2221 out:
2222 TALLOC_FREE(smb_fname_adp);
2223 return rc;
2226 static int fruit_unlinkat(vfs_handle_struct *handle,
2227 struct files_struct *dirfsp,
2228 const struct smb_filename *smb_fname,
2229 int flags)
2231 struct fruit_config_data *config = NULL;
2232 struct smb_filename *rsrc_smb_fname = NULL;
2233 int ret;
2235 if (flags & AT_REMOVEDIR) {
2236 return SMB_VFS_NEXT_UNLINKAT(handle,
2237 dirfsp,
2238 smb_fname,
2239 AT_REMOVEDIR);
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,
2247 dirfsp,
2248 smb_fname);
2249 } else if (is_afpresource_stream(smb_fname->stream_name)) {
2250 return fruit_unlink_rsrc(handle,
2251 dirfsp,
2252 smb_fname,
2253 false);
2254 } else if (is_named_stream(smb_fname)) {
2255 return SMB_VFS_NEXT_UNLINKAT(handle,
2256 dirfsp,
2257 smb_fname,
2259 } else if (is_adouble_file(smb_fname->base_name)) {
2260 return SMB_VFS_NEXT_UNLINKAT(handle,
2261 dirfsp,
2262 smb_fname,
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,
2275 NULL,
2276 smb_fname->twrp,
2277 smb_fname->flags);
2278 if (rsrc_smb_fname == NULL) {
2279 return -1;
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);
2287 return -1;
2289 TALLOC_FREE(rsrc_smb_fname);
2291 return SMB_VFS_NEXT_UNLINKAT(handle,
2292 dirfsp,
2293 smb_fname,
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);
2302 ssize_t nread;
2303 int ret;
2305 if ((fio == NULL) || fio->fake_fd) {
2306 return -1;
2309 nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
2310 if (nread == -1 || nread == n) {
2311 return nread;
2314 DBG_ERR("Removing [%s] after short read [%zd]\n",
2315 fsp_str_dbg(fsp), nread);
2317 ret = SMB_VFS_NEXT_UNLINKAT(handle,
2318 fsp->conn->cwd_fsp,
2319 fsp->fsp_name,
2321 if (ret != 0) {
2322 DBG_ERR("Removing [%s] failed\n", fsp_str_dbg(fsp));
2323 return -1;
2326 errno = EINVAL;
2327 return -1;
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)
2334 AfpInfo *ai = NULL;
2335 struct adouble *ad = NULL;
2336 char afpinfo_buf[AFP_INFO_SIZE];
2337 char *p = NULL;
2338 ssize_t nread;
2340 ai = afpinfo_new(talloc_tos());
2341 if (ai == NULL) {
2342 return -1;
2345 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
2346 if (ad == NULL) {
2347 nread = -1;
2348 goto fail;
2351 p = ad_get_entry(ad, ADEID_FINDERI);
2352 if (p == NULL) {
2353 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
2354 nread = -1;
2355 goto fail;
2358 memcpy(&ai->afpi_FinderInfo[0], p, ADEDLEN_FINDERI);
2360 nread = afpinfo_pack(ai, afpinfo_buf);
2361 if (nread != AFP_INFO_SIZE) {
2362 nread = -1;
2363 goto fail;
2366 memcpy(data, afpinfo_buf, n);
2367 nread = n;
2369 fail:
2370 TALLOC_FREE(ai);
2371 return nread;
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);
2379 ssize_t nread;
2380 ssize_t to_return;
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)) {
2389 return 0;
2392 if (fio == NULL) {
2393 DBG_ERR("Failed to fetch fsp extension");
2394 return -1;
2397 /* Yes, macOS always reads from offset 0 */
2398 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,
2404 to_return, offset);
2405 break;
2407 case FRUIT_META_NETATALK:
2408 nread = fruit_pread_meta_adouble(handle, fsp, data,
2409 to_return, offset);
2410 break;
2412 default:
2413 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
2414 return -1;
2417 if (nread == -1 && fio->fake_fd) {
2418 AfpInfo *ai = NULL;
2419 char afpinfo_buf[AFP_INFO_SIZE];
2421 ai = afpinfo_new(talloc_tos());
2422 if (ai == NULL) {
2423 return -1;
2426 nread = afpinfo_pack(ai, afpinfo_buf);
2427 TALLOC_FREE(ai);
2428 if (nread != AFP_INFO_SIZE) {
2429 return -1;
2432 memcpy(data, afpinfo_buf, to_return);
2433 return to_return;
2436 return nread;
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;
2459 ssize_t nread;
2461 if (fio == NULL || fio->ad_fsp == NULL) {
2462 DBG_ERR("fio/ad_fsp=NULL for [%s]\n", fsp_str_dbg(fsp));
2463 errno = EBADF;
2464 return -1;
2467 ad = ad_fget(talloc_tos(), handle, fio->ad_fsp, ADOUBLE_RSRC);
2468 if (ad == NULL) {
2469 DBG_ERR("ad_fget [%s] failed [%s]\n",
2470 fsp_str_dbg(fio->ad_fsp), strerror(errno));
2471 return -1;
2474 nread = SMB_VFS_NEXT_PREAD(handle, fio->ad_fsp, data, n,
2475 offset + ad_getentryoff(ad, ADEID_RFORK));
2477 TALLOC_FREE(ad);
2478 return nread;
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);
2486 ssize_t nread;
2488 if (fio == NULL) {
2489 errno = EINVAL;
2490 return -1;
2493 switch (fio->config->rsrc) {
2494 case FRUIT_RSRC_STREAM:
2495 nread = fruit_pread_rsrc_stream(handle, fsp, data, n, offset);
2496 break;
2498 case FRUIT_RSRC_ADFILE:
2499 nread = fruit_pread_rsrc_adouble(handle, fsp, data, n, offset);
2500 break;
2502 case FRUIT_RSRC_XATTR:
2503 nread = fruit_pread_rsrc_xattr(handle, fsp, data, n, offset);
2504 break;
2506 default:
2507 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
2508 return -1;
2511 return nread;
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);
2519 ssize_t nread;
2521 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
2522 fsp_str_dbg(fsp), (intmax_t)offset, n);
2524 if (fio == NULL) {
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);
2530 } else {
2531 nread = fruit_pread_rsrc(handle, fsp, data, n, offset);
2534 DBG_DEBUG("Path [%s] nread [%zd]\n", fsp_str_dbg(fsp), nread);
2535 return nread;
2538 static bool fruit_must_handle_aio_stream(struct fio *fio)
2540 if (fio == NULL) {
2541 return false;
2544 if (fio->type == ADOUBLE_META) {
2545 return true;
2548 if ((fio->type == ADOUBLE_RSRC) &&
2549 (fio->config->rsrc == FRUIT_RSRC_ADFILE))
2551 return true;
2554 return false;
2557 struct fruit_pread_state {
2558 ssize_t nread;
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,
2569 void *data,
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);
2579 if (req == NULL) {
2580 return NULL;
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) {
2587 errno = EIO;
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,
2597 data, n, offset);
2598 if (tevent_req_nomem(req, subreq)) {
2599 return tevent_req_post(req, ev);
2601 tevent_req_set_callback(subreq, fruit_pread_done, req);
2602 return 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)) {
2616 return;
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);
2630 return -1;
2633 *vfs_aio_state = state->vfs_aio_state;
2634 retval = state->nread;
2635 tevent_req_received(req);
2636 return retval;
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);
2644 AfpInfo *ai = NULL;
2645 size_t nwritten;
2646 int ret;
2647 bool ok;
2649 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
2650 fsp_str_dbg(fsp), (intmax_t)offset, n);
2652 if (fio == NULL) {
2653 return -1;
2656 if (fio->fake_fd) {
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);
2664 if (ret != 0) {
2665 DBG_ERR("Close [%s] failed: %s\n",
2666 fsp_str_dbg(fsp), strerror(errno));
2667 return -1;
2670 fd = SMB_VFS_NEXT_OPENAT(handle,
2671 NULL, /* opening a stream */
2672 fsp->fsp_name,
2673 fsp,
2674 &how);
2675 if (fd == -1) {
2676 DBG_ERR("On-demand create [%s] in write failed: %s\n",
2677 fsp_str_dbg(fsp), strerror(errno));
2678 return -1;
2680 fsp_set_fd(fsp, fd);
2681 fio->fake_fd = false;
2684 ai = afpinfo_unpack(talloc_tos(), data);
2685 if (ai == NULL) {
2686 return -1;
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);
2697 if (ret != 0) {
2698 DBG_ERR("SMB_VFS_NEXT_FTRUNCATE on [%s] failed\n",
2699 fsp_str_dbg(fsp));
2700 return -1;
2703 ok = set_delete_on_close(
2704 fsp,
2705 true,
2706 handle->conn->session_info->security_token,
2707 handle->conn->session_info->unix_token);
2708 if (!ok) {
2709 DBG_ERR("set_delete_on_close on [%s] failed\n",
2710 fsp_str_dbg(fsp));
2711 return -1;
2713 return n;
2716 nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
2717 if (nwritten != n) {
2718 return -1;
2721 return 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;
2729 AfpInfo *ai = NULL;
2730 char *p = NULL;
2731 int ret;
2732 bool ok;
2734 ai = afpinfo_unpack(talloc_tos(), data);
2735 if (ai == NULL) {
2736 return -1;
2739 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
2740 if (ad == NULL) {
2741 ad = ad_init(talloc_tos(), ADOUBLE_META);
2742 if (ad == NULL) {
2743 return -1;
2746 p = ad_get_entry(ad, ADEID_FINDERI);
2747 if (p == NULL) {
2748 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
2749 TALLOC_FREE(ad);
2750 return -1;
2753 memcpy(p, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
2755 ret = ad_fset(handle, ad, fsp);
2756 if (ret != 0) {
2757 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
2758 TALLOC_FREE(ad);
2759 return -1;
2762 TALLOC_FREE(ad);
2764 if (!ai_empty_finderinfo(ai)) {
2765 return n;
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(
2775 fsp,
2776 true,
2777 handle->conn->session_info->security_token,
2778 handle->conn->session_info->unix_token);
2779 if (!ok) {
2780 DBG_ERR("set_delete_on_close on [%s] failed\n",
2781 fsp_str_dbg(fsp));
2782 return -1;
2785 return 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);
2793 ssize_t nwritten;
2794 uint8_t buf[AFP_INFO_SIZE];
2795 size_t to_write;
2796 size_t to_copy;
2797 int cmp;
2799 if (fio == NULL) {
2800 DBG_ERR("Failed to fetch fsp extension");
2801 return -1;
2804 if (n < 3) {
2805 errno = EINVAL;
2806 return -1;
2809 if (offset != 0 && n < 60) {
2810 errno = EINVAL;
2811 return -1;
2814 cmp = memcmp(data, "AFP", 3);
2815 if (cmp != 0) {
2816 errno = EINVAL;
2817 return -1;
2820 if (n <= AFP_OFF_FinderInfo) {
2822 * Nothing to do here really, just return
2824 return n;
2827 offset = 0;
2829 to_copy = n;
2830 if (to_copy > AFP_INFO_SIZE) {
2831 to_copy = AFP_INFO_SIZE;
2833 memcpy(buf, data, to_copy);
2835 to_write = n;
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,
2843 fsp,
2844 buf,
2845 to_write,
2846 offset);
2847 break;
2849 case FRUIT_META_NETATALK:
2850 nwritten = fruit_pwrite_meta_netatalk(handle,
2851 fsp,
2852 buf,
2853 to_write,
2854 offset);
2855 break;
2857 default:
2858 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
2859 return -1;
2862 if (nwritten != to_write) {
2863 return -1;
2867 * Return the requested amount, verified against macOS SMB server
2869 return n;
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;
2892 ssize_t nwritten;
2893 int ret;
2895 if (fio == NULL || fio->ad_fsp == NULL) {
2896 DBG_ERR("fio/ad_fsp=NULL for [%s]\n", fsp_str_dbg(fsp));
2897 errno = EBADF;
2898 return -1;
2901 ad = ad_fget(talloc_tos(), handle, fio->ad_fsp, ADOUBLE_RSRC);
2902 if (ad == NULL) {
2903 DBG_ERR("ad_fget [%s] failed [%s]\n",
2904 fsp_str_dbg(fio->ad_fsp), strerror(errno));
2905 return -1;
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);
2913 TALLOC_FREE(ad);
2914 return -1;
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);
2920 if (ret != 0) {
2921 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fio->ad_fsp));
2922 TALLOC_FREE(ad);
2923 return -1;
2927 TALLOC_FREE(ad);
2928 return n;
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);
2936 ssize_t nwritten;
2938 if (fio == NULL) {
2939 DBG_ERR("Failed to fetch fsp extension");
2940 return -1;
2943 switch (fio->config->rsrc) {
2944 case FRUIT_RSRC_STREAM:
2945 nwritten = fruit_pwrite_rsrc_stream(handle, fsp, data, n, offset);
2946 break;
2948 case FRUIT_RSRC_ADFILE:
2949 nwritten = fruit_pwrite_rsrc_adouble(handle, fsp, data, n, offset);
2950 break;
2952 case FRUIT_RSRC_XATTR:
2953 nwritten = fruit_pwrite_rsrc_xattr(handle, fsp, data, n, offset);
2954 break;
2956 default:
2957 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
2958 return -1;
2961 return nwritten;
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);
2969 ssize_t nwritten;
2971 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
2972 fsp_str_dbg(fsp), (intmax_t)offset, n);
2974 if (fio == NULL) {
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);
2980 } else {
2981 nwritten = fruit_pwrite_rsrc(handle, fsp, data, n, offset);
2984 DBG_DEBUG("Path [%s] nwritten=%zd\n", fsp_str_dbg(fsp), nwritten);
2985 return nwritten;
2988 struct fruit_pwrite_state {
2989 ssize_t nwritten;
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,
3000 const void *data,
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);
3010 if (req == NULL) {
3011 return NULL;
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) {
3018 errno = EIO;
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,
3028 data, n, offset);
3029 if (tevent_req_nomem(req, subreq)) {
3030 return tevent_req_post(req, ev);
3032 tevent_req_set_callback(subreq, fruit_pwrite_done, req);
3033 return 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)) {
3047 return;
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);
3061 return -1;
3064 *vfs_aio_state = state->vfs_aio_state;
3065 retval = state->nwritten;
3066 tevent_req_received(req);
3067 return retval;
3070 struct fruit_fsync_state {
3071 int ret;
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);
3090 if (req == NULL) {
3091 return NULL;
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
3103 * true.
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);
3121 if (ad == NULL) {
3122 tevent_req_error(req, ENOMEM);
3123 return tevent_req_post(req, ev);
3125 fsp = fio->ad_fsp;
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);
3133 return 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);
3147 return;
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);
3157 int retval = -1;
3159 if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
3160 tevent_req_received(req);
3161 return -1;
3164 *vfs_aio_state = state->vfs_aio_state;
3165 retval = state->ret;
3166 tevent_req_received(req);
3167 return retval;
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,
3175 bool follow_links)
3177 char *tmp_stream_name;
3178 int rc;
3180 tmp_stream_name = smb_fname->stream_name;
3181 smb_fname->stream_name = NULL;
3182 if (follow_links) {
3183 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
3184 } else {
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);
3193 return rc;
3196 static int fruit_stat_meta_stream(vfs_handle_struct *handle,
3197 struct smb_filename *smb_fname,
3198 bool follow_links)
3200 int ret;
3201 ino_t ino;
3203 ret = fruit_stat_base(handle, smb_fname, false);
3204 if (ret != 0) {
3205 return -1;
3208 ino = hash_inode(&smb_fname->st, smb_fname->stream_name);
3210 if (follow_links) {
3211 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
3212 } else {
3213 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
3216 smb_fname->st.st_ex_ino = ino;
3218 return ret;
3221 static int fruit_stat_meta_netatalk(vfs_handle_struct *handle,
3222 struct smb_filename *smb_fname,
3223 bool follow_links)
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) {
3229 return -1;
3232 ad = ad_get_meta_fsp(talloc_tos(), handle, smb_fname);
3233 if (ad == NULL) {
3234 DBG_INFO("fruit_stat_meta %s: %s\n",
3235 smb_fname_str_dbg(smb_fname), strerror(errno));
3236 errno = ENOENT;
3237 return -1;
3239 TALLOC_FREE(ad);
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);
3244 return 0;
3247 static int fruit_stat_meta(vfs_handle_struct *handle,
3248 struct smb_filename *smb_fname,
3249 bool follow_links)
3251 struct fruit_config_data *config = NULL;
3252 int ret;
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);
3260 break;
3262 case FRUIT_META_NETATALK:
3263 ret = fruit_stat_meta_netatalk(handle, smb_fname, follow_links);
3264 break;
3266 default:
3267 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3268 return -1;
3271 return ret;
3274 static int fruit_stat_rsrc_netatalk(vfs_handle_struct *handle,
3275 struct smb_filename *smb_fname,
3276 bool follow_links)
3278 struct adouble *ad = NULL;
3279 int ret;
3281 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
3282 if (ad == NULL) {
3283 errno = ENOENT;
3284 return -1;
3287 /* Populate the stat struct with info from the base file. */
3288 ret = fruit_stat_base(handle, smb_fname, follow_links);
3289 if (ret != 0) {
3290 TALLOC_FREE(ad);
3291 return -1;
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);
3297 TALLOC_FREE(ad);
3298 return 0;
3301 static int fruit_stat_rsrc_stream(vfs_handle_struct *handle,
3302 struct smb_filename *smb_fname,
3303 bool follow_links)
3305 int ret;
3307 if (follow_links) {
3308 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
3309 } else {
3310 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
3313 return ret;
3316 static int fruit_stat_rsrc_xattr(vfs_handle_struct *handle,
3317 struct smb_filename *smb_fname,
3318 bool follow_links)
3320 #ifdef HAVE_ATTROPEN
3321 int ret;
3322 int fd = -1;
3324 /* Populate the stat struct with info from the base file. */
3325 ret = fruit_stat_base(handle, smb_fname, follow_links);
3326 if (ret != 0) {
3327 return -1;
3330 fd = attropen(smb_fname->base_name,
3331 AFPRESOURCE_EA_NETATALK,
3332 O_RDONLY);
3333 if (fd == -1) {
3334 return 0;
3337 ret = sys_fstat(fd, &smb_fname->st, false);
3338 if (ret != 0) {
3339 close(fd);
3340 DBG_ERR("fstat [%s:%s] failed\n", smb_fname->base_name,
3341 AFPRESOURCE_EA_NETATALK);
3342 return -1;
3344 close(fd);
3345 fd = -1;
3347 smb_fname->st.st_ex_ino = hash_inode(&smb_fname->st,
3348 smb_fname->stream_name);
3350 return ret;
3352 #else
3353 errno = ENOSYS;
3354 return -1;
3355 #endif
3358 static int fruit_stat_rsrc(vfs_handle_struct *handle,
3359 struct smb_filename *smb_fname,
3360 bool follow_links)
3362 struct fruit_config_data *config = NULL;
3363 int ret;
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);
3373 break;
3375 case FRUIT_RSRC_XATTR:
3376 ret = fruit_stat_rsrc_xattr(handle, smb_fname, follow_links);
3377 break;
3379 case FRUIT_RSRC_ADFILE:
3380 ret = fruit_stat_rsrc_netatalk(handle, smb_fname, follow_links);
3381 break;
3383 default:
3384 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3385 return -1;
3388 return ret;
3391 static int fruit_stat(vfs_handle_struct *handle,
3392 struct smb_filename *smb_fname)
3394 int rc = -1;
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);
3401 if (rc == 0) {
3402 update_btime(handle, smb_fname);
3404 return rc;
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);
3418 } else {
3419 return SMB_VFS_NEXT_STAT(handle, smb_fname);
3422 if (rc == 0) {
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;
3429 return rc;
3432 static int fruit_lstat(vfs_handle_struct *handle,
3433 struct smb_filename *smb_fname)
3435 int rc = -1;
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);
3442 if (rc == 0) {
3443 update_btime(handle, smb_fname);
3445 return rc;
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);
3452 } else {
3453 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
3456 if (rc == 0) {
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;
3463 return rc;
3466 static int fruit_fstat_meta_stream(vfs_handle_struct *handle,
3467 files_struct *fsp,
3468 SMB_STRUCT_STAT *sbuf)
3470 struct fio *fio = fruit_get_complete_fio(handle, fsp);
3471 struct smb_filename smb_fname;
3472 ino_t ino;
3473 int ret;
3475 if (fio == NULL) {
3476 return -1;
3479 if (fio->fake_fd) {
3480 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
3481 if (ret != 0) {
3482 return -1;
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);
3488 return 0;
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);
3497 if (ret != 0) {
3498 return -1;
3500 *sbuf = smb_fname.st;
3502 ino = hash_inode(sbuf, fsp->fsp_name->stream_name);
3504 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
3505 if (ret != 0) {
3506 return -1;
3509 sbuf->st_ex_ino = ino;
3510 return 0;
3513 static int fruit_fstat_meta_netatalk(vfs_handle_struct *handle,
3514 files_struct *fsp,
3515 SMB_STRUCT_STAT *sbuf)
3517 int ret;
3519 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
3520 if (ret != 0) {
3521 return -1;
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);
3528 return 0;
3531 static int fruit_fstat_meta(vfs_handle_struct *handle,
3532 files_struct *fsp,
3533 SMB_STRUCT_STAT *sbuf,
3534 struct fio *fio)
3536 int ret;
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);
3543 break;
3545 case FRUIT_META_NETATALK:
3546 ret = fruit_fstat_meta_netatalk(handle, fsp, sbuf);
3547 break;
3549 default:
3550 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
3551 return -1;
3554 DBG_DEBUG("Path [%s] ret [%d]\n", fsp_str_dbg(fsp), ret);
3555 return ret;
3558 static int fruit_fstat_rsrc_xattr(vfs_handle_struct *handle,
3559 files_struct *fsp,
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,
3566 files_struct *fsp,
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,
3573 files_struct *fsp,
3574 SMB_STRUCT_STAT *sbuf)
3576 struct fio *fio = fruit_get_complete_fio(handle, fsp);
3577 struct adouble *ad = NULL;
3578 int ret;
3580 if (fio == NULL || fio->ad_fsp == NULL) {
3581 DBG_ERR("fio/ad_fsp=NULL for [%s]\n", fsp_str_dbg(fsp));
3582 errno = EBADF;
3583 return -1;
3586 /* Populate the stat struct with info from the base file. */
3587 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
3588 if (ret == -1) {
3589 return -1;
3592 ad = ad_fget(talloc_tos(), handle, fio->ad_fsp, ADOUBLE_RSRC);
3593 if (ad == NULL) {
3594 DBG_ERR("ad_fget [%s] failed [%s]\n",
3595 fsp_str_dbg(fio->ad_fsp), strerror(errno));
3596 return -1;
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);
3603 TALLOC_FREE(ad);
3604 return 0;
3607 static int fruit_fstat_rsrc(vfs_handle_struct *handle, files_struct *fsp,
3608 SMB_STRUCT_STAT *sbuf, struct fio *fio)
3610 int ret;
3612 switch (fio->config->rsrc) {
3613 case FRUIT_RSRC_STREAM:
3614 ret = fruit_fstat_rsrc_stream(handle, fsp, sbuf);
3615 break;
3617 case FRUIT_RSRC_ADFILE:
3618 ret = fruit_fstat_rsrc_adouble(handle, fsp, sbuf);
3619 break;
3621 case FRUIT_RSRC_XATTR:
3622 ret = fruit_fstat_rsrc_xattr(handle, fsp, sbuf);
3623 break;
3625 default:
3626 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
3627 return -1;
3630 return ret;
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);
3637 int rc;
3639 if (fio == NULL) {
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);
3647 } else {
3648 rc = fruit_fstat_rsrc(handle, fsp, sbuf, fio);
3651 if (rc == 0) {
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);
3659 return rc;
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,
3668 off_t size)
3670 struct smb_filename *sname = NULL;
3671 NTSTATUS status;
3672 int ret;
3673 bool ok;
3675 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams, AFPINFO_STREAM);
3676 if (!ok) {
3677 return NT_STATUS_INTERNAL_ERROR;
3680 if (size == 0) {
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,
3688 NULL,
3689 smb_fname->twrp,
3691 &sname);
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,
3698 sname,
3700 if (ret != 0) {
3701 DBG_ERR("Removing [%s] failed\n", smb_fname_str_dbg(sname));
3702 TALLOC_FREE(sname);
3703 return map_nt_error_from_unix(errno);
3706 TALLOC_FREE(sname);
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;
3720 int i;
3722 for (i = 0; i < num_streams; i++) {
3723 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
3724 break;
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,
3737 smb_fname,
3738 mem_ctx,
3739 pnum_streams,
3740 pstreams,
3741 stream[i].size);
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;
3759 bool is_fi_empty;
3760 int i;
3761 bool ok;
3763 /* Remove the Netatalk xattr from the list */
3764 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
3765 ":" NETATALK_META_XATTR ":$DATA");
3766 if (!ok) {
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)) {
3776 break;
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,
3785 AFPINFO_STREAM);
3786 if (!ok) {
3787 return NT_STATUS_INTERNAL_ERROR;
3791 ad = ad_get_meta_fsp(talloc_tos(), handle, smb_fname);
3792 if (ad == NULL) {
3793 return NT_STATUS_OK;
3796 is_fi_empty = ad_empty_finderinfo(ad);
3797 TALLOC_FREE(ad);
3799 if (is_fi_empty) {
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));
3806 if (!ok) {
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;
3821 NTSTATUS status;
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,
3830 pstreams);
3831 break;
3833 case FRUIT_META_STREAM:
3834 status = fruit_streaminfo_meta_stream(handle, fsp, smb_fname,
3835 mem_ctx, pnum_streams,
3836 pstreams);
3837 break;
3839 default:
3840 return NT_STATUS_INTERNAL_ERROR;
3843 return status;
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)
3854 bool ok;
3856 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
3857 if (!ok) {
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)
3872 bool ok;
3874 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
3875 if (!ok) {
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;
3893 bool ok;
3894 size_t rlen;
3895 int i;
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)) {
3903 break;
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);
3913 if (!ok) {
3914 return NT_STATUS_INTERNAL_ERROR;
3918 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
3919 if (ad == NULL) {
3920 return NT_STATUS_OK;
3923 rlen = ad_getentrylen(ad, ADEID_RFORK);
3924 TALLOC_FREE(ad);
3926 if (rlen == 0) {
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));
3933 if (!ok) {
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;
3948 NTSTATUS status;
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,
3961 pstreams);
3962 break;
3964 case FRUIT_RSRC_XATTR:
3965 status = fruit_streaminfo_rsrc_xattr(handle, fsp, smb_fname,
3966 mem_ctx, pnum_streams,
3967 pstreams);
3968 break;
3970 case FRUIT_RSRC_ADFILE:
3971 status = fruit_streaminfo_rsrc_adouble(handle, fsp, smb_fname,
3972 mem_ctx, pnum_streams,
3973 pstreams);
3974 break;
3976 default:
3977 return NT_STATUS_INTERNAL_ERROR;
3980 return status;
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;
3988 unsigned i = 0;
3990 if (!global_fruit_config.nego_aapl) {
3991 return;
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)
4002 i++;
4003 continue;
4006 streams[i] = streams[num_streams - 1];
4007 num_streams--;
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;
4021 NTSTATUS status;
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)) {
4033 return 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)) {
4041 return status;
4044 status = fruit_streaminfo_rsrc(handle, fsp, smb_fname,
4045 mem_ctx, pnum_streams, pstreams);
4046 if (!NT_STATUS_IS_OK(status)) {
4047 return status;
4050 return NT_STATUS_OK;
4053 static int fruit_fntimes(vfs_handle_struct *handle,
4054 files_struct *fsp,
4055 struct smb_file_time *ft)
4057 int rc = 0;
4058 struct adouble *ad = NULL;
4059 struct fruit_config_data *config = NULL;
4061 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
4062 return -1);
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);
4074 if (ad == NULL) {
4075 goto exit;
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);
4083 exit:
4085 TALLOC_FREE(ad);
4086 if (rc != 0) {
4087 DBG_WARNING("%s\n", fsp_str_dbg(fsp));
4088 return -1;
4090 return SMB_VFS_NEXT_FNTIMES(handle, fsp, ft);
4093 static int fruit_fallocate(struct vfs_handle_struct *handle,
4094 struct files_struct *fsp,
4095 uint32_t mode,
4096 off_t offset,
4097 off_t len)
4099 struct fio *fio = fruit_get_complete_fio(handle, fsp);
4101 if (fio == NULL) {
4102 return SMB_VFS_NEXT_FALLOCATE(handle, fsp, mode, offset, len);
4105 /* Let the pwrite code path handle it. */
4106 errno = ENOSYS;
4107 return -1;
4110 static int fruit_ftruncate_rsrc_xattr(struct vfs_handle_struct *handle,
4111 struct files_struct *fsp,
4112 off_t offset)
4114 #ifdef HAVE_ATTROPEN
4115 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
4116 #endif
4117 return 0;
4120 static int fruit_ftruncate_rsrc_adouble(struct vfs_handle_struct *handle,
4121 struct files_struct *fsp,
4122 off_t offset)
4124 struct fio *fio = fruit_get_complete_fio(handle, fsp);
4125 int rc;
4126 struct adouble *ad = NULL;
4127 off_t ad_off;
4129 if (fio == NULL || fio->ad_fsp == NULL) {
4130 DBG_ERR("fio/ad_fsp=NULL for [%s]\n", fsp_str_dbg(fsp));
4131 errno = EBADF;
4132 return -1;
4135 ad = ad_fget(talloc_tos(), handle, fio->ad_fsp, ADOUBLE_RSRC);
4136 if (ad == NULL) {
4137 DBG_ERR("ad_fget [%s] failed [%s]\n",
4138 fsp_str_dbg(fio->ad_fsp), strerror(errno));
4139 return -1;
4142 ad_off = ad_getentryoff(ad, ADEID_RFORK);
4144 rc = SMB_VFS_NEXT_FTRUNCATE(handle, fio->ad_fsp, offset + ad_off);
4145 if (rc != 0) {
4146 TALLOC_FREE(ad);
4147 return -1;
4150 ad_setentrylen(ad, ADEID_RFORK, offset);
4152 rc = ad_fset(handle, ad, fio->ad_fsp);
4153 if (rc != 0) {
4154 DBG_ERR("ad_fset [%s] failed [%s]\n",
4155 fsp_str_dbg(fio->ad_fsp), strerror(errno));
4156 TALLOC_FREE(ad);
4157 return -1;
4160 TALLOC_FREE(ad);
4161 return 0;
4164 static int fruit_ftruncate_rsrc_stream(struct vfs_handle_struct *handle,
4165 struct files_struct *fsp,
4166 off_t offset)
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,
4173 off_t offset)
4175 struct fio *fio = fruit_get_complete_fio(handle, fsp);
4176 int ret;
4178 if (fio == NULL) {
4179 DBG_ERR("Failed to fetch fsp extension");
4180 return -1;
4183 switch (fio->config->rsrc) {
4184 case FRUIT_RSRC_XATTR:
4185 ret = fruit_ftruncate_rsrc_xattr(handle, fsp, offset);
4186 break;
4188 case FRUIT_RSRC_ADFILE:
4189 ret = fruit_ftruncate_rsrc_adouble(handle, fsp, offset);
4190 break;
4192 case FRUIT_RSRC_STREAM:
4193 ret = fruit_ftruncate_rsrc_stream(handle, fsp, offset);
4194 break;
4196 default:
4197 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
4198 return -1;
4202 return ret;
4205 static int fruit_ftruncate_meta(struct vfs_handle_struct *handle,
4206 struct files_struct *fsp,
4207 off_t offset)
4209 if (offset > 60) {
4210 DBG_WARNING("ftruncate %s to %jd",
4211 fsp_str_dbg(fsp), (intmax_t)offset);
4212 /* OS X returns NT_STATUS_ALLOTTED_SPACE_EXCEEDED */
4213 errno = EOVERFLOW;
4214 return -1;
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);
4220 return 0;
4223 static int fruit_ftruncate(struct vfs_handle_struct *handle,
4224 struct files_struct *fsp,
4225 off_t offset)
4227 struct fio *fio = fruit_get_complete_fio(handle, fsp);
4228 int ret;
4230 DBG_DEBUG("Path [%s] offset [%"PRIdMAX"]\n", fsp_str_dbg(fsp),
4231 (intmax_t)offset);
4233 if (fio == NULL) {
4234 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
4237 if (fio->type == ADOUBLE_META) {
4238 ret = fruit_ftruncate_meta(handle, fsp, offset);
4239 } else {
4240 ret = fruit_ftruncate_rsrc(handle, fsp, offset);
4243 DBG_DEBUG("Path [%s] result [%d]\n", fsp_str_dbg(fsp), ret);
4244 return 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,
4263 int *pinfo,
4264 const struct smb2_create_blobs *in_context_blobs,
4265 struct smb2_create_blobs *out_context_blobs)
4267 NTSTATUS status;
4268 struct fruit_config_data *config = NULL;
4269 files_struct *fsp = NULL;
4270 bool internal_open = (oplock_request & INTERNAL_OPEN_ONLY);
4271 int ret;
4273 status = check_aapl(handle, req, in_context_blobs, out_context_blobs);
4274 if (!NT_STATUS_IS_OK(status)) {
4275 goto fail;
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) &&
4282 !internal_open &&
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,
4295 smb_fname,
4296 macos_string_replace_map,
4297 conv_flags);
4298 if (ret != 0) {
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,
4309 lease,
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)) {
4314 return status;
4317 fsp = *result;
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;
4341 goto fail;
4344 if (is_named_stream(smb_fname) || fsp->fsp_flags.is_directory) {
4345 return status;
4348 if ((config->locking == FRUIT_LOCKING_NETATALK) &&
4349 (fsp->op != NULL) &&
4350 !fsp->fsp_flags.is_pathref)
4352 status = fruit_check_access(
4353 handle, *result,
4354 access_mask,
4355 share_access);
4356 if (!NT_STATUS_IS_OK(status)) {
4357 goto fail;
4361 return status;
4363 fail:
4364 DEBUG(10, ("fruit_create_file: %s\n", nt_errstr(status)));
4366 if (fsp) {
4367 close_file_free(req, &fsp, ERROR_CLOSE);
4368 *result = NULL;
4371 return status;
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;
4382 NTSTATUS status;
4383 int ret;
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,
4391 fsp,
4392 mem_ctx,
4393 pattr_data);
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,
4407 fsp->fsp_name,
4408 macos_string_replace_map,
4409 conv_flags);
4410 if (ret != 0) {
4411 DBG_ERR("ad_convert(\"%s\") failed\n",
4412 fsp_str_dbg(fsp));
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
4425 * and creation date
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)) {
4436 goto fail;
4441 * UNIX mode
4443 if (config->unix_info_enabled) {
4444 attr_data->attr_data.aapl.unix_mode =
4445 fsp->fsp_name->st.st_ex_mode;
4449 * max_access
4451 if (!config->readdir_attr_max_access) {
4452 attr_data->attr_data.aapl.max_access = FILE_GENERIC_ALL;
4453 } else {
4454 status = smbd_calculate_access_mask_fsp(fsp->conn->cwd_fsp,
4455 fsp,
4456 false,
4457 SEC_FLAG_MAXIMUM_ALLOWED,
4458 &attr_data->attr_data.aapl.max_access);
4459 if (!NT_STATUS_IS_OK(status)) {
4460 goto fail;
4464 return NT_STATUS_OK;
4466 fail:
4467 DBG_WARNING("Path [%s], error: %s\n", fsp_str_dbg(fsp),
4468 nt_errstr(status));
4469 TALLOC_FREE(*pattr_data);
4470 return status;
4473 static NTSTATUS fruit_fget_nt_acl(vfs_handle_struct *handle,
4474 files_struct *fsp,
4475 uint32_t security_info,
4476 TALLOC_CTX *mem_ctx,
4477 struct security_descriptor **ppdesc)
4479 NTSTATUS status;
4480 struct security_ace ace;
4481 struct dom_sid sid;
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,
4489 mem_ctx, ppdesc);
4490 if (!NT_STATUS_IS_OK(status)) {
4491 return 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");
4508 return status;
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"));
4517 return status;
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"));
4526 return status;
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"));
4535 return status;
4538 return NT_STATUS_OK;
4541 static NTSTATUS fruit_fset_nt_acl(vfs_handle_struct *handle,
4542 files_struct *fsp,
4543 uint32_t security_info_sent,
4544 const struct security_descriptor *orig_psd)
4546 NTSTATUS status;
4547 bool do_chmod;
4548 mode_t ms_nfs_mode = 0;
4549 int result;
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);
4558 if (psd == NULL) {
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)));
4567 TALLOC_FREE(psd);
4568 return status;
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)));
4590 TALLOC_FREE(psd);
4591 return status;
4594 if (do_chmod) {
4595 result = SMB_VFS_FCHMOD(fsp, ms_nfs_mode);
4596 if (result != 0) {
4597 DBG_WARNING("%s, result: %d, %04o error %s\n",
4598 fsp_str_dbg(fsp),
4599 result,
4600 (unsigned)ms_nfs_mode,
4601 strerror(errno));
4602 status = map_nt_error_from_unix(errno);
4603 TALLOC_FREE(psd);
4604 return status;
4608 TALLOC_FREE(psd);
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;
4617 files_struct *fsp;
4618 uint32_t fsctl;
4619 uint32_t flags;
4620 uint64_t xferlen;
4621 DATA_BLOB token;
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,
4630 files_struct *fsp,
4631 uint32_t fsctl,
4632 uint32_t ttl,
4633 off_t offset,
4634 size_t to_copy)
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);
4642 if (req == NULL) {
4643 return NULL;
4645 *state = (struct fruit_offload_read_state) {
4646 .handle = handle,
4647 .ev = ev,
4648 .fsp = fsp,
4649 .fsctl = fsctl,
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);
4658 return 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);
4667 NTSTATUS status;
4669 status = SMB_VFS_NEXT_OFFLOAD_READ_RECV(subreq,
4670 state->handle,
4671 state,
4672 &state->flags,
4673 &state->xferlen,
4674 &state->token);
4675 TALLOC_FREE(subreq);
4676 if (tevent_req_nterror(req, status)) {
4677 return;
4680 if (state->fsctl != FSCTL_SRV_REQUEST_RESUME_KEY) {
4681 tevent_req_done(req);
4682 return;
4685 status = vfs_offload_token_ctx_init(state->fsp->conn->sconn->client,
4686 &fruit_offload_ctx);
4687 if (tevent_req_nterror(req, status)) {
4688 return;
4691 status = vfs_offload_token_db_store_fsp(fruit_offload_ctx,
4692 state->fsp,
4693 &state->token);
4694 if (tevent_req_nterror(req, status)) {
4695 return;
4698 tevent_req_done(req);
4699 return;
4702 static NTSTATUS fruit_offload_read_recv(struct tevent_req *req,
4703 struct vfs_handle_struct *handle,
4704 TALLOC_CTX *mem_ctx,
4705 uint32_t *flags,
4706 uint64_t *xferlen,
4707 DATA_BLOB *token)
4709 struct fruit_offload_read_state *state = tevent_req_data(
4710 req, struct fruit_offload_read_state);
4711 NTSTATUS status;
4713 if (tevent_req_is_nterror(req, &status)) {
4714 tevent_req_received(req);
4715 return status;
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;
4729 off_t copied;
4730 struct files_struct *src_fsp;
4731 struct files_struct *dst_fsp;
4732 bool is_copyfile;
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,
4739 uint32_t fsctl,
4740 DATA_BLOB *token,
4741 off_t transfer_offset,
4742 struct files_struct *dest_fsp,
4743 off_t dest_off,
4744 off_t num)
4746 struct tevent_req *req, *subreq;
4747 struct fruit_offload_write_state *state;
4748 NTSTATUS status;
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,
4760 return NULL);
4762 req = tevent_req_create(mem_ctx, &state,
4763 struct fruit_offload_write_state);
4764 if (req == NULL) {
4765 return NULL;
4767 state->handle = handle;
4768 state->dst_fsp = dest_fsp;
4770 switch (fsctl) {
4771 case FSCTL_SRV_COPYCHUNK:
4772 case FSCTL_SRV_COPYCHUNK_WRITE:
4773 copyfile_enabled = config->copyfile_enabled;
4774 break;
4775 default:
4776 break;
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,
4803 mem_ctx,
4805 fsctl,
4806 token,
4807 transfer_offset,
4808 dest_fsp,
4809 dest_off,
4810 to_copy);
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);
4816 return 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);
4825 NTSTATUS status;
4826 unsigned int num_streams = 0;
4827 struct stream_struct *streams = NULL;
4828 unsigned int i;
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,
4833 subreq,
4834 &state->copied);
4835 TALLOC_FREE(subreq);
4836 if (tevent_req_nterror(req, status)) {
4837 return;
4840 if (!state->is_copyfile) {
4841 tevent_req_done(req);
4842 return;
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)) {
4853 return;
4856 if (num_streams == 1) {
4857 /* There is always one stream, ::$DATA. */
4858 tevent_req_done(req);
4859 return;
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(
4867 req,
4868 state->src_fsp->fsp_name->base_name,
4869 streams[i].name,
4870 NULL,
4871 state->src_fsp->fsp_name->twrp,
4872 state->src_fsp->fsp_name->flags);
4873 if (tevent_req_nomem(src_fname_tmp, req)) {
4874 return;
4877 if (is_ntfs_default_stream_smb_fname(src_fname_tmp)) {
4878 TALLOC_FREE(src_fname_tmp);
4879 continue;
4882 dst_fname_tmp = synthetic_smb_fname(
4883 req,
4884 state->dst_fsp->fsp_name->base_name,
4885 streams[i].name,
4886 NULL,
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);
4891 return;
4894 status = copy_file(req,
4895 state->handle->conn,
4896 src_fname_tmp,
4897 dst_fname_tmp,
4898 FILE_CREATE);
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);
4907 return;
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,
4922 off_t *copied)
4924 struct fruit_offload_write_state *state = tevent_req_data(
4925 req, struct fruit_offload_write_state);
4926 NTSTATUS status;
4928 if (tevent_req_is_nterror(req, &status)) {
4929 DEBUG(1, ("server side copy chunk failed: %s\n",
4930 nt_errstr(status)));
4931 *copied = 0;
4932 tevent_req_received(req);
4933 return status;
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)
4944 static regex_t re;
4945 static bool re_initialized = false;
4946 int i;
4947 int ret;
4949 if (!re_initialized) {
4950 ret = regcomp(&re, "^[[:blank:]]*<key>band-size</key>$", 0);
4951 if (ret != 0) {
4952 return NULL;
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);
4961 if (ret == 0) {
4963 * Check if the match was on the last line, sa we want
4964 * the subsequent line.
4966 if (i + 1 == numlines) {
4967 return NULL;
4969 return lines[i + 1];
4971 if (ret != REG_NOMATCH) {
4972 return NULL;
4976 return NULL;
4979 static bool fruit_get_bandsize_from_line(char *line, size_t *_band_size)
4981 static regex_t re;
4982 static bool re_initialized = false;
4983 regmatch_t matches[2];
4984 uint64_t band_size;
4985 int ret;
4986 bool ok;
4988 if (!re_initialized) {
4989 ret = regcomp(&re,
4990 "^[[:blank:]]*"
4991 "<integer>\\([[:digit:]]*\\)</integer>$",
4993 if (ret != 0) {
4994 return false;
4996 re_initialized = true;
4999 ret = regexec(&re, line, 2, matches, 0);
5000 if (ret != 0) {
5001 DBG_ERR("regex failed [%s]\n", line);
5002 return false;
5005 line[matches[1].rm_eo] = '\0';
5007 ok = conv_str_u64(&line[matches[1].rm_so], &band_size);
5008 if (!ok) {
5009 return false;
5011 *_band_size = (size_t)band_size;
5012 return true;
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,
5020 const char *dir,
5021 size_t *band_size)
5023 #define INFO_PLIST_MAX_SIZE 64*1024
5024 char *plist = NULL;
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;
5031 ssize_t nread;
5032 int numlines;
5033 int ret;
5034 bool ok = false;
5035 NTSTATUS status;
5037 plist = talloc_asprintf(talloc_tos(),
5038 "%s/%s/Info.plist",
5039 handle->conn->connectpath,
5040 dir);
5041 if (plist == NULL) {
5042 ok = false;
5043 goto out;
5046 smb_fname = synthetic_smb_fname(talloc_tos(),
5047 plist,
5048 NULL,
5049 NULL,
5052 if (smb_fname == NULL) {
5053 ok = false;
5054 goto out;
5057 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
5058 if (ret != 0) {
5059 DBG_INFO("Ignoring Sparsebundle without Info.plist [%s]\n", dir);
5060 ok = true;
5061 goto out;
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);
5068 ok = true;
5069 goto out;
5072 status = SMB_VFS_NEXT_CREATE_FILE(
5073 handle, /* conn */
5074 NULL, /* req */
5075 NULL, /* dirfsp */
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 */
5083 NULL, /* lease */
5084 0, /* allocation_size */
5085 0, /* private_flags */
5086 NULL, /* sd */
5087 NULL, /* ea_list */
5088 &fsp, /* result */
5089 NULL, /* psbuf */
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));
5094 ok = false;
5095 goto out;
5098 file_data = talloc_zero_array(talloc_tos(),
5099 uint8_t,
5100 plist_file_size + 1);
5101 if (file_data == NULL) {
5102 ok = false;
5103 goto out;
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);
5110 ok = false;
5111 goto out;
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));
5118 ok = false;
5119 goto out;
5122 lines = file_lines_parse((char *)file_data,
5123 plist_file_size,
5124 &numlines,
5125 talloc_tos());
5126 if (lines == NULL) {
5127 ok = false;
5128 goto out;
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));
5135 ok = false;
5136 goto out;
5139 ok = fruit_get_bandsize_from_line(band_size_line, band_size);
5140 if (!ok) {
5141 DBG_ERR("fruit_get_bandsize_from_line failed\n");
5142 goto out;
5145 DBG_DEBUG("Parsed band-size [%zu] for [%s]\n", *band_size, plist);
5147 out:
5148 if (fsp != NULL) {
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));
5154 TALLOC_FREE(plist);
5155 TALLOC_FREE(smb_fname);
5156 TALLOC_FREE(file_data);
5157 TALLOC_FREE(lines);
5158 return ok;
5161 struct fruit_disk_free_state {
5162 off_t total_size;
5165 static bool fruit_get_num_bands(vfs_handle_struct *handle,
5166 const char *bundle,
5167 size_t *_nbands)
5169 char *path = NULL;
5170 struct smb_filename *bands_dir = NULL;
5171 struct smb_Dir *dir_hnd = NULL;
5172 const char *dname = NULL;
5173 char *talloced = NULL;
5174 size_t nbands;
5175 NTSTATUS status;
5177 path = talloc_asprintf(talloc_tos(),
5178 "%s/%s/bands",
5179 handle->conn->connectpath,
5180 bundle);
5181 if (path == NULL) {
5182 return false;
5185 bands_dir = synthetic_smb_fname(talloc_tos(),
5186 path,
5187 NULL,
5188 NULL,
5191 TALLOC_FREE(path);
5192 if (bands_dir == NULL) {
5193 return false;
5196 status = OpenDir(talloc_tos(),
5197 handle->conn,
5198 bands_dir,
5199 NULL,
5201 &dir_hnd);
5202 if (!NT_STATUS_IS_OK(status)) {
5203 TALLOC_FREE(bands_dir);
5204 errno = map_errno_from_nt_status(status);
5205 return false;
5208 nbands = 0;
5210 while ((dname = ReadDirName(dir_hnd, &talloced)) != NULL) {
5211 if (ISDOT(dname) || ISDOTDOT(dname)) {
5212 continue;
5214 nbands++;
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);
5222 *_nbands = nbands;
5223 return true;
5226 static bool fruit_tmsize_do_dirent(vfs_handle_struct *handle,
5227 struct fruit_disk_free_state *state,
5228 const char *name)
5230 bool ok;
5231 char *p = NULL;
5232 size_t sparsebundle_strlen = strlen("sparsebundle");
5233 size_t bandsize = 0;
5234 size_t nbands;
5235 off_t tm_size;
5237 p = strstr(name, "sparsebundle");
5238 if (p == NULL) {
5239 return true;
5242 if (p[sparsebundle_strlen] != '\0') {
5243 return true;
5246 DBG_DEBUG("Processing sparsebundle [%s]\n", name);
5248 ok = fruit_get_bandsize(handle, name, &bandsize);
5249 if (!ok) {
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);
5256 return true;
5259 ok = fruit_get_num_bands(handle, name, &nbands);
5260 if (!ok) {
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);
5267 return true;
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",
5278 bandsize, nbands);
5279 return false;
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",
5285 bandsize, nbands);
5286 return false;
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);
5294 return true;
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,
5310 uint64_t *_bsize,
5311 uint64_t *_dfree,
5312 uint64_t *_dsize)
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;
5319 uint64_t dfree;
5320 uint64_t dsize;
5321 bool ok;
5322 NTSTATUS status;
5324 SMB_VFS_HANDLE_GET_DATA(handle, config,
5325 struct fruit_config_data,
5326 return UINT64_MAX);
5328 if (!config->time_machine ||
5329 config->time_machine_max_size == 0)
5331 return SMB_VFS_NEXT_DISK_FREE(handle,
5332 smb_fname,
5333 _bsize,
5334 _dfree,
5335 _dsize);
5338 status = OpenDir(talloc_tos(),
5339 handle->conn,
5340 smb_fname,
5341 NULL,
5343 &dir_hnd);
5344 if (!NT_STATUS_IS_OK(status)) {
5345 errno = map_errno_from_nt_status(status);
5346 return UINT64_MAX;
5349 while ((dname = ReadDirName(dir_hnd, &talloced)) != NULL) {
5350 ok = fruit_tmsize_do_dirent(handle, &state, dname);
5351 if (!ok) {
5352 TALLOC_FREE(talloced);
5353 TALLOC_FREE(dir_hnd);
5354 return UINT64_MAX;
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) {
5364 dfree = 0;
5367 *_bsize = 512;
5368 *_dsize = dsize;
5369 *_dfree = dfree;
5370 return dfree / 2;
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,
5380 return 0);
5382 if (global_fruit_config.nego_aapl &&
5383 config->aapl_zero_file_id)
5385 return 0;
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,
5429 static_decl_vfs;
5430 NTSTATUS vfs_fruit_init(TALLOC_CTX *ctx)
5432 NTSTATUS ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "fruit",
5433 &vfs_fruit_fns);
5434 if (!NT_STATUS_IS_OK(ret)) {
5435 return 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",
5442 "vfs_fruit_init"));
5443 } else {
5444 DEBUG(10, ("%s: Debug class number of '%s': %d\n",
5445 "vfs_fruit_init","fruit",vfs_fruit_debug_level));
5448 return ret;