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