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