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