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