1 /*
2 * OS X and Netatalk interoperability VFS module for Samba-3.x
3 *
4 * Copyright (C) Ralph Boehme, 2013, 2014
5 *
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.
10 *
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.
15 *
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/>.
18 */
19
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"
38
39 /*
40 * Enhanced OS X and Netatalk compatibility
41 * ========================================
42 *
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:
46 *
47 * vfs modules = catia fruit streams_xattr
48 *
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.
52 *
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.
57 *
58 * Finally, open modes are optionally checked against Netatalk AFP
59 * share modes.
60 *
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.
67 *
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.
72 *
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".
76 *
77 *
78 * Extended Attributes
79 * ===================
80 *
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:
86 *
87 * streams_xattr:prefix = user.
88 * streams_xattr:store_stream_type = false
89 *
90 *
91 * TODO
92 * ====
93 *
94 * - log diagnostic if any needed VFS module is not loaded
95 * (eg with lp_vfs_objects())
96 * - add tests
97 */
98
99 static int vfs_fruit_debug_level = DBGC_VFS;
100
101 static struct global_fruit_config {
102 bool nego_aapl; /* client negotiated AAPL */
103
104 } global_fruit_config;
105
106 #undef DBGC_CLASS
107 #define DBGC_CLASS vfs_fruit_debug_level
108
109 #define FRUIT_PARAM_TYPE_NAME "fruit"
110
111 enum apple_fork {APPLE_FORK_DATA, APPLE_FORK_RSRC};
112
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};
117
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;
136
137 /*
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.
143 */
144 bool readdir_attr_rsize;
145 bool readdir_attr_finder_info;
146 bool readdir_attr_max_access;
147 };
148
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}
154 };
155
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}
160 };
161
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}
166 };
167
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}
172 };
173
174 struct fio {
175 /* tcon config handle */
176 struct fruit_config_data *config;
177
178 /* Denote stream type, meta or rsrc */
179 adouble_type_t type;
180
181 /*
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.
187 */
188 bool fake_fd;
189 int flags;
190 int mode;
191 };
192
193 /*****************************************************************************
194 * Helper functions
195 *****************************************************************************/
196
197 /**
198 * Initialize config struct from our smb.conf config parameters
199 **/
init_fruit_config(vfs_handle_struct * handle)200 static int init_fruit_config(vfs_handle_struct *handle)
201 {
202 struct fruit_config_data *config;
203 int enumval;
204 const char *tm_size_str = NULL;
205
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;
211 }
212
213 /*
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).
217 *
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.
220 */
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;
227 }
228 config->rsrc = (enum fruit_rsrc)enumval;
229
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;
236 }
237 config->rsrc = (enum fruit_rsrc)enumval;
238
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;
245 }
246 config->meta = (enum fruit_meta)enumval;
247
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;
254 }
255 config->locking = (enum fruit_locking)enumval;
256
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;
263 }
264 config->encoding = (enum fruit_encoding)enumval;
265
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);
271 }
272
273 config->use_aapl = lp_parm_bool(
274 -1, FRUIT_PARAM_TYPE_NAME, "aapl", true);
275
276 config->time_machine = lp_parm_bool(
277 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "time machine", false);
278
279 config->unix_info_enabled = lp_parm_bool(
280 -1, FRUIT_PARAM_TYPE_NAME, "nfs_aces", true);
281
282 config->use_copyfile = lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME,
283 "copyfile", false);
284
285 config->posix_rename = lp_parm_bool(
286 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "posix_rename", true);
287
288 config->aapl_zero_file_id =
289 lp_parm_bool(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
290 "zero_file_id", false);
291
292 config->readdir_attr_rsize = lp_parm_bool(
293 SNUM(handle->conn), "readdir_attr", "aapl_rsize", true);
294
295 config->readdir_attr_finder_info = lp_parm_bool(
296 SNUM(handle->conn), "readdir_attr", "aapl_finder_info", true);
297
298 config->readdir_attr_max_access = lp_parm_bool(
299 SNUM(handle->conn), "readdir_attr", "aapl_max_access", true);
300
301 config->model = lp_parm_const_string(
302 -1, FRUIT_PARAM_TYPE_NAME, "model", "MacSamba");
303
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);
309 }
310
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);
314
315 config->delete_empty_adfiles = lp_parm_bool(
316 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
317 "delete_empty_adfiles", false);
318
319 SMB_VFS_HANDLE_SET_DATA(handle, config,
320 NULL, struct fruit_config_data,
321 return -1);
322
323 return 0;
324 }
325
add_fruit_stream(TALLOC_CTX * mem_ctx,unsigned int * num_streams,struct stream_struct ** streams,const char * name,off_t size,off_t alloc_size)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)
330 {
331 struct stream_struct *tmp;
332
333 tmp = talloc_realloc(mem_ctx, *streams, struct stream_struct,
334 (*num_streams)+1);
335 if (tmp == NULL) {
336 return false;
337 }
338
339 tmp[*num_streams].name = talloc_asprintf(tmp, "%s:$DATA", name);
340 if (tmp[*num_streams].name == NULL) {
341 return false;
342 }
343
344 tmp[*num_streams].size = size;
345 tmp[*num_streams].alloc_size = alloc_size;
346
347 *streams = tmp;
348 *num_streams += 1;
349 return true;
350 }
351
filter_empty_rsrc_stream(unsigned int * num_streams,struct stream_struct ** streams)352 static bool filter_empty_rsrc_stream(unsigned int *num_streams,
353 struct stream_struct **streams)
354 {
355 struct stream_struct *tmp = *streams;
356 unsigned int i;
357
358 if (*num_streams == 0) {
359 return true;
360 }
361
362 for (i = 0; i < *num_streams; i++) {
363 if (strequal_m(tmp[i].name, AFPRESOURCE_STREAM)) {
364 break;
365 }
366 }
367
368 if (i == *num_streams) {
369 return true;
370 }
371
372 if (tmp[i].size > 0) {
373 return true;
374 }
375
376 TALLOC_FREE(tmp[i].name);
377 if (*num_streams - 1 > i) {
378 memmove(&tmp[i], &tmp[i+1],
379 (*num_streams - i - 1) * sizeof(struct stream_struct));
380 }
381
382 *num_streams -= 1;
383 return true;
384 }
385
del_fruit_stream(TALLOC_CTX * mem_ctx,unsigned int * num_streams,struct stream_struct ** streams,const char * name)386 static bool del_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
387 struct stream_struct **streams,
388 const char *name)
389 {
390 struct stream_struct *tmp = *streams;
391 unsigned int i;
392
393 if (*num_streams == 0) {
394 return true;
395 }
396
397 for (i = 0; i < *num_streams; i++) {
398 if (strequal_m(tmp[i].name, name)) {
399 break;
400 }
401 }
402
403 if (i == *num_streams) {
404 return true;
405 }
406
407 TALLOC_FREE(tmp[i].name);
408 if (*num_streams - 1 > i) {
409 memmove(&tmp[i], &tmp[i+1],
410 (*num_streams - i - 1) * sizeof(struct stream_struct));
411 }
412
413 *num_streams -= 1;
414 return true;
415 }
416
ad_empty_finderinfo(const struct adouble * ad)417 static bool ad_empty_finderinfo(const struct adouble *ad)
418 {
419 int cmp;
420 char emptybuf[ADEDLEN_FINDERI] = {0};
421 char *fi = NULL;
422
423 fi = ad_get_entry(ad, ADEID_FINDERI);
424 if (fi == NULL) {
425 DBG_ERR("Missing FinderInfo in struct adouble [%p]\n", ad);
426 return false;
427 }
428
429 cmp = memcmp(emptybuf, fi, ADEDLEN_FINDERI);
430 return (cmp == 0);
431 }
432
ai_empty_finderinfo(const AfpInfo * ai)433 static bool ai_empty_finderinfo(const AfpInfo *ai)
434 {
435 int cmp;
436 char emptybuf[ADEDLEN_FINDERI] = {0};
437
438 cmp = memcmp(emptybuf, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
439 return (cmp == 0);
440 }
441
442 /**
443 * Update btime with btime from Netatalk
444 **/
update_btime(vfs_handle_struct * handle,struct smb_filename * smb_fname)445 static void update_btime(vfs_handle_struct *handle,
446 struct smb_filename *smb_fname)
447 {
448 uint32_t t;
449 struct timespec creation_time = {0};
450 struct adouble *ad;
451 struct fruit_config_data *config = NULL;
452
453 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
454 return);
455
456 switch (config->meta) {
457 case FRUIT_META_STREAM:
458 return;
459 case FRUIT_META_NETATALK:
460 /* Handled below */
461 break;
462 default:
463 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
464 return;
465 }
466
467 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
468 if (ad == NULL) {
469 return;
470 }
471 if (ad_getdate(ad, AD_DATE_UNIX | AD_DATE_CREATE, &t) != 0) {
472 TALLOC_FREE(ad);
473 return;
474 }
475 TALLOC_FREE(ad);
476
477 creation_time.tv_sec = convert_uint32_t_to_time_t(t);
478 update_stat_ex_create_time(&smb_fname->st, creation_time);
479
480 return;
481 }
482
483 /**
484 * Map an access mask to a Netatalk single byte byte range lock
485 **/
access_to_netatalk_brl(enum apple_fork fork_type,uint32_t access_mask)486 static off_t access_to_netatalk_brl(enum apple_fork fork_type,
487 uint32_t access_mask)
488 {
489 off_t offset;
490
491 switch (access_mask) {
492 case FILE_READ_DATA:
493 offset = AD_FILELOCK_OPEN_RD;
494 break;
495
496 case FILE_WRITE_DATA:
497 case FILE_APPEND_DATA:
498 offset = AD_FILELOCK_OPEN_WR;
499 break;
500
501 default:
502 offset = AD_FILELOCK_OPEN_NONE;
503 break;
504 }
505
506 if (fork_type == APPLE_FORK_RSRC) {
507 if (offset == AD_FILELOCK_OPEN_NONE) {
508 offset = AD_FILELOCK_RSRC_OPEN_NONE;
509 } else {
510 offset += 2;
511 }
512 }
513
514 return offset;
515 }
516
517 /**
518 * Map a deny mode to a Netatalk brl
519 **/
denymode_to_netatalk_brl(enum apple_fork fork_type,uint32_t deny_mode)520 static off_t denymode_to_netatalk_brl(enum apple_fork fork_type,
521 uint32_t deny_mode)
522 {
523 off_t offset = 0;
524
525 switch (deny_mode) {
526 case DENY_READ:
527 offset = AD_FILELOCK_DENY_RD;
528 break;
529
530 case DENY_WRITE:
531 offset = AD_FILELOCK_DENY_WR;
532 break;
533
534 default:
535 smb_panic("denymode_to_netatalk_brl: bad deny mode\n");
536 }
537
538 if (fork_type == APPLE_FORK_RSRC) {
539 offset += 2;
540 }
541
542 return offset;
543 }
544
545 /**
546 * Call fcntl() with an exclusive F_GETLK request in order to
547 * determine if there's an existing shared lock
548 *
549 * @return true if the requested lock was found or any error occurred
550 * false if the lock was not found
551 **/
test_netatalk_lock(files_struct * fsp,off_t in_offset)552 static bool test_netatalk_lock(files_struct *fsp, off_t in_offset)
553 {
554 bool result;
555 off_t offset = in_offset;
556 off_t len = 1;
557 int type = F_WRLCK;
558 pid_t pid = 0;
559
560 result = SMB_VFS_GETLOCK(fsp, &offset, &len, &type, &pid);
561 if (result == false) {
562 return true;
563 }
564
565 if (type != F_UNLCK) {
566 return true;
567 }
568
569 return false;
570 }
571
fruit_check_access(vfs_handle_struct * handle,files_struct * fsp,uint32_t access_mask,uint32_t share_mode)572 static NTSTATUS fruit_check_access(vfs_handle_struct *handle,
573 files_struct *fsp,
574 uint32_t access_mask,
575 uint32_t share_mode)
576 {
577 NTSTATUS status = NT_STATUS_OK;
578 off_t off;
579 bool share_for_read = (share_mode & FILE_SHARE_READ);
580 bool share_for_write = (share_mode & FILE_SHARE_WRITE);
581 bool netatalk_already_open_for_reading = false;
582 bool netatalk_already_open_for_writing = false;
583 bool netatalk_already_open_with_deny_read = false;
584 bool netatalk_already_open_with_deny_write = false;
585 struct GUID req_guid = GUID_random();
586
587 /* FIXME: hardcoded data fork, add resource fork */
588 enum apple_fork fork_type = APPLE_FORK_DATA;
589
590 DBG_DEBUG("fruit_check_access: %s, am: %s/%s, sm: 0x%x\n",
591 fsp_str_dbg(fsp),
592 access_mask & FILE_READ_DATA ? "READ" :"-",
593 access_mask & FILE_WRITE_DATA ? "WRITE" : "-",
594 share_mode);
595
596 if (fsp->fh->fd == -1) {
597 return NT_STATUS_OK;
598 }
599
600 /* Read NetATalk opens and deny modes on the file. */
601 netatalk_already_open_for_reading = test_netatalk_lock(fsp,
602 access_to_netatalk_brl(fork_type,
603 FILE_READ_DATA));
604
605 netatalk_already_open_with_deny_read = test_netatalk_lock(fsp,
606 denymode_to_netatalk_brl(fork_type,
607 DENY_READ));
608
609 netatalk_already_open_for_writing = test_netatalk_lock(fsp,
610 access_to_netatalk_brl(fork_type,
611 FILE_WRITE_DATA));
612
613 netatalk_already_open_with_deny_write = test_netatalk_lock(fsp,
614 denymode_to_netatalk_brl(fork_type,
615 DENY_WRITE));
616
617 /* If there are any conflicts - sharing violation. */
618 if ((access_mask & FILE_READ_DATA) &&
619 netatalk_already_open_with_deny_read) {
620 return NT_STATUS_SHARING_VIOLATION;
621 }
622
623 if (!share_for_read &&
624 netatalk_already_open_for_reading) {
625 return NT_STATUS_SHARING_VIOLATION;
626 }
627
628 if ((access_mask & FILE_WRITE_DATA) &&
629 netatalk_already_open_with_deny_write) {
630 return NT_STATUS_SHARING_VIOLATION;
631 }
632
633 if (!share_for_write &&
634 netatalk_already_open_for_writing) {
635 return NT_STATUS_SHARING_VIOLATION;
636 }
637
638 if (!(access_mask & FILE_READ_DATA)) {
639 /*
640 * Nothing we can do here, we need read access
641 * to set locks.
642 */
643 return NT_STATUS_OK;
644 }
645
646 /* Set NetAtalk locks matching our access */
647 if (access_mask & FILE_READ_DATA) {
648 off = access_to_netatalk_brl(fork_type, FILE_READ_DATA);
649 req_guid.time_hi_and_version = __LINE__;
650 status = do_lock(
651 fsp,
652 talloc_tos(),
653 &req_guid,
654 fsp->op->global->open_persistent_id,
655 1,
656 off,
657 READ_LOCK,
658 POSIX_LOCK,
659 NULL,
660 NULL);
661
662 if (!NT_STATUS_IS_OK(status)) {
663 return status;
664 }
665 }
666
667 if (!share_for_read) {
668 off = denymode_to_netatalk_brl(fork_type, DENY_READ);
669 req_guid.time_hi_and_version = __LINE__;
670 status = do_lock(
671 fsp,
672 talloc_tos(),
673 &req_guid,
674 fsp->op->global->open_persistent_id,
675 1,
676 off,
677 READ_LOCK,
678 POSIX_LOCK,
679 NULL,
680 NULL);
681
682 if (!NT_STATUS_IS_OK(status)) {
683 return status;
684 }
685 }
686
687 if (access_mask & FILE_WRITE_DATA) {
688 off = access_to_netatalk_brl(fork_type, FILE_WRITE_DATA);
689 req_guid.time_hi_and_version = __LINE__;
690 status = do_lock(
691 fsp,
692 talloc_tos(),
693 &req_guid,
694 fsp->op->global->open_persistent_id,
695 1,
696 off,
697 READ_LOCK,
698 POSIX_LOCK,
699 NULL,
700 NULL);
701
702 if (!NT_STATUS_IS_OK(status)) {
703 return status;
704 }
705 }
706
707 if (!share_for_write) {
708 off = denymode_to_netatalk_brl(fork_type, DENY_WRITE);
709 req_guid.time_hi_and_version = __LINE__;
710 status = do_lock(
711 fsp,
712 talloc_tos(),
713 &req_guid,
714 fsp->op->global->open_persistent_id,
715 1,
716 off,
717 READ_LOCK,
718 POSIX_LOCK,
719 NULL,
720 NULL);
721
722 if (!NT_STATUS_IS_OK(status)) {
723 return status;
724 }
725 }
726
727 return NT_STATUS_OK;
728 }
729
check_aapl(vfs_handle_struct * handle,struct smb_request * req,const struct smb2_create_blobs * in_context_blobs,struct smb2_create_blobs * out_context_blobs)730 static NTSTATUS check_aapl(vfs_handle_struct *handle,
731 struct smb_request *req,
732 const struct smb2_create_blobs *in_context_blobs,
733 struct smb2_create_blobs *out_context_blobs)
734 {
735 struct fruit_config_data *config;
736 NTSTATUS status;
737 struct smb2_create_blob *aapl = NULL;
738 uint32_t cmd;
739 bool ok;
740 uint8_t p[16];
741 DATA_BLOB blob = data_blob_talloc(req, NULL, 0);
742 uint64_t req_bitmap, client_caps;
743 uint64_t server_caps = SMB2_CRTCTX_AAPL_UNIX_BASED;
744 smb_ucs2_t *model;
745 size_t modellen;
746
747 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
748 return NT_STATUS_UNSUCCESSFUL);
749
750 if (!config->use_aapl
751 || in_context_blobs == NULL
752 || out_context_blobs == NULL) {
753 return NT_STATUS_OK;
754 }
755
756 aapl = smb2_create_blob_find(in_context_blobs,
757 SMB2_CREATE_TAG_AAPL);
758 if (aapl == NULL) {
759 return NT_STATUS_OK;
760 }
761
762 if (aapl->data.length != 24) {
763 DEBUG(1, ("unexpected AAPL ctxt length: %ju\n",
764 (uintmax_t)aapl->data.length));
765 return NT_STATUS_INVALID_PARAMETER;
766 }
767
768 cmd = IVAL(aapl->data.data, 0);
769 if (cmd != SMB2_CRTCTX_AAPL_SERVER_QUERY) {
770 DEBUG(1, ("unsupported AAPL cmd: %d\n", cmd));
771 return NT_STATUS_INVALID_PARAMETER;
772 }
773
774 req_bitmap = BVAL(aapl->data.data, 8);
775 client_caps = BVAL(aapl->data.data, 16);
776
777 SIVAL(p, 0, SMB2_CRTCTX_AAPL_SERVER_QUERY);
778 SIVAL(p, 4, 0);
779 SBVAL(p, 8, req_bitmap);
780 ok = data_blob_append(req, &blob, p, 16);
781 if (!ok) {
782 return NT_STATUS_UNSUCCESSFUL;
783 }
784
785 if (req_bitmap & SMB2_CRTCTX_AAPL_SERVER_CAPS) {
786 if ((client_caps & SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR) &&
787 (handle->conn->tcon->compat->fs_capabilities & FILE_NAMED_STREAMS)) {
788 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR;
789 config->readdir_attr_enabled = true;
790 }
791
792 if (config->use_copyfile) {
793 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_OSX_COPYFILE;
794 config->copyfile_enabled = true;
795 }
796
797 /*
798 * The client doesn't set the flag, so we can't check
799 * for it and just set it unconditionally
800 */
801 if (config->unix_info_enabled) {
802 server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_NFS_ACE;
803 }
804
805 SBVAL(p, 0, server_caps);
806 ok = data_blob_append(req, &blob, p, 8);
807 if (!ok) {
808 return NT_STATUS_UNSUCCESSFUL;
809 }
810 }
811
812 if (req_bitmap & SMB2_CRTCTX_AAPL_VOLUME_CAPS) {
813 int val = lp_case_sensitive(SNUM(handle->conn->tcon->compat));
814 uint64_t caps = 0;
815
816 switch (val) {
817 case Auto:
818 break;
819
820 case True:
821 caps |= SMB2_CRTCTX_AAPL_CASE_SENSITIVE;
822 break;
823
824 default:
825 break;
826 }
827
828 if (config->time_machine) {
829 caps |= SMB2_CRTCTX_AAPL_FULL_SYNC;
830 }
831
832 SBVAL(p, 0, caps);
833
834 ok = data_blob_append(req, &blob, p, 8);
835 if (!ok) {
836 return NT_STATUS_UNSUCCESSFUL;
837 }
838 }
839
840 if (req_bitmap & SMB2_CRTCTX_AAPL_MODEL_INFO) {
841 ok = convert_string_talloc(req,
842 CH_UNIX, CH_UTF16LE,
843 config->model, strlen(config->model),
844 &model, &modellen);
845 if (!ok) {
846 return NT_STATUS_UNSUCCESSFUL;
847 }
848
849 SIVAL(p, 0, 0);
850 SIVAL(p + 4, 0, modellen);
851 ok = data_blob_append(req, &blob, p, 8);
852 if (!ok) {
853 talloc_free(model);
854 return NT_STATUS_UNSUCCESSFUL;
855 }
856
857 ok = data_blob_append(req, &blob, model, modellen);
858 talloc_free(model);
859 if (!ok) {
860 return NT_STATUS_UNSUCCESSFUL;
861 }
862 }
863
864 status = smb2_create_blob_add(out_context_blobs,
865 out_context_blobs,
866 SMB2_CREATE_TAG_AAPL,
867 blob);
868 if (NT_STATUS_IS_OK(status)) {
869 global_fruit_config.nego_aapl = true;
870 }
871
872 return status;
873 }
874
readdir_attr_meta_finderi_stream(struct vfs_handle_struct * handle,const struct smb_filename * smb_fname,AfpInfo * ai)875 static bool readdir_attr_meta_finderi_stream(
876 struct vfs_handle_struct *handle,
877 const struct smb_filename *smb_fname,
878 AfpInfo *ai)
879 {
880 struct smb_filename *stream_name = NULL;
881 files_struct *fsp = NULL;
882 ssize_t nread;
883 NTSTATUS status;
884 int ret;
885 bool ok;
886 uint8_t buf[AFP_INFO_SIZE];
887
888 stream_name = synthetic_smb_fname(talloc_tos(),
889 smb_fname->base_name,
890 AFPINFO_STREAM_NAME,
891 NULL, smb_fname->flags);
892 if (stream_name == NULL) {
893 return false;
894 }
895
896 ret = SMB_VFS_STAT(handle->conn, stream_name);
897 if (ret != 0) {
898 return false;
899 }
900
901 status = SMB_VFS_CREATE_FILE(
902 handle->conn, /* conn */
903 NULL, /* req */
904 0, /* root_dir_fid */
905 stream_name, /* fname */
906 FILE_READ_DATA, /* access_mask */
907 (FILE_SHARE_READ | FILE_SHARE_WRITE | /* share_access */
908 FILE_SHARE_DELETE),
909 FILE_OPEN, /* create_disposition*/
910 0, /* create_options */
911 0, /* file_attributes */
912 INTERNAL_OPEN_ONLY, /* oplock_request */
913 NULL, /* lease */
914 0, /* allocation_size */
915 0, /* private_flags */
916 NULL, /* sd */
917 NULL, /* ea_list */
918 &fsp, /* result */
919 NULL, /* pinfo */
920 NULL, NULL); /* create context */
921
922 TALLOC_FREE(stream_name);
923
924 if (!NT_STATUS_IS_OK(status)) {
925 return false;
926 }
927
928 nread = SMB_VFS_PREAD(fsp, &buf[0], AFP_INFO_SIZE, 0);
929 if (nread != AFP_INFO_SIZE) {
930 DBG_ERR("short read [%s] [%zd/%d]\n",
931 smb_fname_str_dbg(stream_name), nread, AFP_INFO_SIZE);
932 ok = false;
933 goto fail;
934 }
935
936 memcpy(&ai->afpi_FinderInfo[0], &buf[AFP_OFF_FinderInfo],
937 AFP_FinderSize);
938
939 ok = true;
940
941 fail:
942 if (fsp != NULL) {
943 close_file(NULL, fsp, NORMAL_CLOSE);
944 }
945
946 return ok;
947 }
948
readdir_attr_meta_finderi_netatalk(struct vfs_handle_struct * handle,const struct smb_filename * smb_fname,AfpInfo * ai)949 static bool readdir_attr_meta_finderi_netatalk(
950 struct vfs_handle_struct *handle,
951 const struct smb_filename *smb_fname,
952 AfpInfo *ai)
953 {
954 struct adouble *ad = NULL;
955 char *p = NULL;
956
957 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
958 if (ad == NULL) {
959 return false;
960 }
961
962 p = ad_get_entry(ad, ADEID_FINDERI);
963 if (p == NULL) {
964 DBG_ERR("No ADEID_FINDERI for [%s]\n", smb_fname->base_name);
965 TALLOC_FREE(ad);
966 return false;
967 }
968
969 memcpy(&ai->afpi_FinderInfo[0], p, AFP_FinderSize);
970 TALLOC_FREE(ad);
971 return true;
972 }
973
readdir_attr_meta_finderi(struct vfs_handle_struct * handle,const struct smb_filename * smb_fname,struct readdir_attr_data * attr_data)974 static bool readdir_attr_meta_finderi(struct vfs_handle_struct *handle,
975 const struct smb_filename *smb_fname,
976 struct readdir_attr_data *attr_data)
977 {
978 struct fruit_config_data *config = NULL;
979 uint32_t date_added;
980 AfpInfo ai = {0};
981 bool ok;
982
983 SMB_VFS_HANDLE_GET_DATA(handle, config,
984 struct fruit_config_data,
985 return false);
986
987 switch (config->meta) {
988 case FRUIT_META_NETATALK:
989 ok = readdir_attr_meta_finderi_netatalk(
990 handle, smb_fname, &ai);
991 break;
992
993 case FRUIT_META_STREAM:
994 ok = readdir_attr_meta_finderi_stream(
995 handle, smb_fname, &ai);
996 break;
997
998 default:
999 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
1000 return false;
1001 }
1002
1003 if (!ok) {
1004 /* Don't bother with errors, it's likely ENOENT */
1005 return true;
1006 }
1007
1008 if (S_ISREG(smb_fname->st.st_ex_mode)) {
1009 /* finder_type */
1010 memcpy(&attr_data->attr_data.aapl.finder_info[0],
1011 &ai.afpi_FinderInfo[0], 4);
1012
1013 /* finder_creator */
1014 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 4,
1015 &ai.afpi_FinderInfo[4], 4);
1016 }
1017
1018 /* finder_flags */
1019 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 8,
1020 &ai.afpi_FinderInfo[8], 2);
1021
1022 /* finder_ext_flags */
1023 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 10,
1024 &ai.afpi_FinderInfo[24], 2);
1025
1026 /* creation date */
1027 date_added = convert_time_t_to_uint32_t(
1028 smb_fname->st.st_ex_btime.tv_sec - AD_DATE_DELTA);
1029
1030 RSIVAL(&attr_data->attr_data.aapl.finder_info[0], 12, date_added);
1031
1032 return true;
1033 }
1034
readdir_attr_rfork_size_adouble(struct vfs_handle_struct * handle,const struct smb_filename * smb_fname)1035 static uint64_t readdir_attr_rfork_size_adouble(
1036 struct vfs_handle_struct *handle,
1037 const struct smb_filename *smb_fname)
1038 {
1039 struct adouble *ad = NULL;
1040 uint64_t rfork_size;
1041
1042 ad = ad_get(talloc_tos(), handle, smb_fname,
1043 ADOUBLE_RSRC);
1044 if (ad == NULL) {
1045 return 0;
1046 }
1047
1048 rfork_size = ad_getentrylen(ad, ADEID_RFORK);
1049 TALLOC_FREE(ad);
1050
1051 return rfork_size;
1052 }
1053
readdir_attr_rfork_size_stream(struct vfs_handle_struct * handle,const struct smb_filename * smb_fname)1054 static uint64_t readdir_attr_rfork_size_stream(
1055 struct vfs_handle_struct *handle,
1056 const struct smb_filename *smb_fname)
1057 {
1058 struct smb_filename *stream_name = NULL;
1059 int ret;
1060 uint64_t rfork_size;
1061
1062 stream_name = synthetic_smb_fname(talloc_tos(),
1063 smb_fname->base_name,
1064 AFPRESOURCE_STREAM_NAME,
1065 NULL, 0);
1066 if (stream_name == NULL) {
1067 return 0;
1068 }
1069
1070 ret = SMB_VFS_STAT(handle->conn, stream_name);
1071 if (ret != 0) {
1072 TALLOC_FREE(stream_name);
1073 return 0;
1074 }
1075
1076 rfork_size = stream_name->st.st_ex_size;
1077 TALLOC_FREE(stream_name);
1078
1079 return rfork_size;
1080 }
1081
readdir_attr_rfork_size(struct vfs_handle_struct * handle,const struct smb_filename * smb_fname)1082 static uint64_t readdir_attr_rfork_size(struct vfs_handle_struct *handle,
1083 const struct smb_filename *smb_fname)
1084 {
1085 struct fruit_config_data *config = NULL;
1086 uint64_t rfork_size;
1087
1088 SMB_VFS_HANDLE_GET_DATA(handle, config,
1089 struct fruit_config_data,
1090 return 0);
1091
1092 switch (config->rsrc) {
1093 case FRUIT_RSRC_ADFILE:
1094 rfork_size = readdir_attr_rfork_size_adouble(handle,
1095 smb_fname);
1096 break;
1097
1098 case FRUIT_RSRC_XATTR:
1099 case FRUIT_RSRC_STREAM:
1100 rfork_size = readdir_attr_rfork_size_stream(handle,
1101 smb_fname);
1102 break;
1103
1104 default:
1105 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
1106 rfork_size = 0;
1107 break;
1108 }
1109
1110 return rfork_size;
1111 }
1112
readdir_attr_macmeta(struct vfs_handle_struct * handle,const struct smb_filename * smb_fname,struct readdir_attr_data * attr_data)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)
1116 {
1117 NTSTATUS status = NT_STATUS_OK;
1118 struct fruit_config_data *config = NULL;
1119 bool ok;
1120
1121 SMB_VFS_HANDLE_GET_DATA(handle, config,
1122 struct fruit_config_data,
1123 return NT_STATUS_UNSUCCESSFUL);
1124
1125
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);
1128
1129 /*
1130 * Resource fork length
1131 */
1132
1133 if (config->readdir_attr_rsize) {
1134 uint64_t rfork_size;
1135
1136 rfork_size = readdir_attr_rfork_size(handle, smb_fname);
1137 attr_data->attr_data.aapl.rfork_size = rfork_size;
1138 }
1139
1140 /*
1141 * FinderInfo
1142 */
1143
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;
1148 }
1149 }
1150
1151 return status;
1152 }
1153
remove_virtual_nfs_aces(struct security_descriptor * psd)1154 static NTSTATUS remove_virtual_nfs_aces(struct security_descriptor *psd)
1155 {
1156 NTSTATUS status;
1157 uint32_t i;
1158
1159 if (psd->dacl == NULL) {
1160 return NT_STATUS_OK;
1161 }
1162
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;
1171 }
1172
1173 /*
1174 * security_descriptor_dacl_del()
1175 * *must* return NT_STATUS_OK as we know
1176 * we have something to remove.
1177 */
1178
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;
1185 }
1186
1187 /*
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.
1192 */
1193 i--;
1194 }
1195 return NT_STATUS_OK;
1196 }
1197
1198 /* Search MS NFS style ACE with UNIX mode */
check_ms_nfs(vfs_handle_struct * handle,files_struct * fsp,struct security_descriptor * psd,mode_t * pmode,bool * pdo_chmod)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)
1204 {
1205 uint32_t i;
1206 struct fruit_config_data *config = NULL;
1207
1208 *pdo_chmod = false;
1209
1210 SMB_VFS_HANDLE_GET_DATA(handle, config,
1211 struct fruit_config_data,
1212 return NT_STATUS_UNSUCCESSFUL);
1213
1214 if (!global_fruit_config.nego_aapl) {
1215 return NT_STATUS_OK;
1216 }
1217 if (psd->dacl == NULL || !config->unix_info_enabled) {
1218 return NT_STATUS_OK;
1219 }
1220
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;
1228
1229 DEBUG(10, ("MS NFS chmod request %s, %04o\n",
1230 fsp_str_dbg(fsp), (unsigned)(*pmode)));
1231 break;
1232 }
1233 }
1234
1235 /*
1236 * Remove any incoming virtual ACE entries generated by
1237 * fruit_fget_nt_acl().
1238 */
1239
1240 return remove_virtual_nfs_aces(psd);
1241 }
1242
1243 /****************************************************************************
1244 * VFS ops
1245 ****************************************************************************/
1246
fruit_connect(vfs_handle_struct * handle,const char * service,const char * user)1247 static int fruit_connect(vfs_handle_struct *handle,
1248 const char *service,
1249 const char *user)
1250 {
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();
1256
1257 DEBUG(10, ("fruit_connect\n"));
1258
1259 rc = SMB_VFS_NEXT_CONNECT(handle, service, user);
1260 if (rc < 0) {
1261 return rc;
1262 }
1263
1264 rc = init_fruit_config(handle);
1265 if (rc != 0) {
1266 return rc;
1267 }
1268
1269 SMB_VFS_HANDLE_GET_DATA(handle, config,
1270 struct fruit_config_data, return -1);
1271
1272 if (config->veto_appledouble) {
1273 list = lp_veto_files(talloc_tos(), lp_sub, SNUM(handle->conn));
1274
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);
1284 }
1285 } else {
1286 lp_do_parameter(SNUM(handle->conn),
1287 "veto files",
1288 "/" ADOUBLE_NAME_PREFIX "*/");
1289 }
1290
1291 TALLOC_FREE(list);
1292 }
1293
1294 if (config->encoding == FRUIT_ENC_NATIVE) {
1295 lp_do_parameter(SNUM(handle->conn),
1296 "catia:mappings",
1297 macos_string_replace_map);
1298 }
1299
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");
1309 }
1310 lp_do_parameter(SNUM(handle->conn), "posix locking", "no");
1311 }
1312
1313 return rc;
1314 }
1315
fruit_fake_fd(void)1316 static int fruit_fake_fd(void)
1317 {
1318 int pipe_fds[2];
1319 int fd;
1320 int ret;
1321
1322 /*
1323 * Return a valid fd, but ensure any attempt to use it returns
1324 * an error (EPIPE). Once we get a write on the handle, we open
1325 * the real fd.
1326 */
1327 ret = pipe(pipe_fds);
1328 if (ret != 0) {
1329 return -1;
1330 }
1331 fd = pipe_fds[0];
1332 close(pipe_fds[1]);
1333
1334 return fd;
1335 }
1336
fruit_open_meta_stream(vfs_handle_struct * handle,struct smb_filename * smb_fname,files_struct * fsp,int flags,mode_t mode)1337 static int fruit_open_meta_stream(vfs_handle_struct *handle,
1338 struct smb_filename *smb_fname,
1339 files_struct *fsp,
1340 int flags,
1341 mode_t mode)
1342 {
1343 struct fruit_config_data *config = NULL;
1344 struct fio *fio = NULL;
1345 int open_flags = flags & ~O_CREAT;
1346 int fd;
1347
1348 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
1349
1350 SMB_VFS_HANDLE_GET_DATA(handle, config,
1351 struct fruit_config_data, return -1);
1352
1353 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
1354 fio->type = ADOUBLE_META;
1355 fio->config = config;
1356
1357 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, open_flags, mode);
1358 if (fd != -1) {
1359 return fd;
1360 }
1361
1362 if (!(flags & O_CREAT)) {
1363 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
1364 return -1;
1365 }
1366
1367 fd = fruit_fake_fd();
1368 if (fd == -1) {
1369 VFS_REMOVE_FSP_EXTENSION(handle, fsp);
1370 return -1;
1371 }
1372
1373 fio->fake_fd = true;
1374 fio->flags = flags;
1375 fio->mode = mode;
1376
1377 return fd;
1378 }
1379
fruit_open_meta_netatalk(vfs_handle_struct * handle,struct smb_filename * smb_fname,files_struct * fsp,int flags,mode_t mode)1380 static int fruit_open_meta_netatalk(vfs_handle_struct *handle,
1381 struct smb_filename *smb_fname,
1382 files_struct *fsp,
1383 int flags,
1384 mode_t mode)
1385 {
1386 struct fruit_config_data *config = NULL;
1387 struct fio *fio = NULL;
1388 struct adouble *ad = NULL;
1389 bool meta_exists = false;
1390 int fd;
1391
1392 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
1393
1394 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
1395 if (ad != NULL) {
1396 meta_exists = true;
1397 }
1398
1399 TALLOC_FREE(ad);
1400
1401 if (!meta_exists && !(flags & O_CREAT)) {
1402 errno = ENOENT;
1403 return -1;
1404 }
1405
1406 fd = fruit_fake_fd();
1407 if (fd == -1) {
1408 return -1;
1409 }
1410
1411 SMB_VFS_HANDLE_GET_DATA(handle, config,
1412 struct fruit_config_data, return -1);
1413
1414 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
1415 fio->type = ADOUBLE_META;
1416 fio->config = config;
1417 fio->fake_fd = true;
1418 fio->flags = flags;
1419 fio->mode = mode;
1420
1421 return fd;
1422 }
1423
fruit_open_meta(vfs_handle_struct * handle,struct smb_filename * smb_fname,files_struct * fsp,int flags,mode_t mode)1424 static int fruit_open_meta(vfs_handle_struct *handle,
1425 struct smb_filename *smb_fname,
1426 files_struct *fsp, int flags, mode_t mode)
1427 {
1428 int fd;
1429 struct fruit_config_data *config = NULL;
1430
1431 DBG_DEBUG("path [%s]\n", smb_fname_str_dbg(smb_fname));
1432
1433 SMB_VFS_HANDLE_GET_DATA(handle, config,
1434 struct fruit_config_data, return -1);
1435
1436 switch (config->meta) {
1437 case FRUIT_META_STREAM:
1438 fd = fruit_open_meta_stream(handle, smb_fname,
1439 fsp, flags, mode);
1440 break;
1441
1442 case FRUIT_META_NETATALK:
1443 fd = fruit_open_meta_netatalk(handle, smb_fname,
1444 fsp, flags, mode);
1445 break;
1446
1447 default:
1448 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
1449 return -1;
1450 }
1451
1452 DBG_DEBUG("path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
1453
1454 return fd;
1455 }
1456
fruit_open_rsrc_adouble(vfs_handle_struct * handle,struct smb_filename * smb_fname,files_struct * fsp,int flags,mode_t mode)1457 static int fruit_open_rsrc_adouble(vfs_handle_struct *handle,
1458 struct smb_filename *smb_fname,
1459 files_struct *fsp,
1460 int flags,
1461 mode_t mode)
1462 {
1463 int rc = 0;
1464 struct adouble *ad = NULL;
1465 struct smb_filename *smb_fname_base = NULL;
1466 struct fruit_config_data *config = NULL;
1467 int hostfd = -1;
1468
1469 SMB_VFS_HANDLE_GET_DATA(handle, config,
1470 struct fruit_config_data, return -1);
1471
1472 if ((!(flags & O_CREAT)) &&
1473 S_ISDIR(fsp->base_fsp->fsp_name->st.st_ex_mode))
1474 {
1475 /* sorry, but directories don't habe a resource fork */
1476 rc = -1;
1477 goto exit;
1478 }
1479
1480 rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_base);
1481 if (rc != 0) {
1482 goto exit;
1483 }
1484
1485 /* We always need read/write access for the metadata header too */
1486 flags &= ~(O_RDONLY | O_WRONLY);
1487 flags |= O_RDWR;
1488
1489 hostfd = SMB_VFS_NEXT_OPEN(handle, smb_fname_base, fsp,
1490 flags, mode);
1491 if (hostfd == -1) {
1492 rc = -1;
1493 goto exit;
1494 }
1495
1496 if (flags & (O_CREAT | O_TRUNC)) {
1497 ad = ad_init(fsp, ADOUBLE_RSRC);
1498 if (ad == NULL) {
1499 rc = -1;
1500 goto exit;
1501 }
1502
1503 fsp->fh->fd = hostfd;
1504
1505 rc = ad_fset(handle, ad, fsp);
1506 fsp->fh->fd = -1;
1507 if (rc != 0) {
1508 rc = -1;
1509 goto exit;
1510 }
1511 TALLOC_FREE(ad);
1512 }
1513
1514 exit:
1515
1516 TALLOC_FREE(smb_fname_base);
1517
1518 DEBUG(10, ("fruit_open resource fork: rc=%d, fd=%d\n", rc, hostfd));
1519 if (rc != 0) {
1520 int saved_errno = errno;
1521 if (hostfd >= 0) {
1522 /*
1523 * BUGBUGBUG -- we would need to call
1524 * fd_close_posix here, but we don't have a
1525 * full fsp yet
1526 */
1527 fsp->fh->fd = hostfd;
1528 SMB_VFS_CLOSE(fsp);
1529 }
1530 hostfd = -1;
1531 errno = saved_errno;
1532 }
1533 return hostfd;
1534 }
1535
fruit_open_rsrc_xattr(vfs_handle_struct * handle,struct smb_filename * smb_fname,files_struct * fsp,int flags,mode_t mode)1536 static int fruit_open_rsrc_xattr(vfs_handle_struct *handle,
1537 struct smb_filename *smb_fname,
1538 files_struct *fsp,
1539 int flags,
1540 mode_t mode)
1541 {
1542 #ifdef HAVE_ATTROPEN
1543 int fd = -1;
1544
1545 fd = attropen(smb_fname->base_name,
1546 AFPRESOURCE_EA_NETATALK,
1547 flags,
1548 mode);
1549 if (fd == -1) {
1550 return -1;
1551 }
1552
1553 return fd;
1554
1555 #else
1556 errno = ENOSYS;
1557 return -1;
1558 #endif
1559 }
1560
fruit_open_rsrc(vfs_handle_struct * handle,struct smb_filename * smb_fname,files_struct * fsp,int flags,mode_t mode)1561 static int fruit_open_rsrc(vfs_handle_struct *handle,
1562 struct smb_filename *smb_fname,
1563 files_struct *fsp, int flags, mode_t mode)
1564 {
1565 int fd;
1566 struct fruit_config_data *config = NULL;
1567 struct fio *fio = NULL;
1568
1569 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
1570
1571 SMB_VFS_HANDLE_GET_DATA(handle, config,
1572 struct fruit_config_data, return -1);
1573
1574 switch (config->rsrc) {
1575 case FRUIT_RSRC_STREAM:
1576 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
1577 break;
1578
1579 case FRUIT_RSRC_ADFILE:
1580 fd = fruit_open_rsrc_adouble(handle, smb_fname,
1581 fsp, flags, mode);
1582 break;
1583
1584 case FRUIT_RSRC_XATTR:
1585 fd = fruit_open_rsrc_xattr(handle, smb_fname,
1586 fsp, flags, mode);
1587 break;
1588
1589 default:
1590 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
1591 return -1;
1592 }
1593
1594 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
1595
1596 if (fd == -1) {
1597 return -1;
1598 }
1599
1600 fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
1601 fio->type = ADOUBLE_RSRC;
1602 fio->config = config;
1603
1604 return fd;
1605 }
1606
fruit_open(vfs_handle_struct * handle,struct smb_filename * smb_fname,files_struct * fsp,int flags,mode_t mode)1607 static int fruit_open(vfs_handle_struct *handle,
1608 struct smb_filename *smb_fname,
1609 files_struct *fsp, int flags, mode_t mode)
1610 {
1611 int fd;
1612
1613 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
1614
1615 if (!is_named_stream(smb_fname)) {
1616 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
1617 }
1618
1619 if (is_afpinfo_stream(smb_fname->stream_name)) {
1620 fd = fruit_open_meta(handle, smb_fname, fsp, flags, mode);
1621 } else if (is_afpresource_stream(smb_fname->stream_name)) {
1622 fd = fruit_open_rsrc(handle, smb_fname, fsp, flags, mode);
1623 } else {
1624 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
1625 }
1626
1627 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
1628
1629 return fd;
1630 }
1631
fruit_close_meta(vfs_handle_struct * handle,files_struct * fsp)1632 static int fruit_close_meta(vfs_handle_struct *handle,
1633 files_struct *fsp)
1634 {
1635 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
1636 int ret;
1637 struct fruit_config_data *config = NULL;
1638
1639 SMB_VFS_HANDLE_GET_DATA(handle, config,
1640 struct fruit_config_data, return -1);
1641
1642 switch (config->meta) {
1643 case FRUIT_META_STREAM:
1644 if (fio->fake_fd) {
1645 ret = vfs_fake_fd_close(fsp->fh->fd);
1646 fsp->fh->fd = -1;
1647 } else {
1648 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
1649 }
1650 break;
1651
1652 case FRUIT_META_NETATALK:
1653 ret = vfs_fake_fd_close(fsp->fh->fd);
1654 fsp->fh->fd = -1;
1655 break;
1656
1657 default:
1658 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
1659 return -1;
1660 }
1661
1662 return ret;
1663 }
1664
1665
fruit_close_rsrc(vfs_handle_struct * handle,files_struct * fsp)1666 static int fruit_close_rsrc(vfs_handle_struct *handle,
1667 files_struct *fsp)
1668 {
1669 int ret;
1670 struct fruit_config_data *config = NULL;
1671
1672 SMB_VFS_HANDLE_GET_DATA(handle, config,
1673 struct fruit_config_data, return -1);
1674
1675 switch (config->rsrc) {
1676 case FRUIT_RSRC_STREAM:
1677 case FRUIT_RSRC_ADFILE:
1678 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
1679 break;
1680
1681 case FRUIT_RSRC_XATTR:
1682 ret = vfs_fake_fd_close(fsp->fh->fd);
1683 fsp->fh->fd = -1;
1684 break;
1685
1686 default:
1687 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
1688 return -1;
1689 }
1690
1691 return ret;
1692 }
1693
fruit_close(vfs_handle_struct * handle,files_struct * fsp)1694 static int fruit_close(vfs_handle_struct *handle,
1695 files_struct *fsp)
1696 {
1697 int ret;
1698 int fd;
1699
1700 fd = fsp->fh->fd;
1701
1702 DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(fsp->fsp_name), fd);
1703
1704 if (!is_named_stream(fsp->fsp_name)) {
1705 return SMB_VFS_NEXT_CLOSE(handle, fsp);
1706 }
1707
1708 if (is_afpinfo_stream(fsp->fsp_name->stream_name)) {
1709 ret = fruit_close_meta(handle, fsp);
1710 } else if (is_afpresource_stream(fsp->fsp_name->stream_name)) {
1711 ret = fruit_close_rsrc(handle, fsp);
1712 } else {
1713 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
1714 }
1715
1716 return ret;
1717 }
1718
fruit_renameat(struct vfs_handle_struct * handle,files_struct * srcfsp,const struct smb_filename * smb_fname_src,files_struct * dstfsp,const struct smb_filename * smb_fname_dst)1719 static int fruit_renameat(struct vfs_handle_struct *handle,
1720 files_struct *srcfsp,
1721 const struct smb_filename *smb_fname_src,
1722 files_struct *dstfsp,
1723 const struct smb_filename *smb_fname_dst)
1724 {
1725 int rc = -1;
1726 struct fruit_config_data *config = NULL;
1727 struct smb_filename *src_adp_smb_fname = NULL;
1728 struct smb_filename *dst_adp_smb_fname = NULL;
1729
1730 SMB_VFS_HANDLE_GET_DATA(handle, config,
1731 struct fruit_config_data, return -1);
1732
1733 if (!VALID_STAT(smb_fname_src->st)) {
1734 DBG_ERR("Need valid stat for [%s]\n",
1735 smb_fname_str_dbg(smb_fname_src));
1736 return -1;
1737 }
1738
1739 rc = SMB_VFS_NEXT_RENAMEAT(handle,
1740 srcfsp,
1741 smb_fname_src,
1742 dstfsp,
1743 smb_fname_dst);
1744 if (rc != 0) {
1745 return -1;
1746 }
1747
1748 if ((config->rsrc != FRUIT_RSRC_ADFILE) ||
1749 (!S_ISREG(smb_fname_src->st.st_ex_mode)))
1750 {
1751 return 0;
1752 }
1753
1754 rc = adouble_path(talloc_tos(), smb_fname_src, &src_adp_smb_fname);
1755 if (rc != 0) {
1756 goto done;
1757 }
1758
1759 rc = adouble_path(talloc_tos(), smb_fname_dst, &dst_adp_smb_fname);
1760 if (rc != 0) {
1761 goto done;
1762 }
1763
1764 DBG_DEBUG("%s -> %s\n",
1765 smb_fname_str_dbg(src_adp_smb_fname),
1766 smb_fname_str_dbg(dst_adp_smb_fname));
1767
1768 rc = SMB_VFS_NEXT_RENAMEAT(handle,
1769 srcfsp,
1770 src_adp_smb_fname,
1771 dstfsp,
1772 dst_adp_smb_fname);
1773 if (errno == ENOENT) {
1774 rc = 0;
1775 }
1776
1777 done:
1778 TALLOC_FREE(src_adp_smb_fname);
1779 TALLOC_FREE(dst_adp_smb_fname);
1780 return rc;
1781 }
1782
fruit_unlink_meta_stream(vfs_handle_struct * handle,struct files_struct * dirfsp,const struct smb_filename * smb_fname)1783 static int fruit_unlink_meta_stream(vfs_handle_struct *handle,
1784 struct files_struct *dirfsp,
1785 const struct smb_filename *smb_fname)
1786 {
1787 return SMB_VFS_NEXT_UNLINKAT(handle,
1788 dirfsp,
1789 smb_fname,
1790 0);
1791 }
1792
fruit_unlink_meta_netatalk(vfs_handle_struct * handle,const struct smb_filename * smb_fname)1793 static int fruit_unlink_meta_netatalk(vfs_handle_struct *handle,
1794 const struct smb_filename *smb_fname)
1795 {
1796 return SMB_VFS_REMOVEXATTR(handle->conn,
1797 smb_fname,
1798 AFPINFO_EA_NETATALK);
1799 }
1800
fruit_unlink_meta(vfs_handle_struct * handle,struct files_struct * dirfsp,const struct smb_filename * smb_fname)1801 static int fruit_unlink_meta(vfs_handle_struct *handle,
1802 struct files_struct *dirfsp,
1803 const struct smb_filename *smb_fname)
1804 {
1805 struct fruit_config_data *config = NULL;
1806 int rc;
1807
1808 SMB_VFS_HANDLE_GET_DATA(handle, config,
1809 struct fruit_config_data, return -1);
1810
1811 switch (config->meta) {
1812 case FRUIT_META_STREAM:
1813 rc = fruit_unlink_meta_stream(handle,
1814 dirfsp,
1815 smb_fname);
1816 break;
1817
1818 case FRUIT_META_NETATALK:
1819 rc = fruit_unlink_meta_netatalk(handle, smb_fname);
1820 break;
1821
1822 default:
1823 DBG_ERR("Unsupported meta config [%d]\n", config->meta);
1824 return -1;
1825 }
1826
1827 return rc;
1828 }
1829
fruit_unlink_rsrc_stream(vfs_handle_struct * handle,struct files_struct * dirfsp,const struct smb_filename * smb_fname,bool force_unlink)1830 static int fruit_unlink_rsrc_stream(vfs_handle_struct *handle,
1831 struct files_struct *dirfsp,
1832 const struct smb_filename *smb_fname,
1833 bool force_unlink)
1834 {
1835 int ret;
1836
1837 if (!force_unlink) {
1838 struct smb_filename *smb_fname_cp = NULL;
1839 off_t size;
1840
1841 smb_fname_cp = cp_smb_filename(talloc_tos(), smb_fname);
1842 if (smb_fname_cp == NULL) {
1843 return -1;
1844 }
1845
1846 /*
1847 * 0 byte resource fork streams are not listed by
1848 * vfs_streaminfo, as a result stream cleanup/deletion of file
1849 * deletion doesn't remove the resourcefork stream.
1850 */
1851
1852 ret = SMB_VFS_NEXT_STAT(handle, smb_fname_cp);
1853 if (ret != 0) {
1854 TALLOC_FREE(smb_fname_cp);
1855 DBG_ERR("stat [%s] failed [%s]\n",
1856 smb_fname_str_dbg(smb_fname_cp), strerror(errno));
1857 return -1;
1858 }
1859
1860 size = smb_fname_cp->st.st_ex_size;
1861 TALLOC_FREE(smb_fname_cp);
1862
1863 if (size > 0) {
1864 /* OS X ignores resource fork stream delete requests */
1865 return 0;
1866 }
1867 }
1868
1869 ret = SMB_VFS_NEXT_UNLINKAT(handle,
1870 dirfsp,
1871 smb_fname,
1872 0);
1873 if ((ret != 0) && (errno == ENOENT) && force_unlink) {
1874 ret = 0;
1875 }
1876
1877 return ret;
1878 }
1879
fruit_unlink_rsrc_adouble(vfs_handle_struct * handle,struct files_struct * dirfsp,const struct smb_filename * smb_fname,bool force_unlink)1880 static int fruit_unlink_rsrc_adouble(vfs_handle_struct *handle,
1881 struct files_struct *dirfsp,
1882 const struct smb_filename *smb_fname,
1883 bool force_unlink)
1884 {
1885 int rc;
1886 struct adouble *ad = NULL;
1887 struct smb_filename *adp_smb_fname = NULL;
1888
1889 if (!force_unlink) {
1890 ad = ad_get(talloc_tos(), handle, smb_fname,
1891 ADOUBLE_RSRC);
1892 if (ad == NULL) {
1893 errno = ENOENT;
1894 return -1;
1895 }
1896
1897
1898 /*
1899 * 0 byte resource fork streams are not listed by
1900 * vfs_streaminfo, as a result stream cleanup/deletion of file
1901 * deletion doesn't remove the resourcefork stream.
1902 */
1903
1904 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
1905 /* OS X ignores resource fork stream delete requests */
1906 TALLOC_FREE(ad);
1907 return 0;
1908 }
1909
1910 TALLOC_FREE(ad);
1911 }
1912
1913 rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
1914 if (rc != 0) {
1915 return -1;
1916 }
1917
1918 rc = SMB_VFS_NEXT_UNLINKAT(handle,
1919 dirfsp,
1920 adp_smb_fname,
1921 0);
1922 TALLOC_FREE(adp_smb_fname);
1923 if ((rc != 0) && (errno == ENOENT) && force_unlink) {
1924 rc = 0;
1925 }
1926
1927 return rc;
1928 }
1929
fruit_unlink_rsrc_xattr(vfs_handle_struct * handle,const struct smb_filename * smb_fname,bool force_unlink)1930 static int fruit_unlink_rsrc_xattr(vfs_handle_struct *handle,
1931 const struct smb_filename *smb_fname,
1932 bool force_unlink)
1933 {
1934 /*
1935 * OS X ignores resource fork stream delete requests, so nothing to do
1936 * here. Removing the file will remove the xattr anyway, so we don't
1937 * have to take care of removing 0 byte resource forks that could be
1938 * left behind.
1939 */
1940 return 0;
1941 }
1942
fruit_unlink_rsrc(vfs_handle_struct * handle,struct files_struct * dirfsp,const struct smb_filename * smb_fname,bool force_unlink)1943 static int fruit_unlink_rsrc(vfs_handle_struct *handle,
1944 struct files_struct *dirfsp,
1945 const struct smb_filename *smb_fname,
1946 bool force_unlink)
1947 {
1948 struct fruit_config_data *config = NULL;
1949 int rc;
1950
1951 SMB_VFS_HANDLE_GET_DATA(handle, config,
1952 struct fruit_config_data, return -1);
1953
1954 switch (config->rsrc) {
1955 case FRUIT_RSRC_STREAM:
1956 rc = fruit_unlink_rsrc_stream(handle,
1957 dirfsp,
1958 smb_fname,
1959 force_unlink);
1960 break;
1961
1962 case FRUIT_RSRC_ADFILE:
1963 rc = fruit_unlink_rsrc_adouble(handle,
1964 dirfsp,
1965 smb_fname,
1966 force_unlink);
1967 break;
1968
1969 case FRUIT_RSRC_XATTR:
1970 rc = fruit_unlink_rsrc_xattr(handle, smb_fname, force_unlink);
1971 break;
1972
1973 default:
1974 DBG_ERR("Unsupported rsrc config [%d]\n", config->rsrc);
1975 return -1;
1976 }
1977
1978 return rc;
1979 }
1980
fruit_unlink_internal(vfs_handle_struct * handle,struct files_struct * dirfsp,const struct smb_filename * smb_fname)1981 static int fruit_unlink_internal(vfs_handle_struct *handle,
1982 struct files_struct *dirfsp,
1983 const struct smb_filename *smb_fname)
1984 {
1985 int rc;
1986 struct fruit_config_data *config = NULL;
1987 struct smb_filename *rsrc_smb_fname = NULL;
1988
1989 SMB_VFS_HANDLE_GET_DATA(handle, config,
1990 struct fruit_config_data, return -1);
1991
1992 if (is_afpinfo_stream(smb_fname->stream_name)) {
1993 return fruit_unlink_meta(handle,
1994 dirfsp,
1995 smb_fname);
1996 } else if (is_afpresource_stream(smb_fname->stream_name)) {
1997 return fruit_unlink_rsrc(handle,
1998 dirfsp,
1999 smb_fname,
2000 false);
2001 } else if (is_named_stream(smb_fname)) {
2002 return SMB_VFS_NEXT_UNLINKAT(handle,
2003 dirfsp,
2004 smb_fname,
2005 0);
2006 } else if (is_adouble_file(smb_fname->base_name)) {
2007 return SMB_VFS_NEXT_UNLINKAT(handle,
2008 dirfsp,
2009 smb_fname,
2010 0);
2011 }
2012
2013 /*
2014 * A request to delete the base file. Because 0 byte resource
2015 * fork streams are not listed by fruit_streaminfo,
2016 * delete_all_streams() can't remove 0 byte resource fork
2017 * streams, so we have to cleanup this here.
2018 */
2019 rsrc_smb_fname = synthetic_smb_fname(talloc_tos(),
2020 smb_fname->base_name,
2021 AFPRESOURCE_STREAM_NAME,
2022 NULL,
2023 smb_fname->flags);
2024 if (rsrc_smb_fname == NULL) {
2025 return -1;
2026 }
2027
2028 rc = fruit_unlink_rsrc(handle, dirfsp, rsrc_smb_fname, true);
2029 if ((rc != 0) && (errno != ENOENT)) {
2030 DBG_ERR("Forced unlink of [%s] failed [%s]\n",
2031 smb_fname_str_dbg(rsrc_smb_fname), strerror(errno));
2032 TALLOC_FREE(rsrc_smb_fname);
2033 return -1;
2034 }
2035 TALLOC_FREE(rsrc_smb_fname);
2036
2037 return SMB_VFS_NEXT_UNLINKAT(handle,
2038 dirfsp,
2039 smb_fname,
2040 0);
2041 }
2042
fruit_chmod(vfs_handle_struct * handle,const struct smb_filename * smb_fname,mode_t mode)2043 static int fruit_chmod(vfs_handle_struct *handle,
2044 const struct smb_filename *smb_fname,
2045 mode_t mode)
2046 {
2047 int rc = -1;
2048 struct fruit_config_data *config = NULL;
2049 struct smb_filename *smb_fname_adp = NULL;
2050
2051 rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname, mode);
2052 if (rc != 0) {
2053 return rc;
2054 }
2055
2056 SMB_VFS_HANDLE_GET_DATA(handle, config,
2057 struct fruit_config_data, return -1);
2058
2059 if (config->rsrc != FRUIT_RSRC_ADFILE) {
2060 return 0;
2061 }
2062
2063 if (!VALID_STAT(smb_fname->st)) {
2064 return 0;
2065 }
2066
2067 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
2068 return 0;
2069 }
2070
2071 rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_adp);
2072 if (rc != 0) {
2073 return -1;
2074 }
2075
2076 DEBUG(10, ("fruit_chmod: %s\n", smb_fname_adp->base_name));
2077
2078 rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname_adp, mode);
2079 if (errno == ENOENT) {
2080 rc = 0;
2081 }
2082
2083 TALLOC_FREE(smb_fname_adp);
2084 return rc;
2085 }
2086
fruit_rmdir_internal(struct vfs_handle_struct * handle,struct files_struct * dirfsp,const struct smb_filename * smb_fname)2087 static int fruit_rmdir_internal(struct vfs_handle_struct *handle,
2088 struct files_struct *dirfsp,
2089 const struct smb_filename *smb_fname)
2090 {
2091 DIR *dh = NULL;
2092 struct dirent *de;
2093 struct fruit_config_data *config;
2094
2095 SMB_VFS_HANDLE_GET_DATA(handle, config,
2096 struct fruit_config_data, return -1);
2097
2098 if (config->rsrc != FRUIT_RSRC_ADFILE) {
2099 goto exit_rmdir;
2100 }
2101
2102 /*
2103 * Due to there is no way to change bDeleteVetoFiles variable
2104 * from this module, need to clean up ourselves
2105 */
2106
2107 dh = SMB_VFS_OPENDIR(handle->conn, smb_fname, NULL, 0);
2108 if (dh == NULL) {
2109 goto exit_rmdir;
2110 }
2111
2112 while ((de = SMB_VFS_READDIR(handle->conn, dh, NULL)) != NULL) {
2113 struct adouble *ad = NULL;
2114 char *p = NULL;
2115 struct smb_filename *ad_smb_fname = NULL;
2116 int ret;
2117
2118 if (!is_adouble_file(de->d_name)) {
2119 continue;
2120 }
2121
2122 p = talloc_asprintf(talloc_tos(), "%s/%s",
2123 smb_fname->base_name, de->d_name);
2124 if (p == NULL) {
2125 DBG_ERR("talloc_asprintf failed\n");
2126 return -1;
2127 }
2128
2129 ad_smb_fname = synthetic_smb_fname(talloc_tos(), p,
2130 NULL, NULL,
2131 smb_fname->flags);
2132 TALLOC_FREE(p);
2133 if (ad_smb_fname == NULL) {
2134 DBG_ERR("synthetic_smb_fname failed\n");
2135 return -1;
2136 }
2137
2138 /*
2139 * Check whether it's a valid AppleDouble file, if
2140 * yes, delete it, ignore it otherwise.
2141 */
2142 ad = ad_get(talloc_tos(), handle, ad_smb_fname, ADOUBLE_RSRC);
2143 if (ad == NULL) {
2144 TALLOC_FREE(ad_smb_fname);
2145 TALLOC_FREE(p);
2146 continue;
2147 }
2148 TALLOC_FREE(ad);
2149
2150 ret = SMB_VFS_NEXT_UNLINKAT(handle,
2151 dirfsp,
2152 ad_smb_fname,
2153 0);
2154 if (ret != 0) {
2155 DBG_ERR("Deleting [%s] failed\n",
2156 smb_fname_str_dbg(ad_smb_fname));
2157 }
2158 TALLOC_FREE(ad_smb_fname);
2159 }
2160
2161 exit_rmdir:
2162 if (dh) {
2163 SMB_VFS_CLOSEDIR(handle->conn, dh);
2164 }
2165 return SMB_VFS_NEXT_UNLINKAT(handle,
2166 dirfsp,
2167 smb_fname,
2168 AT_REMOVEDIR);
2169 }
2170
fruit_unlinkat(vfs_handle_struct * handle,struct files_struct * dirfsp,const struct smb_filename * smb_fname,int flags)2171 static int fruit_unlinkat(vfs_handle_struct *handle,
2172 struct files_struct *dirfsp,
2173 const struct smb_filename *smb_fname,
2174 int flags)
2175 {
2176 int ret;
2177
2178 SMB_ASSERT(dirfsp == dirfsp->conn->cwd_fsp);
2179 if (flags & AT_REMOVEDIR) {
2180 ret = fruit_rmdir_internal(handle,
2181 dirfsp,
2182 smb_fname);
2183 } else {
2184 ret = fruit_unlink_internal(handle,
2185 dirfsp,
2186 smb_fname);
2187 }
2188 return ret;
2189 }
2190
fruit_pread_meta_stream(vfs_handle_struct * handle,files_struct * fsp,void * data,size_t n,off_t offset)2191 static ssize_t fruit_pread_meta_stream(vfs_handle_struct *handle,
2192 files_struct *fsp, void *data,
2193 size_t n, off_t offset)
2194 {
2195 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
2196 ssize_t nread;
2197 int ret;
2198 char *p = (char *)data;
2199
2200 if (fio->fake_fd) {
2201 return -1;
2202 }
2203
2204 nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
2205 if (nread <= 0) {
2206 /*
2207 * fruit_meta_open_stream() removes O_CREAT flag
2208 * from xattr open. This results in vfs_streams_xattr
2209 * not generating an FSP extension for the files_struct
2210 * and causes subsequent pread() of stream to return
2211 * nread=0 if pread() occurs before pwrite().
2212 */
2213 return nread;
2214 }
2215
2216 if (nread == n) {
2217 if (offset == 0 && nread > 3 && p[0] == 0 && p[1] == 'F' && p[2] == 'P') {
2218 DBG_NOTICE("Fixing AFP_Info of [%s]\n",
2219 fsp_str_dbg(fsp));
2220 p[0] = 'A';
2221 }
2222 return nread;
2223 }
2224
2225 DBG_ERR("Removing [%s] after short read [%zd]\n",
2226 fsp_str_dbg(fsp), nread);
2227
2228 ret = SMB_VFS_NEXT_UNLINKAT(handle,
2229 fsp->conn->cwd_fsp,
2230 fsp->fsp_name,
2231 0);
2232 if (ret != 0) {
2233 DBG_ERR("Removing [%s] failed\n", fsp_str_dbg(fsp));
2234 return -1;
2235 }
2236
2237 errno = EINVAL;
2238 return -1;
2239 }
2240
fruit_pread_meta_adouble(vfs_handle_struct * handle,files_struct * fsp,void * data,size_t n,off_t offset)2241 static ssize_t fruit_pread_meta_adouble(vfs_handle_struct *handle,
2242 files_struct *fsp, void *data,
2243 size_t n, off_t offset)
2244 {
2245 AfpInfo *ai = NULL;
2246 struct adouble *ad = NULL;
2247 char afpinfo_buf[AFP_INFO_SIZE];
2248 char *p = NULL;
2249 ssize_t nread;
2250
2251 ai = afpinfo_new(talloc_tos());
2252 if (ai == NULL) {
2253 return -1;
2254 }
2255
2256 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
2257 if (ad == NULL) {
2258 nread = -1;
2259 goto fail;
2260 }
2261
2262 p = ad_get_entry(ad, ADEID_FINDERI);
2263 if (p == NULL) {
2264 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
2265 nread = -1;
2266 goto fail;
2267 }
2268
2269 memcpy(&ai->afpi_FinderInfo[0], p, ADEDLEN_FINDERI);
2270
2271 nread = afpinfo_pack(ai, afpinfo_buf);
2272 if (nread != AFP_INFO_SIZE) {
2273 nread = -1;
2274 goto fail;
2275 }
2276
2277 memcpy(data, afpinfo_buf, n);
2278 nread = n;
2279
2280 fail:
2281 TALLOC_FREE(ai);
2282 return nread;
2283 }
2284
fruit_pread_meta(vfs_handle_struct * handle,files_struct * fsp,void * data,size_t n,off_t offset)2285 static ssize_t fruit_pread_meta(vfs_handle_struct *handle,
2286 files_struct *fsp, void *data,
2287 size_t n, off_t offset)
2288 {
2289 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
2290 ssize_t nread;
2291 ssize_t to_return;
2292
2293 /*
2294 * OS X has a off-by-1 error in the offset calculation, so we're
2295 * bug compatible here. It won't hurt, as any relevant real
2296 * world read requests from the AFP_AfpInfo stream will be
2297 * offset=0 n=60. offset is ignored anyway, see below.
2298 */
2299 if ((offset < 0) || (offset >= AFP_INFO_SIZE + 1)) {
2300 return 0;
2301 }
2302
2303 if (fio == NULL) {
2304 DBG_ERR("Failed to fetch fsp extension");
2305 return -1;
2306 }
2307
2308 /* Yes, macOS always reads from offset 0 */
2309 offset = 0;
2310 to_return = MIN(n, AFP_INFO_SIZE);
2311
2312 switch (fio->config->meta) {
2313 case FRUIT_META_STREAM:
2314 nread = fruit_pread_meta_stream(handle, fsp, data,
2315 to_return, offset);
2316 break;
2317
2318 case FRUIT_META_NETATALK:
2319 nread = fruit_pread_meta_adouble(handle, fsp, data,
2320 to_return, offset);
2321 break;
2322
2323 default:
2324 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
2325 return -1;
2326 }
2327
2328 if (nread == -1 && fio->fake_fd) {
2329 AfpInfo *ai = NULL;
2330 char afpinfo_buf[AFP_INFO_SIZE];
2331
2332 ai = afpinfo_new(talloc_tos());
2333 if (ai == NULL) {
2334 return -1;
2335 }
2336
2337 nread = afpinfo_pack(ai, afpinfo_buf);
2338 TALLOC_FREE(ai);
2339 if (nread != AFP_INFO_SIZE) {
2340 return -1;
2341 }
2342
2343 memcpy(data, afpinfo_buf, to_return);
2344 return to_return;
2345 }
2346
2347 return nread;
2348 }
2349
fruit_pread_rsrc_stream(vfs_handle_struct * handle,files_struct * fsp,void * data,size_t n,off_t offset)2350 static ssize_t fruit_pread_rsrc_stream(vfs_handle_struct *handle,
2351 files_struct *fsp, void *data,
2352 size_t n, off_t offset)
2353 {
2354 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
2355 }
2356
fruit_pread_rsrc_xattr(vfs_handle_struct * handle,files_struct * fsp,void * data,size_t n,off_t offset)2357 static ssize_t fruit_pread_rsrc_xattr(vfs_handle_struct *handle,
2358 files_struct *fsp, void *data,
2359 size_t n, off_t offset)
2360 {
2361 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
2362 }
2363
fruit_pread_rsrc_adouble(vfs_handle_struct * handle,files_struct * fsp,void * data,size_t n,off_t offset)2364 static ssize_t fruit_pread_rsrc_adouble(vfs_handle_struct *handle,
2365 files_struct *fsp, void *data,
2366 size_t n, off_t offset)
2367 {
2368 struct adouble *ad = NULL;
2369 ssize_t nread;
2370
2371 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
2372 if (ad == NULL) {
2373 return -1;
2374 }
2375
2376 nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n,
2377 offset + ad_getentryoff(ad, ADEID_RFORK));
2378
2379 TALLOC_FREE(ad);
2380 return nread;
2381 }
2382
fruit_pread_rsrc(vfs_handle_struct * handle,files_struct * fsp,void * data,size_t n,off_t offset)2383 static ssize_t fruit_pread_rsrc(vfs_handle_struct *handle,
2384 files_struct *fsp, void *data,
2385 size_t n, off_t offset)
2386 {
2387 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
2388 ssize_t nread;
2389
2390 if (fio == NULL) {
2391 errno = EINVAL;
2392 return -1;
2393 }
2394
2395 switch (fio->config->rsrc) {
2396 case FRUIT_RSRC_STREAM:
2397 nread = fruit_pread_rsrc_stream(handle, fsp, data, n, offset);
2398 break;
2399
2400 case FRUIT_RSRC_ADFILE:
2401 nread = fruit_pread_rsrc_adouble(handle, fsp, data, n, offset);
2402 break;
2403
2404 case FRUIT_RSRC_XATTR:
2405 nread = fruit_pread_rsrc_xattr(handle, fsp, data, n, offset);
2406 break;
2407
2408 default:
2409 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
2410 return -1;
2411 }
2412
2413 return nread;
2414 }
2415
fruit_pread(vfs_handle_struct * handle,files_struct * fsp,void * data,size_t n,off_t offset)2416 static ssize_t fruit_pread(vfs_handle_struct *handle,
2417 files_struct *fsp, void *data,
2418 size_t n, off_t offset)
2419 {
2420 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
2421 ssize_t nread;
2422
2423 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
2424 fsp_str_dbg(fsp), (intmax_t)offset, n);
2425
2426 if (fio == NULL) {
2427 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
2428 }
2429
2430 if (fio->type == ADOUBLE_META) {
2431 nread = fruit_pread_meta(handle, fsp, data, n, offset);
2432 } else {
2433 nread = fruit_pread_rsrc(handle, fsp, data, n, offset);
2434 }
2435
2436 DBG_DEBUG("Path [%s] nread [%zd]\n", fsp_str_dbg(fsp), nread);
2437 return nread;
2438 }
2439
fruit_must_handle_aio_stream(struct fio * fio)2440 static bool fruit_must_handle_aio_stream(struct fio *fio)
2441 {
2442 if (fio == NULL) {
2443 return false;
2444 };
2445
2446 if (fio->type == ADOUBLE_META) {
2447 return true;
2448 }
2449
2450 if ((fio->type == ADOUBLE_RSRC) &&
2451 (fio->config->rsrc == FRUIT_RSRC_ADFILE))
2452 {
2453 return true;
2454 }
2455
2456 return false;
2457 }
2458
2459 struct fruit_pread_state {
2460 ssize_t nread;
2461 struct vfs_aio_state vfs_aio_state;
2462 };
2463
2464 static void fruit_pread_done(struct tevent_req *subreq);
2465
fruit_pread_send(struct vfs_handle_struct * handle,TALLOC_CTX * mem_ctx,struct tevent_context * ev,struct files_struct * fsp,void * data,size_t n,off_t offset)2466 static struct tevent_req *fruit_pread_send(
2467 struct vfs_handle_struct *handle,
2468 TALLOC_CTX *mem_ctx,
2469 struct tevent_context *ev,
2470 struct files_struct *fsp,
2471 void *data,
2472 size_t n, off_t offset)
2473 {
2474 struct tevent_req *req = NULL;
2475 struct tevent_req *subreq = NULL;
2476 struct fruit_pread_state *state = NULL;
2477 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
2478
2479 req = tevent_req_create(mem_ctx, &state,
2480 struct fruit_pread_state);
2481 if (req == NULL) {
2482 return NULL;
2483 }
2484
2485 if (fruit_must_handle_aio_stream(fio)) {
2486 state->nread = SMB_VFS_PREAD(fsp, data, n, offset);
2487 if (state->nread != n) {
2488 if (state->nread != -1) {
2489 errno = EIO;
2490 }
2491 tevent_req_error(req, errno);
2492 return tevent_req_post(req, ev);
2493 }
2494 tevent_req_done(req);
2495 return tevent_req_post(req, ev);
2496 }
2497
2498 subreq = SMB_VFS_NEXT_PREAD_SEND(state, ev, handle, fsp,
2499 data, n, offset);
2500 if (tevent_req_nomem(req, subreq)) {
2501 return tevent_req_post(req, ev);
2502 }
2503 tevent_req_set_callback(subreq, fruit_pread_done, req);
2504 return req;
2505 }
2506
fruit_pread_done(struct tevent_req * subreq)2507 static void fruit_pread_done(struct tevent_req *subreq)
2508 {
2509 struct tevent_req *req = tevent_req_callback_data(
2510 subreq, struct tevent_req);
2511 struct fruit_pread_state *state = tevent_req_data(
2512 req, struct fruit_pread_state);
2513
2514 state->nread = SMB_VFS_PREAD_RECV(subreq, &state->vfs_aio_state);
2515 TALLOC_FREE(subreq);
2516
2517 if (tevent_req_error(req, state->vfs_aio_state.error)) {
2518 return;
2519 }
2520 tevent_req_done(req);
2521 }
2522
fruit_pread_recv(struct tevent_req * req,struct vfs_aio_state * vfs_aio_state)2523 static ssize_t fruit_pread_recv(struct tevent_req *req,
2524 struct vfs_aio_state *vfs_aio_state)
2525 {
2526 struct fruit_pread_state *state = tevent_req_data(
2527 req, struct fruit_pread_state);
2528
2529 if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
2530 return -1;
2531 }
2532
2533 *vfs_aio_state = state->vfs_aio_state;
2534 return state->nread;
2535 }
2536
fruit_pwrite_meta_stream(vfs_handle_struct * handle,files_struct * fsp,const void * data,size_t n,off_t offset)2537 static ssize_t fruit_pwrite_meta_stream(vfs_handle_struct *handle,
2538 files_struct *fsp, const void *data,
2539 size_t n, off_t offset)
2540 {
2541 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
2542 AfpInfo *ai = NULL;
2543 size_t nwritten;
2544 int ret;
2545 bool ok;
2546
2547 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
2548 fsp_str_dbg(fsp), (intmax_t)offset, n);
2549
2550 if (fio == NULL) {
2551 return -1;
2552 }
2553
2554 if (fio->fake_fd) {
2555 int fd = fsp->fh->fd;
2556
2557 ret = vfs_fake_fd_close(fd);
2558 fsp->fh->fd = -1;
2559 if (ret != 0) {
2560 DBG_ERR("Close [%s] failed: %s\n",
2561 fsp_str_dbg(fsp), strerror(errno));
2562 return -1;
2563 }
2564
2565 fd = SMB_VFS_NEXT_OPEN(handle,
2566 fsp->fsp_name,
2567 fsp,
2568 fio->flags,
2569 fio->mode);
2570 if (fd == -1) {
2571 DBG_ERR("On-demand create [%s] in write failed: %s\n",
2572 fsp_str_dbg(fsp), strerror(errno));
2573 return -1;
2574 }
2575 fsp->fh->fd = fd;
2576 fio->fake_fd = false;
2577 }
2578
2579 ai = afpinfo_unpack(talloc_tos(), data);
2580 if (ai == NULL) {
2581 return -1;
2582 }
2583
2584 if (ai_empty_finderinfo(ai)) {
2585 /*
2586 * Writing an all 0 blob to the metadata stream results in the
2587 * stream being removed on a macOS server. This ensures we
2588 * behave the same and it verified by the "delete AFP_AfpInfo by
2589 * writing all 0" test.
2590 */
2591 ret = SMB_VFS_NEXT_FTRUNCATE(handle, fsp, 0);
2592 if (ret != 0) {
2593 DBG_ERR("SMB_VFS_NEXT_FTRUNCATE on [%s] failed\n",
2594 fsp_str_dbg(fsp));
2595 return -1;
2596 }
2597
2598 ok = set_delete_on_close(
2599 fsp,
2600 true,
2601 handle->conn->session_info->security_token,
2602 handle->conn->session_info->unix_token);
2603 if (!ok) {
2604 DBG_ERR("set_delete_on_close on [%s] failed\n",
2605 fsp_str_dbg(fsp));
2606 return -1;
2607 }
2608 return n;
2609 }
2610
2611 nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
2612 if (nwritten != n) {
2613 return -1;
2614 }
2615
2616 return n;
2617 }
2618
fruit_pwrite_meta_netatalk(vfs_handle_struct * handle,files_struct * fsp,const void * data,size_t n,off_t offset)2619 static ssize_t fruit_pwrite_meta_netatalk(vfs_handle_struct *handle,
2620 files_struct *fsp, const void *data,
2621 size_t n, off_t offset)
2622 {
2623 struct adouble *ad = NULL;
2624 AfpInfo *ai = NULL;
2625 char *p = NULL;
2626 int ret;
2627 bool ok;
2628
2629 ai = afpinfo_unpack(talloc_tos(), data);
2630 if (ai == NULL) {
2631 return -1;
2632 }
2633
2634 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
2635 if (ad == NULL) {
2636 ad = ad_init(talloc_tos(), ADOUBLE_META);
2637 if (ad == NULL) {
2638 return -1;
2639 }
2640 }
2641 p = ad_get_entry(ad, ADEID_FINDERI);
2642 if (p == NULL) {
2643 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
2644 TALLOC_FREE(ad);
2645 return -1;
2646 }
2647
2648 memcpy(p, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
2649
2650 ret = ad_fset(handle, ad, fsp);
2651 if (ret != 0) {
2652 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
2653 TALLOC_FREE(ad);
2654 return -1;
2655 }
2656
2657 TALLOC_FREE(ad);
2658
2659 if (!ai_empty_finderinfo(ai)) {
2660 return n;
2661 }
2662
2663 /*
2664 * Writing an all 0 blob to the metadata stream results in the stream
2665 * being removed on a macOS server. This ensures we behave the same and
2666 * it verified by the "delete AFP_AfpInfo by writing all 0" test.
2667 */
2668
2669 ok = set_delete_on_close(
2670 fsp,
2671 true,
2672 handle->conn->session_info->security_token,
2673 handle->conn->session_info->unix_token);
2674 if (!ok) {
2675 DBG_ERR("set_delete_on_close on [%s] failed\n",
2676 fsp_str_dbg(fsp));
2677 return -1;
2678 }
2679
2680 return n;
2681 }
2682
fruit_pwrite_meta(vfs_handle_struct * handle,files_struct * fsp,const void * data,size_t n,off_t offset)2683 static ssize_t fruit_pwrite_meta(vfs_handle_struct *handle,
2684 files_struct *fsp, const void *data,
2685 size_t n, off_t offset)
2686 {
2687 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
2688 ssize_t nwritten;
2689 uint8_t buf[AFP_INFO_SIZE];
2690 size_t to_write;
2691 size_t to_copy;
2692 int cmp;
2693
2694 if (fio == NULL) {
2695 DBG_ERR("Failed to fetch fsp extension");
2696 return -1;
2697 }
2698
2699 if (n < 3) {
2700 errno = EINVAL;
2701 return -1;
2702 }
2703
2704 if (offset != 0 && n < 60) {
2705 errno = EINVAL;
2706 return -1;
2707 }
2708
2709 cmp = memcmp(data, "AFP", 3);
2710 if (cmp != 0) {
2711 errno = EINVAL;
2712 return -1;
2713 }
2714
2715 if (n <= AFP_OFF_FinderInfo) {
2716 /*
2717 * Nothing to do here really, just return
2718 */
2719 return n;
2720 }
2721
2722 offset = 0;
2723
2724 to_copy = n;
2725 if (to_copy > AFP_INFO_SIZE) {
2726 to_copy = AFP_INFO_SIZE;
2727 }
2728 memcpy(buf, data, to_copy);
2729
2730 to_write = n;
2731 if (to_write != AFP_INFO_SIZE) {
2732 to_write = AFP_INFO_SIZE;
2733 }
2734
2735 switch (fio->config->meta) {
2736 case FRUIT_META_STREAM:
2737 nwritten = fruit_pwrite_meta_stream(handle,
2738 fsp,
2739 buf,
2740 to_write,
2741 offset);
2742 break;
2743
2744 case FRUIT_META_NETATALK:
2745 nwritten = fruit_pwrite_meta_netatalk(handle,
2746 fsp,
2747 buf,
2748 to_write,
2749 offset);
2750 break;
2751
2752 default:
2753 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
2754 return -1;
2755 }
2756
2757 if (nwritten != to_write) {
2758 return -1;
2759 }
2760
2761 /*
2762 * Return the requested amount, verified against macOS SMB server
2763 */
2764 return n;
2765 }
2766
fruit_pwrite_rsrc_stream(vfs_handle_struct * handle,files_struct * fsp,const void * data,size_t n,off_t offset)2767 static ssize_t fruit_pwrite_rsrc_stream(vfs_handle_struct *handle,
2768 files_struct *fsp, const void *data,
2769 size_t n, off_t offset)
2770 {
2771 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
2772 }
2773
fruit_pwrite_rsrc_xattr(vfs_handle_struct * handle,files_struct * fsp,const void * data,size_t n,off_t offset)2774 static ssize_t fruit_pwrite_rsrc_xattr(vfs_handle_struct *handle,
2775 files_struct *fsp, const void *data,
2776 size_t n, off_t offset)
2777 {
2778 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
2779 }
2780
fruit_pwrite_rsrc_adouble(vfs_handle_struct * handle,files_struct * fsp,const void * data,size_t n,off_t offset)2781 static ssize_t fruit_pwrite_rsrc_adouble(vfs_handle_struct *handle,
2782 files_struct *fsp, const void *data,
2783 size_t n, off_t offset)
2784 {
2785 struct adouble *ad = NULL;
2786 ssize_t nwritten;
2787 int ret;
2788
2789 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
2790 if (ad == NULL) {
2791 DBG_ERR("ad_get [%s] failed\n", fsp_str_dbg(fsp));
2792 return -1;
2793 }
2794
2795 nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n,
2796 offset + ad_getentryoff(ad, ADEID_RFORK));
2797 if (nwritten != n) {
2798 DBG_ERR("Short write on [%s] [%zd/%zd]\n",
2799 fsp_str_dbg(fsp), nwritten, n);
2800 TALLOC_FREE(ad);
2801 return -1;
2802 }
2803
2804 if ((n + offset) > ad_getentrylen(ad, ADEID_RFORK)) {
2805 ad_setentrylen(ad, ADEID_RFORK, n + offset);
2806 ret = ad_fset(handle, ad, fsp);
2807 if (ret != 0) {
2808 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
2809 TALLOC_FREE(ad);
2810 return -1;
2811 }
2812 }
2813
2814 TALLOC_FREE(ad);
2815 return n;
2816 }
2817
fruit_pwrite_rsrc(vfs_handle_struct * handle,files_struct * fsp,const void * data,size_t n,off_t offset)2818 static ssize_t fruit_pwrite_rsrc(vfs_handle_struct *handle,
2819 files_struct *fsp, const void *data,
2820 size_t n, off_t offset)
2821 {
2822 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
2823 ssize_t nwritten;
2824
2825 if (fio == NULL) {
2826 DBG_ERR("Failed to fetch fsp extension");
2827 return -1;
2828 }
2829
2830 switch (fio->config->rsrc) {
2831 case FRUIT_RSRC_STREAM:
2832 nwritten = fruit_pwrite_rsrc_stream(handle, fsp, data, n, offset);
2833 break;
2834
2835 case FRUIT_RSRC_ADFILE:
2836 nwritten = fruit_pwrite_rsrc_adouble(handle, fsp, data, n, offset);
2837 break;
2838
2839 case FRUIT_RSRC_XATTR:
2840 nwritten = fruit_pwrite_rsrc_xattr(handle, fsp, data, n, offset);
2841 break;
2842
2843 default:
2844 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
2845 return -1;
2846 }
2847
2848 return nwritten;
2849 }
2850
fruit_pwrite(vfs_handle_struct * handle,files_struct * fsp,const void * data,size_t n,off_t offset)2851 static ssize_t fruit_pwrite(vfs_handle_struct *handle,
2852 files_struct *fsp, const void *data,
2853 size_t n, off_t offset)
2854 {
2855 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
2856 ssize_t nwritten;
2857
2858 DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
2859 fsp_str_dbg(fsp), (intmax_t)offset, n);
2860
2861 if (fio == NULL) {
2862 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
2863 }
2864
2865 if (fio->type == ADOUBLE_META) {
2866 nwritten = fruit_pwrite_meta(handle, fsp, data, n, offset);
2867 } else {
2868 nwritten = fruit_pwrite_rsrc(handle, fsp, data, n, offset);
2869 }
2870
2871 DBG_DEBUG("Path [%s] nwritten=%zd\n", fsp_str_dbg(fsp), nwritten);
2872 return nwritten;
2873 }
2874
2875 struct fruit_pwrite_state {
2876 ssize_t nwritten;
2877 struct vfs_aio_state vfs_aio_state;
2878 };
2879
2880 static void fruit_pwrite_done(struct tevent_req *subreq);
2881
fruit_pwrite_send(struct vfs_handle_struct * handle,TALLOC_CTX * mem_ctx,struct tevent_context * ev,struct files_struct * fsp,const void * data,size_t n,off_t offset)2882 static struct tevent_req *fruit_pwrite_send(
2883 struct vfs_handle_struct *handle,
2884 TALLOC_CTX *mem_ctx,
2885 struct tevent_context *ev,
2886 struct files_struct *fsp,
2887 const void *data,
2888 size_t n, off_t offset)
2889 {
2890 struct tevent_req *req = NULL;
2891 struct tevent_req *subreq = NULL;
2892 struct fruit_pwrite_state *state = NULL;
2893 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
2894
2895 req = tevent_req_create(mem_ctx, &state,
2896 struct fruit_pwrite_state);
2897 if (req == NULL) {
2898 return NULL;
2899 }
2900
2901 if (fruit_must_handle_aio_stream(fio)) {
2902 state->nwritten = SMB_VFS_PWRITE(fsp, data, n, offset);
2903 if (state->nwritten != n) {
2904 if (state->nwritten != -1) {
2905 errno = EIO;
2906 }
2907 tevent_req_error(req, errno);
2908 return tevent_req_post(req, ev);
2909 }
2910 tevent_req_done(req);
2911 return tevent_req_post(req, ev);
2912 }
2913
2914 subreq = SMB_VFS_NEXT_PWRITE_SEND(state, ev, handle, fsp,
2915 data, n, offset);
2916 if (tevent_req_nomem(req, subreq)) {
2917 return tevent_req_post(req, ev);
2918 }
2919 tevent_req_set_callback(subreq, fruit_pwrite_done, req);
2920 return req;
2921 }
2922
fruit_pwrite_done(struct tevent_req * subreq)2923 static void fruit_pwrite_done(struct tevent_req *subreq)
2924 {
2925 struct tevent_req *req = tevent_req_callback_data(
2926 subreq, struct tevent_req);
2927 struct fruit_pwrite_state *state = tevent_req_data(
2928 req, struct fruit_pwrite_state);
2929
2930 state->nwritten = SMB_VFS_PWRITE_RECV(subreq, &state->vfs_aio_state);
2931 TALLOC_FREE(subreq);
2932
2933 if (tevent_req_error(req, state->vfs_aio_state.error)) {
2934 return;
2935 }
2936 tevent_req_done(req);
2937 }
2938
fruit_pwrite_recv(struct tevent_req * req,struct vfs_aio_state * vfs_aio_state)2939 static ssize_t fruit_pwrite_recv(struct tevent_req *req,
2940 struct vfs_aio_state *vfs_aio_state)
2941 {
2942 struct fruit_pwrite_state *state = tevent_req_data(
2943 req, struct fruit_pwrite_state);
2944
2945 if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
2946 return -1;
2947 }
2948
2949 *vfs_aio_state = state->vfs_aio_state;
2950 return state->nwritten;
2951 }
2952
2953 /**
2954 * Helper to stat/lstat the base file of an smb_fname.
2955 */
fruit_stat_base(vfs_handle_struct * handle,struct smb_filename * smb_fname,bool follow_links)2956 static int fruit_stat_base(vfs_handle_struct *handle,
2957 struct smb_filename *smb_fname,
2958 bool follow_links)
2959 {
2960 char *tmp_stream_name;
2961 int rc;
2962
2963 tmp_stream_name = smb_fname->stream_name;
2964 smb_fname->stream_name = NULL;
2965 if (follow_links) {
2966 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
2967 } else {
2968 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
2969 }
2970 smb_fname->stream_name = tmp_stream_name;
2971
2972 DBG_DEBUG("fruit_stat_base [%s] dev [%ju] ino [%ju]\n",
2973 smb_fname->base_name,
2974 (uintmax_t)smb_fname->st.st_ex_dev,
2975 (uintmax_t)smb_fname->st.st_ex_ino);
2976 return rc;
2977 }
2978
fruit_stat_meta_stream(vfs_handle_struct * handle,struct smb_filename * smb_fname,bool follow_links)2979 static int fruit_stat_meta_stream(vfs_handle_struct *handle,
2980 struct smb_filename *smb_fname,
2981 bool follow_links)
2982 {
2983 int ret;
2984 ino_t ino;
2985
2986 ret = fruit_stat_base(handle, smb_fname, false);
2987 if (ret != 0) {
2988 return -1;
2989 }
2990
2991 ino = hash_inode(&smb_fname->st, smb_fname->stream_name);
2992
2993 if (follow_links) {
2994 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
2995 } else {
2996 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
2997 }
2998
2999 smb_fname->st.st_ex_ino = ino;
3000
3001 return ret;
3002 }
3003
fruit_stat_meta_netatalk(vfs_handle_struct * handle,struct smb_filename * smb_fname,bool follow_links)3004 static int fruit_stat_meta_netatalk(vfs_handle_struct *handle,
3005 struct smb_filename *smb_fname,
3006 bool follow_links)
3007 {
3008 struct adouble *ad = NULL;
3009
3010 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
3011 if (ad == NULL) {
3012 DBG_INFO("fruit_stat_meta %s: %s\n",
3013 smb_fname_str_dbg(smb_fname), strerror(errno));
3014 errno = ENOENT;
3015 return -1;
3016 }
3017 TALLOC_FREE(ad);
3018
3019 /* Populate the stat struct with info from the base file. */
3020 if (fruit_stat_base(handle, smb_fname, follow_links) == -1) {
3021 return -1;
3022 }
3023 smb_fname->st.st_ex_size = AFP_INFO_SIZE;
3024 smb_fname->st.st_ex_ino = hash_inode(&smb_fname->st,
3025 smb_fname->stream_name);
3026 return 0;
3027 }
3028
fruit_stat_meta(vfs_handle_struct * handle,struct smb_filename * smb_fname,bool follow_links)3029 static int fruit_stat_meta(vfs_handle_struct *handle,
3030 struct smb_filename *smb_fname,
3031 bool follow_links)
3032 {
3033 struct fruit_config_data *config = NULL;
3034 int ret;
3035
3036 SMB_VFS_HANDLE_GET_DATA(handle, config,
3037 struct fruit_config_data, return -1);
3038
3039 switch (config->meta) {
3040 case FRUIT_META_STREAM:
3041 ret = fruit_stat_meta_stream(handle, smb_fname, follow_links);
3042 break;
3043
3044 case FRUIT_META_NETATALK:
3045 ret = fruit_stat_meta_netatalk(handle, smb_fname, follow_links);
3046 break;
3047
3048 default:
3049 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3050 return -1;
3051 }
3052
3053 return ret;
3054 }
3055
fruit_stat_rsrc_netatalk(vfs_handle_struct * handle,struct smb_filename * smb_fname,bool follow_links)3056 static int fruit_stat_rsrc_netatalk(vfs_handle_struct *handle,
3057 struct smb_filename *smb_fname,
3058 bool follow_links)
3059 {
3060 struct adouble *ad = NULL;
3061 int ret;
3062
3063 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
3064 if (ad == NULL) {
3065 errno = ENOENT;
3066 return -1;
3067 }
3068
3069 /* Populate the stat struct with info from the base file. */
3070 ret = fruit_stat_base(handle, smb_fname, follow_links);
3071 if (ret != 0) {
3072 TALLOC_FREE(ad);
3073 return -1;
3074 }
3075
3076 smb_fname->st.st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
3077 smb_fname->st.st_ex_ino = hash_inode(&smb_fname->st,
3078 smb_fname->stream_name);
3079 TALLOC_FREE(ad);
3080 return 0;
3081 }
3082
fruit_stat_rsrc_stream(vfs_handle_struct * handle,struct smb_filename * smb_fname,bool follow_links)3083 static int fruit_stat_rsrc_stream(vfs_handle_struct *handle,
3084 struct smb_filename *smb_fname,
3085 bool follow_links)
3086 {
3087 int ret;
3088
3089 if (follow_links) {
3090 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
3091 } else {
3092 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
3093 }
3094
3095 return ret;
3096 }
3097
fruit_stat_rsrc_xattr(vfs_handle_struct * handle,struct smb_filename * smb_fname,bool follow_links)3098 static int fruit_stat_rsrc_xattr(vfs_handle_struct *handle,
3099 struct smb_filename *smb_fname,
3100 bool follow_links)
3101 {
3102 #ifdef HAVE_ATTROPEN
3103 int ret;
3104 int fd = -1;
3105
3106 /* Populate the stat struct with info from the base file. */
3107 ret = fruit_stat_base(handle, smb_fname, follow_links);
3108 if (ret != 0) {
3109 return -1;
3110 }
3111
3112 fd = attropen(smb_fname->base_name,
3113 AFPRESOURCE_EA_NETATALK,
3114 O_RDONLY);
3115 if (fd == -1) {
3116 return 0;
3117 }
3118
3119 ret = sys_fstat(fd, &smb_fname->st, false);
3120 if (ret != 0) {
3121 close(fd);
3122 DBG_ERR("fstat [%s:%s] failed\n", smb_fname->base_name,
3123 AFPRESOURCE_EA_NETATALK);
3124 return -1;
3125 }
3126 close(fd);
3127 fd = -1;
3128
3129 smb_fname->st.st_ex_ino = hash_inode(&smb_fname->st,
3130 smb_fname->stream_name);
3131
3132 return ret;
3133
3134 #else
3135 errno = ENOSYS;
3136 return -1;
3137 #endif
3138 }
3139
fruit_stat_rsrc(vfs_handle_struct * handle,struct smb_filename * smb_fname,bool follow_links)3140 static int fruit_stat_rsrc(vfs_handle_struct *handle,
3141 struct smb_filename *smb_fname,
3142 bool follow_links)
3143 {
3144 struct fruit_config_data *config = NULL;
3145 int ret;
3146
3147 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3148
3149 SMB_VFS_HANDLE_GET_DATA(handle, config,
3150 struct fruit_config_data, return -1);
3151
3152 switch (config->rsrc) {
3153 case FRUIT_RSRC_STREAM:
3154 ret = fruit_stat_rsrc_stream(handle, smb_fname, follow_links);
3155 break;
3156
3157 case FRUIT_RSRC_XATTR:
3158 ret = fruit_stat_rsrc_xattr(handle, smb_fname, follow_links);
3159 break;
3160
3161 case FRUIT_RSRC_ADFILE:
3162 ret = fruit_stat_rsrc_netatalk(handle, smb_fname, follow_links);
3163 break;
3164
3165 default:
3166 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3167 return -1;
3168 }
3169
3170 return ret;
3171 }
3172
fruit_stat(vfs_handle_struct * handle,struct smb_filename * smb_fname)3173 static int fruit_stat(vfs_handle_struct *handle,
3174 struct smb_filename *smb_fname)
3175 {
3176 int rc = -1;
3177
3178 DEBUG(10, ("fruit_stat called for %s\n",
3179 smb_fname_str_dbg(smb_fname)));
3180
3181 if (!is_named_stream(smb_fname)) {
3182 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
3183 if (rc == 0) {
3184 update_btime(handle, smb_fname);
3185 }
3186 return rc;
3187 }
3188
3189 /*
3190 * Note if lp_posix_paths() is true, we can never
3191 * get here as is_ntfs_stream_smb_fname() is
3192 * always false. So we never need worry about
3193 * not following links here.
3194 */
3195
3196 if (is_afpinfo_stream(smb_fname->stream_name)) {
3197 rc = fruit_stat_meta(handle, smb_fname, true);
3198 } else if (is_afpresource_stream(smb_fname->stream_name)) {
3199 rc = fruit_stat_rsrc(handle, smb_fname, true);
3200 } else {
3201 return SMB_VFS_NEXT_STAT(handle, smb_fname);
3202 }
3203
3204 if (rc == 0) {
3205 update_btime(handle, smb_fname);
3206 smb_fname->st.st_ex_mode &= ~S_IFMT;
3207 smb_fname->st.st_ex_mode |= S_IFREG;
3208 smb_fname->st.st_ex_blocks =
3209 smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
3210 }
3211 return rc;
3212 }
3213
fruit_lstat(vfs_handle_struct * handle,struct smb_filename * smb_fname)3214 static int fruit_lstat(vfs_handle_struct *handle,
3215 struct smb_filename *smb_fname)
3216 {
3217 int rc = -1;
3218
3219 DEBUG(10, ("fruit_lstat called for %s\n",
3220 smb_fname_str_dbg(smb_fname)));
3221
3222 if (!is_named_stream(smb_fname)) {
3223 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
3224 if (rc == 0) {
3225 update_btime(handle, smb_fname);
3226 }
3227 return rc;
3228 }
3229
3230 if (is_afpinfo_stream(smb_fname->stream_name)) {
3231 rc = fruit_stat_meta(handle, smb_fname, false);
3232 } else if (is_afpresource_stream(smb_fname->stream_name)) {
3233 rc = fruit_stat_rsrc(handle, smb_fname, false);
3234 } else {
3235 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
3236 }
3237
3238 if (rc == 0) {
3239 update_btime(handle, smb_fname);
3240 smb_fname->st.st_ex_mode &= ~S_IFMT;
3241 smb_fname->st.st_ex_mode |= S_IFREG;
3242 smb_fname->st.st_ex_blocks =
3243 smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
3244 }
3245 return rc;
3246 }
3247
fruit_fstat_meta_stream(vfs_handle_struct * handle,files_struct * fsp,SMB_STRUCT_STAT * sbuf)3248 static int fruit_fstat_meta_stream(vfs_handle_struct *handle,
3249 files_struct *fsp,
3250 SMB_STRUCT_STAT *sbuf)
3251 {
3252 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
3253 struct smb_filename smb_fname;
3254 ino_t ino;
3255 int ret;
3256
3257 if (fio == NULL) {
3258 return -1;
3259 }
3260
3261 if (fio->fake_fd) {
3262 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
3263 if (ret != 0) {
3264 return -1;
3265 }
3266
3267 *sbuf = fsp->base_fsp->fsp_name->st;
3268 sbuf->st_ex_size = AFP_INFO_SIZE;
3269 sbuf->st_ex_ino = hash_inode(sbuf, fsp->fsp_name->stream_name);
3270 return 0;
3271 }
3272
3273 smb_fname = (struct smb_filename) {
3274 .base_name = fsp->fsp_name->base_name,
3275 };
3276
3277 ret = fruit_stat_base(handle, &smb_fname, false);
3278 if (ret != 0) {
3279 return -1;
3280 }
3281 *sbuf = smb_fname.st;
3282
3283 ino = hash_inode(sbuf, fsp->fsp_name->stream_name);
3284
3285 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
3286 if (ret != 0) {
3287 return -1;
3288 }
3289
3290 sbuf->st_ex_ino = ino;
3291 return 0;
3292 }
3293
fruit_fstat_meta_netatalk(vfs_handle_struct * handle,files_struct * fsp,SMB_STRUCT_STAT * sbuf)3294 static int fruit_fstat_meta_netatalk(vfs_handle_struct *handle,
3295 files_struct *fsp,
3296 SMB_STRUCT_STAT *sbuf)
3297 {
3298 int ret;
3299
3300 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
3301 if (ret != 0) {
3302 return -1;
3303 }
3304
3305 *sbuf = fsp->base_fsp->fsp_name->st;
3306 sbuf->st_ex_size = AFP_INFO_SIZE;
3307 sbuf->st_ex_ino = hash_inode(sbuf, fsp->fsp_name->stream_name);
3308
3309 return 0;
3310 }
3311
fruit_fstat_meta(vfs_handle_struct * handle,files_struct * fsp,SMB_STRUCT_STAT * sbuf,struct fio * fio)3312 static int fruit_fstat_meta(vfs_handle_struct *handle,
3313 files_struct *fsp,
3314 SMB_STRUCT_STAT *sbuf,
3315 struct fio *fio)
3316 {
3317 int ret;
3318
3319 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
3320
3321 switch (fio->config->meta) {
3322 case FRUIT_META_STREAM:
3323 ret = fruit_fstat_meta_stream(handle, fsp, sbuf);
3324 break;
3325
3326 case FRUIT_META_NETATALK:
3327 ret = fruit_fstat_meta_netatalk(handle, fsp, sbuf);
3328 break;
3329
3330 default:
3331 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
3332 return -1;
3333 }
3334
3335 DBG_DEBUG("Path [%s] ret [%d]\n", fsp_str_dbg(fsp), ret);
3336 return ret;
3337 }
3338
fruit_fstat_rsrc_xattr(vfs_handle_struct * handle,files_struct * fsp,SMB_STRUCT_STAT * sbuf)3339 static int fruit_fstat_rsrc_xattr(vfs_handle_struct *handle,
3340 files_struct *fsp,
3341 SMB_STRUCT_STAT *sbuf)
3342 {
3343 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
3344 }
3345
fruit_fstat_rsrc_stream(vfs_handle_struct * handle,files_struct * fsp,SMB_STRUCT_STAT * sbuf)3346 static int fruit_fstat_rsrc_stream(vfs_handle_struct *handle,
3347 files_struct *fsp,
3348 SMB_STRUCT_STAT *sbuf)
3349 {
3350 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
3351 }
3352
fruit_fstat_rsrc_adouble(vfs_handle_struct * handle,files_struct * fsp,SMB_STRUCT_STAT * sbuf)3353 static int fruit_fstat_rsrc_adouble(vfs_handle_struct *handle,
3354 files_struct *fsp,
3355 SMB_STRUCT_STAT *sbuf)
3356 {
3357 struct adouble *ad = NULL;
3358 int ret;
3359
3360 /* Populate the stat struct with info from the base file. */
3361 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
3362 if (ret == -1) {
3363 return -1;
3364 }
3365
3366 ad = ad_get(talloc_tos(), handle,
3367 fsp->base_fsp->fsp_name,
3368 ADOUBLE_RSRC);
3369 if (ad == NULL) {
3370 DBG_ERR("ad_get [%s] failed [%s]\n",
3371 fsp_str_dbg(fsp), strerror(errno));
3372 return -1;
3373 }
3374
3375 *sbuf = fsp->base_fsp->fsp_name->st;
3376 sbuf->st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
3377 sbuf->st_ex_ino = hash_inode(sbuf, fsp->fsp_name->stream_name);
3378
3379 TALLOC_FREE(ad);
3380 return 0;
3381 }
3382
fruit_fstat_rsrc(vfs_handle_struct * handle,files_struct * fsp,SMB_STRUCT_STAT * sbuf,struct fio * fio)3383 static int fruit_fstat_rsrc(vfs_handle_struct *handle, files_struct *fsp,
3384 SMB_STRUCT_STAT *sbuf, struct fio *fio)
3385 {
3386 int ret;
3387
3388 switch (fio->config->rsrc) {
3389 case FRUIT_RSRC_STREAM:
3390 ret = fruit_fstat_rsrc_stream(handle, fsp, sbuf);
3391 break;
3392
3393 case FRUIT_RSRC_ADFILE:
3394 ret = fruit_fstat_rsrc_adouble(handle, fsp, sbuf);
3395 break;
3396
3397 case FRUIT_RSRC_XATTR:
3398 ret = fruit_fstat_rsrc_xattr(handle, fsp, sbuf);
3399 break;
3400
3401 default:
3402 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
3403 return -1;
3404 }
3405
3406 return ret;
3407 }
3408
fruit_fstat(vfs_handle_struct * handle,files_struct * fsp,SMB_STRUCT_STAT * sbuf)3409 static int fruit_fstat(vfs_handle_struct *handle, files_struct *fsp,
3410 SMB_STRUCT_STAT *sbuf)
3411 {
3412 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
3413 int rc;
3414
3415 if (fio == NULL) {
3416 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
3417 }
3418
3419 DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
3420
3421 if (fio->type == ADOUBLE_META) {
3422 rc = fruit_fstat_meta(handle, fsp, sbuf, fio);
3423 } else {
3424 rc = fruit_fstat_rsrc(handle, fsp, sbuf, fio);
3425 }
3426
3427 if (rc == 0) {
3428 sbuf->st_ex_mode &= ~S_IFMT;
3429 sbuf->st_ex_mode |= S_IFREG;
3430 sbuf->st_ex_blocks = sbuf->st_ex_size / STAT_ST_BLOCKSIZE + 1;
3431 }
3432
3433 DBG_DEBUG("Path [%s] rc [%d] size [%"PRIdMAX"]\n",
3434 fsp_str_dbg(fsp), rc, (intmax_t)sbuf->st_ex_size);
3435 return rc;
3436 }
3437
delete_invalid_meta_stream(vfs_handle_struct * handle,const struct smb_filename * smb_fname,TALLOC_CTX * mem_ctx,unsigned int * pnum_streams,struct stream_struct ** pstreams,off_t size)3438 static NTSTATUS delete_invalid_meta_stream(
3439 vfs_handle_struct *handle,
3440 const struct smb_filename *smb_fname,
3441 TALLOC_CTX *mem_ctx,
3442 unsigned int *pnum_streams,
3443 struct stream_struct **pstreams,
3444 off_t size)
3445 {
3446 struct smb_filename *sname = NULL;
3447 int ret;
3448 bool ok;
3449
3450 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams, AFPINFO_STREAM);
3451 if (!ok) {
3452 return NT_STATUS_INTERNAL_ERROR;
3453 }
3454
3455 if (size == 0) {
3456 return NT_STATUS_OK;
3457 }
3458
3459 sname = synthetic_smb_fname(talloc_tos(),
3460 smb_fname->base_name,
3461 AFPINFO_STREAM_NAME,
3462 NULL, 0);
3463 if (sname == NULL) {
3464 return NT_STATUS_NO_MEMORY;
3465 }
3466
3467 ret = SMB_VFS_NEXT_UNLINKAT(handle,
3468 handle->conn->cwd_fsp,
3469 sname,
3470 0);
3471 TALLOC_FREE(sname);
3472 if (ret != 0) {
3473 DBG_ERR("Removing [%s] failed\n", smb_fname_str_dbg(sname));
3474 return map_nt_error_from_unix(errno);
3475 }
3476
3477 return NT_STATUS_OK;
3478 }
3479
fruit_streaminfo_meta_stream(vfs_handle_struct * handle,struct files_struct * fsp,const struct smb_filename * smb_fname,TALLOC_CTX * mem_ctx,unsigned int * pnum_streams,struct stream_struct ** pstreams)3480 static NTSTATUS fruit_streaminfo_meta_stream(
3481 vfs_handle_struct *handle,
3482 struct files_struct *fsp,
3483 const struct smb_filename *smb_fname,
3484 TALLOC_CTX *mem_ctx,
3485 unsigned int *pnum_streams,
3486 struct stream_struct **pstreams)
3487 {
3488 struct stream_struct *stream = *pstreams;
3489 unsigned int num_streams = *pnum_streams;
3490 int i;
3491
3492 for (i = 0; i < num_streams; i++) {
3493 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
3494 break;
3495 }
3496 }
3497
3498 if (i == num_streams) {
3499 return NT_STATUS_OK;
3500 }
3501
3502 if (stream[i].size != AFP_INFO_SIZE) {
3503 DBG_ERR("Removing invalid AFPINFO_STREAM size [%jd] from [%s]\n",
3504 (intmax_t)stream[i].size, smb_fname_str_dbg(smb_fname));
3505
3506 return delete_invalid_meta_stream(handle,
3507 smb_fname,
3508 mem_ctx,
3509 pnum_streams,
3510 pstreams,
3511 stream[i].size);
3512 }
3513
3514
3515 return NT_STATUS_OK;
3516 }
3517
fruit_streaminfo_meta_netatalk(vfs_handle_struct * handle,struct files_struct * fsp,const struct smb_filename * smb_fname,TALLOC_CTX * mem_ctx,unsigned int * pnum_streams,struct stream_struct ** pstreams)3518 static NTSTATUS fruit_streaminfo_meta_netatalk(
3519 vfs_handle_struct *handle,
3520 struct files_struct *fsp,
3521 const struct smb_filename *smb_fname,
3522 TALLOC_CTX *mem_ctx,
3523 unsigned int *pnum_streams,
3524 struct stream_struct **pstreams)
3525 {
3526 struct stream_struct *stream = *pstreams;
3527 unsigned int num_streams = *pnum_streams;
3528 struct adouble *ad = NULL;
3529 bool is_fi_empty;
3530 int i;
3531 bool ok;
3532
3533 /* Remove the Netatalk xattr from the list */
3534 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
3535 ":" NETATALK_META_XATTR ":$DATA");
3536 if (!ok) {
3537 return NT_STATUS_NO_MEMORY;
3538 }
3539
3540 /*
3541 * Check if there's a AFPINFO_STREAM from the VFS streams
3542 * backend and if yes, remove it from the list
3543 */
3544 for (i = 0; i < num_streams; i++) {
3545 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
3546 break;
3547 }
3548 }
3549
3550 if (i < num_streams) {
3551 DBG_WARNING("Unexpected AFPINFO_STREAM on [%s]\n",
3552 smb_fname_str_dbg(smb_fname));
3553
3554 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
3555 AFPINFO_STREAM);
3556 if (!ok) {
3557 return NT_STATUS_INTERNAL_ERROR;
3558 }
3559 }
3560
3561 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
3562 if (ad == NULL) {
3563 return NT_STATUS_OK;
3564 }
3565
3566 is_fi_empty = ad_empty_finderinfo(ad);
3567 TALLOC_FREE(ad);
3568
3569 if (is_fi_empty) {
3570 return NT_STATUS_OK;
3571 }
3572
3573 ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
3574 AFPINFO_STREAM_NAME, AFP_INFO_SIZE,
3575 smb_roundup(handle->conn, AFP_INFO_SIZE));
3576 if (!ok) {
3577 return NT_STATUS_NO_MEMORY;
3578 }
3579
3580 return NT_STATUS_OK;
3581 }
3582
fruit_streaminfo_meta(vfs_handle_struct * handle,struct files_struct * fsp,const struct smb_filename * smb_fname,TALLOC_CTX * mem_ctx,unsigned int * pnum_streams,struct stream_struct ** pstreams)3583 static NTSTATUS fruit_streaminfo_meta(vfs_handle_struct *handle,
3584 struct files_struct *fsp,
3585 const struct smb_filename *smb_fname,
3586 TALLOC_CTX *mem_ctx,
3587 unsigned int *pnum_streams,
3588 struct stream_struct **pstreams)
3589 {
3590 struct fruit_config_data *config = NULL;
3591 NTSTATUS status;
3592
3593 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
3594 return NT_STATUS_INTERNAL_ERROR);
3595
3596 switch (config->meta) {
3597 case FRUIT_META_NETATALK:
3598 status = fruit_streaminfo_meta_netatalk(handle, fsp, smb_fname,
3599 mem_ctx, pnum_streams,
3600 pstreams);
3601 break;
3602
3603 case FRUIT_META_STREAM:
3604 status = fruit_streaminfo_meta_stream(handle, fsp, smb_fname,
3605 mem_ctx, pnum_streams,
3606 pstreams);
3607 break;
3608
3609 default:
3610 return NT_STATUS_INTERNAL_ERROR;
3611 }
3612
3613 return status;
3614 }
3615
fruit_streaminfo_rsrc_stream(vfs_handle_struct * handle,struct files_struct * fsp,const struct smb_filename * smb_fname,TALLOC_CTX * mem_ctx,unsigned int * pnum_streams,struct stream_struct ** pstreams)3616 static NTSTATUS fruit_streaminfo_rsrc_stream(
3617 vfs_handle_struct *handle,
3618 struct files_struct *fsp,
3619 const struct smb_filename *smb_fname,
3620 TALLOC_CTX *mem_ctx,
3621 unsigned int *pnum_streams,
3622 struct stream_struct **pstreams)
3623 {
3624 bool ok;
3625
3626 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
3627 if (!ok) {
3628 DBG_ERR("Filtering resource stream failed\n");
3629 return NT_STATUS_INTERNAL_ERROR;
3630 }
3631 return NT_STATUS_OK;
3632 }
3633
fruit_streaminfo_rsrc_xattr(vfs_handle_struct * handle,struct files_struct * fsp,const struct smb_filename * smb_fname,TALLOC_CTX * mem_ctx,unsigned int * pnum_streams,struct stream_struct ** pstreams)3634 static NTSTATUS fruit_streaminfo_rsrc_xattr(
3635 vfs_handle_struct *handle,
3636 struct files_struct *fsp,
3637 const struct smb_filename *smb_fname,
3638 TALLOC_CTX *mem_ctx,
3639 unsigned int *pnum_streams,
3640 struct stream_struct **pstreams)
3641 {
3642 bool ok;
3643
3644 ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
3645 if (!ok) {
3646 DBG_ERR("Filtering resource stream failed\n");
3647 return NT_STATUS_INTERNAL_ERROR;
3648 }
3649 return NT_STATUS_OK;
3650 }
3651
fruit_streaminfo_rsrc_adouble(vfs_handle_struct * handle,struct files_struct * fsp,const struct smb_filename * smb_fname,TALLOC_CTX * mem_ctx,unsigned int * pnum_streams,struct stream_struct ** pstreams)3652 static NTSTATUS fruit_streaminfo_rsrc_adouble(
3653 vfs_handle_struct *handle,
3654 struct files_struct *fsp,
3655 const struct smb_filename *smb_fname,
3656 TALLOC_CTX *mem_ctx,
3657 unsigned int *pnum_streams,
3658 struct stream_struct **pstreams)
3659 {
3660 struct stream_struct *stream = *pstreams;
3661 unsigned int num_streams = *pnum_streams;
3662 struct adouble *ad = NULL;
3663 bool ok;
3664 size_t rlen;
3665 int i;
3666
3667 /*
3668 * Check if there's a AFPRESOURCE_STREAM from the VFS streams backend
3669 * and if yes, remove it from the list
3670 */
3671 for (i = 0; i < num_streams; i++) {
3672 if (strequal_m(stream[i].name, AFPRESOURCE_STREAM)) {
3673 break;
3674 }
3675 }
3676
3677 if (i < num_streams) {
3678 DBG_WARNING("Unexpected AFPRESOURCE_STREAM on [%s]\n",
3679 smb_fname_str_dbg(smb_fname));
3680
3681 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
3682 AFPRESOURCE_STREAM);
3683 if (!ok) {
3684 return NT_STATUS_INTERNAL_ERROR;
3685 }
3686 }
3687
3688 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
3689 if (ad == NULL) {
3690 return NT_STATUS_OK;
3691 }
3692
3693 rlen = ad_getentrylen(ad, ADEID_RFORK);
3694 TALLOC_FREE(ad);
3695
3696 if (rlen == 0) {
3697 return NT_STATUS_OK;
3698 }
3699
3700 ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
3701 AFPRESOURCE_STREAM_NAME, rlen,
3702 smb_roundup(handle->conn, rlen));
3703 if (!ok) {
3704 return NT_STATUS_NO_MEMORY;
3705 }
3706
3707 return NT_STATUS_OK;
3708 }
3709
fruit_streaminfo_rsrc(vfs_handle_struct * handle,struct files_struct * fsp,const struct smb_filename * smb_fname,TALLOC_CTX * mem_ctx,unsigned int * pnum_streams,struct stream_struct ** pstreams)3710 static NTSTATUS fruit_streaminfo_rsrc(vfs_handle_struct *handle,
3711 struct files_struct *fsp,
3712 const struct smb_filename *smb_fname,
3713 TALLOC_CTX *mem_ctx,
3714 unsigned int *pnum_streams,
3715 struct stream_struct **pstreams)
3716 {
3717 struct fruit_config_data *config = NULL;
3718 NTSTATUS status;
3719
3720 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
3721 return NT_STATUS_INTERNAL_ERROR);
3722
3723 switch (config->rsrc) {
3724 case FRUIT_RSRC_STREAM:
3725 status = fruit_streaminfo_rsrc_stream(handle, fsp, smb_fname,
3726 mem_ctx, pnum_streams,
3727 pstreams);
3728 break;
3729
3730 case FRUIT_RSRC_XATTR:
3731 status = fruit_streaminfo_rsrc_xattr(handle, fsp, smb_fname,
3732 mem_ctx, pnum_streams,
3733 pstreams);
3734 break;
3735
3736 case FRUIT_RSRC_ADFILE:
3737 status = fruit_streaminfo_rsrc_adouble(handle, fsp, smb_fname,
3738 mem_ctx, pnum_streams,
3739 pstreams);
3740 break;
3741
3742 default:
3743 return NT_STATUS_INTERNAL_ERROR;
3744 }
3745
3746 return status;
3747 }
3748
fruit_filter_empty_streams(unsigned int * pnum_streams,struct stream_struct ** pstreams)3749 static void fruit_filter_empty_streams(unsigned int *pnum_streams,
3750 struct stream_struct **pstreams)
3751 {
3752 unsigned num_streams = *pnum_streams;
3753 struct stream_struct *streams = *pstreams;
3754 unsigned i = 0;
3755
3756 if (!global_fruit_config.nego_aapl) {
3757 return;
3758 }
3759
3760 while (i < num_streams) {
3761 struct smb_filename smb_fname = (struct smb_filename) {
3762 .stream_name = streams[i].name,
3763 };
3764
3765 if (is_ntfs_default_stream_smb_fname(&smb_fname)
3766 || streams[i].size > 0)
3767 {
3768 i++;
3769 continue;
3770 }
3771
3772 streams[i] = streams[num_streams - 1];
3773 num_streams--;
3774 }
3775
3776 *pnum_streams = num_streams;
3777 }
3778
fruit_streaminfo(vfs_handle_struct * handle,struct files_struct * fsp,const struct smb_filename * smb_fname,TALLOC_CTX * mem_ctx,unsigned int * pnum_streams,struct stream_struct ** pstreams)3779 static NTSTATUS fruit_streaminfo(vfs_handle_struct *handle,
3780 struct files_struct *fsp,
3781 const struct smb_filename *smb_fname,
3782 TALLOC_CTX *mem_ctx,
3783 unsigned int *pnum_streams,
3784 struct stream_struct **pstreams)
3785 {
3786 struct fruit_config_data *config = NULL;
3787 NTSTATUS status;
3788
3789 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
3790 return NT_STATUS_UNSUCCESSFUL);
3791
3792 DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3793
3794 status = SMB_VFS_NEXT_STREAMINFO(handle, fsp, smb_fname, mem_ctx,
3795 pnum_streams, pstreams);
3796 if (!NT_STATUS_IS_OK(status)) {
3797 return status;
3798 }
3799
3800 fruit_filter_empty_streams(pnum_streams, pstreams);
3801
3802 status = fruit_streaminfo_meta(handle, fsp, smb_fname,
3803 mem_ctx, pnum_streams, pstreams);
3804 if (!NT_STATUS_IS_OK(status)) {
3805 return status;
3806 }
3807
3808 status = fruit_streaminfo_rsrc(handle, fsp, smb_fname,
3809 mem_ctx, pnum_streams, pstreams);
3810 if (!NT_STATUS_IS_OK(status)) {
3811 return status;
3812 }
3813
3814 return NT_STATUS_OK;
3815 }
3816
fruit_ntimes(vfs_handle_struct * handle,const struct smb_filename * smb_fname,struct smb_file_time * ft)3817 static int fruit_ntimes(vfs_handle_struct *handle,
3818 const struct smb_filename *smb_fname,
3819 struct smb_file_time *ft)
3820 {
3821 int rc = 0;
3822 struct adouble *ad = NULL;
3823 struct fruit_config_data *config = NULL;
3824
3825 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
3826 return -1);
3827
3828 if ((config->meta != FRUIT_META_NETATALK) ||
3829 is_omit_timespec(&ft->create_time))
3830 {
3831 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
3832 }
3833
3834 DEBUG(10,("set btime for %s to %s\n", smb_fname_str_dbg(smb_fname),
3835 time_to_asc(convert_timespec_to_time_t(ft->create_time))));
3836
3837 ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
3838 if (ad == NULL) {
3839 goto exit;
3840 }
3841
3842 ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX,
3843 convert_time_t_to_uint32_t(ft->create_time.tv_sec));
3844
3845 rc = ad_set(handle, ad, smb_fname);
3846
3847 exit:
3848
3849 TALLOC_FREE(ad);
3850 if (rc != 0) {
3851 DEBUG(1, ("fruit_ntimes: %s\n", smb_fname_str_dbg(smb_fname)));
3852 return -1;
3853 }
3854 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
3855 }
3856
fruit_fallocate(struct vfs_handle_struct * handle,struct files_struct * fsp,uint32_t mode,off_t offset,off_t len)3857 static int fruit_fallocate(struct vfs_handle_struct *handle,
3858 struct files_struct *fsp,
3859 uint32_t mode,
3860 off_t offset,
3861 off_t len)
3862 {
3863 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
3864
3865 if (fio == NULL) {
3866 return SMB_VFS_NEXT_FALLOCATE(handle, fsp, mode, offset, len);
3867 }
3868
3869 /* Let the pwrite code path handle it. */
3870 errno = ENOSYS;
3871 return -1;
3872 }
3873
fruit_ftruncate_rsrc_xattr(struct vfs_handle_struct * handle,struct files_struct * fsp,off_t offset)3874 static int fruit_ftruncate_rsrc_xattr(struct vfs_handle_struct *handle,
3875 struct files_struct *fsp,
3876 off_t offset)
3877 {
3878 #ifdef HAVE_ATTROPEN
3879 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
3880 #endif
3881 return 0;
3882 }
3883
fruit_ftruncate_rsrc_adouble(struct vfs_handle_struct * handle,struct files_struct * fsp,off_t offset)3884 static int fruit_ftruncate_rsrc_adouble(struct vfs_handle_struct *handle,
3885 struct files_struct *fsp,
3886 off_t offset)
3887 {
3888 int rc;
3889 struct adouble *ad = NULL;
3890 off_t ad_off;
3891
3892 ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
3893 if (ad == NULL) {
3894 DBG_DEBUG("ad_get [%s] failed [%s]\n",
3895 fsp_str_dbg(fsp), strerror(errno));
3896 return -1;
3897 }
3898
3899 ad_off = ad_getentryoff(ad, ADEID_RFORK);
3900
3901 rc = ftruncate(fsp->fh->fd, offset + ad_off);
3902 if (rc != 0) {
3903 TALLOC_FREE(ad);
3904 return -1;
3905 }
3906
3907 ad_setentrylen(ad, ADEID_RFORK, offset);
3908
3909 rc = ad_fset(handle, ad, fsp);
3910 if (rc != 0) {
3911 DBG_ERR("ad_fset [%s] failed [%s]\n",
3912 fsp_str_dbg(fsp), strerror(errno));
3913 TALLOC_FREE(ad);
3914 return -1;
3915 }
3916
3917 TALLOC_FREE(ad);
3918 return 0;
3919 }
3920
fruit_ftruncate_rsrc_stream(struct vfs_handle_struct * handle,struct files_struct * fsp,off_t offset)3921 static int fruit_ftruncate_rsrc_stream(struct vfs_handle_struct *handle,
3922 struct files_struct *fsp,
3923 off_t offset)
3924 {
3925 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
3926 }
3927
fruit_ftruncate_rsrc(struct vfs_handle_struct * handle,struct files_struct * fsp,off_t offset)3928 static int fruit_ftruncate_rsrc(struct vfs_handle_struct *handle,
3929 struct files_struct *fsp,
3930 off_t offset)
3931 {
3932 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
3933 int ret;
3934
3935 if (fio == NULL) {
3936 DBG_ERR("Failed to fetch fsp extension");
3937 return -1;
3938 }
3939
3940 switch (fio->config->rsrc) {
3941 case FRUIT_RSRC_XATTR:
3942 ret = fruit_ftruncate_rsrc_xattr(handle, fsp, offset);
3943 break;
3944
3945 case FRUIT_RSRC_ADFILE:
3946 ret = fruit_ftruncate_rsrc_adouble(handle, fsp, offset);
3947 break;
3948
3949 case FRUIT_RSRC_STREAM:
3950 ret = fruit_ftruncate_rsrc_stream(handle, fsp, offset);
3951 break;
3952
3953 default:
3954 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
3955 return -1;
3956 }
3957
3958
3959 return ret;
3960 }
3961
fruit_ftruncate_meta(struct vfs_handle_struct * handle,struct files_struct * fsp,off_t offset)3962 static int fruit_ftruncate_meta(struct vfs_handle_struct *handle,
3963 struct files_struct *fsp,
3964 off_t offset)
3965 {
3966 if (offset > 60) {
3967 DBG_WARNING("ftruncate %s to %jd",
3968 fsp_str_dbg(fsp), (intmax_t)offset);
3969 /* OS X returns NT_STATUS_ALLOTTED_SPACE_EXCEEDED */
3970 errno = EOVERFLOW;
3971 return -1;
3972 }
3973
3974 /* OS X returns success but does nothing */
3975 DBG_INFO("ignoring ftruncate %s to %jd\n",
3976 fsp_str_dbg(fsp), (intmax_t)offset);
3977 return 0;
3978 }
3979
fruit_ftruncate(struct vfs_handle_struct * handle,struct files_struct * fsp,off_t offset)3980 static int fruit_ftruncate(struct vfs_handle_struct *handle,
3981 struct files_struct *fsp,
3982 off_t offset)
3983 {
3984 struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
3985 int ret;
3986
3987 DBG_DEBUG("Path [%s] offset [%"PRIdMAX"]\n", fsp_str_dbg(fsp),
3988 (intmax_t)offset);
3989
3990 if (fio == NULL) {
3991 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
3992 }
3993
3994 if (fio->type == ADOUBLE_META) {
3995 ret = fruit_ftruncate_meta(handle, fsp, offset);
3996 } else {
3997 ret = fruit_ftruncate_rsrc(handle, fsp, offset);
3998 }
3999
4000 DBG_DEBUG("Path [%s] result [%d]\n", fsp_str_dbg(fsp), ret);
4001 return ret;
4002 }
4003
fruit_create_file(vfs_handle_struct * handle,struct smb_request * req,uint16_t root_dir_fid,struct smb_filename * smb_fname,uint32_t access_mask,uint32_t share_access,uint32_t create_disposition,uint32_t create_options,uint32_t file_attributes,uint32_t oplock_request,const struct smb2_lease * lease,uint64_t allocation_size,uint32_t private_flags,struct security_descriptor * sd,struct ea_list * ea_list,files_struct ** result,int * pinfo,const struct smb2_create_blobs * in_context_blobs,struct smb2_create_blobs * out_context_blobs)4004 static NTSTATUS fruit_create_file(vfs_handle_struct *handle,
4005 struct smb_request *req,
4006 uint16_t root_dir_fid,
4007 struct smb_filename *smb_fname,
4008 uint32_t access_mask,
4009 uint32_t share_access,
4010 uint32_t create_disposition,
4011 uint32_t create_options,
4012 uint32_t file_attributes,
4013 uint32_t oplock_request,
4014 const struct smb2_lease *lease,
4015 uint64_t allocation_size,
4016 uint32_t private_flags,
4017 struct security_descriptor *sd,
4018 struct ea_list *ea_list,
4019 files_struct **result,
4020 int *pinfo,
4021 const struct smb2_create_blobs *in_context_blobs,
4022 struct smb2_create_blobs *out_context_blobs)
4023 {
4024 NTSTATUS status;
4025 struct fruit_config_data *config = NULL;
4026 files_struct *fsp = NULL;
4027 bool internal_open = (oplock_request & INTERNAL_OPEN_ONLY);
4028 int ret;
4029
4030 status = check_aapl(handle, req, in_context_blobs, out_context_blobs);
4031 if (!NT_STATUS_IS_OK(status)) {
4032 goto fail;
4033 }
4034
4035 SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
4036 return NT_STATUS_UNSUCCESSFUL);
4037
4038 if (is_apple_stream(smb_fname->stream_name) && !internal_open) {
4039 uint32_t conv_flags = 0;
4040
4041 if (config->wipe_intentionally_left_blank_rfork) {
4042 conv_flags |= AD_CONV_WIPE_BLANK;
4043 }
4044 if (config->delete_empty_adfiles) {
4045 conv_flags |= AD_CONV_DELETE;
4046 }
4047
4048 ret = ad_convert(handle,
4049 handle->conn->cwd_fsp,
4050 smb_fname,
4051 macos_string_replace_map,
4052 conv_flags);
4053 if (ret != 0) {
4054 DBG_ERR("ad_convert() failed\n");
4055 return NT_STATUS_UNSUCCESSFUL;
4056 }
4057 }
4058
4059 status = SMB_VFS_NEXT_CREATE_FILE(
4060 handle, req, root_dir_fid, smb_fname,
4061 access_mask, share_access,
4062 create_disposition, create_options,
4063 file_attributes, oplock_request,
4064 lease,
4065 allocation_size, private_flags,
4066 sd, ea_list, result,
4067 pinfo, in_context_blobs, out_context_blobs);
4068 if (!NT_STATUS_IS_OK(status)) {
4069 return status;
4070 }
4071
4072 fsp = *result;
4073
4074 if (global_fruit_config.nego_aapl) {
4075 if (config->posix_rename && fsp->is_directory) {
4076 /*
4077 * Enable POSIX directory rename behaviour
4078 */
4079 fsp->posix_flags |= FSP_POSIX_FLAGS_RENAME;
4080 }
4081 }
4082
4083 /*
4084 * If this is a plain open for existing files, opening an 0
4085 * byte size resource fork MUST fail with
4086 * NT_STATUS_OBJECT_NAME_NOT_FOUND.
4087 *
4088 * Cf the vfs_fruit torture tests in test_rfork_create().
4089 */
4090 if (global_fruit_config.nego_aapl &&
4091 create_disposition == FILE_OPEN &&
4092 smb_fname->st.st_ex_size == 0 &&
4093 is_named_stream(smb_fname))
4094 {
4095 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
4096 goto fail;
4097 }
4098
4099 if (is_named_stream(smb_fname)
4100 || fsp->is_directory) {
4101 return status;
4102 }
4103
4104 if ((config->locking == FRUIT_LOCKING_NETATALK) &&
4105 (fsp->op != NULL))
4106 {
4107 status = fruit_check_access(
4108 handle, *result,
4109 access_mask,
4110 share_access);
4111 if (!NT_STATUS_IS_OK(status)) {
4112 goto fail;
4113 }
4114 }
4115
4116 return status;
4117
4118 fail:
4119 DEBUG(10, ("fruit_create_file: %s\n", nt_errstr(status)));
4120
4121 if (fsp) {
4122 close_file(req, fsp, ERROR_CLOSE);
4123 *result = fsp = NULL;
4124 }
4125
4126 return status;
4127 }
4128
fruit_readdir_attr(struct vfs_handle_struct * handle,const struct smb_filename * fname,TALLOC_CTX * mem_ctx,struct readdir_attr_data ** pattr_data)4129 static NTSTATUS fruit_readdir_attr(struct vfs_handle_struct *handle,
4130 const struct smb_filename *fname,
4131 TALLOC_CTX *mem_ctx,
4132 struct readdir_attr_data **pattr_data)
4133 {
4134 struct fruit_config_data *config = NULL;
4135 struct readdir_attr_data *attr_data;
4136 uint32_t conv_flags = 0;
4137 NTSTATUS status;
4138 int ret;
4139
4140 SMB_VFS_HANDLE_GET_DATA(handle, config,
4141 struct fruit_config_data,
4142 return NT_STATUS_UNSUCCESSFUL);
4143
4144 if (!global_fruit_config.nego_aapl) {
4145 return SMB_VFS_NEXT_READDIR_ATTR(handle, fname, mem_ctx, pattr_data);
4146 }
4147
4148 DEBUG(10, ("fruit_readdir_attr %s\n", fname->base_name));
4149
4150 if (config->wipe_intentionally_left_blank_rfork) {
4151 conv_flags |= AD_CONV_WIPE_BLANK;
4152 }
4153 if (config->delete_empty_adfiles) {
4154 conv_flags |= AD_CONV_DELETE;
4155 }
4156
4157 ret = ad_convert(handle,
4158 handle->conn->cwd_fsp,
4159 fname,
4160 macos_string_replace_map,
4161 conv_flags);
4162 if (ret != 0) {
4163 DBG_ERR("ad_convert() failed\n");
4164 return NT_STATUS_UNSUCCESSFUL;
4165 }
4166
4167 *pattr_data = talloc_zero(mem_ctx, struct readdir_attr_data);
4168 if (*pattr_data == NULL) {
4169 return NT_STATUS_UNSUCCESSFUL;
4170 }
4171 attr_data = *pattr_data;
4172 attr_data->type = RDATTR_AAPL;
4173
4174 /*
4175 * Mac metadata: compressed FinderInfo, resource fork length
4176 * and creation date
4177 */
4178 status = readdir_attr_macmeta(handle, fname, attr_data);
4179 if (!NT_STATUS_IS_OK(status)) {
4180 /*
4181 * Error handling is tricky: if we return failure from
4182 * this function, the corresponding directory entry
4183 * will to be passed to the client, so we really just
4184 * want to error out on fatal errors.
4185 */
4186 if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
4187 goto fail;
4188 }
4189 }
4190
4191 /*
4192 * UNIX mode
4193 */
4194 if (config->unix_info_enabled) {
4195 attr_data->attr_data.aapl.unix_mode = fname->st.st_ex_mode;
4196 }
4197
4198 /*
4199 * max_access
4200 */
4201 if (!config->readdir_attr_max_access) {
4202 attr_data->attr_data.aapl.max_access = FILE_GENERIC_ALL;
4203 } else {
4204 status = smbd_calculate_access_mask(
4205 handle->conn,
4206 fname,
4207 false,
4208 SEC_FLAG_MAXIMUM_ALLOWED,
4209 &attr_data->attr_data.aapl.max_access);
4210 if (!NT_STATUS_IS_OK(status)) {
4211 goto fail;
4212 }
4213 }
4214
4215 return NT_STATUS_OK;
4216
4217 fail:
4218 DEBUG(1, ("fruit_readdir_attr %s, error: %s\n",
4219 fname->base_name, nt_errstr(status)));
4220 TALLOC_FREE(*pattr_data);
4221 return status;
4222 }
4223
fruit_fget_nt_acl(vfs_handle_struct * handle,files_struct * fsp,uint32_t security_info,TALLOC_CTX * mem_ctx,struct security_descriptor ** ppdesc)4224 static NTSTATUS fruit_fget_nt_acl(vfs_handle_struct *handle,
4225 files_struct *fsp,
4226 uint32_t security_info,
4227 TALLOC_CTX *mem_ctx,
4228 struct security_descriptor **ppdesc)
4229 {
4230 NTSTATUS status;
4231 struct security_ace ace;
4232 struct dom_sid sid;
4233 struct fruit_config_data *config;
4234
4235 SMB_VFS_HANDLE_GET_DATA(handle, config,
4236 struct fruit_config_data,
4237 return NT_STATUS_UNSUCCESSFUL);
4238
4239 status = SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
4240 mem_ctx, ppdesc);
4241 if (!NT_STATUS_IS_OK(status)) {
4242 return status;
4243 }
4244
4245 /*
4246 * Add MS NFS style ACEs with uid, gid and mode
4247 */
4248 if (!global_fruit_config.nego_aapl) {
4249 return NT_STATUS_OK;
4250 }
4251 if (!config->unix_info_enabled) {
4252 return NT_STATUS_OK;
4253 }
4254
4255 /* First remove any existing ACE's with NFS style mode/uid/gid SIDs. */
4256 status = remove_virtual_nfs_aces(*ppdesc);
4257 if (!NT_STATUS_IS_OK(status)) {
4258 DBG_WARNING("failed to remove MS NFS style ACEs\n");
4259 return status;
4260 }
4261
4262 /* MS NFS style mode */
4263 sid_compose(&sid, &global_sid_Unix_NFS_Mode, fsp->fsp_name->st.st_ex_mode);
4264 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
4265 status = security_descriptor_dacl_add(*ppdesc, &ace);
4266 if (!NT_STATUS_IS_OK(status)) {
4267 DEBUG(1,("failed to add MS NFS style ACE\n"));
4268 return status;
4269 }
4270
4271 /* MS NFS style uid */
4272 sid_compose(&sid, &global_sid_Unix_NFS_Users, fsp->fsp_name->st.st_ex_uid);
4273 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
4274 status = security_descriptor_dacl_add(*ppdesc, &ace);
4275 if (!NT_STATUS_IS_OK(status)) {
4276 DEBUG(1,("failed to add MS NFS style ACE\n"));
4277 return status;
4278 }
4279
4280 /* MS NFS style gid */
4281 sid_compose(&sid, &global_sid_Unix_NFS_Groups, fsp->fsp_name->st.st_ex_gid);
4282 init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
4283 status = security_descriptor_dacl_add(*ppdesc, &ace);
4284 if (!NT_STATUS_IS_OK(status)) {
4285 DEBUG(1,("failed to add MS NFS style ACE\n"));
4286 return status;
4287 }
4288
4289 return NT_STATUS_OK;
4290 }
4291
fruit_fset_nt_acl(vfs_handle_struct * handle,files_struct * fsp,uint32_t security_info_sent,const struct security_descriptor * orig_psd)4292 static NTSTATUS fruit_fset_nt_acl(vfs_handle_struct *handle,
4293 files_struct *fsp,
4294 uint32_t security_info_sent,
4295 const struct security_descriptor *orig_psd)
4296 {
4297 NTSTATUS status;
4298 bool do_chmod;
4299 mode_t ms_nfs_mode = 0;
4300 int result;
4301 struct security_descriptor *psd = NULL;
4302 uint32_t orig_num_aces = 0;
4303
4304 if (orig_psd->dacl != NULL) {
4305 orig_num_aces = orig_psd->dacl->num_aces;
4306 }
4307
4308 psd = security_descriptor_copy(talloc_tos(), orig_psd);
4309 if (psd == NULL) {
4310 return NT_STATUS_NO_MEMORY;
4311 }
4312
4313 DBG_DEBUG("fruit_fset_nt_acl: %s\n", fsp_str_dbg(fsp));
4314
4315 status = check_ms_nfs(handle, fsp, psd, &ms_nfs_mode, &do_chmod);
4316 if (!NT_STATUS_IS_OK(status)) {
4317 DEBUG(1, ("fruit_fset_nt_acl: check_ms_nfs failed%s\n", fsp_str_dbg(fsp)));
4318 TALLOC_FREE(psd);
4319 return status;
4320 }
4321
4322 /*
4323 * If only ms_nfs ACE entries were sent, ensure we set the DACL
4324 * sent/present flags correctly now we've removed them.
4325 */
4326
4327 if (orig_num_aces != 0) {
4328 /*
4329 * Are there any ACE's left ?
4330 */
4331 if (psd->dacl->num_aces == 0) {
4332 /* No - clear the DACL sent/present flags. */
4333 security_info_sent &= ~SECINFO_DACL;
4334 psd->type &= ~SEC_DESC_DACL_PRESENT;
4335 }
4336 }
4337
4338 status = SMB_VFS_NEXT_FSET_NT_ACL(handle, fsp, security_info_sent, psd);
4339 if (!NT_STATUS_IS_OK(status)) {
4340 DEBUG(1, ("fruit_fset_nt_acl: SMB_VFS_NEXT_FSET_NT_ACL failed%s\n", fsp_str_dbg(fsp)));
4341 TALLOC_FREE(psd);
4342 return status;
4343 }
4344
4345 if (do_chmod) {
4346 result = SMB_VFS_FCHMOD(fsp, ms_nfs_mode);
4347 if (result != 0) {
4348 DBG_WARNING("%s, result: %d, %04o error %s\n",
4349 fsp_str_dbg(fsp),
4350 result,
4351 (unsigned)ms_nfs_mode,
4352 strerror(errno));
4353 status = map_nt_error_from_unix(errno);
4354 TALLOC_FREE(psd);
4355 return status;
4356 }
4357 }
4358
4359 TALLOC_FREE(psd);
4360 return NT_STATUS_OK;
4361 }
4362
4363 static struct vfs_offload_ctx *fruit_offload_ctx;
4364
4365 struct fruit_offload_read_state {
4366 struct vfs_handle_struct *handle;
4367 struct tevent_context *ev;
4368 files_struct *fsp;
4369 uint32_t fsctl;
4370 DATA_BLOB token;
4371 };
4372
4373 static void fruit_offload_read_done(struct tevent_req *subreq);
4374
fruit_offload_read_send(TALLOC_CTX * mem_ctx,struct tevent_context * ev,struct vfs_handle_struct * handle,files_struct * fsp,uint32_t fsctl,uint32_t ttl,off_t offset,size_t to_copy)4375 static struct tevent_req *fruit_offload_read_send(
4376 TALLOC_CTX *mem_ctx,
4377 struct tevent_context *ev,
4378 struct vfs_handle_struct *handle,
4379 files_struct *fsp,
4380 uint32_t fsctl,
4381 uint32_t ttl,
4382 off_t offset,
4383 size_t to_copy)
4384 {
4385 struct tevent_req *req = NULL;
4386 struct tevent_req *subreq = NULL;
4387 struct fruit_offload_read_state *state = NULL;
4388
4389 req = tevent_req_create(mem_ctx, &state,
4390 struct fruit_offload_read_state);
4391 if (req == NULL) {
4392 return NULL;
4393 }
4394 *state = (struct fruit_offload_read_state) {
4395 .handle = handle,
4396 .ev = ev,
4397 .fsp = fsp,
4398 .fsctl = fsctl,
4399 };
4400
4401 subreq = SMB_VFS_NEXT_OFFLOAD_READ_SEND(mem_ctx, ev, handle, fsp,
4402 fsctl, ttl, offset, to_copy);
4403 if (tevent_req_nomem(subreq, req)) {
4404 return tevent_req_post(req, ev);
4405 }
4406 tevent_req_set_callback(subreq, fruit_offload_read_done, req);
4407 return req;
4408 }
4409
fruit_offload_read_done(struct tevent_req * subreq)4410 static void fruit_offload_read_done(struct tevent_req *subreq)
4411 {
4412 struct tevent_req *req = tevent_req_callback_data(
4413 subreq, struct tevent_req);
4414 struct fruit_offload_read_state *state = tevent_req_data(
4415 req, struct fruit_offload_read_state);
4416 NTSTATUS status;
4417
4418 status = SMB_VFS_NEXT_OFFLOAD_READ_RECV(subreq,
4419 state->handle,
4420 state,
4421 &state->token);
4422 TALLOC_FREE(subreq);
4423 if (tevent_req_nterror(req, status)) {
4424 return;
4425 }
4426
4427 if (state->fsctl != FSCTL_SRV_REQUEST_RESUME_KEY) {
4428 tevent_req_done(req);
4429 return;
4430 }
4431
4432 status = vfs_offload_token_ctx_init(state->fsp->conn->sconn->client,
4433 &fruit_offload_ctx);
4434 if (tevent_req_nterror(req, status)) {
4435 return;
4436 }
4437
4438 status = vfs_offload_token_db_store_fsp(fruit_offload_ctx,
4439 state->fsp,
4440 &state->token);
4441 if (tevent_req_nterror(req, status)) {
4442 return;
4443 }
4444
4445 tevent_req_done(req);
4446 return;
4447 }
4448
fruit_offload_read_recv(struct tevent_req * req,struct vfs_handle_struct * handle,TALLOC_CTX * mem_ctx,DATA_BLOB * token)4449 static NTSTATUS fruit_offload_read_recv(struct tevent_req *req,
4450 struct vfs_handle_struct *handle,
4451 TALLOC_CTX *mem_ctx,
4452 DATA_BLOB *token)
4453 {
4454 struct fruit_offload_read_state *state = tevent_req_data(
4455 req, struct fruit_offload_read_state);
4456 NTSTATUS status;
4457
4458 if (tevent_req_is_nterror(req, &status)) {
4459 tevent_req_received(req);
4460 return status;
4461 }
4462
4463 token->length = state->token.length;
4464 token->data = talloc_move(mem_ctx, &state->token.data);
4465
4466 tevent_req_received(req);
4467 return NT_STATUS_OK;
4468 }
4469
4470 struct fruit_offload_write_state {
4471 struct vfs_handle_struct *handle;
4472 off_t copied;
4473 struct files_struct *src_fsp;
4474 struct files_struct *dst_fsp;
4475 bool is_copyfile;
4476 };
4477
4478 static void fruit_offload_write_done(struct tevent_req *subreq);
fruit_offload_write_send(struct vfs_handle_struct * handle,TALLOC_CTX * mem_ctx,struct tevent_context * ev,uint32_t fsctl,DATA_BLOB * token,off_t transfer_offset,struct files_struct * dest_fsp,off_t dest_off,off_t num)4479 static struct tevent_req *fruit_offload_write_send(struct vfs_handle_struct *handle,
4480 TALLOC_CTX *mem_ctx,
4481 struct tevent_context *ev,
4482 uint32_t fsctl,
4483 DATA_BLOB *token,
4484 off_t transfer_offset,
4485 struct files_struct *dest_fsp,
4486 off_t dest_off,
4487 off_t num)
4488 {
4489 struct tevent_req *req, *subreq;
4490 struct fruit_offload_write_state *state;
4491 NTSTATUS status;
4492 struct fruit_config_data *config;
4493 off_t src_off = transfer_offset;
4494 files_struct *src_fsp = NULL;
4495 off_t to_copy = num;
4496 bool copyfile_enabled = false;
4497
4498 DEBUG(10,("soff: %ju, doff: %ju, len: %ju\n",
4499 (uintmax_t)src_off, (uintmax_t)dest_off, (uintmax_t)num));
4500
4501 SMB_VFS_HANDLE_GET_DATA(handle, config,
4502 struct fruit_config_data,
4503 return NULL);
4504
4505 req = tevent_req_create(mem_ctx, &state,
4506 struct fruit_offload_write_state);
4507 if (req == NULL) {
4508 return NULL;
4509 }
4510 state->handle = handle;
4511 state->dst_fsp = dest_fsp;
4512
4513 switch (fsctl) {
4514 case FSCTL_SRV_COPYCHUNK:
4515 case FSCTL_SRV_COPYCHUNK_WRITE:
4516 copyfile_enabled = config->copyfile_enabled;
4517 break;
4518 default:
4519 break;
4520 }
4521
4522 /*
4523 * Check if this a OS X copyfile style copychunk request with
4524 * a requested chunk count of 0 that was translated to a
4525 * offload_write_send VFS call overloading the parameters src_off
4526 * = dest_off = num = 0.
4527 */
4528 if (copyfile_enabled && num == 0 && src_off == 0 && dest_off == 0) {
4529 status = vfs_offload_token_db_fetch_fsp(
4530 fruit_offload_ctx, token, &src_fsp);
4531 if (tevent_req_nterror(req, status)) {
4532 return tevent_req_post(req, ev);
4533 }
4534 state->src_fsp = src_fsp;
4535
4536 status = vfs_stat_fsp(src_fsp);
4537 if (tevent_req_nterror(req, status)) {
4538 return tevent_req_post(req, ev);
4539 }
4540
4541 to_copy = src_fsp->fsp_name->st.st_ex_size;
4542 state->is_copyfile = true;
4543 }
4544
4545 subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle,
4546 mem_ctx,
4547 ev,
4548 fsctl,
4549 token,
4550 transfer_offset,
4551 dest_fsp,
4552 dest_off,
4553 to_copy);
4554 if (tevent_req_nomem(subreq, req)) {
4555 return tevent_req_post(req, ev);
4556 }
4557
4558 tevent_req_set_callback(subreq, fruit_offload_write_done, req);
4559 return req;
4560 }
4561
fruit_offload_write_done(struct tevent_req * subreq)4562 static void fruit_offload_write_done(struct tevent_req *subreq)
4563 {
4564 struct tevent_req *req = tevent_req_callback_data(
4565 subreq, struct tevent_req);
4566 struct fruit_offload_write_state *state = tevent_req_data(
4567 req, struct fruit_offload_write_state);
4568 NTSTATUS status;
4569 unsigned int num_streams = 0;
4570 struct stream_struct *streams = NULL;
4571 unsigned int i;
4572 struct smb_filename *src_fname_tmp = NULL;
4573 struct smb_filename *dst_fname_tmp = NULL;
4574
4575 status = SMB_VFS_NEXT_OFFLOAD_WRITE_RECV(state->handle,
4576 subreq,
4577 &state->copied);
4578 TALLOC_FREE(subreq);
4579 if (tevent_req_nterror(req, status)) {
4580 return;
4581 }
4582
4583 if (!state->is_copyfile) {
4584 tevent_req_done(req);
4585 return;
4586 }
4587
4588 /*
4589 * Now copy all remaining streams. We know the share supports
4590 * streams, because we're in vfs_fruit. We don't do this async
4591 * because streams are few and small.
4592 */
4593 status = vfs_streaminfo(state->handle->conn, state->src_fsp,
4594 state->src_fsp->fsp_name,
4595 req, &num_streams, &streams);
4596 if (tevent_req_nterror(req, status)) {
4597 return;
4598 }
4599
4600 if (num_streams == 1) {
4601 /* There is always one stream, ::$DATA. */
4602 tevent_req_done(req);
4603 return;
4604 }
4605
4606 for (i = 0; i < num_streams; i++) {
4607 DEBUG(10, ("%s: stream: '%s'/%zu\n",
4608 __func__, streams[i].name, (size_t)streams[i].size));
4609
4610 src_fname_tmp = synthetic_smb_fname(
4611 req,
4612 state->src_fsp->fsp_name->base_name,
4613 streams[i].name,
4614 NULL,
4615 state->src_fsp->fsp_name->flags);
4616 if (tevent_req_nomem(src_fname_tmp, req)) {
4617 return;
4618 }
4619
4620 if (is_ntfs_default_stream_smb_fname(src_fname_tmp)) {
4621 TALLOC_FREE(src_fname_tmp);
4622 continue;
4623 }
4624
4625 dst_fname_tmp = synthetic_smb_fname(
4626 req,
4627 state->dst_fsp->fsp_name->base_name,
4628 streams[i].name,
4629 NULL,
4630 state->dst_fsp->fsp_name->flags);
4631 if (tevent_req_nomem(dst_fname_tmp, req)) {
4632 TALLOC_FREE(src_fname_tmp);
4633 return;
4634 }
4635
4636 status = copy_file(req,
4637 state->handle->conn,
4638 src_fname_tmp,
4639 dst_fname_tmp,
4640 OPENX_FILE_CREATE_IF_NOT_EXIST,
4641 0, false);
4642 if (!NT_STATUS_IS_OK(status)) {
4643 DEBUG(1, ("%s: copy %s to %s failed: %s\n", __func__,
4644 smb_fname_str_dbg(src_fname_tmp),
4645 smb_fname_str_dbg(dst_fname_tmp),
4646 nt_errstr(status)));
4647 TALLOC_FREE(src_fname_tmp);
4648 TALLOC_FREE(dst_fname_tmp);
4649 tevent_req_nterror(req, status);
4650 return;
4651 }
4652
4653 TALLOC_FREE(src_fname_tmp);
4654 TALLOC_FREE(dst_fname_tmp);
4655 }
4656
4657 TALLOC_FREE(streams);
4658 TALLOC_FREE(src_fname_tmp);
4659 TALLOC_FREE(dst_fname_tmp);
4660 tevent_req_done(req);
4661 }
4662
fruit_offload_write_recv(struct vfs_handle_struct * handle,struct tevent_req * req,off_t * copied)4663 static NTSTATUS fruit_offload_write_recv(struct vfs_handle_struct *handle,
4664 struct tevent_req *req,
4665 off_t *copied)
4666 {
4667 struct fruit_offload_write_state *state = tevent_req_data(
4668 req, struct fruit_offload_write_state);
4669 NTSTATUS status;
4670
4671 if (tevent_req_is_nterror(req, &status)) {
4672 DEBUG(1, ("server side copy chunk failed: %s\n",
4673 nt_errstr(status)));
4674 *copied = 0;
4675 tevent_req_received(req);
4676 return status;
4677 }
4678
4679 *copied = state->copied;
4680 tevent_req_received(req);
4681
4682 return NT_STATUS_OK;
4683 }
4684
fruit_get_bandsize_line(char ** lines,int numlines)4685 static char *fruit_get_bandsize_line(char **lines, int numlines)
4686 {
4687 static regex_t re;
4688 static bool re_initialized = false;
4689 int i;
4690 int ret;
4691
4692 if (!re_initialized) {
4693 ret = regcomp(&re, "^[[:blank:]]*<key>band-size</key>$", 0);
4694 if (ret != 0) {
4695 return NULL;
4696 }
4697 re_initialized = true;
4698 }
4699
4700 for (i = 0; i < numlines; i++) {
4701 regmatch_t matches[1];
4702
4703 ret = regexec(&re, lines[i], 1, matches, 0);
4704 if (ret == 0) {
4705 /*
4706 * Check if the match was on the last line, sa we want
4707 * the subsequent line.
4708 */
4709 if (i + 1 == numlines) {
4710 return NULL;
4711 }
4712 return lines[i + 1];
4713 }
4714 if (ret != REG_NOMATCH) {
4715 return NULL;
4716 }
4717 }
4718
4719 return NULL;
4720 }
4721
fruit_get_bandsize_from_line(char * line,size_t * _band_size)4722 static bool fruit_get_bandsize_from_line(char *line, size_t *_band_size)
4723 {
4724 static regex_t re;
4725 static bool re_initialized = false;
4726 regmatch_t matches[2];
4727 uint64_t band_size;
4728 int ret;
4729 bool ok;
4730
4731 if (!re_initialized) {
4732 ret = regcomp(&re,
4733 "^[[:blank:]]*"
4734 "<integer>\\([[:digit:]]*\\)</integer>$",
4735 0);
4736 if (ret != 0) {
4737 return false;
4738 }
4739 re_initialized = true;
4740 }
4741
4742 ret = regexec(&re, line, 2, matches, 0);
4743 if (ret != 0) {
4744 DBG_ERR("regex failed [%s]\n", line);
4745 return false;
4746 }
4747
4748 line[matches[1].rm_eo] = '\0';
4749
4750 ok = conv_str_u64(&line[matches[1].rm_so], &band_size);
4751 if (!ok) {
4752 return false;
4753 }
4754 *_band_size = (size_t)band_size;
4755 return true;
4756 }
4757
4758 /*
4759 * This reads and parses an Info.plist from a TM sparsebundle looking for the
4760 * "band-size" key and value.
4761 */
fruit_get_bandsize(vfs_handle_struct * handle,const char * dir,size_t * band_size)4762 static bool fruit_get_bandsize(vfs_handle_struct *handle,
4763 const char *dir,
4764 size_t *band_size)
4765 {
4766 #define INFO_PLIST_MAX_SIZE 64*1024
4767 char *plist = NULL;
4768 struct smb_filename *smb_fname = NULL;
4769 files_struct *fsp = NULL;
4770 uint8_t *file_data = NULL;
4771 char **lines = NULL;
4772 char *band_size_line = NULL;
4773 size_t plist_file_size;
4774 ssize_t nread;
4775 int numlines;
4776 int ret;
4777 bool ok = false;
4778 NTSTATUS status;
4779
4780 plist = talloc_asprintf(talloc_tos(),
4781 "%s/%s/Info.plist",
4782 handle->conn->connectpath,
4783 dir);
4784 if (plist == NULL) {
4785 ok = false;
4786 goto out;
4787 }
4788
4789 smb_fname = synthetic_smb_fname(talloc_tos(), plist, NULL, NULL, 0);
4790 if (smb_fname == NULL) {
4791 ok = false;
4792 goto out;
4793 }
4794
4795 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
4796 if (ret != 0) {
4797 DBG_INFO("Ignoring Sparsebundle without Info.plist [%s]\n", dir);
4798 ok = true;
4799 goto out;
4800 }
4801
4802 plist_file_size = smb_fname->st.st_ex_size;
4803
4804 if (plist_file_size > INFO_PLIST_MAX_SIZE) {
4805 DBG_INFO("%s is too large, ignoring\n", plist);
4806 ok = true;
4807 goto out;
4808 }
4809
4810 status = SMB_VFS_NEXT_CREATE_FILE(
4811 handle, /* conn */
4812 NULL, /* req */
4813 0, /* root_dir_fid */
4814 smb_fname, /* fname */
4815 FILE_GENERIC_READ, /* access_mask */
4816 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
4817 FILE_OPEN, /* create_disposition */
4818 0, /* create_options */
4819 0, /* file_attributes */
4820 INTERNAL_OPEN_ONLY, /* oplock_request */
4821 NULL, /* lease */
4822 0, /* allocation_size */
4823 0, /* private_flags */
4824 NULL, /* sd */
4825 NULL, /* ea_list */
4826 &fsp, /* result */
4827 NULL, /* psbuf */
4828 NULL, NULL); /* create context */
4829 if (!NT_STATUS_IS_OK(status)) {
4830 DBG_INFO("Opening [%s] failed [%s]\n",
4831 smb_fname_str_dbg(smb_fname), nt_errstr(status));
4832 ok = false;
4833 goto out;
4834 }
4835
4836 file_data = talloc_array(talloc_tos(), uint8_t, plist_file_size);
4837 if (file_data == NULL) {
4838 ok = false;
4839 goto out;
4840 }
4841
4842 nread = SMB_VFS_NEXT_PREAD(handle, fsp, file_data, plist_file_size, 0);
4843 if (nread != plist_file_size) {
4844 DBG_ERR("Short read on [%s]: %zu/%zd\n",
4845 fsp_str_dbg(fsp), nread, plist_file_size);
4846 ok = false;
4847 goto out;
4848
4849 }
4850
4851 status = close_file(NULL, fsp, NORMAL_CLOSE);
4852 fsp = NULL;
4853 if (!NT_STATUS_IS_OK(status)) {
4854 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
4855 ok = false;
4856 goto out;
4857 }
4858
4859 lines = file_lines_parse((char *)file_data,
4860 plist_file_size,
4861 &numlines,
4862 talloc_tos());
4863 if (lines == NULL) {
4864 ok = false;
4865 goto out;
4866 }
4867
4868 band_size_line = fruit_get_bandsize_line(lines, numlines);
4869 if (band_size_line == NULL) {
4870 DBG_ERR("Didn't find band-size key in [%s]\n",
4871 smb_fname_str_dbg(smb_fname));
4872 ok = false;
4873 goto out;
4874 }
4875
4876 ok = fruit_get_bandsize_from_line(band_size_line, band_size);
4877 if (!ok) {
4878 DBG_ERR("fruit_get_bandsize_from_line failed\n");
4879 goto out;
4880 }
4881
4882 DBG_DEBUG("Parsed band-size [%zu] for [%s]\n", *band_size, plist);
4883
4884 out:
4885 if (fsp != NULL) {
4886 status = close_file(NULL, fsp, NORMAL_CLOSE);
4887 if (!NT_STATUS_IS_OK(status)) {
4888 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
4889 }
4890 fsp = NULL;
4891 }
4892 TALLOC_FREE(plist);
4893 TALLOC_FREE(smb_fname);
4894 TALLOC_FREE(file_data);
4895 TALLOC_FREE(lines);
4896 return ok;
4897 }
4898
4899 struct fruit_disk_free_state {
4900 off_t total_size;
4901 };
4902
fruit_get_num_bands(vfs_handle_struct * handle,char * bundle,size_t * _nbands)4903 static bool fruit_get_num_bands(vfs_handle_struct *handle,
4904 char *bundle,
4905 size_t *_nbands)
4906 {
4907 char *path = NULL;
4908 struct smb_filename *bands_dir = NULL;
4909 DIR *d = NULL;
4910 struct dirent *e = NULL;
4911 size_t nbands;
4912 int ret;
4913
4914 path = talloc_asprintf(talloc_tos(),
4915 "%s/%s/bands",
4916 handle->conn->connectpath,
4917 bundle);
4918 if (path == NULL) {
4919 return false;
4920 }
4921
4922 bands_dir = synthetic_smb_fname(talloc_tos(),
4923 path,
4924 NULL,
4925 NULL,
4926 0);
4927 TALLOC_FREE(path);
4928 if (bands_dir == NULL) {
4929 return false;
4930 }
4931
4932 d = SMB_VFS_NEXT_OPENDIR(handle, bands_dir, NULL, 0);
4933 if (d == NULL) {
4934 TALLOC_FREE(bands_dir);
4935 return false;
4936 }
4937
4938 nbands = 0;
4939
4940 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
4941 e != NULL;
4942 e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
4943 {
4944 if (ISDOT(e->d_name) || ISDOTDOT(e->d_name)) {
4945 continue;
4946 }
4947 nbands++;
4948 }
4949
4950 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
4951 if (ret != 0) {
4952 TALLOC_FREE(bands_dir);
4953 return false;
4954 }
4955
4956 DBG_DEBUG("%zu bands in [%s]\n", nbands, smb_fname_str_dbg(bands_dir));
4957
4958 TALLOC_FREE(bands_dir);
4959
4960 *_nbands = nbands;
4961 return true;
4962 }
4963
fruit_tmsize_do_dirent(vfs_handle_struct * handle,struct fruit_disk_free_state * state,struct dirent * e)4964 static bool fruit_tmsize_do_dirent(vfs_handle_struct *handle,
4965 struct fruit_disk_free_state *state,
4966 struct dirent *e)
4967 {
4968 bool ok;
4969 char *p = NULL;
4970 size_t sparsebundle_strlen = strlen("sparsebundle");
4971 size_t bandsize = 0;
4972 size_t nbands;
4973 off_t tm_size;
4974
4975 p = strstr(e->d_name, "sparsebundle");
4976 if (p == NULL) {
4977 return true;
4978 }
4979
4980 if (p[sparsebundle_strlen] != '\0') {
4981 return true;
4982 }
4983
4984 DBG_DEBUG("Processing sparsebundle [%s]\n", e->d_name);
4985
4986 ok = fruit_get_bandsize(handle, e->d_name, &bandsize);
4987 if (!ok) {
4988 /*
4989 * Beware of race conditions: this may be an uninitialized
4990 * Info.plist that a client is just creating. We don't want let
4991 * this to trigger complete failure.
4992 */
4993 DBG_ERR("Processing sparsebundle [%s] failed\n", e->d_name);
4994 return true;
4995 }
4996
4997 ok = fruit_get_num_bands(handle, e->d_name, &nbands);
4998 if (!ok) {
4999 /*
5000 * Beware of race conditions: this may be a backup sparsebundle
5001 * in an early stage lacking a bands subdirectory. We don't want
5002 * let this to trigger complete failure.
5003 */
5004 DBG_ERR("Processing sparsebundle [%s] failed\n", e->d_name);
5005 return true;
5006 }
5007
5008 /*
5009 * Arithmetic on 32-bit systems may cause overflow, depending on
5010 * size_t precision. First we check its unlikely, then we
5011 * force the precision into target off_t, then we check that
5012 * the total did not overflow either.
5013 */
5014 if (bandsize > SIZE_MAX/nbands) {
5015 DBG_ERR("tmsize potential overflow: bandsize [%zu] nbands [%zu]\n",
5016 bandsize, nbands);
5017 return false;
5018 }
5019 tm_size = (off_t)bandsize * (off_t)nbands;
5020
5021 if (state->total_size + tm_size < state->total_size) {
5022 DBG_ERR("tm total size overflow: bandsize [%zu] nbands [%zu]\n",
5023 bandsize, nbands);
5024 return false;
5025 }
5026
5027 state->total_size += tm_size;
5028
5029 DBG_DEBUG("[%s] tm_size [%jd] total_size [%jd]\n",
5030 e->d_name, (intmax_t)tm_size, (intmax_t)state->total_size);
5031
5032 return true;
5033 }
5034
5035 /**
5036 * Calculate used size of a TimeMachine volume
5037 *
5038 * This assumes that the volume is used only for TimeMachine.
5039 *
5040 * - readdir(basedir of share), then
5041 * - for every element that matches regex "^\(.*\)\.sparsebundle$" :
5042 * - parse "\1.sparsebundle/Info.plist" and read the band-size XML key
5043 * - count band files in "\1.sparsebundle/bands/"
5044 * - calculate used size of all bands: band_count * band_size
5045 **/
fruit_disk_free(vfs_handle_struct * handle,const struct smb_filename * smb_fname,uint64_t * _bsize,uint64_t * _dfree,uint64_t * _dsize)5046 static uint64_t fruit_disk_free(vfs_handle_struct *handle,
5047 const struct smb_filename *smb_fname,
5048 uint64_t *_bsize,
5049 uint64_t *_dfree,
5050 uint64_t *_dsize)
5051 {
5052 struct fruit_config_data *config = NULL;
5053 struct fruit_disk_free_state state = {0};
5054 DIR *d = NULL;
5055 struct dirent *e = NULL;
5056 uint64_t dfree;
5057 uint64_t dsize;
5058 int ret;
5059 bool ok;
5060
5061 SMB_VFS_HANDLE_GET_DATA(handle, config,
5062 struct fruit_config_data,
5063 return UINT64_MAX);
5064
5065 if (!config->time_machine ||
5066 config->time_machine_max_size == 0)
5067 {
5068 return SMB_VFS_NEXT_DISK_FREE(handle,
5069 smb_fname,
5070 _bsize,
5071 _dfree,
5072 _dsize);
5073 }
5074
5075 d = SMB_VFS_NEXT_OPENDIR(handle, smb_fname, NULL, 0);
5076 if (d == NULL) {
5077 return UINT64_MAX;
5078 }
5079
5080 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
5081 e != NULL;
5082 e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
5083 {
5084 ok = fruit_tmsize_do_dirent(handle, &state, e);
5085 if (!ok) {
5086 SMB_VFS_NEXT_CLOSEDIR(handle, d);
5087 return UINT64_MAX;
5088 }
5089 }
5090
5091 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
5092 if (ret != 0) {
5093 return UINT64_MAX;
5094 }
5095
5096 dsize = config->time_machine_max_size / 512;
5097 dfree = dsize - (state.total_size / 512);
5098 if (dfree > dsize) {
5099 dfree = 0;
5100 }
5101
5102 *_bsize = 512;
5103 *_dsize = dsize;
5104 *_dfree = dfree;
5105 return dfree / 2;
5106 }
5107
fruit_fs_file_id(struct vfs_handle_struct * handle,const SMB_STRUCT_STAT * psbuf)5108 static uint64_t fruit_fs_file_id(struct vfs_handle_struct *handle,
5109 const SMB_STRUCT_STAT *psbuf)
5110 {
5111 struct fruit_config_data *config = NULL;
5112
5113 SMB_VFS_HANDLE_GET_DATA(handle, config,
5114 struct fruit_config_data,
5115 return 0);
5116
5117 if (global_fruit_config.nego_aapl &&
5118 config->aapl_zero_file_id)
5119 {
5120 return 0;
5121 }
5122
5123 return SMB_VFS_NEXT_FS_FILE_ID(handle, psbuf);
5124 }
5125
5126 static struct vfs_fn_pointers vfs_fruit_fns = {
5127 .connect_fn = fruit_connect,
5128 .disk_free_fn = fruit_disk_free,
5129
5130 /* File operations */
5131 .chmod_fn = fruit_chmod,
5132 .unlinkat_fn = fruit_unlinkat,
5133 .renameat_fn = fruit_renameat,
5134 .open_fn = fruit_open,
5135 .close_fn = fruit_close,
5136 .pread_fn = fruit_pread,
5137 .pwrite_fn = fruit_pwrite,
5138 .pread_send_fn = fruit_pread_send,
5139 .pread_recv_fn = fruit_pread_recv,
5140 .pwrite_send_fn = fruit_pwrite_send,
5141 .pwrite_recv_fn = fruit_pwrite_recv,
5142 .stat_fn = fruit_stat,
5143 .lstat_fn = fruit_lstat,
5144 .fstat_fn = fruit_fstat,
5145 .streaminfo_fn = fruit_streaminfo,
5146 .ntimes_fn = fruit_ntimes,
5147 .ftruncate_fn = fruit_ftruncate,
5148 .fallocate_fn = fruit_fallocate,
5149 .create_file_fn = fruit_create_file,
5150 .readdir_attr_fn = fruit_readdir_attr,
5151 .offload_read_send_fn = fruit_offload_read_send,
5152 .offload_read_recv_fn = fruit_offload_read_recv,
5153 .offload_write_send_fn = fruit_offload_write_send,
5154 .offload_write_recv_fn = fruit_offload_write_recv,
5155 .fs_file_id_fn = fruit_fs_file_id,
5156
5157 /* NT ACL operations */
5158 .fget_nt_acl_fn = fruit_fget_nt_acl,
5159 .fset_nt_acl_fn = fruit_fset_nt_acl,
5160 };
5161
5162 static_decl_vfs;
vfs_fruit_init(TALLOC_CTX * ctx)5163 NTSTATUS vfs_fruit_init(TALLOC_CTX *ctx)
5164 {
5165 NTSTATUS ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "fruit",
5166 &vfs_fruit_fns);
5167 if (!NT_STATUS_IS_OK(ret)) {
5168 return ret;
5169 }
5170
5171 vfs_fruit_debug_level = debug_add_class("fruit");
5172 if (vfs_fruit_debug_level == -1) {
5173 vfs_fruit_debug_level = DBGC_VFS;
5174 DEBUG(0, ("%s: Couldn't register custom debugging class!\n",
5175 "vfs_fruit_init"));
5176 } else {
5177 DEBUG(10, ("%s: Debug class number of '%s': %d\n",
5178 "vfs_fruit_init","fruit",vfs_fruit_debug_level));
5179 }
5180
5181 return ret;
5182 }
5183