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