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