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