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