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