ce42202b4a3421d3e14bc1fd5e17133365cbe02f
[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
1608         ref_fio = VFS_ADD_FSP_EXTENSION(handle, ad_fsp,
1609                                         struct fio,
1610                                         fio_ref_destroy_fn);
1611         if (ref_fio == NULL) {
1612                 int saved_errno = errno;
1613                 fd_close(ad_fsp);
1614                 file_free(NULL, ad_fsp);
1615                 ad_fsp = NULL;
1616                 errno = saved_errno;
1617                 rc = -1;
1618                 goto exit;
1619         }
1620
1621         SMB_ASSERT(ref_fio->fsp == NULL);
1622         ref_fio->handle = handle;
1623         ref_fio->fsp = ad_fsp;
1624         ref_fio->type = ADOUBLE_RSRC;
1625         ref_fio->config = config;
1626         ref_fio->real_fio = fio;
1627         SMB_ASSERT(fio->ad_fsp == NULL);
1628         fio->ad_fsp = ad_fsp;
1629         fio->fake_fd = true;
1630
1631 exit:
1632
1633         DEBUG(10, ("fruit_open resource fork: rc=%d\n", rc));
1634         if (rc != 0) {
1635                 int saved_errno = errno;
1636                 if (fd != -1) {
1637                         vfs_fake_fd_close(fd);
1638                 }
1639                 errno = saved_errno;
1640                 return rc;
1641         }
1642         return fd;
1643 }
1644
1645 static int fruit_open_rsrc_xattr(vfs_handle_struct *handle,
1646                                  const struct files_struct *dirfsp,
1647                                  const struct smb_filename *smb_fname,
1648                                  files_struct *fsp,
1649                                  int flags,
1650                                  mode_t mode)
1651 {
1652 #ifdef HAVE_ATTROPEN
1653         int fd = -1;
1654
1655         /*
1656          * As there's no attropenat() this is only going to work with AT_FDCWD.
1657          */
1658         SMB_ASSERT(fsp_get_pathref_fd(dirfsp) == AT_FDCWD);
1659
1660         fd = attropen(smb_fname->base_name,
1661                       AFPRESOURCE_EA_NETATALK,
1662                       flags,
1663                       mode);
1664         if (fd == -1) {
1665                 return -1;
1666         }
1667
1668         return fd;
1669
1670 #else
1671         errno = ENOSYS;
1672         return -1;
1673 #endif
1674 }
1675
1676 static int fruit_open_rsrc(vfs_handle_struct *handle,
1677                            const struct files_struct *dirfsp,
1678                            const struct smb_filename *smb_fname,
1679                            files_struct *fsp, int flags, mode_t mode)
1680 {
1681         int fd;
1682         struct fruit_config_data *config = NULL;
1683         struct fio *fio = NULL;
1684
1685         DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
1686
1687         SMB_VFS_HANDLE_GET_DATA(handle, config,
1688                                 struct fruit_config_data, return -1);
1689
1690         fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, fio_destroy_fn);
1691         fio->handle = handle;
1692         fio->fsp = fsp;
1693         fio->type = ADOUBLE_RSRC;
1694         fio->config = config;
1695
1696         switch (config->rsrc) {
1697         case FRUIT_RSRC_STREAM:
1698                 fd = SMB_VFS_NEXT_OPENAT(handle,
1699                                          dirfsp,
1700                                          smb_fname,
1701                                          fsp,
1702                                          flags,
1703                                          mode);
1704                 break;
1705
1706         case FRUIT_RSRC_ADFILE:
1707                 fd = fruit_open_rsrc_adouble(handle, dirfsp, smb_fname,
1708                                              fsp, flags, mode);
1709                 break;
1710
1711         case FRUIT_RSRC_XATTR:
1712                 fd = fruit_open_rsrc_xattr(handle, dirfsp, smb_fname,
1713                                            fsp, flags, mode);
1714                 break;
1715
1716         default:
1717                 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
1718                 return -1;
1719         }
1720
1721         DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
1722
1723         if (fd == -1) {
1724                 return -1;
1725         }
1726
1727         return fd;
1728 }
1729
1730 static int fruit_openat(vfs_handle_struct *handle,
1731                         const struct files_struct *dirfsp,
1732                         const struct smb_filename *smb_fname,
1733                         files_struct *fsp,
1734                         int flags,
1735                         mode_t mode)
1736 {
1737         int fd;
1738
1739         DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
1740
1741         if (!is_named_stream(smb_fname)) {
1742                 return SMB_VFS_NEXT_OPENAT(handle,
1743                                            dirfsp,
1744                                            smb_fname,
1745                                            fsp,
1746                                            flags,
1747                                            mode);
1748         }
1749
1750         if (is_afpinfo_stream(smb_fname->stream_name)) {
1751                 fd = fruit_open_meta(handle,
1752                                      dirfsp,
1753                                      smb_fname,
1754                                      fsp,
1755                                      flags,
1756                                      mode);
1757         } else if (is_afpresource_stream(smb_fname->stream_name)) {
1758                 fd = fruit_open_rsrc(handle,
1759                                      dirfsp,
1760                                      smb_fname,
1761                                      fsp,
1762                                      flags,
1763                                      mode);
1764         } else {
1765                 fd = SMB_VFS_NEXT_OPENAT(handle,
1766                                          dirfsp,
1767                                          smb_fname,
1768                                          fsp,
1769                                          flags,
1770                                          mode);
1771         }
1772
1773         DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
1774
1775         /* Prevent reopen optimisation */
1776         fsp->fsp_flags.have_proc_fds = false;
1777         return fd;
1778 }
1779
1780 static int fruit_close_meta(vfs_handle_struct *handle,
1781                             files_struct *fsp)
1782 {
1783         struct fio *fio = fruit_get_complete_fio(handle, fsp);
1784         int ret;
1785         struct fruit_config_data *config = NULL;
1786
1787         SMB_VFS_HANDLE_GET_DATA(handle, config,
1788                                 struct fruit_config_data, return -1);
1789
1790         if (fio == NULL) {
1791                 return -1;
1792         }
1793
1794         switch (config->meta) {
1795         case FRUIT_META_STREAM:
1796                 if (fio->fake_fd) {
1797                         ret = vfs_fake_fd_close(fsp_get_pathref_fd(fsp));
1798                         fsp_set_fd(fsp, -1);
1799                 } else {
1800                         ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
1801                 }
1802                 break;
1803
1804         case FRUIT_META_NETATALK:
1805                 ret = vfs_fake_fd_close(fsp_get_pathref_fd(fsp));
1806                 fsp_set_fd(fsp, -1);
1807                 break;
1808
1809         default:
1810                 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
1811                 return -1;
1812         }
1813
1814         return ret;
1815 }
1816
1817
1818 static int fruit_close_rsrc(vfs_handle_struct *handle,
1819                             files_struct *fsp)
1820 {
1821         struct fio *fio = fruit_get_complete_fio(handle, fsp);
1822         int ret;
1823         struct fruit_config_data *config = NULL;
1824
1825         SMB_VFS_HANDLE_GET_DATA(handle, config,
1826                                 struct fruit_config_data, return -1);
1827
1828         switch (config->rsrc) {
1829         case FRUIT_RSRC_STREAM:
1830                 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
1831                 break;
1832
1833         case FRUIT_RSRC_ADFILE:
1834                 fio_close_ad_fsp(fio);
1835                 ret = vfs_fake_fd_close(fsp_get_pathref_fd(fsp));
1836                 fsp_set_fd(fsp, -1);
1837                 break;
1838
1839         case FRUIT_RSRC_XATTR:
1840                 ret = vfs_fake_fd_close(fsp_get_pathref_fd(fsp));
1841                 fsp_set_fd(fsp, -1);
1842                 break;
1843
1844         default:
1845                 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
1846                 return -1;
1847         }
1848
1849         return ret;
1850 }
1851
1852 static int fruit_close(vfs_handle_struct *handle,
1853                        files_struct *fsp)
1854 {
1855         int ret;
1856         int fd;
1857
1858         fd = fsp_get_pathref_fd(fsp);
1859
1860         DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(fsp->fsp_name), fd);
1861
1862         if (!is_named_stream(fsp->fsp_name)) {
1863                 return SMB_VFS_NEXT_CLOSE(handle, fsp);
1864         }
1865
1866         if (is_afpinfo_stream(fsp->fsp_name->stream_name)) {
1867                 ret = fruit_close_meta(handle, fsp);
1868         } else if (is_afpresource_stream(fsp->fsp_name->stream_name)) {
1869                 ret = fruit_close_rsrc(handle, fsp);
1870         } else {
1871                 ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
1872         }
1873
1874         return ret;
1875 }
1876
1877 static int fruit_renameat(struct vfs_handle_struct *handle,
1878                         files_struct *srcfsp,
1879                         const struct smb_filename *smb_fname_src,
1880                         files_struct *dstfsp,
1881                         const struct smb_filename *smb_fname_dst)
1882 {
1883         int rc = -1;
1884         struct fruit_config_data *config = NULL;
1885         struct smb_filename *src_adp_smb_fname = NULL;
1886         struct smb_filename *dst_adp_smb_fname = NULL;
1887
1888         SMB_VFS_HANDLE_GET_DATA(handle, config,
1889                                 struct fruit_config_data, return -1);
1890
1891         if (!VALID_STAT(smb_fname_src->st)) {
1892                 DBG_ERR("Need valid stat for [%s]\n",
1893                         smb_fname_str_dbg(smb_fname_src));
1894                 return -1;
1895         }
1896
1897         rc = SMB_VFS_NEXT_RENAMEAT(handle,
1898                                 srcfsp,
1899                                 smb_fname_src,
1900                                 dstfsp,
1901                                 smb_fname_dst);
1902         if (rc != 0) {
1903                 return -1;
1904         }
1905
1906         if ((config->rsrc != FRUIT_RSRC_ADFILE) ||
1907             (!S_ISREG(smb_fname_src->st.st_ex_mode)))
1908         {
1909                 return 0;
1910         }
1911
1912         rc = adouble_path(talloc_tos(), smb_fname_src, &src_adp_smb_fname);
1913         if (rc != 0) {
1914                 goto done;
1915         }
1916
1917         rc = adouble_path(talloc_tos(), smb_fname_dst, &dst_adp_smb_fname);
1918         if (rc != 0) {
1919                 goto done;
1920         }
1921
1922         DBG_DEBUG("%s -> %s\n",
1923                   smb_fname_str_dbg(src_adp_smb_fname),
1924                   smb_fname_str_dbg(dst_adp_smb_fname));
1925
1926         rc = SMB_VFS_NEXT_RENAMEAT(handle,
1927                         srcfsp,
1928                         src_adp_smb_fname,
1929                         dstfsp,
1930                         dst_adp_smb_fname);
1931         if (errno == ENOENT) {
1932                 rc = 0;
1933         }
1934
1935 done:
1936         TALLOC_FREE(src_adp_smb_fname);
1937         TALLOC_FREE(dst_adp_smb_fname);
1938         return rc;
1939 }
1940
1941 static int fruit_unlink_meta_stream(vfs_handle_struct *handle,
1942                                 struct files_struct *dirfsp,
1943                                 const struct smb_filename *smb_fname)
1944 {
1945         return SMB_VFS_NEXT_UNLINKAT(handle,
1946                                 dirfsp,
1947                                 smb_fname,
1948                                 0);
1949 }
1950
1951 static int fruit_unlink_meta_netatalk(vfs_handle_struct *handle,
1952                                       const struct smb_filename *smb_fname)
1953 {
1954         SMB_ASSERT(smb_fname->fsp != NULL);
1955         SMB_ASSERT(smb_fname->fsp->base_fsp != NULL);
1956         return SMB_VFS_FREMOVEXATTR(smb_fname->fsp->base_fsp,
1957                                    AFPINFO_EA_NETATALK);
1958 }
1959
1960 static int fruit_unlink_meta(vfs_handle_struct *handle,
1961                         struct files_struct *dirfsp,
1962                         const struct smb_filename *smb_fname)
1963 {
1964         struct fruit_config_data *config = NULL;
1965         int rc;
1966
1967         SMB_VFS_HANDLE_GET_DATA(handle, config,
1968                                 struct fruit_config_data, return -1);
1969
1970         switch (config->meta) {
1971         case FRUIT_META_STREAM:
1972                 rc = fruit_unlink_meta_stream(handle,
1973                                 dirfsp,
1974                                 smb_fname);
1975                 break;
1976
1977         case FRUIT_META_NETATALK:
1978                 rc = fruit_unlink_meta_netatalk(handle, smb_fname);
1979                 break;
1980
1981         default:
1982                 DBG_ERR("Unsupported meta config [%d]\n", config->meta);
1983                 return -1;
1984         }
1985
1986         return rc;
1987 }
1988
1989 static int fruit_unlink_rsrc_stream(vfs_handle_struct *handle,
1990                                 struct files_struct *dirfsp,
1991                                 const struct smb_filename *smb_fname,
1992                                 bool force_unlink)
1993 {
1994         int ret;
1995
1996         if (!force_unlink) {
1997                 struct smb_filename *full_fname = NULL;
1998                 off_t size;
1999
2000                 /*
2001                  * TODO: use SMB_VFS_STATX() once we have it.
2002                  */
2003
2004                 full_fname = full_path_from_dirfsp_atname(talloc_tos(),
2005                                                           dirfsp,
2006                                                           smb_fname);
2007                 if (full_fname == NULL) {
2008                         return -1;
2009                 }
2010
2011                 /*
2012                  * 0 byte resource fork streams are not listed by
2013                  * vfs_streaminfo, as a result stream cleanup/deletion of file
2014                  * deletion doesn't remove the resourcefork stream.
2015                  */
2016
2017                 ret = SMB_VFS_NEXT_STAT(handle, full_fname);
2018                 if (ret != 0) {
2019                         TALLOC_FREE(full_fname);
2020                         DBG_ERR("stat [%s] failed [%s]\n",
2021                                 smb_fname_str_dbg(full_fname), strerror(errno));
2022                         return -1;
2023                 }
2024
2025                 size = full_fname->st.st_ex_size;
2026                 TALLOC_FREE(full_fname);
2027
2028                 if (size > 0) {
2029                         /* OS X ignores resource fork stream delete requests */
2030                         return 0;
2031                 }
2032         }
2033
2034         ret = SMB_VFS_NEXT_UNLINKAT(handle,
2035                         dirfsp,
2036                         smb_fname,
2037                         0);
2038         if ((ret != 0) && (errno == ENOENT) && force_unlink) {
2039                 ret = 0;
2040         }
2041
2042         return ret;
2043 }
2044
2045 static int fruit_unlink_rsrc_adouble(vfs_handle_struct *handle,
2046                                 struct files_struct *dirfsp,
2047                                 const struct smb_filename *smb_fname,
2048                                 bool force_unlink)
2049 {
2050         int rc;
2051         struct adouble *ad = NULL;
2052         struct smb_filename *adp_smb_fname = NULL;
2053
2054         if (!force_unlink) {
2055                 struct smb_filename *full_fname = NULL;
2056
2057                 full_fname = full_path_from_dirfsp_atname(talloc_tos(),
2058                                                           dirfsp,
2059                                                           smb_fname);
2060                 if (full_fname == NULL) {
2061                         return -1;
2062                 }
2063
2064                 ad = ad_get(talloc_tos(), handle, full_fname,
2065                             ADOUBLE_RSRC);
2066                 TALLOC_FREE(full_fname);
2067                 if (ad == NULL) {
2068                         errno = ENOENT;
2069                         return -1;
2070                 }
2071
2072
2073                 /*
2074                  * 0 byte resource fork streams are not listed by
2075                  * vfs_streaminfo, as a result stream cleanup/deletion of file
2076                  * deletion doesn't remove the resourcefork stream.
2077                  */
2078
2079                 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
2080                         /* OS X ignores resource fork stream delete requests */
2081                         TALLOC_FREE(ad);
2082                         return 0;
2083                 }
2084
2085                 TALLOC_FREE(ad);
2086         }
2087
2088         rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
2089         if (rc != 0) {
2090                 return -1;
2091         }
2092
2093         rc = SMB_VFS_NEXT_UNLINKAT(handle,
2094                         dirfsp,
2095                         adp_smb_fname,
2096                         0);
2097         TALLOC_FREE(adp_smb_fname);
2098         if ((rc != 0) && (errno == ENOENT) && force_unlink) {
2099                 rc = 0;
2100         }
2101
2102         return rc;
2103 }
2104
2105 static int fruit_unlink_rsrc_xattr(vfs_handle_struct *handle,
2106                                    const struct smb_filename *smb_fname,
2107                                    bool force_unlink)
2108 {
2109         /*
2110          * OS X ignores resource fork stream delete requests, so nothing to do
2111          * here. Removing the file will remove the xattr anyway, so we don't
2112          * have to take care of removing 0 byte resource forks that could be
2113          * left behind.
2114          */
2115         return 0;
2116 }
2117
2118 static int fruit_unlink_rsrc(vfs_handle_struct *handle,
2119                         struct files_struct *dirfsp,
2120                         const struct smb_filename *smb_fname,
2121                         bool force_unlink)
2122 {
2123         struct fruit_config_data *config = NULL;
2124         int rc;
2125
2126         SMB_VFS_HANDLE_GET_DATA(handle, config,
2127                                 struct fruit_config_data, return -1);
2128
2129         switch (config->rsrc) {
2130         case FRUIT_RSRC_STREAM:
2131                 rc = fruit_unlink_rsrc_stream(handle,
2132                                 dirfsp,
2133                                 smb_fname,
2134                                 force_unlink);
2135                 break;
2136
2137         case FRUIT_RSRC_ADFILE:
2138                 rc = fruit_unlink_rsrc_adouble(handle,
2139                                 dirfsp,
2140                                 smb_fname,
2141                                 force_unlink);
2142                 break;
2143
2144         case FRUIT_RSRC_XATTR:
2145                 rc = fruit_unlink_rsrc_xattr(handle, smb_fname, force_unlink);
2146                 break;
2147
2148         default:
2149                 DBG_ERR("Unsupported rsrc config [%d]\n", config->rsrc);
2150                 return -1;
2151         }
2152
2153         return rc;
2154 }
2155
2156 static int fruit_fchmod(vfs_handle_struct *handle,
2157                       struct files_struct *fsp,
2158                       mode_t mode)
2159 {
2160         int rc = -1;
2161         struct fruit_config_data *config = NULL;
2162         struct smb_filename *smb_fname_adp = NULL;
2163         const struct smb_filename *smb_fname = NULL;
2164         NTSTATUS status;
2165
2166         rc = SMB_VFS_NEXT_FCHMOD(handle, fsp, mode);
2167         if (rc != 0) {
2168                 return rc;
2169         }
2170
2171         smb_fname = fsp->fsp_name;
2172         SMB_VFS_HANDLE_GET_DATA(handle, config,
2173                                 struct fruit_config_data, return -1);
2174
2175         if (config->rsrc != FRUIT_RSRC_ADFILE) {
2176                 return 0;
2177         }
2178
2179         if (!VALID_STAT(smb_fname->st)) {
2180                 return 0;
2181         }
2182
2183         if (!S_ISREG(smb_fname->st.st_ex_mode)) {
2184                 return 0;
2185         }
2186
2187         rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_adp);
2188         if (rc != 0) {
2189                 return -1;
2190         }
2191
2192         status = openat_pathref_fsp(handle->conn->cwd_fsp,
2193                                     smb_fname_adp);
2194         if (!NT_STATUS_IS_OK(status)) {
2195                 /* detect ENOENT (mapped to OBJECT_NAME_NOT_FOUND) */
2196                 if (NT_STATUS_EQUAL(status,
2197                                     NT_STATUS_OBJECT_NAME_NOT_FOUND)){
2198                         rc = 0;
2199                         goto out;
2200                 }
2201                 rc = -1;
2202                 goto out;
2203         }
2204
2205         DBG_DEBUG("%s\n", smb_fname_adp->base_name);
2206
2207         rc = SMB_VFS_NEXT_FCHMOD(handle, smb_fname_adp->fsp, mode);
2208         if (errno == ENOENT) {
2209                 rc = 0;
2210         }
2211 out:
2212         TALLOC_FREE(smb_fname_adp);
2213         return rc;
2214 }
2215
2216 static int fruit_unlinkat(vfs_handle_struct *handle,
2217                         struct files_struct *dirfsp,
2218                         const struct smb_filename *smb_fname,
2219                         int flags)
2220 {
2221         struct fruit_config_data *config = NULL;
2222         struct smb_filename *rsrc_smb_fname = NULL;
2223         int ret;
2224
2225         if (flags & AT_REMOVEDIR) {
2226                 return SMB_VFS_NEXT_UNLINKAT(handle,
2227                                              dirfsp,
2228                                              smb_fname,
2229                                              AT_REMOVEDIR);
2230         }
2231
2232         SMB_VFS_HANDLE_GET_DATA(handle, config,
2233                                 struct fruit_config_data, return -1);
2234
2235         if (is_afpinfo_stream(smb_fname->stream_name)) {
2236                 return fruit_unlink_meta(handle,
2237                                 dirfsp,
2238                                 smb_fname);
2239         } else if (is_afpresource_stream(smb_fname->stream_name)) {
2240                 return fruit_unlink_rsrc(handle,
2241                                 dirfsp,
2242                                 smb_fname,
2243                                 false);
2244         } else if (is_named_stream(smb_fname)) {
2245                 return SMB_VFS_NEXT_UNLINKAT(handle,
2246                                 dirfsp,
2247                                 smb_fname,
2248                                 0);
2249         } else if (is_adouble_file(smb_fname->base_name)) {
2250                 return SMB_VFS_NEXT_UNLINKAT(handle,
2251                                 dirfsp,
2252                                 smb_fname,
2253                                 0);
2254         }
2255
2256         /*
2257          * A request to delete the base file. Because 0 byte resource
2258          * fork streams are not listed by fruit_streaminfo,
2259          * delete_all_streams() can't remove 0 byte resource fork
2260          * streams, so we have to cleanup this here.
2261          */
2262         rsrc_smb_fname = synthetic_smb_fname(talloc_tos(),
2263                                              smb_fname->base_name,
2264                                              AFPRESOURCE_STREAM_NAME,
2265                                              NULL,
2266                                              smb_fname->twrp,
2267                                              smb_fname->flags);
2268         if (rsrc_smb_fname == NULL) {
2269                 return -1;
2270         }
2271
2272         ret = fruit_unlink_rsrc(handle, dirfsp, rsrc_smb_fname, true);
2273         if ((ret != 0) && (errno != ENOENT)) {
2274                 DBG_ERR("Forced unlink of [%s] failed [%s]\n",
2275                         smb_fname_str_dbg(rsrc_smb_fname), strerror(errno));
2276                 TALLOC_FREE(rsrc_smb_fname);
2277                 return -1;
2278         }
2279         TALLOC_FREE(rsrc_smb_fname);
2280
2281         return SMB_VFS_NEXT_UNLINKAT(handle,
2282                         dirfsp,
2283                         smb_fname,
2284                         0);
2285 }
2286
2287 static ssize_t fruit_pread_meta_stream(vfs_handle_struct *handle,
2288                                        files_struct *fsp, void *data,
2289                                        size_t n, off_t offset)
2290 {
2291         struct fio *fio = fruit_get_complete_fio(handle, fsp);
2292         ssize_t nread;
2293         int ret;
2294
2295         if ((fio == NULL) || fio->fake_fd) {
2296                 return -1;
2297         }
2298
2299         nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
2300         if (nread == -1 || nread == n) {
2301                 return nread;
2302         }
2303
2304         DBG_ERR("Removing [%s] after short read [%zd]\n",
2305                 fsp_str_dbg(fsp), nread);
2306
2307         ret = SMB_VFS_NEXT_UNLINKAT(handle,
2308                         fsp->conn->cwd_fsp,
2309                         fsp->fsp_name,
2310                         0);
2311         if (ret != 0) {
2312                 DBG_ERR("Removing [%s] failed\n", fsp_str_dbg(fsp));
2313                 return -1;
2314         }
2315
2316         errno = EINVAL;
2317         return -1;
2318 }
2319
2320 static ssize_t fruit_pread_meta_adouble(vfs_handle_struct *handle,
2321                                         files_struct *fsp, void *data,
2322                                         size_t n, off_t offset)
2323 {
2324         AfpInfo *ai = NULL;
2325         struct adouble *ad = NULL;
2326         char afpinfo_buf[AFP_INFO_SIZE];
2327         char *p = NULL;
2328         ssize_t nread;
2329
2330         ai = afpinfo_new(talloc_tos());
2331         if (ai == NULL) {
2332                 return -1;
2333         }
2334
2335         ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
2336         if (ad == NULL) {
2337                 nread = -1;
2338                 goto fail;
2339         }
2340
2341         p = ad_get_entry(ad, ADEID_FINDERI);
2342         if (p == NULL) {
2343                 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
2344                 nread = -1;
2345                 goto fail;
2346         }
2347
2348         memcpy(&ai->afpi_FinderInfo[0], p, ADEDLEN_FINDERI);
2349
2350         nread = afpinfo_pack(ai, afpinfo_buf);
2351         if (nread != AFP_INFO_SIZE) {
2352                 nread = -1;
2353                 goto fail;
2354         }
2355
2356         memcpy(data, afpinfo_buf, n);
2357         nread = n;
2358
2359 fail:
2360         TALLOC_FREE(ai);
2361         return nread;
2362 }
2363
2364 static ssize_t fruit_pread_meta(vfs_handle_struct *handle,
2365                                 files_struct *fsp, void *data,
2366                                 size_t n, off_t offset)
2367 {
2368         struct fio *fio = fruit_get_complete_fio(handle, fsp);
2369         ssize_t nread;
2370         ssize_t to_return;
2371
2372         /*
2373          * OS X has a off-by-1 error in the offset calculation, so we're
2374          * bug compatible here. It won't hurt, as any relevant real
2375          * world read requests from the AFP_AfpInfo stream will be
2376          * offset=0 n=60. offset is ignored anyway, see below.
2377          */
2378         if ((offset < 0) || (offset >= AFP_INFO_SIZE + 1)) {
2379                 return 0;
2380         }
2381
2382         if (fio == NULL) {
2383                 DBG_ERR("Failed to fetch fsp extension");
2384                 return -1;
2385         }
2386
2387         /* Yes, macOS always reads from offset 0 */
2388         offset = 0;
2389         to_return = MIN(n, AFP_INFO_SIZE);
2390
2391         switch (fio->config->meta) {
2392         case FRUIT_META_STREAM:
2393                 nread = fruit_pread_meta_stream(handle, fsp, data,
2394                                                 to_return, offset);
2395                 break;
2396
2397         case FRUIT_META_NETATALK:
2398                 nread = fruit_pread_meta_adouble(handle, fsp, data,
2399                                                  to_return, offset);
2400                 break;
2401
2402         default:
2403                 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
2404                 return -1;
2405         }
2406
2407         if (nread == -1 && fio->fake_fd) {
2408                 AfpInfo *ai = NULL;
2409                 char afpinfo_buf[AFP_INFO_SIZE];
2410
2411                 ai = afpinfo_new(talloc_tos());
2412                 if (ai == NULL) {
2413                         return -1;
2414                 }
2415
2416                 nread = afpinfo_pack(ai, afpinfo_buf);
2417                 TALLOC_FREE(ai);
2418                 if (nread != AFP_INFO_SIZE) {
2419                         return -1;
2420                 }
2421
2422                 memcpy(data, afpinfo_buf, to_return);
2423                 return to_return;
2424         }
2425
2426         return nread;
2427 }
2428
2429 static ssize_t fruit_pread_rsrc_stream(vfs_handle_struct *handle,
2430                                        files_struct *fsp, void *data,
2431                                        size_t n, off_t offset)
2432 {
2433         return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
2434 }
2435
2436 static ssize_t fruit_pread_rsrc_xattr(vfs_handle_struct *handle,
2437                                       files_struct *fsp, void *data,
2438                                       size_t n, off_t offset)
2439 {
2440         return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
2441 }
2442
2443 static ssize_t fruit_pread_rsrc_adouble(vfs_handle_struct *handle,
2444                                         files_struct *fsp, void *data,
2445                                         size_t n, off_t offset)
2446 {
2447         struct fio *fio = fruit_get_complete_fio(handle, fsp);
2448         struct adouble *ad = NULL;
2449         ssize_t nread;
2450
2451         if (fio->ad_fsp == NULL) {
2452                 DBG_ERR("ad_fsp=NULL for [%s]\n", fsp_str_dbg(fsp));
2453                 errno = EBADF;
2454                 return -1;
2455         }
2456
2457         ad = ad_fget(talloc_tos(), handle, fio->ad_fsp, ADOUBLE_RSRC);
2458         if (ad == NULL) {
2459                 DBG_ERR("ad_fget [%s] failed [%s]\n",
2460                         fsp_str_dbg(fio->ad_fsp), strerror(errno));
2461                 return -1;
2462         }
2463
2464         nread = SMB_VFS_NEXT_PREAD(handle, fio->ad_fsp, data, n,
2465                                    offset + ad_getentryoff(ad, ADEID_RFORK));
2466
2467         TALLOC_FREE(ad);
2468         return nread;
2469 }
2470
2471 static ssize_t fruit_pread_rsrc(vfs_handle_struct *handle,
2472                                 files_struct *fsp, void *data,
2473                                 size_t n, off_t offset)
2474 {
2475         struct fio *fio = fruit_get_complete_fio(handle, fsp);
2476         ssize_t nread;
2477
2478         if (fio == NULL) {
2479                 errno = EINVAL;
2480                 return -1;
2481         }
2482
2483         switch (fio->config->rsrc) {
2484         case FRUIT_RSRC_STREAM:
2485                 nread = fruit_pread_rsrc_stream(handle, fsp, data, n, offset);
2486                 break;
2487
2488         case FRUIT_RSRC_ADFILE:
2489                 nread = fruit_pread_rsrc_adouble(handle, fsp, data, n, offset);
2490                 break;
2491
2492         case FRUIT_RSRC_XATTR:
2493                 nread = fruit_pread_rsrc_xattr(handle, fsp, data, n, offset);
2494                 break;
2495
2496         default:
2497                 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
2498                 return -1;
2499         }
2500
2501         return nread;
2502 }
2503
2504 static ssize_t fruit_pread(vfs_handle_struct *handle,
2505                            files_struct *fsp, void *data,
2506                            size_t n, off_t offset)
2507 {
2508         struct fio *fio = fruit_get_complete_fio(handle, fsp);
2509         ssize_t nread;
2510
2511         DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
2512                   fsp_str_dbg(fsp), (intmax_t)offset, n);
2513
2514         if (fio == NULL) {
2515                 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
2516         }
2517
2518         if (fio->type == ADOUBLE_META) {
2519                 nread = fruit_pread_meta(handle, fsp, data, n, offset);
2520         } else {
2521                 nread = fruit_pread_rsrc(handle, fsp, data, n, offset);
2522         }
2523
2524         DBG_DEBUG("Path [%s] nread [%zd]\n", fsp_str_dbg(fsp), nread);
2525         return nread;
2526 }
2527
2528 static bool fruit_must_handle_aio_stream(struct fio *fio)
2529 {
2530         if (fio == NULL) {
2531                 return false;
2532         };
2533
2534         if (fio->type == ADOUBLE_META) {
2535                 return true;
2536         }
2537
2538         if ((fio->type == ADOUBLE_RSRC) &&
2539             (fio->config->rsrc == FRUIT_RSRC_ADFILE))
2540         {
2541                 return true;
2542         }
2543
2544         return false;
2545 }
2546
2547 struct fruit_pread_state {
2548         ssize_t nread;
2549         struct vfs_aio_state vfs_aio_state;
2550 };
2551
2552 static void fruit_pread_done(struct tevent_req *subreq);
2553
2554 static struct tevent_req *fruit_pread_send(
2555         struct vfs_handle_struct *handle,
2556         TALLOC_CTX *mem_ctx,
2557         struct tevent_context *ev,
2558         struct files_struct *fsp,
2559         void *data,
2560         size_t n, off_t offset)
2561 {
2562         struct tevent_req *req = NULL;
2563         struct tevent_req *subreq = NULL;
2564         struct fruit_pread_state *state = NULL;
2565         struct fio *fio = fruit_get_complete_fio(handle, fsp);
2566
2567         req = tevent_req_create(mem_ctx, &state,
2568                                 struct fruit_pread_state);
2569         if (req == NULL) {
2570                 return NULL;
2571         }
2572
2573         if (fruit_must_handle_aio_stream(fio)) {
2574                 state->nread = SMB_VFS_PREAD(fsp, data, n, offset);
2575                 if (state->nread != n) {
2576                         if (state->nread != -1) {
2577                                 errno = EIO;
2578                         }
2579                         tevent_req_error(req, errno);
2580                         return tevent_req_post(req, ev);
2581                 }
2582                 tevent_req_done(req);
2583                 return tevent_req_post(req, ev);
2584         }
2585
2586         subreq = SMB_VFS_NEXT_PREAD_SEND(state, ev, handle, fsp,
2587                                          data, n, offset);
2588         if (tevent_req_nomem(req, subreq)) {
2589                 return tevent_req_post(req, ev);
2590         }
2591         tevent_req_set_callback(subreq, fruit_pread_done, req);
2592         return req;
2593 }
2594
2595 static void fruit_pread_done(struct tevent_req *subreq)
2596 {
2597         struct tevent_req *req = tevent_req_callback_data(
2598                 subreq, struct tevent_req);
2599         struct fruit_pread_state *state = tevent_req_data(
2600                 req, struct fruit_pread_state);
2601
2602         state->nread = SMB_VFS_PREAD_RECV(subreq, &state->vfs_aio_state);
2603         TALLOC_FREE(subreq);
2604
2605         if (tevent_req_error(req, state->vfs_aio_state.error)) {
2606                 return;
2607         }
2608         tevent_req_done(req);
2609 }
2610
2611 static ssize_t fruit_pread_recv(struct tevent_req *req,
2612                                         struct vfs_aio_state *vfs_aio_state)
2613 {
2614         struct fruit_pread_state *state = tevent_req_data(
2615                 req, struct fruit_pread_state);
2616
2617         if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
2618                 return -1;
2619         }
2620
2621         *vfs_aio_state = state->vfs_aio_state;
2622         return state->nread;
2623 }
2624
2625 static ssize_t fruit_pwrite_meta_stream(vfs_handle_struct *handle,
2626                                         files_struct *fsp, const void *data,
2627                                         size_t n, off_t offset)
2628 {
2629         struct fio *fio = fruit_get_complete_fio(handle, fsp);
2630         AfpInfo *ai = NULL;
2631         size_t nwritten;
2632         int ret;
2633         bool ok;
2634
2635         DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
2636                   fsp_str_dbg(fsp), (intmax_t)offset, n);
2637
2638         if (fio == NULL) {
2639                 return -1;
2640         }
2641
2642         if (fio->fake_fd) {
2643                 int fd = fsp_get_pathref_fd(fsp);
2644
2645                 ret = vfs_fake_fd_close(fd);
2646                 fsp_set_fd(fsp, -1);
2647                 if (ret != 0) {
2648                         DBG_ERR("Close [%s] failed: %s\n",
2649                                 fsp_str_dbg(fsp), strerror(errno));
2650                         return -1;
2651                 }
2652
2653                 fd = SMB_VFS_NEXT_OPENAT(handle,
2654                                          fsp->conn->cwd_fsp,
2655                                          fsp->fsp_name,
2656                                          fsp,
2657                                          fio->flags,
2658                                          fio->mode);
2659                 if (fd == -1) {
2660                         DBG_ERR("On-demand create [%s] in write failed: %s\n",
2661                                 fsp_str_dbg(fsp), strerror(errno));
2662                         return -1;
2663                 }
2664                 fsp_set_fd(fsp, fd);
2665                 fio->fake_fd = false;
2666         }
2667
2668         ai = afpinfo_unpack(talloc_tos(), data);
2669         if (ai == NULL) {
2670                 return -1;
2671         }
2672
2673         if (ai_empty_finderinfo(ai)) {
2674                 /*
2675                  * Writing an all 0 blob to the metadata stream results in the
2676                  * stream being removed on a macOS server. This ensures we
2677                  * behave the same and it verified by the "delete AFP_AfpInfo by
2678                  * writing all 0" test.
2679                  */
2680                 ret = SMB_VFS_NEXT_FTRUNCATE(handle, fsp, 0);
2681                 if (ret != 0) {
2682                         DBG_ERR("SMB_VFS_NEXT_FTRUNCATE on [%s] failed\n",
2683                                 fsp_str_dbg(fsp));
2684                         return -1;
2685                 }
2686
2687                 ok = set_delete_on_close(
2688                         fsp,
2689                         true,
2690                         handle->conn->session_info->security_token,
2691                         handle->conn->session_info->unix_token);
2692                 if (!ok) {
2693                         DBG_ERR("set_delete_on_close on [%s] failed\n",
2694                                 fsp_str_dbg(fsp));
2695                         return -1;
2696                 }
2697                 return n;
2698         }
2699
2700         nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
2701         if (nwritten != n) {
2702                 return -1;
2703         }
2704
2705         return n;
2706 }
2707
2708 static ssize_t fruit_pwrite_meta_netatalk(vfs_handle_struct *handle,
2709                                           files_struct *fsp, const void *data,
2710                                           size_t n, off_t offset)
2711 {
2712         struct adouble *ad = NULL;
2713         AfpInfo *ai = NULL;
2714         char *p = NULL;
2715         int ret;
2716         bool ok;
2717
2718         ai = afpinfo_unpack(talloc_tos(), data);
2719         if (ai == NULL) {
2720                 return -1;
2721         }
2722
2723         ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
2724         if (ad == NULL) {
2725                 ad = ad_init(talloc_tos(), ADOUBLE_META);
2726                 if (ad == NULL) {
2727                         return -1;
2728                 }
2729         }
2730         p = ad_get_entry(ad, ADEID_FINDERI);
2731         if (p == NULL) {
2732                 DBG_ERR("No ADEID_FINDERI for [%s]\n", fsp_str_dbg(fsp));
2733                 TALLOC_FREE(ad);
2734                 return -1;
2735         }
2736
2737         memcpy(p, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
2738
2739         ret = ad_fset(handle, ad, fsp);
2740         if (ret != 0) {
2741                 DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
2742                 TALLOC_FREE(ad);
2743                 return -1;
2744         }
2745
2746         TALLOC_FREE(ad);
2747
2748         if (!ai_empty_finderinfo(ai)) {
2749                 return n;
2750         }
2751
2752         /*
2753          * Writing an all 0 blob to the metadata stream results in the stream
2754          * being removed on a macOS server. This ensures we behave the same and
2755          * it verified by the "delete AFP_AfpInfo by writing all 0" test.
2756          */
2757
2758         ok = set_delete_on_close(
2759                 fsp,
2760                 true,
2761                 handle->conn->session_info->security_token,
2762                 handle->conn->session_info->unix_token);
2763         if (!ok) {
2764                 DBG_ERR("set_delete_on_close on [%s] failed\n",
2765                         fsp_str_dbg(fsp));
2766                 return -1;
2767         }
2768
2769         return n;
2770 }
2771
2772 static ssize_t fruit_pwrite_meta(vfs_handle_struct *handle,
2773                                  files_struct *fsp, const void *data,
2774                                  size_t n, off_t offset)
2775 {
2776         struct fio *fio = fruit_get_complete_fio(handle, fsp);
2777         ssize_t nwritten;
2778         uint8_t buf[AFP_INFO_SIZE];
2779         size_t to_write;
2780         size_t to_copy;
2781         int cmp;
2782
2783         if (fio == NULL) {
2784                 DBG_ERR("Failed to fetch fsp extension");
2785                 return -1;
2786         }
2787
2788         if (n < 3) {
2789                 errno = EINVAL;
2790                 return -1;
2791         }
2792
2793         if (offset != 0 && n < 60) {
2794                 errno = EINVAL;
2795                 return -1;
2796         }
2797
2798         cmp = memcmp(data, "AFP", 3);
2799         if (cmp != 0) {
2800                 errno = EINVAL;
2801                 return -1;
2802         }
2803
2804         if (n <= AFP_OFF_FinderInfo) {
2805                 /*
2806                  * Nothing to do here really, just return
2807                  */
2808                 return n;
2809         }
2810
2811         offset = 0;
2812
2813         to_copy = n;
2814         if (to_copy > AFP_INFO_SIZE) {
2815                 to_copy = AFP_INFO_SIZE;
2816         }
2817         memcpy(buf, data, to_copy);
2818
2819         to_write = n;
2820         if (to_write != AFP_INFO_SIZE) {
2821                 to_write = AFP_INFO_SIZE;
2822         }
2823
2824         switch (fio->config->meta) {
2825         case FRUIT_META_STREAM:
2826                 nwritten = fruit_pwrite_meta_stream(handle,
2827                                                     fsp,
2828                                                     buf,
2829                                                     to_write,
2830                                                     offset);
2831                 break;
2832
2833         case FRUIT_META_NETATALK:
2834                 nwritten = fruit_pwrite_meta_netatalk(handle,
2835                                                       fsp,
2836                                                       buf,
2837                                                       to_write,
2838                                                       offset);
2839                 break;
2840
2841         default:
2842                 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
2843                 return -1;
2844         }
2845
2846         if (nwritten != to_write) {
2847                 return -1;
2848         }
2849
2850         /*
2851          * Return the requested amount, verified against macOS SMB server
2852          */
2853         return n;
2854 }
2855
2856 static ssize_t fruit_pwrite_rsrc_stream(vfs_handle_struct *handle,
2857                                         files_struct *fsp, const void *data,
2858                                         size_t n, off_t offset)
2859 {
2860         return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
2861 }
2862
2863 static ssize_t fruit_pwrite_rsrc_xattr(vfs_handle_struct *handle,
2864                                        files_struct *fsp, const void *data,
2865                                        size_t n, off_t offset)
2866 {
2867         return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
2868 }
2869
2870 static ssize_t fruit_pwrite_rsrc_adouble(vfs_handle_struct *handle,
2871                                          files_struct *fsp, const void *data,
2872                                          size_t n, off_t offset)
2873 {
2874         struct fio *fio = fruit_get_complete_fio(handle, fsp);
2875         struct adouble *ad = NULL;
2876         ssize_t nwritten;
2877         int ret;
2878
2879         if (fio->ad_fsp == NULL) {
2880                 DBG_ERR("ad_fsp=NULL for [%s]\n", fsp_str_dbg(fsp));
2881                 errno = EBADF;
2882                 return -1;
2883         }
2884
2885         ad = ad_fget(talloc_tos(), handle, fio->ad_fsp, ADOUBLE_RSRC);
2886         if (ad == NULL) {
2887                 DBG_ERR("ad_fget [%s] failed [%s]\n",
2888                         fsp_str_dbg(fio->ad_fsp), strerror(errno));
2889                 return -1;
2890         }
2891
2892         nwritten = SMB_VFS_NEXT_PWRITE(handle, fio->ad_fsp, data, n,
2893                                        offset + ad_getentryoff(ad, ADEID_RFORK));
2894         if (nwritten != n) {
2895                 DBG_ERR("Short write on [%s] [%zd/%zd]\n",
2896                         fsp_str_dbg(fio->ad_fsp), nwritten, n);
2897                 TALLOC_FREE(ad);
2898                 return -1;
2899         }
2900
2901         if ((n + offset) > ad_getentrylen(ad, ADEID_RFORK)) {
2902                 ad_setentrylen(ad, ADEID_RFORK, n + offset);
2903                 ret = ad_fset(handle, ad, fio->ad_fsp);
2904                 if (ret != 0) {
2905                         DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fio->ad_fsp));
2906                         TALLOC_FREE(ad);
2907                         return -1;
2908                 }
2909         }
2910
2911         TALLOC_FREE(ad);
2912         return n;
2913 }
2914
2915 static ssize_t fruit_pwrite_rsrc(vfs_handle_struct *handle,
2916                                  files_struct *fsp, const void *data,
2917                                  size_t n, off_t offset)
2918 {
2919         struct fio *fio = fruit_get_complete_fio(handle, fsp);
2920         ssize_t nwritten;
2921
2922         if (fio == NULL) {
2923                 DBG_ERR("Failed to fetch fsp extension");
2924                 return -1;
2925         }
2926
2927         switch (fio->config->rsrc) {
2928         case FRUIT_RSRC_STREAM:
2929                 nwritten = fruit_pwrite_rsrc_stream(handle, fsp, data, n, offset);
2930                 break;
2931
2932         case FRUIT_RSRC_ADFILE:
2933                 nwritten = fruit_pwrite_rsrc_adouble(handle, fsp, data, n, offset);
2934                 break;
2935
2936         case FRUIT_RSRC_XATTR:
2937                 nwritten = fruit_pwrite_rsrc_xattr(handle, fsp, data, n, offset);
2938                 break;
2939
2940         default:
2941                 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
2942                 return -1;
2943         }
2944
2945         return nwritten;
2946 }
2947
2948 static ssize_t fruit_pwrite(vfs_handle_struct *handle,
2949                             files_struct *fsp, const void *data,
2950                             size_t n, off_t offset)
2951 {
2952         struct fio *fio = fruit_get_complete_fio(handle, fsp);
2953         ssize_t nwritten;
2954
2955         DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
2956                   fsp_str_dbg(fsp), (intmax_t)offset, n);
2957
2958         if (fio == NULL) {
2959                 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
2960         }
2961
2962         if (fio->type == ADOUBLE_META) {
2963                 nwritten = fruit_pwrite_meta(handle, fsp, data, n, offset);
2964         } else {
2965                 nwritten = fruit_pwrite_rsrc(handle, fsp, data, n, offset);
2966         }
2967
2968         DBG_DEBUG("Path [%s] nwritten=%zd\n", fsp_str_dbg(fsp), nwritten);
2969         return nwritten;
2970 }
2971
2972 struct fruit_pwrite_state {
2973         ssize_t nwritten;
2974         struct vfs_aio_state vfs_aio_state;
2975 };
2976
2977 static void fruit_pwrite_done(struct tevent_req *subreq);
2978
2979 static struct tevent_req *fruit_pwrite_send(
2980         struct vfs_handle_struct *handle,
2981         TALLOC_CTX *mem_ctx,
2982         struct tevent_context *ev,
2983         struct files_struct *fsp,
2984         const void *data,
2985         size_t n, off_t offset)
2986 {
2987         struct tevent_req *req = NULL;
2988         struct tevent_req *subreq = NULL;
2989         struct fruit_pwrite_state *state = NULL;
2990         struct fio *fio = fruit_get_complete_fio(handle, fsp);
2991
2992         req = tevent_req_create(mem_ctx, &state,
2993                                 struct fruit_pwrite_state);
2994         if (req == NULL) {
2995                 return NULL;
2996         }
2997
2998         if (fruit_must_handle_aio_stream(fio)) {
2999                 state->nwritten = SMB_VFS_PWRITE(fsp, data, n, offset);
3000                 if (state->nwritten != n) {
3001                         if (state->nwritten != -1) {
3002                                 errno = EIO;
3003                         }
3004                         tevent_req_error(req, errno);
3005                         return tevent_req_post(req, ev);
3006                 }
3007                 tevent_req_done(req);
3008                 return tevent_req_post(req, ev);
3009         }
3010
3011         subreq = SMB_VFS_NEXT_PWRITE_SEND(state, ev, handle, fsp,
3012                                           data, n, offset);
3013         if (tevent_req_nomem(req, subreq)) {
3014                 return tevent_req_post(req, ev);
3015         }
3016         tevent_req_set_callback(subreq, fruit_pwrite_done, req);
3017         return req;
3018 }
3019
3020 static void fruit_pwrite_done(struct tevent_req *subreq)
3021 {
3022         struct tevent_req *req = tevent_req_callback_data(
3023                 subreq, struct tevent_req);
3024         struct fruit_pwrite_state *state = tevent_req_data(
3025                 req, struct fruit_pwrite_state);
3026
3027         state->nwritten = SMB_VFS_PWRITE_RECV(subreq, &state->vfs_aio_state);
3028         TALLOC_FREE(subreq);
3029
3030         if (tevent_req_error(req, state->vfs_aio_state.error)) {
3031                 return;
3032         }
3033         tevent_req_done(req);
3034 }
3035
3036 static ssize_t fruit_pwrite_recv(struct tevent_req *req,
3037                                          struct vfs_aio_state *vfs_aio_state)
3038 {
3039         struct fruit_pwrite_state *state = tevent_req_data(
3040                 req, struct fruit_pwrite_state);
3041
3042         if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
3043                 return -1;
3044         }
3045
3046         *vfs_aio_state = state->vfs_aio_state;
3047         return state->nwritten;
3048 }
3049
3050 /**
3051  * Helper to stat/lstat the base file of an smb_fname.
3052  */
3053 static int fruit_stat_base(vfs_handle_struct *handle,
3054                            struct smb_filename *smb_fname,
3055                            bool follow_links)
3056 {
3057         char *tmp_stream_name;
3058         int rc;
3059
3060         tmp_stream_name = smb_fname->stream_name;
3061         smb_fname->stream_name = NULL;
3062         if (follow_links) {
3063                 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
3064         } else {
3065                 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
3066         }
3067         smb_fname->stream_name = tmp_stream_name;
3068
3069         DBG_DEBUG("fruit_stat_base [%s] dev [%ju] ino [%ju]\n",
3070                   smb_fname->base_name,
3071                   (uintmax_t)smb_fname->st.st_ex_dev,
3072                   (uintmax_t)smb_fname->st.st_ex_ino);
3073         return rc;
3074 }
3075
3076 static int fruit_stat_meta_stream(vfs_handle_struct *handle,
3077                                   struct smb_filename *smb_fname,
3078                                   bool follow_links)
3079 {
3080         int ret;
3081         ino_t ino;
3082
3083         ret = fruit_stat_base(handle, smb_fname, false);
3084         if (ret != 0) {
3085                 return -1;
3086         }
3087
3088         ino = hash_inode(&smb_fname->st, smb_fname->stream_name);
3089
3090         if (follow_links) {
3091                 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
3092         } else {
3093                 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
3094         }
3095
3096         smb_fname->st.st_ex_ino = ino;
3097
3098         return ret;
3099 }
3100
3101 static int fruit_stat_meta_netatalk(vfs_handle_struct *handle,
3102                                     struct smb_filename *smb_fname,
3103                                     bool follow_links)
3104 {
3105         struct adouble *ad = NULL;
3106
3107         /* Populate the stat struct with info from the base file. */
3108         if (fruit_stat_base(handle, smb_fname, follow_links) == -1) {
3109                 return -1;
3110         }
3111
3112         ad = ad_get_meta_fsp(talloc_tos(), handle, smb_fname);
3113         if (ad == NULL) {
3114                 DBG_INFO("fruit_stat_meta %s: %s\n",
3115                          smb_fname_str_dbg(smb_fname), strerror(errno));
3116                 errno = ENOENT;
3117                 return -1;
3118         }
3119         TALLOC_FREE(ad);
3120
3121         smb_fname->st.st_ex_size = AFP_INFO_SIZE;
3122         smb_fname->st.st_ex_ino = hash_inode(&smb_fname->st,
3123                                               smb_fname->stream_name);
3124         return 0;
3125 }
3126
3127 static int fruit_stat_meta(vfs_handle_struct *handle,
3128                            struct smb_filename *smb_fname,
3129                            bool follow_links)
3130 {
3131         struct fruit_config_data *config = NULL;
3132         int ret;
3133
3134         SMB_VFS_HANDLE_GET_DATA(handle, config,
3135                                 struct fruit_config_data, return -1);
3136
3137         switch (config->meta) {
3138         case FRUIT_META_STREAM:
3139                 ret = fruit_stat_meta_stream(handle, smb_fname, follow_links);
3140                 break;
3141
3142         case FRUIT_META_NETATALK:
3143                 ret = fruit_stat_meta_netatalk(handle, smb_fname, follow_links);
3144                 break;
3145
3146         default:
3147                 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3148                 return -1;
3149         }
3150
3151         return ret;
3152 }
3153
3154 static int fruit_stat_rsrc_netatalk(vfs_handle_struct *handle,
3155                                     struct smb_filename *smb_fname,
3156                                     bool follow_links)
3157 {
3158         struct adouble *ad = NULL;
3159         int ret;
3160
3161         ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
3162         if (ad == NULL) {
3163                 errno = ENOENT;
3164                 return -1;
3165         }
3166
3167         /* Populate the stat struct with info from the base file. */
3168         ret = fruit_stat_base(handle, smb_fname, follow_links);
3169         if (ret != 0) {
3170                 TALLOC_FREE(ad);
3171                 return -1;
3172         }
3173
3174         smb_fname->st.st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
3175         smb_fname->st.st_ex_ino = hash_inode(&smb_fname->st,
3176                                               smb_fname->stream_name);
3177         TALLOC_FREE(ad);
3178         return 0;
3179 }
3180
3181 static int fruit_stat_rsrc_stream(vfs_handle_struct *handle,
3182                                   struct smb_filename *smb_fname,
3183                                   bool follow_links)
3184 {
3185         int ret;
3186
3187         if (follow_links) {
3188                 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
3189         } else {
3190                 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
3191         }
3192
3193         return ret;
3194 }
3195
3196 static int fruit_stat_rsrc_xattr(vfs_handle_struct *handle,
3197                                  struct smb_filename *smb_fname,
3198                                  bool follow_links)
3199 {
3200 #ifdef HAVE_ATTROPEN
3201         int ret;
3202         int fd = -1;
3203
3204         /* Populate the stat struct with info from the base file. */
3205         ret = fruit_stat_base(handle, smb_fname, follow_links);
3206         if (ret != 0) {
3207                 return -1;
3208         }
3209
3210         fd = attropen(smb_fname->base_name,
3211                       AFPRESOURCE_EA_NETATALK,
3212                       O_RDONLY);
3213         if (fd == -1) {
3214                 return 0;
3215         }
3216
3217         ret = sys_fstat(fd, &smb_fname->st, false);
3218         if (ret != 0) {
3219                 close(fd);
3220                 DBG_ERR("fstat [%s:%s] failed\n", smb_fname->base_name,
3221                         AFPRESOURCE_EA_NETATALK);
3222                 return -1;
3223         }
3224         close(fd);
3225         fd = -1;
3226
3227         smb_fname->st.st_ex_ino = hash_inode(&smb_fname->st,
3228                                              smb_fname->stream_name);
3229
3230         return ret;
3231
3232 #else
3233         errno = ENOSYS;
3234         return -1;
3235 #endif
3236 }
3237
3238 static int fruit_stat_rsrc(vfs_handle_struct *handle,
3239                            struct smb_filename *smb_fname,
3240                            bool follow_links)
3241 {
3242         struct fruit_config_data *config = NULL;
3243         int ret;
3244
3245         DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3246
3247         SMB_VFS_HANDLE_GET_DATA(handle, config,
3248                                 struct fruit_config_data, return -1);
3249
3250         switch (config->rsrc) {
3251         case FRUIT_RSRC_STREAM:
3252                 ret = fruit_stat_rsrc_stream(handle, smb_fname, follow_links);
3253                 break;
3254
3255         case FRUIT_RSRC_XATTR:
3256                 ret = fruit_stat_rsrc_xattr(handle, smb_fname, follow_links);
3257                 break;
3258
3259         case FRUIT_RSRC_ADFILE:
3260                 ret = fruit_stat_rsrc_netatalk(handle, smb_fname, follow_links);
3261                 break;
3262
3263         default:
3264                 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3265                 return -1;
3266         }
3267
3268         return ret;
3269 }
3270
3271 static int fruit_stat(vfs_handle_struct *handle,
3272                       struct smb_filename *smb_fname)
3273 {
3274         int rc = -1;
3275
3276         DEBUG(10, ("fruit_stat called for %s\n",
3277                    smb_fname_str_dbg(smb_fname)));
3278
3279         if (!is_named_stream(smb_fname)) {
3280                 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
3281                 if (rc == 0) {
3282                         update_btime(handle, smb_fname);
3283                 }
3284                 return rc;
3285         }
3286
3287         /*
3288          * Note if lp_posix_paths() is true, we can never
3289          * get here as is_ntfs_stream_smb_fname() is
3290          * always false. So we never need worry about
3291          * not following links here.
3292          */
3293
3294         if (is_afpinfo_stream(smb_fname->stream_name)) {
3295                 rc = fruit_stat_meta(handle, smb_fname, true);
3296         } else if (is_afpresource_stream(smb_fname->stream_name)) {
3297                 rc = fruit_stat_rsrc(handle, smb_fname, true);
3298         } else {
3299                 return SMB_VFS_NEXT_STAT(handle, smb_fname);
3300         }
3301
3302         if (rc == 0) {
3303                 update_btime(handle, smb_fname);
3304                 smb_fname->st.st_ex_mode &= ~S_IFMT;
3305                 smb_fname->st.st_ex_mode |= S_IFREG;
3306                 smb_fname->st.st_ex_blocks =
3307                         smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
3308         }
3309         return rc;
3310 }
3311
3312 static int fruit_lstat(vfs_handle_struct *handle,
3313                        struct smb_filename *smb_fname)
3314 {
3315         int rc = -1;
3316
3317         DEBUG(10, ("fruit_lstat called for %s\n",
3318                    smb_fname_str_dbg(smb_fname)));
3319
3320         if (!is_named_stream(smb_fname)) {
3321                 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
3322                 if (rc == 0) {
3323                         update_btime(handle, smb_fname);
3324                 }
3325                 return rc;
3326         }
3327
3328         if (is_afpinfo_stream(smb_fname->stream_name)) {
3329                 rc = fruit_stat_meta(handle, smb_fname, false);
3330         } else if (is_afpresource_stream(smb_fname->stream_name)) {
3331                 rc = fruit_stat_rsrc(handle, smb_fname, false);
3332         } else {
3333                 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
3334         }
3335
3336         if (rc == 0) {
3337                 update_btime(handle, smb_fname);
3338                 smb_fname->st.st_ex_mode &= ~S_IFMT;
3339                 smb_fname->st.st_ex_mode |= S_IFREG;
3340                 smb_fname->st.st_ex_blocks =
3341                         smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
3342         }
3343         return rc;
3344 }
3345
3346 static int fruit_fstat_meta_stream(vfs_handle_struct *handle,
3347                                    files_struct *fsp,
3348                                    SMB_STRUCT_STAT *sbuf)
3349 {
3350         struct fio *fio = fruit_get_complete_fio(handle, fsp);
3351         struct smb_filename smb_fname;
3352         ino_t ino;
3353         int ret;
3354
3355         if (fio == NULL) {
3356                 return -1;
3357         }
3358
3359         if (fio->fake_fd) {
3360                 ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
3361                 if (ret != 0) {
3362                         return -1;
3363                 }
3364
3365                 *sbuf = fsp->base_fsp->fsp_name->st;
3366                 sbuf->st_ex_size = AFP_INFO_SIZE;
3367                 sbuf->st_ex_ino = hash_inode(sbuf, fsp->fsp_name->stream_name);
3368                 return 0;
3369         }
3370
3371         smb_fname = (struct smb_filename) {
3372                 .base_name = fsp->fsp_name->base_name,
3373                 .twrp = fsp->fsp_name->twrp,
3374         };
3375
3376         ret = fruit_stat_base(handle, &smb_fname, false);
3377         if (ret != 0) {
3378                 return -1;
3379         }
3380         *sbuf = smb_fname.st;
3381
3382         ino = hash_inode(sbuf, fsp->fsp_name->stream_name);
3383
3384         ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
3385         if (ret != 0) {
3386                 return -1;
3387         }
3388
3389         sbuf->st_ex_ino = ino;
3390         return 0;
3391 }
3392
3393 static int fruit_fstat_meta_netatalk(vfs_handle_struct *handle,
3394                                      files_struct *fsp,
3395                                      SMB_STRUCT_STAT *sbuf)
3396 {
3397         int ret;
3398
3399         ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
3400         if (ret != 0) {
3401                 return -1;
3402         }
3403
3404         *sbuf = fsp->base_fsp->fsp_name->st;
3405         sbuf->st_ex_size = AFP_INFO_SIZE;
3406         sbuf->st_ex_ino = hash_inode(sbuf, fsp->fsp_name->stream_name);
3407
3408         return 0;
3409 }
3410
3411 static int fruit_fstat_meta(vfs_handle_struct *handle,
3412                             files_struct *fsp,
3413                             SMB_STRUCT_STAT *sbuf,
3414                             struct fio *fio)
3415 {
3416         int ret;
3417
3418         DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
3419
3420         switch (fio->config->meta) {
3421         case FRUIT_META_STREAM:
3422                 ret = fruit_fstat_meta_stream(handle, fsp, sbuf);
3423                 break;
3424
3425         case FRUIT_META_NETATALK:
3426                 ret = fruit_fstat_meta_netatalk(handle, fsp, sbuf);
3427                 break;
3428
3429         default:
3430                 DBG_ERR("Unexpected meta config [%d]\n", fio->config->meta);
3431                 return -1;
3432         }
3433
3434         DBG_DEBUG("Path [%s] ret [%d]\n", fsp_str_dbg(fsp), ret);
3435         return ret;
3436 }
3437
3438 static int fruit_fstat_rsrc_xattr(vfs_handle_struct *handle,
3439                                   files_struct *fsp,
3440                                   SMB_STRUCT_STAT *sbuf)
3441 {
3442         return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
3443 }
3444
3445 static int fruit_fstat_rsrc_stream(vfs_handle_struct *handle,
3446                                    files_struct *fsp,
3447                                    SMB_STRUCT_STAT *sbuf)
3448 {
3449         return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
3450 }
3451
3452 static int fruit_fstat_rsrc_adouble(vfs_handle_struct *handle,
3453                                     files_struct *fsp,
3454                                     SMB_STRUCT_STAT *sbuf)
3455 {
3456         struct fio *fio = fruit_get_complete_fio(handle, fsp);
3457         struct adouble *ad = NULL;
3458         int ret;
3459
3460         if (fio->ad_fsp == NULL) {
3461                 DBG_ERR("ad_fsp=NULL for [%s]\n", fsp_str_dbg(fsp));
3462                 errno = EBADF;
3463                 return -1;
3464         }
3465
3466         /* Populate the stat struct with info from the base file. */
3467         ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
3468         if (ret == -1) {
3469                 return -1;
3470         }
3471
3472         ad = ad_fget(talloc_tos(), handle, fio->ad_fsp, ADOUBLE_RSRC);
3473         if (ad == NULL) {
3474                 DBG_ERR("ad_fget [%s] failed [%s]\n",
3475                         fsp_str_dbg(fio->ad_fsp), strerror(errno));
3476                 return -1;
3477         }
3478
3479         *sbuf = fsp->base_fsp->fsp_name->st;
3480         sbuf->st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
3481         sbuf->st_ex_ino = hash_inode(sbuf, fsp->fsp_name->stream_name);
3482
3483         TALLOC_FREE(ad);
3484         return 0;
3485 }
3486
3487 static int fruit_fstat_rsrc(vfs_handle_struct *handle, files_struct *fsp,
3488                             SMB_STRUCT_STAT *sbuf, struct fio *fio)
3489 {
3490         int ret;
3491
3492         switch (fio->config->rsrc) {
3493         case FRUIT_RSRC_STREAM:
3494                 ret = fruit_fstat_rsrc_stream(handle, fsp, sbuf);
3495                 break;
3496
3497         case FRUIT_RSRC_ADFILE:
3498                 ret = fruit_fstat_rsrc_adouble(handle, fsp, sbuf);
3499                 break;
3500
3501         case FRUIT_RSRC_XATTR:
3502                 ret = fruit_fstat_rsrc_xattr(handle, fsp, sbuf);
3503                 break;
3504
3505         default:
3506                 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
3507                 return -1;
3508         }
3509
3510         return ret;
3511 }
3512
3513 static int fruit_fstat(vfs_handle_struct *handle, files_struct *fsp,
3514                        SMB_STRUCT_STAT *sbuf)
3515 {
3516         struct fio *fio = fruit_get_complete_fio(handle, fsp);
3517         int rc;
3518
3519         if (fio == NULL) {
3520                 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
3521         }
3522
3523         DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
3524
3525         if (fio->type == ADOUBLE_META) {
3526                 rc = fruit_fstat_meta(handle, fsp, sbuf, fio);
3527         } else {
3528                 rc = fruit_fstat_rsrc(handle, fsp, sbuf, fio);
3529         }
3530
3531         if (rc == 0) {
3532                 sbuf->st_ex_mode &= ~S_IFMT;
3533                 sbuf->st_ex_mode |= S_IFREG;
3534                 sbuf->st_ex_blocks = sbuf->st_ex_size / STAT_ST_BLOCKSIZE + 1;
3535         }
3536
3537         DBG_DEBUG("Path [%s] rc [%d] size [%"PRIdMAX"]\n",
3538                   fsp_str_dbg(fsp), rc, (intmax_t)sbuf->st_ex_size);
3539         return rc;
3540 }
3541
3542 static NTSTATUS delete_invalid_meta_stream(
3543         vfs_handle_struct *handle,
3544         const struct smb_filename *smb_fname,
3545         TALLOC_CTX *mem_ctx,
3546         unsigned int *pnum_streams,
3547         struct stream_struct **pstreams,
3548         off_t size)
3549 {
3550         struct smb_filename *sname = NULL;
3551         NTSTATUS status;
3552         int ret;
3553         bool ok;
3554
3555         ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams, AFPINFO_STREAM);
3556         if (!ok) {
3557                 return NT_STATUS_INTERNAL_ERROR;
3558         }
3559
3560         if (size == 0) {
3561                 return NT_STATUS_OK;
3562         }
3563
3564         status = synthetic_pathref(talloc_tos(),
3565                                    handle->conn->cwd_fsp,
3566                                    smb_fname->base_name,
3567                                    AFPINFO_STREAM_NAME,
3568                                    NULL,
3569                                    smb_fname->twrp,
3570                                    0,
3571                                    &sname);
3572         if (!NT_STATUS_IS_OK(status)) {
3573                 return NT_STATUS_NO_MEMORY;
3574         }
3575
3576         ret = SMB_VFS_NEXT_UNLINKAT(handle,
3577                         handle->conn->cwd_fsp,
3578                         sname,
3579                         0);
3580         if (ret != 0) {
3581                 DBG_ERR("Removing [%s] failed\n", smb_fname_str_dbg(sname));
3582                 TALLOC_FREE(sname);
3583                 return map_nt_error_from_unix(errno);
3584         }
3585
3586         TALLOC_FREE(sname);
3587         return NT_STATUS_OK;
3588 }
3589
3590 static NTSTATUS fruit_streaminfo_meta_stream(
3591         vfs_handle_struct *handle,
3592         struct files_struct *fsp,
3593         const struct smb_filename *smb_fname,
3594         TALLOC_CTX *mem_ctx,
3595         unsigned int *pnum_streams,
3596         struct stream_struct **pstreams)
3597 {
3598         struct stream_struct *stream = *pstreams;
3599         unsigned int num_streams = *pnum_streams;
3600         int i;
3601
3602         for (i = 0; i < num_streams; i++) {
3603                 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
3604                         break;
3605                 }
3606         }
3607
3608         if (i == num_streams) {
3609                 return NT_STATUS_OK;
3610         }
3611
3612         if (stream[i].size != AFP_INFO_SIZE) {
3613                 DBG_ERR("Removing invalid AFPINFO_STREAM size [%jd] from [%s]\n",
3614                         (intmax_t)stream[i].size, smb_fname_str_dbg(smb_fname));
3615
3616                 return delete_invalid_meta_stream(handle,
3617                                                   smb_fname,
3618                                                   mem_ctx,
3619                                                   pnum_streams,
3620                                                   pstreams,
3621                                                   stream[i].size);
3622         }
3623
3624
3625         return NT_STATUS_OK;
3626 }
3627
3628 static NTSTATUS fruit_streaminfo_meta_netatalk(
3629         vfs_handle_struct *handle,
3630         struct files_struct *fsp,
3631         const struct smb_filename *smb_fname,
3632         TALLOC_CTX *mem_ctx,
3633         unsigned int *pnum_streams,
3634         struct stream_struct **pstreams)
3635 {
3636         struct stream_struct *stream = *pstreams;
3637         unsigned int num_streams = *pnum_streams;
3638         struct adouble *ad = NULL;
3639         bool is_fi_empty;
3640         int i;
3641         bool ok;
3642
3643         /* Remove the Netatalk xattr from the list */
3644         ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
3645                               ":" NETATALK_META_XATTR ":$DATA");
3646         if (!ok) {
3647                 return NT_STATUS_NO_MEMORY;
3648         }
3649
3650         /*
3651          * Check if there's a AFPINFO_STREAM from the VFS streams
3652          * backend and if yes, remove it from the list
3653          */
3654         for (i = 0; i < num_streams; i++) {
3655                 if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
3656                         break;
3657                 }
3658         }
3659
3660         if (i < num_streams) {
3661                 DBG_WARNING("Unexpected AFPINFO_STREAM on [%s]\n",
3662                             smb_fname_str_dbg(smb_fname));
3663
3664                 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
3665                                       AFPINFO_STREAM);
3666                 if (!ok) {
3667                         return NT_STATUS_INTERNAL_ERROR;
3668                 }
3669         }
3670
3671         ad = ad_get_meta_fsp(talloc_tos(), handle, smb_fname);
3672         if (ad == NULL) {
3673                 return NT_STATUS_OK;
3674         }
3675
3676         is_fi_empty = ad_empty_finderinfo(ad);
3677         TALLOC_FREE(ad);
3678
3679         if (is_fi_empty) {
3680                 return NT_STATUS_OK;
3681         }
3682
3683         ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
3684                               AFPINFO_STREAM_NAME, AFP_INFO_SIZE,
3685                               smb_roundup(handle->conn, AFP_INFO_SIZE));
3686         if (!ok) {
3687                 return NT_STATUS_NO_MEMORY;
3688         }
3689
3690         return NT_STATUS_OK;
3691 }
3692
3693 static NTSTATUS fruit_streaminfo_meta(vfs_handle_struct *handle,
3694                                       struct files_struct *fsp,
3695                                       const struct smb_filename *smb_fname,
3696                                       TALLOC_CTX *mem_ctx,
3697                                       unsigned int *pnum_streams,
3698                                       struct stream_struct **pstreams)
3699 {
3700         struct fruit_config_data *config = NULL;
3701         NTSTATUS status;
3702
3703         SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
3704                                 return NT_STATUS_INTERNAL_ERROR);
3705
3706         switch (config->meta) {
3707         case FRUIT_META_NETATALK:
3708                 status = fruit_streaminfo_meta_netatalk(handle, fsp, smb_fname,
3709                                                         mem_ctx, pnum_streams,
3710                                                         pstreams);
3711                 break;
3712
3713         case FRUIT_META_STREAM:
3714                 status = fruit_streaminfo_meta_stream(handle, fsp, smb_fname,
3715                                                       mem_ctx, pnum_streams,
3716                                                       pstreams);
3717                 break;
3718
3719         default:
3720                 return NT_STATUS_INTERNAL_ERROR;
3721         }
3722
3723         return status;
3724 }
3725
3726 static NTSTATUS fruit_streaminfo_rsrc_stream(
3727         vfs_handle_struct *handle,
3728         struct files_struct *fsp,
3729         const struct smb_filename *smb_fname,
3730         TALLOC_CTX *mem_ctx,
3731         unsigned int *pnum_streams,
3732         struct stream_struct **pstreams)
3733 {
3734         bool ok;
3735
3736         ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
3737         if (!ok) {
3738                 DBG_ERR("Filtering resource stream failed\n");
3739                 return NT_STATUS_INTERNAL_ERROR;
3740         }
3741         return NT_STATUS_OK;
3742 }
3743
3744 static NTSTATUS fruit_streaminfo_rsrc_xattr(
3745         vfs_handle_struct *handle,
3746         struct files_struct *fsp,
3747         const struct smb_filename *smb_fname,
3748         TALLOC_CTX *mem_ctx,
3749         unsigned int *pnum_streams,
3750         struct stream_struct **pstreams)
3751 {
3752         bool ok;
3753
3754         ok = filter_empty_rsrc_stream(pnum_streams, pstreams);
3755         if (!ok) {
3756                 DBG_ERR("Filtering resource stream failed\n");
3757                 return NT_STATUS_INTERNAL_ERROR;
3758         }
3759         return NT_STATUS_OK;
3760 }
3761
3762 static NTSTATUS fruit_streaminfo_rsrc_adouble(
3763         vfs_handle_struct *handle,
3764         struct files_struct *fsp,
3765         const struct smb_filename *smb_fname,
3766         TALLOC_CTX *mem_ctx,
3767         unsigned int *pnum_streams,
3768         struct stream_struct **pstreams)
3769 {
3770         struct stream_struct *stream = *pstreams;
3771         unsigned int num_streams = *pnum_streams;
3772         struct adouble *ad = NULL;
3773         bool ok;
3774         size_t rlen;
3775         int i;
3776
3777         /*
3778          * Check if there's a AFPRESOURCE_STREAM from the VFS streams backend
3779          * and if yes, remove it from the list
3780          */
3781         for (i = 0; i < num_streams; i++) {
3782                 if (strequal_m(stream[i].name, AFPRESOURCE_STREAM)) {
3783                         break;
3784                 }
3785         }
3786
3787         if (i < num_streams) {
3788                 DBG_WARNING("Unexpected AFPRESOURCE_STREAM on [%s]\n",
3789                             smb_fname_str_dbg(smb_fname));
3790
3791                 ok = del_fruit_stream(mem_ctx, pnum_streams, pstreams,
3792                                       AFPRESOURCE_STREAM);
3793                 if (!ok) {
3794                         return NT_STATUS_INTERNAL_ERROR;
3795                 }
3796         }
3797
3798         ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
3799         if (ad == NULL) {
3800                 return NT_STATUS_OK;
3801         }
3802
3803         rlen = ad_getentrylen(ad, ADEID_RFORK);
3804         TALLOC_FREE(ad);
3805
3806         if (rlen == 0) {
3807                 return NT_STATUS_OK;
3808         }
3809
3810         ok = add_fruit_stream(mem_ctx, pnum_streams, pstreams,
3811                               AFPRESOURCE_STREAM_NAME, rlen,
3812                               smb_roundup(handle->conn, rlen));
3813         if (!ok) {
3814                 return NT_STATUS_NO_MEMORY;
3815         }
3816
3817         return NT_STATUS_OK;
3818 }
3819
3820 static NTSTATUS fruit_streaminfo_rsrc(vfs_handle_struct *handle,
3821                                       struct files_struct *fsp,
3822                                       const struct smb_filename *smb_fname,
3823                                       TALLOC_CTX *mem_ctx,
3824                                       unsigned int *pnum_streams,
3825                                       struct stream_struct **pstreams)
3826 {
3827         struct fruit_config_data *config = NULL;
3828         NTSTATUS status;
3829
3830         SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
3831                                 return NT_STATUS_INTERNAL_ERROR);
3832
3833         switch (config->rsrc) {
3834         case FRUIT_RSRC_STREAM:
3835                 status = fruit_streaminfo_rsrc_stream(handle, fsp, smb_fname,
3836                                                       mem_ctx, pnum_streams,
3837                                                       pstreams);
3838                 break;
3839
3840         case FRUIT_RSRC_XATTR:
3841                 status = fruit_streaminfo_rsrc_xattr(handle, fsp, smb_fname,
3842                                                      mem_ctx, pnum_streams,
3843                                                      pstreams);
3844                 break;
3845
3846         case FRUIT_RSRC_ADFILE:
3847                 status = fruit_streaminfo_rsrc_adouble(handle, fsp, smb_fname,
3848                                                        mem_ctx, pnum_streams,
3849                                                        pstreams);
3850                 break;
3851
3852         default:
3853                 return NT_STATUS_INTERNAL_ERROR;
3854         }
3855
3856         return status;
3857 }
3858
3859 static void fruit_filter_empty_streams(unsigned int *pnum_streams,
3860                                        struct stream_struct **pstreams)
3861 {
3862         unsigned num_streams = *pnum_streams;
3863         struct stream_struct *streams = *pstreams;
3864         unsigned i = 0;
3865
3866         if (!global_fruit_config.nego_aapl) {
3867                 return;
3868         }
3869
3870         while (i < num_streams) {
3871                 struct smb_filename smb_fname = (struct smb_filename) {
3872                         .stream_name = streams[i].name,
3873                 };
3874
3875                 if (is_ntfs_default_stream_smb_fname(&smb_fname)
3876                     || streams[i].size > 0)
3877                 {
3878                         i++;
3879                         continue;
3880                 }
3881
3882                 streams[i] = streams[num_streams - 1];
3883                 num_streams--;
3884         }
3885
3886         *pnum_streams = num_streams;
3887 }
3888
3889 static NTSTATUS fruit_fstreaminfo(vfs_handle_struct *handle,
3890                                  struct files_struct *fsp,
3891                                  TALLOC_CTX *mem_ctx,
3892                                  unsigned int *pnum_streams,
3893                                  struct stream_struct **pstreams)
3894 {
3895         struct fruit_config_data *config = NULL;
3896         const struct smb_filename *smb_fname = NULL;
3897         NTSTATUS status;
3898
3899         smb_fname = fsp->fsp_name;
3900
3901         SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
3902                                 return NT_STATUS_UNSUCCESSFUL);
3903
3904         DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3905
3906         status = SMB_VFS_NEXT_FSTREAMINFO(handle, fsp, mem_ctx,
3907                                          pnum_streams, pstreams);
3908         if (!NT_STATUS_IS_OK(status)) {
3909                 return status;
3910         }
3911
3912         fruit_filter_empty_streams(pnum_streams, pstreams);
3913
3914         status = fruit_streaminfo_meta(handle, fsp, smb_fname,
3915                                        mem_ctx, pnum_streams, pstreams);
3916         if (!NT_STATUS_IS_OK(status)) {
3917                 return status;
3918         }
3919
3920         status = fruit_streaminfo_rsrc(handle, fsp, smb_fname,
3921                                        mem_ctx, pnum_streams, pstreams);
3922         if (!NT_STATUS_IS_OK(status)) {
3923                 return status;
3924         }
3925
3926         return NT_STATUS_OK;
3927 }
3928
3929 static int fruit_fntimes(vfs_handle_struct *handle,
3930                          files_struct *fsp,
3931                          struct smb_file_time *ft)
3932 {
3933         int rc = 0;
3934         struct adouble *ad = NULL;
3935         struct fruit_config_data *config = NULL;
3936
3937         SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
3938                                 return -1);
3939
3940         if ((config->meta != FRUIT_META_NETATALK) ||
3941             is_omit_timespec(&ft->create_time))
3942         {
3943                 return SMB_VFS_NEXT_FNTIMES(handle, fsp, ft);
3944         }
3945
3946         DBG_DEBUG("set btime for %s to %s\n", fsp_str_dbg(fsp),
3947                   time_to_asc(convert_timespec_to_time_t(ft->create_time)));
3948
3949         ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
3950         if (ad == NULL) {
3951                 goto exit;
3952         }
3953
3954         ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX,
3955                    convert_time_t_to_uint32_t(ft->create_time.tv_sec));
3956
3957         rc = ad_fset(handle, ad, fsp);
3958
3959 exit:
3960
3961         TALLOC_FREE(ad);
3962         if (rc != 0) {
3963                 DBG_WARNING("%s\n", fsp_str_dbg(fsp));
3964                 return -1;
3965         }
3966         return SMB_VFS_NEXT_FNTIMES(handle, fsp, ft);
3967 }
3968
3969 static int fruit_fallocate(struct vfs_handle_struct *handle,
3970                            struct files_struct *fsp,
3971                            uint32_t mode,
3972                            off_t offset,
3973                            off_t len)
3974 {
3975         struct fio *fio = fruit_get_complete_fio(handle, fsp);
3976
3977         if (fio == NULL) {
3978                 return SMB_VFS_NEXT_FALLOCATE(handle, fsp, mode, offset, len);
3979         }
3980
3981         /* Let the pwrite code path handle it. */
3982         errno = ENOSYS;
3983         return -1;
3984 }
3985
3986 static int fruit_ftruncate_rsrc_xattr(struct vfs_handle_struct *handle,
3987                                       struct files_struct *fsp,
3988                                       off_t offset)
3989 {
3990 #ifdef HAVE_ATTROPEN
3991         return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
3992 #endif
3993         return 0;
3994 }
3995
3996 static int fruit_ftruncate_rsrc_adouble(struct vfs_handle_struct *handle,
3997                                         struct files_struct *fsp,
3998                                         off_t offset)
3999 {
4000         struct fio *fio = fruit_get_complete_fio(handle, fsp);
4001         int rc;
4002         struct adouble *ad = NULL;
4003         off_t ad_off;
4004
4005         if (fio->ad_fsp == NULL) {
4006                 DBG_ERR("ad_fsp=NULL for [%s]\n", fsp_str_dbg(fsp));
4007                 errno = EBADF;
4008                 return -1;
4009         }
4010
4011         ad = ad_fget(talloc_tos(), handle, fio->ad_fsp, ADOUBLE_RSRC);
4012         if (ad == NULL) {
4013                 DBG_ERR("ad_fget [%s] failed [%s]\n",
4014                         fsp_str_dbg(fio->ad_fsp), strerror(errno));
4015                 return -1;
4016         }
4017
4018         ad_off = ad_getentryoff(ad, ADEID_RFORK);
4019
4020         rc = SMB_VFS_NEXT_FTRUNCATE(handle, fio->ad_fsp, offset + ad_off);
4021         if (rc != 0) {
4022                 TALLOC_FREE(ad);
4023                 return -1;
4024         }
4025
4026         ad_setentrylen(ad, ADEID_RFORK, offset);
4027
4028         rc = ad_fset(handle, ad, fio->ad_fsp);
4029         if (rc != 0) {
4030                 DBG_ERR("ad_fset [%s] failed [%s]\n",
4031                         fsp_str_dbg(fio->ad_fsp), strerror(errno));
4032                 TALLOC_FREE(ad);
4033                 return -1;
4034         }
4035
4036         TALLOC_FREE(ad);
4037         return 0;
4038 }
4039
4040 static int fruit_ftruncate_rsrc_stream(struct vfs_handle_struct *handle,
4041                                        struct files_struct *fsp,
4042                                        off_t offset)
4043 {
4044         return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
4045 }
4046
4047 static int fruit_ftruncate_rsrc(struct vfs_handle_struct *handle,
4048                                 struct files_struct *fsp,
4049                                 off_t offset)
4050 {
4051         struct fio *fio = fruit_get_complete_fio(handle, fsp);
4052         int ret;
4053
4054         if (fio == NULL) {
4055                 DBG_ERR("Failed to fetch fsp extension");
4056                 return -1;
4057         }
4058
4059         switch (fio->config->rsrc) {
4060         case FRUIT_RSRC_XATTR:
4061                 ret = fruit_ftruncate_rsrc_xattr(handle, fsp, offset);
4062                 break;
4063
4064         case FRUIT_RSRC_ADFILE:
4065                 ret = fruit_ftruncate_rsrc_adouble(handle, fsp, offset);
4066                 break;
4067
4068         case FRUIT_RSRC_STREAM:
4069                 ret = fruit_ftruncate_rsrc_stream(handle, fsp, offset);
4070                 break;
4071
4072         default:
4073                 DBG_ERR("Unexpected rsrc config [%d]\n", fio->config->rsrc);
4074                 return -1;
4075         }
4076
4077
4078         return ret;
4079 }
4080
4081 static int fruit_ftruncate_meta(struct vfs_handle_struct *handle,
4082                                 struct files_struct *fsp,
4083                                 off_t offset)
4084 {
4085         if (offset > 60) {
4086                 DBG_WARNING("ftruncate %s to %jd",
4087                             fsp_str_dbg(fsp), (intmax_t)offset);
4088                 /* OS X returns NT_STATUS_ALLOTTED_SPACE_EXCEEDED  */
4089                 errno = EOVERFLOW;
4090                 return -1;
4091         }
4092
4093         /* OS X returns success but does nothing  */
4094         DBG_INFO("ignoring ftruncate %s to %jd\n",
4095                  fsp_str_dbg(fsp), (intmax_t)offset);
4096         return 0;
4097 }
4098
4099 static int fruit_ftruncate(struct vfs_handle_struct *handle,
4100                            struct files_struct *fsp,
4101                            off_t offset)
4102 {
4103         struct fio *fio = fruit_get_complete_fio(handle, fsp);
4104         int ret;
4105
4106         DBG_DEBUG("Path [%s] offset [%"PRIdMAX"]\n", fsp_str_dbg(fsp),
4107                   (intmax_t)offset);
4108
4109         if (fio == NULL) {
4110                 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
4111         }
4112
4113         if (fio->type == ADOUBLE_META) {
4114                 ret = fruit_ftruncate_meta(handle, fsp, offset);
4115         } else {
4116                 ret = fruit_ftruncate_rsrc(handle, fsp, offset);
4117         }
4118
4119         DBG_DEBUG("Path [%s] result [%d]\n", fsp_str_dbg(fsp), ret);
4120         return ret;
4121 }
4122
4123 static NTSTATUS fruit_create_file(vfs_handle_struct *handle,
4124                                   struct smb_request *req,
4125                                   struct smb_filename *smb_fname,
4126                                   uint32_t access_mask,
4127                                   uint32_t share_access,
4128                                   uint32_t create_disposition,
4129                                   uint32_t create_options,
4130                                   uint32_t file_attributes,
4131                                   uint32_t oplock_request,
4132                                   const struct smb2_lease *lease,
4133                                   uint64_t allocation_size,
4134                                   uint32_t private_flags,
4135                                   struct security_descriptor *sd,
4136                                   struct ea_list *ea_list,
4137                                   files_struct **result,
4138                                   int *pinfo,
4139                                   const struct smb2_create_blobs *in_context_blobs,
4140                                   struct smb2_create_blobs *out_context_blobs)
4141 {
4142         NTSTATUS status;
4143         struct fruit_config_data *config = NULL;
4144         files_struct *fsp = NULL;
4145         bool internal_open = (oplock_request & INTERNAL_OPEN_ONLY);
4146         int ret;
4147
4148         status = check_aapl(handle, req, in_context_blobs, out_context_blobs);
4149         if (!NT_STATUS_IS_OK(status)) {
4150                 goto fail;
4151         }
4152
4153         SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
4154                                 return NT_STATUS_UNSUCCESSFUL);
4155
4156         if (is_apple_stream(smb_fname->stream_name) && !internal_open) {
4157                 uint32_t conv_flags  = 0;
4158
4159                 if (config->wipe_intentionally_left_blank_rfork) {
4160                         conv_flags |= AD_CONV_WIPE_BLANK;
4161                 }
4162                 if (config->delete_empty_adfiles) {
4163                         conv_flags |= AD_CONV_DELETE;
4164                 }
4165
4166                 ret = ad_convert(handle,
4167                                  smb_fname,
4168                                  macos_string_replace_map,
4169                                  conv_flags);
4170                 if (ret != 0) {
4171                         DBG_ERR("ad_convert() failed\n");
4172                         return NT_STATUS_UNSUCCESSFUL;
4173                 }
4174         }
4175
4176         status = SMB_VFS_NEXT_CREATE_FILE(
4177                 handle, req, smb_fname,
4178                 access_mask, share_access,
4179                 create_disposition, create_options,
4180                 file_attributes, oplock_request,
4181                 lease,
4182                 allocation_size, private_flags,
4183                 sd, ea_list, result,
4184                 pinfo, in_context_blobs, out_context_blobs);
4185         if (!NT_STATUS_IS_OK(status)) {
4186                 return status;
4187         }
4188
4189         fsp = *result;
4190
4191         if (global_fruit_config.nego_aapl) {
4192                 if (config->posix_rename && fsp->fsp_flags.is_directory) {
4193                         /*
4194                          * Enable POSIX directory rename behaviour
4195                          */
4196                         fsp->posix_flags |= FSP_POSIX_FLAGS_RENAME;
4197                 }
4198         }
4199
4200         /*
4201          * If this is a plain open for existing files, opening an 0
4202          * byte size resource fork MUST fail with
4203          * NT_STATUS_OBJECT_NAME_NOT_FOUND.
4204          *
4205          * Cf the vfs_fruit torture tests in test_rfork_create().
4206          */
4207         if (global_fruit_config.nego_aapl &&
4208             create_disposition == FILE_OPEN &&
4209             smb_fname->st.st_ex_size == 0 &&
4210             is_named_stream(smb_fname))
4211         {
4212                 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
4213                 goto fail;
4214         }
4215
4216         if (is_named_stream(smb_fname) || fsp->fsp_flags.is_directory) {
4217                 return status;
4218         }
4219
4220         if ((config->locking == FRUIT_LOCKING_NETATALK) &&
4221             (fsp->op != NULL) &&
4222             !fsp->fsp_flags.is_pathref)
4223         {
4224                 status = fruit_check_access(
4225                         handle, *result,
4226                         access_mask,
4227                         share_access);
4228                 if (!NT_STATUS_IS_OK(status)) {
4229                         goto fail;
4230                 }
4231         }
4232
4233         return status;
4234
4235 fail:
4236         DEBUG(10, ("fruit_create_file: %s\n", nt_errstr(status)));
4237
4238         if (fsp) {
4239                 close_file(req, fsp, ERROR_CLOSE);
4240                 *result = fsp = NULL;
4241         }
4242
4243         return status;
4244 }
4245
4246 static NTSTATUS fruit_freaddir_attr(struct vfs_handle_struct *handle,
4247                                     struct files_struct *fsp,
4248                                     TALLOC_CTX *mem_ctx,
4249                                     struct readdir_attr_data **pattr_data)
4250 {
4251         struct fruit_config_data *config = NULL;
4252         struct readdir_attr_data *attr_data;
4253         uint32_t conv_flags  = 0;
4254         NTSTATUS status;
4255         int ret;
4256
4257         SMB_VFS_HANDLE_GET_DATA(handle, config,
4258                                 struct fruit_config_data,
4259                                 return NT_STATUS_UNSUCCESSFUL);
4260
4261         if (!global_fruit_config.nego_aapl) {
4262                 return SMB_VFS_NEXT_FREADDIR_ATTR(handle,
4263                                                   fsp,
4264                                                   mem_ctx,
4265                                                   pattr_data);
4266         }
4267
4268         DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
4269
4270         if (config->wipe_intentionally_left_blank_rfork) {
4271                 conv_flags |= AD_CONV_WIPE_BLANK;
4272         }
4273         if (config->delete_empty_adfiles) {
4274                 conv_flags |= AD_CONV_DELETE;
4275         }
4276
4277         ret = ad_convert(handle,
4278                          fsp->fsp_name,
4279                          macos_string_replace_map,
4280                          conv_flags);
4281         if (ret != 0) {
4282                 DBG_ERR("ad_convert() failed\n");
4283                 return NT_STATUS_UNSUCCESSFUL;
4284         }
4285
4286         *pattr_data = talloc_zero(mem_ctx, struct readdir_attr_data);
4287         if (*pattr_data == NULL) {
4288                 return NT_STATUS_NO_MEMORY;
4289         }
4290         attr_data = *pattr_data;
4291         attr_data->type = RDATTR_AAPL;
4292
4293         /*
4294          * Mac metadata: compressed FinderInfo, resource fork length
4295          * and creation date
4296          */
4297         status = readdir_attr_macmeta(handle, fsp->fsp_name, attr_data);
4298         if (!NT_STATUS_IS_OK(status)) {
4299                 /*
4300                  * Error handling is tricky: if we return failure from
4301                  * this function, the corresponding directory entry
4302                  * will to be passed to the client, so we really just
4303                  * want to error out on fatal errors.
4304                  */
4305                 if  (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
4306                         goto fail;
4307                 }
4308         }
4309
4310         /*
4311          * UNIX mode
4312          */
4313         if (config->unix_info_enabled) {
4314                 attr_data->attr_data.aapl.unix_mode =
4315                         fsp->fsp_name->st.st_ex_mode;
4316         }
4317
4318         /*
4319          * max_access
4320          */
4321         if (!config->readdir_attr_max_access) {
4322                 attr_data->attr_data.aapl.max_access = FILE_GENERIC_ALL;
4323         } else {
4324                 status = smbd_calculate_access_mask_fsp(fsp->conn->cwd_fsp,
4325                         fsp,
4326                         false,
4327                         SEC_FLAG_MAXIMUM_ALLOWED,
4328                         &attr_data->attr_data.aapl.max_access);
4329                 if (!NT_STATUS_IS_OK(status)) {
4330                         goto fail;
4331                 }
4332         }
4333
4334         return NT_STATUS_OK;
4335
4336 fail:
4337         DBG_WARNING("Path [%s], error: %s\n", fsp_str_dbg(fsp),
4338                    nt_errstr(status));
4339         TALLOC_FREE(*pattr_data);
4340         return status;
4341 }
4342
4343 static NTSTATUS fruit_fget_nt_acl(vfs_handle_struct *handle,
4344                                   files_struct *fsp,
4345                                   uint32_t security_info,
4346                                   TALLOC_CTX *mem_ctx,
4347                                   struct security_descriptor **ppdesc)
4348 {
4349         NTSTATUS status;
4350         struct security_ace ace;
4351         struct dom_sid sid;
4352         struct fruit_config_data *config;
4353
4354         SMB_VFS_HANDLE_GET_DATA(handle, config,
4355                                 struct fruit_config_data,
4356                                 return NT_STATUS_UNSUCCESSFUL);
4357
4358         status = SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
4359                                           mem_ctx, ppdesc);
4360         if (!NT_STATUS_IS_OK(status)) {
4361                 return status;
4362         }
4363
4364         /*
4365          * Add MS NFS style ACEs with uid, gid and mode
4366          */
4367         if (!global_fruit_config.nego_aapl) {
4368                 return NT_STATUS_OK;
4369         }
4370         if (!config->unix_info_enabled) {
4371                 return NT_STATUS_OK;
4372         }
4373
4374         /* First remove any existing ACE's with NFS style mode/uid/gid SIDs. */
4375         status = remove_virtual_nfs_aces(*ppdesc);
4376         if (!NT_STATUS_IS_OK(status)) {
4377                 DBG_WARNING("failed to remove MS NFS style ACEs\n");
4378                 return status;
4379         }
4380
4381         /* MS NFS style mode */
4382         sid_compose(&sid, &global_sid_Unix_NFS_Mode, fsp->fsp_name->st.st_ex_mode);
4383         init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
4384         status = security_descriptor_dacl_add(*ppdesc, &ace);
4385         if (!NT_STATUS_IS_OK(status)) {
4386                 DEBUG(1,("failed to add MS NFS style ACE\n"));
4387                 return status;
4388         }
4389
4390         /* MS NFS style uid */
4391         sid_compose(&sid, &global_sid_Unix_NFS_Users, fsp->fsp_name->st.st_ex_uid);
4392         init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
4393         status = security_descriptor_dacl_add(*ppdesc, &ace);
4394         if (!NT_STATUS_IS_OK(status)) {
4395                 DEBUG(1,("failed to add MS NFS style ACE\n"));
4396                 return status;
4397         }
4398
4399         /* MS NFS style gid */
4400         sid_compose(&sid, &global_sid_Unix_NFS_Groups, fsp->fsp_name->st.st_ex_gid);
4401         init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
4402         status = security_descriptor_dacl_add(*ppdesc, &ace);
4403         if (!NT_STATUS_IS_OK(status)) {
4404                 DEBUG(1,("failed to add MS NFS style ACE\n"));
4405                 return status;
4406         }
4407
4408         return NT_STATUS_OK;
4409 }
4410
4411 static NTSTATUS fruit_fset_nt_acl(vfs_handle_struct *handle,
4412                                   files_struct *fsp,
4413                                   uint32_t security_info_sent,
4414                                   const struct security_descriptor *orig_psd)
4415 {
4416         NTSTATUS status;
4417         bool do_chmod;
4418         mode_t ms_nfs_mode = 0;
4419         int result;
4420         struct security_descriptor *psd = NULL;
4421         uint32_t orig_num_aces = 0;
4422
4423         if (orig_psd->dacl != NULL) {
4424                 orig_num_aces = orig_psd->dacl->num_aces;
4425         }
4426
4427         psd = security_descriptor_copy(talloc_tos(), orig_psd);
4428         if (psd == NULL) {
4429                 return NT_STATUS_NO_MEMORY;
4430         }
4431
4432         DBG_DEBUG("fruit_fset_nt_acl: %s\n", fsp_str_dbg(fsp));
4433
4434         status = check_ms_nfs(handle, fsp, psd, &ms_nfs_mode, &do_chmod);
4435         if (!NT_STATUS_IS_OK(status)) {
4436                 DEBUG(1, ("fruit_fset_nt_acl: check_ms_nfs failed%s\n", fsp_str_dbg(fsp)));
4437                 TALLOC_FREE(psd);
4438                 return status;
4439         }
4440
4441         /*
4442          * If only ms_nfs ACE entries were sent, ensure we set the DACL
4443          * sent/present flags correctly now we've removed them.
4444          */
4445
4446         if (orig_num_aces != 0) {
4447                 /*
4448                  * Are there any ACE's left ?
4449                  */
4450                 if (psd->dacl->num_aces == 0) {
4451                         /* No - clear the DACL sent/present flags. */
4452                         security_info_sent &= ~SECINFO_DACL;
4453                         psd->type &= ~SEC_DESC_DACL_PRESENT;
4454                 }
4455         }
4456
4457         status = SMB_VFS_NEXT_FSET_NT_ACL(handle, fsp, security_info_sent, psd);
4458         if (!NT_STATUS_IS_OK(status)) {
4459                 DEBUG(1, ("fruit_fset_nt_acl: SMB_VFS_NEXT_FSET_NT_ACL failed%s\n", fsp_str_dbg(fsp)));
4460                 TALLOC_FREE(psd);
4461                 return status;
4462         }
4463
4464         if (do_chmod) {
4465                 result = SMB_VFS_FCHMOD(fsp, ms_nfs_mode);
4466                 if (result != 0) {
4467                         DBG_WARNING("%s, result: %d, %04o error %s\n",
4468                                 fsp_str_dbg(fsp),
4469                                 result,
4470                                 (unsigned)ms_nfs_mode,
4471                                 strerror(errno));
4472                         status = map_nt_error_from_unix(errno);
4473                         TALLOC_FREE(psd);
4474                         return status;
4475                 }
4476         }
4477
4478         TALLOC_FREE(psd);
4479         return NT_STATUS_OK;
4480 }
4481
4482 static struct vfs_offload_ctx *fruit_offload_ctx;
4483
4484 struct fruit_offload_read_state {
4485         struct vfs_handle_struct *handle;
4486         struct tevent_context *ev;
4487         files_struct *fsp;
4488         uint32_t fsctl;
4489         DATA_BLOB token;
4490 };
4491
4492 static void fruit_offload_read_done(struct tevent_req *subreq);
4493
4494 static struct tevent_req *fruit_offload_read_send(
4495         TALLOC_CTX *mem_ctx,
4496         struct tevent_context *ev,
4497         struct vfs_handle_struct *handle,
4498         files_struct *fsp,
4499         uint32_t fsctl,
4500         uint32_t ttl,
4501         off_t offset,
4502         size_t to_copy)
4503 {
4504         struct tevent_req *req = NULL;
4505         struct tevent_req *subreq = NULL;
4506         struct fruit_offload_read_state *state = NULL;
4507
4508         req = tevent_req_create(mem_ctx, &state,
4509                                 struct fruit_offload_read_state);
4510         if (req == NULL) {
4511                 return NULL;
4512         }
4513         *state = (struct fruit_offload_read_state) {
4514                 .handle = handle,
4515                 .ev = ev,
4516                 .fsp = fsp,
4517                 .fsctl = fsctl,
4518         };
4519
4520         subreq = SMB_VFS_NEXT_OFFLOAD_READ_SEND(mem_ctx, ev, handle, fsp,
4521                                                 fsctl, ttl, offset, to_copy);
4522         if (tevent_req_nomem(subreq, req)) {
4523                 return tevent_req_post(req, ev);
4524         }
4525         tevent_req_set_callback(subreq, fruit_offload_read_done, req);
4526         return req;
4527 }
4528
4529 static void fruit_offload_read_done(struct tevent_req *subreq)
4530 {
4531         struct tevent_req *req = tevent_req_callback_data(
4532                 subreq, struct tevent_req);
4533         struct fruit_offload_read_state *state = tevent_req_data(
4534                 req, struct fruit_offload_read_state);
4535         NTSTATUS status;
4536
4537         status = SMB_VFS_NEXT_OFFLOAD_READ_RECV(subreq,
4538                                                 state->handle,
4539                                                 state,
4540                                                 &state->token);
4541         TALLOC_FREE(subreq);
4542         if (tevent_req_nterror(req, status)) {
4543                 return;
4544         }
4545
4546         if (state->fsctl != FSCTL_SRV_REQUEST_RESUME_KEY) {
4547                 tevent_req_done(req);
4548                 return;
4549         }
4550
4551         status = vfs_offload_token_ctx_init(state->fsp->conn->sconn->client,
4552                                             &fruit_offload_ctx);
4553         if (tevent_req_nterror(req, status)) {
4554                 return;
4555         }
4556
4557         status = vfs_offload_token_db_store_fsp(fruit_offload_ctx,
4558                                                 state->fsp,
4559                                                 &state->token);
4560         if (tevent_req_nterror(req, status)) {
4561                 return;
4562         }
4563
4564         tevent_req_done(req);
4565         return;
4566 }
4567
4568 static NTSTATUS fruit_offload_read_recv(struct tevent_req *req,
4569                                         struct vfs_handle_struct *handle,
4570                                         TALLOC_CTX *mem_ctx,
4571                                         DATA_BLOB *token)
4572 {
4573         struct fruit_offload_read_state *state = tevent_req_data(
4574                 req, struct fruit_offload_read_state);
4575         NTSTATUS status;
4576
4577         if (tevent_req_is_nterror(req, &status)) {
4578                 tevent_req_received(req);
4579                 return status;
4580         }
4581
4582         token->length = state->token.length;
4583         token->data = talloc_move(mem_ctx, &state->token.data);
4584
4585         tevent_req_received(req);
4586         return NT_STATUS_OK;
4587 }
4588
4589 struct fruit_offload_write_state {
4590         struct vfs_handle_struct *handle;
4591         off_t copied;
4592         struct files_struct *src_fsp;
4593         struct files_struct *dst_fsp;
4594         bool is_copyfile;
4595 };
4596
4597 static void fruit_offload_write_done(struct tevent_req *subreq);
4598 static struct tevent_req *fruit_offload_write_send(struct vfs_handle_struct *handle,
4599                                                 TALLOC_CTX *mem_ctx,
4600                                                 struct tevent_context *ev,
4601                                                 uint32_t fsctl,
4602                                                 DATA_BLOB *token,
4603                                                 off_t transfer_offset,
4604                                                 struct files_struct *dest_fsp,
4605                                                 off_t dest_off,
4606                                                 off_t num)
4607 {
4608         struct tevent_req *req, *subreq;
4609         struct fruit_offload_write_state *state;
4610         NTSTATUS status;
4611         struct fruit_config_data *config;
4612         off_t src_off = transfer_offset;
4613         files_struct *src_fsp = NULL;
4614         off_t to_copy = num;
4615         bool copyfile_enabled = false;
4616
4617         DEBUG(10,("soff: %ju, doff: %ju, len: %ju\n",
4618                   (uintmax_t)src_off, (uintmax_t)dest_off, (uintmax_t)num));
4619
4620         SMB_VFS_HANDLE_GET_DATA(handle, config,
4621                                 struct fruit_config_data,
4622                                 return NULL);
4623
4624         req = tevent_req_create(mem_ctx, &state,
4625                                 struct fruit_offload_write_state);
4626         if (req == NULL) {
4627                 return NULL;
4628         }
4629         state->handle = handle;
4630         state->dst_fsp = dest_fsp;
4631
4632         switch (fsctl) {
4633         case FSCTL_SRV_COPYCHUNK:
4634         case FSCTL_SRV_COPYCHUNK_WRITE:
4635                 copyfile_enabled = config->copyfile_enabled;
4636                 break;
4637         default:
4638                 break;
4639         }
4640
4641         /*
4642          * Check if this a OS X copyfile style copychunk request with
4643          * a requested chunk count of 0 that was translated to a
4644          * offload_write_send VFS call overloading the parameters src_off
4645          * = dest_off = num = 0.
4646          */
4647         if (copyfile_enabled && num == 0 && src_off == 0 && dest_off == 0) {
4648                 status = vfs_offload_token_db_fetch_fsp(
4649                         fruit_offload_ctx, token, &src_fsp);
4650                 if (tevent_req_nterror(req, status)) {
4651                         return tevent_req_post(req, ev);
4652                 }
4653                 state->src_fsp = src_fsp;
4654
4655                 status = vfs_stat_fsp(src_fsp);
4656                 if (tevent_req_nterror(req, status)) {
4657                         return tevent_req_post(req, ev);
4658                 }
4659
4660                 to_copy = src_fsp->fsp_name->st.st_ex_size;
4661                 state->is_copyfile = true;
4662         }
4663
4664         subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle,
4665                                               mem_ctx,
4666                                               ev,
4667                                               fsctl,
4668                                               token,
4669                                               transfer_offset,
4670                                               dest_fsp,
4671                                               dest_off,
4672                                               to_copy);
4673         if (tevent_req_nomem(subreq, req)) {
4674                 return tevent_req_post(req, ev);
4675         }
4676
4677         tevent_req_set_callback(subreq, fruit_offload_write_done, req);
4678         return req;
4679 }
4680
4681 static void fruit_offload_write_done(struct tevent_req *subreq)
4682 {
4683         struct tevent_req *req = tevent_req_callback_data(
4684                 subreq, struct tevent_req);
4685         struct fruit_offload_write_state *state = tevent_req_data(
4686                 req, struct fruit_offload_write_state);
4687         NTSTATUS status;
4688         unsigned int num_streams = 0;
4689         struct stream_struct *streams = NULL;
4690         unsigned int i;
4691         struct smb_filename *src_fname_tmp = NULL;
4692         struct smb_filename *dst_fname_tmp = NULL;
4693
4694         status = SMB_VFS_NEXT_OFFLOAD_WRITE_RECV(state->handle,
4695                                               subreq,
4696                                               &state->copied);
4697         TALLOC_FREE(subreq);
4698         if (tevent_req_nterror(req, status)) {
4699                 return;
4700         }
4701
4702         if (!state->is_copyfile) {
4703                 tevent_req_done(req);
4704                 return;
4705         }
4706
4707         /*
4708          * Now copy all remaining streams. We know the share supports
4709          * streams, because we're in vfs_fruit. We don't do this async
4710          * because streams are few and small.
4711          */
4712         status = vfs_fstreaminfo(state->src_fsp,
4713                                 req, &num_streams, &streams);
4714         if (tevent_req_nterror(req, status)) {
4715                 return;
4716         }
4717
4718         if (num_streams == 1) {
4719                 /* There is always one stream, ::$DATA. */
4720                 tevent_req_done(req);
4721                 return;
4722         }
4723
4724         for (i = 0; i < num_streams; i++) {
4725                 DEBUG(10, ("%s: stream: '%s'/%zu\n",
4726                           __func__, streams[i].name, (size_t)streams[i].size));
4727
4728                 src_fname_tmp = synthetic_smb_fname(
4729                         req,
4730                         state->src_fsp->fsp_name->base_name,
4731                         streams[i].name,
4732                         NULL,
4733                         state->src_fsp->fsp_name->twrp,
4734                         state->src_fsp->fsp_name->flags);
4735                 if (tevent_req_nomem(src_fname_tmp, req)) {
4736                         return;
4737                 }
4738
4739                 if (is_ntfs_default_stream_smb_fname(src_fname_tmp)) {
4740                         TALLOC_FREE(src_fname_tmp);
4741                         continue;
4742                 }
4743
4744                 dst_fname_tmp = synthetic_smb_fname(
4745                         req,
4746                         state->dst_fsp->fsp_name->base_name,
4747                         streams[i].name,
4748                         NULL,
4749                         state->dst_fsp->fsp_name->twrp,
4750                         state->dst_fsp->fsp_name->flags);
4751                 if (tevent_req_nomem(dst_fname_tmp, req)) {
4752                         TALLOC_FREE(src_fname_tmp);
4753                         return;
4754                 }
4755
4756                 status = copy_file(req,
4757                                    state->handle->conn,
4758                                    src_fname_tmp,
4759                                    dst_fname_tmp,
4760                                    OPENX_FILE_CREATE_IF_NOT_EXIST,
4761                                    0, false);
4762                 if (!NT_STATUS_IS_OK(status)) {
4763                         DEBUG(1, ("%s: copy %s to %s failed: %s\n", __func__,
4764                                   smb_fname_str_dbg(src_fname_tmp),
4765                                   smb_fname_str_dbg(dst_fname_tmp),
4766                                   nt_errstr(status)));
4767                         TALLOC_FREE(src_fname_tmp);
4768                         TALLOC_FREE(dst_fname_tmp);
4769                         tevent_req_nterror(req, status);
4770                         return;
4771                 }
4772
4773                 TALLOC_FREE(src_fname_tmp);
4774                 TALLOC_FREE(dst_fname_tmp);
4775         }
4776
4777         TALLOC_FREE(streams);
4778         TALLOC_FREE(src_fname_tmp);
4779         TALLOC_FREE(dst_fname_tmp);
4780         tevent_req_done(req);
4781 }
4782
4783 static NTSTATUS fruit_offload_write_recv(struct vfs_handle_struct *handle,
4784                                       struct tevent_req *req,
4785                                       off_t *copied)
4786 {
4787         struct fruit_offload_write_state *state = tevent_req_data(
4788                 req, struct fruit_offload_write_state);
4789         NTSTATUS status;
4790
4791         if (tevent_req_is_nterror(req, &status)) {
4792                 DEBUG(1, ("server side copy chunk failed: %s\n",
4793                           nt_errstr(status)));
4794                 *copied = 0;
4795                 tevent_req_received(req);
4796                 return status;
4797         }
4798
4799         *copied = state->copied;
4800         tevent_req_received(req);
4801
4802         return NT_STATUS_OK;
4803 }
4804
4805 static char *fruit_get_bandsize_line(char **lines, int numlines)
4806 {
4807         static regex_t re;
4808         static bool re_initialized = false;
4809         int i;
4810         int ret;
4811
4812         if (!re_initialized) {
4813                 ret = regcomp(&re, "^[[:blank:]]*<key>band-size</key>$", 0);
4814                 if (ret != 0) {
4815                         return NULL;
4816                 }
4817                 re_initialized = true;
4818         }
4819
4820         for (i = 0; i < numlines; i++) {
4821                 regmatch_t matches[1];
4822
4823                 ret = regexec(&re, lines[i], 1, matches, 0);
4824                 if (ret == 0) {
4825                         /*
4826                          * Check if the match was on the last line, sa we want
4827                          * the subsequent line.
4828                          */
4829                         if (i + 1 == numlines) {
4830                                 return NULL;
4831                         }
4832                         return lines[i + 1];
4833                 }
4834                 if (ret != REG_NOMATCH) {
4835                         return NULL;
4836                 }
4837         }
4838
4839         return NULL;
4840 }
4841
4842 static bool fruit_get_bandsize_from_line(char *line, size_t *_band_size)
4843 {
4844         static regex_t re;
4845         static bool re_initialized = false;
4846         regmatch_t matches[2];
4847         uint64_t band_size;
4848         int ret;
4849         bool ok;
4850
4851         if (!re_initialized) {
4852                 ret = regcomp(&re,
4853                               "^[[:blank:]]*"
4854                               "<integer>\\([[:digit:]]*\\)</integer>$",
4855                               0);
4856                 if (ret != 0) {
4857                         return false;
4858                 }
4859                 re_initialized = true;
4860         }
4861
4862         ret = regexec(&re, line, 2, matches, 0);
4863         if (ret != 0) {
4864                 DBG_ERR("regex failed [%s]\n", line);
4865                 return false;
4866         }
4867
4868         line[matches[1].rm_eo] = '\0';
4869
4870         ok = conv_str_u64(&line[matches[1].rm_so], &band_size);
4871         if (!ok) {
4872                 return false;
4873         }
4874         *_band_size = (size_t)band_size;
4875         return true;
4876 }
4877
4878 /*
4879  * This reads and parses an Info.plist from a TM sparsebundle looking for the
4880  * "band-size" key and value.
4881  */
4882 static bool fruit_get_bandsize(vfs_handle_struct *handle,
4883                                const char *dir,
4884                                size_t *band_size)
4885 {
4886 #define INFO_PLIST_MAX_SIZE 64*1024
4887         char *plist = NULL;
4888         struct smb_filename *smb_fname = NULL;
4889         files_struct *fsp = NULL;
4890         uint8_t *file_data = NULL;
4891         char **lines = NULL;
4892         char *band_size_line = NULL;
4893         size_t plist_file_size;
4894         ssize_t nread;
4895         int numlines;
4896         int ret;
4897         bool ok = false;
4898         NTSTATUS status;
4899
4900         plist = talloc_asprintf(talloc_tos(),
4901                                 "%s/%s/Info.plist",
4902                                 handle->conn->connectpath,
4903                                 dir);
4904         if (plist == NULL) {
4905                 ok = false;
4906                 goto out;
4907         }
4908
4909         smb_fname = synthetic_smb_fname(talloc_tos(),
4910                                         plist,
4911                                         NULL,
4912                                         NULL,
4913                                         0,
4914                                         0);
4915         if (smb_fname == NULL) {
4916                 ok = false;
4917                 goto out;
4918         }
4919
4920         ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
4921         if (ret != 0) {
4922                 DBG_INFO("Ignoring Sparsebundle without Info.plist [%s]\n", dir);
4923                 ok = true;
4924                 goto out;
4925         }
4926
4927         plist_file_size = smb_fname->st.st_ex_size;
4928
4929         if (plist_file_size > INFO_PLIST_MAX_SIZE) {
4930                 DBG_INFO("%s is too large, ignoring\n", plist);
4931                 ok = true;
4932                 goto out;
4933         }
4934
4935         status = SMB_VFS_NEXT_CREATE_FILE(
4936                 handle,                         /* conn */
4937                 NULL,                           /* req */
4938                 smb_fname,                      /* fname */
4939                 FILE_GENERIC_READ,              /* access_mask */
4940                 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
4941                 FILE_OPEN,                      /* create_disposition */
4942                 0,                              /* create_options */
4943                 0,                              /* file_attributes */
4944                 INTERNAL_OPEN_ONLY,             /* oplock_request */
4945                 NULL,                           /* lease */
4946                 0,                              /* allocation_size */
4947                 0,                              /* private_flags */
4948                 NULL,                           /* sd */
4949                 NULL,                           /* ea_list */
4950                 &fsp,                           /* result */
4951                 NULL,                           /* psbuf */
4952                 NULL, NULL);                    /* create context */
4953         if (!NT_STATUS_IS_OK(status)) {
4954                 DBG_INFO("Opening [%s] failed [%s]\n",
4955                          smb_fname_str_dbg(smb_fname), nt_errstr(status));
4956                 ok = false;
4957                 goto out;
4958         }
4959
4960         file_data = talloc_zero_array(talloc_tos(),
4961                                       uint8_t,
4962                                       plist_file_size + 1);
4963         if (file_data == NULL) {
4964                 ok = false;
4965                 goto out;
4966         }
4967
4968         nread = SMB_VFS_NEXT_PREAD(handle, fsp, file_data, plist_file_size, 0);
4969         if (nread != plist_file_size) {
4970                 DBG_ERR("Short read on [%s]: %zu/%zd\n",
4971                         fsp_str_dbg(fsp), nread, plist_file_size);
4972                 ok = false;
4973                 goto out;
4974
4975         }
4976
4977         status = close_file(NULL, fsp, NORMAL_CLOSE);
4978         fsp = NULL;
4979         if (!NT_STATUS_IS_OK(status)) {
4980                 DBG_ERR("close_file failed: %s\n", nt_errstr(status));
4981                 ok = false;
4982                 goto out;
4983         }
4984
4985         lines = file_lines_parse((char *)file_data,
4986                                  plist_file_size,
4987                                  &numlines,
4988                                  talloc_tos());
4989         if (lines == NULL) {
4990                 ok = false;
4991                 goto out;
4992         }
4993
4994         band_size_line = fruit_get_bandsize_line(lines, numlines);
4995         if (band_size_line == NULL) {
4996                 DBG_ERR("Didn't find band-size key in [%s]\n",
4997                         smb_fname_str_dbg(smb_fname));
4998                 ok = false;
4999                 goto out;
5000         }
5001
5002         ok = fruit_get_bandsize_from_line(band_size_line, band_size);
5003         if (!ok) {
5004                 DBG_ERR("fruit_get_bandsize_from_line failed\n");
5005                 goto out;
5006         }
5007
5008         DBG_DEBUG("Parsed band-size [%zu] for [%s]\n", *band_size, plist);
5009
5010 out:
5011         if (fsp != NULL) {
5012                 status = close_file(NULL, fsp, NORMAL_CLOSE);
5013                 if (!NT_STATUS_IS_OK(status)) {
5014                         DBG_ERR("close_file failed: %s\n", nt_errstr(status));
5015                 }
5016                 fsp = NULL;
5017         }
5018         TALLOC_FREE(plist);
5019         TALLOC_FREE(smb_fname);
5020         TALLOC_FREE(file_data);
5021         TALLOC_FREE(lines);
5022         return ok;
5023 }
5024
5025 struct fruit_disk_free_state {
5026         off_t total_size;
5027 };
5028
5029 static bool fruit_get_num_bands(vfs_handle_struct *handle,
5030                                 const char *bundle,
5031                                 size_t *_nbands)
5032 {
5033         char *path = NULL;
5034         struct smb_filename *bands_dir = NULL;
5035         struct smb_Dir *dir_hnd = NULL;
5036         const char *dname = NULL;
5037         char *talloced = NULL;
5038         long offset = 0;
5039         size_t nbands;
5040
5041         path = talloc_asprintf(talloc_tos(),
5042                                "%s/%s/bands",
5043                                handle->conn->connectpath,
5044                                bundle);
5045         if (path == NULL) {
5046                 return false;
5047         }
5048
5049         bands_dir = synthetic_smb_fname(talloc_tos(),
5050                                         path,
5051                                         NULL,
5052                                         NULL,
5053                                         0,
5054                                         0);
5055         TALLOC_FREE(path);
5056         if (bands_dir == NULL) {
5057                 return false;
5058         }
5059
5060         dir_hnd = OpenDir(talloc_tos(), handle->conn, bands_dir, NULL, 0);
5061         if (dir_hnd == NULL) {
5062                 TALLOC_FREE(bands_dir);
5063                 return false;
5064         }
5065
5066         nbands = 0;
5067
5068         while ((dname = ReadDirName(dir_hnd, &offset, NULL, &talloced))
5069                != NULL)
5070         {
5071                 if (ISDOT(dname) || ISDOTDOT(dname)) {
5072                         continue;
5073                 }
5074                 nbands++;
5075         }
5076         TALLOC_FREE(dir_hnd);
5077
5078         DBG_DEBUG("%zu bands in [%s]\n", nbands, smb_fname_str_dbg(bands_dir));
5079
5080         TALLOC_FREE(bands_dir);
5081
5082         *_nbands = nbands;
5083         return true;
5084 }
5085
5086 static bool fruit_tmsize_do_dirent(vfs_handle_struct *handle,
5087                                    struct fruit_disk_free_state *state,
5088                                    const char *name)
5089 {
5090         bool ok;
5091         char *p = NULL;
5092         size_t sparsebundle_strlen = strlen("sparsebundle");
5093         size_t bandsize = 0;
5094         size_t nbands;
5095         off_t tm_size;
5096
5097         p = strstr(name, "sparsebundle");
5098         if (p == NULL) {
5099                 return true;
5100         }
5101
5102         if (p[sparsebundle_strlen] != '\0') {
5103                 return true;
5104         }
5105
5106         DBG_DEBUG("Processing sparsebundle [%s]\n", name);
5107
5108         ok = fruit_get_bandsize(handle, name, &bandsize);
5109         if (!ok) {
5110                 /*
5111                  * Beware of race conditions: this may be an uninitialized
5112                  * Info.plist that a client is just creating. We don't want let
5113                  * this to trigger complete failure.
5114                  */
5115                 DBG_ERR("Processing sparsebundle [%s] failed\n", name);
5116                 return true;
5117         }
5118
5119         ok = fruit_get_num_bands(handle, name, &nbands);
5120         if (!ok) {
5121                 /*
5122                  * Beware of race conditions: this may be a backup sparsebundle
5123                  * in an early stage lacking a bands subdirectory. We don't want
5124                  * let this to trigger complete failure.
5125                  */
5126                 DBG_ERR("Processing sparsebundle [%s] failed\n", name);
5127                 return true;
5128         }
5129
5130         /*
5131          * Arithmetic on 32-bit systems may cause overflow, depending on
5132          * size_t precision. First we check its unlikely, then we
5133          * force the precision into target off_t, then we check that
5134          * the total did not overflow either.
5135          */
5136         if (bandsize > SIZE_MAX/nbands) {
5137                 DBG_ERR("tmsize potential overflow: bandsize [%zu] nbands [%zu]\n",
5138                         bandsize, nbands);
5139                 return false;
5140         }
5141         tm_size = (off_t)bandsize * (off_t)nbands;
5142
5143         if (state->total_size + tm_size < state->total_size) {
5144                 DBG_ERR("tm total size overflow: bandsize [%zu] nbands [%zu]\n",
5145                         bandsize, nbands);
5146                 return false;
5147         }
5148
5149         state->total_size += tm_size;
5150
5151         DBG_DEBUG("[%s] tm_size [%jd] total_size [%jd]\n",
5152                   name, (intmax_t)tm_size, (intmax_t)state->total_size);
5153
5154         return true;
5155 }
5156
5157 /**
5158  * Calculate used size of a TimeMachine volume
5159  *
5160  * This assumes that the volume is used only for TimeMachine.
5161  *
5162  * - readdir(basedir of share), then
5163  * - for every element that matches regex "^\(.*\)\.sparsebundle$" :
5164  * - parse "\1.sparsebundle/Info.plist" and read the band-size XML key
5165  * - count band files in "\1.sparsebundle/bands/"
5166  * - calculate used size of all bands: band_count * band_size
5167  **/
5168 static uint64_t fruit_disk_free(vfs_handle_struct *handle,
5169                                 const struct smb_filename *smb_fname,
5170                                 uint64_t *_bsize,
5171                                 uint64_t *_dfree,
5172                                 uint64_t *_dsize)
5173 {
5174         struct fruit_config_data *config = NULL;
5175         struct fruit_disk_free_state state = {0};
5176         struct smb_Dir *dir_hnd = NULL;
5177         const char *dname = NULL;
5178         char *talloced = NULL;
5179         long offset = 0;
5180         uint64_t dfree;
5181         uint64_t dsize;
5182         bool ok;
5183
5184         SMB_VFS_HANDLE_GET_DATA(handle, config,
5185                                 struct fruit_config_data,
5186                                 return UINT64_MAX);
5187
5188         if (!config->time_machine ||
5189             config->time_machine_max_size == 0)
5190         {
5191                 return SMB_VFS_NEXT_DISK_FREE(handle,
5192                                               smb_fname,
5193                                               _bsize,
5194                                               _dfree,
5195                                               _dsize);
5196         }
5197
5198         dir_hnd = OpenDir(talloc_tos(), handle->conn, smb_fname, NULL, 0);
5199         if (dir_hnd == NULL) {
5200                 return UINT64_MAX;
5201         }
5202
5203         while ((dname = ReadDirName(dir_hnd, &offset, NULL, &talloced))
5204                != NULL)
5205         {
5206                 ok = fruit_tmsize_do_dirent(handle, &state, dname);
5207                 if (!ok) {
5208                         TALLOC_FREE(talloced);
5209                         TALLOC_FREE(dir_hnd);
5210                         return UINT64_MAX;
5211                 }
5212                 TALLOC_FREE(talloced);
5213         }
5214
5215         TALLOC_FREE(dir_hnd);
5216
5217         dsize = config->time_machine_max_size / 512;
5218         dfree = dsize - (state.total_size / 512);
5219         if (dfree > dsize) {
5220                 dfree = 0;
5221         }
5222
5223         *_bsize = 512;
5224         *_dsize = dsize;
5225         *_dfree = dfree;
5226         return dfree / 2;
5227 }
5228
5229 static uint64_t fruit_fs_file_id(struct vfs_handle_struct *handle,
5230                                  const SMB_STRUCT_STAT *psbuf)
5231 {
5232         struct fruit_config_data *config = NULL;
5233
5234         SMB_VFS_HANDLE_GET_DATA(handle, config,
5235                                 struct fruit_config_data,
5236                                 return 0);
5237
5238         if (global_fruit_config.nego_aapl &&
5239             config->aapl_zero_file_id)
5240         {
5241                 return 0;
5242         }
5243
5244         return SMB_VFS_NEXT_FS_FILE_ID(handle, psbuf);
5245 }
5246
5247 static struct vfs_fn_pointers vfs_fruit_fns = {
5248         .connect_fn = fruit_connect,
5249         .disk_free_fn = fruit_disk_free,
5250
5251         /* File operations */
5252         .fchmod_fn = fruit_fchmod,
5253         .unlinkat_fn = fruit_unlinkat,
5254         .renameat_fn = fruit_renameat,
5255         .openat_fn = fruit_openat,
5256         .close_fn = fruit_close,
5257         .pread_fn = fruit_pread,
5258         .pwrite_fn = fruit_pwrite,
5259         .pread_send_fn = fruit_pread_send,
5260         .pread_recv_fn = fruit_pread_recv,
5261         .pwrite_send_fn = fruit_pwrite_send,
5262         .pwrite_recv_fn = fruit_pwrite_recv,
5263         .stat_fn = fruit_stat,
5264         .lstat_fn = fruit_lstat,
5265         .fstat_fn = fruit_fstat,
5266         .fstreaminfo_fn = fruit_fstreaminfo,
5267         .fntimes_fn = fruit_fntimes,
5268         .ftruncate_fn = fruit_ftruncate,
5269         .fallocate_fn = fruit_fallocate,
5270         .create_file_fn = fruit_create_file,
5271         .freaddir_attr_fn = fruit_freaddir_attr,
5272         .offload_read_send_fn = fruit_offload_read_send,
5273         .offload_read_recv_fn = fruit_offload_read_recv,
5274         .offload_write_send_fn = fruit_offload_write_send,
5275         .offload_write_recv_fn = fruit_offload_write_recv,
5276         .fs_file_id_fn = fruit_fs_file_id,
5277
5278         /* NT ACL operations */
5279         .fget_nt_acl_fn = fruit_fget_nt_acl,
5280         .fset_nt_acl_fn = fruit_fset_nt_acl,
5281 };
5282
5283 static_decl_vfs;
5284 NTSTATUS vfs_fruit_init(TALLOC_CTX *ctx)
5285 {
5286         NTSTATUS ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "fruit",
5287                                         &vfs_fruit_fns);
5288         if (!NT_STATUS_IS_OK(ret)) {
5289                 return ret;
5290         }
5291
5292         vfs_fruit_debug_level = debug_add_class("fruit");
5293         if (vfs_fruit_debug_level == -1) {
5294                 vfs_fruit_debug_level = DBGC_VFS;
5295                 DEBUG(0, ("%s: Couldn't register custom debugging class!\n",
5296                           "vfs_fruit_init"));
5297         } else {
5298                 DEBUG(10, ("%s: Debug class number of '%s': %d\n",
5299                            "vfs_fruit_init","fruit",vfs_fruit_debug_level));
5300         }
5301
5302         return ret;
5303 }