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