VFS: Modify rmdir to take a const struct smb_filename * instead of const char *
[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 char *path,
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
2500         rc = SMB_VFS_NEXT_CHMOD(handle, path, mode);
2501         if (rc != 0) {
2502                 return rc;
2503         }
2504
2505         SMB_VFS_HANDLE_GET_DATA(handle, config,
2506                                 struct fruit_config_data, return -1);
2507
2508         if (config->rsrc == FRUIT_RSRC_XATTR) {
2509                 return 0;
2510         }
2511
2512         /* FIXME: direct sys_lstat(), missing smb_fname */
2513         rc = sys_lstat(path, &sb, false);
2514         if (rc != 0 || !S_ISREG(sb.st_ex_mode)) {
2515                 return rc;
2516         }
2517
2518         rc = adouble_path(talloc_tos(), path, &adp);
2519         if (rc != 0) {
2520                 return -1;
2521         }
2522
2523         DEBUG(10, ("fruit_chmod: %s\n", adp));
2524
2525         rc = SMB_VFS_NEXT_CHMOD(handle, adp, mode);
2526         if (errno == ENOENT) {
2527                 rc = 0;
2528         }
2529
2530         TALLOC_FREE(adp);
2531         return rc;
2532 }
2533
2534 static int fruit_chown(vfs_handle_struct *handle,
2535                        const char *path,
2536                        uid_t uid,
2537                        gid_t gid)
2538 {
2539         int rc = -1;
2540         char *adp = NULL;
2541         struct fruit_config_data *config = NULL;
2542         SMB_STRUCT_STAT sb;
2543
2544         rc = SMB_VFS_NEXT_CHOWN(handle, path, uid, gid);
2545         if (rc != 0) {
2546                 return rc;
2547         }
2548
2549         SMB_VFS_HANDLE_GET_DATA(handle, config,
2550                                 struct fruit_config_data, return -1);
2551
2552         if (config->rsrc == FRUIT_RSRC_XATTR) {
2553                 return rc;
2554         }
2555
2556         /* FIXME: direct sys_lstat(), missing smb_fname */
2557         rc = sys_lstat(path, &sb, false);
2558         if (rc != 0 || !S_ISREG(sb.st_ex_mode)) {
2559                 return rc;
2560         }
2561
2562         rc = adouble_path(talloc_tos(), path, &adp);
2563         if (rc != 0) {
2564                 goto done;
2565         }
2566
2567         DEBUG(10, ("fruit_chown: %s\n", adp));
2568
2569         rc = SMB_VFS_NEXT_CHOWN(handle, adp, uid, gid);
2570         if (errno == ENOENT) {
2571                 rc = 0;
2572         }
2573
2574  done:
2575         TALLOC_FREE(adp);
2576         return rc;
2577 }
2578
2579 static int fruit_rmdir(struct vfs_handle_struct *handle,
2580                         const struct smb_filename *smb_fname)
2581 {
2582         DIR *dh = NULL;
2583         struct dirent *de;
2584         struct fruit_config_data *config;
2585         const char *path = smb_fname->base_name;
2586
2587         SMB_VFS_HANDLE_GET_DATA(handle, config,
2588                                 struct fruit_config_data, return -1);
2589
2590         if (!handle->conn->cwd || !path || (config->rsrc == FRUIT_RSRC_XATTR)) {
2591                 goto exit_rmdir;
2592         }
2593
2594         /*
2595          * Due to there is no way to change bDeleteVetoFiles variable
2596          * from this module, need to clean up ourselves
2597          */
2598         dh = opendir(path);
2599         if (dh == NULL) {
2600                 goto exit_rmdir;
2601         }
2602
2603         while ((de = readdir(dh)) != NULL) {
2604                 if ((strncmp(de->d_name,
2605                              ADOUBLE_NAME_PREFIX,
2606                              strlen(ADOUBLE_NAME_PREFIX))) == 0) {
2607                         char *p = talloc_asprintf(talloc_tos(),
2608                                                   "%s/%s",
2609                                                   path, de->d_name);
2610                         if (p == NULL) {
2611                                 goto exit_rmdir;
2612                         }
2613                         DEBUG(10, ("fruit_rmdir: delete %s\n", p));
2614                         (void)unlink(p);
2615                         TALLOC_FREE(p);
2616                 }
2617         }
2618
2619 exit_rmdir:
2620         if (dh) {
2621                 closedir(dh);
2622         }
2623         return SMB_VFS_NEXT_RMDIR(handle, smb_fname);
2624 }
2625
2626 static ssize_t fruit_pread(vfs_handle_struct *handle,
2627                            files_struct *fsp, void *data,
2628                            size_t n, off_t offset)
2629 {
2630         int rc = 0;
2631         struct adouble *ad = (struct adouble *)VFS_FETCH_FSP_EXTENSION(
2632                 handle, fsp);
2633         struct fruit_config_data *config = NULL;
2634         AfpInfo *ai = NULL;
2635         ssize_t len = -1;
2636         char *name = NULL;
2637         char *tmp_base_name = NULL;
2638         NTSTATUS status;
2639
2640         DEBUG(10, ("fruit_pread: offset=%d, size=%d\n", (int)offset, (int)n));
2641
2642         if (!fsp->base_fsp) {
2643                 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
2644         }
2645
2646         SMB_VFS_HANDLE_GET_DATA(handle, config,
2647                                 struct fruit_config_data, return -1);
2648
2649         /* fsp_name is not converted with vfs_catia */
2650         tmp_base_name = fsp->base_fsp->fsp_name->base_name;
2651         status = SMB_VFS_TRANSLATE_NAME(handle->conn,
2652                                         fsp->base_fsp->fsp_name->base_name,
2653                                         vfs_translate_to_unix,
2654                                         talloc_tos(), &name);
2655         if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
2656                 name = talloc_strdup(talloc_tos(), tmp_base_name);
2657                 if (name == NULL) {
2658                         rc = -1;
2659                         goto exit;
2660                 }
2661         } else if (!NT_STATUS_IS_OK(status)) {
2662                 errno = map_errno_from_nt_status(status);
2663                 rc = -1;
2664                 goto exit;
2665         }
2666         fsp->base_fsp->fsp_name->base_name = name;
2667
2668         if (ad == NULL) {
2669                 len = SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
2670                 if (len == -1) {
2671                         rc = -1;
2672                         goto exit;
2673                 }
2674                 goto exit;
2675         }
2676
2677         if (!fruit_fsp_recheck(ad, fsp)) {
2678                 rc = -1;
2679                 goto exit;
2680         }
2681
2682         if (ad->ad_type == ADOUBLE_META) {
2683                 char afpinfo_buf[AFP_INFO_SIZE];
2684                 size_t to_return;
2685
2686                 /*
2687                  * OS X has a off-by-1 error in the offset calculation, so we're
2688                  * bug compatible here. It won't hurt, as any relevant real
2689                  * world read requests from the AFP_AfpInfo stream will be
2690                  * offset=0 n=60. offset is ignored anyway, see below.
2691                  */
2692                 if ((offset < 0) || (offset >= AFP_INFO_SIZE + 1)) {
2693                         len = 0;
2694                         rc = 0;
2695                         goto exit;
2696                 }
2697
2698                 to_return = MIN(n, AFP_INFO_SIZE);
2699
2700                 ai = afpinfo_new(talloc_tos());
2701                 if (ai == NULL) {
2702                         rc = -1;
2703                         goto exit;
2704                 }
2705
2706                 len = ad_read(ad, fsp->base_fsp->fsp_name->base_name);
2707                 if (len == -1) {
2708                         rc = -1;
2709                         goto exit;
2710                 }
2711
2712                 memcpy(&ai->afpi_FinderInfo[0],
2713                        ad_entry(ad, ADEID_FINDERI),
2714                        ADEDLEN_FINDERI);
2715                 len = afpinfo_pack(ai, afpinfo_buf);
2716                 if (len != AFP_INFO_SIZE) {
2717                         rc = -1;
2718                         goto exit;
2719                 }
2720
2721                 /*
2722                  * OS X ignores offset when reading from AFP_AfpInfo stream!
2723                  */
2724                 memcpy(data, afpinfo_buf, to_return);
2725                 len = to_return;
2726         } else {
2727                 len = SMB_VFS_NEXT_PREAD(
2728                         handle, fsp, data, n,
2729                         offset + ad_getentryoff(ad, ADEID_RFORK));
2730                 if (len == -1) {
2731                         rc = -1;
2732                         goto exit;
2733                 }
2734         }
2735 exit:
2736         fsp->base_fsp->fsp_name->base_name = tmp_base_name;
2737         TALLOC_FREE(name);
2738         TALLOC_FREE(ai);
2739         if (rc != 0) {
2740                 len = -1;
2741         }
2742         DEBUG(10, ("fruit_pread: rc=%d, len=%zd\n", rc, len));
2743         return len;
2744 }
2745
2746 static ssize_t fruit_pwrite(vfs_handle_struct *handle,
2747                             files_struct *fsp, const void *data,
2748                             size_t n, off_t offset)
2749 {
2750         int rc = 0;
2751         struct adouble *ad = (struct adouble *)VFS_FETCH_FSP_EXTENSION(
2752                 handle, fsp);
2753         struct fruit_config_data *config = NULL;
2754         AfpInfo *ai = NULL;
2755         ssize_t len;
2756         char *name = NULL;
2757         char *tmp_base_name = NULL;
2758         NTSTATUS status;
2759
2760         DEBUG(10, ("fruit_pwrite: offset=%d, size=%d\n", (int)offset, (int)n));
2761
2762         if (!fsp->base_fsp) {
2763                 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
2764         }
2765
2766         SMB_VFS_HANDLE_GET_DATA(handle, config,
2767                                 struct fruit_config_data, return -1);
2768
2769         tmp_base_name = fsp->base_fsp->fsp_name->base_name;
2770         status = SMB_VFS_TRANSLATE_NAME(handle->conn,
2771                                         fsp->base_fsp->fsp_name->base_name,
2772                                         vfs_translate_to_unix,
2773                                         talloc_tos(), &name);
2774         if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
2775                 name = talloc_strdup(talloc_tos(), tmp_base_name);
2776                 if (name == NULL) {
2777                         rc = -1;
2778                         goto exit;
2779                 }
2780         } else if (!NT_STATUS_IS_OK(status)) {
2781                 errno = map_errno_from_nt_status(status);
2782                 rc = -1;
2783                 goto exit;
2784         }
2785         fsp->base_fsp->fsp_name->base_name = name;
2786
2787         if (ad == NULL) {
2788                 len = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
2789                 if (len != n) {
2790                         rc = -1;
2791                         goto exit;
2792                 }
2793                 goto exit;
2794         }
2795
2796         if (!fruit_fsp_recheck(ad, fsp)) {
2797                 rc = -1;
2798                 goto exit;
2799         }
2800
2801         if (ad->ad_type == ADOUBLE_META) {
2802                 if (n != AFP_INFO_SIZE || offset != 0) {
2803                         DEBUG(1, ("unexpected offset=%jd or size=%jd\n",
2804                                   (intmax_t)offset, (intmax_t)n));
2805                         rc = -1;
2806                         goto exit;
2807                 }
2808                 ai = afpinfo_unpack(talloc_tos(), data);
2809                 if (ai == NULL) {
2810                         rc = -1;
2811                         goto exit;
2812                 }
2813                 memcpy(ad_entry(ad, ADEID_FINDERI),
2814                        &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
2815                 if (empty_finderinfo(ad)) {
2816                         /* Discard metadata */
2817                         if (config->meta == FRUIT_META_STREAM) {
2818                                 rc = SMB_VFS_FTRUNCATE(fsp, 0);
2819                         } else {
2820                                 rc = SMB_VFS_REMOVEXATTR(handle->conn,
2821                                                          fsp->fsp_name->base_name,
2822                                                          AFPINFO_EA_NETATALK);
2823                         }
2824                         if (rc != 0 && errno != ENOENT && errno != ENOATTR) {
2825                                 DBG_WARNING("Can't delete metadata for %s: %s\n",
2826                                             fsp->fsp_name->base_name, strerror(errno));
2827                                 goto exit;
2828                         }
2829                         rc = 0;
2830                         goto exit;
2831                 }
2832                 rc = ad_write(ad, name);
2833         } else {
2834                 len = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n,
2835                                    offset + ad_getentryoff(ad, ADEID_RFORK));
2836                 if (len != n) {
2837                         rc = -1;
2838                         goto exit;
2839                 }
2840
2841                 if (config->rsrc == FRUIT_RSRC_ADFILE) {
2842                         rc = ad_read(ad, name);
2843                         if (rc == -1) {
2844                                 goto exit;
2845                         }
2846                         rc = 0;
2847
2848                         if ((len + offset) > ad_getentrylen(ad, ADEID_RFORK)) {
2849                                 ad_setentrylen(ad, ADEID_RFORK, len + offset);
2850                                 rc = ad_write(ad, name);
2851                         }
2852                 }
2853         }
2854
2855 exit:
2856         fsp->base_fsp->fsp_name->base_name = tmp_base_name;
2857         TALLOC_FREE(name);
2858         TALLOC_FREE(ai);
2859         if (rc != 0) {
2860                 return -1;
2861         }
2862         return n;
2863 }
2864
2865 /**
2866  * Helper to stat/lstat the base file of an smb_fname.
2867  */
2868 static int fruit_stat_base(vfs_handle_struct *handle,
2869                            struct smb_filename *smb_fname,
2870                            bool follow_links)
2871 {
2872         char *tmp_stream_name;
2873         int rc;
2874
2875         tmp_stream_name = smb_fname->stream_name;
2876         smb_fname->stream_name = NULL;
2877         if (follow_links) {
2878                 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
2879         } else {
2880                 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
2881         }
2882         smb_fname->stream_name = tmp_stream_name;
2883         return rc;
2884 }
2885
2886 static int fruit_stat_meta(vfs_handle_struct *handle,
2887                            struct smb_filename *smb_fname,
2888                            bool follow_links)
2889 {
2890         struct adouble *ad = NULL;
2891
2892         ad = ad_get(talloc_tos(), handle, smb_fname->base_name, ADOUBLE_META);
2893         if (ad == NULL) {
2894                 DBG_INFO("fruit_stat_meta %s: %s\n",
2895                          smb_fname_str_dbg(smb_fname), strerror(errno));
2896                 errno = ENOENT;
2897                 return -1;
2898         }
2899         TALLOC_FREE(ad);
2900
2901         /* Populate the stat struct with info from the base file. */
2902         if (fruit_stat_base(handle, smb_fname, follow_links) == -1) {
2903                 return -1;
2904         }
2905         smb_fname->st.st_ex_size = AFP_INFO_SIZE;
2906         smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
2907                                               smb_fname->stream_name);
2908         return 0;
2909 }
2910
2911 static int fruit_stat_rsrc(vfs_handle_struct *handle,
2912                            struct smb_filename *smb_fname,
2913                            bool follow_links)
2914
2915 {
2916         struct adouble *ad = NULL;
2917
2918         DEBUG(10, ("fruit_stat_rsrc called for %s\n",
2919                    smb_fname_str_dbg(smb_fname)));
2920
2921         ad = ad_get(talloc_tos(), handle, smb_fname->base_name, ADOUBLE_RSRC);
2922         if (ad == NULL) {
2923                 errno = ENOENT;
2924                 return -1;
2925         }
2926
2927         /* Populate the stat struct with info from the base file. */
2928         if (fruit_stat_base(handle, smb_fname, follow_links) == -1) {
2929                 TALLOC_FREE(ad);
2930                 return -1;
2931         }
2932
2933         smb_fname->st.st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
2934         smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
2935                                               smb_fname->stream_name);
2936         TALLOC_FREE(ad);
2937         return 0;
2938 }
2939
2940 static int fruit_stat(vfs_handle_struct *handle,
2941                       struct smb_filename *smb_fname)
2942 {
2943         int rc = -1;
2944
2945         DEBUG(10, ("fruit_stat called for %s\n",
2946                    smb_fname_str_dbg(smb_fname)));
2947
2948         if (!is_ntfs_stream_smb_fname(smb_fname)
2949             || is_ntfs_default_stream_smb_fname(smb_fname)) {
2950                 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
2951                 if (rc == 0) {
2952                         update_btime(handle, smb_fname);
2953                 }
2954                 return rc;
2955         }
2956
2957         /*
2958          * Note if lp_posix_paths() is true, we can never
2959          * get here as is_ntfs_stream_smb_fname() is
2960          * always false. So we never need worry about
2961          * not following links here.
2962          */
2963
2964         if (is_afpinfo_stream(smb_fname)) {
2965                 rc = fruit_stat_meta(handle, smb_fname, true);
2966         } else if (is_afpresource_stream(smb_fname)) {
2967                 rc = fruit_stat_rsrc(handle, smb_fname, true);
2968         } else {
2969                 return SMB_VFS_NEXT_STAT(handle, smb_fname);
2970         }
2971
2972         if (rc == 0) {
2973                 update_btime(handle, smb_fname);
2974                 smb_fname->st.st_ex_mode &= ~S_IFMT;
2975                 smb_fname->st.st_ex_mode |= S_IFREG;
2976                 smb_fname->st.st_ex_blocks =
2977                         smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
2978         }
2979         return rc;
2980 }
2981
2982 static int fruit_lstat(vfs_handle_struct *handle,
2983                        struct smb_filename *smb_fname)
2984 {
2985         int rc = -1;
2986
2987         DEBUG(10, ("fruit_lstat called for %s\n",
2988                    smb_fname_str_dbg(smb_fname)));
2989
2990         if (!is_ntfs_stream_smb_fname(smb_fname)
2991             || is_ntfs_default_stream_smb_fname(smb_fname)) {
2992                 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
2993                 if (rc == 0) {
2994                         update_btime(handle, smb_fname);
2995                 }
2996                 return rc;
2997         }
2998
2999         if (is_afpinfo_stream(smb_fname)) {
3000                 rc = fruit_stat_meta(handle, smb_fname, false);
3001         } else if (is_afpresource_stream(smb_fname)) {
3002                 rc = fruit_stat_rsrc(handle, smb_fname, false);
3003         } else {
3004                 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
3005         }
3006
3007         if (rc == 0) {
3008                 update_btime(handle, smb_fname);
3009                 smb_fname->st.st_ex_mode &= ~S_IFMT;
3010                 smb_fname->st.st_ex_mode |= S_IFREG;
3011                 smb_fname->st.st_ex_blocks =
3012                         smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
3013         }
3014         return rc;
3015 }
3016
3017 static int fruit_fstat_meta(vfs_handle_struct *handle,
3018                             files_struct *fsp,
3019                             SMB_STRUCT_STAT *sbuf)
3020 {
3021         DEBUG(10, ("fruit_fstat_meta called for %s\n",
3022                    smb_fname_str_dbg(fsp->base_fsp->fsp_name)));
3023
3024         /* Populate the stat struct with info from the base file. */
3025         if (fruit_stat_base(handle, fsp->base_fsp->fsp_name, false) == -1) {
3026                 return -1;
3027         }
3028         *sbuf = fsp->base_fsp->fsp_name->st;
3029         sbuf->st_ex_size = AFP_INFO_SIZE;
3030         sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
3031
3032         return 0;
3033 }
3034
3035 static int fruit_fstat_rsrc(vfs_handle_struct *handle, files_struct *fsp,
3036                             SMB_STRUCT_STAT *sbuf)
3037 {
3038         struct fruit_config_data *config;
3039         struct adouble *ad = (struct adouble *)VFS_FETCH_FSP_EXTENSION(
3040                 handle, fsp);
3041
3042         DEBUG(10, ("fruit_fstat_rsrc called for %s\n",
3043                    smb_fname_str_dbg(fsp->base_fsp->fsp_name)));
3044
3045         SMB_VFS_HANDLE_GET_DATA(handle, config,
3046                                 struct fruit_config_data, return -1);
3047
3048         if (config->rsrc == FRUIT_RSRC_STREAM) {
3049                 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
3050         }
3051
3052         /* Populate the stat struct with info from the base file. */
3053         if (fruit_stat_base(handle, fsp->base_fsp->fsp_name, false) == -1) {
3054                 return -1;
3055         }
3056         *sbuf = fsp->base_fsp->fsp_name->st;
3057         sbuf->st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
3058         sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
3059
3060         DEBUG(10, ("fruit_fstat_rsrc %s, size: %zd\n",
3061                    smb_fname_str_dbg(fsp->fsp_name),
3062                    (ssize_t)sbuf->st_ex_size));
3063
3064         return 0;
3065 }
3066
3067 static int fruit_fstat(vfs_handle_struct *handle, files_struct *fsp,
3068                        SMB_STRUCT_STAT *sbuf)
3069 {
3070         int rc;
3071         char *name = NULL;
3072         char *tmp_base_name = NULL;
3073         NTSTATUS status;
3074         struct adouble *ad = (struct adouble *)
3075                 VFS_FETCH_FSP_EXTENSION(handle, fsp);
3076
3077         DEBUG(10, ("fruit_fstat called for %s\n",
3078                    smb_fname_str_dbg(fsp->fsp_name)));
3079
3080         if (fsp->base_fsp) {
3081                 tmp_base_name = fsp->base_fsp->fsp_name->base_name;
3082                 /* fsp_name is not converted with vfs_catia */
3083                 status = SMB_VFS_TRANSLATE_NAME(
3084                         handle->conn,
3085                         fsp->base_fsp->fsp_name->base_name,
3086                         vfs_translate_to_unix,
3087                         talloc_tos(), &name);
3088
3089                 if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
3090                         name = talloc_strdup(talloc_tos(), tmp_base_name);
3091                         if (name == NULL) {
3092                                 rc = -1;
3093                                 goto exit;
3094                         }
3095                 } else if (!NT_STATUS_IS_OK(status)) {
3096                         errno = map_errno_from_nt_status(status);
3097                         rc = -1;
3098                         goto exit;
3099                 }
3100                 fsp->base_fsp->fsp_name->base_name = name;
3101         }
3102
3103         if (ad == NULL || fsp->base_fsp == NULL) {
3104                 rc = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
3105                 goto exit;
3106         }
3107
3108         if (!fruit_fsp_recheck(ad, fsp)) {
3109                 rc = -1;
3110                 goto exit;
3111         }
3112
3113         switch (ad->ad_type) {
3114         case ADOUBLE_META:
3115                 rc = fruit_fstat_meta(handle, fsp, sbuf);
3116                 break;
3117         case ADOUBLE_RSRC:
3118                 rc = fruit_fstat_rsrc(handle, fsp, sbuf);
3119                 break;
3120         default:
3121                 DEBUG(10, ("fruit_fstat %s: bad type\n",
3122                            smb_fname_str_dbg(fsp->fsp_name)));
3123                 rc = -1;
3124                 goto exit;
3125         }
3126
3127         if (rc == 0) {
3128                 sbuf->st_ex_mode &= ~S_IFMT;
3129                 sbuf->st_ex_mode |= S_IFREG;
3130                 sbuf->st_ex_blocks = sbuf->st_ex_size / STAT_ST_BLOCKSIZE + 1;
3131         }
3132
3133 exit:
3134         DEBUG(10, ("fruit_fstat %s, size: %zd\n",
3135                    smb_fname_str_dbg(fsp->fsp_name),
3136                    (ssize_t)sbuf->st_ex_size));
3137         if (tmp_base_name) {
3138                 fsp->base_fsp->fsp_name->base_name = tmp_base_name;
3139         }
3140         TALLOC_FREE(name);
3141         return rc;
3142 }
3143
3144 static NTSTATUS fruit_streaminfo(vfs_handle_struct *handle,
3145                                  struct files_struct *fsp,
3146                                  const char *fname,
3147                                  TALLOC_CTX *mem_ctx,
3148                                  unsigned int *pnum_streams,
3149                                  struct stream_struct **pstreams)
3150 {
3151         struct fruit_config_data *config = NULL;
3152         struct smb_filename *smb_fname = NULL;
3153         struct adouble *ad = NULL;
3154         NTSTATUS status;
3155
3156         SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
3157                                 return NT_STATUS_UNSUCCESSFUL);
3158         DEBUG(10, ("fruit_streaminfo called for %s\n", fname));
3159
3160         smb_fname = synthetic_smb_fname(talloc_tos(), fname, NULL, NULL);
3161         if (smb_fname == NULL) {
3162                 return NT_STATUS_NO_MEMORY;
3163         }
3164
3165         if (config->meta == FRUIT_META_NETATALK) {
3166                 ad = ad_get(talloc_tos(), handle,
3167                             smb_fname->base_name, ADOUBLE_META);
3168                 if (ad && !empty_finderinfo(ad)) {
3169                         if (!add_fruit_stream(
3170                                     mem_ctx, pnum_streams, pstreams,
3171                                     AFPINFO_STREAM_NAME, AFP_INFO_SIZE,
3172                                     smb_roundup(handle->conn,
3173                                                 AFP_INFO_SIZE))) {
3174                                 TALLOC_FREE(ad);
3175                                 TALLOC_FREE(smb_fname);
3176                                 return NT_STATUS_NO_MEMORY;
3177                         }
3178                 }
3179                 TALLOC_FREE(ad);
3180         }
3181
3182         if (config->rsrc != FRUIT_RSRC_STREAM) {
3183                 ad = ad_get(talloc_tos(), handle, smb_fname->base_name,
3184                             ADOUBLE_RSRC);
3185                 if (ad && (ad_getentrylen(ad, ADEID_RFORK) > 0)) {
3186                         if (!add_fruit_stream(
3187                                     mem_ctx, pnum_streams, pstreams,
3188                                     AFPRESOURCE_STREAM_NAME,
3189                                     ad_getentrylen(ad, ADEID_RFORK),
3190                                     smb_roundup(handle->conn,
3191                                                 ad_getentrylen(
3192                                                         ad, ADEID_RFORK)))) {
3193                                 TALLOC_FREE(ad);
3194                                 TALLOC_FREE(smb_fname);
3195                                 return NT_STATUS_NO_MEMORY;
3196                         }
3197                 }
3198                 TALLOC_FREE(ad);
3199         }
3200
3201         TALLOC_FREE(smb_fname);
3202
3203         status = SMB_VFS_NEXT_STREAMINFO(handle, fsp, fname, mem_ctx,
3204                                          pnum_streams, pstreams);
3205         if (!NT_STATUS_IS_OK(status)) {
3206                 return status;
3207         }
3208
3209         if (config->meta == FRUIT_META_NETATALK) {
3210                 /* Remove the Netatalk xattr from the list */
3211                 if (!del_fruit_stream(mem_ctx, pnum_streams, pstreams,
3212                                       ":" NETATALK_META_XATTR ":$DATA")) {
3213                                 TALLOC_FREE(ad);
3214                                 TALLOC_FREE(smb_fname);
3215                                 return NT_STATUS_NO_MEMORY;
3216                 }
3217         }
3218
3219         return NT_STATUS_OK;
3220 }
3221
3222 static int fruit_ntimes(vfs_handle_struct *handle,
3223                         const struct smb_filename *smb_fname,
3224                         struct smb_file_time *ft)
3225 {
3226         int rc = 0;
3227         struct adouble *ad = NULL;
3228
3229         if (null_timespec(ft->create_time)) {
3230                 goto exit;
3231         }
3232
3233         DEBUG(10,("set btime for %s to %s\n", smb_fname_str_dbg(smb_fname),
3234                  time_to_asc(convert_timespec_to_time_t(ft->create_time))));
3235
3236         ad = ad_get(talloc_tos(), handle, smb_fname->base_name, ADOUBLE_META);
3237         if (ad == NULL) {
3238                 goto exit;
3239         }
3240
3241         ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX,
3242                    convert_time_t_to_uint32_t(ft->create_time.tv_sec));
3243
3244         rc = ad_write(ad, smb_fname->base_name);
3245
3246 exit:
3247
3248         TALLOC_FREE(ad);
3249         if (rc != 0) {
3250                 DEBUG(1, ("fruit_ntimes: %s\n", smb_fname_str_dbg(smb_fname)));
3251                 return -1;
3252         }
3253         return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
3254 }
3255
3256 static int fruit_fallocate(struct vfs_handle_struct *handle,
3257                            struct files_struct *fsp,
3258                            uint32_t mode,
3259                            off_t offset,
3260                            off_t len)
3261 {
3262         struct adouble *ad =
3263                 (struct adouble *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
3264
3265         if (ad == NULL) {
3266                 return SMB_VFS_NEXT_FALLOCATE(handle, fsp, mode, offset, len);
3267         }
3268
3269         if (!fruit_fsp_recheck(ad, fsp)) {
3270                 return -1;
3271         }
3272
3273         /* Let the pwrite code path handle it. */
3274         errno = ENOSYS;
3275         return -1;
3276 }
3277
3278 static int fruit_ftruncate_meta(struct vfs_handle_struct *handle,
3279                                 struct files_struct *fsp,
3280                                 off_t offset,
3281                                 struct adouble *ad)
3282 {
3283         struct fruit_config_data *config;
3284
3285         SMB_VFS_HANDLE_GET_DATA(handle, config,
3286                                 struct fruit_config_data, return -1);
3287
3288         if (offset > 60) {
3289                 DBG_WARNING("ftruncate %s to %jd",
3290                             fsp_str_dbg(fsp), (intmax_t)offset);
3291                 /* OS X returns NT_STATUS_ALLOTTED_SPACE_EXCEEDED  */
3292                 errno = EOVERFLOW;
3293                 return -1;
3294         }
3295
3296         DBG_WARNING("ignoring ftruncate %s to %jd",
3297                     fsp_str_dbg(fsp), (intmax_t)offset);
3298         /* OS X returns success but does nothing  */
3299         return 0;
3300 }
3301
3302 static int fruit_ftruncate_rsrc(struct vfs_handle_struct *handle,
3303                                 struct files_struct *fsp,
3304                                 off_t offset,
3305                                 struct adouble *ad)
3306 {
3307         int rc;
3308         struct fruit_config_data *config;
3309
3310         SMB_VFS_HANDLE_GET_DATA(handle, config,
3311                                 struct fruit_config_data, return -1);
3312
3313         if (config->rsrc == FRUIT_RSRC_XATTR && offset == 0) {
3314                 return SMB_VFS_FREMOVEXATTR(fsp,
3315                                             AFPRESOURCE_EA_NETATALK);
3316         }
3317
3318         rc = SMB_VFS_NEXT_FTRUNCATE(
3319                 handle, fsp,
3320                 offset + ad_getentryoff(ad, ADEID_RFORK));
3321         if (rc != 0) {
3322                 return -1;
3323         }
3324
3325         if (config->rsrc == FRUIT_RSRC_ADFILE) {
3326                 ad_setentrylen(ad, ADEID_RFORK, offset);
3327                 rc = ad_write(ad, NULL);
3328                 if (rc != 0) {
3329                         return -1;
3330                 }
3331                 DEBUG(10, ("fruit_ftruncate_rsrc file %s offset %jd\n",
3332                            fsp_str_dbg(fsp), (intmax_t)offset));
3333         }
3334
3335         return 0;
3336 }
3337
3338 static int fruit_ftruncate(struct vfs_handle_struct *handle,
3339                            struct files_struct *fsp,
3340                            off_t offset)
3341 {
3342         int rc = 0;
3343         struct adouble *ad =
3344                 (struct adouble *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
3345
3346         DBG_DEBUG("fruit_ftruncate called for file %s offset %.0f\n",
3347                    fsp_str_dbg(fsp), (double)offset);
3348
3349         if (ad == NULL) {
3350                 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
3351         }
3352
3353         if (!fruit_fsp_recheck(ad, fsp)) {
3354                 return -1;
3355         }
3356
3357         switch (ad->ad_type) {
3358         case ADOUBLE_META:
3359                 rc = fruit_ftruncate_meta(handle, fsp, offset, ad);
3360                 break;
3361
3362         case ADOUBLE_RSRC:
3363                 rc = fruit_ftruncate_rsrc(handle, fsp, offset, ad);
3364                 break;
3365
3366         default:
3367                 return -1;
3368         }
3369
3370         return rc;
3371 }
3372
3373 static NTSTATUS fruit_create_file(vfs_handle_struct *handle,
3374                                   struct smb_request *req,
3375                                   uint16_t root_dir_fid,
3376                                   struct smb_filename *smb_fname,
3377                                   uint32_t access_mask,
3378                                   uint32_t share_access,
3379                                   uint32_t create_disposition,
3380                                   uint32_t create_options,
3381                                   uint32_t file_attributes,
3382                                   uint32_t oplock_request,
3383                                   struct smb2_lease *lease,
3384                                   uint64_t allocation_size,
3385                                   uint32_t private_flags,
3386                                   struct security_descriptor *sd,
3387                                   struct ea_list *ea_list,
3388                                   files_struct **result,
3389                                   int *pinfo,
3390                                   const struct smb2_create_blobs *in_context_blobs,
3391                                   struct smb2_create_blobs *out_context_blobs)
3392 {
3393         NTSTATUS status;
3394         struct fruit_config_data *config = NULL;
3395         files_struct *fsp = NULL;
3396
3397         status = check_aapl(handle, req, in_context_blobs, out_context_blobs);
3398         if (!NT_STATUS_IS_OK(status)) {
3399                 goto fail;
3400         }
3401
3402         SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
3403                                 return NT_STATUS_UNSUCCESSFUL);
3404
3405         status = SMB_VFS_NEXT_CREATE_FILE(
3406                 handle, req, root_dir_fid, smb_fname,
3407                 access_mask, share_access,
3408                 create_disposition, create_options,
3409                 file_attributes, oplock_request,
3410                 lease,
3411                 allocation_size, private_flags,
3412                 sd, ea_list, result,
3413                 pinfo, in_context_blobs, out_context_blobs);
3414         if (!NT_STATUS_IS_OK(status)) {
3415                 return status;
3416         }
3417
3418         fsp = *result;
3419
3420         if (config->nego_aapl) {
3421                 if (config->copyfile_enabled) {
3422                         /*
3423                          * Set a flag in the fsp. Gets used in
3424                          * copychunk to check whether the special
3425                          * Apple copyfile semantics for copychunk
3426                          * should be allowed in a copychunk request
3427                          * with a count of 0.
3428                          */
3429                         fsp->aapl_copyfile_supported = true;
3430                 }
3431
3432                 if (fsp->is_directory) {
3433                         /*
3434                          * Enable POSIX directory rename behaviour
3435                          */
3436                         fsp->posix_flags |= FSP_POSIX_FLAGS_RENAME;
3437                 }
3438         }
3439
3440         /*
3441          * If this is a plain open for existing files, opening an 0
3442          * byte size resource fork MUST fail with
3443          * NT_STATUS_OBJECT_NAME_NOT_FOUND.
3444          *
3445          * Cf the vfs_fruit torture tests in test_rfork_create().
3446          */
3447         if (is_afpresource_stream(fsp->fsp_name) &&
3448             create_disposition == FILE_OPEN)
3449         {
3450                 if (fsp->fsp_name->st.st_ex_size == 0) {
3451                         status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
3452                         goto fail;
3453                 }
3454         }
3455
3456         if (is_ntfs_stream_smb_fname(smb_fname)
3457             || fsp->is_directory) {
3458                 return status;
3459         }
3460
3461         if (config->locking == FRUIT_LOCKING_NETATALK) {
3462                 status = fruit_check_access(
3463                         handle, *result,
3464                         access_mask,
3465                         map_share_mode_to_deny_mode(share_access, 0));
3466                 if (!NT_STATUS_IS_OK(status)) {
3467                         goto fail;
3468                 }
3469         }
3470
3471         return status;
3472
3473 fail:
3474         DEBUG(10, ("fruit_create_file: %s\n", nt_errstr(status)));
3475
3476         if (fsp) {
3477                 close_file(req, fsp, ERROR_CLOSE);
3478                 *result = fsp = NULL;
3479         }
3480
3481         return status;
3482 }
3483
3484 static NTSTATUS fruit_readdir_attr(struct vfs_handle_struct *handle,
3485                                    const struct smb_filename *fname,
3486                                    TALLOC_CTX *mem_ctx,
3487                                    struct readdir_attr_data **pattr_data)
3488 {
3489         struct fruit_config_data *config = NULL;
3490         struct readdir_attr_data *attr_data;
3491         NTSTATUS status;
3492
3493         SMB_VFS_HANDLE_GET_DATA(handle, config,
3494                                 struct fruit_config_data,
3495                                 return NT_STATUS_UNSUCCESSFUL);
3496
3497         if (!config->use_aapl) {
3498                 return SMB_VFS_NEXT_READDIR_ATTR(handle, fname, mem_ctx, pattr_data);
3499         }
3500
3501         DEBUG(10, ("fruit_readdir_attr %s\n", fname->base_name));
3502
3503         *pattr_data = talloc_zero(mem_ctx, struct readdir_attr_data);
3504         if (*pattr_data == NULL) {
3505                 return NT_STATUS_UNSUCCESSFUL;
3506         }
3507         attr_data = *pattr_data;
3508         attr_data->type = RDATTR_AAPL;
3509
3510         /*
3511          * Mac metadata: compressed FinderInfo, resource fork length
3512          * and creation date
3513          */
3514         status = readdir_attr_macmeta(handle, fname, attr_data);
3515         if (!NT_STATUS_IS_OK(status)) {
3516                 /*
3517                  * Error handling is tricky: if we return failure from
3518                  * this function, the corresponding directory entry
3519                  * will to be passed to the client, so we really just
3520                  * want to error out on fatal errors.
3521                  */
3522                 if  (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
3523                         goto fail;
3524                 }
3525         }
3526
3527         /*
3528          * UNIX mode
3529          */
3530         if (config->unix_info_enabled) {
3531                 attr_data->attr_data.aapl.unix_mode = fname->st.st_ex_mode;
3532         }
3533
3534         /*
3535          * max_access
3536          */
3537         if (!config->readdir_attr_max_access) {
3538                 attr_data->attr_data.aapl.max_access = FILE_GENERIC_ALL;
3539         } else {
3540                 status = smbd_calculate_access_mask(
3541                         handle->conn,
3542                         fname,
3543                         false,
3544                         SEC_FLAG_MAXIMUM_ALLOWED,
3545                         &attr_data->attr_data.aapl.max_access);
3546                 if (!NT_STATUS_IS_OK(status)) {
3547                         goto fail;
3548                 }
3549         }
3550
3551         return NT_STATUS_OK;
3552
3553 fail:
3554         DEBUG(1, ("fruit_readdir_attr %s, error: %s\n",
3555                   fname->base_name, nt_errstr(status)));
3556         TALLOC_FREE(*pattr_data);
3557         return status;
3558 }
3559
3560 static NTSTATUS fruit_fget_nt_acl(vfs_handle_struct *handle,
3561                                   files_struct *fsp,
3562                                   uint32_t security_info,
3563                                   TALLOC_CTX *mem_ctx,
3564                                   struct security_descriptor **ppdesc)
3565 {
3566         NTSTATUS status;
3567         struct security_ace ace;
3568         struct dom_sid sid;
3569         struct fruit_config_data *config;
3570
3571         SMB_VFS_HANDLE_GET_DATA(handle, config,
3572                                 struct fruit_config_data,
3573                                 return NT_STATUS_UNSUCCESSFUL);
3574
3575         status = SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
3576                                           mem_ctx, ppdesc);
3577         if (!NT_STATUS_IS_OK(status)) {
3578                 return status;
3579         }
3580
3581         /*
3582          * Add MS NFS style ACEs with uid, gid and mode
3583          */
3584         if (!config->unix_info_enabled) {
3585                 return NT_STATUS_OK;
3586         }
3587
3588         /* MS NFS style mode */
3589         sid_compose(&sid, &global_sid_Unix_NFS_Mode, fsp->fsp_name->st.st_ex_mode);
3590         init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
3591         status = security_descriptor_dacl_add(*ppdesc, &ace);
3592         if (!NT_STATUS_IS_OK(status)) {
3593                 DEBUG(1,("failed to add MS NFS style ACE\n"));
3594                 return status;
3595         }
3596
3597         /* MS NFS style uid */
3598         sid_compose(&sid, &global_sid_Unix_NFS_Users, fsp->fsp_name->st.st_ex_uid);
3599         init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
3600         status = security_descriptor_dacl_add(*ppdesc, &ace);
3601         if (!NT_STATUS_IS_OK(status)) {
3602                 DEBUG(1,("failed to add MS NFS style ACE\n"));
3603                 return status;
3604         }
3605
3606         /* MS NFS style gid */
3607         sid_compose(&sid, &global_sid_Unix_NFS_Groups, fsp->fsp_name->st.st_ex_gid);
3608         init_sec_ace(&ace, &sid, SEC_ACE_TYPE_ACCESS_DENIED, 0, 0);
3609         status = security_descriptor_dacl_add(*ppdesc, &ace);
3610         if (!NT_STATUS_IS_OK(status)) {
3611                 DEBUG(1,("failed to add MS NFS style ACE\n"));
3612                 return status;
3613         }
3614
3615         return NT_STATUS_OK;
3616 }
3617
3618 static NTSTATUS fruit_fset_nt_acl(vfs_handle_struct *handle,
3619                                   files_struct *fsp,
3620                                   uint32_t security_info_sent,
3621                                   const struct security_descriptor *psd)
3622 {
3623         NTSTATUS status;
3624         bool do_chmod;
3625         mode_t ms_nfs_mode;
3626         int result;
3627
3628         DBG_DEBUG("fruit_fset_nt_acl: %s\n", fsp_str_dbg(fsp));
3629
3630         status = check_ms_nfs(handle, fsp, psd, &ms_nfs_mode, &do_chmod);
3631         if (!NT_STATUS_IS_OK(status)) {
3632                 DEBUG(1, ("fruit_fset_nt_acl: check_ms_nfs failed%s\n", fsp_str_dbg(fsp)));
3633                 return status;
3634         }
3635
3636         status = SMB_VFS_NEXT_FSET_NT_ACL(handle, fsp, security_info_sent, psd);
3637         if (!NT_STATUS_IS_OK(status)) {
3638                 DEBUG(1, ("fruit_fset_nt_acl: SMB_VFS_NEXT_FSET_NT_ACL failed%s\n", fsp_str_dbg(fsp)));
3639                 return status;
3640         }
3641
3642         if (do_chmod) {
3643                 if (fsp->fh->fd != -1) {
3644                         result = SMB_VFS_FCHMOD(fsp, ms_nfs_mode);
3645                 } else {
3646                         result = SMB_VFS_CHMOD(fsp->conn,
3647                                                fsp->fsp_name->base_name,
3648                                                ms_nfs_mode);
3649                 }
3650
3651                 if (result != 0) {
3652                         DEBUG(1, ("chmod: %s, result: %d, %04o error %s\n", fsp_str_dbg(fsp),
3653                                   result, (unsigned)ms_nfs_mode,
3654                                   strerror(errno)));
3655                         status = map_nt_error_from_unix(errno);
3656                         return status;
3657                 }
3658         }
3659
3660         return NT_STATUS_OK;
3661 }
3662
3663 struct fruit_copy_chunk_state {
3664         struct vfs_handle_struct *handle;
3665         off_t copied;
3666         struct files_struct *src_fsp;
3667         struct files_struct *dst_fsp;
3668         bool is_copyfile;
3669 };
3670
3671 static void fruit_copy_chunk_done(struct tevent_req *subreq);
3672 static struct tevent_req *fruit_copy_chunk_send(struct vfs_handle_struct *handle,
3673                                                 TALLOC_CTX *mem_ctx,
3674                                                 struct tevent_context *ev,
3675                                                 struct files_struct *src_fsp,
3676                                                 off_t src_off,
3677                                                 struct files_struct *dest_fsp,
3678                                                 off_t dest_off,
3679                                                 off_t num)
3680 {
3681         struct tevent_req *req, *subreq;
3682         struct fruit_copy_chunk_state *fruit_copy_chunk_state;
3683         NTSTATUS status;
3684         struct fruit_config_data *config;
3685         off_t to_copy = num;
3686
3687         DEBUG(10,("soff: %ju, doff: %ju, len: %ju\n",
3688                   (uintmax_t)src_off, (uintmax_t)dest_off, (uintmax_t)num));
3689
3690         SMB_VFS_HANDLE_GET_DATA(handle, config,
3691                                 struct fruit_config_data,
3692                                 return NULL);
3693
3694         req = tevent_req_create(mem_ctx, &fruit_copy_chunk_state,
3695                                 struct fruit_copy_chunk_state);
3696         if (req == NULL) {
3697                 return NULL;
3698         }
3699         fruit_copy_chunk_state->handle = handle;
3700         fruit_copy_chunk_state->src_fsp = src_fsp;
3701         fruit_copy_chunk_state->dst_fsp = dest_fsp;
3702
3703         /*
3704          * Check if this a OS X copyfile style copychunk request with
3705          * a requested chunk count of 0 that was translated to a
3706          * copy_chunk_send VFS call overloading the parameters src_off
3707          * = dest_off = num = 0.
3708          */
3709         if ((src_off == 0) && (dest_off == 0) && (num == 0) &&
3710             src_fsp->aapl_copyfile_supported &&
3711             dest_fsp->aapl_copyfile_supported)
3712         {
3713                 status = vfs_stat_fsp(src_fsp);
3714                 if (tevent_req_nterror(req, status)) {
3715                         return tevent_req_post(req, ev);
3716                 }
3717
3718                 to_copy = src_fsp->fsp_name->st.st_ex_size;
3719                 fruit_copy_chunk_state->is_copyfile = true;
3720         }
3721
3722         subreq = SMB_VFS_NEXT_COPY_CHUNK_SEND(handle,
3723                                               mem_ctx,
3724                                               ev,
3725                                               src_fsp,
3726                                               src_off,
3727                                               dest_fsp,
3728                                               dest_off,
3729                                               to_copy);
3730         if (tevent_req_nomem(subreq, req)) {
3731                 return tevent_req_post(req, ev);
3732         }
3733
3734         tevent_req_set_callback(subreq, fruit_copy_chunk_done, req);
3735         return req;
3736 }
3737
3738 static void fruit_copy_chunk_done(struct tevent_req *subreq)
3739 {
3740         struct tevent_req *req = tevent_req_callback_data(
3741                 subreq, struct tevent_req);
3742         struct fruit_copy_chunk_state *state = tevent_req_data(
3743                 req, struct fruit_copy_chunk_state);
3744         NTSTATUS status;
3745         unsigned int num_streams = 0;
3746         struct stream_struct *streams = NULL;
3747         int i;
3748         struct smb_filename *src_fname_tmp = NULL;
3749         struct smb_filename *dst_fname_tmp = NULL;
3750
3751         status = SMB_VFS_NEXT_COPY_CHUNK_RECV(state->handle,
3752                                               subreq,
3753                                               &state->copied);
3754         TALLOC_FREE(subreq);
3755         if (tevent_req_nterror(req, status)) {
3756                 return;
3757         }
3758
3759         if (!state->is_copyfile) {
3760                 tevent_req_done(req);
3761                 return;
3762         }
3763
3764         /*
3765          * Now copy all reamining streams. We know the share supports
3766          * streams, because we're in vfs_fruit. We don't do this async
3767          * because streams are few and small.
3768          */
3769         status = vfs_streaminfo(state->handle->conn, NULL,
3770                                 state->src_fsp->fsp_name->base_name,
3771                                 req, &num_streams, &streams);
3772         if (tevent_req_nterror(req, status)) {
3773                 return;
3774         }
3775
3776         if (num_streams == 1) {
3777                 /* There is always one stream, ::$DATA. */
3778                 tevent_req_done(req);
3779                 return;
3780         }
3781
3782         for (i = 0; i < num_streams; i++) {
3783                 DEBUG(10, ("%s: stream: '%s'/%ju\n",
3784                            __func__, streams[i].name,
3785                            (uintmax_t)streams[i].size));
3786
3787                 src_fname_tmp = synthetic_smb_fname(
3788                         req,
3789                         state->src_fsp->fsp_name->base_name,
3790                         streams[i].name,
3791                         NULL);
3792                 if (tevent_req_nomem(src_fname_tmp, req)) {
3793                         return;
3794                 }
3795
3796                 if (is_ntfs_default_stream_smb_fname(src_fname_tmp)) {
3797                         TALLOC_FREE(src_fname_tmp);
3798                         continue;
3799                 }
3800
3801                 dst_fname_tmp = synthetic_smb_fname(
3802                         req,
3803                         state->dst_fsp->fsp_name->base_name,
3804                         streams[i].name,
3805                         NULL);
3806                 if (tevent_req_nomem(dst_fname_tmp, req)) {
3807                         TALLOC_FREE(src_fname_tmp);
3808                         return;
3809                 }
3810
3811                 status = copy_file(req,
3812                                    state->handle->conn,
3813                                    src_fname_tmp,
3814                                    dst_fname_tmp,
3815                                    OPENX_FILE_CREATE_IF_NOT_EXIST,
3816                                    0, false);
3817                 if (!NT_STATUS_IS_OK(status)) {
3818                         DEBUG(1, ("%s: copy %s to %s failed: %s\n", __func__,
3819                                   smb_fname_str_dbg(src_fname_tmp),
3820                                   smb_fname_str_dbg(dst_fname_tmp),
3821                                   nt_errstr(status)));
3822                         TALLOC_FREE(src_fname_tmp);
3823                         TALLOC_FREE(dst_fname_tmp);
3824                         tevent_req_nterror(req, status);
3825                         return;
3826                 }
3827
3828                 TALLOC_FREE(src_fname_tmp);
3829                 TALLOC_FREE(dst_fname_tmp);
3830         }
3831
3832         TALLOC_FREE(streams);
3833         TALLOC_FREE(src_fname_tmp);
3834         TALLOC_FREE(dst_fname_tmp);
3835         tevent_req_done(req);
3836 }
3837
3838 static NTSTATUS fruit_copy_chunk_recv(struct vfs_handle_struct *handle,
3839                                       struct tevent_req *req,
3840                                       off_t *copied)
3841 {
3842         struct fruit_copy_chunk_state *fruit_copy_chunk_state = tevent_req_data(
3843                 req, struct fruit_copy_chunk_state);
3844         NTSTATUS status;
3845
3846         if (tevent_req_is_nterror(req, &status)) {
3847                 DEBUG(1, ("server side copy chunk failed: %s\n",
3848                           nt_errstr(status)));
3849                 *copied = 0;
3850                 tevent_req_received(req);
3851                 return status;
3852         }
3853
3854         *copied = fruit_copy_chunk_state->copied;
3855         tevent_req_received(req);
3856
3857         return NT_STATUS_OK;
3858 }
3859
3860 static struct vfs_fn_pointers vfs_fruit_fns = {
3861         .connect_fn = fruit_connect,
3862
3863         /* File operations */
3864         .chmod_fn = fruit_chmod,
3865         .chown_fn = fruit_chown,
3866         .unlink_fn = fruit_unlink,
3867         .rename_fn = fruit_rename,
3868         .rmdir_fn = fruit_rmdir,
3869         .open_fn = fruit_open,
3870         .pread_fn = fruit_pread,
3871         .pwrite_fn = fruit_pwrite,
3872         .stat_fn = fruit_stat,
3873         .lstat_fn = fruit_lstat,
3874         .fstat_fn = fruit_fstat,
3875         .streaminfo_fn = fruit_streaminfo,
3876         .ntimes_fn = fruit_ntimes,
3877         .ftruncate_fn = fruit_ftruncate,
3878         .fallocate_fn = fruit_fallocate,
3879         .create_file_fn = fruit_create_file,
3880         .readdir_attr_fn = fruit_readdir_attr,
3881         .copy_chunk_send_fn = fruit_copy_chunk_send,
3882         .copy_chunk_recv_fn = fruit_copy_chunk_recv,
3883
3884         /* NT ACL operations */
3885         .fget_nt_acl_fn = fruit_fget_nt_acl,
3886         .fset_nt_acl_fn = fruit_fset_nt_acl,
3887 };
3888
3889 NTSTATUS vfs_fruit_init(void);
3890 NTSTATUS vfs_fruit_init(void)
3891 {
3892         NTSTATUS ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "fruit",
3893                                         &vfs_fruit_fns);
3894         if (!NT_STATUS_IS_OK(ret)) {
3895                 return ret;
3896         }
3897
3898         vfs_fruit_debug_level = debug_add_class("fruit");
3899         if (vfs_fruit_debug_level == -1) {
3900                 vfs_fruit_debug_level = DBGC_VFS;
3901                 DEBUG(0, ("%s: Couldn't register custom debugging class!\n",
3902                           "vfs_fruit_init"));
3903         } else {
3904                 DEBUG(10, ("%s: Debug class number of '%s': %d\n",
3905                            "vfs_fruit_init","fruit",vfs_fruit_debug_level));
3906         }
3907
3908         return ret;
3909 }