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