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