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