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