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