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