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