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