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