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