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