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