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