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