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