30fab85ee987308f50bfe9d477b75f5b6cb3767b
[samba.git] / source3 / modules / vfs_fruit.c
1 /*
2  * OS X and Netatalk interoperability VFS module for Samba-3.x
3  *
4  * Copyright (C) Ralph Boehme, 2013, 2014
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include "includes.h"
21 #include "MacExtensions.h"
22 #include "smbd/smbd.h"
23 #include "system/filesys.h"
24 #include "lib/util/time.h"
25 #include "../lib/crypto/md5.h"
26 #include "system/shmem.h"
27 #include "locking/proto.h"
28 #include "smbd/globals.h"
29 #include "messages.h"
30 #include "libcli/security/security.h"
31 #include "../libcli/smb/smb2_create_ctx.h"
32 #include "lib/util/sys_rw.h"
33 #include "lib/util/tevent_ntstatus.h"
34 #include "lib/util/tevent_unix.h"
35 #include "offload_token.h"
36 #include "string_replace.h"
37
38 /*
39  * Enhanced OS X and Netatalk compatibility
40  * ========================================
41  *
42  * This modules takes advantage of vfs_streams_xattr and
43  * vfs_catia. VFS modules vfs_fruit and vfs_streams_xattr must be
44  * loaded in the correct order:
45  *
46  *   vfs modules = catia fruit streams_xattr
47  *
48  * The module intercepts the OS X special streams "AFP_AfpInfo" and
49  * "AFP_Resource" and handles them in a special way. All other named
50  * streams are deferred to vfs_streams_xattr.
51  *
52  * The OS X client maps all NTFS illegal characters to the Unicode
53  * private range. This module optionally stores the charcters using
54  * their native ASCII encoding using vfs_catia. If you're not enabling
55  * this feature, you can skip catia from vfs modules.
56  *
57  * Finally, open modes are optionally checked against Netatalk AFP
58  * share modes.
59  *
60  * The "AFP_AfpInfo" named stream is a binary blob containing OS X
61  * extended metadata for files and directories. This module optionally
62  * reads and stores this metadata in a way compatible with Netatalk 3
63  * which stores the metadata in an EA "org.netatalk.metadata". Cf
64  * source3/include/MacExtensions.h for a description of the binary
65  * blobs content.
66  *
67  * The "AFP_Resource" named stream may be arbitrarily large, thus it
68  * can't be stored in an xattr on most filesystem. ZFS on Solaris is
69  * the only available filesystem where xattrs can be of any size and
70  * the OS supports using the file APIs for xattrs.
71  *
72  * The AFP_Resource stream is stored in an AppleDouble file prepending
73  * "._" to the filename. On Solaris with ZFS the stream is optionally
74  * stored in an EA "org.netatalk.resource".
75  *
76  *
77  * Extended Attributes
78  * ===================
79  *
80  * The OS X SMB client sends xattrs as ADS too. For xattr interop with
81  * other protocols you may want to adjust the xattr names the VFS
82  * module vfs_streams_xattr uses for storing ADS's. This defaults to
83  * user.DosStream.ADS_NAME:$DATA and can be changed by specifying
84  * these module parameters:
85  *
86  *   streams_xattr:prefix = user.
87  *   streams_xattr:store_stream_type = false
88  *
89  *
90  * TODO
91  * ====
92  *
93  * - log diagnostic if any needed VFS module is not loaded
94  *   (eg with lp_vfs_objects())
95  * - add tests
96  */
97
98 static int vfs_fruit_debug_level = DBGC_VFS;
99
100 static struct global_fruit_config {
101         bool nego_aapl; /* client negotiated AAPL */
102
103 } global_fruit_config;
104
105 #undef DBGC_CLASS
106 #define DBGC_CLASS vfs_fruit_debug_level
107
108 #define FRUIT_PARAM_TYPE_NAME "fruit"
109 #define ADOUBLE_NAME_PREFIX "._"
110
111 #define NETATALK_META_XATTR "org.netatalk.Metadata"
112 #define NETATALK_RSRC_XATTR "org.netatalk.ResourceFork"
113
114 #if defined(HAVE_ATTROPEN)
115 #define AFPINFO_EA_NETATALK NETATALK_META_XATTR
116 #define AFPRESOURCE_EA_NETATALK NETATALK_RSRC_XATTR
117 #else
118 #define AFPINFO_EA_NETATALK "user." NETATALK_META_XATTR
119 #define AFPRESOURCE_EA_NETATALK "user." NETATALK_RSRC_XATTR
120 #endif
121
122 enum apple_fork {APPLE_FORK_DATA, APPLE_FORK_RSRC};
123
124 enum fruit_rsrc {FRUIT_RSRC_STREAM, FRUIT_RSRC_ADFILE, FRUIT_RSRC_XATTR};
125 enum fruit_meta {FRUIT_META_STREAM, FRUIT_META_NETATALK};
126 enum fruit_locking {FRUIT_LOCKING_NETATALK, FRUIT_LOCKING_NONE};
127 enum fruit_encoding {FRUIT_ENC_NATIVE, FRUIT_ENC_PRIVATE};
128
129 struct fruit_config_data {
130         enum fruit_rsrc rsrc;
131         enum fruit_meta meta;
132         enum fruit_locking locking;
133         enum fruit_encoding encoding;
134         bool use_aapl;          /* config from smb.conf */
135         bool use_copyfile;
136         bool readdir_attr_enabled;
137         bool unix_info_enabled;
138         bool copyfile_enabled;
139         bool veto_appledouble;
140         bool posix_rename;
141         bool aapl_zero_file_id;
142         const char *model;
143         bool time_machine;
144         off_t time_machine_max_size;
145
146         /*
147          * Additional options, all enabled by default,
148          * possibly useful for analyzing performance. The associated
149          * operations with each of them may be expensive, so having
150          * the chance to disable them individually gives a chance
151          * tweaking the setup for the particular usecase.
152          */
153         bool readdir_attr_rsize;
154         bool readdir_attr_finder_info;
155         bool readdir_attr_max_access;
156 };
157
158 static const struct enum_list fruit_rsrc[] = {
159         {FRUIT_RSRC_STREAM, "stream"}, /* pass on to vfs_streams_xattr */
160         {FRUIT_RSRC_ADFILE, "file"}, /* ._ AppleDouble file */
161         {FRUIT_RSRC_XATTR, "xattr"}, /* Netatalk compatible xattr (ZFS only) */
162         { -1, NULL}
163 };
164
165 static const struct enum_list fruit_meta[] = {
166         {FRUIT_META_STREAM, "stream"}, /* pass on to vfs_streams_xattr */
167         {FRUIT_META_NETATALK, "netatalk"}, /* Netatalk compatible xattr */
168         { -1, NULL}
169 };
170
171 static const struct enum_list fruit_locking[] = {
172         {FRUIT_LOCKING_NETATALK, "netatalk"}, /* synchronize locks with Netatalk */
173         {FRUIT_LOCKING_NONE, "none"},
174         { -1, NULL}
175 };
176
177 static const struct enum_list fruit_encoding[] = {
178         {FRUIT_ENC_NATIVE, "native"}, /* map unicode private chars to ASCII */
179         {FRUIT_ENC_PRIVATE, "private"}, /* keep unicode private chars */
180         { -1, NULL}
181 };
182
183 static const char *fruit_catia_maps =
184         "0x01:0xf001,0x02:0xf002,0x03:0xf003,0x04:0xf004,"
185         "0x05:0xf005,0x06:0xf006,0x07:0xf007,0x08:0xf008,"
186         "0x09:0xf009,0x0a:0xf00a,0x0b:0xf00b,0x0c:0xf00c,"
187         "0x0d:0xf00d,0x0e:0xf00e,0x0f:0xf00f,0x10:0xf010,"
188         "0x11:0xf011,0x12:0xf012,0x13:0xf013,0x14:0xf014,"
189         "0x15:0xf015,0x16:0xf016,0x17:0xf017,0x18:0xf018,"
190         "0x19:0xf019,0x1a:0xf01a,0x1b:0xf01b,0x1c:0xf01c,"
191         "0x1d:0xf01d,0x1e:0xf01e,0x1f:0xf01f,"
192         "0x22:0xf020,0x2a:0xf021,0x3a:0xf022,0x3c:0xf023,"
193         "0x3e:0xf024,0x3f:0xf025,0x5c:0xf026,0x7c:0xf027,"
194         "0x0d:0xf00d";
195
196 /*****************************************************************************
197  * Defines, functions and data structures that deal with AppleDouble
198  *****************************************************************************/
199
200 /*
201  * There are two AppleDouble blobs we deal with:
202  *
203  * - ADOUBLE_META - AppleDouble blob used by Netatalk for storing
204  *   metadata in an xattr
205  *
206  * - ADOUBLE_RSRC - AppleDouble blob used by OS X and Netatalk in
207  *   ._ files
208  */
209 typedef enum {ADOUBLE_META, ADOUBLE_RSRC} adouble_type_t;
210
211 /* Version info */
212 #define AD_VERSION2     0x00020000
213 #define AD_VERSION      AD_VERSION2
214
215 /*
216  * AppleDouble entry IDs.
217  */
218 #define ADEID_DFORK         1
219 #define ADEID_RFORK         2
220 #define ADEID_NAME          3
221 #define ADEID_COMMENT       4
222 #define ADEID_ICONBW        5
223 #define ADEID_ICONCOL       6
224 #define ADEID_FILEI         7
225 #define ADEID_FILEDATESI    8
226 #define ADEID_FINDERI       9
227 #define ADEID_MACFILEI      10
228 #define ADEID_PRODOSFILEI   11
229 #define ADEID_MSDOSFILEI    12
230 #define ADEID_SHORTNAME     13
231 #define ADEID_AFPFILEI      14
232 #define ADEID_DID           15
233
234 /* Private Netatalk entries */
235 #define ADEID_PRIVDEV       16
236 #define ADEID_PRIVINO       17
237 #define ADEID_PRIVSYN       18
238 #define ADEID_PRIVID        19
239 #define ADEID_MAX           (ADEID_PRIVID + 1)
240
241 /*
242  * These are the real ids for the private entries,
243  * as stored in the adouble file
244  */
245 #define AD_DEV              0x80444556
246 #define AD_INO              0x80494E4F
247 #define AD_SYN              0x8053594E
248 #define AD_ID               0x8053567E
249
250 /* Number of actually used entries */
251 #define ADEID_NUM_XATTR      8
252 #define ADEID_NUM_DOT_UND    2
253 #define ADEID_NUM_RSRC_XATTR 1
254
255 /* AppleDouble magic */
256 #define AD_APPLESINGLE_MAGIC 0x00051600
257 #define AD_APPLEDOUBLE_MAGIC 0x00051607
258 #define AD_MAGIC             AD_APPLEDOUBLE_MAGIC
259
260 /* Sizes of relevant entry bits */
261 #define ADEDLEN_MAGIC       4
262 #define ADEDLEN_VERSION     4
263 #define ADEDLEN_FILLER      16
264 #define AD_FILLER_TAG       "Netatalk        " /* should be 16 bytes */
265 #define AD_FILLER_TAG_OSX   "Mac OS X        " /* should be 16 bytes */
266 #define ADEDLEN_NENTRIES    2
267 #define AD_HEADER_LEN       (ADEDLEN_MAGIC + ADEDLEN_VERSION + \
268                              ADEDLEN_FILLER + ADEDLEN_NENTRIES) /* 26 */
269 #define AD_ENTRY_LEN_EID    4
270 #define AD_ENTRY_LEN_OFF    4
271 #define AD_ENTRY_LEN_LEN    4
272 #define AD_ENTRY_LEN (AD_ENTRY_LEN_EID + AD_ENTRY_LEN_OFF + AD_ENTRY_LEN_LEN)
273
274 /* Field widths */
275 #define ADEDLEN_NAME            255
276 #define ADEDLEN_COMMENT         200
277 #define ADEDLEN_FILEI           16
278 #define ADEDLEN_FINDERI         32
279 #define ADEDLEN_FILEDATESI      16
280 #define ADEDLEN_SHORTNAME       12 /* length up to 8.3 */
281 #define ADEDLEN_AFPFILEI        4
282 #define ADEDLEN_MACFILEI        4
283 #define ADEDLEN_PRODOSFILEI     8
284 #define ADEDLEN_MSDOSFILEI      2
285 #define ADEDLEN_DID             4
286 #define ADEDLEN_PRIVDEV         8
287 #define ADEDLEN_PRIVINO         8
288 #define ADEDLEN_PRIVSYN         8
289 #define ADEDLEN_PRIVID          4
290
291 /* Offsets */
292 #define ADEDOFF_MAGIC         0
293 #define ADEDOFF_VERSION       (ADEDOFF_MAGIC + ADEDLEN_MAGIC)
294 #define ADEDOFF_FILLER        (ADEDOFF_VERSION + ADEDLEN_VERSION)
295 #define ADEDOFF_NENTRIES      (ADEDOFF_FILLER + ADEDLEN_FILLER)
296
297 #define ADEDOFF_FINDERI_XATTR    (AD_HEADER_LEN + \
298                                   (ADEID_NUM_XATTR * AD_ENTRY_LEN))
299 #define ADEDOFF_COMMENT_XATTR    (ADEDOFF_FINDERI_XATTR    + ADEDLEN_FINDERI)
300 #define ADEDOFF_FILEDATESI_XATTR (ADEDOFF_COMMENT_XATTR    + ADEDLEN_COMMENT)
301 #define ADEDOFF_AFPFILEI_XATTR   (ADEDOFF_FILEDATESI_XATTR + \
302                                   ADEDLEN_FILEDATESI)
303 #define ADEDOFF_PRIVDEV_XATTR    (ADEDOFF_AFPFILEI_XATTR   + ADEDLEN_AFPFILEI)
304 #define ADEDOFF_PRIVINO_XATTR    (ADEDOFF_PRIVDEV_XATTR    + ADEDLEN_PRIVDEV)
305 #define ADEDOFF_PRIVSYN_XATTR    (ADEDOFF_PRIVINO_XATTR    + ADEDLEN_PRIVINO)
306 #define ADEDOFF_PRIVID_XATTR     (ADEDOFF_PRIVSYN_XATTR    + ADEDLEN_PRIVSYN)
307
308 #define ADEDOFF_FINDERI_DOT_UND  (AD_HEADER_LEN + \
309                                   (ADEID_NUM_DOT_UND * AD_ENTRY_LEN))
310 #define ADEDOFF_RFORK_DOT_UND    (ADEDOFF_FINDERI_DOT_UND + ADEDLEN_FINDERI)
311
312 #define AD_DATASZ_XATTR (AD_HEADER_LEN + \
313                          (ADEID_NUM_XATTR * AD_ENTRY_LEN) + \
314                          ADEDLEN_FINDERI + ADEDLEN_COMMENT + \
315                          ADEDLEN_FILEDATESI + ADEDLEN_AFPFILEI + \
316                          ADEDLEN_PRIVDEV + ADEDLEN_PRIVINO + \
317                          ADEDLEN_PRIVSYN + ADEDLEN_PRIVID)
318
319 #if AD_DATASZ_XATTR != 402
320 #error bad size for AD_DATASZ_XATTR
321 #endif
322
323 #define AD_DATASZ_DOT_UND (AD_HEADER_LEN + \
324                            (ADEID_NUM_DOT_UND * AD_ENTRY_LEN) + \
325                            ADEDLEN_FINDERI)
326 #if AD_DATASZ_DOT_UND != 82
327 #error bad size for AD_DATASZ_DOT_UND
328 #endif
329
330 /*
331  * Sharemode locks fcntl() offsets
332  */
333 #if _FILE_OFFSET_BITS == 64 || defined(HAVE_LARGEFILE)
334 #define AD_FILELOCK_BASE (UINT64_C(0x7FFFFFFFFFFFFFFF) - 9)
335 #else
336 #define AD_FILELOCK_BASE (UINT32_C(0x7FFFFFFF) - 9)
337 #endif
338 #define BYTELOCK_MAX (AD_FILELOCK_BASE - 1)
339
340 #define AD_FILELOCK_OPEN_WR        (AD_FILELOCK_BASE + 0)
341 #define AD_FILELOCK_OPEN_RD        (AD_FILELOCK_BASE + 1)
342 #define AD_FILELOCK_RSRC_OPEN_WR   (AD_FILELOCK_BASE + 2)
343 #define AD_FILELOCK_RSRC_OPEN_RD   (AD_FILELOCK_BASE + 3)
344 #define AD_FILELOCK_DENY_WR        (AD_FILELOCK_BASE + 4)
345 #define AD_FILELOCK_DENY_RD        (AD_FILELOCK_BASE + 5)
346 #define AD_FILELOCK_RSRC_DENY_WR   (AD_FILELOCK_BASE + 6)
347 #define AD_FILELOCK_RSRC_DENY_RD   (AD_FILELOCK_BASE + 7)
348 #define AD_FILELOCK_OPEN_NONE      (AD_FILELOCK_BASE + 8)
349 #define AD_FILELOCK_RSRC_OPEN_NONE (AD_FILELOCK_BASE + 9)
350
351 /* Time stuff we overload the bits a little */
352 #define AD_DATE_CREATE         0
353 #define AD_DATE_MODIFY         4
354 #define AD_DATE_BACKUP         8
355 #define AD_DATE_ACCESS        12
356 #define AD_DATE_MASK          (AD_DATE_CREATE | AD_DATE_MODIFY | \
357                                AD_DATE_BACKUP | AD_DATE_ACCESS)
358 #define AD_DATE_UNIX          (1 << 10)
359 #define AD_DATE_START         0x80000000
360 #define AD_DATE_DELTA         946684800
361 #define AD_DATE_FROM_UNIX(x)  (htonl((x) - AD_DATE_DELTA))
362 #define AD_DATE_TO_UNIX(x)    (ntohl(x) + AD_DATE_DELTA)
363
364 #define AD_XATTR_HDR_MAGIC    0x41545452 /* 'ATTR' */
365 #define AD_XATTR_MAX_ENTRIES  1024 /* Some arbitrarily enforced limit */
366 #define AD_XATTR_HDR_SIZE     36
367 #define AD_XATTR_MAX_HDR_SIZE 65536
368
369 /* Accessor macros */
370 #define ad_getentrylen(ad,eid)     ((ad)->ad_eid[(eid)].ade_len)
371 #define ad_getentryoff(ad,eid)     ((ad)->ad_eid[(eid)].ade_off)
372 #define ad_setentrylen(ad,eid,len) ((ad)->ad_eid[(eid)].ade_len = (len))
373 #define ad_setentryoff(ad,eid,off) ((ad)->ad_eid[(eid)].ade_off = (off))
374
375 /*
376  * Both struct ad_xattr_header and struct ad_xattr_entry describe the in memory
377  * representation as well as the on-disk format.
378  *
379  * The ad_xattr_header follows the FinderInfo data in the FinderInfo entry if
380  * the length of the FinderInfo entry is larger then 32 bytes. It is then
381  * preceeded with 2 bytes padding.
382  *
383  * Cf: https://opensource.apple.com/source/xnu/xnu-4570.1.46/bsd/vfs/vfs_xattr.c
384  */
385
386 struct ad_xattr_header {
387         uint32_t adx_magic;        /* ATTR_HDR_MAGIC */
388         uint32_t adx_debug_tag;    /* for debugging == file id of owning file */
389         uint32_t adx_total_size;   /* file offset of end of attribute header + entries + data */
390         uint32_t adx_data_start;   /* file offset to attribute data area */
391         uint32_t adx_data_length;  /* length of attribute data area */
392         uint32_t adx_reserved[3];
393         uint16_t adx_flags;
394         uint16_t adx_num_attrs;
395 };
396
397 /* On-disk entries are aligned on 4 byte boundaries */
398 struct ad_xattr_entry {
399         uint32_t adx_offset;    /* file offset to data */
400         uint32_t adx_length;    /* size of attribute data */
401         uint16_t adx_flags;
402         uint8_t  adx_namelen;   /* included the NULL terminator */
403         char    *adx_name;      /* NULL-terminated UTF-8 name */
404 };
405
406 struct ad_entry {
407         size_t ade_off;
408         size_t ade_len;
409 };
410
411 struct adouble {
412         vfs_handle_struct        *ad_handle;
413         int                       ad_fd;
414         bool                      ad_opened;
415         adouble_type_t            ad_type;
416         uint32_t                  ad_magic;
417         uint32_t                  ad_version;
418         uint8_t                   ad_filler[ADEDLEN_FILLER];
419         struct ad_entry           ad_eid[ADEID_MAX];
420         char                     *ad_data;
421         struct ad_xattr_header    adx_header;
422         struct ad_xattr_entry    *adx_entries;
423 };
424
425 struct ad_entry_order {
426         uint32_t id, offset, len;
427 };
428
429 /* Netatalk AppleDouble metadata xattr */
430 static const
431 struct ad_entry_order entry_order_meta_xattr[ADEID_NUM_XATTR + 1] = {
432         {ADEID_FINDERI,    ADEDOFF_FINDERI_XATTR,    ADEDLEN_FINDERI},
433         {ADEID_COMMENT,    ADEDOFF_COMMENT_XATTR,    0},
434         {ADEID_FILEDATESI, ADEDOFF_FILEDATESI_XATTR, ADEDLEN_FILEDATESI},
435         {ADEID_AFPFILEI,   ADEDOFF_AFPFILEI_XATTR,   ADEDLEN_AFPFILEI},
436         {ADEID_PRIVDEV,    ADEDOFF_PRIVDEV_XATTR,    0},
437         {ADEID_PRIVINO,    ADEDOFF_PRIVINO_XATTR,    0},
438         {ADEID_PRIVSYN,    ADEDOFF_PRIVSYN_XATTR,    0},
439         {ADEID_PRIVID,     ADEDOFF_PRIVID_XATTR,     0},
440         {0, 0, 0}
441 };
442
443 /* AppleDouble resource fork file (the ones prefixed by "._") */
444 static const
445 struct ad_entry_order entry_order_dot_und[ADEID_NUM_DOT_UND + 1] = {
446         {ADEID_FINDERI,    ADEDOFF_FINDERI_DOT_UND,  ADEDLEN_FINDERI},
447         {ADEID_RFORK,      ADEDOFF_RFORK_DOT_UND,    0},
448         {0, 0, 0}
449 };
450
451 /*
452  * Fake AppleDouble entry oder for resource fork xattr.  The xattr
453  * isn't an AppleDouble file, it simply contains the resource data,
454  * but in order to be able to use some API calls like ad_getentryoff()
455  * we build a fake/helper struct adouble with this entry order struct.
456  */
457 static const
458 struct ad_entry_order entry_order_rsrc_xattr[ADEID_NUM_RSRC_XATTR + 1] = {
459         {ADEID_RFORK, 0, 0},
460         {0, 0, 0}
461 };
462
463 /* Conversion from enumerated id to on-disk AppleDouble id */
464 #define AD_EID_DISK(a) (set_eid[a])
465 static const uint32_t set_eid[] = {
466         0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
467         AD_DEV, AD_INO, AD_SYN, AD_ID
468 };
469
470 struct fio {
471         /* tcon config handle */
472         struct fruit_config_data *config;
473
474         /* Denote stream type, meta or rsrc */
475         adouble_type_t type;
476 };
477
478 /*
479  * Forward declarations
480  */
481 static struct adouble *ad_init(TALLOC_CTX *ctx, vfs_handle_struct *handle,
482                                adouble_type_t type);
483 static int ad_set(struct adouble *ad, const struct smb_filename *smb_fname);
484 static int ad_fset(struct adouble *ad, files_struct *fsp);
485 static int adouble_path(TALLOC_CTX *ctx,
486                         const struct smb_filename *smb_fname__in,
487                         struct smb_filename **ppsmb_fname_out);
488 static AfpInfo *afpinfo_new(TALLOC_CTX *ctx);
489 static ssize_t afpinfo_pack(const AfpInfo *ai, char *buf);
490 static AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data);
491
492
493 /**
494  * Return a pointer to an AppleDouble entry
495  *
496  * Returns NULL if the entry is not present
497  **/
498 static char *ad_get_entry(const struct adouble *ad, int eid)
499 {
500         off_t off = ad_getentryoff(ad, eid);
501         size_t len = ad_getentrylen(ad, eid);
502
503         if (off == 0 || len == 0) {
504                 return NULL;
505         }
506
507         return ad->ad_data + off;
508 }
509
510 /**
511  * Get a date
512  **/
513 static int ad_getdate(const struct adouble *ad,
514                       unsigned int dateoff,
515                       uint32_t *date)
516 {
517         bool xlate = (dateoff & AD_DATE_UNIX);
518         char *p = NULL;
519
520         dateoff &= AD_DATE_MASK;
521         p = ad_get_entry(ad, ADEID_FILEDATESI);
522         if (p == NULL) {
523                 return -1;
524         }
525
526         if (dateoff > AD_DATE_ACCESS) {
527             return -1;
528         }
529
530         memcpy(date, p + dateoff, sizeof(uint32_t));
531
532         if (xlate) {
533                 *date = AD_DATE_TO_UNIX(*date);
534         }
535         return 0;
536 }
537
538 /**
539  * Set a date
540  **/
541 static int ad_setdate(struct adouble *ad, unsigned int dateoff, uint32_t date)
542 {
543         bool xlate = (dateoff & AD_DATE_UNIX);
544         char *p = NULL;
545
546         p = ad_get_entry(ad, ADEID_FILEDATESI);
547         if (p == NULL) {
548                 return -1;
549         }
550
551         dateoff &= AD_DATE_MASK;
552         if (xlate) {
553                 date = AD_DATE_FROM_UNIX(date);
554         }
555
556         if (dateoff > AD_DATE_ACCESS) {
557                 return -1;
558         }
559
560         memcpy(p + dateoff, &date, sizeof(date));
561
562         return 0;
563 }
564
565
566 /**
567  * Map on-disk AppleDouble id to enumerated id
568  **/
569 static uint32_t get_eid(uint32_t eid)
570 {
571         if (eid <= 15) {
572                 return eid;
573         }
574
575         switch (eid) {
576         case AD_DEV:
577                 return ADEID_PRIVDEV;
578         case AD_INO:
579                 return ADEID_PRIVINO;
580         case AD_SYN:
581                 return ADEID_PRIVSYN;
582         case AD_ID:
583                 return ADEID_PRIVID;
584         default:
585                 break;
586         }
587
588         return 0;
589 }
590
591 /**
592  * Pack AppleDouble structure into data buffer
593  **/
594 static bool ad_pack(struct adouble *ad)
595 {
596         uint32_t       eid;
597         uint16_t       nent;
598         uint32_t       bufsize;
599         uint32_t       offset = 0;
600
601         bufsize = talloc_get_size(ad->ad_data);
602         if (bufsize < AD_DATASZ_DOT_UND) {
603                 DBG_ERR("bad buffer size [0x%" PRIx32 "]\n", bufsize);
604                 return false;
605         }
606
607         if (offset + ADEDLEN_MAGIC < offset ||
608                         offset + ADEDLEN_MAGIC >= bufsize) {
609                 return false;
610         }
611         RSIVAL(ad->ad_data, offset, ad->ad_magic);
612         offset += ADEDLEN_MAGIC;
613
614         if (offset + ADEDLEN_VERSION < offset ||
615                         offset + ADEDLEN_VERSION >= bufsize) {
616                 return false;
617         }
618         RSIVAL(ad->ad_data, offset, ad->ad_version);
619         offset += ADEDLEN_VERSION;
620
621         if (offset + ADEDLEN_FILLER < offset ||
622                         offset + ADEDLEN_FILLER >= bufsize) {
623                 return false;
624         }
625         if (ad->ad_type == ADOUBLE_RSRC) {
626                 memcpy(ad->ad_data + offset, AD_FILLER_TAG, ADEDLEN_FILLER);
627         }
628         offset += ADEDLEN_FILLER;
629
630         if (offset + ADEDLEN_NENTRIES < offset ||
631                         offset + ADEDLEN_NENTRIES >= bufsize) {
632                 return false;
633         }
634         offset += ADEDLEN_NENTRIES;
635
636         for (eid = 0, nent = 0; eid < ADEID_MAX; eid++) {
637                 if (ad->ad_eid[eid].ade_off == 0) {
638                         /*
639                          * ade_off is also used as indicator whether a
640                          * specific entry is used or not
641                          */
642                         continue;
643                 }
644
645                 if (offset + AD_ENTRY_LEN_EID < offset ||
646                                 offset + AD_ENTRY_LEN_EID >= bufsize) {
647                         return false;
648                 }
649                 RSIVAL(ad->ad_data, offset, AD_EID_DISK(eid));
650                 offset += AD_ENTRY_LEN_EID;
651
652                 if (offset + AD_ENTRY_LEN_OFF < offset ||
653                                 offset + AD_ENTRY_LEN_OFF >= bufsize) {
654                         return false;
655                 }
656                 RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_off);
657                 offset += AD_ENTRY_LEN_OFF;
658
659                 if (offset + AD_ENTRY_LEN_LEN < offset ||
660                                 offset + AD_ENTRY_LEN_LEN >= bufsize) {
661                         return false;
662                 }
663                 RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_len);
664                 offset += AD_ENTRY_LEN_LEN;
665
666                 nent++;
667         }
668
669         if (ADEDOFF_NENTRIES + 2 >= bufsize) {
670                 return false;
671         }
672         RSSVAL(ad->ad_data, ADEDOFF_NENTRIES, nent);
673
674         return true;
675 }
676
677 static bool ad_unpack_xattrs(struct adouble *ad)
678 {
679         struct ad_xattr_header *h = &ad->adx_header;
680         const char *p = ad->ad_data;
681         uint32_t hoff;
682         uint32_t i;
683
684         if (ad_getentrylen(ad, ADEID_FINDERI) <= ADEDLEN_FINDERI) {
685                 return true;
686         }
687
688         /* 2 bytes padding */
689         hoff = ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI + 2;
690
691         h->adx_magic       = RIVAL(p, hoff + 0);
692         h->adx_debug_tag   = RIVAL(p, hoff + 4); /* Not used -> not checked */
693         h->adx_total_size  = RIVAL(p, hoff + 8);
694         h->adx_data_start  = RIVAL(p, hoff + 12);
695         h->adx_data_length = RIVAL(p, hoff + 16);
696         h->adx_flags       = RSVAL(p, hoff + 32); /* Not used -> not checked */
697         h->adx_num_attrs   = RSVAL(p, hoff + 34);
698
699         if (h->adx_magic != AD_XATTR_HDR_MAGIC) {
700                 DBG_ERR("Bad magic: 0x%" PRIx32 "\n", h->adx_magic);
701                 return false;
702         }
703
704         if (h->adx_total_size > ad_getentryoff(ad, ADEID_RFORK)) {
705                 DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size);
706                 return false;
707         }
708         if (h->adx_total_size > AD_XATTR_MAX_HDR_SIZE) {
709                 DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size);
710                 return false;
711         }
712
713         if (h->adx_data_start < (hoff + AD_XATTR_HDR_SIZE)) {
714                 DBG_ERR("Bad start: 0x%" PRIx32 "\n", h->adx_data_start);
715                 return false;
716         }
717
718         if ((h->adx_data_start + h->adx_data_length) < h->adx_data_start) {
719                 DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length);
720                 return false;
721         }
722         if ((h->adx_data_start + h->adx_data_length) >
723             ad->adx_header.adx_total_size)
724         {
725                 DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length);
726                 return false;
727         }
728
729         if (h->adx_num_attrs > AD_XATTR_MAX_ENTRIES) {
730                 DBG_ERR("Bad num xattrs: %" PRIu16 "\n", h->adx_num_attrs);
731                 return false;
732         }
733
734         if (h->adx_num_attrs == 0) {
735                 return true;
736         }
737
738         ad->adx_entries = talloc_zero_array(
739                 ad, struct ad_xattr_entry, h->adx_num_attrs);
740         if (ad->adx_entries == NULL) {
741                 return false;
742         }
743
744         hoff += AD_XATTR_HDR_SIZE;
745
746         for (i = 0; i < h->adx_num_attrs; i++) {
747                 struct ad_xattr_entry *e = &ad->adx_entries[i];
748
749                 hoff = (hoff + 3) & ~3;
750
751                 e->adx_offset  = RIVAL(p, hoff + 0);
752                 e->adx_length  = RIVAL(p, hoff + 4);
753                 e->adx_flags   = RSVAL(p, hoff + 8);
754                 e->adx_namelen = *(p + hoff + 10);
755
756                 if (e->adx_offset >= ad->adx_header.adx_total_size) {
757                         DBG_ERR("Bad adx_offset: %" PRIx32 "\n",
758                                 e->adx_offset);
759                         return false;
760                 }
761
762                 if ((e->adx_offset + e->adx_length) < e->adx_offset) {
763                         DBG_ERR("Bad adx_length: %" PRIx32 "\n",
764                                 e->adx_length);
765                         return false;
766                 }
767
768                 if ((e->adx_offset + e->adx_length) >
769                     ad->adx_header.adx_total_size)
770                 {
771                         DBG_ERR("Bad adx_length: %" PRIx32 "\n",
772                                 e->adx_length);
773                         return false;
774                 }
775
776                 if (e->adx_namelen == 0) {
777                         DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
778                                 e->adx_namelen);
779                         return false;
780                 }
781                 if ((hoff + 11 + e->adx_namelen) < hoff + 11) {
782                         DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
783                                 e->adx_namelen);
784                         return false;
785                 }
786                 if ((hoff + 11 + e->adx_namelen) >
787                     ad->adx_header.adx_data_start)
788                 {
789                         DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
790                                 e->adx_namelen);
791                         return false;
792                 }
793
794                 e->adx_name = talloc_strndup(ad->adx_entries,
795                                              p + hoff + 11,
796                                              e->adx_namelen);
797                 if (e->adx_name == NULL) {
798                         return false;
799                 }
800
801                 DBG_DEBUG("xattr [%s] offset [0x%x] size [0x%x]\n",
802                           e->adx_name, e->adx_offset, e->adx_length);
803                 dump_data(10, (uint8_t *)(ad->ad_data + e->adx_offset),
804                           e->adx_length);
805
806                 hoff += 11 + e->adx_namelen;
807         }
808
809         return true;
810 }
811
812 /**
813  * Unpack an AppleDouble blob into a struct adoble
814  **/
815 static bool ad_unpack(struct adouble *ad, const size_t nentries,
816                       size_t filesize)
817 {
818         size_t bufsize = talloc_get_size(ad->ad_data);
819         size_t adentries, i;
820         uint32_t eid, len, off;
821         bool ok;
822
823         /*
824          * The size of the buffer ad->ad_data is checked when read, so
825          * we wouldn't have to check our own offsets, a few extra
826          * checks won't hurt though. We have to check the offsets we
827          * read from the buffer anyway.
828          */
829
830         if (bufsize < (AD_HEADER_LEN + (AD_ENTRY_LEN * nentries))) {
831                 DEBUG(1, ("bad size\n"));
832                 return false;
833         }
834
835         ad->ad_magic = RIVAL(ad->ad_data, 0);
836         ad->ad_version = RIVAL(ad->ad_data, ADEDOFF_VERSION);
837         if ((ad->ad_magic != AD_MAGIC) || (ad->ad_version != AD_VERSION)) {
838                 DEBUG(1, ("wrong magic or version\n"));
839                 return false;
840         }
841
842         memcpy(ad->ad_filler, ad->ad_data + ADEDOFF_FILLER, ADEDLEN_FILLER);
843
844         adentries = RSVAL(ad->ad_data, ADEDOFF_NENTRIES);
845         if (adentries != nentries) {
846                 DEBUG(1, ("invalid number of entries: %zu\n",
847                           adentries));
848                 return false;
849         }
850
851         /* now, read in the entry bits */
852         for (i = 0; i < adentries; i++) {
853                 eid = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN));
854                 eid = get_eid(eid);
855                 off = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 4);
856                 len = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 8);
857
858                 if (!eid || eid >= ADEID_MAX) {
859                         DEBUG(1, ("bogus eid %d\n", eid));
860                         return false;
861                 }
862
863                 /*
864                  * All entries other than the resource fork are
865                  * expected to be read into the ad_data buffer, so
866                  * ensure the specified offset is within that bound
867                  */
868                 if ((off > bufsize) && (eid != ADEID_RFORK)) {
869                         DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
870                                   eid, off, len));
871                         return false;
872                 }
873
874                 /*
875                  * All entries besides FinderInfo and resource fork
876                  * must fit into the buffer. FinderInfo is special as
877                  * it may be larger then the default 32 bytes (if it
878                  * contains marshalled xattrs), but we will fixup that
879                  * in ad_convert(). And the resource fork is never
880                  * accessed directly by the ad_data buf (also see
881                  * comment above) anyway.
882                  */
883                 if ((eid != ADEID_RFORK) &&
884                     (eid != ADEID_FINDERI) &&
885                     ((off + len) > bufsize)) {
886                         DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
887                                   eid, off, len));
888                         return false;
889                 }
890
891                 /*
892                  * That would be obviously broken
893                  */
894                 if (off > filesize) {
895                         DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
896                                   eid, off, len));
897                         return false;
898                 }
899
900                 /*
901                  * Check for any entry that has its end beyond the
902                  * filesize.
903                  */
904                 if (off + len < off) {
905                         DEBUG(1, ("offset wrap in eid %d: off: %" PRIu32
906                                   ", len: %" PRIu32 "\n",
907                                   eid, off, len));
908                         return false;
909
910                 }
911                 if (off + len > filesize) {
912                         /*
913                          * If this is the resource fork entry, we fix
914                          * up the length, for any other entry we bail
915                          * out.
916                          */
917                         if (eid != ADEID_RFORK) {
918                                 DEBUG(1, ("bogus eid %d: off: %" PRIu32
919                                           ", len: %" PRIu32 "\n",
920                                           eid, off, len));
921                                 return false;
922                         }
923
924                         /*
925                          * Fixup the resource fork entry by limiting
926                          * the size to entryoffset - filesize.
927                          */
928                         len = filesize - off;
929                         DEBUG(1, ("Limiting ADEID_RFORK: off: %" PRIu32
930                                   ", len: %" PRIu32 "\n", off, len));
931                 }
932
933                 ad->ad_eid[eid].ade_off = off;
934                 ad->ad_eid[eid].ade_len = len;
935         }
936
937         ok = ad_unpack_xattrs(ad);
938         if (!ok) {
939                 return false;
940         }
941
942         return true;
943 }
944
945 static bool ad_convert_move_reso(struct adouble *ad,
946                                  const struct smb_filename *smb_fname)
947 {
948         char *map = MAP_FAILED;
949         size_t maplen;
950         ssize_t len;
951         int rc;
952         bool ok;
953
954         if (ad_getentrylen(ad, ADEID_RFORK) == 0) {
955                 return true;
956         }
957
958         maplen = ad_getentryoff(ad, ADEID_RFORK) +
959                 ad_getentrylen(ad, ADEID_RFORK);
960
961         /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
962         map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
963                    ad->ad_fd, 0);
964         if (map == MAP_FAILED) {
965                 DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
966                 return false;
967         }
968
969
970         memmove(map + ADEDOFF_RFORK_DOT_UND,
971                 map + ad_getentryoff(ad, ADEID_RFORK),
972                 ad_getentrylen(ad, ADEID_RFORK));
973
974         rc = munmap(map, maplen);
975         if (rc != 0) {
976                 DBG_ERR("munmap failed: %s\n", strerror(errno));
977                 return false;
978         }
979
980         ad_setentryoff(ad, ADEID_RFORK, ADEDOFF_RFORK_DOT_UND);
981
982         ok = ad_pack(ad);
983         if (!ok) {
984                 DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name);
985                 return false;
986         }
987
988         len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
989         if (len != AD_DATASZ_DOT_UND) {
990                 DBG_ERR("%s: bad size: %zd\n", smb_fname->base_name, len);
991                 return false;
992         }
993
994         return true;
995 }
996
997 static bool ad_convert_xattr(struct adouble *ad,
998                              const struct smb_filename *smb_fname,
999                              bool *converted_xattr)
1000 {
1001         static struct char_mappings **string_replace_cmaps = NULL;
1002         char *map = MAP_FAILED;
1003         size_t maplen;
1004         uint16_t i;
1005         ssize_t len;
1006         int saved_errno = 0;
1007         NTSTATUS status;
1008         int rc;
1009         bool ok;
1010
1011         *converted_xattr = false;
1012
1013         if (ad_getentrylen(ad, ADEID_FINDERI) == ADEDLEN_FINDERI) {
1014                 return true;
1015         }
1016
1017         if (ad->adx_header.adx_num_attrs == 0) {
1018                 return true;
1019         }
1020
1021         if (string_replace_cmaps == NULL) {
1022                 const char **mappings = NULL;
1023
1024                 mappings = str_list_make_v3_const(
1025                         talloc_tos(), fruit_catia_maps, NULL);
1026                 if (mappings == NULL) {
1027                         return false;
1028                 }
1029                 string_replace_cmaps = string_replace_init_map(mappings);
1030                 TALLOC_FREE(mappings);
1031         }
1032
1033         maplen = ad_getentryoff(ad, ADEID_RFORK) +
1034                 ad_getentrylen(ad, ADEID_RFORK);
1035
1036         /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
1037         map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
1038                    ad->ad_fd, 0);
1039         if (map == MAP_FAILED) {
1040                 DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
1041                 return false;
1042         }
1043
1044         for (i = 0; i < ad->adx_header.adx_num_attrs; i++) {
1045                 struct ad_xattr_entry *e = &ad->adx_entries[i];
1046                 char *mapped_name = NULL;
1047                 char *tmp = NULL;
1048                 struct smb_filename *stream_name = NULL;
1049                 files_struct *fsp = NULL;
1050                 ssize_t nwritten;
1051
1052                 status = string_replace_allocate(ad->ad_handle->conn,
1053                                                  e->adx_name,
1054                                                  string_replace_cmaps,
1055                                                  talloc_tos(),
1056                                                  &mapped_name,
1057                                                  vfs_translate_to_windows);
1058                 if (!NT_STATUS_IS_OK(status) &&
1059                     !NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))
1060                 {
1061                         DBG_ERR("string_replace_allocate failed\n");
1062                         ok = false;
1063                         goto fail;
1064                 }
1065
1066                 tmp = mapped_name;
1067                 mapped_name = talloc_asprintf(talloc_tos(), ":%s", tmp);
1068                 TALLOC_FREE(tmp);
1069                 if (mapped_name == NULL) {
1070                         ok = false;
1071                         goto fail;
1072                 }
1073
1074                 stream_name = synthetic_smb_fname(talloc_tos(),
1075                                                   smb_fname->base_name,
1076                                                   mapped_name,
1077                                                   NULL,
1078                                                   smb_fname->flags);
1079                 TALLOC_FREE(mapped_name);
1080                 if (stream_name == NULL) {
1081                         DBG_ERR("synthetic_smb_fname failed\n");
1082                         ok = false;
1083                         goto fail;
1084                 }
1085
1086                 DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1087
1088                 status = SMB_VFS_CREATE_FILE(
1089                         ad->ad_handle->conn,            /* conn */
1090                         NULL,                           /* req */
1091                         0,                              /* root_dir_fid */
1092                         stream_name,                    /* fname */
1093                         FILE_GENERIC_WRITE,             /* access_mask */
1094                         FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
1095                         FILE_OPEN_IF,                   /* create_disposition */
1096                         0,                              /* create_options */
1097                         0,                              /* file_attributes */
1098                         INTERNAL_OPEN_ONLY,             /* oplock_request */
1099                         NULL,                           /* lease */
1100                         0,                              /* allocation_size */
1101                         0,                              /* private_flags */
1102                         NULL,                           /* sd */
1103                         NULL,                           /* ea_list */
1104                         &fsp,                           /* result */
1105                         NULL,                           /* psbuf */
1106                         NULL, NULL);                    /* create context */
1107                 TALLOC_FREE(stream_name);
1108                 if (!NT_STATUS_IS_OK(status)) {
1109                         DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1110                         ok = false;
1111                         goto fail;
1112                 }
1113
1114                 nwritten = SMB_VFS_PWRITE(fsp,
1115                                           map + e->adx_offset,
1116                                           e->adx_length,
1117                                           0);
1118                 if (nwritten == -1) {
1119                         DBG_ERR("SMB_VFS_PWRITE failed\n");
1120                         saved_errno = errno;
1121                         close_file(NULL, fsp, ERROR_CLOSE);
1122                         errno = saved_errno;
1123                         ok = false;
1124                         goto fail;
1125                 }
1126
1127                 status = close_file(NULL, fsp, NORMAL_CLOSE);
1128                 if (!NT_STATUS_IS_OK(status)) {
1129                         ok = false;
1130                         goto fail;
1131                 }
1132                 fsp = NULL;
1133         }
1134
1135         ad_setentrylen(ad, ADEID_FINDERI, ADEDLEN_FINDERI);
1136
1137         ok = ad_pack(ad);
1138         if (!ok) {
1139                 DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name);
1140                 goto fail;
1141         }
1142
1143         len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
1144         if (len != AD_DATASZ_DOT_UND) {
1145                 DBG_ERR("%s: bad size: %zd\n", smb_fname->base_name, len);
1146                 ok = false;
1147                 goto fail;
1148         }
1149
1150         ok = ad_convert_move_reso(ad, smb_fname);
1151         if (!ok) {
1152                 goto fail;
1153         }
1154
1155         *converted_xattr = true;
1156         ok = true;
1157
1158 fail:
1159         rc = munmap(map, maplen);
1160         if (rc != 0) {
1161                 DBG_ERR("munmap failed: %s\n", strerror(errno));
1162                 return false;
1163         }
1164
1165         return ok;
1166 }
1167
1168 static bool ad_convert_finderinfo(struct adouble *ad,
1169                                   const struct smb_filename *smb_fname)
1170 {
1171         char *p_ad = NULL;
1172         AfpInfo *ai = NULL;
1173         DATA_BLOB aiblob;
1174         struct smb_filename *stream_name = NULL;
1175         files_struct *fsp = NULL;
1176         size_t size;
1177         ssize_t nwritten;
1178         NTSTATUS status;
1179         int saved_errno = 0;
1180         int cmp;
1181
1182         cmp = memcmp(ad->ad_filler, AD_FILLER_TAG_OSX, ADEDLEN_FILLER);
1183         if (cmp != 0) {
1184                 return true;
1185         }
1186
1187         p_ad = ad_get_entry(ad, ADEID_FINDERI);
1188         if (p_ad == NULL) {
1189                 return false;
1190         }
1191
1192         ai = afpinfo_new(talloc_tos());
1193         if (ai == NULL) {
1194                 return false;
1195         }
1196
1197         memcpy(ai->afpi_FinderInfo, p_ad, ADEDLEN_FINDERI);
1198
1199         aiblob = data_blob_talloc(talloc_tos(), NULL, AFP_INFO_SIZE);
1200         if (aiblob.data == NULL) {
1201                 TALLOC_FREE(ai);
1202                 return false;
1203         }
1204
1205         size = afpinfo_pack(ai, (char *)aiblob.data);
1206         TALLOC_FREE(ai);
1207         if (size != AFP_INFO_SIZE) {
1208                 return false;
1209         }
1210
1211         stream_name = synthetic_smb_fname(talloc_tos(),
1212                                           smb_fname->base_name,
1213                                           AFPINFO_STREAM,
1214                                           NULL,
1215                                           smb_fname->flags);
1216         if (stream_name == NULL) {
1217                 data_blob_free(&aiblob);
1218                 DBG_ERR("synthetic_smb_fname failed\n");
1219                 return false;
1220         }
1221
1222         DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
1223
1224         status = SMB_VFS_CREATE_FILE(
1225                 ad->ad_handle->conn,            /* conn */
1226                 NULL,                           /* req */
1227                 0,                              /* root_dir_fid */
1228                 stream_name,                    /* fname */
1229                 FILE_GENERIC_WRITE,             /* access_mask */
1230                 FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
1231                 FILE_OPEN_IF,                   /* create_disposition */
1232                 0,                              /* create_options */
1233                 0,                              /* file_attributes */
1234                 INTERNAL_OPEN_ONLY,             /* oplock_request */
1235                 NULL,                           /* lease */
1236                 0,                              /* allocation_size */
1237                 0,                              /* private_flags */
1238                 NULL,                           /* sd */
1239                 NULL,                           /* ea_list */
1240                 &fsp,                           /* result */
1241                 NULL,                           /* psbuf */
1242                 NULL, NULL);                    /* create context */
1243         TALLOC_FREE(stream_name);
1244         if (!NT_STATUS_IS_OK(status)) {
1245                 DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
1246                 return false;
1247         }
1248
1249         nwritten = SMB_VFS_PWRITE(fsp,
1250                                   aiblob.data,
1251                                   aiblob.length,
1252                                   0);
1253         if (nwritten == -1) {
1254                 DBG_ERR("SMB_VFS_PWRITE failed\n");
1255                 saved_errno = errno;
1256                 close_file(NULL, fsp, ERROR_CLOSE);
1257                 errno = saved_errno;
1258                 return false;
1259         }
1260
1261         status = close_file(NULL, fsp, NORMAL_CLOSE);
1262         if (!NT_STATUS_IS_OK(status)) {
1263                 return false;
1264         }
1265         fsp = NULL;
1266
1267         return true;
1268 }
1269
1270 static bool ad_convert_truncate(struct adouble *ad,
1271                                 const struct smb_filename *smb_fname)
1272 {
1273         int rc;
1274
1275         /*
1276          * FIXME: direct ftruncate(), but we don't have a fsp for the
1277          * VFS call
1278          */
1279         rc = ftruncate(ad->ad_fd, ADEDOFF_RFORK_DOT_UND +
1280                        ad_getentrylen(ad, ADEID_RFORK));
1281         if (rc != 0) {
1282                 return false;
1283         }
1284
1285         return true;
1286 }
1287
1288 /**
1289  * Convert from Apple's ._ file to Netatalk
1290  *
1291  * Apple's AppleDouble may contain a FinderInfo entry longer then 32
1292  * bytes containing packed xattrs.
1293  *
1294  * @return -1 in case an error occurred, 0 if no conversion was done, 1
1295  * otherwise
1296  **/
1297 static int ad_convert(struct adouble *ad,
1298                       const struct smb_filename *smb_fname)
1299 {
1300         bool ok;
1301         bool converted_xattr = false;
1302
1303         ok = ad_convert_xattr(ad, smb_fname, &converted_xattr);
1304         if (!ok) {
1305                 return -1;
1306         }
1307
1308         if (converted_xattr) {
1309                 ok = ad_convert_truncate(ad, smb_fname);
1310                 if (!ok) {
1311                         return -1;
1312                 }
1313         }
1314
1315         ok = ad_convert_finderinfo(ad, smb_fname);
1316         if (!ok) {
1317                 DBG_ERR("Failed to convert [%s]\n",
1318                         smb_fname_str_dbg(smb_fname));
1319                 return -1;
1320         }
1321
1322         return 0;
1323 }
1324
1325 /**
1326  * Read and parse Netatalk AppleDouble metadata xattr
1327  **/
1328 static ssize_t ad_read_meta(struct adouble *ad,
1329                                 const struct smb_filename *smb_fname)
1330 {
1331         int      rc = 0;
1332         ssize_t  ealen;
1333         bool     ok;
1334
1335         DEBUG(10, ("reading meta xattr for %s\n", smb_fname->base_name));
1336
1337         ealen = SMB_VFS_GETXATTR(ad->ad_handle->conn, smb_fname,
1338                                  AFPINFO_EA_NETATALK, ad->ad_data,
1339                                  AD_DATASZ_XATTR);
1340         if (ealen == -1) {
1341                 switch (errno) {
1342                 case ENOATTR:
1343                 case ENOENT:
1344                         if (errno == ENOATTR) {
1345                                 errno = ENOENT;
1346                         }
1347                         rc = -1;
1348                         goto exit;
1349                 default:
1350                         DEBUG(2, ("error reading meta xattr: %s\n",
1351                                   strerror(errno)));
1352                         rc = -1;
1353                         goto exit;
1354                 }
1355         }
1356         if (ealen != AD_DATASZ_XATTR) {
1357                 DEBUG(2, ("bad size %zd\n", ealen));
1358                 errno = EINVAL;
1359                 rc = -1;
1360                 goto exit;
1361         }
1362
1363         /* Now parse entries */
1364         ok = ad_unpack(ad, ADEID_NUM_XATTR, AD_DATASZ_XATTR);
1365         if (!ok) {
1366                 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1367                 errno = EINVAL;
1368                 rc = -1;
1369                 goto exit;
1370         }
1371
1372         if (!ad_getentryoff(ad, ADEID_FINDERI)
1373             || !ad_getentryoff(ad, ADEID_COMMENT)
1374             || !ad_getentryoff(ad, ADEID_FILEDATESI)
1375             || !ad_getentryoff(ad, ADEID_AFPFILEI)
1376             || !ad_getentryoff(ad, ADEID_PRIVDEV)
1377             || !ad_getentryoff(ad, ADEID_PRIVINO)
1378             || !ad_getentryoff(ad, ADEID_PRIVSYN)
1379             || !ad_getentryoff(ad, ADEID_PRIVID)) {
1380                 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
1381                 errno = EINVAL;
1382                 rc = -1;
1383                 goto exit;
1384         }
1385
1386 exit:
1387         DEBUG(10, ("reading meta xattr for %s, rc: %d\n",
1388                 smb_fname->base_name, rc));
1389
1390         if (rc != 0) {
1391                 ealen = -1;
1392                 if (errno == EINVAL) {
1393                         become_root();
1394                         removexattr(smb_fname->base_name, AFPINFO_EA_NETATALK);
1395                         unbecome_root();
1396                         errno = ENOENT;
1397                 }
1398         }
1399         return ealen;
1400 }
1401
1402 static int ad_open_rsrc_xattr(const struct smb_filename *smb_fname,
1403                                 int flags,
1404                                 mode_t mode)
1405 {
1406 #ifdef HAVE_ATTROPEN
1407         /* FIXME: direct Solaris xattr syscall */
1408         return attropen(smb_fname->base_name,
1409                         AFPRESOURCE_EA_NETATALK, flags, mode);
1410 #else
1411         errno = ENOSYS;
1412         return -1;
1413 #endif
1414 }
1415
1416 static int ad_open_rsrc_adouble(const struct smb_filename *smb_fname,
1417                                 int flags,
1418                                 mode_t mode)
1419 {
1420         int ret;
1421         int fd;
1422         struct smb_filename *adp_smb_fname = NULL;
1423
1424         ret = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
1425         if (ret != 0) {
1426                 return -1;
1427         }
1428
1429         fd = open(adp_smb_fname->base_name, flags, mode);
1430         TALLOC_FREE(adp_smb_fname);
1431
1432         return fd;
1433 }
1434
1435 static int ad_open_rsrc(vfs_handle_struct *handle,
1436                         const struct smb_filename *smb_fname,
1437                         int flags,
1438                         mode_t mode)
1439 {
1440         struct fruit_config_data *config = NULL;
1441         int fd;
1442
1443         SMB_VFS_HANDLE_GET_DATA(handle, config,
1444                                 struct fruit_config_data, return -1);
1445
1446         if (config->rsrc == FRUIT_RSRC_XATTR) {
1447                 fd = ad_open_rsrc_xattr(smb_fname, flags, mode);
1448         } else {
1449                 fd = ad_open_rsrc_adouble(smb_fname, flags, mode);
1450         }
1451
1452         return fd;
1453 }
1454
1455 /*
1456  * Here's the deal: for ADOUBLE_META we can do without an fd as we can issue
1457  * path based xattr calls. For ADOUBLE_RSRC however we need a full-fledged fd
1458  * for file IO on the ._ file.
1459  */
1460 static int ad_open(vfs_handle_struct *handle,
1461                    struct adouble *ad,
1462                    files_struct *fsp,
1463                    const struct smb_filename *smb_fname,
1464                    int flags,
1465                    mode_t mode)
1466 {
1467         int fd;
1468
1469         DBG_DEBUG("Path [%s] type [%s]\n", smb_fname->base_name,
1470                   ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
1471
1472         if (ad->ad_type == ADOUBLE_META) {
1473                 return 0;
1474         }
1475
1476         if ((fsp != NULL) && (fsp->fh != NULL) && (fsp->fh->fd != -1)) {
1477                 ad->ad_fd = fsp->fh->fd;
1478                 ad->ad_opened = false;
1479                 return 0;
1480         }
1481
1482         fd = ad_open_rsrc(handle, smb_fname, flags, mode);
1483         if (fd == -1) {
1484                 return -1;
1485         }
1486         ad->ad_opened = true;
1487         ad->ad_fd = fd;
1488
1489         DBG_DEBUG("Path [%s] type [%s] fd [%d]\n",
1490                   smb_fname->base_name,
1491                   ad->ad_type == ADOUBLE_META ? "meta" : "rsrc", fd);
1492
1493         return 0;
1494 }
1495
1496 static ssize_t ad_read_rsrc_xattr(struct adouble *ad)
1497 {
1498         int ret;
1499         SMB_STRUCT_STAT st;
1500
1501         /* FIXME: direct sys_fstat(), don't have an fsp */
1502         ret = sys_fstat(ad->ad_fd, &st,
1503                         lp_fake_directory_create_times(
1504                                 SNUM(ad->ad_handle->conn)));
1505         if (ret != 0) {
1506                 return -1;
1507         }
1508
1509         ad_setentrylen(ad, ADEID_RFORK, st.st_ex_size);
1510         return st.st_ex_size;
1511 }
1512
1513 static ssize_t ad_read_rsrc_adouble(struct adouble *ad,
1514                                 const struct smb_filename *smb_fname)
1515 {
1516         SMB_STRUCT_STAT sbuf;
1517         char *p_ad = NULL;
1518         size_t size;
1519         ssize_t len;
1520         int ret;
1521         bool ok;
1522
1523         ret = sys_fstat(ad->ad_fd, &sbuf, lp_fake_directory_create_times(
1524                                 SNUM(ad->ad_handle->conn)));
1525         if (ret != 0) {
1526                 return -1;
1527         }
1528
1529         /*
1530          * AppleDouble file header content and size, two cases:
1531          *
1532          * - without xattrs it is exactly AD_DATASZ_DOT_UND (82) bytes large
1533          * - with embedded xattrs it can be larger, up to AD_XATTR_MAX_HDR_SIZE
1534          *
1535          * Read as much as we can up to AD_XATTR_MAX_HDR_SIZE.
1536          */
1537         size = sbuf.st_ex_size;
1538         if (size > talloc_array_length(ad->ad_data)) {
1539                 if (size > AD_XATTR_MAX_HDR_SIZE) {
1540                         size = AD_XATTR_MAX_HDR_SIZE;
1541                 }
1542                 p_ad = talloc_realloc(ad, ad->ad_data, char, size);
1543                 if (p_ad == NULL) {
1544                         return -1;
1545                 }
1546                 ad->ad_data = p_ad;
1547         }
1548
1549         len = sys_pread(ad->ad_fd, ad->ad_data,
1550                         talloc_array_length(ad->ad_data), 0);
1551         if (len != talloc_array_length(ad->ad_data)) {
1552                 DBG_NOTICE("%s %s: bad size: %zd\n",
1553                            smb_fname->base_name, strerror(errno), len);
1554                 return -1;
1555         }
1556
1557         /* Now parse entries */
1558         ok = ad_unpack(ad, ADEID_NUM_DOT_UND, sbuf.st_ex_size);
1559         if (!ok) {
1560                 DBG_ERR("invalid AppleDouble resource %s\n",
1561                         smb_fname->base_name);
1562                 errno = EINVAL;
1563                 return -1;
1564         }
1565
1566         if ((ad_getentryoff(ad, ADEID_FINDERI) != ADEDOFF_FINDERI_DOT_UND)
1567             || (ad_getentrylen(ad, ADEID_FINDERI) < ADEDLEN_FINDERI)
1568             || (ad_getentryoff(ad, ADEID_RFORK) < ADEDOFF_RFORK_DOT_UND)) {
1569                 DBG_ERR("invalid AppleDouble resource %s\n",
1570                         smb_fname->base_name);
1571                 errno = EINVAL;
1572                 return -1;
1573         }
1574
1575         /*
1576          * Try to fixup AppleDouble files created by OS X with xattrs
1577          * appended to the ADEID_FINDERI entry.
1578          */
1579
1580         ret = ad_convert(ad, smb_fname);
1581         if (ret != 0) {
1582                 DBG_WARNING("Failed to convert [%s]\n", smb_fname->base_name);
1583                 return len;
1584         }
1585
1586         return len;
1587 }
1588
1589 /**
1590  * Read and parse resource fork, either ._ AppleDouble file or xattr
1591  **/
1592 static ssize_t ad_read_rsrc(struct adouble *ad,
1593                         const struct smb_filename *smb_fname)
1594 {
1595         struct fruit_config_data *config = NULL;
1596         ssize_t len;
1597
1598         SMB_VFS_HANDLE_GET_DATA(ad->ad_handle, config,
1599                                 struct fruit_config_data, return -1);
1600
1601         if (config->rsrc == FRUIT_RSRC_XATTR) {
1602                 len = ad_read_rsrc_xattr(ad);
1603         } else {
1604                 len = ad_read_rsrc_adouble(ad, smb_fname);
1605         }
1606
1607         return len;
1608 }
1609
1610 /**
1611  * Read and unpack an AppleDouble metadata xattr or resource
1612  **/
1613 static ssize_t ad_read(struct adouble *ad, const struct smb_filename *smb_fname)
1614 {
1615         switch (ad->ad_type) {
1616         case ADOUBLE_META:
1617                 return ad_read_meta(ad, smb_fname);
1618         case ADOUBLE_RSRC:
1619                 return ad_read_rsrc(ad, smb_fname);
1620         default:
1621                 return -1;
1622         }
1623 }
1624
1625 static int adouble_destructor(struct adouble *ad)
1626 {
1627         if ((ad->ad_fd != -1) && ad->ad_opened) {
1628                 close(ad->ad_fd);
1629                 ad->ad_fd = -1;
1630         }
1631         return 0;
1632 }
1633
1634 /**
1635  * Allocate a struct adouble without initialiing it
1636  *
1637  * The struct is either hang of the fsp extension context or if fsp is
1638  * NULL from ctx.
1639  *
1640  * @param[in] ctx        talloc context
1641  * @param[in] handle     vfs handle
1642  * @param[in] type       type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1643  *
1644  * @return               adouble handle
1645  **/
1646 static struct adouble *ad_alloc(TALLOC_CTX *ctx, vfs_handle_struct *handle,
1647                                 adouble_type_t type)
1648 {
1649         int rc = 0;
1650         size_t adsize = 0;
1651         struct adouble *ad;
1652         struct fruit_config_data *config;
1653
1654         SMB_VFS_HANDLE_GET_DATA(handle, config,
1655                                 struct fruit_config_data, return NULL);
1656
1657         switch (type) {
1658         case ADOUBLE_META:
1659                 adsize = AD_DATASZ_XATTR;
1660                 break;
1661         case ADOUBLE_RSRC:
1662                 if (config->rsrc == FRUIT_RSRC_ADFILE) {
1663                         adsize = AD_DATASZ_DOT_UND;
1664                 }
1665                 break;
1666         default:
1667                 return NULL;
1668         }
1669
1670         ad = talloc_zero(ctx, struct adouble);
1671         if (ad == NULL) {
1672                 rc = -1;
1673                 goto exit;
1674         }
1675
1676         if (adsize) {
1677                 ad->ad_data = talloc_zero_array(ad, char, adsize);
1678                 if (ad->ad_data == NULL) {
1679                         rc = -1;
1680                         goto exit;
1681                 }
1682         }
1683
1684         ad->ad_handle = handle;
1685         ad->ad_type = type;
1686         ad->ad_magic = AD_MAGIC;
1687         ad->ad_version = AD_VERSION;
1688         ad->ad_fd = -1;
1689
1690         talloc_set_destructor(ad, adouble_destructor);
1691
1692 exit:
1693         if (rc != 0) {
1694                 TALLOC_FREE(ad);
1695         }
1696         return ad;
1697 }
1698
1699 /**
1700  * Allocate and initialize a new struct adouble
1701  *
1702  * @param[in] ctx        talloc context
1703  * @param[in] handle     vfs handle
1704  * @param[in] type       type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1705  *
1706  * @return               adouble handle, initialized
1707  **/
1708 static struct adouble *ad_init(TALLOC_CTX *ctx, vfs_handle_struct *handle,
1709                                adouble_type_t type)
1710 {
1711         int rc = 0;
1712         const struct ad_entry_order  *eid;
1713         struct adouble *ad = NULL;
1714         struct fruit_config_data *config;
1715         time_t t = time(NULL);
1716
1717         SMB_VFS_HANDLE_GET_DATA(handle, config,
1718                                 struct fruit_config_data, return NULL);
1719
1720         switch (type) {
1721         case ADOUBLE_META:
1722                 eid = entry_order_meta_xattr;
1723                 break;
1724         case ADOUBLE_RSRC:
1725                 if (config->rsrc == FRUIT_RSRC_ADFILE) {
1726                         eid = entry_order_dot_und;
1727                 } else {
1728                         eid = entry_order_rsrc_xattr;
1729                 }
1730                 break;
1731         default:
1732                 return NULL;
1733         }
1734
1735         ad = ad_alloc(ctx, handle, type);
1736         if (ad == NULL) {
1737                 return NULL;
1738         }
1739
1740         while (eid->id) {
1741                 ad->ad_eid[eid->id].ade_off = eid->offset;
1742                 ad->ad_eid[eid->id].ade_len = eid->len;
1743                 eid++;
1744         }
1745
1746         /* put something sane in the date fields */
1747         ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, t);
1748         ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, t);
1749         ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, t);
1750         ad_setdate(ad, AD_DATE_BACKUP, htonl(AD_DATE_START));
1751
1752         if (rc != 0) {
1753                 TALLOC_FREE(ad);
1754         }
1755         return ad;
1756 }
1757
1758 static struct adouble *ad_get_internal(TALLOC_CTX *ctx,
1759                                        vfs_handle_struct *handle,
1760                                        files_struct *fsp,
1761                                        const struct smb_filename *smb_fname,
1762                                        adouble_type_t type)
1763 {
1764         int rc = 0;
1765         ssize_t len;
1766         struct adouble *ad = NULL;
1767         int mode;
1768
1769         if (fsp != NULL) {
1770                 smb_fname = fsp->base_fsp->fsp_name;
1771         }
1772
1773         DEBUG(10, ("ad_get(%s) called for %s\n",
1774                    type == ADOUBLE_META ? "meta" : "rsrc",
1775                    smb_fname->base_name));
1776
1777         ad = ad_alloc(ctx, handle, type);
1778         if (ad == NULL) {
1779                 rc = -1;
1780                 goto exit;
1781         }
1782
1783         /* Try rw first so we can use the fd in ad_convert() */
1784         mode = O_RDWR;
1785
1786         rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
1787         if (rc == -1 && ((errno == EROFS) || (errno == EACCES))) {
1788                 mode = O_RDONLY;
1789                 rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
1790         }
1791         if (rc == -1) {
1792                 DBG_DEBUG("ad_open [%s] error [%s]\n",
1793                           smb_fname->base_name, strerror(errno));
1794                 goto exit;
1795
1796         }
1797
1798         len = ad_read(ad, smb_fname);
1799         if (len == -1) {
1800                 DEBUG(10, ("error reading AppleDouble for %s\n",
1801                         smb_fname->base_name));
1802                 rc = -1;
1803                 goto exit;
1804         }
1805
1806 exit:
1807         DEBUG(10, ("ad_get(%s) for %s returning %d\n",
1808                   type == ADOUBLE_META ? "meta" : "rsrc",
1809                   smb_fname->base_name, rc));
1810
1811         if (rc != 0) {
1812                 TALLOC_FREE(ad);
1813         }
1814         return ad;
1815 }
1816
1817 /**
1818  * Return AppleDouble data for a file
1819  *
1820  * @param[in] ctx      talloc context
1821  * @param[in] handle   vfs handle
1822  * @param[in] smb_fname pathname to file or directory
1823  * @param[in] type     type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1824  *
1825  * @return             talloced struct adouble or NULL on error
1826  **/
1827 static struct adouble *ad_get(TALLOC_CTX *ctx,
1828                               vfs_handle_struct *handle,
1829                               const struct smb_filename *smb_fname,
1830                               adouble_type_t type)
1831 {
1832         return ad_get_internal(ctx, handle, NULL, smb_fname, type);
1833 }
1834
1835 /**
1836  * Return AppleDouble data for a file
1837  *
1838  * @param[in] ctx      talloc context
1839  * @param[in] handle   vfs handle
1840  * @param[in] fsp      fsp to use for IO
1841  * @param[in] type     type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1842  *
1843  * @return             talloced struct adouble or NULL on error
1844  **/
1845 static struct adouble *ad_fget(TALLOC_CTX *ctx, vfs_handle_struct *handle,
1846                                files_struct *fsp, adouble_type_t type)
1847 {
1848         return ad_get_internal(ctx, handle, fsp, NULL, type);
1849 }
1850
1851 /**
1852  * Set AppleDouble metadata on a file or directory
1853  *
1854  * @param[in] ad      adouble handle
1855  *
1856  * @param[in] smb_fname    pathname to file or directory
1857  *
1858  * @return            status code, 0 means success
1859  **/
1860 static int ad_set(struct adouble *ad, const struct smb_filename *smb_fname)
1861 {
1862         bool ok;
1863         int ret;
1864
1865         DBG_DEBUG("Path [%s]\n", smb_fname->base_name);
1866
1867         if (ad->ad_type != ADOUBLE_META) {
1868                 DBG_ERR("ad_set on [%s] used with ADOUBLE_RSRC\n",
1869                         smb_fname->base_name);
1870                 return -1;
1871         }
1872
1873         ok = ad_pack(ad);
1874         if (!ok) {
1875                 return -1;
1876         }
1877
1878         ret = SMB_VFS_SETXATTR(ad->ad_handle->conn,
1879                                smb_fname,
1880                                AFPINFO_EA_NETATALK,
1881                                ad->ad_data,
1882                                AD_DATASZ_XATTR, 0);
1883
1884         DBG_DEBUG("Path [%s] ret [%d]\n", smb_fname->base_name, ret);
1885
1886         return ret;
1887 }
1888
1889 /**
1890  * Set AppleDouble metadata on a file or directory
1891  *
1892  * @param[in] ad      adouble handle
1893  * @param[in] fsp     file handle
1894  *
1895  * @return            status code, 0 means success
1896  **/
1897 static int ad_fset(struct adouble *ad, files_struct *fsp)
1898 {
1899         int rc = -1;
1900         ssize_t len;
1901         bool ok;
1902
1903         DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
1904
1905         if ((fsp == NULL)
1906             || (fsp->fh == NULL)
1907             || (fsp->fh->fd == -1))
1908         {
1909                 smb_panic("bad fsp");
1910         }
1911
1912         ok = ad_pack(ad);
1913         if (!ok) {
1914                 return -1;
1915         }
1916
1917         switch (ad->ad_type) {
1918         case ADOUBLE_META:
1919                 rc = SMB_VFS_NEXT_SETXATTR(ad->ad_handle,
1920                                            fsp->fsp_name,
1921                                            AFPINFO_EA_NETATALK,
1922                                            ad->ad_data,
1923                                            AD_DATASZ_XATTR, 0);
1924                 break;
1925
1926         case ADOUBLE_RSRC:
1927                 len = SMB_VFS_NEXT_PWRITE(ad->ad_handle,
1928                                           fsp,
1929                                           ad->ad_data,
1930                                           AD_DATASZ_DOT_UND,
1931                                           0);
1932                 if (len != AD_DATASZ_DOT_UND) {
1933                         DBG_ERR("short write on %s: %zd", fsp_str_dbg(fsp), len);
1934                         return -1;
1935                 }
1936                 rc = 0;
1937                 break;
1938
1939         default:
1940                 return -1;
1941         }
1942
1943         DBG_DEBUG("Path [%s] rc [%d]\n", fsp_str_dbg(fsp), rc);
1944
1945         return rc;
1946 }
1947
1948 /*****************************************************************************
1949  * Helper functions
1950  *****************************************************************************/
1951
1952 static bool is_afpinfo_stream(const struct smb_filename *smb_fname)
1953 {
1954         if (strncasecmp_m(smb_fname->stream_name,
1955                           AFPINFO_STREAM_NAME,
1956                           strlen(AFPINFO_STREAM_NAME)) == 0) {
1957                 return true;
1958         }
1959         return false;
1960 }
1961
1962 static bool is_afpresource_stream(const struct smb_filename *smb_fname)
1963 {
1964         if (strncasecmp_m(smb_fname->stream_name,
1965                           AFPRESOURCE_STREAM_NAME,
1966                           strlen(AFPRESOURCE_STREAM_NAME)) == 0) {
1967                 return true;
1968         }
1969         return false;
1970 }
1971
1972 /**
1973  * Test whether stream is an Apple stream, not used atm
1974  **/
1975 #if 0
1976 static bool is_apple_stream(const struct smb_filename *smb_fname)
1977 {
1978         if (is_afpinfo_stream(smb_fname)) {
1979                 return true;
1980         }
1981         if (is_afpresource_stream(smb_fname)) {
1982                 return true;
1983         }
1984         return false;
1985 }
1986 #endif
1987
1988 /**
1989  * Initialize config struct from our smb.conf config parameters
1990  **/
1991 static int init_fruit_config(vfs_handle_struct *handle)
1992 {
1993         struct fruit_config_data *config;
1994         int enumval;
1995         const char *tm_size_str = NULL;
1996
1997         config = talloc_zero(handle->conn, struct fruit_config_data);
1998         if (!config) {
1999                 DEBUG(1, ("talloc_zero() failed\n"));
2000                 errno = ENOMEM;
2001                 return -1;
2002         }
2003
2004         /*
2005          * Versions up to Samba 4.5.x had a spelling bug in the
2006          * fruit:resource option calling lp_parm_enum with
2007          * "res*s*ource" (ie two s).
2008          *
2009          * In Samba 4.6 we accept both the wrong and the correct
2010          * spelling, in Samba 4.7 the bad spelling will be removed.
2011          */
2012         enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2013                                "ressource", fruit_rsrc, FRUIT_RSRC_ADFILE);
2014         if (enumval == -1) {
2015                 DEBUG(1, ("value for %s: resource type unknown\n",
2016                           FRUIT_PARAM_TYPE_NAME));
2017                 return -1;
2018         }
2019         config->rsrc = (enum fruit_rsrc)enumval;
2020
2021         enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2022                                "resource", fruit_rsrc, enumval);
2023         if (enumval == -1) {
2024                 DEBUG(1, ("value for %s: resource type unknown\n",
2025                           FRUIT_PARAM_TYPE_NAME));
2026                 return -1;
2027         }
2028         config->rsrc = (enum fruit_rsrc)enumval;
2029
2030         enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2031                                "metadata", fruit_meta, FRUIT_META_NETATALK);
2032         if (enumval == -1) {
2033                 DEBUG(1, ("value for %s: metadata type unknown\n",
2034                           FRUIT_PARAM_TYPE_NAME));
2035                 return -1;
2036         }
2037         config->meta = (enum fruit_meta)enumval;
2038
2039         enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2040                                "locking", fruit_locking, FRUIT_LOCKING_NONE);
2041         if (enumval == -1) {
2042                 DEBUG(1, ("value for %s: locking type unknown\n",
2043                           FRUIT_PARAM_TYPE_NAME));
2044                 return -1;
2045         }
2046         config->locking = (enum fruit_locking)enumval;
2047
2048         enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2049                                "encoding", fruit_encoding, FRUIT_ENC_PRIVATE);
2050         if (enumval == -1) {
2051                 DEBUG(1, ("value for %s: encoding type unknown\n",
2052                           FRUIT_PARAM_TYPE_NAME));
2053                 return -1;
2054         }
2055         config->encoding = (enum fruit_encoding)enumval;
2056
2057         if (config->rsrc == FRUIT_RSRC_ADFILE) {
2058                 config->veto_appledouble = lp_parm_bool(SNUM(handle->conn),
2059                                                         FRUIT_PARAM_TYPE_NAME,
2060                                                         "veto_appledouble",
2061                                                         true);
2062         }
2063
2064         config->use_aapl = lp_parm_bool(
2065                 -1, FRUIT_PARAM_TYPE_NAME, "aapl", true);
2066
2067         config->time_machine = lp_parm_bool(
2068                 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "time machine", false);
2069
2070         config->unix_info_enabled = lp_parm_bool(
2071                 -1, FRUIT_PARAM_TYPE_NAME, "nfs_aces", true);
2072
2073         config->use_copyfile = lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME,
2074                                            "copyfile", false);
2075
2076         config->posix_rename = lp_parm_bool(
2077                 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "posix_rename", true);
2078
2079         config->aapl_zero_file_id =
2080             lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME, "zero_file_id", true);
2081
2082         config->readdir_attr_rsize = lp_parm_bool(
2083                 SNUM(handle->conn), "readdir_attr", "aapl_rsize", true);
2084
2085         config->readdir_attr_finder_info = lp_parm_bool(
2086                 SNUM(handle->conn), "readdir_attr", "aapl_finder_info", true);
2087
2088         config->readdir_attr_max_access = lp_parm_bool(
2089                 SNUM(handle->conn), "readdir_attr", "aapl_max_access", true);
2090
2091         config->model = lp_parm_const_string(
2092                 -1, FRUIT_PARAM_TYPE_NAME, "model", "MacSamba");
2093
2094         tm_size_str = lp_parm_const_string(
2095                 SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
2096                 "time machine max size", NULL);
2097         if (tm_size_str != NULL) {
2098                 config->time_machine_max_size = conv_str_size(tm_size_str);
2099         }
2100
2101         SMB_VFS_HANDLE_SET_DATA(handle, config,
2102                                 NULL, struct fruit_config_data,
2103                                 return -1);
2104
2105         return 0;
2106 }
2107
2108 /**
2109  * Prepend "._" to a basename
2110  * Return a new struct smb_filename with stream_name == NULL.
2111  **/
2112 static int adouble_path(TALLOC_CTX *ctx,
2113                         const struct smb_filename *smb_fname_in,
2114                         struct smb_filename **pp_smb_fname_out)
2115 {
2116         char *parent;
2117         const char *base;
2118         struct smb_filename *smb_fname = cp_smb_filename(ctx,
2119                                                 smb_fname_in);
2120
2121         if (smb_fname == NULL) {
2122                 return -1;
2123         }
2124
2125         /* We need streamname to be NULL */
2126         TALLOC_FREE(smb_fname->stream_name);
2127
2128         /* And we're replacing base_name. */
2129         TALLOC_FREE(smb_fname->base_name);
2130
2131         if (!parent_dirname(smb_fname, smb_fname_in->base_name,
2132                                 &parent, &base)) {
2133                 TALLOC_FREE(smb_fname);
2134                 return -1;
2135         }
2136
2137         smb_fname->base_name = talloc_asprintf(smb_fname,
2138                                         "%s/._%s", parent, base);
2139         if (smb_fname->base_name == NULL) {
2140                 TALLOC_FREE(smb_fname);
2141                 return -1;
2142         }
2143
2144         *pp_smb_fname_out = smb_fname;
2145
2146         return 0;
2147 }
2148
2149 /**
2150  * Allocate and initialize an AfpInfo struct
2151  **/
2152 static AfpInfo *afpinfo_new(TALLOC_CTX *ctx)
2153 {
2154         AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2155         if (ai == NULL) {
2156                 return NULL;
2157         }
2158         ai->afpi_Signature = AFP_Signature;
2159         ai->afpi_Version = AFP_Version;
2160         ai->afpi_BackupTime = AD_DATE_START;
2161         return ai;
2162 }
2163
2164 /**
2165  * Pack an AfpInfo struct into a buffer
2166  *
2167  * Buffer size must be at least AFP_INFO_SIZE
2168  * Returns size of packed buffer
2169  **/
2170 static ssize_t afpinfo_pack(const AfpInfo *ai, char *buf)
2171 {
2172         memset(buf, 0, AFP_INFO_SIZE);
2173
2174         RSIVAL(buf, 0, ai->afpi_Signature);
2175         RSIVAL(buf, 4, ai->afpi_Version);
2176         RSIVAL(buf, 12, ai->afpi_BackupTime);
2177         memcpy(buf + 16, ai->afpi_FinderInfo, sizeof(ai->afpi_FinderInfo));
2178
2179         return AFP_INFO_SIZE;
2180 }
2181
2182 /**
2183  * Unpack a buffer into a AfpInfo structure
2184  *
2185  * Buffer size must be at least AFP_INFO_SIZE
2186  * Returns allocated AfpInfo struct
2187  **/
2188 static AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data)
2189 {
2190         AfpInfo *ai = talloc_zero(ctx, AfpInfo);
2191         if (ai == NULL) {
2192                 return NULL;
2193         }
2194
2195         ai->afpi_Signature = RIVAL(data, 0);
2196         ai->afpi_Version = RIVAL(data, 4);
2197         ai->afpi_BackupTime = RIVAL(data, 12);
2198         memcpy(ai->afpi_FinderInfo, (const char *)data + 16,
2199                sizeof(ai->afpi_FinderInfo));
2200
2201         if (ai->afpi_Signature != AFP_Signature
2202             || ai->afpi_Version != AFP_Version) {
2203                 DEBUG(1, ("Bad AfpInfo signature or version\n"));
2204                 TALLOC_FREE(ai);
2205         }
2206
2207         return ai;
2208 }
2209
2210 /**
2211  * Fake an inode number from the md5 hash of the (xattr) name
2212  **/
2213 static SMB_INO_T fruit_inode(const SMB_STRUCT_STAT *sbuf, const char *sname)
2214 {
2215         MD5_CTX ctx;
2216         unsigned char hash[16];
2217         SMB_INO_T result;
2218         char *upper_sname;
2219
2220         upper_sname = talloc_strdup_upper(talloc_tos(), sname);
2221         SMB_ASSERT(upper_sname != NULL);
2222
2223         MD5Init(&ctx);
2224         MD5Update(&ctx, (const unsigned char *)&(sbuf->st_ex_dev),
2225                   sizeof(sbuf->st_ex_dev));
2226         MD5Update(&ctx, (const unsigned char *)&(sbuf->st_ex_ino),
2227                   sizeof(sbuf->st_ex_ino));
2228         MD5Update(&ctx, (unsigned char *)upper_sname,
2229                   talloc_get_size(upper_sname)-1);
2230         MD5Final(hash, &ctx);
2231
2232         TALLOC_FREE(upper_sname);
2233
2234         /* Hopefully all the variation is in the lower 4 (or 8) bytes! */
2235         memcpy(&result, hash, sizeof(result));
2236
2237         DEBUG(10, ("fruit_inode \"%s\": ino=0x%llu\n",
2238                    sname, (unsigned long long)result));
2239
2240         return result;
2241 }
2242
2243 static bool add_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
2244                              struct stream_struct **streams,
2245                              const char *name, off_t size,
2246                              off_t alloc_size)
2247 {
2248         struct stream_struct *tmp;
2249
2250         tmp = talloc_realloc(mem_ctx, *streams, struct stream_struct,
2251                              (*num_streams)+1);
2252         if (tmp == NULL) {
2253                 return false;
2254         }
2255
2256         tmp[*num_streams].name = talloc_asprintf(tmp, "%s:$DATA", name);
2257         if (tmp[*num_streams].name == NULL) {
2258                 return false;
2259         }
2260
2261         tmp[*num_streams].size = size;
2262         tmp[*num_streams].alloc_size = alloc_size;
2263
2264         *streams = tmp;
2265         *num_streams += 1;
2266         return true;
2267 }
2268
2269 static bool filter_empty_rsrc_stream(unsigned int *num_streams,
2270                                      struct stream_struct **streams)
2271 {
2272         struct stream_struct *tmp = *streams;
2273         unsigned int i;
2274
2275         if (*num_streams == 0) {
2276                 return true;
2277         }
2278
2279         for (i = 0; i < *num_streams; i++) {
2280                 if (strequal_m(tmp[i].name, AFPRESOURCE_STREAM)) {
2281                         break;
2282                 }
2283         }
2284
2285         if (i == *num_streams) {
2286                 return true;
2287         }
2288
2289         if (tmp[i].size > 0) {
2290                 return true;
2291         }
2292
2293         TALLOC_FREE(tmp[i].name);
2294         if (*num_streams - 1 > i) {
2295                 memmove(&tmp[i], &tmp[i+1],
2296                         (*num_streams - i - 1) * sizeof(struct stream_struct));
2297         }
2298
2299         *num_streams -= 1;
2300         return true;
2301 }
2302
2303 static bool del_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
2304                              struct stream_struct **streams,
2305                              const char *name)
2306 {
2307         struct stream_struct *tmp = *streams;
2308         unsigned int i;
2309
2310         if (*num_streams == 0) {
2311                 return true;
2312         }
2313
2314         for (i = 0; i < *num_streams; i++) {
2315                 if (strequal_m(tmp[i].name, name)) {
2316                         break;
2317                 }
2318         }
2319
2320         if (i == *num_streams) {
2321                 return true;
2322         }
2323
2324         TALLOC_FREE(tmp[i].name);
2325         if (*num_streams - 1 > i) {
2326                 memmove(&tmp[i], &tmp[i+1],
2327                         (*num_streams - i - 1) * sizeof(struct stream_struct));
2328         }
2329
2330         *num_streams -= 1;
2331         return true;
2332 }
2333
2334 static bool ad_empty_finderinfo(const struct adouble *ad)
2335 {
2336         int cmp;
2337         char emptybuf[ADEDLEN_FINDERI] = {0};
2338         char *fi = NULL;
2339
2340         fi = ad_get_entry(ad, ADEID_FINDERI);
2341         if (fi == NULL) {
2342                 DBG_ERR("Missing FinderInfo in struct adouble [%p]\n", ad);
2343                 return false;
2344         }
2345
2346         cmp = memcmp(emptybuf, fi, ADEDLEN_FINDERI);
2347         return (cmp == 0);
2348 }
2349
2350 static bool ai_empty_finderinfo(const AfpInfo *ai)
2351 {
2352         int cmp;
2353         char emptybuf[ADEDLEN_FINDERI] = {0};
2354
2355         cmp = memcmp(emptybuf, &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
2356         return (cmp == 0);
2357 }
2358
2359 /**
2360  * Update btime with btime from Netatalk
2361  **/
2362 static void update_btime(vfs_handle_struct *handle,
2363                          struct smb_filename *smb_fname)
2364 {
2365         uint32_t t;
2366         struct timespec creation_time = {0};
2367         struct adouble *ad;
2368         struct fruit_config_data *config = NULL;
2369
2370         SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
2371                                 return);
2372
2373         switch (config->meta) {
2374         case FRUIT_META_STREAM:
2375                 return;
2376         case FRUIT_META_NETATALK:
2377                 /* Handled below */
2378                 break;
2379         default:
2380                 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
2381                 return;
2382         }
2383
2384         ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
2385         if (ad == NULL) {
2386                 return;
2387         }
2388         if (ad_getdate(ad, AD_DATE_UNIX | AD_DATE_CREATE, &t) != 0) {
2389                 TALLOC_FREE(ad);
2390                 return;
2391         }
2392         TALLOC_FREE(ad);
2393
2394         creation_time.tv_sec = convert_uint32_t_to_time_t(t);
2395         update_stat_ex_create_time(&smb_fname->st, creation_time);
2396
2397         return;
2398 }
2399
2400 /**
2401  * Map an access mask to a Netatalk single byte byte range lock
2402  **/
2403 static off_t access_to_netatalk_brl(enum apple_fork fork_type,
2404                                     uint32_t access_mask)
2405 {
2406         off_t offset;
2407
2408         switch (access_mask) {
2409         case FILE_READ_DATA:
2410                 offset = AD_FILELOCK_OPEN_RD;
2411                 break;
2412
2413         case FILE_WRITE_DATA:
2414         case FILE_APPEND_DATA:
2415                 offset = AD_FILELOCK_OPEN_WR;
2416                 break;
2417
2418         default:
2419                 offset = AD_FILELOCK_OPEN_NONE;
2420                 break;
2421         }
2422
2423         if (fork_type == APPLE_FORK_RSRC) {
2424                 if (offset == AD_FILELOCK_OPEN_NONE) {
2425                         offset = AD_FILELOCK_RSRC_OPEN_NONE;
2426                 } else {
2427                         offset += 2;
2428                 }
2429         }
2430
2431         return offset;
2432 }
2433
2434 /**
2435  * Map a deny mode to a Netatalk brl
2436  **/
2437 static off_t denymode_to_netatalk_brl(enum apple_fork fork_type,
2438                                       uint32_t deny_mode)
2439 {
2440         off_t offset = 0;
2441
2442         switch (deny_mode) {
2443         case DENY_READ:
2444                 offset = AD_FILELOCK_DENY_RD;
2445                 break;
2446
2447         case DENY_WRITE:
2448                 offset = AD_FILELOCK_DENY_WR;
2449                 break;
2450
2451         default:
2452                 smb_panic("denymode_to_netatalk_brl: bad deny mode\n");
2453         }
2454
2455         if (fork_type == APPLE_FORK_RSRC) {
2456                 offset += 2;
2457         }
2458
2459         return offset;
2460 }
2461
2462 /**
2463  * Call fcntl() with an exclusive F_GETLK request in order to
2464  * determine if there's an exisiting shared lock
2465  *
2466  * @return true if the requested lock was found or any error occurred
2467  *         false if the lock was not found
2468  **/
2469 static bool test_netatalk_lock(files_struct *fsp, off_t in_offset)
2470 {
2471         bool result;
2472         off_t offset = in_offset;
2473         off_t len = 1;
2474         int type = F_WRLCK;
2475         pid_t pid;
2476
2477         result = SMB_VFS_GETLOCK(fsp, &offset, &len, &type, &pid);
2478         if (result == false) {
2479                 return true;
2480         }
2481
2482         if (type != F_UNLCK) {
2483                 return true;
2484         }
2485
2486         return false;
2487 }
2488
2489 static NTSTATUS fruit_check_access(vfs_handle_struct *handle,
2490                                    files_struct *fsp,
2491                                    uint32_t access_mask,
2492                                    uint32_t deny_mode)
2493 {
2494         NTSTATUS status = NT_STATUS_OK;
2495         bool open_for_reading, open_for_writing, deny_read, deny_write;
2496         off_t off;
2497         bool have_read = false;
2498         int flags;
2499
2500         /* FIXME: hardcoded data fork, add resource fork */
2501         enum apple_fork fork_type = APPLE_FORK_DATA;
2502
2503         DEBUG(10, ("fruit_check_access: %s, am: %s/%s, dm: %s/%s\n",
2504                   fsp_str_dbg(fsp),
2505                   access_mask & FILE_READ_DATA ? "READ" :"-",
2506                   access_mask & FILE_WRITE_DATA ? "WRITE" : "-",
2507                   deny_mode & DENY_READ ? "DENY_READ" : "-",
2508                   deny_mode & DENY_WRITE ? "DENY_WRITE" : "-"));
2509
2510         if (fsp->fh->fd == -1) {
2511                 return NT_STATUS_OK;
2512         }
2513
2514         flags = fcntl(fsp->fh->fd, F_GETFL);
2515         if (flags == -1) {
2516                 DBG_ERR("fcntl get flags [%s] fd [%d] failed [%s]\n",
2517                         fsp_str_dbg(fsp), fsp->fh->fd, strerror(errno));
2518                 return map_nt_error_from_unix(errno);
2519         }
2520
2521         if (flags & (O_RDONLY|O_RDWR)) {
2522                 /*
2523                  * Applying fcntl read locks requires an fd opened for
2524                  * reading. This means we won't be applying locks for
2525                  * files openend write-only, but what can we do...
2526                  */
2527                 have_read = true;
2528         }
2529
2530         /*
2531          * Check read access and deny read mode
2532          */
2533         if ((access_mask & FILE_READ_DATA) || (deny_mode & DENY_READ)) {
2534                 /* Check access */
2535                 open_for_reading = test_netatalk_lock(
2536                         fsp, access_to_netatalk_brl(fork_type, FILE_READ_DATA));
2537
2538                 deny_read = test_netatalk_lock(
2539                         fsp, denymode_to_netatalk_brl(fork_type, DENY_READ));
2540
2541                 DEBUG(10, ("read: %s, deny_write: %s\n",
2542                           open_for_reading == true ? "yes" : "no",
2543                           deny_read == true ? "yes" : "no"));
2544
2545                 if (((access_mask & FILE_READ_DATA) && deny_read)
2546                     || ((deny_mode & DENY_READ) && open_for_reading)) {
2547                         return NT_STATUS_SHARING_VIOLATION;
2548                 }
2549
2550                 /* Set locks */
2551                 if ((access_mask & FILE_READ_DATA) && have_read) {
2552                         struct byte_range_lock *br_lck = NULL;
2553
2554                         off = access_to_netatalk_brl(fork_type, FILE_READ_DATA);
2555                         br_lck = do_lock(
2556                                 handle->conn->sconn->msg_ctx, fsp,
2557                                 fsp->op->global->open_persistent_id, 1, off,
2558                                 READ_LOCK, POSIX_LOCK, false,
2559                                 &status, NULL);
2560
2561                         TALLOC_FREE(br_lck);
2562
2563                         if (!NT_STATUS_IS_OK(status))  {
2564                                 return status;
2565                         }
2566                 }
2567
2568                 if ((deny_mode & DENY_READ) && have_read) {
2569                         struct byte_range_lock *br_lck = NULL;
2570
2571                         off = denymode_to_netatalk_brl(fork_type, DENY_READ);
2572                         br_lck = do_lock(
2573                                 handle->conn->sconn->msg_ctx, fsp,
2574                                 fsp->op->global->open_persistent_id, 1, off,
2575                                 READ_LOCK, POSIX_LOCK, false,
2576                                 &status, NULL);
2577
2578                         TALLOC_FREE(br_lck);
2579
2580                         if (!NT_STATUS_IS_OK(status)) {
2581                                 return status;
2582                         }
2583                 }
2584         }
2585
2586         /*
2587          * Check write access and deny write mode
2588          */
2589         if ((access_mask & FILE_WRITE_DATA) || (deny_mode & DENY_WRITE)) {
2590                 /* Check access */
2591                 open_for_writing = test_netatalk_lock(
2592                         fsp, access_to_netatalk_brl(fork_type, FILE_WRITE_DATA));
2593
2594                 deny_write = test_netatalk_lock(
2595                         fsp, denymode_to_netatalk_brl(fork_type, DENY_WRITE));
2596
2597                 DEBUG(10, ("write: %s, deny_write: %s\n",
2598                           open_for_writing == true ? "yes" : "no",
2599                           deny_write == true ? "yes" : "no"));
2600
2601                 if (((access_mask & FILE_WRITE_DATA) && deny_write)
2602                     || ((deny_mode & DENY_WRITE) && open_for_writing)) {
2603                         return NT_STATUS_SHARING_VIOLATION;
2604                 }
2605
2606                 /* Set locks */
2607                 if ((access_mask & FILE_WRITE_DATA) && have_read) {
2608                         struct byte_range_lock *br_lck = NULL;
2609
2610                         off = access_to_netatalk_brl(fork_type, FILE_WRITE_DATA);
2611                         br_lck = do_lock(
2612                                 handle->conn->sconn->msg_ctx, fsp,
2613                                 fsp->op->global->open_persistent_id, 1, off,
2614                                 READ_LOCK, POSIX_LOCK, false,
2615                                 &status, NULL);
2616
2617                         TALLOC_FREE(br_lck);
2618
2619                         if (!NT_STATUS_IS_OK(status)) {
2620                                 return status;
2621                         }
2622                 }
2623                 if ((deny_mode & DENY_WRITE) && have_read) {
2624                         struct byte_range_lock *br_lck = NULL;
2625
2626                         off = denymode_to_netatalk_brl(fork_type, DENY_WRITE);
2627                         br_lck = do_lock(
2628                                 handle->conn->sconn->msg_ctx, fsp,
2629                                 fsp->op->global->open_persistent_id, 1, off,
2630                                 READ_LOCK, POSIX_LOCK, false,
2631                                 &status, NULL);
2632
2633                         TALLOC_FREE(br_lck);
2634
2635                         if (!NT_STATUS_IS_OK(status)) {
2636                                 return status;
2637                         }
2638                 }
2639         }
2640
2641         return status;
2642 }
2643
2644 static NTSTATUS check_aapl(vfs_handle_struct *handle,
2645                            struct smb_request *req,
2646                            const struct smb2_create_blobs *in_context_blobs,
2647                            struct smb2_create_blobs *out_context_blobs)
2648 {
2649         struct fruit_config_data *config;
2650         NTSTATUS status;
2651         struct smb2_create_blob *aapl = NULL;
2652         uint32_t cmd;
2653         bool ok;
2654         uint8_t p[16];
2655         DATA_BLOB blob = data_blob_talloc(req, NULL, 0);
2656         uint64_t req_bitmap, client_caps;
2657         uint64_t server_caps = SMB2_CRTCTX_AAPL_UNIX_BASED;
2658         smb_ucs2_t *model;
2659         size_t modellen;
2660
2661         SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
2662                                 return NT_STATUS_UNSUCCESSFUL);
2663
2664         if (!config->use_aapl
2665             || in_context_blobs == NULL
2666             || out_context_blobs == NULL) {
2667                 return NT_STATUS_OK;
2668         }
2669
2670         aapl = smb2_create_blob_find(in_context_blobs,
2671                                      SMB2_CREATE_TAG_AAPL);
2672         if (aapl == NULL) {
2673                 return NT_STATUS_OK;
2674         }
2675
2676         if (aapl->data.length != 24) {
2677                 DEBUG(1, ("unexpected AAPL ctxt length: %ju\n",
2678                           (uintmax_t)aapl->data.length));
2679                 return NT_STATUS_INVALID_PARAMETER;
2680         }
2681
2682         cmd = IVAL(aapl->data.data, 0);
2683         if (cmd != SMB2_CRTCTX_AAPL_SERVER_QUERY) {
2684                 DEBUG(1, ("unsupported AAPL cmd: %d\n", cmd));
2685                 return NT_STATUS_INVALID_PARAMETER;
2686         }
2687
2688         req_bitmap = BVAL(aapl->data.data, 8);
2689         client_caps = BVAL(aapl->data.data, 16);
2690
2691         SIVAL(p, 0, SMB2_CRTCTX_AAPL_SERVER_QUERY);
2692         SIVAL(p, 4, 0);
2693         SBVAL(p, 8, req_bitmap);
2694         ok = data_blob_append(req, &blob, p, 16);
2695         if (!ok) {
2696                 return NT_STATUS_UNSUCCESSFUL;
2697         }
2698
2699         if (req_bitmap & SMB2_CRTCTX_AAPL_SERVER_CAPS) {
2700                 if ((client_caps & SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR) &&
2701                     (handle->conn->tcon->compat->fs_capabilities & FILE_NAMED_STREAMS)) {
2702                         server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR;
2703                         config->readdir_attr_enabled = true;
2704                 }
2705
2706                 if (config->use_copyfile) {
2707                         server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_OSX_COPYFILE;
2708                         config->copyfile_enabled = true;
2709                 }
2710
2711                 /*
2712                  * The client doesn't set the flag, so we can't check
2713                  * for it and just set it unconditionally
2714                  */
2715                 if (config->unix_info_enabled) {
2716                         server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_NFS_ACE;
2717                 }
2718
2719                 SBVAL(p, 0, server_caps);
2720                 ok = data_blob_append(req, &blob, p, 8);
2721                 if (!ok) {
2722                         return NT_STATUS_UNSUCCESSFUL;
2723                 }
2724         }
2725
2726         if (req_bitmap & SMB2_CRTCTX_AAPL_VOLUME_CAPS) {
2727                 int val = lp_case_sensitive(SNUM(handle->conn->tcon->compat));
2728                 uint64_t caps = 0;
2729
2730                 switch (val) {
2731                 case Auto:
2732                         break;
2733
2734                 case True:
2735                         caps |= SMB2_CRTCTX_AAPL_CASE_SENSITIVE;
2736                         break;
2737
2738                 default:
2739                         break;
2740                 }
2741
2742                 if (config->time_machine) {
2743                         caps |= SMB2_CRTCTX_AAPL_FULL_SYNC;
2744                 }
2745
2746                 SBVAL(p, 0, caps);
2747
2748                 ok = data_blob_append(req, &blob, p, 8);
2749                 if (!ok) {
2750                         return NT_STATUS_UNSUCCESSFUL;
2751                 }
2752         }
2753
2754         if (req_bitmap & SMB2_CRTCTX_AAPL_MODEL_INFO) {
2755                 ok = convert_string_talloc(req,
2756                                            CH_UNIX, CH_UTF16LE,
2757                                            config->model, strlen(config->model),
2758                                            &model, &modellen);
2759                 if (!ok) {
2760                         return NT_STATUS_UNSUCCESSFUL;
2761                 }
2762
2763                 SIVAL(p, 0, 0);
2764                 SIVAL(p + 4, 0, modellen);
2765                 ok = data_blob_append(req, &blob, p, 8);
2766                 if (!ok) {
2767                         talloc_free(model);
2768                         return NT_STATUS_UNSUCCESSFUL;
2769                 }
2770
2771                 ok = data_blob_append(req, &blob, model, modellen);
2772                 talloc_free(model);
2773                 if (!ok) {
2774                         return NT_STATUS_UNSUCCESSFUL;
2775                 }
2776         }
2777
2778         status = smb2_create_blob_add(out_context_blobs,
2779                                       out_context_blobs,
2780                                       SMB2_CREATE_TAG_AAPL,
2781                                       blob);
2782         if (NT_STATUS_IS_OK(status)) {
2783                 global_fruit_config.nego_aapl = true;
2784                 if (config->aapl_zero_file_id) {
2785                         aapl_force_zero_file_id(handle->conn->sconn);
2786                 }
2787         }
2788
2789         return status;
2790 }
2791
2792 static bool readdir_attr_meta_finderi_stream(
2793         struct vfs_handle_struct *handle,
2794         const struct smb_filename *smb_fname,
2795         AfpInfo *ai)
2796 {
2797         struct smb_filename *stream_name = NULL;
2798         files_struct *fsp = NULL;
2799         ssize_t nread;
2800         NTSTATUS status;
2801         int ret;
2802         bool ok;
2803         uint8_t buf[AFP_INFO_SIZE];
2804
2805         stream_name = synthetic_smb_fname(talloc_tos(),
2806                                           smb_fname->base_name,
2807                                           AFPINFO_STREAM_NAME,
2808                                           NULL, smb_fname->flags);
2809         if (stream_name == NULL) {
2810                 return false;
2811         }
2812
2813         ret = SMB_VFS_STAT(handle->conn, stream_name);
2814         if (ret != 0) {
2815                 return false;
2816         }
2817
2818         status = SMB_VFS_CREATE_FILE(
2819                 handle->conn,                           /* conn */
2820                 NULL,                                   /* req */
2821                 0,                                      /* root_dir_fid */
2822                 stream_name,                            /* fname */
2823                 FILE_READ_DATA,                         /* access_mask */
2824                 (FILE_SHARE_READ | FILE_SHARE_WRITE |   /* share_access */
2825                         FILE_SHARE_DELETE),
2826                 FILE_OPEN,                              /* create_disposition*/
2827                 0,                                      /* create_options */
2828                 0,                                      /* file_attributes */
2829                 INTERNAL_OPEN_ONLY,                     /* oplock_request */
2830                 NULL,                                   /* lease */
2831                 0,                                      /* allocation_size */
2832                 0,                                      /* private_flags */
2833                 NULL,                                   /* sd */
2834                 NULL,                                   /* ea_list */
2835                 &fsp,                                   /* result */
2836                 NULL,                                   /* pinfo */
2837                 NULL, NULL);                            /* create context */
2838
2839         TALLOC_FREE(stream_name);
2840
2841         if (!NT_STATUS_IS_OK(status)) {
2842                 return false;
2843         }
2844
2845         nread = SMB_VFS_PREAD(fsp, &buf[0], AFP_INFO_SIZE, 0);
2846         if (nread != AFP_INFO_SIZE) {
2847                 DBG_ERR("short read [%s] [%zd/%d]\n",
2848                         smb_fname_str_dbg(stream_name), nread, AFP_INFO_SIZE);
2849                 ok = false;
2850                 goto fail;
2851         }
2852
2853         memcpy(&ai->afpi_FinderInfo[0], &buf[AFP_OFF_FinderInfo],
2854                AFP_FinderSize);
2855
2856         ok = true;
2857
2858 fail:
2859         if (fsp != NULL) {
2860                 close_file(NULL, fsp, NORMAL_CLOSE);
2861         }
2862
2863         return ok;
2864 }
2865
2866 static bool readdir_attr_meta_finderi_netatalk(
2867         struct vfs_handle_struct *handle,
2868         const struct smb_filename *smb_fname,
2869         AfpInfo *ai)
2870 {
2871         struct adouble *ad = NULL;
2872         char *p = NULL;
2873
2874         ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
2875         if (ad == NULL) {
2876                 return false;
2877         }
2878
2879         p = ad_get_entry(ad, ADEID_FINDERI);
2880         if (p == NULL) {
2881                 DBG_ERR("No ADEID_FINDERI for [%s]\n", smb_fname->base_name);
2882                 TALLOC_FREE(ad);
2883                 return false;
2884         }
2885
2886         memcpy(&ai->afpi_FinderInfo[0], p, AFP_FinderSize);
2887         TALLOC_FREE(ad);
2888         return true;
2889 }
2890
2891 static bool readdir_attr_meta_finderi(struct vfs_handle_struct *handle,
2892                                       const struct smb_filename *smb_fname,
2893                                       struct readdir_attr_data *attr_data)
2894 {
2895         struct fruit_config_data *config = NULL;
2896         uint32_t date_added;
2897         AfpInfo ai = {0};
2898         bool ok;
2899
2900         SMB_VFS_HANDLE_GET_DATA(handle, config,
2901                                 struct fruit_config_data,
2902                                 return false);
2903
2904         switch (config->meta) {
2905         case FRUIT_META_NETATALK:
2906                 ok = readdir_attr_meta_finderi_netatalk(
2907                         handle, smb_fname, &ai);
2908                 break;
2909
2910         case FRUIT_META_STREAM:
2911                 ok = readdir_attr_meta_finderi_stream(
2912                         handle, smb_fname, &ai);
2913                 break;
2914
2915         default:
2916                 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
2917                 return false;
2918         }
2919
2920         if (!ok) {
2921                 /* Don't bother with errors, it's likely ENOENT */
2922                 return true;
2923         }
2924
2925         if (S_ISREG(smb_fname->st.st_ex_mode)) {
2926                 /* finder_type */
2927                 memcpy(&attr_data->attr_data.aapl.finder_info[0],
2928                        &ai.afpi_FinderInfo[0], 4);
2929
2930                 /* finder_creator */
2931                 memcpy(&attr_data->attr_data.aapl.finder_info[0] + 4,
2932                        &ai.afpi_FinderInfo[4], 4);
2933         }
2934
2935         /* finder_flags */
2936         memcpy(&attr_data->attr_data.aapl.finder_info[0] + 8,
2937                &ai.afpi_FinderInfo[8], 2);
2938
2939         /* finder_ext_flags */
2940         memcpy(&attr_data->attr_data.aapl.finder_info[0] + 10,
2941                &ai.afpi_FinderInfo[24], 2);
2942
2943         /* creation date */
2944         date_added = convert_time_t_to_uint32_t(
2945                 smb_fname->st.st_ex_btime.tv_sec - AD_DATE_DELTA);
2946
2947         RSIVAL(&attr_data->attr_data.aapl.finder_info[0], 12, date_added);
2948
2949         return true;
2950 }
2951
2952 static uint64_t readdir_attr_rfork_size_adouble(
2953         struct vfs_handle_struct *handle,
2954         const struct smb_filename *smb_fname)
2955 {
2956         struct adouble *ad = NULL;
2957         uint64_t rfork_size;
2958
2959         ad = ad_get(talloc_tos(), handle, smb_fname,
2960                     ADOUBLE_RSRC);
2961         if (ad == NULL) {
2962                 return 0;
2963         }
2964
2965         rfork_size = ad_getentrylen(ad, ADEID_RFORK);
2966         TALLOC_FREE(ad);
2967
2968         return rfork_size;
2969 }
2970
2971 static uint64_t readdir_attr_rfork_size_stream(
2972         struct vfs_handle_struct *handle,
2973         const struct smb_filename *smb_fname)
2974 {
2975         struct smb_filename *stream_name = NULL;
2976         int ret;
2977         uint64_t rfork_size;
2978
2979         stream_name = synthetic_smb_fname(talloc_tos(),
2980                                           smb_fname->base_name,
2981                                           AFPRESOURCE_STREAM_NAME,
2982                                           NULL, 0);
2983         if (stream_name == NULL) {
2984                 return 0;
2985         }
2986
2987         ret = SMB_VFS_STAT(handle->conn, stream_name);
2988         if (ret != 0) {
2989                 TALLOC_FREE(stream_name);
2990                 return 0;
2991         }
2992
2993         rfork_size = stream_name->st.st_ex_size;
2994         TALLOC_FREE(stream_name);
2995
2996         return rfork_size;
2997 }
2998
2999 static uint64_t readdir_attr_rfork_size(struct vfs_handle_struct *handle,
3000                                         const struct smb_filename *smb_fname)
3001 {
3002         struct fruit_config_data *config = NULL;
3003         uint64_t rfork_size;
3004
3005         SMB_VFS_HANDLE_GET_DATA(handle, config,
3006                                 struct fruit_config_data,
3007                                 return 0);
3008
3009         switch (config->rsrc) {
3010         case FRUIT_RSRC_ADFILE:
3011         case FRUIT_RSRC_XATTR:
3012                 rfork_size = readdir_attr_rfork_size_adouble(handle,
3013                                                              smb_fname);
3014                 break;
3015
3016         case FRUIT_META_STREAM:
3017                 rfork_size = readdir_attr_rfork_size_stream(handle,
3018                                                             smb_fname);
3019                 break;
3020
3021         default:
3022                 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3023                 rfork_size = 0;
3024                 break;
3025         }
3026
3027         return rfork_size;
3028 }
3029
3030 static NTSTATUS readdir_attr_macmeta(struct vfs_handle_struct *handle,
3031                                      const struct smb_filename *smb_fname,
3032                                      struct readdir_attr_data *attr_data)
3033 {
3034         NTSTATUS status = NT_STATUS_OK;
3035         struct fruit_config_data *config = NULL;
3036         bool ok;
3037
3038         SMB_VFS_HANDLE_GET_DATA(handle, config,
3039                                 struct fruit_config_data,
3040                                 return NT_STATUS_UNSUCCESSFUL);
3041
3042
3043         /* Ensure we return a default value in the creation_date field */
3044         RSIVAL(&attr_data->attr_data.aapl.finder_info, 12, AD_DATE_START);
3045
3046         /*
3047          * Resource fork length
3048          */
3049
3050         if (config->readdir_attr_rsize) {
3051                 uint64_t rfork_size;
3052
3053                 rfork_size = readdir_attr_rfork_size(handle, smb_fname);
3054                 attr_data->attr_data.aapl.rfork_size = rfork_size;
3055         }
3056
3057         /*
3058          * FinderInfo
3059          */
3060
3061         if (config->readdir_attr_finder_info) {
3062                 ok = readdir_attr_meta_finderi(handle, smb_fname, attr_data);
3063                 if (!ok) {
3064                         status = NT_STATUS_INTERNAL_ERROR;
3065                 }
3066         }
3067
3068         return status;
3069 }
3070
3071 static NTSTATUS remove_virtual_nfs_aces(struct security_descriptor *psd)
3072 {
3073         NTSTATUS status;
3074         uint32_t i;
3075
3076         if (psd->dacl == NULL) {
3077                 return NT_STATUS_OK;
3078         }
3079
3080         for (i = 0; i < psd->dacl->num_aces; i++) {
3081                 /* MS NFS style mode/uid/gid */
3082                 int cmp = dom_sid_compare_domain(
3083                                 &global_sid_Unix_NFS,
3084                                 &psd->dacl->aces[i].trustee);
3085                 if (cmp != 0) {
3086                         /* Normal ACE entry. */
3087                         continue;
3088                 }
3089
3090                 /*
3091                  * security_descriptor_dacl_del()
3092                  * *must* return NT_STATUS_OK as we know
3093                  * we have something to remove.
3094                  */
3095
3096                 status = security_descriptor_dacl_del(psd,
3097                                 &psd->dacl->aces[i].trustee);
3098                 if (!NT_STATUS_IS_OK(status)) {
3099                         DBG_WARNING("failed to remove MS NFS style ACE: %s\n",
3100                                 nt_errstr(status));
3101                         return status;
3102                 }
3103
3104                 /*
3105                  * security_descriptor_dacl_del() may delete more
3106                  * then one entry subsequent to this one if the
3107                  * SID matches, but we only need to ensure that
3108                  * we stay looking at the same element in the array.
3109                  */
3110                 i--;
3111         }
3112         return NT_STATUS_OK;
3113 }
3114
3115 /* Search MS NFS style ACE with UNIX mode */
3116 static NTSTATUS check_ms_nfs(vfs_handle_struct *handle,
3117                              files_struct *fsp,
3118                              struct security_descriptor *psd,
3119                              mode_t *pmode,
3120                              bool *pdo_chmod)
3121 {
3122         uint32_t i;
3123         struct fruit_config_data *config = NULL;
3124
3125         *pdo_chmod = false;
3126
3127         SMB_VFS_HANDLE_GET_DATA(handle, config,
3128                                 struct fruit_config_data,
3129                                 return NT_STATUS_UNSUCCESSFUL);
3130
3131         if (!global_fruit_config.nego_aapl) {
3132                 return NT_STATUS_OK;
3133         }
3134         if (psd->dacl == NULL || !config->unix_info_enabled) {
3135                 return NT_STATUS_OK;
3136         }
3137
3138         for (i = 0; i < psd->dacl->num_aces; i++) {
3139                 if (dom_sid_compare_domain(
3140                             &global_sid_Unix_NFS_Mode,
3141                             &psd->dacl->aces[i].trustee) == 0) {
3142                         *pmode = (mode_t)psd->dacl->aces[i].trustee.sub_auths[2];
3143                         *pmode &= (S_IRWXU | S_IRWXG | S_IRWXO);
3144                         *pdo_chmod = true;
3145
3146                         DEBUG(10, ("MS NFS chmod request %s, %04o\n",
3147                                    fsp_str_dbg(fsp), (unsigned)(*pmode)));
3148                         break;
3149                 }
3150         }
3151
3152         /*
3153          * Remove any incoming virtual ACE entries generated by
3154          * fruit_fget_nt_acl().
3155          */
3156
3157         return remove_virtual_nfs_aces(psd);
3158 }
3159
3160 /****************************************************************************
3161  * VFS ops
3162  ****************************************************************************/
3163
3164 static int fruit_connect(vfs_handle_struct *handle,
3165                          const char *service,
3166                          const char *user)
3167 {
3168         int rc;
3169         char *list = NULL, *newlist = NULL;
3170         struct fruit_config_data *config;
3171
3172         DEBUG(10, ("fruit_connect\n"));
3173
3174         rc = SMB_VFS_NEXT_CONNECT(handle, service, user);
3175         if (rc < 0) {
3176                 return rc;
3177         }
3178
3179         rc = init_fruit_config(handle);
3180         if (rc != 0) {
3181                 return rc;
3182         }
3183
3184         SMB_VFS_HANDLE_GET_DATA(handle, config,
3185                                 struct fruit_config_data, return -1);
3186
3187         if (config->veto_appledouble) {
3188                 list = lp_veto_files(talloc_tos(), SNUM(handle->conn));
3189
3190                 if (list) {
3191                         if (strstr(list, "/" ADOUBLE_NAME_PREFIX "*/") == NULL) {
3192                                 newlist = talloc_asprintf(
3193                                         list,
3194                                         "%s/" ADOUBLE_NAME_PREFIX "*/",
3195                                         list);
3196                                 lp_do_parameter(SNUM(handle->conn),
3197                                                 "veto files",
3198                                                 newlist);
3199                         }
3200                 } else {
3201                         lp_do_parameter(SNUM(handle->conn),
3202                                         "veto files",
3203                                         "/" ADOUBLE_NAME_PREFIX "*/");
3204                 }
3205
3206                 TALLOC_FREE(list);
3207         }
3208
3209         if (config->encoding == FRUIT_ENC_NATIVE) {
3210                 lp_do_parameter(SNUM(handle->conn),
3211                                 "catia:mappings",
3212                                 fruit_catia_maps);
3213         }
3214
3215         if (config->time_machine) {
3216                 DBG_NOTICE("Enabling durable handles for Time Machine "
3217                            "support on [%s]\n", service);
3218                 lp_do_parameter(SNUM(handle->conn), "durable handles", "yes");
3219                 lp_do_parameter(SNUM(handle->conn), "kernel oplocks", "no");
3220                 lp_do_parameter(SNUM(handle->conn), "kernel share modes", "no");
3221                 if (!lp_strict_sync(SNUM(handle->conn))) {
3222                         DBG_WARNING("Time Machine without strict sync is not "
3223                                     "recommended!\n");
3224                 }
3225                 lp_do_parameter(SNUM(handle->conn), "posix locking", "no");
3226         }
3227
3228         return rc;
3229 }
3230
3231 static int fruit_open_meta_stream(vfs_handle_struct *handle,
3232                                   struct smb_filename *smb_fname,
3233                                   files_struct *fsp,
3234                                   int flags,
3235                                   mode_t mode)
3236 {
3237         AfpInfo *ai = NULL;
3238         char afpinfo_buf[AFP_INFO_SIZE];
3239         ssize_t len, written;
3240         int hostfd = -1;
3241         int rc = -1;
3242
3243         hostfd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3244         if (hostfd == -1) {
3245                 return -1;
3246         }
3247
3248         if (!(flags & (O_CREAT | O_TRUNC))) {
3249                 return hostfd;
3250         }
3251
3252         ai = afpinfo_new(talloc_tos());
3253         if (ai == NULL) {
3254                 rc = -1;
3255                 goto fail;
3256         }
3257
3258         len = afpinfo_pack(ai, afpinfo_buf);
3259         if (len != AFP_INFO_SIZE) {
3260                 rc = -1;
3261                 goto fail;
3262         }
3263
3264         /* Set fd, needed in SMB_VFS_NEXT_PWRITE() */
3265         fsp->fh->fd = hostfd;
3266
3267         written = SMB_VFS_NEXT_PWRITE(handle, fsp, afpinfo_buf,
3268                                       AFP_INFO_SIZE, 0);
3269         fsp->fh->fd = -1;
3270         if (written != AFP_INFO_SIZE) {
3271                 DBG_ERR("bad write [%zd/%d]\n", written, AFP_INFO_SIZE);
3272                 rc = -1;
3273                 goto fail;
3274         }
3275
3276         rc = 0;
3277
3278 fail:
3279         DBG_DEBUG("rc=%d, fd=%d\n", rc, hostfd);
3280
3281         if (rc != 0) {
3282                 int saved_errno = errno;
3283                 if (hostfd >= 0) {
3284                         fsp->fh->fd = hostfd;
3285                         SMB_VFS_NEXT_CLOSE(handle, fsp);
3286                 }
3287                 hostfd = -1;
3288                 errno = saved_errno;
3289         }
3290         return hostfd;
3291 }
3292
3293 static int fruit_open_meta_netatalk(vfs_handle_struct *handle,
3294                                     struct smb_filename *smb_fname,
3295                                     files_struct *fsp,
3296                                     int flags,
3297                                     mode_t mode)
3298 {
3299         int rc;
3300         int fakefd = -1;
3301         struct adouble *ad = NULL;
3302         int fds[2];
3303
3304         DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3305
3306         /*
3307          * Return a valid fd, but ensure any attempt to use it returns an error
3308          * (EPIPE). All operations on the smb_fname or the fsp will use path
3309          * based syscalls.
3310          */
3311         rc = pipe(fds);
3312         if (rc != 0) {
3313                 goto exit;
3314         }
3315         fakefd = fds[0];
3316         close(fds[1]);
3317
3318         if (flags & (O_CREAT | O_TRUNC)) {
3319                 /*
3320                  * The attribute does not exist or needs to be truncated,
3321                  * create an AppleDouble EA
3322                  */
3323                 ad = ad_init(fsp, handle, ADOUBLE_META);
3324                 if (ad == NULL) {
3325                         rc = -1;
3326                         goto exit;
3327                 }
3328
3329                 rc = ad_set(ad, fsp->fsp_name);
3330                 if (rc != 0) {
3331                         rc = -1;
3332                         goto exit;
3333                 }
3334
3335                 TALLOC_FREE(ad);
3336         }
3337
3338 exit:
3339         DEBUG(10, ("fruit_open meta rc=%d, fd=%d\n", rc, fakefd));
3340         if (rc != 0) {
3341                 int saved_errno = errno;
3342                 if (fakefd >= 0) {
3343                         close(fakefd);
3344                 }
3345                 fakefd = -1;
3346                 errno = saved_errno;
3347         }
3348         return fakefd;
3349 }
3350
3351 static int fruit_open_meta(vfs_handle_struct *handle,
3352                            struct smb_filename *smb_fname,
3353                            files_struct *fsp, int flags, mode_t mode)
3354 {
3355         int fd;
3356         struct fruit_config_data *config = NULL;
3357         struct fio *fio = NULL;
3358
3359         DBG_DEBUG("path [%s]\n", smb_fname_str_dbg(smb_fname));
3360
3361         SMB_VFS_HANDLE_GET_DATA(handle, config,
3362                                 struct fruit_config_data, return -1);
3363
3364         switch (config->meta) {
3365         case FRUIT_META_STREAM:
3366                 fd = fruit_open_meta_stream(handle, smb_fname,
3367                                             fsp, flags, mode);
3368                 break;
3369
3370         case FRUIT_META_NETATALK:
3371                 fd = fruit_open_meta_netatalk(handle, smb_fname,
3372                                               fsp, flags, mode);
3373                 break;
3374
3375         default:
3376                 DBG_ERR("Unexpected meta config [%d]\n", config->meta);
3377                 return -1;
3378         }
3379
3380         DBG_DEBUG("path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3381
3382         if (fd == -1) {
3383                 return -1;
3384         }
3385
3386         fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3387         fio->type = ADOUBLE_META;
3388         fio->config = config;
3389
3390         return fd;
3391 }
3392
3393 static int fruit_open_rsrc_adouble(vfs_handle_struct *handle,
3394                                    struct smb_filename *smb_fname,
3395                                    files_struct *fsp,
3396                                    int flags,
3397                                    mode_t mode)
3398 {
3399         int rc = 0;
3400         struct adouble *ad = NULL;
3401         struct smb_filename *smb_fname_base = NULL;
3402         struct fruit_config_data *config = NULL;
3403         int hostfd = -1;
3404
3405         SMB_VFS_HANDLE_GET_DATA(handle, config,
3406                                 struct fruit_config_data, return -1);
3407
3408         if ((!(flags & O_CREAT)) &&
3409             S_ISDIR(fsp->base_fsp->fsp_name->st.st_ex_mode))
3410         {
3411                 /* sorry, but directories don't habe a resource fork */
3412                 rc = -1;
3413                 goto exit;
3414         }
3415
3416         rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_base);
3417         if (rc != 0) {
3418                 goto exit;
3419         }
3420
3421         /* Sanitize flags */
3422         if (flags & O_WRONLY) {
3423                 /* We always need read access for the metadata header too */
3424                 flags &= ~O_WRONLY;
3425                 flags |= O_RDWR;
3426         }
3427
3428         hostfd = SMB_VFS_NEXT_OPEN(handle, smb_fname_base, fsp,
3429                                    flags, mode);
3430         if (hostfd == -1) {
3431                 rc = -1;
3432                 goto exit;
3433         }
3434
3435         if (flags & (O_CREAT | O_TRUNC)) {
3436                 ad = ad_init(fsp, handle, ADOUBLE_RSRC);
3437                 if (ad == NULL) {
3438                         rc = -1;
3439                         goto exit;
3440                 }
3441
3442                 fsp->fh->fd = hostfd;
3443
3444                 rc = ad_fset(ad, fsp);
3445                 fsp->fh->fd = -1;
3446                 if (rc != 0) {
3447                         rc = -1;
3448                         goto exit;
3449                 }
3450                 TALLOC_FREE(ad);
3451         }
3452
3453 exit:
3454
3455         TALLOC_FREE(smb_fname_base);
3456
3457         DEBUG(10, ("fruit_open resource fork: rc=%d, fd=%d\n", rc, hostfd));
3458         if (rc != 0) {
3459                 int saved_errno = errno;
3460                 if (hostfd >= 0) {
3461                         /*
3462                          * BUGBUGBUG -- we would need to call
3463                          * fd_close_posix here, but we don't have a
3464                          * full fsp yet
3465                          */
3466                         fsp->fh->fd = hostfd;
3467                         SMB_VFS_CLOSE(fsp);
3468                 }
3469                 hostfd = -1;
3470                 errno = saved_errno;
3471         }
3472         return hostfd;
3473 }
3474
3475 static int fruit_open_rsrc_xattr(vfs_handle_struct *handle,
3476                                  struct smb_filename *smb_fname,
3477                                  files_struct *fsp,
3478                                  int flags,
3479                                  mode_t mode)
3480 {
3481 #ifdef HAVE_ATTROPEN
3482         int fd = -1;
3483
3484         fd = attropen(smb_fname->base_name,
3485                       AFPRESOURCE_EA_NETATALK,
3486                       flags,
3487                       mode);
3488         if (fd == -1) {
3489                 return -1;
3490         }
3491
3492         return fd;
3493
3494 #else
3495         errno = ENOSYS;
3496         return -1;
3497 #endif
3498 }
3499
3500 static int fruit_open_rsrc(vfs_handle_struct *handle,
3501                            struct smb_filename *smb_fname,
3502                            files_struct *fsp, int flags, mode_t mode)
3503 {
3504         int fd;
3505         struct fruit_config_data *config = NULL;
3506         struct fio *fio = NULL;
3507
3508         DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3509
3510         SMB_VFS_HANDLE_GET_DATA(handle, config,
3511                                 struct fruit_config_data, return -1);
3512
3513         if (((flags & O_ACCMODE) == O_RDONLY)
3514             && (flags & O_CREAT)
3515             && !VALID_STAT(fsp->fsp_name->st))
3516         {
3517                 /*
3518                  * This means the stream doesn't exist. macOS SMB server fails
3519                  * this with NT_STATUS_OBJECT_NAME_NOT_FOUND, so must we. Cf bug
3520                  * 12565 and the test for this combination in
3521                  * test_rfork_create().
3522                  */
3523                 errno = ENOENT;
3524                 return -1;
3525         }
3526
3527         switch (config->rsrc) {
3528         case FRUIT_RSRC_STREAM:
3529                 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3530                 break;
3531
3532         case FRUIT_RSRC_ADFILE:
3533                 fd = fruit_open_rsrc_adouble(handle, smb_fname,
3534                                              fsp, flags, mode);
3535                 break;
3536
3537         case FRUIT_RSRC_XATTR:
3538                 fd = fruit_open_rsrc_xattr(handle, smb_fname,
3539                                            fsp, flags, mode);
3540                 break;
3541
3542         default:
3543                 DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
3544                 return -1;
3545         }
3546
3547         DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3548
3549         if (fd == -1) {
3550                 return -1;
3551         }
3552
3553         fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
3554         fio->type = ADOUBLE_RSRC;
3555         fio->config = config;
3556
3557         return fd;
3558 }
3559
3560 static int fruit_open(vfs_handle_struct *handle,
3561                       struct smb_filename *smb_fname,
3562                       files_struct *fsp, int flags, mode_t mode)
3563 {
3564         int fd;
3565
3566         DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
3567
3568         if (!is_ntfs_stream_smb_fname(smb_fname)) {
3569                 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3570         }
3571
3572         if (is_afpinfo_stream(smb_fname)) {
3573                 fd = fruit_open_meta(handle, smb_fname, fsp, flags, mode);
3574         } else if (is_afpresource_stream(smb_fname)) {
3575                 fd = fruit_open_rsrc(handle, smb_fname, fsp, flags, mode);
3576         } else {
3577                 fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
3578         }
3579
3580         DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
3581
3582         return fd;
3583 }
3584
3585 static int fruit_rename(struct vfs_handle_struct *handle,
3586                         const struct smb_filename *smb_fname_src,
3587                         const struct smb_filename *smb_fname_dst)
3588 {
3589         int rc = -1;
3590         struct fruit_config_data *config = NULL;
3591         struct smb_filename *src_adp_smb_fname = NULL;
3592         struct smb_filename *dst_adp_smb_fname = NULL;
3593
3594         SMB_VFS_HANDLE_GET_DATA(handle, config,
3595                                 struct fruit_config_data, return -1);
3596
3597         if (!VALID_STAT(smb_fname_src->st)) {
3598                 DBG_ERR("Need valid stat for [%s]\n",
3599                         smb_fname_str_dbg(smb_fname_src));
3600                 return -1;
3601         }
3602
3603         rc = SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
3604         if (rc != 0) {
3605                 return -1;
3606         }
3607
3608         if ((config->rsrc != FRUIT_RSRC_ADFILE) ||
3609             (!S_ISREG(smb_fname_src->st.st_ex_mode)))
3610         {
3611                 return 0;
3612         }
3613
3614         rc = adouble_path(talloc_tos(), smb_fname_src, &src_adp_smb_fname);
3615         if (rc != 0) {
3616                 goto done;
3617         }
3618
3619         rc = adouble_path(talloc_tos(), smb_fname_dst, &dst_adp_smb_fname);
3620         if (rc != 0) {
3621                 goto done;
3622         }
3623
3624         DBG_DEBUG("%s -> %s\n",
3625                   smb_fname_str_dbg(src_adp_smb_fname),
3626                   smb_fname_str_dbg(dst_adp_smb_fname));
3627
3628         rc = SMB_VFS_NEXT_RENAME(handle, src_adp_smb_fname, dst_adp_smb_fname);
3629         if (errno == ENOENT) {
3630                 rc = 0;
3631         }
3632
3633 done:
3634         TALLOC_FREE(src_adp_smb_fname);
3635         TALLOC_FREE(dst_adp_smb_fname);
3636         return rc;
3637 }
3638
3639 static int fruit_unlink_meta_stream(vfs_handle_struct *handle,
3640                                     const struct smb_filename *smb_fname)
3641 {
3642         return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3643 }
3644
3645 static int fruit_unlink_meta_netatalk(vfs_handle_struct *handle,
3646                                       const struct smb_filename *smb_fname)
3647 {
3648         return SMB_VFS_REMOVEXATTR(handle->conn,
3649                                    smb_fname,
3650                                    AFPINFO_EA_NETATALK);
3651 }
3652
3653 static int fruit_unlink_meta(vfs_handle_struct *handle,
3654                              const struct smb_filename *smb_fname)
3655 {
3656         struct fruit_config_data *config = NULL;
3657         int rc;
3658
3659         SMB_VFS_HANDLE_GET_DATA(handle, config,
3660                                 struct fruit_config_data, return -1);
3661
3662         switch (config->meta) {
3663         case FRUIT_META_STREAM:
3664                 rc = fruit_unlink_meta_stream(handle, smb_fname);
3665                 break;
3666
3667         case FRUIT_META_NETATALK:
3668                 rc = fruit_unlink_meta_netatalk(handle, smb_fname);
3669                 break;
3670
3671         default:
3672                 DBG_ERR("Unsupported meta config [%d]\n", config->meta);
3673                 return -1;
3674         }
3675
3676         return rc;
3677 }
3678
3679 static int fruit_unlink_rsrc_stream(vfs_handle_struct *handle,
3680                                     const struct smb_filename *smb_fname,
3681                                     bool force_unlink)
3682 {
3683         int ret;
3684
3685         if (!force_unlink) {
3686                 struct smb_filename *smb_fname_cp = NULL;
3687                 off_t size;
3688
3689                 smb_fname_cp = cp_smb_filename(talloc_tos(), smb_fname);
3690                 if (smb_fname_cp == NULL) {
3691                         return -1;
3692                 }
3693
3694                 /*
3695                  * 0 byte resource fork streams are not listed by
3696                  * vfs_streaminfo, as a result stream cleanup/deletion of file
3697                  * deletion doesn't remove the resourcefork stream.
3698                  */
3699
3700                 ret = SMB_VFS_NEXT_STAT(handle, smb_fname_cp);
3701                 if (ret != 0) {
3702                         TALLOC_FREE(smb_fname_cp);
3703                         DBG_ERR("stat [%s] failed [%s]\n",
3704                                 smb_fname_str_dbg(smb_fname_cp), strerror(errno));
3705                         return -1;
3706                 }
3707
3708                 size = smb_fname_cp->st.st_ex_size;
3709                 TALLOC_FREE(smb_fname_cp);
3710
3711                 if (size > 0) {
3712                         /* OS X ignores resource fork stream delete requests */
3713                         return 0;
3714                 }
3715         }
3716
3717         ret = SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3718         if ((ret != 0) && (errno == ENOENT) && force_unlink) {
3719                 ret = 0;
3720         }
3721
3722         return ret;
3723 }
3724
3725 static int fruit_unlink_rsrc_adouble(vfs_handle_struct *handle,
3726                                      const struct smb_filename *smb_fname,
3727                                      bool force_unlink)
3728 {
3729         int rc;
3730         struct adouble *ad = NULL;
3731         struct smb_filename *adp_smb_fname = NULL;
3732
3733         if (!force_unlink) {
3734                 ad = ad_get(talloc_tos(), handle, smb_fname,
3735                             ADOUBLE_RSRC);
3736                 if (ad == NULL) {
3737                         errno = ENOENT;
3738                         return -1;
3739                 }
3740
3741
3742                 /*
3743                  * 0 byte resource fork streams are not listed by
3744                  * vfs_streaminfo, as a result stream cleanup/deletion of file
3745                  * deletion doesn't remove the resourcefork stream.
3746                  */
3747
3748                 if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
3749                         /* OS X ignores resource fork stream delete requests */
3750                         TALLOC_FREE(ad);
3751                         return 0;
3752                 }
3753
3754                 TALLOC_FREE(ad);
3755         }
3756
3757         rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
3758         if (rc != 0) {
3759                 return -1;
3760         }
3761
3762         rc = SMB_VFS_NEXT_UNLINK(handle, adp_smb_fname);
3763         TALLOC_FREE(adp_smb_fname);
3764         if ((rc != 0) && (errno == ENOENT) && force_unlink) {
3765                 rc = 0;
3766         }
3767
3768         return rc;
3769 }
3770
3771 static int fruit_unlink_rsrc_xattr(vfs_handle_struct *handle,
3772                                    const struct smb_filename *smb_fname,
3773                                    bool force_unlink)
3774 {
3775         /*
3776          * OS X ignores resource fork stream delete requests, so nothing to do
3777          * here. Removing the file will remove the xattr anyway, so we don't
3778          * have to take care of removing 0 byte resource forks that could be
3779          * left behind.
3780          */
3781         return 0;
3782 }
3783
3784 static int fruit_unlink_rsrc(vfs_handle_struct *handle,
3785                              const struct smb_filename *smb_fname,
3786                              bool force_unlink)
3787 {
3788         struct fruit_config_data *config = NULL;
3789         int rc;
3790
3791         SMB_VFS_HANDLE_GET_DATA(handle, config,
3792                                 struct fruit_config_data, return -1);
3793
3794         switch (config->rsrc) {
3795         case FRUIT_RSRC_STREAM:
3796                 rc = fruit_unlink_rsrc_stream(handle, smb_fname, force_unlink);
3797                 break;
3798
3799         case FRUIT_RSRC_ADFILE:
3800                 rc = fruit_unlink_rsrc_adouble(handle, smb_fname, force_unlink);
3801                 break;
3802
3803         case FRUIT_RSRC_XATTR:
3804                 rc = fruit_unlink_rsrc_xattr(handle, smb_fname, force_unlink);
3805                 break;
3806
3807         default:
3808                 DBG_ERR("Unsupported rsrc config [%d]\n", config->rsrc);
3809                 return -1;
3810         }
3811
3812         return rc;
3813 }
3814
3815 static int fruit_unlink(vfs_handle_struct *handle,
3816                         const struct smb_filename *smb_fname)
3817 {
3818         int rc;
3819         struct fruit_config_data *config = NULL;
3820         struct smb_filename *rsrc_smb_fname = NULL;
3821
3822         SMB_VFS_HANDLE_GET_DATA(handle, config,
3823                                 struct fruit_config_data, return -1);
3824
3825         if (is_afpinfo_stream(smb_fname)) {
3826                 return fruit_unlink_meta(handle, smb_fname);
3827         } else if (is_afpresource_stream(smb_fname)) {
3828                 return fruit_unlink_rsrc(handle, smb_fname, false);
3829         } if (is_ntfs_stream_smb_fname(smb_fname)) {
3830                 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3831         }
3832
3833         /*
3834          * A request to delete the base file. Because 0 byte resource
3835          * fork streams are not listed by fruit_streaminfo,
3836          * delete_all_streams() can't remove 0 byte resource fork
3837          * streams, so we have to cleanup this here.
3838          */
3839         rsrc_smb_fname = synthetic_smb_fname(talloc_tos(),
3840                                              smb_fname->base_name,
3841                                              AFPRESOURCE_STREAM_NAME,
3842                                              NULL,
3843                                              smb_fname->flags);
3844         if (rsrc_smb_fname == NULL) {
3845                 return -1;
3846         }
3847
3848         rc = fruit_unlink_rsrc(handle, rsrc_smb_fname, true);
3849         if ((rc != 0) && (errno != ENOENT)) {
3850                 DBG_ERR("Forced unlink of [%s] failed [%s]\n",
3851                         smb_fname_str_dbg(rsrc_smb_fname), strerror(errno));
3852                 TALLOC_FREE(rsrc_smb_fname);
3853                 return -1;
3854         }
3855         TALLOC_FREE(rsrc_smb_fname);
3856
3857         return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
3858 }
3859
3860 static int fruit_chmod(vfs_handle_struct *handle,
3861                        const struct smb_filename *smb_fname,
3862                        mode_t mode)
3863 {
3864         int rc = -1;
3865         struct fruit_config_data *config = NULL;
3866         struct smb_filename *smb_fname_adp = NULL;
3867
3868         rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname, mode);
3869         if (rc != 0) {
3870                 return rc;
3871         }
3872
3873         SMB_VFS_HANDLE_GET_DATA(handle, config,
3874                                 struct fruit_config_data, return -1);
3875
3876         if (config->rsrc != FRUIT_RSRC_ADFILE) {
3877                 return 0;
3878         }
3879
3880         if (!VALID_STAT(smb_fname->st)) {
3881                 return 0;
3882         }
3883
3884         if (!S_ISREG(smb_fname->st.st_ex_mode)) {
3885                 return 0;
3886         }
3887
3888         rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_adp);
3889         if (rc != 0) {
3890                 return -1;
3891         }
3892
3893         DEBUG(10, ("fruit_chmod: %s\n", smb_fname_adp->base_name));
3894
3895         rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname_adp, mode);
3896         if (errno == ENOENT) {
3897                 rc = 0;
3898         }
3899
3900         TALLOC_FREE(smb_fname_adp);
3901         return rc;
3902 }
3903
3904 static int fruit_chown(vfs_handle_struct *handle,
3905                        const struct smb_filename *smb_fname,
3906                        uid_t uid,
3907                        gid_t gid)
3908 {
3909         int rc = -1;
3910         struct fruit_config_data *config = NULL;
3911         struct smb_filename *adp_smb_fname = NULL;
3912
3913         rc = SMB_VFS_NEXT_CHOWN(handle, smb_fname, uid, gid);
3914         if (rc != 0) {
3915                 return rc;
3916         }
3917
3918         SMB_VFS_HANDLE_GET_DATA(handle, config,
3919                                 struct fruit_config_data, return -1);
3920
3921         if (config->rsrc != FRUIT_RSRC_ADFILE) {
3922                 return 0;
3923         }
3924
3925         if (!VALID_STAT(smb_fname->st)) {
3926                 return 0;
3927         }
3928
3929         if (!S_ISREG(smb_fname->st.st_ex_mode)) {
3930                 return 0;
3931         }
3932
3933         rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
3934         if (rc != 0) {
3935                 goto done;
3936         }
3937
3938         DEBUG(10, ("fruit_chown: %s\n", adp_smb_fname->base_name));
3939
3940         rc = SMB_VFS_NEXT_CHOWN(handle, adp_smb_fname, uid, gid);
3941         if (errno == ENOENT) {
3942                 rc = 0;
3943         }
3944
3945  done:
3946         TALLOC_FREE(adp_smb_fname);
3947         return rc;
3948 }
3949
3950 static int fruit_rmdir(struct vfs_handle_struct *handle,
3951                         const struct smb_filename *smb_fname)
3952 {
3953         DIR *dh = NULL;
3954         struct dirent *de;
3955         struct fruit_config_data *config;
3956
3957         SMB_VFS_HANDLE_GET_DATA(handle, config,
3958                                 struct fruit_config_data, return -1);
3959
3960         if (config->rsrc != FRUIT_RSRC_ADFILE) {
3961                 goto exit_rmdir;
3962         }
3963
3964         /*
3965          * Due to there is no way to change bDeleteVetoFiles variable
3966          * from this module, need to clean up ourselves
3967          */
3968
3969         dh = SMB_VFS_OPENDIR(handle->conn, smb_fname, NULL,