vfs_fruit: use "fake_fd" instead of "created"
[Samba.git] / source3 / modules / vfs_fruit.c
blob0cf819f7f780a6a7dc91136ca930d764c1c37887
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 wipe_intentionally_left_blank_rfork;
135 bool delete_empty_adfiles;
138 * Additional options, all enabled by default,
139 * possibly useful for analyzing performance. The associated
140 * operations with each of them may be expensive, so having
141 * the chance to disable them individually gives a chance
142 * tweaking the setup for the particular usecase.
144 bool readdir_attr_rsize;
145 bool readdir_attr_finder_info;
146 bool readdir_attr_max_access;
149 static const struct enum_list fruit_rsrc[] = {
150 {FRUIT_RSRC_STREAM, "stream"}, /* pass on to vfs_streams_xattr */
151 {FRUIT_RSRC_ADFILE, "file"}, /* ._ AppleDouble file */
152 {FRUIT_RSRC_XATTR, "xattr"}, /* Netatalk compatible xattr (ZFS only) */
153 { -1, NULL}
156 static const struct enum_list fruit_meta[] = {
157 {FRUIT_META_STREAM, "stream"}, /* pass on to vfs_streams_xattr */
158 {FRUIT_META_NETATALK, "netatalk"}, /* Netatalk compatible xattr */
159 { -1, NULL}
162 static const struct enum_list fruit_locking[] = {
163 {FRUIT_LOCKING_NETATALK, "netatalk"}, /* synchronize locks with Netatalk */
164 {FRUIT_LOCKING_NONE, "none"},
165 { -1, NULL}
168 static const struct enum_list fruit_encoding[] = {
169 {FRUIT_ENC_NATIVE, "native"}, /* map unicode private chars to ASCII */
170 {FRUIT_ENC_PRIVATE, "private"}, /* keep unicode private chars */
171 { -1, NULL}
174 struct fio {
175 /* tcon config handle */
176 struct fruit_config_data *config;
178 /* Denote stream type, meta or rsrc */
179 adouble_type_t type;
182 * AFP_AfpInfo stream created, but not written yet, thus still a fake
183 * pipe fd. This is set to true in fruit_open_meta if there was no
184 * existing stream but the caller requested O_CREAT. It is later set to
185 * false when we get a write on the stream that then does open and
186 * create the stream.
188 bool fake_fd;
189 int flags;
190 int mode;
193 /*****************************************************************************
194 * Helper functions
195 *****************************************************************************/
198 * Initialize config struct from our smb.conf config parameters
200 static int init_fruit_config(vfs_handle_struct *handle)
202 struct fruit_config_data *config;
203 int enumval;
204 const char *tm_size_str = NULL;
206 config = talloc_zero(handle->conn, struct fruit_config_data);
207 if (!config) {
208 DEBUG(1, ("talloc_zero() failed\n"));
209 errno = ENOMEM;
210 return -1;
214 * Versions up to Samba 4.5.x had a spelling bug in the
215 * fruit:resource option calling lp_parm_enum with
216 * "res*s*ource" (ie two s).
218 * In Samba 4.6 we accept both the wrong and the correct
219 * spelling, in Samba 4.7 the bad spelling will be removed.
221 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
222 "ressource", fruit_rsrc, FRUIT_RSRC_ADFILE);
223 if (enumval == -1) {
224 DEBUG(1, ("value for %s: resource type unknown\n",
225 FRUIT_PARAM_TYPE_NAME));
226 return -1;
228 config->rsrc = (enum fruit_rsrc)enumval;
230 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
231 "resource", fruit_rsrc, enumval);
232 if (enumval == -1) {
233 DEBUG(1, ("value for %s: resource type unknown\n",
234 FRUIT_PARAM_TYPE_NAME));
235 return -1;
237 config->rsrc = (enum fruit_rsrc)enumval;
239 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
240 "metadata", fruit_meta, FRUIT_META_NETATALK);
241 if (enumval == -1) {
242 DEBUG(1, ("value for %s: metadata type unknown\n",
243 FRUIT_PARAM_TYPE_NAME));
244 return -1;
246 config->meta = (enum fruit_meta)enumval;
248 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
249 "locking", fruit_locking, FRUIT_LOCKING_NONE);
250 if (enumval == -1) {
251 DEBUG(1, ("value for %s: locking type unknown\n",
252 FRUIT_PARAM_TYPE_NAME));
253 return -1;
255 config->locking = (enum fruit_locking)enumval;
257 enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
258 "encoding", fruit_encoding, FRUIT_ENC_PRIVATE);
259 if (enumval == -1) {
260 DEBUG(1, ("value for %s: encoding type unknown\n",
261 FRUIT_PARAM_TYPE_NAME));
262 return -1;
264 config->encoding = (enum fruit_encoding)enumval;
266 if (config->rsrc == FRUIT_RSRC_ADFILE) {
267 config->veto_appledouble = lp_parm_bool(SNUM(handle->conn),
268 FRUIT_PARAM_TYPE_NAME,
269 "veto_appledouble",
270 true);
273 config->use_aapl = lp_parm_bool(
274 -1, FRUIT_PARAM_TYPE_NAME, "aapl", true);
276 config->time_machine = lp_parm_bool(
277 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "time machine", false);
279 config->unix_info_enabled = lp_parm_bool(
280 -1, FRUIT_PARAM_TYPE_NAME, "nfs_aces", true);
282 config->use_copyfile = lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME,
283 "copyfile", false);
285 config->posix_rename = lp_parm_bool(
286 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "posix_rename", true);
288 config->aapl_zero_file_id =
289 lp_parm_bool(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
290 "zero_file_id", false);
292 config->readdir_attr_rsize = lp_parm_bool(
293 SNUM(handle->conn), "readdir_attr", "aapl_rsize", true);
295 config->readdir_attr_finder_info = lp_parm_bool(
296 SNUM(handle->conn), "readdir_attr", "aapl_finder_info", true);
298 config->readdir_attr_max_access = lp_parm_bool(
299 SNUM(handle->conn), "readdir_attr", "aapl_max_access", true);
301 config->model = lp_parm_const_string(
302 -1, FRUIT_PARAM_TYPE_NAME, "model", "MacSamba");
304 tm_size_str = lp_parm_const_string(
305 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
306 "time machine max size", NULL);
307 if (tm_size_str != NULL) {
308 config->time_machine_max_size = conv_str_size(tm_size_str);
311 config->wipe_intentionally_left_blank_rfork = lp_parm_bool(
312 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
313 "wipe_intentionally_left_blank_rfork", false);
315 config->delete_empty_adfiles = lp_parm_bool(
316 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
317 "delete_empty_adfiles", false);
319 SMB_VFS_HANDLE_SET_DATA(handle, config,
320 NULL, struct fruit_config_data,
321 return -1);
323 return 0;
326 static bool add_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
327 struct stream_struct **streams,
328 const char *name, off_t size,
329 off_t alloc_size)
331 struct stream_struct *tmp;
333 tmp = talloc_realloc(mem_ctx, *streams, struct stream_struct,
334 (*num_streams)+1);
335 if (tmp == NULL) {
336 return false;
339 tmp[*num_streams].name = talloc_asprintf(tmp, "%s:$DATA", name);
340 if (tmp[*num_streams].name == NULL) {
341 return false;
344 tmp[*num_streams].size = size;
345 tmp[*num_streams].alloc_size = alloc_size;
347 *streams = tmp;
348 *num_streams += 1;
349 return true;
352 static bool filter_empty_rsrc_stream(unsigned int *num_streams,
353 struct stream_struct **streams)
355 struct stream_struct *tmp = *streams;
356 unsigned int i;
358 if (*num_streams == 0) {
359 return true;
362 for (i = 0; i < *num_streams; i++) {
363 if (strequal_m(tmp[i].name, AFPRESOURCE_STREAM)) {
364 break;
368 if (i == *num_streams) {
369 return true;
372 if (tmp[i].size > 0) {
373 return true;
376 TALLOC_FREE(tmp[i].name);
377 ARRAY_DEL_ELEMENT(tmp, i, *num_streams);
378 *num_streams -= 1;
379 return true;
382 static bool del_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
383 struct stream_struct **streams,
384 const char *name)
386 struct stream_struct *tmp = *streams;
387 unsigned int i;
389 if (*num_streams == 0) {
390 return true;
393 for (i = 0; i < *num_streams; i++) {
394 if (strequal_m(tmp[i].name, name)) {
395 break;
399 if (i == *num_streams) {
400 return true;
403 TALLOC_FREE(tmp[i].name);
404 ARRAY_DEL_ELEMENT(tmp, i, *num_streams);
405 *num_streams -= 1;
406 return true;
409 static bool ad_empty_finderinfo(const struct adouble *ad)
411 int cmp;
412 char emptybuf[ADEDLEN_FINDERI] = {0};
413 char *fi = NULL;
415 fi = ad_get_entry(ad, ADEID_FINDERI);
416 if (fi == NULL) {
417 DBG_ERR("Missing FinderInfo in struct adouble [%p]\n", ad);
418 return false;
421 cmp = memcmp(emptybuf, fi, ADEDLEN_FINDERI);
422 return (cmp == 0);
425 static bool ai_empty_finderinfo(const AfpInfo *ai)
427 int cmp;
428 char emptybuf[ADEDLEN_FINDERI] = {0};
430 cmp = memcmp(emptybuf, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
431 return (cmp == 0);
435 * Update btime with btime from Netatalk
437 static void update_btime(vfs_handle_struct *handle,
438 struct smb_filename *smb_fname)
440 uint32_t t;
441 struct timespec creation_time = {0};
442 struct adouble *ad;
443 struct fruit_config_data *config = NULL;
445 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
446 return);
448 switch (config->meta) {
449 case FRUIT_META_STREAM:
450 return;
451 case FRUIT_META_NETATALK:
452 /* Handled below */
453 break;
454 default:
455 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
456 return;
459 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
460 if (ad == NULL) {
461 return;
463 if (ad_getdate(ad, AD_DATE_UNIX | AD_DATE_CREATE, &t) != 0) {
464 TALLOC_FREE(ad);
465 return;
467 TALLOC_FREE(ad);
469 creation_time.tv_sec = convert_uint32_t_to_time_t(t);
470 update_stat_ex_create_time(&smb_fname->st, creation_time);
472 return;
476 * Map an access mask to a Netatalk single byte byte range lock
478 static off_t access_to_netatalk_brl(enum apple_fork fork_type,
479 uint32_t access_mask)
481 off_t offset;
483 switch (access_mask) {
484 case FILE_READ_DATA:
485 offset = AD_FILELOCK_OPEN_RD;
486 break;
488 case FILE_WRITE_DATA:
489 case FILE_APPEND_DATA:
490 offset = AD_FILELOCK_OPEN_WR;
491 break;
493 default:
494 offset = AD_FILELOCK_OPEN_NONE;
495 break;
498 if (fork_type == APPLE_FORK_RSRC) {
499 if (offset == AD_FILELOCK_OPEN_NONE) {
500 offset = AD_FILELOCK_RSRC_OPEN_NONE;
501 } else {
502 offset += 2;
506 return offset;
510 * Map a deny mode to a Netatalk brl
512 static off_t denymode_to_netatalk_brl(enum apple_fork fork_type,
513 uint32_t deny_mode)
515 off_t offset = 0;
517 switch (deny_mode) {
518 case DENY_READ:
519 offset = AD_FILELOCK_DENY_RD;
520 break;
522 case DENY_WRITE:
523 offset = AD_FILELOCK_DENY_WR;
524 break;
526 default:
527 smb_panic("denymode_to_netatalk_brl: bad deny mode\n");
530 if (fork_type == APPLE_FORK_RSRC) {
531 offset += 2;
534 return offset;
538 * Call fcntl() with an exclusive F_GETLK request in order to
539 * determine if there's an existing shared lock
541 * @return true if the requested lock was found or any error occurred
542 * false if the lock was not found
544 static bool test_netatalk_lock(files_struct *fsp, off_t in_offset)
546 bool result;
547 off_t offset = in_offset;
548 off_t len = 1;
549 int type = F_WRLCK;
550 pid_t pid = 0;
552 result = SMB_VFS_GETLOCK(fsp, &offset, &len, &type, &pid);
553 if (result == false) {
554 return true;
557 if (type != F_UNLCK) {
558 return true;
561 return false;
564 static NTSTATUS fruit_check_access(vfs_handle_struct *handle,
565 files_struct *fsp,
566 uint32_t access_mask,
567 uint32_t share_mode)
569 NTSTATUS status = NT_STATUS_OK;
570 off_t off;
571 bool share_for_read = (share_mode & FILE_SHARE_READ);
572 bool share_for_write = (share_mode & FILE_SHARE_WRITE);
573 bool netatalk_already_open_for_reading = false;
574 bool netatalk_already_open_for_writing = false;
575 bool netatalk_already_open_with_deny_read = false;
576 bool netatalk_already_open_with_deny_write = false;
577 struct GUID req_guid = GUID_random();
579 /* FIXME: hardcoded data fork, add resource fork */
580 enum apple_fork fork_type = APPLE_FORK_DATA;
582 DBG_DEBUG("fruit_check_access: %s, am: %s/%s, sm: 0x%x\n",
583 fsp_str_dbg(fsp),
584 access_mask & FILE_READ_DATA ? "READ" :"-",
585 access_mask & FILE_WRITE_DATA ? "WRITE" : "-",
586 share_mode);
588 if (fsp_get_io_fd(fsp) == -1) {
589 return NT_STATUS_OK;
592 /* Read NetATalk opens and deny modes on the file. */
593 netatalk_already_open_for_reading = test_netatalk_lock(fsp,
594 access_to_netatalk_brl(fork_type,
595 FILE_READ_DATA));
597 netatalk_already_open_with_deny_read = test_netatalk_lock(fsp,
598 denymode_to_netatalk_brl(fork_type,
599 DENY_READ));
601 netatalk_already_open_for_writing = test_netatalk_lock(fsp,
602 access_to_netatalk_brl(fork_type,
603 FILE_WRITE_DATA));
605 netatalk_already_open_with_deny_write = test_netatalk_lock(fsp,
606 denymode_to_netatalk_brl(fork_type,
607 DENY_WRITE));
609 /* If there are any conflicts - sharing violation. */
610 if ((access_mask & FILE_READ_DATA) &&
611 netatalk_already_open_with_deny_read) {
612 return NT_STATUS_SHARING_VIOLATION;
615 if (!share_for_read &&
616 netatalk_already_open_for_reading) {
617 return NT_STATUS_SHARING_VIOLATION;
620 if ((access_mask & FILE_WRITE_DATA) &&
621 netatalk_already_open_with_deny_write) {
622 return NT_STATUS_SHARING_VIOLATION;
625 if (!share_for_write &&
626 netatalk_already_open_for_writing) {
627 return NT_STATUS_SHARING_VIOLATION;
630 if (!(access_mask & FILE_READ_DATA)) {
632 * Nothing we can do here, we need read access
633 * to set locks.
635 return NT_STATUS_OK;
638 /* Set NetAtalk locks matching our access */
639 if (access_mask & FILE_READ_DATA) {
640 off = access_to_netatalk_brl(fork_type, FILE_READ_DATA);
641 req_guid.time_hi_and_version = __LINE__;
642 status = do_lock(
643 fsp,
644 talloc_tos(),
645 &req_guid,
646 fsp->op->global->open_persistent_id,
648 off,
649 READ_LOCK,
650 POSIX_LOCK,
651 NULL,
652 NULL);
654 if (!NT_STATUS_IS_OK(status)) {
655 return status;
659 if (!share_for_read) {
660 off = denymode_to_netatalk_brl(fork_type, DENY_READ);
661 req_guid.time_hi_and_version = __LINE__;
662 status = do_lock(
663 fsp,
664 talloc_tos(),
665 &req_guid,
666 fsp->op->global->open_persistent_id,
668 off,
669 READ_LOCK,
670 POSIX_LOCK,
671 NULL,
672 NULL);
674 if (!NT_STATUS_IS_OK(status)) {
675 return status;
679 if (access_mask & FILE_WRITE_DATA) {
680 off = access_to_netatalk_brl(fork_type, FILE_WRITE_DATA);
681 req_guid.time_hi_and_version = __LINE__;
682 status = do_lock(
683 fsp,
684 talloc_tos(),
685 &req_guid,
686 fsp->op->global->open_persistent_id,
688 off,
689 READ_LOCK,
690 POSIX_LOCK,
691 NULL,
692 NULL);
694 if (!NT_STATUS_IS_OK(status)) {
695 return status;
699 if (!share_for_write) {
700 off = denymode_to_netatalk_brl(fork_type, DENY_WRITE);
701 req_guid.time_hi_and_version = __LINE__;
702 status = do_lock(
703 fsp,
704 talloc_tos(),
705 &req_guid,
706 fsp->op->global->open_persistent_id,
708 off,
709 READ_LOCK,
710 POSIX_LOCK,
711 NULL,
712 NULL);
714 if (!NT_STATUS_IS_OK(status)) {
715 return status;
719 return NT_STATUS_OK;
722 static NTSTATUS check_aapl(vfs_handle_struct *handle,
723 struct smb_request *req,
724 const struct smb2_create_blobs *in_context_blobs,
725 struct smb2_create_blobs *out_context_blobs)
727 struct fruit_config_data *config;
728 NTSTATUS status;
729 struct smb2_create_blob *aapl = NULL;
730 uint32_t cmd;
731 bool ok;
732 uint8_t p[16];
733 DATA_BLOB blob = data_blob_talloc(req, NULL, 0);
734 uint64_t req_bitmap, client_caps;
735 uint64_t server_caps = SMB2_CRTCTX_AAPL_UNIX_BASED;
736 smb_ucs2_t *model;
737 size_t modellen;
739 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
740 return NT_STATUS_UNSUCCESSFUL);
742 if (!config->use_aapl
743 || in_context_blobs == NULL
744 || out_context_blobs == NULL) {
745 return NT_STATUS_OK;
748 aapl = smb2_create_blob_find(in_context_blobs,
749 SMB2_CREATE_TAG_AAPL);
750 if (aapl == NULL) {
751 return NT_STATUS_OK;
754 if (aapl->data.length != 24) {
755 DEBUG(1, ("unexpected AAPL ctxt length: %ju\n",
756 (uintmax_t)aapl->data.length));
757 return NT_STATUS_INVALID_PARAMETER;
760 cmd = IVAL(aapl->data.data, 0);
761 if (cmd != SMB2_CRTCTX_AAPL_SERVER_QUERY) {
762 DEBUG(1, ("unsupported AAPL cmd: %d\n", cmd));
763 return NT_STATUS_INVALID_PARAMETER;
766 req_bitmap = BVAL(aapl->data.data, 8);
767 client_caps = BVAL(aapl->data.data, 16);
769 SIVAL(p, 0, SMB2_CRTCTX_AAPL_SERVER_QUERY);
770 SIVAL(p, 4, 0);
771 SBVAL(p, 8, req_bitmap);
772 ok = data_blob_append(req, &blob, p, 16);
773 if (!ok) {
774 return NT_STATUS_UNSUCCESSFUL;
777 if (req_bitmap & SMB2_CRTCTX_AAPL_SERVER_CAPS) {
778 if ((client_caps & SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR) &&
779 (handle->conn->tcon->compat->fs_capabilities & FILE_NAMED_STREAMS)) {
780 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR;
781 config->readdir_attr_enabled = true;
784 if (config->use_copyfile) {
785 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_OSX_COPYFILE;
786 config->copyfile_enabled = true;
790 * The client doesn't set the flag, so we can't check
791 * for it and just set it unconditionally
793 if (config->unix_info_enabled) {
794 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_NFS_ACE;
797 SBVAL(p, 0, server_caps);
798 ok = data_blob_append(req, &blob, p, 8);
799 if (!ok) {
800 return NT_STATUS_UNSUCCESSFUL;
804 if (req_bitmap & SMB2_CRTCTX_AAPL_VOLUME_CAPS) {
805 int val = lp_case_sensitive(SNUM(handle->conn->tcon->compat));
806 uint64_t caps = 0;
808 switch (val) {
809 case Auto:
810 break;
812 case True:
813 caps |= SMB2_CRTCTX_AAPL_CASE_SENSITIVE;
814 break;
816 default:
817 break;
820 if (config->time_machine) {
821 caps |= SMB2_CRTCTX_AAPL_FULL_SYNC;
824 SBVAL(p, 0, caps);
826 ok = data_blob_append(req, &blob, p, 8);
827 if (!ok) {
828 return NT_STATUS_UNSUCCESSFUL;
832 if (req_bitmap & SMB2_CRTCTX_AAPL_MODEL_INFO) {
833 ok = convert_string_talloc(req,
834 CH_UNIX, CH_UTF16LE,
835 config->model, strlen(config->model),
836 &model, &modellen);
837 if (!ok) {
838 return NT_STATUS_UNSUCCESSFUL;
841 SIVAL(p, 0, 0);
842 SIVAL(p + 4, 0, modellen);
843 ok = data_blob_append(req, &blob, p, 8);
844 if (!ok) {
845 talloc_free(model);
846 return NT_STATUS_UNSUCCESSFUL;
849 ok = data_blob_append(req, &blob, model, modellen);
850 talloc_free(model);
851 if (!ok) {
852 return NT_STATUS_UNSUCCESSFUL;
856 status = smb2_create_blob_add(out_context_blobs,
857 out_context_blobs,
858 SMB2_CREATE_TAG_AAPL,
859 blob);
860 if (NT_STATUS_IS_OK(status)) {
861 global_fruit_config.nego_aapl = true;
864 return status;
867 static bool readdir_attr_meta_finderi_stream(
868 struct vfs_handle_struct *handle,
869 const struct smb_filename *smb_fname,
870 AfpInfo *ai)
872 struct smb_filename *stream_name = NULL;
873 files_struct *fsp = NULL;
874 ssize_t nread;
875 NTSTATUS status;
876 int ret;
877 bool ok;
878 uint8_t buf[AFP_INFO_SIZE];
880 stream_name = synthetic_smb_fname(talloc_tos(),
881 smb_fname->base_name,
882 AFPINFO_STREAM_NAME,
883 NULL,
884 smb_fname->twrp,
885 smb_fname->flags);
886 if (stream_name == NULL) {
887 return false;
890 ret = SMB_VFS_STAT(handle->conn, stream_name);
891 if (ret != 0) {
892 return false;
895 status = openat_pathref_fsp(handle->conn->cwd_fsp, stream_name);
896 if (!NT_STATUS_IS_OK(status)) {
897 return false;
900 status = SMB_VFS_CREATE_FILE(
901 handle->conn, /* conn */
902 NULL, /* req */
903 stream_name, /* fname */
904 FILE_READ_DATA, /* access_mask */
905 (FILE_SHARE_READ | FILE_SHARE_WRITE | /* share_access */
906 FILE_SHARE_DELETE),
907 FILE_OPEN, /* create_disposition*/
908 0, /* create_options */
909 0, /* file_attributes */
910 INTERNAL_OPEN_ONLY, /* oplock_request */
911 NULL, /* lease */
912 0, /* allocation_size */
913 0, /* private_flags */
914 NULL, /* sd */
915 NULL, /* ea_list */
916 &fsp, /* result */
917 NULL, /* pinfo */
918 NULL, NULL); /* create context */
920 TALLOC_FREE(stream_name);
922 if (!NT_STATUS_IS_OK(status)) {
923 return false;
926 nread = SMB_VFS_PREAD(fsp, &buf[0], AFP_INFO_SIZE, 0);
927 if (nread != AFP_INFO_SIZE) {
928 DBG_ERR("short read [%s] [%zd/%d]\n",
929 smb_fname_str_dbg(stream_name), nread, AFP_INFO_SIZE);
930 ok = false;
931 goto fail;
934 memcpy(&ai->afpi_FinderInfo[0], &buf[AFP_OFF_FinderInfo],
935 AFP_FinderSize);
937 ok = true;
939 fail:
940 if (fsp != NULL) {
941 close_file(NULL, fsp, NORMAL_CLOSE);
944 return ok;
947 static bool readdir_attr_meta_finderi_netatalk(
948 struct vfs_handle_struct *handle,
949 const struct smb_filename *smb_fname,
950 AfpInfo *ai)
952 struct adouble *ad = NULL;
953 char *p = NULL;
955 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
956 if (ad == NULL) {
957 return false;
960 p = ad_get_entry(ad, ADEID_FINDERI);
961 if (p == NULL) {
962 DBG_ERR("No ADEID_FINDERI for [%s]\n", smb_fname->base_name);
963 TALLOC_FREE(ad);
964 return false;
967 memcpy(&ai->afpi_FinderInfo[0], p, AFP_FinderSize);
968 TALLOC_FREE(ad);
969 return true;
972 static bool readdir_attr_meta_finderi(struct vfs_handle_struct *handle,
973 const struct smb_filename *smb_fname,
974 struct readdir_attr_data *attr_data)
976 struct fruit_config_data *config = NULL;
977 uint32_t date_added;
978 AfpInfo ai = {0};
979 bool ok;
981 SMB_VFS_HANDLE_GET_DATA(handle, config,
982 struct fruit_config_data,
983 return false);
985 switch (config->meta) {
986 case FRUIT_META_NETATALK:
987 ok = readdir_attr_meta_finderi_netatalk(
988 handle, smb_fname, &ai);
989 break;
991 case FRUIT_META_STREAM:
992 ok = readdir_attr_meta_finderi_stream(
993 handle, smb_fname, &ai);
994 break;
996 default:
997 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
998 return false;
1001 if (!ok) {
1002 /* Don't bother with errors, it's likely ENOENT */
1003 return true;
1006 if (S_ISREG(smb_fname->st.st_ex_mode)) {
1007 /* finder_type */
1008 memcpy(&attr_data->attr_data.aapl.finder_info[0],
1009 &ai.afpi_FinderInfo[0], 4);
1011 /* finder_creator */
1012 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 4,
1013 &ai.afpi_FinderInfo[4], 4);
1016 /* finder_flags */
1017 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 8,
1018 &ai.afpi_FinderInfo[8], 2);
1020 /* finder_ext_flags */
1021 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 10,
1022 &ai.afpi_FinderInfo[24], 2);
1024 /* creation date */
1025 date_added = convert_time_t_to_uint32_t(
1026 smb_fname->st.st_ex_btime.tv_sec - AD_DATE_DELTA);
1028 RSIVAL(&attr_data->attr_data.aapl.finder_info[0], 12, date_added);
1030 return true;
1033 static uint64_t readdir_attr_rfork_size_adouble(
1034 struct vfs_handle_struct *handle,
1035 const struct smb_filename *smb_fname)
1037 struct adouble *ad = NULL;
1038 uint64_t rfork_size;
1040 ad = ad_get(talloc_tos(), handle, smb_fname,
1041 ADOUBLE_RSRC);
1042 if (ad == NULL) {
1043 return 0;
1046 rfork_size = ad_getentrylen(ad, ADEID_RFORK);
1047 TALLOC_FREE(ad);
1049 return rfork_size;
1052 static uint64_t readdir_attr_rfork_size_stream(
1053 struct vfs_handle_struct *handle,
1054 const struct smb_filename *smb_fname)
1056 struct smb_filename *stream_name = NULL;
1057 int ret;
1058 uint64_t rfork_size;
1060 stream_name = synthetic_smb_fname(talloc_tos(),
1061 smb_fname->base_name,
1062 AFPRESOURCE_STREAM_NAME,
1063 NULL,
1064 smb_fname->twrp,
1066 if (stream_name == NULL) {
1067 return 0;
1070 ret = SMB_VFS_STAT(handle->conn, stream_name);
1071 if (ret != 0) {
1072 TALLOC_FREE(stream_name);
1073 return 0;
1076 rfork_size = stream_name->st.st_ex_size;
1077 TALLOC_FREE(stream_name);
1079 return rfork_size;
1082 static uint64_t readdir_attr_rfork_size(struct vfs_handle_struct *handle,
1083 const struct smb_filename *smb_fname)
1085 struct fruit_config_data *config = NULL;
1086 uint64_t rfork_size;
1088 SMB_VFS_HANDLE_GET_DATA(handle, config,
1089 struct fruit_config_data,
1090 return 0);
1092 switch (config->rsrc) {
1093 case FRUIT_RSRC_ADFILE:
1094 rfork_size = readdir_attr_rfork_size_adouble(handle,
1095 smb_fname);
1096 break;
1098 case FRUIT_RSRC_XATTR:
1099 case FRUIT_RSRC_STREAM:
1100 rfork_size = readdir_attr_rfork_size_stream(handle,
1101 smb_fname);
1102 break;
1104 default:
1105 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
1106 rfork_size = 0;
1107 break;
1110 return rfork_size;
1113 static NTSTATUS readdir_attr_macmeta(struct vfs_handle_struct *handle,
1114 const struct smb_filename *smb_fname,
1115 struct readdir_attr_data *attr_data)
1117 NTSTATUS status = NT_STATUS_OK;
1118 struct fruit_config_data *config = NULL;
1119 bool ok;
1121 SMB_VFS_HANDLE_GET_DATA(handle, config,
1122 struct fruit_config_data,
1123 return NT_STATUS_UNSUCCESSFUL);
1126 /* Ensure we return a default value in the creation_date field */
1127 RSIVAL(&attr_data->attr_data.aapl.finder_info, 12, AD_DATE_START);
1130 * Resource fork length
1133 if (config->readdir_attr_rsize) {
1134 uint64_t rfork_size;
1136 rfork_size = readdir_attr_rfork_size(handle, smb_fname);
1137 attr_data->attr_data.aapl.rfork_size = rfork_size;
1141 * FinderInfo
1144 if (config->readdir_attr_finder_info) {
1145 ok = readdir_attr_meta_finderi(handle, smb_fname, attr_data);
1146 if (!ok) {
1147 status = NT_STATUS_INTERNAL_ERROR;
1151 return status;
1154 static NTSTATUS remove_virtual_nfs_aces(struct security_descriptor *psd)
1156 NTSTATUS status;
1157 uint32_t i;
1159 if (psd->dacl == NULL) {
1160 return NT_STATUS_OK;
1163 for (i = 0; i < psd->dacl->num_aces; i++) {
1164 /* MS NFS style mode/uid/gid */
1165 int cmp = dom_sid_compare_domain(
1166 &global_sid_Unix_NFS,
1167 &psd->dacl->aces[i].trustee);
1168 if (cmp != 0) {
1169 /* Normal ACE entry. */
1170 continue;
1174 * security_descriptor_dacl_del()
1175 * *must* return NT_STATUS_OK as we know
1176 * we have something to remove.
1179 status = security_descriptor_dacl_del(psd,
1180 &psd->dacl->aces[i].trustee);
1181 if (!NT_STATUS_IS_OK(status)) {
1182 DBG_WARNING("failed to remove MS NFS style ACE: %s\n",
1183 nt_errstr(status));
1184 return status;
1188 * security_descriptor_dacl_del() may delete more
1189 * then one entry subsequent to this one if the
1190 * SID matches, but we only need to ensure that
1191 * we stay looking at the same element in the array.
1193 i--;
1195 return NT_STATUS_OK;
1198 /* Search MS NFS style ACE with UNIX mode */
1199 static NTSTATUS check_ms_nfs(vfs_handle_struct *handle,
1200 files_struct *fsp,
1201 struct security_descriptor *psd,
1202 mode_t *pmode,
1203 bool *pdo_chmod)
1205 uint32_t i;
1206 struct fruit_config_data *config = NULL;
1208 *pdo_chmod = false;
1210 SMB_VFS_HANDLE_GET_DATA(handle, config,
1211 struct fruit_config_data,
1212 return NT_STATUS_UNSUCCESSFUL);
1214 if (!global_fruit_config.nego_aapl) {
1215 return NT_STATUS_OK;
1217 if (psd->dacl == NULL || !config->unix_info_enabled) {
1218 return NT_STATUS_OK;
1221 for (i = 0; i < psd->dacl->num_aces; i++) {
1222 if (dom_sid_compare_domain(
1223 &global_sid_Unix_NFS_Mode,
1224 &psd->dacl->aces[i].trustee) == 0) {
1225 *pmode = (mode_t)psd->dacl->aces[i].trustee.sub_auths[2];
1226 *pmode &= (S_IRWXU | S_IRWXG | S_IRWXO);
1227 *pdo_chmod = true;
1229 DEBUG(10, ("MS NFS chmod request %s, %04o\n",
1230 fsp_str_dbg(fsp), (unsigned)(*pmode)));
1231 break;
1236 * Remove any incoming virtual ACE entries generated by
1237 * fruit_fget_nt_acl().
1240 return remove_virtual_nfs_aces(psd);
1243 /****************************************************************************
1244 * VFS ops
1245 ****************************************************************************/
1247 static int fruit_connect(vfs_handle_struct *handle,
1248 const char *service,
1249 const char *user)
1251 int rc;
1252 char *list = NULL, *newlist = NULL;
1253 struct fruit_config_data *config;
1254 const struct loadparm_substitution *lp_sub =
1255 loadparm_s3_global_substitution();
1257 DEBUG(10, ("fruit_connect\n"));
1259 rc = SMB_VFS_NEXT_CONNECT(handle, service, user);
1260 if (rc < 0) {
1261 return rc;
1264 rc = init_fruit_config(handle);
1265 if (rc != 0) {
1266 return rc;
1269 SMB_VFS_HANDLE_GET_DATA(handle, config,
1270 struct fruit_config_data, return -1);
1272 if (config->veto_appledouble) {
1273 list = lp_veto_files(talloc_tos(), lp_sub, SNUM(handle->conn));
1275 if (list) {
1276 if (strstr(list, "/" ADOUBLE_NAME_PREFIX "*/") == NULL) {
1277 newlist = talloc_asprintf(
1278 list,
1279 "%s/" ADOUBLE_NAME_PREFIX "*/",
1280 list);
1281 lp_do_parameter(SNUM(handle->conn),
1282 "veto files",
1283 newlist);
1285 } else {
1286 lp_do_parameter(SNUM(handle->conn),
1287 "veto files",
1288 "/" ADOUBLE_NAME_PREFIX "*/");
1291 TALLOC_FREE(list);
1294 if (config->encoding == FRUIT_ENC_NATIVE) {
1295 lp_do_parameter(SNUM(handle->conn),
1296 "catia:mappings",
1297 macos_string_replace_map);
1300 if (config->time_machine) {
1301 DBG_NOTICE("Enabling durable handles for Time Machine "
1302 "support on [%s]\n", service);
1303 lp_do_parameter(SNUM(handle->conn), "durable handles", "yes");
1304 lp_do_parameter(SNUM(handle->conn), "kernel oplocks", "no");
1305 lp_do_parameter(SNUM(handle->conn), "kernel share modes", "no");
1306 if (!lp_strict_sync(SNUM(handle->conn))) {
1307 DBG_WARNING("Time Machine without strict sync is not "
1308 "recommended!\n");
1310 lp_do_parameter(SNUM(handle->conn), "posix locking", "no");
1313 return rc;
1316 static int fruit_open_meta_stream(vfs_handle_struct *handle,
1317 const struct files_struct *dirfsp,
1318 const struct smb_filename *smb_fname,
1319 files_struct *fsp,
1320 int flags,
1321 mode_t mode)
1323 struct fruit_config_data *config = NULL;
1324 struct fio *fio = NULL;
1325 int open_flags = flags & ~O_CREAT;
1326 int fd;
1328 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
1330 SMB_VFS_HANDLE_GET_DATA(handle, config,
1331 struct fruit_config_data, return -1);
1333 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
1334 fio->type = ADOUBLE_META;
1335 fio->config = config;
1337 fd = SMB_VFS_NEXT_OPENAT(handle,
1338 dirfsp,
1339 smb_fname,
1340 fsp,
1341 open_flags,
1342 mode);
1343 if (fd != -1) {
1344 return fd;
1347 if (!(flags & O_CREAT)) {
1348 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
1349 return -1;
1352 fd = vfs_fake_fd();
1353 if (fd == -1) {
1354 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
1355 return -1;
1358 fio->fake_fd = true;
1359 fio->flags = flags;
1360 fio->mode = mode;
1362 return fd;
1365 static int fruit_open_meta_netatalk(vfs_handle_struct *handle,
1366 const struct files_struct *dirfsp,
1367 const struct smb_filename *smb_fname,
1368 files_struct *fsp,
1369 int flags,
1370 mode_t mode)
1372 struct fruit_config_data *config = NULL;
1373 struct fio *fio = NULL;
1374 struct adouble *ad = NULL;
1375 bool meta_exists = false;
1376 int fd;
1378 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
1380 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
1381 if (ad != NULL) {
1382 meta_exists = true;
1385 TALLOC_FREE(ad);
1387 if (!meta_exists && !(flags & O_CREAT)) {
1388 errno = ENOENT;
1389 return -1;
1392 fd = vfs_fake_fd();
1393 if (fd == -1) {
1394 return -1;
1397 SMB_VFS_HANDLE_GET_DATA(handle, config,
1398 struct fruit_config_data, return -1);
1400 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
1401 fio->type = ADOUBLE_META;
1402 fio->config = config;
1403 fio->fake_fd = true;
1404 fio->flags = flags;
1405 fio->mode = mode;
1407 return fd;
1410 static int fruit_open_meta(vfs_handle_struct *handle,
1411 const struct files_struct *dirfsp,
1412 const struct smb_filename *smb_fname,
1413 files_struct *fsp, int flags, mode_t mode)
1415 int fd;
1416 struct fruit_config_data *config = NULL;
1418 DBG_DEBUG("path [%s]\n", smb_fname_str_dbg(smb_fname));
1420 SMB_VFS_HANDLE_GET_DATA(handle, config,
1421 struct fruit_config_data, return -1);
1423 switch (config->meta) {
1424 case FRUIT_META_STREAM:
1425 fd = fruit_open_meta_stream(handle, dirfsp, smb_fname,
1426 fsp, flags, mode);
1427 break;
1429 case FRUIT_META_NETATALK:
1430 fd = fruit_open_meta_netatalk(handle, dirfsp, smb_fname,
1431 fsp, flags, mode);
1432 break;
1434 default:
1435 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
1436 return -1;
1439 DBG_DEBUG("path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
1441 return fd;
1444 static int fruit_open_rsrc_adouble(vfs_handle_struct *handle,
1445 const struct files_struct *dirfsp,
1446 const struct smb_filename *smb_fname,
1447 files_struct *fsp,
1448 int flags,
1449 mode_t mode)
1451 int rc = 0;
1452 struct adouble *ad = NULL;
1453 struct smb_filename *smb_fname_base = NULL;
1454 struct fruit_config_data *config = NULL;
1455 int hostfd = -1;
1457 SMB_VFS_HANDLE_GET_DATA(handle, config,
1458 struct fruit_config_data, return -1);
1460 if ((!(flags & O_CREAT)) &&
1461 S_ISDIR(fsp->base_fsp->fsp_name->st.st_ex_mode))
1463 /* sorry, but directories don't habe a resource fork */
1464 rc = -1;
1465 goto exit;
1468 rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_base);
1469 if (rc != 0) {
1470 goto exit;
1473 /* We always need read/write access for the metadata header too */
1474 flags &= ~(O_RDONLY | O_WRONLY);
1475 flags |= O_RDWR;
1477 hostfd = SMB_VFS_NEXT_OPENAT(handle,
1478 dirfsp,
1479 smb_fname_base,
1480 fsp,
1481 flags,
1482 mode);
1483 if (hostfd == -1) {
1484 rc = -1;
1485 goto exit;
1488 if (flags & (O_CREAT | O_TRUNC)) {
1489 ad = ad_init(fsp, ADOUBLE_RSRC);
1490 if (ad == NULL) {
1491 rc = -1;
1492 goto exit;
1495 fsp_set_fd(fsp, hostfd);
1497 rc = ad_fset(handle, ad, fsp);
1498 fsp_set_fd(fsp, -1);
1499 if (rc != 0) {
1500 rc = -1;
1501 goto exit;
1503 TALLOC_FREE(ad);
1506 exit:
1508 TALLOC_FREE(smb_fname_base);
1510 DEBUG(10, ("fruit_open resource fork: rc=%d, fd=%d\n", rc, hostfd));
1511 if (rc != 0) {
1512 int saved_errno = errno;
1513 if (hostfd >= 0) {
1515 * BUGBUGBUG -- we would need to call
1516 * fd_close_posix here, but we don't have a
1517 * full fsp yet
1519 fsp_set_fd(fsp, hostfd);
1520 SMB_VFS_CLOSE(fsp);
1522 hostfd = -1;
1523 errno = saved_errno;
1525 return hostfd;
1528 static int fruit_open_rsrc_xattr(vfs_handle_struct *handle,
1529 const struct files_struct *dirfsp,
1530 const struct smb_filename *smb_fname,
1531 files_struct *fsp,
1532 int flags,
1533 mode_t mode)
1535 #ifdef HAVE_ATTROPEN
1536 int fd = -1;
1539 * As there's no attropenat() this is only going to work with AT_FDCWD.
1541 SMB_ASSERT(fsp_get_pathref_fd(dirfsp) == AT_FDCWD);
1543 fd = attropen(smb_fname->base_name,
1544 AFPRESOURCE_EA_NETATALK,
1545 flags,
1546 mode);
1547 if (fd == -1) {
1548 return -1;
1551 return fd;
1553 #else
1554 errno = ENOSYS;
1555 return -1;
1556 #endif
1559 static int fruit_open_rsrc(vfs_handle_struct *handle,
1560 const struct files_struct *dirfsp,
1561 const struct smb_filename *smb_fname,
1562 files_struct *fsp, int flags, mode_t mode)
1564 int fd;
1565 struct fruit_config_data *config = NULL;
1566 struct fio *fio = NULL;
1568 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
1570 SMB_VFS_HANDLE_GET_DATA(handle, config,
1571 struct fruit_config_data, return -1);
1573 switch (config->rsrc) {
1574 case FRUIT_RSRC_STREAM:
1575 fd = SMB_VFS_NEXT_OPENAT(handle,
1576 dirfsp,
1577 smb_fname,
1578 fsp,
1579 flags,
1580 mode);
1581 break;
1583 case FRUIT_RSRC_ADFILE:
1584 fd = fruit_open_rsrc_adouble(handle, dirfsp, smb_fname,
1585 fsp, flags, mode);
1586 break;
1588 case FRUIT_RSRC_XATTR:
1589 fd = fruit_open_rsrc_xattr(handle, dirfsp, smb_fname,
1590 fsp, flags, mode);
1591 break;
1593 default:
1594 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
1595 return -1;
1598 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
1600 if (fd == -1) {
1601 return -1;
1604 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
1605 fio->type = ADOUBLE_RSRC;
1606 fio->config = config;
1608 return fd;
1611 static int fruit_openat(vfs_handle_struct *handle,
1612 const struct files_struct *dirfsp,
1613 const struct smb_filename *smb_fname,
1614 files_struct *fsp,
1615 int flags,
1616 mode_t mode)
1618 int fd;
1620 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
1622 if (!is_named_stream(smb_fname)) {
1623 return SMB_VFS_NEXT_OPENAT(handle,
1624 dirfsp,
1625 smb_fname,
1626 fsp,
1627 flags,
1628 mode);
1631 if (is_afpinfo_stream(smb_fname->stream_name)) {
1632 fd = fruit_open_meta(handle,
1633 dirfsp,
1634 smb_fname,
1635 fsp,
1636 flags,
1637 mode);
1638 } else if (is_afpresource_stream(smb_fname->stream_name)) {
1639 fd = fruit_open_rsrc(handle,
1640 dirfsp,
1641 smb_fname,
1642 fsp,
1643 flags,
1644 mode);
1645 } else {
1646 fd = SMB_VFS_NEXT_OPENAT(handle,
1647 dirfsp,
1648 smb_fname,
1649 fsp,
1650 flags,
1651 mode);
1654 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
1656 /* Prevent reopen optimisation */
1657 fsp->fsp_flags.have_proc_fds = false;
1658 return fd;
1661 static int fruit_close_meta(vfs_handle_struct *handle,
1662 files_struct *fsp)
1664 int ret;
1665 struct fruit_config_data *config = NULL;
1667 SMB_VFS_HANDLE_GET_DATA(handle, config,
1668 struct fruit_config_data, return -1);
1670 switch (config->meta) {
1671 case FRUIT_META_STREAM:
1672 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
1673 break;
1675 case FRUIT_META_NETATALK:
1676 ret = vfs_fake_fd_close(fsp_get_pathref_fd(fsp));
1677 fsp_set_fd(fsp, -1);
1678 break;
1680 default:
1681 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
1682 return -1;
1685 return ret;
1689 static int fruit_close_rsrc(vfs_handle_struct *handle,
1690 files_struct *fsp)
1692 int ret;
1693 struct fruit_config_data *config = NULL;
1695 SMB_VFS_HANDLE_GET_DATA(handle, config,
1696 struct fruit_config_data, return -1);
1698 switch (config->rsrc) {
1699 case FRUIT_RSRC_STREAM:
1700 case FRUIT_RSRC_ADFILE:
1701 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
1702 break;
1704 case FRUIT_RSRC_XATTR:
1705 ret = vfs_fake_fd_close(fsp_get_pathref_fd(fsp));
1706 fsp_set_fd(fsp, -1);
1707 break;
1709 default:
1710 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
1711 return -1;
1714 return ret;
1717 static int fruit_close(vfs_handle_struct *handle,
1718 files_struct *fsp)
1720 int ret;
1721 int fd;
1723 fd = fsp_get_pathref_fd(fsp);
1725 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(fsp->fsp_name), fd);
1727 if (!is_named_stream(fsp->fsp_name)) {
1728 return SMB_VFS_NEXT_CLOSE(handle, fsp);
1731 if (is_afpinfo_stream(fsp->fsp_name->stream_name)) {
1732 ret = fruit_close_meta(handle, fsp);
1733 } else if (is_afpresource_stream(fsp->fsp_name->stream_name)) {
1734 ret = fruit_close_rsrc(handle, fsp);
1735 } else {
1736 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
1739 return ret;
1742 static int fruit_renameat(struct vfs_handle_struct *handle,
1743 files_struct *srcfsp,
1744 const struct smb_filename *smb_fname_src,
1745 files_struct *dstfsp,
1746 const struct smb_filename *smb_fname_dst)
1748 int rc = -1;
1749 struct fruit_config_data *config = NULL;
1750 struct smb_filename *src_adp_smb_fname = NULL;
1751 struct smb_filename *dst_adp_smb_fname = NULL;
1753 SMB_VFS_HANDLE_GET_DATA(handle, config,
1754 struct fruit_config_data, return -1);
1756 if (!VALID_STAT(smb_fname_src->st)) {
1757 DBG_ERR("Need valid stat for [%s]\n",
1758 smb_fname_str_dbg(smb_fname_src));
1759 return -1;
1762 rc = SMB_VFS_NEXT_RENAMEAT(handle,
1763 srcfsp,
1764 smb_fname_src,
1765 dstfsp,
1766 smb_fname_dst);
1767 if (rc != 0) {
1768 return -1;
1771 if ((config->rsrc != FRUIT_RSRC_ADFILE) ||
1772 (!S_ISREG(smb_fname_src->st.st_ex_mode)))
1774 return 0;
1777 rc = adouble_path(talloc_tos(), smb_fname_src, &src_adp_smb_fname);
1778 if (rc != 0) {
1779 goto done;
1782 rc = adouble_path(talloc_tos(), smb_fname_dst, &dst_adp_smb_fname);
1783 if (rc != 0) {
1784 goto done;
1787 DBG_DEBUG("%s -> %s\n",
1788 smb_fname_str_dbg(src_adp_smb_fname),
1789 smb_fname_str_dbg(dst_adp_smb_fname));
1791 rc = SMB_VFS_NEXT_RENAMEAT(handle,
1792 srcfsp,
1793 src_adp_smb_fname,
1794 dstfsp,
1795 dst_adp_smb_fname);
1796 if (errno == ENOENT) {
1797 rc = 0;
1800 done:
1801 TALLOC_FREE(src_adp_smb_fname);
1802 TALLOC_FREE(dst_adp_smb_fname);
1803 return rc;
1806 static int fruit_unlink_meta_stream(vfs_handle_struct *handle,
1807 struct files_struct *dirfsp,
1808 const struct smb_filename *smb_fname)
1810 return SMB_VFS_NEXT_UNLINKAT(handle,
1811 dirfsp,
1812 smb_fname,
1816 static int fruit_unlink_meta_netatalk(vfs_handle_struct *handle,
1817 const struct smb_filename *smb_fname)
1819 return SMB_VFS_REMOVEXATTR(handle->conn,
1820 smb_fname,
1821 AFPINFO_EA_NETATALK);
1824 static int fruit_unlink_meta(vfs_handle_struct *handle,
1825 struct files_struct *dirfsp,
1826 const struct smb_filename *smb_fname)
1828 struct fruit_config_data *config = NULL;
1829 int rc;
1831 SMB_VFS_HANDLE_GET_DATA(handle, config,
1832 struct fruit_config_data, return -1);
1834 switch (config->meta) {
1835 case FRUIT_META_STREAM:
1836 rc = fruit_unlink_meta_stream(handle,
1837 dirfsp,
1838 smb_fname);
1839 break;
1841 case FRUIT_META_NETATALK:
1842 rc = fruit_unlink_meta_netatalk(handle, smb_fname);
1843 break;
1845 default:
1846 DBG_ERR("Unsupported meta config [%d]\n", config->meta);
1847 return -1;
1850 return rc;
1853 static int fruit_unlink_rsrc_stream(vfs_handle_struct *handle,
1854 struct files_struct *dirfsp,
1855 const struct smb_filename *smb_fname,
1856 bool force_unlink)
1858 int ret;
1860 if (!force_unlink) {
1861 struct smb_filename *smb_fname_cp = NULL;
1862 off_t size;
1864 smb_fname_cp = cp_smb_filename(talloc_tos(), smb_fname);
1865 if (smb_fname_cp == NULL) {
1866 return -1;
1870 * 0 byte resource fork streams are not listed by
1871 * vfs_streaminfo, as a result stream cleanup/deletion of file
1872 * deletion doesn't remove the resourcefork stream.
1875 ret = SMB_VFS_NEXT_STAT(handle, smb_fname_cp);
1876 if (ret != 0) {
1877 TALLOC_FREE(smb_fname_cp);
1878 DBG_ERR("stat [%s] failed [%s]\n",
1879 smb_fname_str_dbg(smb_fname_cp), strerror(errno));
1880 return -1;
1883 size = smb_fname_cp->st.st_ex_size;
1884 TALLOC_FREE(smb_fname_cp);
1886 if (size > 0) {
1887 /* OS X ignores resource fork stream delete requests */
1888 return 0;
1892 ret = SMB_VFS_NEXT_UNLINKAT(handle,
1893 dirfsp,
1894 smb_fname,
1896 if ((ret != 0) && (errno == ENOENT) && force_unlink) {
1897 ret = 0;
1900 return ret;
1903 static int fruit_unlink_rsrc_adouble(vfs_handle_struct *handle,
1904 struct files_struct *dirfsp,
1905 const struct smb_filename *smb_fname,
1906 bool force_unlink)
1908 int rc;
1909 struct adouble *ad = NULL;
1910 struct smb_filename *adp_smb_fname = NULL;
1912 if (!force_unlink) {
1913 ad = ad_get(talloc_tos(), handle, smb_fname,
1914 ADOUBLE_RSRC);
1915 if (ad == NULL) {
1916 errno = ENOENT;
1917 return -1;
1922 * 0 byte resource fork streams are not listed by
1923 * vfs_streaminfo, as a result stream cleanup/deletion of file
1924 * deletion doesn't remove the resourcefork stream.
1927 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
1928 /* OS X ignores resource fork stream delete requests */
1929 TALLOC_FREE(ad);
1930 return 0;
1933 TALLOC_FREE(ad);
1936 rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
1937 if (rc != 0) {
1938 return -1;
1941 rc = SMB_VFS_NEXT_UNLINKAT(handle,
1942 dirfsp,
1943 adp_smb_fname,
1945 TALLOC_FREE(adp_smb_fname);
1946 if ((rc != 0) && (errno == ENOENT) && force_unlink) {
1947 rc = 0;
1950 return rc;
1953 static int fruit_unlink_rsrc_xattr(vfs_handle_struct *handle,
1954 const struct smb_filename *smb_fname,
1955 bool force_unlink)
1958 * OS X ignores resource fork stream delete requests, so nothing to do
1959 * here. Removing the file will remove the xattr anyway, so we don't
1960 * have to take care of removing 0 byte resource forks that could be
1961 * left behind.
1963 return 0;
1966 static int fruit_unlink_rsrc(vfs_handle_struct *handle,
1967 struct files_struct *dirfsp,
1968 const struct smb_filename *smb_fname,
1969 bool force_unlink)
1971 struct fruit_config_data *config = NULL;
1972 int rc;
1974 SMB_VFS_HANDLE_GET_DATA(handle, config,
1975 struct fruit_config_data, return -1);
1977 switch (config->rsrc) {
1978 case FRUIT_RSRC_STREAM:
1979 rc = fruit_unlink_rsrc_stream(handle,
1980 dirfsp,
1981 smb_fname,
1982 force_unlink);
1983 break;
1985 case FRUIT_RSRC_ADFILE:
1986 rc = fruit_unlink_rsrc_adouble(handle,
1987 dirfsp,
1988 smb_fname,
1989 force_unlink);
1990 break;
1992 case FRUIT_RSRC_XATTR:
1993 rc = fruit_unlink_rsrc_xattr(handle, smb_fname, force_unlink);
1994 break;
1996 default:
1997 DBG_ERR("Unsupported rsrc config [%d]\n", config->rsrc);
1998 return -1;
2001 return rc;
2004 static int fruit_chmod(vfs_handle_struct *handle,
2005 const struct smb_filename *smb_fname,
2006 mode_t mode)
2008 int rc = -1;
2009 struct fruit_config_data *config = NULL;
2010 struct smb_filename *smb_fname_adp = NULL;
2012 rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname, mode);
2013 if (rc != 0) {
2014 return rc;
2017 SMB_VFS_HANDLE_GET_DATA(handle, config,
2018 struct fruit_config_data, return -1);
2020 if (config->rsrc != FRUIT_RSRC_ADFILE) {
2021 return 0;
2024 if (!VALID_STAT(smb_fname->st)) {
2025 return 0;
2028 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
2029 return 0;
2032 rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_adp);
2033 if (rc != 0) {
2034 return -1;
2037 DEBUG(10, ("fruit_chmod: %s\n", smb_fname_adp->base_name));
2039 rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname_adp, mode);
2040 if (errno == ENOENT) {
2041 rc = 0;
2044 TALLOC_FREE(smb_fname_adp);
2045 return rc;
2048 static int fruit_unlinkat(vfs_handle_struct *handle,
2049 struct files_struct *dirfsp,
2050 const struct smb_filename *smb_fname,
2051 int flags)
2053 struct fruit_config_data *config = NULL;
2054 struct smb_filename *rsrc_smb_fname = NULL;
2055 int ret;
2057 SMB_ASSERT(dirfsp == dirfsp->conn->cwd_fsp);
2059 if (flags & AT_REMOVEDIR) {
2060 return SMB_VFS_NEXT_UNLINKAT(handle,
2061 dirfsp,
2062 smb_fname,
2063 AT_REMOVEDIR);
2066 SMB_VFS_HANDLE_GET_DATA(handle, config,
2067 struct fruit_config_data, return -1);
2069 if (is_afpinfo_stream(smb_fname->stream_name)) {
2070 return fruit_unlink_meta(handle,
2071 dirfsp,
2072 smb_fname);
2073 } else if (is_afpresource_stream(smb_fname->stream_name)) {
2074 return fruit_unlink_rsrc(handle,
2075 dirfsp,
2076 smb_fname,
2077 false);
2078 } else if (is_named_stream(smb_fname)) {
2079 return SMB_VFS_NEXT_UNLINKAT(handle,
2080 dirfsp,
2081 smb_fname,
2083 } else if (is_adouble_file(smb_fname->base_name)) {
2084 return SMB_VFS_NEXT_UNLINKAT(handle,
2085 dirfsp,
2086 smb_fname,
2091 * A request to delete the base file. Because 0 byte resource
2092 * fork streams are not listed by fruit_streaminfo,
2093 * delete_all_streams() can't remove 0 byte resource fork
2094 * streams, so we have to cleanup this here.
2096 rsrc_smb_fname = synthetic_smb_fname(talloc_tos(),
2097 smb_fname->base_name,
2098 AFPRESOURCE_STREAM_NAME,
2099 NULL,
2100 smb_fname->twrp,
2101 smb_fname->flags);
2102 if (rsrc_smb_fname == NULL) {
2103 return -1;
2106 ret = fruit_unlink_rsrc(handle, dirfsp, rsrc_smb_fname, true);
2107 if ((ret != 0) && (errno != ENOENT)) {
2108 DBG_ERR("Forced unlink of [%s] failed [%s]\n",
2109 smb_fname_str_dbg(rsrc_smb_fname), strerror(errno));
2110 TALLOC_FREE(rsrc_smb_fname);
2111 return -1;
2113 TALLOC_FREE(rsrc_smb_fname);
2115 return SMB_VFS_NEXT_UNLINKAT(handle,
2116 dirfsp,
2117 smb_fname,
2121 static ssize_t fruit_pread_meta_stream(vfs_handle_struct *handle,
2122 files_struct *fsp, void *data,
2123 size_t n, off_t offset)
2125 ssize_t nread;
2126 int ret;
2128 nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
2129 if (nread == -1 || nread == n) {
2130 return nread;
2133 DBG_ERR("Removing [%s] after short read [%zd]\n",
2134 fsp_str_dbg(fsp), nread);
2136 ret = SMB_VFS_NEXT_UNLINKAT(handle,
2137 fsp->conn->cwd_fsp,
2138 fsp->fsp_name,
2140 if (ret != 0) {
2141 DBG_ERR("Removing [%s] failed\n", fsp_str_dbg(fsp));
2142 return -1;
2145 errno = EINVAL;
2146 return -1;
2149 static ssize_t fruit_pread_meta_adouble(vfs_handle_struct *handle,
2150 files_struct *fsp, void *data,
2151 size_t n, off_t offset)
2153 AfpInfo *ai = NULL;
2154 struct adouble *ad = NULL;
2155 char afpinfo_buf[AFP_INFO_SIZE];
2156 char *p = NULL;
2157 ssize_t nread;
2159 ai = afpinfo_new(talloc_tos());
2160 if (ai == NULL) {
2161 return -1;
2164 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
2165 if (ad == NULL) {
2166 nread = -1;
2167 goto fail;
2170 p = ad_get_entry(ad, ADEID_FINDERI);
2171 if (p == NULL) {
2172 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
2173 nread = -1;
2174 goto fail;
2177 memcpy(&ai->afpi_FinderInfo[0], p, ADEDLEN_FINDERI);
2179 nread = afpinfo_pack(ai, afpinfo_buf);
2180 if (nread != AFP_INFO_SIZE) {
2181 nread = -1;
2182 goto fail;
2185 memcpy(data, afpinfo_buf, n);
2186 nread = n;
2188 fail:
2189 TALLOC_FREE(ai);
2190 return nread;
2193 static ssize_t fruit_pread_meta(vfs_handle_struct *handle,
2194 files_struct *fsp, void *data,
2195 size_t n, off_t offset)
2197 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
2198 ssize_t nread;
2199 ssize_t to_return;
2202 * OS X has a off-by-1 error in the offset calculation, so we're
2203 * bug compatible here. It won't hurt, as any relevant real
2204 * world read requests from the AFP_AfpInfo stream will be
2205 * offset=0 n=60. offset is ignored anyway, see below.
2207 if ((offset < 0) || (offset >= AFP_INFO_SIZE + 1)) {
2208 return 0;
2211 if (fio == NULL) {
2212 DBG_ERR("Failed to fetch fsp extension");
2213 return -1;
2216 /* Yes, macOS always reads from offset 0 */
2217 offset = 0;
2218 to_return = MIN(n, AFP_INFO_SIZE);
2220 switch (fio->config->meta) {
2221 case FRUIT_META_STREAM:
2222 nread = fruit_pread_meta_stream(handle, fsp, data,
2223 to_return, offset);
2224 break;
2226 case FRUIT_META_NETATALK:
2227 nread = fruit_pread_meta_adouble(handle, fsp, data,
2228 to_return, offset);
2229 break;
2231 default:
2232 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
2233 return -1;
2236 if (nread == -1 && fio->fake_fd) {
2237 AfpInfo *ai = NULL;
2238 char afpinfo_buf[AFP_INFO_SIZE];
2240 ai = afpinfo_new(talloc_tos());
2241 if (ai == NULL) {
2242 return -1;
2245 nread = afpinfo_pack(ai, afpinfo_buf);
2246 TALLOC_FREE(ai);
2247 if (nread != AFP_INFO_SIZE) {
2248 return -1;
2251 memcpy(data, afpinfo_buf, to_return);
2252 return to_return;
2255 return nread;
2258 static ssize_t fruit_pread_rsrc_stream(vfs_handle_struct *handle,
2259 files_struct *fsp, void *data,
2260 size_t n, off_t offset)
2262 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
2265 static ssize_t fruit_pread_rsrc_xattr(vfs_handle_struct *handle,
2266 files_struct *fsp, void *data,
2267 size_t n, off_t offset)
2269 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
2272 static ssize_t fruit_pread_rsrc_adouble(vfs_handle_struct *handle,
2273 files_struct *fsp, void *data,
2274 size_t n, off_t offset)
2276 struct adouble *ad = NULL;
2277 ssize_t nread;
2279 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
2280 if (ad == NULL) {
2281 return -1;
2284 nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n,
2285 offset + ad_getentryoff(ad, ADEID_RFORK));
2287 TALLOC_FREE(ad);
2288 return nread;
2291 static ssize_t fruit_pread_rsrc(vfs_handle_struct *handle,
2292 files_struct *fsp, void *data,
2293 size_t n, off_t offset)
2295 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
2296 ssize_t nread;
2298 if (fio == NULL) {
2299 errno = EINVAL;
2300 return -1;
2303 switch (fio->config->rsrc) {
2304 case FRUIT_RSRC_STREAM:
2305 nread = fruit_pread_rsrc_stream(handle, fsp, data, n, offset);
2306 break;
2308 case FRUIT_RSRC_ADFILE:
2309 nread = fruit_pread_rsrc_adouble(handle, fsp, data, n, offset);
2310 break;
2312 case FRUIT_RSRC_XATTR:
2313 nread = fruit_pread_rsrc_xattr(handle, fsp, data, n, offset);
2314 break;
2316 default:
2317 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
2318 return -1;
2321 return nread;
2324 static ssize_t fruit_pread(vfs_handle_struct *handle,
2325 files_struct *fsp, void *data,
2326 size_t n, off_t offset)
2328 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
2329 ssize_t nread;
2331 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
2332 fsp_str_dbg(fsp), (intmax_t)offset, n);
2334 if (fio == NULL) {
2335 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
2338 if (fio->type == ADOUBLE_META) {
2339 nread = fruit_pread_meta(handle, fsp, data, n, offset);
2340 } else {
2341 nread = fruit_pread_rsrc(handle, fsp, data, n, offset);
2344 DBG_DEBUG("Path [%s] nread [%zd]\n", fsp_str_dbg(fsp), nread);
2345 return nread;
2348 static bool fruit_must_handle_aio_stream(struct fio *fio)
2350 if (fio == NULL) {
2351 return false;
2354 if (fio->type == ADOUBLE_META) {
2355 return true;
2358 if ((fio->type == ADOUBLE_RSRC) &&
2359 (fio->config->rsrc == FRUIT_RSRC_ADFILE))
2361 return true;
2364 return false;
2367 struct fruit_pread_state {
2368 ssize_t nread;
2369 struct vfs_aio_state vfs_aio_state;
2372 static void fruit_pread_done(struct tevent_req *subreq);
2374 static struct tevent_req *fruit_pread_send(
2375 struct vfs_handle_struct *handle,
2376 TALLOC_CTX *mem_ctx,
2377 struct tevent_context *ev,
2378 struct files_struct *fsp,
2379 void *data,
2380 size_t n, off_t offset)
2382 struct tevent_req *req = NULL;
2383 struct tevent_req *subreq = NULL;
2384 struct fruit_pread_state *state = NULL;
2385 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
2387 req = tevent_req_create(mem_ctx, &state,
2388 struct fruit_pread_state);
2389 if (req == NULL) {
2390 return NULL;
2393 if (fruit_must_handle_aio_stream(fio)) {
2394 state->nread = SMB_VFS_PREAD(fsp, data, n, offset);
2395 if (state->nread != n) {
2396 if (state->nread != -1) {
2397 errno = EIO;
2399 tevent_req_error(req, errno);
2400 return tevent_req_post(req, ev);
2402 tevent_req_done(req);
2403 return tevent_req_post(req, ev);
2406 subreq = SMB_VFS_NEXT_PREAD_SEND(state, ev, handle, fsp,
2407 data, n, offset);
2408 if (tevent_req_nomem(req, subreq)) {
2409 return tevent_req_post(req, ev);
2411 tevent_req_set_callback(subreq, fruit_pread_done, req);
2412 return req;
2415 static void fruit_pread_done(struct tevent_req *subreq)
2417 struct tevent_req *req = tevent_req_callback_data(
2418 subreq, struct tevent_req);
2419 struct fruit_pread_state *state = tevent_req_data(
2420 req, struct fruit_pread_state);
2422 state->nread = SMB_VFS_PREAD_RECV(subreq, &state->vfs_aio_state);
2423 TALLOC_FREE(subreq);
2425 if (tevent_req_error(req, state->vfs_aio_state.error)) {
2426 return;
2428 tevent_req_done(req);
2431 static ssize_t fruit_pread_recv(struct tevent_req *req,
2432 struct vfs_aio_state *vfs_aio_state)
2434 struct fruit_pread_state *state = tevent_req_data(
2435 req, struct fruit_pread_state);
2437 if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
2438 return -1;
2441 *vfs_aio_state = state->vfs_aio_state;
2442 return state->nread;
2445 static ssize_t fruit_pwrite_meta_stream(vfs_handle_struct *handle,
2446 files_struct *fsp, const void *data,
2447 size_t n, off_t offset)
2449 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
2450 AfpInfo *ai = NULL;
2451 size_t nwritten;
2452 int ret;
2453 bool ok;
2455 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
2456 fsp_str_dbg(fsp), (intmax_t)offset, n);
2458 if (fio == NULL) {
2459 return -1;
2462 if (fio->fake_fd) {
2463 int fd;
2465 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
2466 fsp_set_fd(fsp, -1);
2467 if (ret != 0) {
2468 DBG_ERR("Close [%s] failed: %s\n",
2469 fsp_str_dbg(fsp), strerror(errno));
2470 return -1;
2473 fd = SMB_VFS_NEXT_OPENAT(handle,
2474 fsp->conn->cwd_fsp,
2475 fsp->fsp_name,
2476 fsp,
2477 fio->flags,
2478 fio->mode);
2479 if (fd == -1) {
2480 DBG_ERR("On-demand create [%s] in write failed: %s\n",
2481 fsp_str_dbg(fsp), strerror(errno));
2482 return -1;
2484 fsp_set_fd(fsp, fd);
2485 fio->fake_fd = false;
2488 ai = afpinfo_unpack(talloc_tos(), data);
2489 if (ai == NULL) {
2490 return -1;
2493 if (ai_empty_finderinfo(ai)) {
2495 * Writing an all 0 blob to the metadata stream results in the
2496 * stream being removed on a macOS server. This ensures we
2497 * behave the same and it verified by the "delete AFP_AfpInfo by
2498 * writing all 0" test.
2500 ret = SMB_VFS_NEXT_FTRUNCATE(handle, fsp, 0);
2501 if (ret != 0) {
2502 DBG_ERR("SMB_VFS_NEXT_FTRUNCATE on [%s] failed\n",
2503 fsp_str_dbg(fsp));
2504 return -1;
2507 ok = set_delete_on_close(
2508 fsp,
2509 true,
2510 handle->conn->session_info->security_token,
2511 handle->conn->session_info->unix_token);
2512 if (!ok) {
2513 DBG_ERR("set_delete_on_close on [%s] failed\n",
2514 fsp_str_dbg(fsp));
2515 return -1;
2517 return n;
2520 nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
2521 if (nwritten != n) {
2522 return -1;
2525 return n;
2528 static ssize_t fruit_pwrite_meta_netatalk(vfs_handle_struct *handle,
2529 files_struct *fsp, const void *data,
2530 size_t n, off_t offset)
2532 struct adouble *ad = NULL;
2533 AfpInfo *ai = NULL;
2534 char *p = NULL;
2535 int ret;
2536 bool ok;
2538 ai = afpinfo_unpack(talloc_tos(), data);
2539 if (ai == NULL) {
2540 return -1;
2543 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
2544 if (ad == NULL) {
2545 ad = ad_init(talloc_tos(), ADOUBLE_META);
2546 if (ad == NULL) {
2547 return -1;
2550 p = ad_get_entry(ad, ADEID_FINDERI);
2551 if (p == NULL) {
2552 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
2553 TALLOC_FREE(ad);
2554 return -1;
2557 memcpy(p, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
2559 ret = ad_fset(handle, ad, fsp);
2560 if (ret != 0) {
2561 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
2562 TALLOC_FREE(ad);
2563 return -1;
2566 TALLOC_FREE(ad);
2568 if (!ai_empty_finderinfo(ai)) {
2569 return n;
2573 * Writing an all 0 blob to the metadata stream results in the stream
2574 * being removed on a macOS server. This ensures we behave the same and
2575 * it verified by the "delete AFP_AfpInfo by writing all 0" test.
2578 ok = set_delete_on_close(
2579 fsp,
2580 true,
2581 handle->conn->session_info->security_token,
2582 handle->conn->session_info->unix_token);
2583 if (!ok) {
2584 DBG_ERR("set_delete_on_close on [%s] failed\n",
2585 fsp_str_dbg(fsp));
2586 return -1;
2589 return n;
2592 static ssize_t fruit_pwrite_meta(vfs_handle_struct *handle,
2593 files_struct *fsp, const void *data,
2594 size_t n, off_t offset)
2596 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
2597 ssize_t nwritten;
2598 uint8_t buf[AFP_INFO_SIZE];
2599 size_t to_write;
2600 size_t to_copy;
2601 int cmp;
2603 if (fio == NULL) {
2604 DBG_ERR("Failed to fetch fsp extension");
2605 return -1;
2608 if (n < 3) {
2609 errno = EINVAL;
2610 return -1;
2613 if (offset != 0 && n < 60) {
2614 errno = EINVAL;
2615 return -1;
2618 cmp = memcmp(data, "AFP", 3);
2619 if (cmp != 0) {
2620 errno = EINVAL;
2621 return -1;
2624 if (n <= AFP_OFF_FinderInfo) {
2626 * Nothing to do here really, just return
2628 return n;
2631 offset = 0;
2633 to_copy = n;
2634 if (to_copy > AFP_INFO_SIZE) {
2635 to_copy = AFP_INFO_SIZE;
2637 memcpy(buf, data, to_copy);
2639 to_write = n;
2640 if (to_write != AFP_INFO_SIZE) {
2641 to_write = AFP_INFO_SIZE;
2644 switch (fio->config->meta) {
2645 case FRUIT_META_STREAM:
2646 nwritten = fruit_pwrite_meta_stream(handle,
2647 fsp,
2648 buf,
2649 to_write,
2650 offset);
2651 break;
2653 case FRUIT_META_NETATALK:
2654 nwritten = fruit_pwrite_meta_netatalk(handle,
2655 fsp,
2656 buf,
2657 to_write,
2658 offset);
2659 break;
2661 default:
2662 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
2663 return -1;
2666 if (nwritten != to_write) {
2667 return -1;
2671 * Return the requested amount, verified against macOS SMB server
2673 return n;
2676 static ssize_t fruit_pwrite_rsrc_stream(vfs_handle_struct *handle,
2677 files_struct *fsp, const void *data,
2678 size_t n, off_t offset)
2680 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
2683 static ssize_t fruit_pwrite_rsrc_xattr(vfs_handle_struct *handle,
2684 files_struct *fsp, const void *data,
2685 size_t n, off_t offset)
2687 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
2690 static ssize_t fruit_pwrite_rsrc_adouble(vfs_handle_struct *handle,
2691 files_struct *fsp, const void *data,
2692 size_t n, off_t offset)
2694 struct adouble *ad = NULL;
2695 ssize_t nwritten;
2696 int ret;
2698 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
2699 if (ad == NULL) {
2700 DBG_ERR("ad_get [%s] failed\n", fsp_str_dbg(fsp));
2701 return -1;
2704 nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n,
2705 offset + ad_getentryoff(ad, ADEID_RFORK));
2706 if (nwritten != n) {
2707 DBG_ERR("Short write on [%s] [%zd/%zd]\n",
2708 fsp_str_dbg(fsp), nwritten, n);
2709 TALLOC_FREE(ad);
2710 return -1;
2713 if ((n + offset) > ad_getentrylen(ad, ADEID_RFORK)) {
2714 ad_setentrylen(ad, ADEID_RFORK, n + offset);
2715 ret = ad_fset(handle, ad, fsp);
2716 if (ret != 0) {
2717 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
2718 TALLOC_FREE(ad);
2719 return -1;
2723 TALLOC_FREE(ad);
2724 return n;
2727 static ssize_t fruit_pwrite_rsrc(vfs_handle_struct *handle,
2728 files_struct *fsp, const void *data,
2729 size_t n, off_t offset)
2731 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
2732 ssize_t nwritten;
2734 if (fio == NULL) {
2735 DBG_ERR("Failed to fetch fsp extension");
2736 return -1;
2739 switch (fio->config->rsrc) {
2740 case FRUIT_RSRC_STREAM:
2741 nwritten = fruit_pwrite_rsrc_stream(handle, fsp, data, n, offset);
2742 break;
2744 case FRUIT_RSRC_ADFILE:
2745 nwritten = fruit_pwrite_rsrc_adouble(handle, fsp, data, n, offset);
2746 break;
2748 case FRUIT_RSRC_XATTR:
2749 nwritten = fruit_pwrite_rsrc_xattr(handle, fsp, data, n, offset);
2750 break;
2752 default:
2753 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
2754 return -1;
2757 return nwritten;
2760 static ssize_t fruit_pwrite(vfs_handle_struct *handle,
2761 files_struct *fsp, const void *data,
2762 size_t n, off_t offset)
2764 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
2765 ssize_t nwritten;
2767 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
2768 fsp_str_dbg(fsp), (intmax_t)offset, n);
2770 if (fio == NULL) {
2771 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
2774 if (fio->type == ADOUBLE_META) {
2775 nwritten = fruit_pwrite_meta(handle, fsp, data, n, offset);
2776 } else {
2777 nwritten = fruit_pwrite_rsrc(handle, fsp, data, n, offset);
2780 DBG_DEBUG("Path [%s] nwritten=%zd\n", fsp_str_dbg(fsp), nwritten);
2781 return nwritten;
2784 struct fruit_pwrite_state {
2785 ssize_t nwritten;
2786 struct vfs_aio_state vfs_aio_state;
2789 static void fruit_pwrite_done(struct tevent_req *subreq);
2791 static struct tevent_req *fruit_pwrite_send(
2792 struct vfs_handle_struct *handle,
2793 TALLOC_CTX *mem_ctx,
2794 struct tevent_context *ev,
2795 struct files_struct *fsp,
2796 const void *data,
2797 size_t n, off_t offset)
2799 struct tevent_req *req = NULL;
2800 struct tevent_req *subreq = NULL;
2801 struct fruit_pwrite_state *state = NULL;
2802 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
2804 req = tevent_req_create(mem_ctx, &state,
2805 struct fruit_pwrite_state);
2806 if (req == NULL) {
2807 return NULL;
2810 if (fruit_must_handle_aio_stream(fio)) {
2811 state->nwritten = SMB_VFS_PWRITE(fsp, data, n, offset);
2812 if (state->nwritten != n) {
2813 if (state->nwritten != -1) {
2814 errno = EIO;
2816 tevent_req_error(req, errno);
2817 return tevent_req_post(req, ev);
2819 tevent_req_done(req);
2820 return tevent_req_post(req, ev);
2823 subreq = SMB_VFS_NEXT_PWRITE_SEND(state, ev, handle, fsp,
2824 data, n, offset);
2825 if (tevent_req_nomem(req, subreq)) {
2826 return tevent_req_post(req, ev);
2828 tevent_req_set_callback(subreq, fruit_pwrite_done, req);
2829 return req;
2832 static void fruit_pwrite_done(struct tevent_req *subreq)
2834 struct tevent_req *req = tevent_req_callback_data(
2835 subreq, struct tevent_req);
2836 struct fruit_pwrite_state *state = tevent_req_data(
2837 req, struct fruit_pwrite_state);
2839 state->nwritten = SMB_VFS_PWRITE_RECV(subreq, &state->vfs_aio_state);
2840 TALLOC_FREE(subreq);
2842 if (tevent_req_error(req, state->vfs_aio_state.error)) {
2843 return;
2845 tevent_req_done(req);
2848 static ssize_t fruit_pwrite_recv(struct tevent_req *req,
2849 struct vfs_aio_state *vfs_aio_state)
2851 struct fruit_pwrite_state *state = tevent_req_data(
2852 req, struct fruit_pwrite_state);
2854 if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
2855 return -1;
2858 *vfs_aio_state = state->vfs_aio_state;
2859 return state->nwritten;
2863 * Helper to stat/lstat the base file of an smb_fname.
2865 static int fruit_stat_base(vfs_handle_struct *handle,
2866 struct smb_filename *smb_fname,
2867 bool follow_links)
2869 char *tmp_stream_name;
2870 int rc;
2872 tmp_stream_name = smb_fname->stream_name;
2873 smb_fname->stream_name = NULL;
2874 if (follow_links) {
2875 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
2876 } else {
2877 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
2879 smb_fname->stream_name = tmp_stream_name;
2881 DBG_DEBUG("fruit_stat_base [%s] dev [%ju] ino [%ju]\n",
2882 smb_fname->base_name,
2883 (uintmax_t)smb_fname->st.st_ex_dev,
2884 (uintmax_t)smb_fname->st.st_ex_ino);
2885 return rc;
2888 static int fruit_stat_meta_stream(vfs_handle_struct *handle,
2889 struct smb_filename *smb_fname,
2890 bool follow_links)
2892 int ret;
2893 ino_t ino;
2895 ret = fruit_stat_base(handle, smb_fname, false);
2896 if (ret != 0) {
2897 return -1;
2900 ino = hash_inode(&smb_fname->st, smb_fname->stream_name);
2902 if (follow_links) {
2903 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
2904 } else {
2905 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
2908 smb_fname->st.st_ex_ino = ino;
2910 return ret;
2913 static int fruit_stat_meta_netatalk(vfs_handle_struct *handle,
2914 struct smb_filename *smb_fname,
2915 bool follow_links)
2917 struct adouble *ad = NULL;
2919 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
2920 if (ad == NULL) {
2921 DBG_INFO("fruit_stat_meta %s: %s\n",
2922 smb_fname_str_dbg(smb_fname), strerror(errno));
2923 errno = ENOENT;
2924 return -1;
2926 TALLOC_FREE(ad);
2928 /* Populate the stat struct with info from the base file. */
2929 if (fruit_stat_base(handle, smb_fname, follow_links) == -1) {
2930 return -1;
2932 smb_fname->st.st_ex_size = AFP_INFO_SIZE;
2933 smb_fname->st.st_ex_ino = hash_inode(&smb_fname->st,
2934 smb_fname->stream_name);
2935 return 0;
2938 static int fruit_stat_meta(vfs_handle_struct *handle,
2939 struct smb_filename *smb_fname,
2940 bool follow_links)
2942 struct fruit_config_data *config = NULL;
2943 int ret;
2945 SMB_VFS_HANDLE_GET_DATA(handle, config,
2946 struct fruit_config_data, return -1);
2948 switch (config->meta) {
2949 case FRUIT_META_STREAM:
2950 ret = fruit_stat_meta_stream(handle, smb_fname, follow_links);
2951 break;
2953 case FRUIT_META_NETATALK:
2954 ret = fruit_stat_meta_netatalk(handle, smb_fname, follow_links);
2955 break;
2957 default:
2958 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
2959 return -1;
2962 return ret;
2965 static int fruit_stat_rsrc_netatalk(vfs_handle_struct *handle,
2966 struct smb_filename *smb_fname,
2967 bool follow_links)
2969 struct adouble *ad = NULL;
2970 int ret;
2972 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
2973 if (ad == NULL) {
2974 errno = ENOENT;
2975 return -1;
2978 /* Populate the stat struct with info from the base file. */
2979 ret = fruit_stat_base(handle, smb_fname, follow_links);
2980 if (ret != 0) {
2981 TALLOC_FREE(ad);
2982 return -1;
2985 smb_fname->st.st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
2986 smb_fname->st.st_ex_ino = hash_inode(&smb_fname->st,
2987 smb_fname->stream_name);
2988 TALLOC_FREE(ad);
2989 return 0;
2992 static int fruit_stat_rsrc_stream(vfs_handle_struct *handle,
2993 struct smb_filename *smb_fname,
2994 bool follow_links)
2996 int ret;
2998 if (follow_links) {
2999 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
3000 } else {
3001 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
3004 return ret;
3007 static int fruit_stat_rsrc_xattr(vfs_handle_struct *handle,
3008 struct smb_filename *smb_fname,
3009 bool follow_links)
3011 #ifdef HAVE_ATTROPEN
3012 int ret;
3013 int fd = -1;
3015 /* Populate the stat struct with info from the base file. */
3016 ret = fruit_stat_base(handle, smb_fname, follow_links);
3017 if (ret != 0) {
3018 return -1;
3021 fd = attropen(smb_fname->base_name,
3022 AFPRESOURCE_EA_NETATALK,
3023 O_RDONLY);
3024 if (fd == -1) {
3025 return 0;
3028 ret = sys_fstat(fd, &smb_fname->st, false);
3029 if (ret != 0) {
3030 close(fd);
3031 DBG_ERR("fstat [%s:%s] failed\n", smb_fname->base_name,
3032 AFPRESOURCE_EA_NETATALK);
3033 return -1;
3035 close(fd);
3036 fd = -1;
3038 smb_fname->st.st_ex_ino = hash_inode(&smb_fname->st,
3039 smb_fname->stream_name);
3041 return ret;
3043 #else
3044 errno = ENOSYS;
3045 return -1;
3046 #endif
3049 static int fruit_stat_rsrc(vfs_handle_struct *handle,
3050 struct smb_filename *smb_fname,
3051 bool follow_links)
3053 struct fruit_config_data *config = NULL;
3054 int ret;
3056 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3058 SMB_VFS_HANDLE_GET_DATA(handle, config,
3059 struct fruit_config_data, return -1);
3061 switch (config->rsrc) {
3062 case FRUIT_RSRC_STREAM:
3063 ret = fruit_stat_rsrc_stream(handle, smb_fname, follow_links);
3064 break;
3066 case FRUIT_RSRC_XATTR:
3067 ret = fruit_stat_rsrc_xattr(handle, smb_fname, follow_links);
3068 break;
3070 case FRUIT_RSRC_ADFILE:
3071 ret = fruit_stat_rsrc_netatalk(handle, smb_fname, follow_links);
3072 break;
3074 default:
3075 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3076 return -1;
3079 return ret;
3082 static int fruit_stat(vfs_handle_struct *handle,
3083 struct smb_filename *smb_fname)
3085 int rc = -1;
3087 DEBUG(10, ("fruit_stat called for %s\n",
3088 smb_fname_str_dbg(smb_fname)));
3090 if (!is_named_stream(smb_fname)) {
3091 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
3092 if (rc == 0) {
3093 update_btime(handle, smb_fname);
3095 return rc;
3099 * Note if lp_posix_paths() is true, we can never
3100 * get here as is_ntfs_stream_smb_fname() is
3101 * always false. So we never need worry about
3102 * not following links here.
3105 if (is_afpinfo_stream(smb_fname->stream_name)) {
3106 rc = fruit_stat_meta(handle, smb_fname, true);
3107 } else if (is_afpresource_stream(smb_fname->stream_name)) {
3108 rc = fruit_stat_rsrc(handle, smb_fname, true);
3109 } else {
3110 return SMB_VFS_NEXT_STAT(handle, smb_fname);
3113 if (rc == 0) {
3114 update_btime(handle, smb_fname);
3115 smb_fname->st.st_ex_mode &= ~S_IFMT;
3116 smb_fname->st.st_ex_mode |= S_IFREG;
3117 smb_fname->st.st_ex_blocks =
3118 smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
3120 return rc;
3123 static int fruit_lstat(vfs_handle_struct *handle,
3124 struct smb_filename *smb_fname)
3126 int rc = -1;
3128 DEBUG(10, ("fruit_lstat called for %s\n",
3129 smb_fname_str_dbg(smb_fname)));
3131 if (!is_named_stream(smb_fname)) {
3132 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
3133 if (rc == 0) {
3134 update_btime(handle, smb_fname);
3136 return rc;
3139 if (is_afpinfo_stream(smb_fname->stream_name)) {
3140 rc = fruit_stat_meta(handle, smb_fname, false);
3141 } else if (is_afpresource_stream(smb_fname->stream_name)) {
3142 rc = fruit_stat_rsrc(handle, smb_fname, false);
3143 } else {
3144 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
3147 if (rc == 0) {
3148 update_btime(handle, smb_fname);
3149 smb_fname->st.st_ex_mode &= ~S_IFMT;
3150 smb_fname->st.st_ex_mode |= S_IFREG;
3151 smb_fname->st.st_ex_blocks =
3152 smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
3154 return rc;
3157 static int fruit_fstat_meta_stream(vfs_handle_struct *handle,
3158 files_struct *fsp,
3159 SMB_STRUCT_STAT *sbuf)
3161 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
3162 struct smb_filename smb_fname;
3163 ino_t ino;
3164 int ret;
3166 if (fio == NULL) {
3167 return -1;
3170 if (fio->fake_fd) {
3171 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
3172 if (ret != 0) {
3173 return -1;
3176 *sbuf = fsp->base_fsp->fsp_name->st;
3177 sbuf->st_ex_size = AFP_INFO_SIZE;
3178 sbuf->st_ex_ino = hash_inode(sbuf, fsp->fsp_name->stream_name);
3179 return 0;
3182 smb_fname = (struct smb_filename) {
3183 .base_name = fsp->fsp_name->base_name,
3184 .twrp = fsp->fsp_name->twrp,
3187 ret = fruit_stat_base(handle, &smb_fname, false);
3188 if (ret != 0) {
3189 return -1;
3191 *sbuf = smb_fname.st;
3193 ino = hash_inode(sbuf, fsp->fsp_name->stream_name);
3195 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
3196 if (ret != 0) {
3197 return -1;
3200 sbuf->st_ex_ino = ino;
3201 return 0;
3204 static int fruit_fstat_meta_netatalk(vfs_handle_struct *handle,
3205 files_struct *fsp,
3206 SMB_STRUCT_STAT *sbuf)
3208 int ret;
3210 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
3211 if (ret != 0) {
3212 return -1;
3215 *sbuf = fsp->base_fsp->fsp_name->st;
3216 sbuf->st_ex_size = AFP_INFO_SIZE;
3217 sbuf->st_ex_ino = hash_inode(sbuf, fsp->fsp_name->stream_name);
3219 return 0;
3222 static int fruit_fstat_meta(vfs_handle_struct *handle,
3223 files_struct *fsp,
3224 SMB_STRUCT_STAT *sbuf,
3225 struct fio *fio)
3227 int ret;
3229 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
3231 switch (fio->config->meta) {
3232 case FRUIT_META_STREAM:
3233 ret = fruit_fstat_meta_stream(handle, fsp, sbuf);
3234 break;
3236 case FRUIT_META_NETATALK:
3237 ret = fruit_fstat_meta_netatalk(handle, fsp, sbuf);
3238 break;
3240 default:
3241 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
3242 return -1;
3245 DBG_DEBUG("Path [%s] ret [%d]\n", fsp_str_dbg(fsp), ret);
3246 return ret;
3249 static int fruit_fstat_rsrc_xattr(vfs_handle_struct *handle,
3250 files_struct *fsp,
3251 SMB_STRUCT_STAT *sbuf)
3253 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
3256 static int fruit_fstat_rsrc_stream(vfs_handle_struct *handle,
3257 files_struct *fsp,
3258 SMB_STRUCT_STAT *sbuf)
3260 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
3263 static int fruit_fstat_rsrc_adouble(vfs_handle_struct *handle,
3264 files_struct *fsp,
3265 SMB_STRUCT_STAT *sbuf)
3267 struct adouble *ad = NULL;
3268 int ret;
3270 /* Populate the stat struct with info from the base file. */
3271 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
3272 if (ret == -1) {
3273 return -1;
3276 ad = ad_get(talloc_tos(), handle,
3277 fsp->base_fsp->fsp_name,
3278 ADOUBLE_RSRC);
3279 if (ad == NULL) {
3280 DBG_ERR("ad_get [%s] failed [%s]\n",
3281 fsp_str_dbg(fsp), strerror(errno));
3282 return -1;
3285 *sbuf = fsp->base_fsp->fsp_name->st;
3286 sbuf->st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
3287 sbuf->st_ex_ino = hash_inode(sbuf, fsp->fsp_name->stream_name);
3289 TALLOC_FREE(ad);
3290 return 0;
3293 static int fruit_fstat_rsrc(vfs_handle_struct *handle, files_struct *fsp,
3294 SMB_STRUCT_STAT *sbuf, struct fio *fio)
3296 int ret;
3298 switch (fio->config->rsrc) {
3299 case FRUIT_RSRC_STREAM:
3300 ret = fruit_fstat_rsrc_stream(handle, fsp, sbuf);
3301 break;
3303 case FRUIT_RSRC_ADFILE:
3304 ret = fruit_fstat_rsrc_adouble(handle, fsp, sbuf);
3305 break;
3307 case FRUIT_RSRC_XATTR:
3308 ret = fruit_fstat_rsrc_xattr(handle, fsp, sbuf);
3309 break;
3311 default:
3312 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
3313 return -1;
3316 return ret;
3319 static int fruit_fstat(vfs_handle_struct *handle, files_struct *fsp,
3320 SMB_STRUCT_STAT *sbuf)
3322 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
3323 int rc;
3325 if (fio == NULL) {
3326 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
3329 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
3331 if (fio->type == ADOUBLE_META) {
3332 rc = fruit_fstat_meta(handle, fsp, sbuf, fio);
3333 } else {
3334 rc = fruit_fstat_rsrc(handle, fsp, sbuf, fio);
3337 if (rc == 0) {
3338 sbuf->st_ex_mode &= ~S_IFMT;
3339 sbuf->st_ex_mode |= S_IFREG;
3340 sbuf->st_ex_blocks = sbuf->st_ex_size / STAT_ST_BLOCKSIZE + 1;
3343 DBG_DEBUG("Path [%s] rc [%d] size [%"PRIdMAX"]\n",
3344 fsp_str_dbg(fsp), rc, (intmax_t)sbuf->st_ex_size);
3345 return rc;
3348 static NTSTATUS delete_invalid_meta_stream(
3349 vfs_handle_struct *handle,
3350 const struct smb_filename *smb_fname,
3351 TALLOC_CTX *mem_ctx,
3352 unsigned int *pnum_streams,
3353 struct stream_struct **pstreams,
3354 off_t size)
3356 struct smb_filename *sname = NULL;
3357 int ret;
3358 bool ok;
3360 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams, AFPINFO_STREAM);
3361 if (!ok) {
3362 return NT_STATUS_INTERNAL_ERROR;
3365 if (size == 0) {
3366 return NT_STATUS_OK;
3369 sname = synthetic_smb_fname(talloc_tos(),
3370 smb_fname->base_name,
3371 AFPINFO_STREAM_NAME,
3372 NULL,
3373 smb_fname->twrp,
3375 if (sname == NULL) {
3376 return NT_STATUS_NO_MEMORY;
3379 ret = SMB_VFS_NEXT_UNLINKAT(handle,
3380 handle->conn->cwd_fsp,
3381 sname,
3383 TALLOC_FREE(sname);
3384 if (ret != 0) {
3385 DBG_ERR("Removing [%s] failed\n", smb_fname_str_dbg(sname));
3386 return map_nt_error_from_unix(errno);
3389 return NT_STATUS_OK;
3392 static NTSTATUS fruit_streaminfo_meta_stream(
3393 vfs_handle_struct *handle,
3394 struct files_struct *fsp,
3395 const struct smb_filename *smb_fname,
3396 TALLOC_CTX *mem_ctx,
3397 unsigned int *pnum_streams,
3398 struct stream_struct **pstreams)
3400 struct stream_struct *stream = *pstreams;
3401 unsigned int num_streams = *pnum_streams;
3402 int i;
3404 for (i = 0; i < num_streams; i++) {
3405 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
3406 break;
3410 if (i == num_streams) {
3411 return NT_STATUS_OK;
3414 if (stream[i].size != AFP_INFO_SIZE) {
3415 DBG_ERR("Removing invalid AFPINFO_STREAM size [%jd] from [%s]\n",
3416 (intmax_t)stream[i].size, smb_fname_str_dbg(smb_fname));
3418 return delete_invalid_meta_stream(handle,
3419 smb_fname,
3420 mem_ctx,
3421 pnum_streams,
3422 pstreams,
3423 stream[i].size);
3427 return NT_STATUS_OK;
3430 static NTSTATUS fruit_streaminfo_meta_netatalk(
3431 vfs_handle_struct *handle,
3432 struct files_struct *fsp,
3433 const struct smb_filename *smb_fname,
3434 TALLOC_CTX *mem_ctx,
3435 unsigned int *pnum_streams,
3436 struct stream_struct **pstreams)
3438 struct stream_struct *stream = *pstreams;
3439 unsigned int num_streams = *pnum_streams;
3440 struct adouble *ad = NULL;
3441 bool is_fi_empty;
3442 int i;
3443 bool ok;
3445 /* Remove the Netatalk xattr from the list */
3446 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
3447 ":" NETATALK_META_XATTR ":$DATA");
3448 if (!ok) {
3449 return NT_STATUS_NO_MEMORY;
3453 * Check if there's a AFPINFO_STREAM from the VFS streams
3454 * backend and if yes, remove it from the list
3456 for (i = 0; i < num_streams; i++) {
3457 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
3458 break;
3462 if (i < num_streams) {
3463 DBG_WARNING("Unexpected AFPINFO_STREAM on [%s]\n",
3464 smb_fname_str_dbg(smb_fname));
3466 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
3467 AFPINFO_STREAM);
3468 if (!ok) {
3469 return NT_STATUS_INTERNAL_ERROR;
3473 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
3474 if (ad == NULL) {
3475 return NT_STATUS_OK;
3478 is_fi_empty = ad_empty_finderinfo(ad);
3479 TALLOC_FREE(ad);
3481 if (is_fi_empty) {
3482 return NT_STATUS_OK;
3485 ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
3486 AFPINFO_STREAM_NAME, AFP_INFO_SIZE,
3487 smb_roundup(handle->conn, AFP_INFO_SIZE));
3488 if (!ok) {
3489 return NT_STATUS_NO_MEMORY;
3492 return NT_STATUS_OK;
3495 static NTSTATUS fruit_streaminfo_meta(vfs_handle_struct *handle,
3496 struct files_struct *fsp,
3497 const struct smb_filename *smb_fname,
3498 TALLOC_CTX *mem_ctx,
3499 unsigned int *pnum_streams,
3500 struct stream_struct **pstreams)
3502 struct fruit_config_data *config = NULL;
3503 NTSTATUS status;
3505 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
3506 return NT_STATUS_INTERNAL_ERROR);
3508 switch (config->meta) {
3509 case FRUIT_META_NETATALK:
3510 status = fruit_streaminfo_meta_netatalk(handle, fsp, smb_fname,
3511 mem_ctx, pnum_streams,
3512 pstreams);
3513 break;
3515 case FRUIT_META_STREAM:
3516 status = fruit_streaminfo_meta_stream(handle, fsp, smb_fname,
3517 mem_ctx, pnum_streams,
3518 pstreams);
3519 break;
3521 default:
3522 return NT_STATUS_INTERNAL_ERROR;
3525 return status;
3528 static NTSTATUS fruit_streaminfo_rsrc_stream(
3529 vfs_handle_struct *handle,
3530 struct files_struct *fsp,
3531 const struct smb_filename *smb_fname,
3532 TALLOC_CTX *mem_ctx,
3533 unsigned int *pnum_streams,
3534 struct stream_struct **pstreams)
3536 bool ok;
3538 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
3539 if (!ok) {
3540 DBG_ERR("Filtering resource stream failed\n");
3541 return NT_STATUS_INTERNAL_ERROR;
3543 return NT_STATUS_OK;
3546 static NTSTATUS fruit_streaminfo_rsrc_xattr(
3547 vfs_handle_struct *handle,
3548 struct files_struct *fsp,
3549 const struct smb_filename *smb_fname,
3550 TALLOC_CTX *mem_ctx,
3551 unsigned int *pnum_streams,
3552 struct stream_struct **pstreams)
3554 bool ok;
3556 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
3557 if (!ok) {
3558 DBG_ERR("Filtering resource stream failed\n");
3559 return NT_STATUS_INTERNAL_ERROR;
3561 return NT_STATUS_OK;
3564 static NTSTATUS fruit_streaminfo_rsrc_adouble(
3565 vfs_handle_struct *handle,
3566 struct files_struct *fsp,
3567 const struct smb_filename *smb_fname,
3568 TALLOC_CTX *mem_ctx,
3569 unsigned int *pnum_streams,
3570 struct stream_struct **pstreams)
3572 struct stream_struct *stream = *pstreams;
3573 unsigned int num_streams = *pnum_streams;
3574 struct adouble *ad = NULL;
3575 bool ok;
3576 size_t rlen;
3577 int i;
3580 * Check if there's a AFPRESOURCE_STREAM from the VFS streams backend
3581 * and if yes, remove it from the list
3583 for (i = 0; i < num_streams; i++) {
3584 if (strequal_m(stream[i].name, AFPRESOURCE_STREAM)) {
3585 break;
3589 if (i < num_streams) {
3590 DBG_WARNING("Unexpected AFPRESOURCE_STREAM on [%s]\n",
3591 smb_fname_str_dbg(smb_fname));
3593 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
3594 AFPRESOURCE_STREAM);
3595 if (!ok) {
3596 return NT_STATUS_INTERNAL_ERROR;
3600 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
3601 if (ad == NULL) {
3602 return NT_STATUS_OK;
3605 rlen = ad_getentrylen(ad, ADEID_RFORK);
3606 TALLOC_FREE(ad);
3608 if (rlen == 0) {
3609 return NT_STATUS_OK;
3612 ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
3613 AFPRESOURCE_STREAM_NAME, rlen,
3614 smb_roundup(handle->conn, rlen));
3615 if (!ok) {
3616 return NT_STATUS_NO_MEMORY;
3619 return NT_STATUS_OK;
3622 static NTSTATUS fruit_streaminfo_rsrc(vfs_handle_struct *handle,
3623 struct files_struct *fsp,
3624 const struct smb_filename *smb_fname,
3625 TALLOC_CTX *mem_ctx,
3626 unsigned int *pnum_streams,
3627 struct stream_struct **pstreams)
3629 struct fruit_config_data *config = NULL;
3630 NTSTATUS status;
3632 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
3633 return NT_STATUS_INTERNAL_ERROR);
3635 switch (config->rsrc) {
3636 case FRUIT_RSRC_STREAM:
3637 status = fruit_streaminfo_rsrc_stream(handle, fsp, smb_fname,
3638 mem_ctx, pnum_streams,
3639 pstreams);
3640 break;
3642 case FRUIT_RSRC_XATTR:
3643 status = fruit_streaminfo_rsrc_xattr(handle, fsp, smb_fname,
3644 mem_ctx, pnum_streams,
3645 pstreams);
3646 break;
3648 case FRUIT_RSRC_ADFILE:
3649 status = fruit_streaminfo_rsrc_adouble(handle, fsp, smb_fname,
3650 mem_ctx, pnum_streams,
3651 pstreams);
3652 break;
3654 default:
3655 return NT_STATUS_INTERNAL_ERROR;
3658 return status;
3661 static void fruit_filter_empty_streams(unsigned int *pnum_streams,
3662 struct stream_struct **pstreams)
3664 unsigned num_streams = *pnum_streams;
3665 struct stream_struct *streams = *pstreams;
3666 unsigned i = 0;
3668 if (!global_fruit_config.nego_aapl) {
3669 return;
3672 while (i < num_streams) {
3673 struct smb_filename smb_fname = (struct smb_filename) {
3674 .stream_name = streams[i].name,
3677 if (is_ntfs_default_stream_smb_fname(&smb_fname)
3678 || streams[i].size > 0)
3680 i++;
3681 continue;
3684 streams[i] = streams[num_streams - 1];
3685 num_streams--;
3688 *pnum_streams = num_streams;
3691 static NTSTATUS fruit_streaminfo(vfs_handle_struct *handle,
3692 struct files_struct *fsp,
3693 const struct smb_filename *smb_fname,
3694 TALLOC_CTX *mem_ctx,
3695 unsigned int *pnum_streams,
3696 struct stream_struct **pstreams)
3698 struct fruit_config_data *config = NULL;
3699 NTSTATUS status;
3701 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
3702 return NT_STATUS_UNSUCCESSFUL);
3704 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3706 status = SMB_VFS_NEXT_STREAMINFO(handle, fsp, smb_fname, mem_ctx,
3707 pnum_streams, pstreams);
3708 if (!NT_STATUS_IS_OK(status)) {
3709 return status;
3712 fruit_filter_empty_streams(pnum_streams, pstreams);
3714 status = fruit_streaminfo_meta(handle, fsp, smb_fname,
3715 mem_ctx, pnum_streams, pstreams);
3716 if (!NT_STATUS_IS_OK(status)) {
3717 return status;
3720 status = fruit_streaminfo_rsrc(handle, fsp, smb_fname,
3721 mem_ctx, pnum_streams, pstreams);
3722 if (!NT_STATUS_IS_OK(status)) {
3723 return status;
3726 return NT_STATUS_OK;
3729 static int fruit_ntimes(vfs_handle_struct *handle,
3730 const struct smb_filename *smb_fname,
3731 struct smb_file_time *ft)
3733 int rc = 0;
3734 struct adouble *ad = NULL;
3735 struct fruit_config_data *config = NULL;
3737 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
3738 return -1);
3740 if ((config->meta != FRUIT_META_NETATALK) ||
3741 is_omit_timespec(&ft->create_time))
3743 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
3746 DEBUG(10,("set btime for %s to %s\n", smb_fname_str_dbg(smb_fname),
3747 time_to_asc(convert_timespec_to_time_t(ft->create_time))));
3749 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
3750 if (ad == NULL) {
3751 goto exit;
3754 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX,
3755 convert_time_t_to_uint32_t(ft->create_time.tv_sec));
3757 rc = ad_set(handle, ad, smb_fname);
3759 exit:
3761 TALLOC_FREE(ad);
3762 if (rc != 0) {
3763 DEBUG(1, ("fruit_ntimes: %s\n", smb_fname_str_dbg(smb_fname)));
3764 return -1;
3766 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
3769 static int fruit_fallocate(struct vfs_handle_struct *handle,
3770 struct files_struct *fsp,
3771 uint32_t mode,
3772 off_t offset,
3773 off_t len)
3775 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
3777 if (fio == NULL) {
3778 return SMB_VFS_NEXT_FALLOCATE(handle, fsp, mode, offset, len);
3781 /* Let the pwrite code path handle it. */
3782 errno = ENOSYS;
3783 return -1;
3786 static int fruit_ftruncate_rsrc_xattr(struct vfs_handle_struct *handle,
3787 struct files_struct *fsp,
3788 off_t offset)
3790 #ifdef HAVE_ATTROPEN
3791 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
3792 #endif
3793 return 0;
3796 static int fruit_ftruncate_rsrc_adouble(struct vfs_handle_struct *handle,
3797 struct files_struct *fsp,
3798 off_t offset)
3800 int rc;
3801 struct adouble *ad = NULL;
3802 off_t ad_off;
3804 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
3805 if (ad == NULL) {
3806 DBG_DEBUG("ad_get [%s] failed [%s]\n",
3807 fsp_str_dbg(fsp), strerror(errno));
3808 return -1;
3811 ad_off = ad_getentryoff(ad, ADEID_RFORK);
3813 rc = SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset + ad_off);
3814 if (rc != 0) {
3815 TALLOC_FREE(ad);
3816 return -1;
3819 ad_setentrylen(ad, ADEID_RFORK, offset);
3821 rc = ad_fset(handle, ad, fsp);
3822 if (rc != 0) {
3823 DBG_ERR("ad_fset [%s] failed [%s]\n",
3824 fsp_str_dbg(fsp), strerror(errno));
3825 TALLOC_FREE(ad);
3826 return -1;
3829 TALLOC_FREE(ad);
3830 return 0;
3833 static int fruit_ftruncate_rsrc_stream(struct vfs_handle_struct *handle,
3834 struct files_struct *fsp,
3835 off_t offset)
3837 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
3840 static int fruit_ftruncate_rsrc(struct vfs_handle_struct *handle,
3841 struct files_struct *fsp,
3842 off_t offset)
3844 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
3845 int ret;
3847 if (fio == NULL) {
3848 DBG_ERR("Failed to fetch fsp extension");
3849 return -1;
3852 switch (fio->config->rsrc) {
3853 case FRUIT_RSRC_XATTR:
3854 ret = fruit_ftruncate_rsrc_xattr(handle, fsp, offset);
3855 break;
3857 case FRUIT_RSRC_ADFILE:
3858 ret = fruit_ftruncate_rsrc_adouble(handle, fsp, offset);
3859 break;
3861 case FRUIT_RSRC_STREAM:
3862 ret = fruit_ftruncate_rsrc_stream(handle, fsp, offset);
3863 break;
3865 default:
3866 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
3867 return -1;
3871 return ret;
3874 static int fruit_ftruncate_meta(struct vfs_handle_struct *handle,
3875 struct files_struct *fsp,
3876 off_t offset)
3878 if (offset > 60) {
3879 DBG_WARNING("ftruncate %s to %jd",
3880 fsp_str_dbg(fsp), (intmax_t)offset);
3881 /* OS X returns NT_STATUS_ALLOTTED_SPACE_EXCEEDED */
3882 errno = EOVERFLOW;
3883 return -1;
3886 /* OS X returns success but does nothing */
3887 DBG_INFO("ignoring ftruncate %s to %jd\n",
3888 fsp_str_dbg(fsp), (intmax_t)offset);
3889 return 0;
3892 static int fruit_ftruncate(struct vfs_handle_struct *handle,
3893 struct files_struct *fsp,
3894 off_t offset)
3896 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
3897 int ret;
3899 DBG_DEBUG("Path [%s] offset [%"PRIdMAX"]\n", fsp_str_dbg(fsp),
3900 (intmax_t)offset);
3902 if (fio == NULL) {
3903 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
3906 if (fio->type == ADOUBLE_META) {
3907 ret = fruit_ftruncate_meta(handle, fsp, offset);
3908 } else {
3909 ret = fruit_ftruncate_rsrc(handle, fsp, offset);
3912 DBG_DEBUG("Path [%s] result [%d]\n", fsp_str_dbg(fsp), ret);
3913 return ret;
3916 static NTSTATUS fruit_create_file(vfs_handle_struct *handle,
3917 struct smb_request *req,
3918 struct smb_filename *smb_fname,
3919 uint32_t access_mask,
3920 uint32_t share_access,
3921 uint32_t create_disposition,
3922 uint32_t create_options,
3923 uint32_t file_attributes,
3924 uint32_t oplock_request,
3925 const struct smb2_lease *lease,
3926 uint64_t allocation_size,
3927 uint32_t private_flags,
3928 struct security_descriptor *sd,
3929 struct ea_list *ea_list,
3930 files_struct **result,
3931 int *pinfo,
3932 const struct smb2_create_blobs *in_context_blobs,
3933 struct smb2_create_blobs *out_context_blobs)
3935 NTSTATUS status;
3936 struct fruit_config_data *config = NULL;
3937 files_struct *fsp = NULL;
3938 bool internal_open = (oplock_request & INTERNAL_OPEN_ONLY);
3939 int ret;
3941 status = check_aapl(handle, req, in_context_blobs, out_context_blobs);
3942 if (!NT_STATUS_IS_OK(status)) {
3943 goto fail;
3946 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
3947 return NT_STATUS_UNSUCCESSFUL);
3949 if (is_apple_stream(smb_fname->stream_name) && !internal_open) {
3950 uint32_t conv_flags = 0;
3952 if (config->wipe_intentionally_left_blank_rfork) {
3953 conv_flags |= AD_CONV_WIPE_BLANK;
3955 if (config->delete_empty_adfiles) {
3956 conv_flags |= AD_CONV_DELETE;
3959 ret = ad_convert(handle,
3960 handle->conn->cwd_fsp,
3961 smb_fname,
3962 macos_string_replace_map,
3963 conv_flags);
3964 if (ret != 0) {
3965 DBG_ERR("ad_convert() failed\n");
3966 return NT_STATUS_UNSUCCESSFUL;
3970 status = SMB_VFS_NEXT_CREATE_FILE(
3971 handle, req, smb_fname,
3972 access_mask, share_access,
3973 create_disposition, create_options,
3974 file_attributes, oplock_request,
3975 lease,
3976 allocation_size, private_flags,
3977 sd, ea_list, result,
3978 pinfo, in_context_blobs, out_context_blobs);
3979 if (!NT_STATUS_IS_OK(status)) {
3980 return status;
3983 fsp = *result;
3985 if (global_fruit_config.nego_aapl) {
3986 if (config->posix_rename && fsp->fsp_flags.is_directory) {
3988 * Enable POSIX directory rename behaviour
3990 fsp->posix_flags |= FSP_POSIX_FLAGS_RENAME;
3995 * If this is a plain open for existing files, opening an 0
3996 * byte size resource fork MUST fail with
3997 * NT_STATUS_OBJECT_NAME_NOT_FOUND.
3999 * Cf the vfs_fruit torture tests in test_rfork_create().
4001 if (global_fruit_config.nego_aapl &&
4002 create_disposition == FILE_OPEN &&
4003 smb_fname->st.st_ex_size == 0 &&
4004 is_named_stream(smb_fname))
4006 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
4007 goto fail;
4010 if (is_named_stream(smb_fname) || fsp->fsp_flags.is_directory) {
4011 return status;
4014 if ((config->locking == FRUIT_LOCKING_NETATALK) &&
4015 (fsp->op != NULL) &&
4016 !fsp->fsp_flags.is_pathref)
4018 status = fruit_check_access(
4019 handle, *result,
4020 access_mask,
4021 share_access);
4022 if (!NT_STATUS_IS_OK(status)) {
4023 goto fail;
4027 return status;
4029 fail:
4030 DEBUG(10, ("fruit_create_file: %s\n", nt_errstr(status)));
4032 if (fsp) {
4033 close_file(req, fsp, ERROR_CLOSE);
4034 *result = fsp = NULL;
4037 return status;
4040 static NTSTATUS fruit_readdir_attr(struct vfs_handle_struct *handle,
4041 const struct smb_filename *fname,
4042 TALLOC_CTX *mem_ctx,
4043 struct readdir_attr_data **pattr_data)
4045 struct fruit_config_data *config = NULL;
4046 struct readdir_attr_data *attr_data;
4047 uint32_t conv_flags = 0;
4048 NTSTATUS status;
4049 int ret;
4051 SMB_VFS_HANDLE_GET_DATA(handle, config,
4052 struct fruit_config_data,
4053 return NT_STATUS_UNSUCCESSFUL);
4055 if (!global_fruit_config.nego_aapl) {
4056 return SMB_VFS_NEXT_READDIR_ATTR(handle, fname, mem_ctx, pattr_data);
4059 DEBUG(10, ("fruit_readdir_attr %s\n", fname->base_name));
4061 if (config->wipe_intentionally_left_blank_rfork) {
4062 conv_flags |= AD_CONV_WIPE_BLANK;
4064 if (config->delete_empty_adfiles) {
4065 conv_flags |= AD_CONV_DELETE;
4068 ret = ad_convert(handle,
4069 handle->conn->cwd_fsp,
4070 fname,
4071 macos_string_replace_map,
4072 conv_flags);
4073 if (ret != 0) {
4074 DBG_ERR("ad_convert() failed\n");
4075 return NT_STATUS_UNSUCCESSFUL;
4078 *pattr_data = talloc_zero(mem_ctx, struct readdir_attr_data);
4079 if (*pattr_data == NULL) {
4080 return NT_STATUS_UNSUCCESSFUL;
4082 attr_data = *pattr_data;
4083 attr_data->type = RDATTR_AAPL;
4086 * Mac metadata: compressed FinderInfo, resource fork length
4087 * and creation date
4089 status = readdir_attr_macmeta(handle, fname, attr_data);
4090 if (!NT_STATUS_IS_OK(status)) {
4092 * Error handling is tricky: if we return failure from
4093 * this function, the corresponding directory entry
4094 * will to be passed to the client, so we really just
4095 * want to error out on fatal errors.
4097 if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
4098 goto fail;
4103 * UNIX mode
4105 if (config->unix_info_enabled) {
4106 attr_data->attr_data.aapl.unix_mode = fname->st.st_ex_mode;
4110 * max_access
4112 if (!config->readdir_attr_max_access) {
4113 attr_data->attr_data.aapl.max_access = FILE_GENERIC_ALL;
4114 } else {
4115 status = smbd_calculate_access_mask(
4116 handle->conn,
4117 handle->conn->cwd_fsp,
4118 fname,
4119 false,
4120 SEC_FLAG_MAXIMUM_ALLOWED,
4121 &attr_data->attr_data.aapl.max_access);
4122 if (!NT_STATUS_IS_OK(status)) {
4123 goto fail;
4127 return NT_STATUS_OK;
4129 fail:
4130 DEBUG(1, ("fruit_readdir_attr %s, error: %s\n",
4131 fname->base_name, nt_errstr(status)));
4132 TALLOC_FREE(*pattr_data);
4133 return status;
4136 static NTSTATUS fruit_fget_nt_acl(vfs_handle_struct *handle,
4137 files_struct *fsp,
4138 uint32_t security_info,
4139 TALLOC_CTX *mem_ctx,
4140 struct security_descriptor **ppdesc)
4142 NTSTATUS status;
4143 struct security_ace ace;
4144 struct dom_sid sid;
4145 struct fruit_config_data *config;
4147 SMB_VFS_HANDLE_GET_DATA(handle, config,
4148 struct fruit_config_data,
4149 return NT_STATUS_UNSUCCESSFUL);
4151 status = SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
4152 mem_ctx, ppdesc);
4153 if (!NT_STATUS_IS_OK(status)) {
4154 return status;
4158 * Add MS NFS style ACEs with uid, gid and mode
4160 if (!global_fruit_config.nego_aapl) {
4161 return NT_STATUS_OK;
4163 if (!config->unix_info_enabled) {
4164 return NT_STATUS_OK;
4167 /* First remove any existing ACE's with NFS style mode/uid/gid SIDs. */
4168 status = remove_virtual_nfs_aces(*ppdesc);
4169 if (!NT_STATUS_IS_OK(status)) {
4170 DBG_WARNING("failed to remove MS NFS style ACEs\n");
4171 return status;
4174 /* MS NFS style mode */
4175 sid_compose(&sid, &global_sid_Unix_NFS_Mode, fsp->fsp_name->st.st_ex_mode);
4176 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
4177 status = security_descriptor_dacl_add(*ppdesc, &ace);
4178 if (!NT_STATUS_IS_OK(status)) {
4179 DEBUG(1,("failed to add MS NFS style ACE\n"));
4180 return status;
4183 /* MS NFS style uid */
4184 sid_compose(&sid, &global_sid_Unix_NFS_Users, fsp->fsp_name->st.st_ex_uid);
4185 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
4186 status = security_descriptor_dacl_add(*ppdesc, &ace);
4187 if (!NT_STATUS_IS_OK(status)) {
4188 DEBUG(1,("failed to add MS NFS style ACE\n"));
4189 return status;
4192 /* MS NFS style gid */
4193 sid_compose(&sid, &global_sid_Unix_NFS_Groups, fsp->fsp_name->st.st_ex_gid);
4194 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
4195 status = security_descriptor_dacl_add(*ppdesc, &ace);
4196 if (!NT_STATUS_IS_OK(status)) {
4197 DEBUG(1,("failed to add MS NFS style ACE\n"));
4198 return status;
4201 return NT_STATUS_OK;
4204 static NTSTATUS fruit_fset_nt_acl(vfs_handle_struct *handle,
4205 files_struct *fsp,
4206 uint32_t security_info_sent,
4207 const struct security_descriptor *orig_psd)
4209 NTSTATUS status;
4210 bool do_chmod;
4211 mode_t ms_nfs_mode = 0;
4212 int result;
4213 struct security_descriptor *psd = NULL;
4214 uint32_t orig_num_aces = 0;
4216 if (orig_psd->dacl != NULL) {
4217 orig_num_aces = orig_psd->dacl->num_aces;
4220 psd = security_descriptor_copy(talloc_tos(), orig_psd);
4221 if (psd == NULL) {
4222 return NT_STATUS_NO_MEMORY;
4225 DBG_DEBUG("fruit_fset_nt_acl: %s\n", fsp_str_dbg(fsp));
4227 status = check_ms_nfs(handle, fsp, psd, &ms_nfs_mode, &do_chmod);
4228 if (!NT_STATUS_IS_OK(status)) {
4229 DEBUG(1, ("fruit_fset_nt_acl: check_ms_nfs failed%s\n", fsp_str_dbg(fsp)));
4230 TALLOC_FREE(psd);
4231 return status;
4235 * If only ms_nfs ACE entries were sent, ensure we set the DACL
4236 * sent/present flags correctly now we've removed them.
4239 if (orig_num_aces != 0) {
4241 * Are there any ACE's left ?
4243 if (psd->dacl->num_aces == 0) {
4244 /* No - clear the DACL sent/present flags. */
4245 security_info_sent &= ~SECINFO_DACL;
4246 psd->type &= ~SEC_DESC_DACL_PRESENT;
4250 status = SMB_VFS_NEXT_FSET_NT_ACL(handle, fsp, security_info_sent, psd);
4251 if (!NT_STATUS_IS_OK(status)) {
4252 DEBUG(1, ("fruit_fset_nt_acl: SMB_VFS_NEXT_FSET_NT_ACL failed%s\n", fsp_str_dbg(fsp)));
4253 TALLOC_FREE(psd);
4254 return status;
4257 if (do_chmod) {
4258 result = SMB_VFS_FCHMOD(fsp, ms_nfs_mode);
4259 if (result != 0) {
4260 DBG_WARNING("%s, result: %d, %04o error %s\n",
4261 fsp_str_dbg(fsp),
4262 result,
4263 (unsigned)ms_nfs_mode,
4264 strerror(errno));
4265 status = map_nt_error_from_unix(errno);
4266 TALLOC_FREE(psd);
4267 return status;
4271 TALLOC_FREE(psd);
4272 return NT_STATUS_OK;
4275 static struct vfs_offload_ctx *fruit_offload_ctx;
4277 struct fruit_offload_read_state {
4278 struct vfs_handle_struct *handle;
4279 struct tevent_context *ev;
4280 files_struct *fsp;
4281 uint32_t fsctl;
4282 DATA_BLOB token;
4285 static void fruit_offload_read_done(struct tevent_req *subreq);
4287 static struct tevent_req *fruit_offload_read_send(
4288 TALLOC_CTX *mem_ctx,
4289 struct tevent_context *ev,
4290 struct vfs_handle_struct *handle,
4291 files_struct *fsp,
4292 uint32_t fsctl,
4293 uint32_t ttl,
4294 off_t offset,
4295 size_t to_copy)
4297 struct tevent_req *req = NULL;
4298 struct tevent_req *subreq = NULL;
4299 struct fruit_offload_read_state *state = NULL;
4301 req = tevent_req_create(mem_ctx, &state,
4302 struct fruit_offload_read_state);
4303 if (req == NULL) {
4304 return NULL;
4306 *state = (struct fruit_offload_read_state) {
4307 .handle = handle,
4308 .ev = ev,
4309 .fsp = fsp,
4310 .fsctl = fsctl,
4313 subreq = SMB_VFS_NEXT_OFFLOAD_READ_SEND(mem_ctx, ev, handle, fsp,
4314 fsctl, ttl, offset, to_copy);
4315 if (tevent_req_nomem(subreq, req)) {
4316 return tevent_req_post(req, ev);
4318 tevent_req_set_callback(subreq, fruit_offload_read_done, req);
4319 return req;
4322 static void fruit_offload_read_done(struct tevent_req *subreq)
4324 struct tevent_req *req = tevent_req_callback_data(
4325 subreq, struct tevent_req);
4326 struct fruit_offload_read_state *state = tevent_req_data(
4327 req, struct fruit_offload_read_state);
4328 NTSTATUS status;
4330 status = SMB_VFS_NEXT_OFFLOAD_READ_RECV(subreq,
4331 state->handle,
4332 state,
4333 &state->token);
4334 TALLOC_FREE(subreq);
4335 if (tevent_req_nterror(req, status)) {
4336 return;
4339 if (state->fsctl != FSCTL_SRV_REQUEST_RESUME_KEY) {
4340 tevent_req_done(req);
4341 return;
4344 status = vfs_offload_token_ctx_init(state->fsp->conn->sconn->client,
4345 &fruit_offload_ctx);
4346 if (tevent_req_nterror(req, status)) {
4347 return;
4350 status = vfs_offload_token_db_store_fsp(fruit_offload_ctx,
4351 state->fsp,
4352 &state->token);
4353 if (tevent_req_nterror(req, status)) {
4354 return;
4357 tevent_req_done(req);
4358 return;
4361 static NTSTATUS fruit_offload_read_recv(struct tevent_req *req,
4362 struct vfs_handle_struct *handle,
4363 TALLOC_CTX *mem_ctx,
4364 DATA_BLOB *token)
4366 struct fruit_offload_read_state *state = tevent_req_data(
4367 req, struct fruit_offload_read_state);
4368 NTSTATUS status;
4370 if (tevent_req_is_nterror(req, &status)) {
4371 tevent_req_received(req);
4372 return status;
4375 token->length = state->token.length;
4376 token->data = talloc_move(mem_ctx, &state->token.data);
4378 tevent_req_received(req);
4379 return NT_STATUS_OK;
4382 struct fruit_offload_write_state {
4383 struct vfs_handle_struct *handle;
4384 off_t copied;
4385 struct files_struct *src_fsp;
4386 struct files_struct *dst_fsp;
4387 bool is_copyfile;
4390 static void fruit_offload_write_done(struct tevent_req *subreq);
4391 static struct tevent_req *fruit_offload_write_send(struct vfs_handle_struct *handle,
4392 TALLOC_CTX *mem_ctx,
4393 struct tevent_context *ev,
4394 uint32_t fsctl,
4395 DATA_BLOB *token,
4396 off_t transfer_offset,
4397 struct files_struct *dest_fsp,
4398 off_t dest_off,
4399 off_t num)
4401 struct tevent_req *req, *subreq;
4402 struct fruit_offload_write_state *state;
4403 NTSTATUS status;
4404 struct fruit_config_data *config;
4405 off_t src_off = transfer_offset;
4406 files_struct *src_fsp = NULL;
4407 off_t to_copy = num;
4408 bool copyfile_enabled = false;
4410 DEBUG(10,("soff: %ju, doff: %ju, len: %ju\n",
4411 (uintmax_t)src_off, (uintmax_t)dest_off, (uintmax_t)num));
4413 SMB_VFS_HANDLE_GET_DATA(handle, config,
4414 struct fruit_config_data,
4415 return NULL);
4417 req = tevent_req_create(mem_ctx, &state,
4418 struct fruit_offload_write_state);
4419 if (req == NULL) {
4420 return NULL;
4422 state->handle = handle;
4423 state->dst_fsp = dest_fsp;
4425 switch (fsctl) {
4426 case FSCTL_SRV_COPYCHUNK:
4427 case FSCTL_SRV_COPYCHUNK_WRITE:
4428 copyfile_enabled = config->copyfile_enabled;
4429 break;
4430 default:
4431 break;
4435 * Check if this a OS X copyfile style copychunk request with
4436 * a requested chunk count of 0 that was translated to a
4437 * offload_write_send VFS call overloading the parameters src_off
4438 * = dest_off = num = 0.
4440 if (copyfile_enabled && num == 0 && src_off == 0 && dest_off == 0) {
4441 status = vfs_offload_token_db_fetch_fsp(
4442 fruit_offload_ctx, token, &src_fsp);
4443 if (tevent_req_nterror(req, status)) {
4444 return tevent_req_post(req, ev);
4446 state->src_fsp = src_fsp;
4448 status = vfs_stat_fsp(src_fsp);
4449 if (tevent_req_nterror(req, status)) {
4450 return tevent_req_post(req, ev);
4453 to_copy = src_fsp->fsp_name->st.st_ex_size;
4454 state->is_copyfile = true;
4457 subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle,
4458 mem_ctx,
4460 fsctl,
4461 token,
4462 transfer_offset,
4463 dest_fsp,
4464 dest_off,
4465 to_copy);
4466 if (tevent_req_nomem(subreq, req)) {
4467 return tevent_req_post(req, ev);
4470 tevent_req_set_callback(subreq, fruit_offload_write_done, req);
4471 return req;
4474 static void fruit_offload_write_done(struct tevent_req *subreq)
4476 struct tevent_req *req = tevent_req_callback_data(
4477 subreq, struct tevent_req);
4478 struct fruit_offload_write_state *state = tevent_req_data(
4479 req, struct fruit_offload_write_state);
4480 NTSTATUS status;
4481 unsigned int num_streams = 0;
4482 struct stream_struct *streams = NULL;
4483 unsigned int i;
4484 struct smb_filename *src_fname_tmp = NULL;
4485 struct smb_filename *dst_fname_tmp = NULL;
4487 status = SMB_VFS_NEXT_OFFLOAD_WRITE_RECV(state->handle,
4488 subreq,
4489 &state->copied);
4490 TALLOC_FREE(subreq);
4491 if (tevent_req_nterror(req, status)) {
4492 return;
4495 if (!state->is_copyfile) {
4496 tevent_req_done(req);
4497 return;
4501 * Now copy all remaining streams. We know the share supports
4502 * streams, because we're in vfs_fruit. We don't do this async
4503 * because streams are few and small.
4505 status = vfs_streaminfo(state->handle->conn, state->src_fsp,
4506 state->src_fsp->fsp_name,
4507 req, &num_streams, &streams);
4508 if (tevent_req_nterror(req, status)) {
4509 return;
4512 if (num_streams == 1) {
4513 /* There is always one stream, ::$DATA. */
4514 tevent_req_done(req);
4515 return;
4518 for (i = 0; i < num_streams; i++) {
4519 DEBUG(10, ("%s: stream: '%s'/%zu\n",
4520 __func__, streams[i].name, (size_t)streams[i].size));
4522 src_fname_tmp = synthetic_smb_fname(
4523 req,
4524 state->src_fsp->fsp_name->base_name,
4525 streams[i].name,
4526 NULL,
4527 state->src_fsp->fsp_name->twrp,
4528 state->src_fsp->fsp_name->flags);
4529 if (tevent_req_nomem(src_fname_tmp, req)) {
4530 return;
4533 if (is_ntfs_default_stream_smb_fname(src_fname_tmp)) {
4534 TALLOC_FREE(src_fname_tmp);
4535 continue;
4538 dst_fname_tmp = synthetic_smb_fname(
4539 req,
4540 state->dst_fsp->fsp_name->base_name,
4541 streams[i].name,
4542 NULL,
4543 state->dst_fsp->fsp_name->twrp,
4544 state->dst_fsp->fsp_name->flags);
4545 if (tevent_req_nomem(dst_fname_tmp, req)) {
4546 TALLOC_FREE(src_fname_tmp);
4547 return;
4550 status = copy_file(req,
4551 state->handle->conn,
4552 src_fname_tmp,
4553 dst_fname_tmp,
4554 OPENX_FILE_CREATE_IF_NOT_EXIST,
4555 0, false);
4556 if (!NT_STATUS_IS_OK(status)) {
4557 DEBUG(1, ("%s: copy %s to %s failed: %s\n", __func__,
4558 smb_fname_str_dbg(src_fname_tmp),
4559 smb_fname_str_dbg(dst_fname_tmp),
4560 nt_errstr(status)));
4561 TALLOC_FREE(src_fname_tmp);
4562 TALLOC_FREE(dst_fname_tmp);
4563 tevent_req_nterror(req, status);
4564 return;
4567 TALLOC_FREE(src_fname_tmp);
4568 TALLOC_FREE(dst_fname_tmp);
4571 TALLOC_FREE(streams);
4572 TALLOC_FREE(src_fname_tmp);
4573 TALLOC_FREE(dst_fname_tmp);
4574 tevent_req_done(req);
4577 static NTSTATUS fruit_offload_write_recv(struct vfs_handle_struct *handle,
4578 struct tevent_req *req,
4579 off_t *copied)
4581 struct fruit_offload_write_state *state = tevent_req_data(
4582 req, struct fruit_offload_write_state);
4583 NTSTATUS status;
4585 if (tevent_req_is_nterror(req, &status)) {
4586 DEBUG(1, ("server side copy chunk failed: %s\n",
4587 nt_errstr(status)));
4588 *copied = 0;
4589 tevent_req_received(req);
4590 return status;
4593 *copied = state->copied;
4594 tevent_req_received(req);
4596 return NT_STATUS_OK;
4599 static char *fruit_get_bandsize_line(char **lines, int numlines)
4601 static regex_t re;
4602 static bool re_initialized = false;
4603 int i;
4604 int ret;
4606 if (!re_initialized) {
4607 ret = regcomp(&re, "^[[:blank:]]*<key>band-size</key>$", 0);
4608 if (ret != 0) {
4609 return NULL;
4611 re_initialized = true;
4614 for (i = 0; i < numlines; i++) {
4615 regmatch_t matches[1];
4617 ret = regexec(&re, lines[i], 1, matches, 0);
4618 if (ret == 0) {
4620 * Check if the match was on the last line, sa we want
4621 * the subsequent line.
4623 if (i + 1 == numlines) {
4624 return NULL;
4626 return lines[i + 1];
4628 if (ret != REG_NOMATCH) {
4629 return NULL;
4633 return NULL;
4636 static bool fruit_get_bandsize_from_line(char *line, size_t *_band_size)
4638 static regex_t re;
4639 static bool re_initialized = false;
4640 regmatch_t matches[2];
4641 uint64_t band_size;
4642 int ret;
4643 bool ok;
4645 if (!re_initialized) {
4646 ret = regcomp(&re,
4647 "^[[:blank:]]*"
4648 "<integer>\\([[:digit:]]*\\)</integer>$",
4650 if (ret != 0) {
4651 return false;
4653 re_initialized = true;
4656 ret = regexec(&re, line, 2, matches, 0);
4657 if (ret != 0) {
4658 DBG_ERR("regex failed [%s]\n", line);
4659 return false;
4662 line[matches[1].rm_eo] = '\0';
4664 ok = conv_str_u64(&line[matches[1].rm_so], &band_size);
4665 if (!ok) {
4666 return false;
4668 *_band_size = (size_t)band_size;
4669 return true;
4673 * This reads and parses an Info.plist from a TM sparsebundle looking for the
4674 * "band-size" key and value.
4676 static bool fruit_get_bandsize(vfs_handle_struct *handle,
4677 const char *dir,
4678 size_t *band_size)
4680 #define INFO_PLIST_MAX_SIZE 64*1024
4681 char *plist = NULL;
4682 struct smb_filename *smb_fname = NULL;
4683 files_struct *fsp = NULL;
4684 uint8_t *file_data = NULL;
4685 char **lines = NULL;
4686 char *band_size_line = NULL;
4687 size_t plist_file_size;
4688 ssize_t nread;
4689 int numlines;
4690 int ret;
4691 bool ok = false;
4692 NTSTATUS status;
4694 plist = talloc_asprintf(talloc_tos(),
4695 "%s/%s/Info.plist",
4696 handle->conn->connectpath,
4697 dir);
4698 if (plist == NULL) {
4699 ok = false;
4700 goto out;
4703 smb_fname = synthetic_smb_fname(talloc_tos(),
4704 plist,
4705 NULL,
4706 NULL,
4709 if (smb_fname == NULL) {
4710 ok = false;
4711 goto out;
4714 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
4715 if (ret != 0) {
4716 DBG_INFO("Ignoring Sparsebundle without Info.plist [%s]\n", dir);
4717 ok = true;
4718 goto out;
4721 plist_file_size = smb_fname->st.st_ex_size;
4723 if (plist_file_size > INFO_PLIST_MAX_SIZE) {
4724 DBG_INFO("%s is too large, ignoring\n", plist);
4725 ok = true;
4726 goto out;
4729 status = SMB_VFS_NEXT_CREATE_FILE(
4730 handle, /* conn */
4731 NULL, /* req */
4732 smb_fname, /* fname */
4733 FILE_GENERIC_READ, /* access_mask */
4734 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
4735 FILE_OPEN, /* create_disposition */
4736 0, /* create_options */
4737 0, /* file_attributes */
4738 INTERNAL_OPEN_ONLY, /* oplock_request */
4739 NULL, /* lease */
4740 0, /* allocation_size */
4741 0, /* private_flags */
4742 NULL, /* sd */
4743 NULL, /* ea_list */
4744 &fsp, /* result */
4745 NULL, /* psbuf */
4746 NULL, NULL); /* create context */
4747 if (!NT_STATUS_IS_OK(status)) {
4748 DBG_INFO("Opening [%s] failed [%s]\n",
4749 smb_fname_str_dbg(smb_fname), nt_errstr(status));
4750 ok = false;
4751 goto out;
4754 file_data = talloc_zero_array(talloc_tos(),
4755 uint8_t,
4756 plist_file_size + 1);
4757 if (file_data == NULL) {
4758 ok = false;
4759 goto out;
4762 nread = SMB_VFS_NEXT_PREAD(handle, fsp, file_data, plist_file_size, 0);
4763 if (nread != plist_file_size) {
4764 DBG_ERR("Short read on [%s]: %zu/%zd\n",
4765 fsp_str_dbg(fsp), nread, plist_file_size);
4766 ok = false;
4767 goto out;
4771 status = close_file(NULL, fsp, NORMAL_CLOSE);
4772 fsp = NULL;
4773 if (!NT_STATUS_IS_OK(status)) {
4774 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
4775 ok = false;
4776 goto out;
4779 lines = file_lines_parse((char *)file_data,
4780 plist_file_size,
4781 &numlines,
4782 talloc_tos());
4783 if (lines == NULL) {
4784 ok = false;
4785 goto out;
4788 band_size_line = fruit_get_bandsize_line(lines, numlines);
4789 if (band_size_line == NULL) {
4790 DBG_ERR("Didn't find band-size key in [%s]\n",
4791 smb_fname_str_dbg(smb_fname));
4792 ok = false;
4793 goto out;
4796 ok = fruit_get_bandsize_from_line(band_size_line, band_size);
4797 if (!ok) {
4798 DBG_ERR("fruit_get_bandsize_from_line failed\n");
4799 goto out;
4802 DBG_DEBUG("Parsed band-size [%zu] for [%s]\n", *band_size, plist);
4804 out:
4805 if (fsp != NULL) {
4806 status = close_file(NULL, fsp, NORMAL_CLOSE);
4807 if (!NT_STATUS_IS_OK(status)) {
4808 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
4810 fsp = NULL;
4812 TALLOC_FREE(plist);
4813 TALLOC_FREE(smb_fname);
4814 TALLOC_FREE(file_data);
4815 TALLOC_FREE(lines);
4816 return ok;
4819 struct fruit_disk_free_state {
4820 off_t total_size;
4823 static bool fruit_get_num_bands(vfs_handle_struct *handle,
4824 const char *bundle,
4825 size_t *_nbands)
4827 char *path = NULL;
4828 struct smb_filename *bands_dir = NULL;
4829 struct smb_Dir *dir_hnd = NULL;
4830 const char *dname = NULL;
4831 char *talloced = NULL;
4832 long offset = 0;
4833 size_t nbands;
4835 path = talloc_asprintf(talloc_tos(),
4836 "%s/%s/bands",
4837 handle->conn->connectpath,
4838 bundle);
4839 if (path == NULL) {
4840 return false;
4843 bands_dir = synthetic_smb_fname(talloc_tos(),
4844 path,
4845 NULL,
4846 NULL,
4849 TALLOC_FREE(path);
4850 if (bands_dir == NULL) {
4851 return false;
4854 dir_hnd = OpenDir(talloc_tos(), handle->conn, bands_dir, NULL, 0);
4855 if (dir_hnd == NULL) {
4856 TALLOC_FREE(bands_dir);
4857 return false;
4860 nbands = 0;
4862 while ((dname = ReadDirName(dir_hnd, &offset, NULL, &talloced))
4863 != NULL)
4865 if (ISDOT(dname) || ISDOTDOT(dname)) {
4866 continue;
4868 nbands++;
4870 TALLOC_FREE(dir_hnd);
4872 DBG_DEBUG("%zu bands in [%s]\n", nbands, smb_fname_str_dbg(bands_dir));
4874 TALLOC_FREE(bands_dir);
4876 *_nbands = nbands;
4877 return true;
4880 static bool fruit_tmsize_do_dirent(vfs_handle_struct *handle,
4881 struct fruit_disk_free_state *state,
4882 const char *name)
4884 bool ok;
4885 char *p = NULL;
4886 size_t sparsebundle_strlen = strlen("sparsebundle");
4887 size_t bandsize = 0;
4888 size_t nbands;
4889 off_t tm_size;
4891 p = strstr(name, "sparsebundle");
4892 if (p == NULL) {
4893 return true;
4896 if (p[sparsebundle_strlen] != '\0') {
4897 return true;
4900 DBG_DEBUG("Processing sparsebundle [%s]\n", name);
4902 ok = fruit_get_bandsize(handle, name, &bandsize);
4903 if (!ok) {
4905 * Beware of race conditions: this may be an uninitialized
4906 * Info.plist that a client is just creating. We don't want let
4907 * this to trigger complete failure.
4909 DBG_ERR("Processing sparsebundle [%s] failed\n", name);
4910 return true;
4913 ok = fruit_get_num_bands(handle, name, &nbands);
4914 if (!ok) {
4916 * Beware of race conditions: this may be a backup sparsebundle
4917 * in an early stage lacking a bands subdirectory. We don't want
4918 * let this to trigger complete failure.
4920 DBG_ERR("Processing sparsebundle [%s] failed\n", name);
4921 return true;
4925 * Arithmetic on 32-bit systems may cause overflow, depending on
4926 * size_t precision. First we check its unlikely, then we
4927 * force the precision into target off_t, then we check that
4928 * the total did not overflow either.
4930 if (bandsize > SIZE_MAX/nbands) {
4931 DBG_ERR("tmsize potential overflow: bandsize [%zu] nbands [%zu]\n",
4932 bandsize, nbands);
4933 return false;
4935 tm_size = (off_t)bandsize * (off_t)nbands;
4937 if (state->total_size + tm_size < state->total_size) {
4938 DBG_ERR("tm total size overflow: bandsize [%zu] nbands [%zu]\n",
4939 bandsize, nbands);
4940 return false;
4943 state->total_size += tm_size;
4945 DBG_DEBUG("[%s] tm_size [%jd] total_size [%jd]\n",
4946 name, (intmax_t)tm_size, (intmax_t)state->total_size);
4948 return true;
4952 * Calculate used size of a TimeMachine volume
4954 * This assumes that the volume is used only for TimeMachine.
4956 * - readdir(basedir of share), then
4957 * - for every element that matches regex "^\(.*\)\.sparsebundle$" :
4958 * - parse "\1.sparsebundle/Info.plist" and read the band-size XML key
4959 * - count band files in "\1.sparsebundle/bands/"
4960 * - calculate used size of all bands: band_count * band_size
4962 static uint64_t fruit_disk_free(vfs_handle_struct *handle,
4963 const struct smb_filename *smb_fname,
4964 uint64_t *_bsize,
4965 uint64_t *_dfree,
4966 uint64_t *_dsize)
4968 struct fruit_config_data *config = NULL;
4969 struct fruit_disk_free_state state = {0};
4970 struct smb_Dir *dir_hnd = NULL;
4971 const char *dname = NULL;
4972 char *talloced = NULL;
4973 long offset = 0;
4974 uint64_t dfree;
4975 uint64_t dsize;
4976 bool ok;
4978 SMB_VFS_HANDLE_GET_DATA(handle, config,
4979 struct fruit_config_data,
4980 return UINT64_MAX);
4982 if (!config->time_machine ||
4983 config->time_machine_max_size == 0)
4985 return SMB_VFS_NEXT_DISK_FREE(handle,
4986 smb_fname,
4987 _bsize,
4988 _dfree,
4989 _dsize);
4992 dir_hnd = OpenDir(talloc_tos(), handle->conn, smb_fname, NULL, 0);
4993 if (dir_hnd == NULL) {
4994 return UINT64_MAX;
4997 while ((dname = ReadDirName(dir_hnd, &offset, NULL, &talloced))
4998 != NULL)
5000 ok = fruit_tmsize_do_dirent(handle, &state, dname);
5001 if (!ok) {
5002 TALLOC_FREE(talloced);
5003 TALLOC_FREE(dir_hnd);
5004 return UINT64_MAX;
5006 TALLOC_FREE(talloced);
5009 TALLOC_FREE(dir_hnd);
5011 dsize = config->time_machine_max_size / 512;
5012 dfree = dsize - (state.total_size / 512);
5013 if (dfree > dsize) {
5014 dfree = 0;
5017 *_bsize = 512;
5018 *_dsize = dsize;
5019 *_dfree = dfree;
5020 return dfree / 2;
5023 static uint64_t fruit_fs_file_id(struct vfs_handle_struct *handle,
5024 const SMB_STRUCT_STAT *psbuf)
5026 struct fruit_config_data *config = NULL;
5028 SMB_VFS_HANDLE_GET_DATA(handle, config,
5029 struct fruit_config_data,
5030 return 0);
5032 if (global_fruit_config.nego_aapl &&
5033 config->aapl_zero_file_id)
5035 return 0;
5038 return SMB_VFS_NEXT_FS_FILE_ID(handle, psbuf);
5041 static struct vfs_fn_pointers vfs_fruit_fns = {
5042 .connect_fn = fruit_connect,
5043 .disk_free_fn = fruit_disk_free,
5045 /* File operations */
5046 .chmod_fn = fruit_chmod,
5047 .unlinkat_fn = fruit_unlinkat,
5048 .renameat_fn = fruit_renameat,
5049 .openat_fn = fruit_openat,
5050 .close_fn = fruit_close,
5051 .pread_fn = fruit_pread,
5052 .pwrite_fn = fruit_pwrite,
5053 .pread_send_fn = fruit_pread_send,
5054 .pread_recv_fn = fruit_pread_recv,
5055 .pwrite_send_fn = fruit_pwrite_send,
5056 .pwrite_recv_fn = fruit_pwrite_recv,
5057 .stat_fn = fruit_stat,
5058 .lstat_fn = fruit_lstat,
5059 .fstat_fn = fruit_fstat,
5060 .streaminfo_fn = fruit_streaminfo,
5061 .ntimes_fn = fruit_ntimes,
5062 .ftruncate_fn = fruit_ftruncate,
5063 .fallocate_fn = fruit_fallocate,
5064 .create_file_fn = fruit_create_file,
5065 .readdir_attr_fn = fruit_readdir_attr,
5066 .offload_read_send_fn = fruit_offload_read_send,
5067 .offload_read_recv_fn = fruit_offload_read_recv,
5068 .offload_write_send_fn = fruit_offload_write_send,
5069 .offload_write_recv_fn = fruit_offload_write_recv,
5070 .fs_file_id_fn = fruit_fs_file_id,
5072 /* NT ACL operations */
5073 .fget_nt_acl_fn = fruit_fget_nt_acl,
5074 .fset_nt_acl_fn = fruit_fset_nt_acl,
5077 static_decl_vfs;
5078 NTSTATUS vfs_fruit_init(TALLOC_CTX *ctx)
5080 NTSTATUS ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "fruit",
5081 &vfs_fruit_fns);
5082 if (!NT_STATUS_IS_OK(ret)) {
5083 return ret;
5086 vfs_fruit_debug_level = debug_add_class("fruit");
5087 if (vfs_fruit_debug_level == -1) {
5088 vfs_fruit_debug_level = DBGC_VFS;
5089 DEBUG(0, ("%s: Couldn't register custom debugging class!\n",
5090 "vfs_fruit_init"));
5091 } else {
5092 DEBUG(10, ("%s: Debug class number of '%s': %d\n",
5093 "vfs_fruit_init","fruit",vfs_fruit_debug_level));
5096 return ret;