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