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