vfs_fruit: ad_write: path may be NULL for rfork
[obnox/samba/samba-obnox.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
32 /*
33  * Enhanced OS X and Netatalk compatibility
34  * ========================================
35  *
36  * This modules takes advantage of vfs_streams_xattr and
37  * vfs_catia. VFS modules vfs_fruit and vfs_streams_xattr must be
38  * loaded in the correct order:
39  *
40  *   vfs modules = catia fruit streams_xattr
41  *
42  * The module intercepts the OS X special streams "AFP_AfpInfo" and
43  * "AFP_Resource" and handles them in a special way. All other named
44  * streams are deferred to vfs_streams_xattr.
45  *
46  * The OS X client maps all NTFS illegal characters to the Unicode
47  * private range. This module optionally stores the charcters using
48  * their native ASCII encoding using vfs_catia. If you're not enabling
49  * this feature, you can skip catia from vfs modules.
50  *
51  * Finally, open modes are optionally checked against Netatalk AFP
52  * share modes.
53  *
54  * The "AFP_AfpInfo" named stream is a binary blob containing OS X
55  * extended metadata for files and directories. This module optionally
56  * reads and stores this metadata in a way compatible with Netatalk 3
57  * which stores the metadata in an EA "org.netatalk.metadata". Cf
58  * source3/include/MacExtensions.h for a description of the binary
59  * blobs content.
60  *
61  * The "AFP_Resource" named stream may be arbitrarily large, thus it
62  * can't be stored in an xattr on most filesystem. ZFS on Solaris is
63  * the only available filesystem where xattrs can be of any size and
64  * the OS supports using the file APIs for xattrs.
65  *
66  * The AFP_Resource stream is stored in an AppleDouble file prepending
67  * "._" to the filename. On Solaris with ZFS the stream is optionally
68  * stored in an EA "org.netatalk.ressource".
69  *
70  *
71  * Extended Attributes
72  * ===================
73  *
74  * The OS X SMB client sends xattrs as ADS too. For xattr interop with
75  * other protocols you may want to adjust the xattr names the VFS
76  * module vfs_streams_xattr uses for storing ADS's. This defaults to
77  * user.DosStream.ADS_NAME:$DATA and can be changed by specifying
78  * these module parameters:
79  *
80  *   streams_xattr:prefix = user.
81  *   streams_xattr:store_stream_type = false
82  *
83  *
84  * TODO
85  * ====
86  *
87  * - log diagnostic if any needed VFS module is not loaded
88  *   (eg with lp_vfs_objects())
89  * - add tests
90  */
91
92 static int vfs_fruit_debug_level = DBGC_VFS;
93
94 #undef DBGC_CLASS
95 #define DBGC_CLASS vfs_fruit_debug_level
96
97 #define FRUIT_PARAM_TYPE_NAME "fruit"
98 #define ADOUBLE_NAME_PREFIX "._"
99
100 /*
101  * REVIEW:
102  * This is hokey, but what else can we do?
103  */
104 #if defined(HAVE_ATTROPEN) || defined(FREEBSD)
105 #define AFPINFO_EA_NETATALK "org.netatalk.Metadata"
106 #define AFPRESOURCE_EA_NETATALK "org.netatalk.ResourceFork"
107 #else
108 #define AFPINFO_EA_NETATALK "user.org.netatalk.Metadata"
109 #define AFPRESOURCE_EA_NETATALK "user.org.netatalk.ResourceFork"
110 #endif
111
112 enum apple_fork {APPLE_FORK_DATA, APPLE_FORK_RSRC};
113
114 enum fruit_rsrc {FRUIT_RSRC_STREAM, FRUIT_RSRC_ADFILE, FRUIT_RSRC_XATTR};
115 enum fruit_meta {FRUIT_META_STREAM, FRUIT_META_NETATALK};
116 enum fruit_locking {FRUIT_LOCKING_NETATALK, FRUIT_LOCKING_NONE};
117 enum fruit_encoding {FRUIT_ENC_NATIVE, FRUIT_ENC_PRIVATE};
118
119 struct fruit_config_data {
120         enum fruit_rsrc rsrc;
121         enum fruit_meta meta;
122         enum fruit_locking locking;
123         enum fruit_encoding encoding;
124 };
125
126 static const struct enum_list fruit_rsrc[] = {
127         {FRUIT_RSRC_STREAM, "stream"}, /* pass on to vfs_streams_xattr */
128         {FRUIT_RSRC_ADFILE, "file"}, /* ._ AppleDouble file */
129         {FRUIT_RSRC_XATTR, "xattr"}, /* Netatalk compatible xattr (ZFS only) */
130         { -1, NULL}
131 };
132
133 static const struct enum_list fruit_meta[] = {
134         {FRUIT_META_STREAM, "stream"}, /* pass on to vfs_streams_xattr */
135         {FRUIT_META_NETATALK, "netatalk"}, /* Netatalk compatible xattr */
136         { -1, NULL}
137 };
138
139 static const struct enum_list fruit_locking[] = {
140         {FRUIT_LOCKING_NETATALK, "netatalk"}, /* synchronize locks with Netatalk */
141         {FRUIT_LOCKING_NONE, "none"},
142         { -1, NULL}
143 };
144
145 static const struct enum_list fruit_encoding[] = {
146         {FRUIT_ENC_NATIVE, "native"}, /* map unicode private chars to ASCII */
147         {FRUIT_ENC_PRIVATE, "private"}, /* keep unicode private chars */
148         { -1, NULL}
149 };
150
151 /*****************************************************************************
152  * Defines, functions and data structures that deal with AppleDouble
153  *****************************************************************************/
154
155 /*
156  * There are two AppleDouble blobs we deal with:
157  *
158  * - ADOUBLE_META - AppleDouble blob used by Netatalk for storing
159  *   metadata in an xattr
160  *
161  * - ADOUBLE_RSRC - AppleDouble blob used by OS X and Netatalk in
162  *   ._ files
163  */
164 typedef enum {ADOUBLE_META, ADOUBLE_RSRC} adouble_type_t;
165
166 /* Version info */
167 #define AD_VERSION2     0x00020000
168 #define AD_VERSION      AD_VERSION2
169
170 /*
171  * AppleDouble entry IDs.
172  */
173 #define ADEID_DFORK         1
174 #define ADEID_RFORK         2
175 #define ADEID_NAME          3
176 #define ADEID_COMMENT       4
177 #define ADEID_ICONBW        5
178 #define ADEID_ICONCOL       6
179 #define ADEID_FILEI         7
180 #define ADEID_FILEDATESI    8
181 #define ADEID_FINDERI       9
182 #define ADEID_MACFILEI      10
183 #define ADEID_PRODOSFILEI   11
184 #define ADEID_MSDOSFILEI    12
185 #define ADEID_SHORTNAME     13
186 #define ADEID_AFPFILEI      14
187 #define ADEID_DID           15
188
189 /* Private Netatalk entries */
190 #define ADEID_PRIVDEV       16
191 #define ADEID_PRIVINO       17
192 #define ADEID_PRIVSYN       18
193 #define ADEID_PRIVID        19
194 #define ADEID_MAX           (ADEID_PRIVID + 1)
195
196 /*
197  * These are the real ids for the private entries,
198  * as stored in the adouble file
199  */
200 #define AD_DEV              0x80444556
201 #define AD_INO              0x80494E4F
202 #define AD_SYN              0x8053594E
203 #define AD_ID               0x8053567E
204
205 /* Number of actually used entries */
206 #define ADEID_NUM_XATTR      8
207 #define ADEID_NUM_DOT_UND    2
208 #define ADEID_NUM_RSRC_XATTR 1
209
210 /* AppleDouble magic */
211 #define AD_APPLESINGLE_MAGIC 0x00051600
212 #define AD_APPLEDOUBLE_MAGIC 0x00051607
213 #define AD_MAGIC             AD_APPLEDOUBLE_MAGIC
214
215 /* Sizes of relevant entry bits */
216 #define ADEDLEN_MAGIC       4
217 #define ADEDLEN_VERSION     4
218 #define ADEDLEN_FILLER      16
219 #define AD_FILLER_TAG       "Netatalk        " /* should be 16 bytes */
220 #define ADEDLEN_NENTRIES    2
221 #define AD_HEADER_LEN       (ADEDLEN_MAGIC + ADEDLEN_VERSION + \
222                              ADEDLEN_FILLER + ADEDLEN_NENTRIES) /* 26 */
223 #define AD_ENTRY_LEN_EID    4
224 #define AD_ENTRY_LEN_OFF    4
225 #define AD_ENTRY_LEN_LEN    4
226 #define AD_ENTRY_LEN (AD_ENTRY_LEN_EID + AD_ENTRY_LEN_OFF + AD_ENTRY_LEN_LEN)
227
228 /* Field widths */
229 #define ADEDLEN_NAME            255
230 #define ADEDLEN_COMMENT         200
231 #define ADEDLEN_FILEI           16
232 #define ADEDLEN_FINDERI         32
233 #define ADEDLEN_FILEDATESI      16
234 #define ADEDLEN_SHORTNAME       12 /* length up to 8.3 */
235 #define ADEDLEN_AFPFILEI        4
236 #define ADEDLEN_MACFILEI        4
237 #define ADEDLEN_PRODOSFILEI     8
238 #define ADEDLEN_MSDOSFILEI      2
239 #define ADEDLEN_DID             4
240 #define ADEDLEN_PRIVDEV         8
241 #define ADEDLEN_PRIVINO         8
242 #define ADEDLEN_PRIVSYN         8
243 #define ADEDLEN_PRIVID          4
244
245 /* Offsets */
246 #define ADEDOFF_MAGIC         0
247 #define ADEDOFF_VERSION       (ADEDOFF_MAGIC + ADEDLEN_MAGIC)
248 #define ADEDOFF_FILLER        (ADEDOFF_VERSION + ADEDLEN_VERSION)
249 #define ADEDOFF_NENTRIES      (ADEDOFF_FILLER + ADEDLEN_FILLER)
250
251 #define ADEDOFF_FINDERI_XATTR    (AD_HEADER_LEN + \
252                                   (ADEID_NUM_XATTR * AD_ENTRY_LEN))
253 #define ADEDOFF_COMMENT_XATTR    (ADEDOFF_FINDERI_XATTR    + ADEDLEN_FINDERI)
254 #define ADEDOFF_FILEDATESI_XATTR (ADEDOFF_COMMENT_XATTR    + ADEDLEN_COMMENT)
255 #define ADEDOFF_AFPFILEI_XATTR   (ADEDOFF_FILEDATESI_XATTR + \
256                                   ADEDLEN_FILEDATESI)
257 #define ADEDOFF_PRIVDEV_XATTR    (ADEDOFF_AFPFILEI_XATTR   + ADEDLEN_AFPFILEI)
258 #define ADEDOFF_PRIVINO_XATTR    (ADEDOFF_PRIVDEV_XATTR    + ADEDLEN_PRIVDEV)
259 #define ADEDOFF_PRIVSYN_XATTR    (ADEDOFF_PRIVINO_XATTR    + ADEDLEN_PRIVINO)
260 #define ADEDOFF_PRIVID_XATTR     (ADEDOFF_PRIVSYN_XATTR    + ADEDLEN_PRIVSYN)
261
262 #define ADEDOFF_FINDERI_DOT_UND  (AD_HEADER_LEN + \
263                                   (ADEID_NUM_DOT_UND * AD_ENTRY_LEN))
264 #define ADEDOFF_RFORK_DOT_UND    (ADEDOFF_FINDERI_DOT_UND + ADEDLEN_FINDERI)
265
266 #define AD_DATASZ_XATTR (AD_HEADER_LEN + \
267                          (ADEID_NUM_XATTR * AD_ENTRY_LEN) + \
268                          ADEDLEN_FINDERI + ADEDLEN_COMMENT + \
269                          ADEDLEN_FILEDATESI + ADEDLEN_AFPFILEI + \
270                          ADEDLEN_PRIVDEV + ADEDLEN_PRIVINO + \
271                          ADEDLEN_PRIVSYN + ADEDLEN_PRIVID)
272
273 #if AD_DATASZ_XATTR != 402
274 #error bad size for AD_DATASZ_XATTR
275 #endif
276
277 #define AD_DATASZ_DOT_UND (AD_HEADER_LEN + \
278                            (ADEID_NUM_DOT_UND * AD_ENTRY_LEN) + \
279                            ADEDLEN_FINDERI)
280 #if AD_DATASZ_DOT_UND != 82
281 #error bad size for AD_DATASZ_DOT_UND
282 #endif
283
284 /*
285  * Sharemode locks fcntl() offsets
286  */
287 #if _FILE_OFFSET_BITS == 64 || defined(HAVE_LARGEFILE)
288 #define AD_FILELOCK_BASE (UINT64_C(0x7FFFFFFFFFFFFFFF) - 9)
289 #else
290 #define AD_FILELOCK_BASE (UINT32_C(0x7FFFFFFF) - 9)
291 #endif
292 #define BYTELOCK_MAX (AD_FILELOCK_BASE - 1)
293
294 #define AD_FILELOCK_OPEN_WR        (AD_FILELOCK_BASE + 0)
295 #define AD_FILELOCK_OPEN_RD        (AD_FILELOCK_BASE + 1)
296 #define AD_FILELOCK_RSRC_OPEN_WR   (AD_FILELOCK_BASE + 2)
297 #define AD_FILELOCK_RSRC_OPEN_RD   (AD_FILELOCK_BASE + 3)
298 #define AD_FILELOCK_DENY_WR        (AD_FILELOCK_BASE + 4)
299 #define AD_FILELOCK_DENY_RD        (AD_FILELOCK_BASE + 5)
300 #define AD_FILELOCK_RSRC_DENY_WR   (AD_FILELOCK_BASE + 6)
301 #define AD_FILELOCK_RSRC_DENY_RD   (AD_FILELOCK_BASE + 7)
302 #define AD_FILELOCK_OPEN_NONE      (AD_FILELOCK_BASE + 8)
303 #define AD_FILELOCK_RSRC_OPEN_NONE (AD_FILELOCK_BASE + 9)
304
305 /* Time stuff we overload the bits a little */
306 #define AD_DATE_CREATE         0
307 #define AD_DATE_MODIFY         4
308 #define AD_DATE_BACKUP         8
309 #define AD_DATE_ACCESS        12
310 #define AD_DATE_MASK          (AD_DATE_CREATE | AD_DATE_MODIFY | \
311                                AD_DATE_BACKUP | AD_DATE_ACCESS)
312 #define AD_DATE_UNIX          (1 << 10)
313 #define AD_DATE_START         0x80000000
314 #define AD_DATE_DELTA         946684800
315 #define AD_DATE_FROM_UNIX(x)  (htonl((x) - AD_DATE_DELTA))
316 #define AD_DATE_TO_UNIX(x)    (ntohl(x) + AD_DATE_DELTA)
317
318 /* Accessor macros */
319 #define ad_getentrylen(ad,eid)     ((ad)->ad_eid[(eid)].ade_len)
320 #define ad_getentryoff(ad,eid)     ((ad)->ad_eid[(eid)].ade_off)
321 #define ad_setentrylen(ad,eid,len) ((ad)->ad_eid[(eid)].ade_len = (len))
322 #define ad_setentryoff(ad,eid,off) ((ad)->ad_eid[(eid)].ade_off = (off))
323 #define ad_entry(ad,eid)           ((ad)->ad_data + ad_getentryoff((ad),(eid)))
324
325 struct ad_entry {
326         size_t ade_off;
327         size_t ade_len;
328 };
329
330 struct adouble {
331         vfs_handle_struct        *ad_handle;
332         files_struct             *ad_fsp;
333         adouble_type_t            ad_type;
334         uint32_t                  ad_magic;
335         uint32_t                  ad_version;
336         struct ad_entry           ad_eid[ADEID_MAX];
337         char                     *ad_data;
338 };
339
340 struct ad_entry_order {
341         uint32_t id, offset, len;
342 };
343
344 /* Netatalk AppleDouble metadata xattr */
345 static const
346 struct ad_entry_order entry_order_meta_xattr[ADEID_NUM_XATTR + 1] = {
347         {ADEID_FINDERI,    ADEDOFF_FINDERI_XATTR,    ADEDLEN_FINDERI},
348         {ADEID_COMMENT,    ADEDOFF_COMMENT_XATTR,    0},
349         {ADEID_FILEDATESI, ADEDOFF_FILEDATESI_XATTR, ADEDLEN_FILEDATESI},
350         {ADEID_AFPFILEI,   ADEDOFF_AFPFILEI_XATTR,   ADEDLEN_AFPFILEI},
351         {ADEID_PRIVDEV,    ADEDOFF_PRIVDEV_XATTR,    0},
352         {ADEID_PRIVINO,    ADEDOFF_PRIVINO_XATTR,    0},
353         {ADEID_PRIVSYN,    ADEDOFF_PRIVSYN_XATTR,    0},
354         {ADEID_PRIVID,     ADEDOFF_PRIVID_XATTR,     0},
355         {0, 0, 0}
356 };
357
358 /* AppleDouble ressource fork file (the ones prefixed by "._") */
359 static const
360 struct ad_entry_order entry_order_dot_und[ADEID_NUM_DOT_UND + 1] = {
361         {ADEID_FINDERI,    ADEDOFF_FINDERI_DOT_UND,  ADEDLEN_FINDERI},
362         {ADEID_RFORK,      ADEDOFF_RFORK_DOT_UND,    0},
363         {0, 0, 0}
364 };
365
366 /*
367  * Fake AppleDouble entry oder for ressource fork xattr.  The xattr
368  * isn't an AppleDouble file, it simply contains the ressource data,
369  * but in order to be able to use some API calls like ad_getentryoff()
370  * we build a fake/helper struct adouble with this entry order struct.
371  */
372 static const
373 struct ad_entry_order entry_order_rsrc_xattr[ADEID_NUM_RSRC_XATTR + 1] = {
374         {ADEID_RFORK, 0, 0},
375         {0, 0, 0}
376 };
377
378 /* Conversion from enumerated id to on-disk AppleDouble id */
379 #define AD_EID_DISK(a) (set_eid[a])
380 static const uint32_t set_eid[] = {
381         0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
382         AD_DEV, AD_INO, AD_SYN, AD_ID
383 };
384
385 /*
386  * Forward declarations
387  */
388 static struct adouble *ad_init(TALLOC_CTX *ctx, vfs_handle_struct *handle,
389                                adouble_type_t type, files_struct *fsp);
390 static int ad_write(struct adouble *ad, const char *path);
391 static int adouble_path(TALLOC_CTX *ctx, const char *path_in, char **path_out);
392
393 /**
394  * Get a date
395  **/
396 static int ad_getdate(const struct adouble *ad,
397                       unsigned int dateoff,
398                       uint32_t *date)
399 {
400         bool xlate = (dateoff & AD_DATE_UNIX);
401
402         dateoff &= AD_DATE_MASK;
403         if (!ad_getentryoff(ad, ADEID_FILEDATESI)) {
404                 return -1;
405         }
406
407         if (dateoff > AD_DATE_ACCESS) {
408             return -1;
409         }
410         memcpy(date,
411                ad_entry(ad, ADEID_FILEDATESI) + dateoff,
412                sizeof(uint32_t));
413
414         if (xlate) {
415                 *date = AD_DATE_TO_UNIX(*date);
416         }
417         return 0;
418 }
419
420 /**
421  * Set a date
422  **/
423 static int ad_setdate(struct adouble *ad, unsigned int dateoff, uint32_t date)
424 {
425         bool xlate = (dateoff & AD_DATE_UNIX);
426
427         if (!ad_getentryoff(ad, ADEID_FILEDATESI)) {
428                 return 0;
429         }
430
431         dateoff &= AD_DATE_MASK;
432         if (xlate) {
433                 date = AD_DATE_FROM_UNIX(date);
434         }
435
436         if (dateoff > AD_DATE_ACCESS) {
437                 return -1;
438         }
439
440         memcpy(ad_entry(ad, ADEID_FILEDATESI) + dateoff, &date, sizeof(date));
441
442         return 0;
443 }
444
445
446 /**
447  * Map on-disk AppleDouble id to enumerated id
448  **/
449 static uint32_t get_eid(uint32_t eid)
450 {
451         if (eid <= 15) {
452                 return eid;
453         }
454
455         switch (eid) {
456         case AD_DEV:
457                 return ADEID_PRIVDEV;
458         case AD_INO:
459                 return ADEID_PRIVINO;
460         case AD_SYN:
461                 return ADEID_PRIVSYN;
462         case AD_ID:
463                 return ADEID_PRIVID;
464         default:
465                 break;
466         }
467
468         return 0;
469 }
470
471 /**
472  * Pack AppleDouble structure into data buffer
473  **/
474 static bool ad_pack(struct adouble *ad)
475 {
476         uint32_t       eid;
477         uint16_t       nent;
478         uint32_t       bufsize;
479         uint32_t       offset = 0;
480
481         bufsize = talloc_get_size(ad->ad_data);
482
483         if (offset + ADEDLEN_MAGIC < offset ||
484                         offset + ADEDLEN_MAGIC >= bufsize) {
485                 return false;
486         }
487         RSIVAL(ad->ad_data, offset, ad->ad_magic);
488         offset += ADEDLEN_MAGIC;
489
490         if (offset + ADEDLEN_VERSION < offset ||
491                         offset + ADEDLEN_VERSION >= bufsize) {
492                 return false;
493         }
494         RSIVAL(ad->ad_data, offset, ad->ad_version);
495         offset += ADEDLEN_VERSION;
496
497         if (offset + ADEDLEN_FILLER < offset ||
498                         offset + ADEDLEN_FILLER >= bufsize) {
499                 return false;
500         }
501         if (ad->ad_type == ADOUBLE_RSRC) {
502                 memcpy(ad->ad_data + offset, AD_FILLER_TAG, ADEDLEN_FILLER);
503         }
504         offset += ADEDLEN_FILLER;
505
506         if (offset + ADEDLEN_NENTRIES < offset ||
507                         offset + ADEDLEN_NENTRIES >= bufsize) {
508                 return false;
509         }
510         offset += ADEDLEN_NENTRIES;
511
512         for (eid = 0, nent = 0; eid < ADEID_MAX; eid++) {
513                 if ((ad->ad_eid[eid].ade_off == 0)) {
514                         /*
515                          * ade_off is also used as indicator whether a
516                          * specific entry is used or not
517                          */
518                         continue;
519                 }
520
521                 if (offset + AD_ENTRY_LEN_EID < offset ||
522                                 offset + AD_ENTRY_LEN_EID >= bufsize) {
523                         return false;
524                 }
525                 RSIVAL(ad->ad_data, offset, AD_EID_DISK(eid));
526                 offset += AD_ENTRY_LEN_EID;
527
528                 if (offset + AD_ENTRY_LEN_OFF < offset ||
529                                 offset + AD_ENTRY_LEN_OFF >= bufsize) {
530                         return false;
531                 }
532                 RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_off);
533                 offset += AD_ENTRY_LEN_OFF;
534
535                 if (offset + AD_ENTRY_LEN_LEN < offset ||
536                                 offset + AD_ENTRY_LEN_LEN >= bufsize) {
537                         return false;
538                 }
539                 RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_len);
540                 offset += AD_ENTRY_LEN_LEN;
541
542                 nent++;
543         }
544
545         if (ADEDOFF_NENTRIES + 2 >= bufsize) {
546                 return false;
547         }
548         RSSVAL(ad->ad_data, ADEDOFF_NENTRIES, nent);
549
550         return 0;
551 }
552
553 /**
554  * Unpack an AppleDouble blob into a struct adoble
555  **/
556 static bool ad_unpack(struct adouble *ad, const int nentries)
557 {
558         size_t bufsize = talloc_get_size(ad->ad_data);
559         int adentries, i;
560         uint32_t eid, len, off;
561
562         /*
563          * The size of the buffer ad->ad_data is checked when read, so
564          * we wouldn't have to check our own offsets, a few extra
565          * checks won't hurt though. We have to check the offsets we
566          * read from the buffer anyway.
567          */
568
569         if (bufsize < (AD_HEADER_LEN + (AD_ENTRY_LEN * nentries))) {
570                 DEBUG(1, ("bad size\n"));
571                 return false;
572         }
573
574         ad->ad_magic = RIVAL(ad->ad_data, 0);
575         ad->ad_version = RIVAL(ad->ad_data, ADEDOFF_VERSION);
576         if ((ad->ad_magic != AD_MAGIC) || (ad->ad_version != AD_VERSION)) {
577                 DEBUG(1, ("wrong magic or version\n"));
578                 return false;
579         }
580
581         adentries = RSVAL(ad->ad_data, ADEDOFF_NENTRIES);
582         if (adentries != nentries) {
583                 DEBUG(1, ("invalid number of entries: %d\n", adentries));
584                 return false;
585         }
586
587         /* now, read in the entry bits */
588         for (i = 0; i < adentries; i++) {
589                 eid = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN));
590                 eid = get_eid(eid);
591                 off = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 4);
592                 len = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 8);
593
594                 if (!eid || eid > ADEID_MAX) {
595                         DEBUG(1, ("bogus eid %d\n", eid));
596                         return false;
597                 }
598
599                 if ((off > bufsize) && (eid != ADEID_RFORK)) {
600                         DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
601                                   eid, off, len));
602                         return false;
603                 }
604                 if ((eid != ADEID_RFORK) &&
605                     (eid != ADEID_FINDERI) &&
606                     ((off + len) > bufsize)) {
607                         DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
608                                   eid, off, len));
609                         return false;
610                 }
611
612                 ad->ad_eid[eid].ade_off = off;
613                 ad->ad_eid[eid].ade_len = len;
614         }
615
616         return true;
617 }
618
619 /**
620  * Convert from Apple's ._ file to Netatalk
621  *
622  * Apple's AppleDouble may contain a FinderInfo entry longer then 32
623  * bytes containing packed xattrs. Netatalk can't deal with that, so
624  * we simply discard the packed xattrs.
625  *
626  * @return -1 in case an error occured, 0 if no conversion was done, 1
627  * otherwise
628  **/
629 static int ad_convert(struct adouble *ad, int fd)
630 {
631         int rc = 0;
632         char *map = MAP_FAILED;
633         size_t origlen;
634
635         origlen = ad_getentryoff(ad, ADEID_RFORK) +
636                 ad_getentrylen(ad, ADEID_RFORK);
637
638         /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
639         map = mmap(NULL, origlen, PROT_WRITE, MAP_SHARED, fd, 0);
640         if (map == MAP_FAILED) {
641                 DEBUG(2, ("mmap AppleDouble: %s\n", strerror(errno)));
642                 rc = -1;
643                 goto exit;
644         }
645
646         memmove(map + ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI,
647                 map + ad_getentryoff(ad, ADEID_RFORK),
648                 ad_getentrylen(ad, ADEID_RFORK));
649
650         ad_setentrylen(ad, ADEID_FINDERI, ADEDLEN_FINDERI);
651         ad_setentryoff(ad, ADEID_RFORK,
652                        ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI);
653
654         /*
655          * FIXME: direct ftruncate(), but we don't have a fsp for the
656          * VFS call
657          */
658         rc = ftruncate(fd, ad_getentryoff(ad, ADEID_RFORK)
659                        + ad_getentrylen(ad, ADEID_RFORK));
660
661 exit:
662         if (map != MAP_FAILED) {
663                 munmap(map, origlen);
664         }
665         return rc;
666 }
667
668 /**
669  * Read and parse Netatalk AppleDouble metadata xattr
670  **/
671 static ssize_t ad_header_read_meta(struct adouble *ad, const char *path)
672 {
673         int      rc = 0;
674         ssize_t  ealen;
675         bool     ok;
676
677         DEBUG(10, ("reading meta xattr for %s\n", path));
678
679         ealen = SMB_VFS_GETXATTR(ad->ad_handle->conn, path,
680                                  AFPINFO_EA_NETATALK, ad->ad_data,
681                                  AD_DATASZ_XATTR);
682         if (ealen == -1) {
683                 switch (errno) {
684                 case ENOATTR:
685                 case ENOENT:
686                         if (errno == ENOATTR) {
687                                 errno = ENOENT;
688                         }
689                         rc = -1;
690                         goto exit;
691                 default:
692                         DEBUG(2, ("error reading meta xattr: %s\n",
693                                   strerror(errno)));
694                         rc = -1;
695                         goto exit;
696                 }
697         }
698         if (ealen != AD_DATASZ_XATTR) {
699                 DEBUG(2, ("bad size %zd\n", ealen));
700                 errno = EINVAL;
701                 rc = -1;
702                 goto exit;
703         }
704
705         /* Now parse entries */
706         ok = ad_unpack(ad, ADEID_NUM_XATTR);
707         if (!ok) {
708                 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
709                 errno = EINVAL;
710                 rc = -1;
711                 goto exit;
712         }
713
714         if (!ad_getentryoff(ad, ADEID_FINDERI)
715             || !ad_getentryoff(ad, ADEID_COMMENT)
716             || !ad_getentryoff(ad, ADEID_FILEDATESI)
717             || !ad_getentryoff(ad, ADEID_AFPFILEI)
718             || !ad_getentryoff(ad, ADEID_PRIVDEV)
719             || !ad_getentryoff(ad, ADEID_PRIVINO)
720             || !ad_getentryoff(ad, ADEID_PRIVSYN)
721             || !ad_getentryoff(ad, ADEID_PRIVID)) {
722                 DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
723                 errno = EINVAL;
724                 rc = -1;
725                 goto exit;
726         }
727
728 exit:
729         DEBUG(10, ("reading meta xattr for %s, rc: %d\n", path, rc));
730
731         if (rc != 0) {
732                 ealen = -1;
733                 if (errno == EINVAL) {
734                         become_root();
735                         removexattr(path, AFPINFO_EA_NETATALK);
736                         unbecome_root();
737                         errno = ENOENT;
738                 }
739         }
740         return ealen;
741 }
742
743 /**
744  * Read and parse resource fork, either ._ AppleDouble file or xattr
745  **/
746 static ssize_t ad_header_read_rsrc(struct adouble *ad, const char *path)
747 {
748         struct fruit_config_data *config = NULL;
749         int fd = -1;
750         int rc = 0;
751         ssize_t len;
752         char *adpath = NULL;
753         bool opened = false;
754         int mode;
755         struct adouble *meta_ad = NULL;
756         SMB_STRUCT_STAT sbuf;
757         bool ok;
758         int saved_errno;
759
760         SMB_VFS_HANDLE_GET_DATA(ad->ad_handle, config,
761                                 struct fruit_config_data, return -1);
762
763         if (ad->ad_fsp && ad->ad_fsp->fh && (ad->ad_fsp->fh->fd != -1)) {
764                 fd = ad->ad_fsp->fh->fd;
765         } else {
766                 if (config->rsrc == FRUIT_RSRC_XATTR) {
767                         adpath = talloc_strdup(talloc_tos(), path);
768                 } else {
769                         rc = adouble_path(talloc_tos(), path, &adpath);
770                         if (rc != 0) {
771                                 goto exit;
772                         }
773                 }
774
775                 /* Try rw first so we can use the fd in ad_convert() */
776                 mode = O_RDWR;
777
778         retry:
779                 if (config->rsrc == FRUIT_RSRC_XATTR) {
780 #ifndef HAVE_ATTROPEN
781                         errno = ENOSYS;
782                         rc = -1;
783                         goto exit;
784 #else
785                         /* FIXME: direct Solaris xattr syscall */
786                         fd = attropen(adpath, AFPRESOURCE_EA_NETATALK,
787                                       mode, 0);
788 #endif
789                 } else {
790                         /* FIXME: direct open(), don't have an fsp */
791                         fd = open(adpath, mode);
792                 }
793
794                 if (fd == -1) {
795                         switch (errno) {
796                         case EROFS:
797                         case EACCES:
798                                 if (mode == O_RDWR) {
799                                         mode = O_RDONLY;
800                                         goto retry;
801                                 }
802                                 /* fall through ... */
803                         default:
804                                 DEBUG(2, ("open AppleDouble: %s, %s\n",
805                                           adpath, strerror(errno)));
806                                 rc = -1;
807                                 goto exit;
808                         }
809                 }
810                 opened = true;
811         }
812
813         if (config->rsrc == FRUIT_RSRC_XATTR) {
814                 /* FIXME: direct sys_fstat(), don't have an fsp */
815                 rc = sys_fstat(
816                         fd, &sbuf,
817                         lp_fake_directory_create_times(
818                                 SNUM(ad->ad_handle->conn)));
819                 if (rc != 0) {
820                         rc = -1;
821                         goto exit;
822                 }
823                 ad_setentrylen(ad, ADEID_RFORK, sbuf.st_ex_size);
824         } else {
825                 /* FIXME: direct sys_pread(), don't have an fsp */
826                 len = sys_pread(fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
827                 if (len != AD_DATASZ_DOT_UND) {
828                         DEBUG(2, ("%s: bad size: %zd\n",
829                                   strerror(errno), len));
830                         rc = -1;
831                         goto exit;
832                 }
833
834                 /* Now parse entries */
835                 ok = ad_unpack(ad, ADEID_NUM_DOT_UND);
836                 if (!ok) {
837                         DEBUG(1, ("invalid AppleDouble ressource %s\n", path));
838                         errno = EINVAL;
839                         rc = -1;
840                         goto exit;
841                 }
842
843                 if ((ad_getentryoff(ad, ADEID_FINDERI)
844                      != ADEDOFF_FINDERI_DOT_UND)
845                     || (ad_getentrylen(ad, ADEID_FINDERI)
846                         < ADEDLEN_FINDERI)
847                     || (ad_getentryoff(ad, ADEID_RFORK)
848                         < ADEDOFF_RFORK_DOT_UND)) {
849                         DEBUG(2, ("invalid AppleDouble ressource %s\n", path));
850                         errno = EINVAL;
851                         rc = -1;
852                         goto exit;
853                 }
854
855                 if ((mode == O_RDWR)
856                     && (ad_getentrylen(ad, ADEID_FINDERI) > ADEDLEN_FINDERI)) {
857                         rc = ad_convert(ad, fd);
858                         if (rc != 0) {
859                                 rc = -1;
860                                 goto exit;
861                         }
862                         /*
863                          * Can't use ad_write() because we might not have a fsp
864                          */
865                         rc = ad_pack(ad);
866                         if (rc != 0) {
867                                 goto exit;
868                         }
869                         /* FIXME: direct sys_pwrite(), don't have an fsp */
870                         len = sys_pwrite(fd, ad->ad_data,
871                                          AD_DATASZ_DOT_UND, 0);
872                         if (len != AD_DATASZ_DOT_UND) {
873                                 DEBUG(2, ("%s: bad size: %zd\n", adpath, len));
874                                 rc = -1;
875                                 goto exit;
876                         }
877
878                         meta_ad = ad_init(talloc_tos(), ad->ad_handle,
879                                           ADOUBLE_META, NULL);
880                         if (meta_ad == NULL) {
881                                 rc = -1;
882                                 goto exit;
883                         }
884
885                         memcpy(ad_entry(meta_ad, ADEID_FINDERI),
886                                ad_entry(ad, ADEID_FINDERI),
887                                ADEDLEN_FINDERI);
888
889                         rc = ad_write(meta_ad, path);
890                         if (rc != 0) {
891                                 rc = -1;
892                                 goto exit;
893                         }
894                 }
895         }
896
897         DEBUG(10, ("opened AppleDouble: %s\n", path));
898
899 exit:
900         if (rc != 0) {
901                 saved_errno = errno;
902                 len = -1;
903         }
904         if (opened && fd != -1) {
905                 close(fd);
906         }
907         TALLOC_FREE(adpath);
908         TALLOC_FREE(meta_ad);
909         if (rc != 0) {
910                 errno = saved_errno;
911         }
912         return len;
913 }
914
915 /**
916  * Read and unpack an AppleDouble metadata xattr or resource
917  **/
918 static ssize_t ad_read(struct adouble *ad, const char *path)
919 {
920         switch (ad->ad_type) {
921         case ADOUBLE_META:
922                 return ad_header_read_meta(ad, path);
923         case ADOUBLE_RSRC:
924                 return ad_header_read_rsrc(ad, path);
925         default:
926                 return -1;
927         }
928 }
929
930 /**
931  * Allocate a struct adouble without initialiing it
932  *
933  * The struct is either hang of the fsp extension context or if fsp is
934  * NULL from ctx.
935  *
936  * @param[in] ctx        talloc context
937  * @param[in] handle     vfs handle
938  * @param[in] type       type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
939
940  * @param[in] fsp        if not NULL (for stream IO), the adouble handle is
941  *                       added as an fsp extension
942  *
943  * @return               adouble handle
944  **/
945 static struct adouble *ad_alloc(TALLOC_CTX *ctx, vfs_handle_struct *handle,
946                                 adouble_type_t type, files_struct *fsp)
947 {
948         int rc = 0;
949         size_t adsize = 0;
950         struct adouble *ad;
951         struct fruit_config_data *config;
952
953         SMB_VFS_HANDLE_GET_DATA(handle, config,
954                                 struct fruit_config_data, return NULL);
955
956         switch (type) {
957         case ADOUBLE_META:
958                 adsize = AD_DATASZ_XATTR;
959                 break;
960         case ADOUBLE_RSRC:
961                 if (config->rsrc == FRUIT_RSRC_ADFILE) {
962                         adsize = AD_DATASZ_DOT_UND;
963                 }
964                 break;
965         default:
966                 return NULL;
967         }
968
969         if (!fsp) {
970                 ad = talloc_zero(ctx, struct adouble);
971                 if (ad == NULL) {
972                         rc = -1;
973                         goto exit;
974                 }
975                 if (adsize) {
976                         ad->ad_data = talloc_zero_array(ad, char, adsize);
977                 }
978         } else {
979                 ad = (struct adouble *)VFS_ADD_FSP_EXTENSION(handle, fsp,
980                                                              struct adouble,
981                                                              NULL);
982                 if (ad == NULL) {
983                         rc = -1;
984                         goto exit;
985                 }
986                 if (adsize) {
987                         ad->ad_data = talloc_zero_array(
988                                 VFS_MEMCTX_FSP_EXTENSION(handle, fsp),
989                                 char, adsize);
990                 }
991                 ad->ad_fsp = fsp;
992         }
993
994         if (adsize && ad->ad_data == NULL) {
995                 rc = -1;
996                 goto exit;
997         }
998         ad->ad_handle = handle;
999         ad->ad_type = type;
1000         ad->ad_magic = AD_MAGIC;
1001         ad->ad_version = AD_VERSION;
1002
1003 exit:
1004         if (rc != 0) {
1005                 TALLOC_FREE(ad);
1006         }
1007         return ad;
1008 }
1009
1010 /**
1011  * Allocate and initialize a new struct adouble
1012  *
1013  * @param[in] ctx        talloc context
1014  * @param[in] handle     vfs handle
1015  * @param[in] type       type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1016  * @param[in] fsp        file handle, may be NULL for a type of e_ad_meta
1017  *
1018  * @return               adouble handle, initialized
1019  **/
1020 static struct adouble *ad_init(TALLOC_CTX *ctx, vfs_handle_struct *handle,
1021                                adouble_type_t type, files_struct *fsp)
1022 {
1023         int rc = 0;
1024         const struct ad_entry_order  *eid;
1025         struct adouble *ad = NULL;
1026         struct fruit_config_data *config;
1027         time_t t = time(NULL);
1028
1029         SMB_VFS_HANDLE_GET_DATA(handle, config,
1030                                 struct fruit_config_data, return NULL);
1031
1032         switch (type) {
1033         case ADOUBLE_META:
1034                 eid = entry_order_meta_xattr;
1035                 break;
1036         case ADOUBLE_RSRC:
1037                 if (config->rsrc == FRUIT_RSRC_ADFILE) {
1038                         eid = entry_order_dot_und;
1039                 } else {
1040                         eid = entry_order_rsrc_xattr;
1041                 }
1042                 break;
1043         default:
1044                 return NULL;
1045         }
1046
1047         ad = ad_alloc(ctx, handle, type, fsp);
1048         if (ad == NULL) {
1049                 return NULL;
1050         }
1051
1052         while (eid->id) {
1053                 ad->ad_eid[eid->id].ade_off = eid->offset;
1054                 ad->ad_eid[eid->id].ade_len = eid->len;
1055                 eid++;
1056         }
1057
1058         /* put something sane in the date fields */
1059         ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, t);
1060         ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, t);
1061         ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, t);
1062         ad_setdate(ad, AD_DATE_BACKUP, htonl(AD_DATE_START));
1063
1064         if (rc != 0) {
1065                 TALLOC_FREE(ad);
1066         }
1067         return ad;
1068 }
1069
1070 /**
1071  * Return AppleDouble data for a file
1072  *
1073  * @param[in] ctx      talloc context
1074  * @param[in] handle   vfs handle
1075  * @param[in] path     pathname to file or directory
1076  * @param[in] type     type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
1077  *
1078  * @return             talloced struct adouble or NULL on error
1079  **/
1080 static struct adouble *ad_get(TALLOC_CTX *ctx, vfs_handle_struct *handle,
1081                               const char *path, adouble_type_t type)
1082 {
1083         int rc = 0;
1084         ssize_t len;
1085         struct adouble *ad = NULL;
1086
1087         DEBUG(10, ("ad_get(%s) called for %s\n",
1088                    type == ADOUBLE_META ? "meta" : "rsrc", path));
1089
1090         ad = ad_alloc(ctx, handle, type, NULL);
1091         if (ad == NULL) {
1092                 rc = -1;
1093                 goto exit;
1094         }
1095
1096         len = ad_read(ad, path);
1097         if (len == -1) {
1098                 DEBUG(10, ("error reading AppleDouble for %s\n", path));
1099                 rc = -1;
1100                 goto exit;
1101         }
1102
1103 exit:
1104         DEBUG(10, ("ad_get(%s) for %s returning %d\n",
1105                   type == ADOUBLE_META ? "meta" : "rsrc", path, rc));
1106
1107         if (rc != 0) {
1108                 TALLOC_FREE(ad);
1109         }
1110         return ad;
1111 }
1112
1113 /**
1114  * Set AppleDouble metadata on a file or directory
1115  *
1116  * @param[in] ad      adouble handle
1117
1118  * @param[in] path    pathname to file or directory, may be NULL for a
1119  *                    resource fork
1120  *
1121  * @return            status code, 0 means success
1122  **/
1123 static int ad_write(struct adouble *ad, const char *path)
1124 {
1125         int rc = 0;
1126         ssize_t len;
1127
1128         rc = ad_pack(ad);
1129         if (rc != 0) {
1130                 goto exit;
1131         }
1132
1133         switch (ad->ad_type) {
1134         case ADOUBLE_META:
1135                 rc = SMB_VFS_SETXATTR(ad->ad_handle->conn, path,
1136                                       AFPINFO_EA_NETATALK, ad->ad_data,
1137                                       AD_DATASZ_XATTR, 0);
1138                 break;
1139         case ADOUBLE_RSRC:
1140                 if ((ad->ad_fsp == NULL)
1141                     || (ad->ad_fsp->fh == NULL)
1142                     || (ad->ad_fsp->fh->fd == -1)) {
1143                         rc = -1;
1144                         goto exit;
1145                 }
1146                 /* FIXME: direct sys_pwrite(), don't have an fsp */
1147                 len = sys_pwrite(ad->ad_fsp->fh->fd, ad->ad_data,
1148                                  talloc_get_size(ad->ad_data), 0);
1149                 if (len != talloc_get_size(ad->ad_data)) {
1150                         DEBUG(1, ("short write on %s: %zd",
1151                                   fsp_str_dbg(ad->ad_fsp), len));
1152                         rc = -1;
1153                         goto exit;
1154                 }
1155                 break;
1156         default:
1157                 return -1;
1158         }
1159 exit:
1160         return rc;
1161 }
1162
1163 /*****************************************************************************
1164  * Helper functions
1165  *****************************************************************************/
1166
1167 static bool is_afpinfo_stream(const struct smb_filename *smb_fname)
1168 {
1169         if (strncasecmp_m(smb_fname->stream_name,
1170                           AFPINFO_STREAM_NAME,
1171                           strlen(AFPINFO_STREAM_NAME)) == 0) {
1172                 return true;
1173         }
1174         return false;
1175 }
1176
1177 static bool is_afpresource_stream(const struct smb_filename *smb_fname)
1178 {
1179         if (strncasecmp_m(smb_fname->stream_name,
1180                           AFPRESOURCE_STREAM_NAME,
1181                           strlen(AFPRESOURCE_STREAM_NAME)) == 0) {
1182                 return true;
1183         }
1184         return false;
1185 }
1186
1187 /**
1188  * Test whether stream is an Apple stream, not used atm
1189  **/
1190 #if 0
1191 static bool is_apple_stream(const struct smb_filename *smb_fname)
1192 {
1193         if (is_afpinfo_stream(smb_fname)) {
1194                 return true;
1195         }
1196         if (is_afpresource_stream(smb_fname)) {
1197                 return true;
1198         }
1199         return false;
1200 }
1201 #endif
1202
1203 /**
1204  * Initialize config struct from our smb.conf config parameters
1205  **/
1206 static int init_fruit_config(vfs_handle_struct *handle)
1207 {
1208         struct fruit_config_data *config;
1209         int enumval;
1210
1211         config = talloc_zero(handle->conn, struct fruit_config_data);
1212         if (!config) {
1213                 DEBUG(1, ("talloc_zero() failed\n"));
1214                 errno = ENOMEM;
1215                 return -1;
1216         }
1217
1218         enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
1219                                "ressource", fruit_rsrc, FRUIT_RSRC_ADFILE);
1220         if (enumval == -1) {
1221                 DEBUG(1, ("value for %s: ressource type unknown\n",
1222                           FRUIT_PARAM_TYPE_NAME));
1223                 return -1;
1224         }
1225         config->rsrc = (enum fruit_rsrc)enumval;
1226
1227         enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
1228                                "metadata", fruit_meta, FRUIT_META_NETATALK);
1229         if (enumval == -1) {
1230                 DEBUG(1, ("value for %s: metadata type unknown\n",
1231                           FRUIT_PARAM_TYPE_NAME));
1232                 return -1;
1233         }
1234         config->meta = (enum fruit_meta)enumval;
1235
1236         enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
1237                                "locking", fruit_locking, FRUIT_LOCKING_NONE);
1238         if (enumval == -1) {
1239                 DEBUG(1, ("value for %s: locking type unknown\n",
1240                           FRUIT_PARAM_TYPE_NAME));
1241                 return -1;
1242         }
1243         config->locking = (enum fruit_locking)enumval;
1244
1245         enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
1246                                "encoding", fruit_encoding, FRUIT_ENC_PRIVATE);
1247         if (enumval == -1) {
1248                 DEBUG(1, ("value for %s: encoding type unknown\n",
1249                           FRUIT_PARAM_TYPE_NAME));
1250                 return -1;
1251         }
1252         config->encoding = (enum fruit_encoding)enumval;
1253
1254         SMB_VFS_HANDLE_SET_DATA(handle, config,
1255                                 NULL, struct fruit_config_data,
1256                                 return -1);
1257
1258         return 0;
1259 }
1260
1261 /**
1262  * Prepend "._" to a basename
1263  **/
1264 static int adouble_path(TALLOC_CTX *ctx, const char *path_in, char **path_out)
1265 {
1266         char *parent;
1267         const char *basename;
1268
1269         if (!parent_dirname(ctx, path_in, &parent, &basename)) {
1270                 return -1;
1271         }
1272
1273         *path_out = talloc_asprintf(ctx, "%s/._%s", parent, basename);
1274         if (*path_out == NULL) {
1275                 return -1;
1276         }
1277
1278         return 0;
1279 }
1280
1281 /**
1282  * Allocate and initialize an AfpInfo struct
1283  **/
1284 static AfpInfo *afpinfo_new(TALLOC_CTX *ctx)
1285 {
1286         AfpInfo *ai = talloc_zero(ctx, AfpInfo);
1287         if (ai == NULL) {
1288                 return NULL;
1289         }
1290         ai->afpi_Signature = AFP_Signature;
1291         ai->afpi_Version = AFP_Version;
1292         ai->afpi_BackupTime = AD_DATE_START;
1293         return ai;
1294 }
1295
1296 /**
1297  * Pack an AfpInfo struct into a buffer
1298  *
1299  * Buffer size must be at least AFP_INFO_SIZE
1300  * Returns size of packed buffer
1301  **/
1302 static ssize_t afpinfo_pack(const AfpInfo *ai, char *buf)
1303 {
1304         memset(buf, 0, AFP_INFO_SIZE);
1305
1306         RSIVAL(buf, 0, ai->afpi_Signature);
1307         RSIVAL(buf, 4, ai->afpi_Version);
1308         RSIVAL(buf, 12, ai->afpi_BackupTime);
1309         memcpy(buf + 16, ai->afpi_FinderInfo, sizeof(ai->afpi_FinderInfo));
1310
1311         return AFP_INFO_SIZE;
1312 }
1313
1314 /**
1315  * Unpack a buffer into a AfpInfo structure
1316  *
1317  * Buffer size must be at least AFP_INFO_SIZE
1318  * Returns allocated AfpInfo struct
1319  **/
1320 static AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data)
1321 {
1322         AfpInfo *ai = talloc_zero(ctx, AfpInfo);
1323         if (ai == NULL) {
1324                 return NULL;
1325         }
1326
1327         ai->afpi_Signature = RIVAL(data, 0);
1328         ai->afpi_Version = RIVAL(data, 4);
1329         ai->afpi_BackupTime = RIVAL(data, 12);
1330         memcpy(ai->afpi_FinderInfo, (const char *)data + 16,
1331                sizeof(ai->afpi_FinderInfo));
1332
1333         if (ai->afpi_Signature != AFP_Signature
1334             || ai->afpi_Version != AFP_Version) {
1335                 DEBUG(1, ("Bad AfpInfo signature or version\n"));
1336                 TALLOC_FREE(ai);
1337         }
1338
1339         return ai;
1340 }
1341
1342 /**
1343  * Fake an inode number from the md5 hash of the (xattr) name
1344  **/
1345 static SMB_INO_T fruit_inode(const SMB_STRUCT_STAT *sbuf, const char *sname)
1346 {
1347         MD5_CTX ctx;
1348         unsigned char hash[16];
1349         SMB_INO_T result;
1350         char *upper_sname;
1351
1352         upper_sname = talloc_strdup_upper(talloc_tos(), sname);
1353         SMB_ASSERT(upper_sname != NULL);
1354
1355         MD5Init(&ctx);
1356         MD5Update(&ctx, (const unsigned char *)&(sbuf->st_ex_dev),
1357                   sizeof(sbuf->st_ex_dev));
1358         MD5Update(&ctx, (const unsigned char *)&(sbuf->st_ex_ino),
1359                   sizeof(sbuf->st_ex_ino));
1360         MD5Update(&ctx, (unsigned char *)upper_sname,
1361                   talloc_get_size(upper_sname)-1);
1362         MD5Final(hash, &ctx);
1363
1364         TALLOC_FREE(upper_sname);
1365
1366         /* Hopefully all the variation is in the lower 4 (or 8) bytes! */
1367         memcpy(&result, hash, sizeof(result));
1368
1369         DEBUG(10, ("fruit_inode \"%s\": ino=0x%llu\n",
1370                    sname, (unsigned long long)result));
1371
1372         return result;
1373 }
1374
1375 /**
1376  * Ensure ad_fsp is still valid
1377  **/
1378 static bool fruit_fsp_recheck(struct adouble *ad, files_struct *fsp)
1379 {
1380         if (ad->ad_fsp == fsp) {
1381                 return true;
1382         }
1383         ad->ad_fsp = fsp;
1384
1385         return true;
1386 }
1387
1388 static bool add_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
1389                              struct stream_struct **streams,
1390                              const char *name, off_t size,
1391                              off_t alloc_size)
1392 {
1393         struct stream_struct *tmp;
1394
1395         tmp = talloc_realloc(mem_ctx, *streams, struct stream_struct,
1396                              (*num_streams)+1);
1397         if (tmp == NULL) {
1398                 return false;
1399         }
1400
1401         tmp[*num_streams].name = talloc_asprintf(tmp, "%s:$DATA", name);
1402         if (tmp[*num_streams].name == NULL) {
1403                 return false;
1404         }
1405
1406         tmp[*num_streams].size = size;
1407         tmp[*num_streams].alloc_size = alloc_size;
1408
1409         *streams = tmp;
1410         *num_streams += 1;
1411         return true;
1412 }
1413
1414 static bool empty_finderinfo(const struct adouble *ad)
1415 {
1416
1417         char emptybuf[ADEDLEN_FINDERI] = {0};
1418         if (memcmp(emptybuf,
1419                    ad_entry(ad, ADEID_FINDERI),
1420                    ADEDLEN_FINDERI) == 0) {
1421                 return true;
1422         }
1423         return false;
1424 }
1425
1426 /**
1427  * Update btime with btime from Netatalk
1428  **/
1429 static void update_btime(vfs_handle_struct *handle,
1430                          struct smb_filename *smb_fname)
1431 {
1432         uint32_t t;
1433         struct timespec creation_time = {0};
1434         struct adouble *ad;
1435
1436         ad = ad_get(talloc_tos(), handle, smb_fname->base_name, ADOUBLE_META);
1437         if (ad == NULL) {
1438                 return;
1439         }
1440         if (ad_getdate(ad, AD_DATE_UNIX | AD_DATE_CREATE, &t) != 0) {
1441                 TALLOC_FREE(ad);
1442                 return;
1443         }
1444         TALLOC_FREE(ad);
1445
1446         creation_time.tv_sec = convert_uint32_t_to_time_t(t);
1447         update_stat_ex_create_time(&smb_fname->st, creation_time);
1448
1449         return;
1450 }
1451
1452 /**
1453  * Map an access mask to a Netatalk single byte byte range lock
1454  **/
1455 static off_t access_to_netatalk_brl(enum apple_fork fork,
1456                                     uint32_t access_mask)
1457 {
1458         off_t offset;
1459
1460         switch (access_mask) {
1461         case FILE_READ_DATA:
1462                 offset = AD_FILELOCK_OPEN_RD;
1463                 break;
1464
1465         case FILE_WRITE_DATA:
1466         case FILE_APPEND_DATA:
1467                 offset = AD_FILELOCK_OPEN_WR;
1468                 break;
1469
1470         default:
1471                 offset = AD_FILELOCK_OPEN_NONE;
1472                 break;
1473         }
1474
1475         if (fork == APPLE_FORK_RSRC) {
1476                 if (offset == AD_FILELOCK_OPEN_NONE) {
1477                         offset = AD_FILELOCK_RSRC_OPEN_NONE;
1478                 } else {
1479                         offset += 2;
1480                 }
1481         }
1482
1483         return offset;
1484 }
1485
1486 /**
1487  * Map a deny mode to a Netatalk brl
1488  **/
1489 static off_t denymode_to_netatalk_brl(enum apple_fork fork,
1490                                       uint32_t deny_mode)
1491 {
1492         off_t offset;
1493
1494         switch (deny_mode) {
1495         case DENY_READ:
1496                 offset = AD_FILELOCK_DENY_RD;
1497                 break;
1498
1499         case DENY_WRITE:
1500                 offset = AD_FILELOCK_DENY_WR;
1501                 break;
1502
1503         default:
1504                 smb_panic("denymode_to_netatalk_brl: bad deny mode\n");
1505         }
1506
1507         if (fork == APPLE_FORK_RSRC) {
1508                 offset += 2;
1509         }
1510
1511         return offset;
1512 }
1513
1514 /**
1515  * Call fcntl() with an exclusive F_GETLK request in order to
1516  * determine if there's an exisiting shared lock
1517  *
1518  * @return true if the requested lock was found or any error occured
1519  *         false if the lock was not found
1520  **/
1521 static bool test_netatalk_lock(files_struct *fsp, off_t in_offset)
1522 {
1523         bool result;
1524         off_t offset = in_offset;
1525         off_t len = 1;
1526         int type = F_WRLCK;
1527         pid_t pid;
1528
1529         result = SMB_VFS_GETLOCK(fsp, &offset, &len, &type, &pid);
1530         if (result == false) {
1531                 return true;
1532         }
1533
1534         if (type != F_UNLCK) {
1535                 return true;
1536         }
1537
1538         return false;
1539 }
1540
1541 static NTSTATUS fruit_check_access(vfs_handle_struct *handle,
1542                                    files_struct *fsp,
1543                                    uint32_t access_mask,
1544                                    uint32_t deny_mode)
1545 {
1546         NTSTATUS status = NT_STATUS_OK;
1547         struct byte_range_lock *br_lck = NULL;
1548         bool open_for_reading, open_for_writing, deny_read, deny_write;
1549         off_t off;
1550
1551         /* FIXME: hardcoded data fork, add resource fork */
1552         enum apple_fork fork = APPLE_FORK_DATA;
1553
1554         DEBUG(10, ("fruit_check_access: %s, am: %s/%s, dm: %s/%s\n",
1555                   fsp_str_dbg(fsp),
1556                   access_mask & FILE_READ_DATA ? "READ" :"-",
1557                   access_mask & FILE_WRITE_DATA ? "WRITE" : "-",
1558                   deny_mode & DENY_READ ? "DENY_READ" : "-",
1559                   deny_mode & DENY_WRITE ? "DENY_WRITE" : "-"));
1560
1561         /*
1562          * Check read access and deny read mode
1563          */
1564         if ((access_mask & FILE_READ_DATA) || (deny_mode & DENY_READ)) {
1565                 /* Check access */
1566                 open_for_reading = test_netatalk_lock(
1567                         fsp, access_to_netatalk_brl(fork, FILE_READ_DATA));
1568
1569                 deny_read = test_netatalk_lock(
1570                         fsp, denymode_to_netatalk_brl(fork, DENY_READ));
1571
1572                 DEBUG(10, ("read: %s, deny_write: %s\n",
1573                           open_for_reading == true ? "yes" : "no",
1574                           deny_read == true ? "yes" : "no"));
1575
1576                 if (((access_mask & FILE_READ_DATA) && deny_read)
1577                     || ((deny_mode & DENY_READ) && open_for_reading)) {
1578                         return NT_STATUS_SHARING_VIOLATION;
1579                 }
1580
1581                 /* Set locks */
1582                 if (access_mask & FILE_READ_DATA) {
1583                         off = access_to_netatalk_brl(fork, FILE_READ_DATA);
1584                         br_lck = do_lock(
1585                                 handle->conn->sconn->msg_ctx, fsp,
1586                                 fsp->op->global->open_persistent_id, 1, off,
1587                                 READ_LOCK, POSIX_LOCK, false,
1588                                 &status, NULL);
1589
1590                         if (!NT_STATUS_IS_OK(status))  {
1591                                 return status;
1592                         }
1593                         TALLOC_FREE(br_lck);
1594                 }
1595
1596                 if (deny_mode & DENY_READ) {
1597                         off = denymode_to_netatalk_brl(fork, DENY_READ);
1598                         br_lck = do_lock(
1599                                 handle->conn->sconn->msg_ctx, fsp,
1600                                 fsp->op->global->open_persistent_id, 1, off,
1601                                 READ_LOCK, POSIX_LOCK, false,
1602                                 &status, NULL);
1603
1604                         if (!NT_STATUS_IS_OK(status)) {
1605                                 return status;
1606                         }
1607                         TALLOC_FREE(br_lck);
1608                 }
1609         }
1610
1611         /*
1612          * Check write access and deny write mode
1613          */
1614         if ((access_mask & FILE_WRITE_DATA) || (deny_mode & DENY_WRITE)) {
1615                 /* Check access */
1616                 open_for_writing = test_netatalk_lock(
1617                         fsp, access_to_netatalk_brl(fork, FILE_WRITE_DATA));
1618
1619                 deny_write = test_netatalk_lock(
1620                         fsp, denymode_to_netatalk_brl(fork, DENY_WRITE));
1621
1622                 DEBUG(10, ("write: %s, deny_write: %s\n",
1623                           open_for_writing == true ? "yes" : "no",
1624                           deny_write == true ? "yes" : "no"));
1625
1626                 if (((access_mask & FILE_WRITE_DATA) && deny_write)
1627                     || ((deny_mode & DENY_WRITE) && open_for_writing)) {
1628                         return NT_STATUS_SHARING_VIOLATION;
1629                 }
1630
1631                 /* Set locks */
1632                 if (access_mask & FILE_WRITE_DATA) {
1633                         off = access_to_netatalk_brl(fork, FILE_WRITE_DATA);
1634                         br_lck = do_lock(
1635                                 handle->conn->sconn->msg_ctx, fsp,
1636                                 fsp->op->global->open_persistent_id, 1, off,
1637                                 READ_LOCK, POSIX_LOCK, false,
1638                                 &status, NULL);
1639
1640                         if (!NT_STATUS_IS_OK(status)) {
1641                                 return status;
1642                         }
1643                         TALLOC_FREE(br_lck);
1644
1645                 }
1646                 if (deny_mode & DENY_WRITE) {
1647                         off = denymode_to_netatalk_brl(fork, DENY_WRITE);
1648                         br_lck = do_lock(
1649                                 handle->conn->sconn->msg_ctx, fsp,
1650                                 fsp->op->global->open_persistent_id, 1, off,
1651                                 READ_LOCK, POSIX_LOCK, false,
1652                                 &status, NULL);
1653
1654                         if (!NT_STATUS_IS_OK(status)) {
1655                                 return status;
1656                         }
1657                         TALLOC_FREE(br_lck);
1658                 }
1659         }
1660
1661         TALLOC_FREE(br_lck);
1662
1663         return status;
1664 }
1665
1666 /****************************************************************************
1667  * VFS ops
1668  ****************************************************************************/
1669
1670 static int fruit_connect(vfs_handle_struct *handle,
1671                          const char *service,
1672                          const char *user)
1673 {
1674         int rc;
1675         char *list = NULL, *newlist = NULL;
1676         struct fruit_config_data *config;
1677
1678         DEBUG(10, ("fruit_connect\n"));
1679
1680         rc = SMB_VFS_NEXT_CONNECT(handle, service, user);
1681         if (rc < 0) {
1682                 return rc;
1683         }
1684
1685         list = lp_veto_files(talloc_tos(), SNUM(handle->conn));
1686
1687         if (list) {
1688                 if (strstr(list, "/" ADOUBLE_NAME_PREFIX "*/") == NULL) {
1689                         newlist = talloc_asprintf(
1690                                 list,
1691                                 "%s/" ADOUBLE_NAME_PREFIX "*/",
1692                                 list);
1693                         lp_do_parameter(SNUM(handle->conn),
1694                                         "veto files",
1695                                         newlist);
1696                 }
1697         } else {
1698                 lp_do_parameter(SNUM(handle->conn),
1699                                 "veto files",
1700                                 "/" ADOUBLE_NAME_PREFIX "*/");
1701         }
1702
1703         TALLOC_FREE(list);
1704
1705         rc = init_fruit_config(handle);
1706         if (rc != 0) {
1707                 return rc;
1708         }
1709
1710         SMB_VFS_HANDLE_GET_DATA(handle, config,
1711                                 struct fruit_config_data, return -1);
1712
1713         if (config->encoding == FRUIT_ENC_NATIVE) {
1714                 lp_do_parameter(
1715                         SNUM(handle->conn),
1716                         "catia:mappings",
1717                         "0x22:0xf020,0x2a:0xf021,0x3a:0xf022,0x3c:0xf023,"
1718                         "0x3e:0xf024,0x3f:0xf025,0x5c:0xf026,0x7c:0xf027,"
1719                         "0x0d:0xf00d");
1720         }
1721
1722         return rc;
1723 }
1724
1725 static int fruit_open_meta(vfs_handle_struct *handle,
1726                            struct smb_filename *smb_fname,
1727                            files_struct *fsp, int flags, mode_t mode)
1728 {
1729         int rc = 0;
1730         struct fruit_config_data *config = NULL;
1731         struct smb_filename *smb_fname_base = NULL;
1732         int baseflags;
1733         int hostfd = -1;
1734         struct adouble *ad = NULL;
1735
1736         DEBUG(10, ("fruit_open_meta for %s\n", smb_fname_str_dbg(smb_fname)));
1737
1738         SMB_VFS_HANDLE_GET_DATA(handle, config,
1739                                 struct fruit_config_data, return -1);
1740
1741         if (config->meta == FRUIT_META_STREAM) {
1742                 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
1743         }
1744
1745         /* Create an smb_filename with stream_name == NULL. */
1746         smb_fname_base = synthetic_smb_fname(talloc_tos(),
1747                                              smb_fname->base_name, NULL, NULL);
1748
1749         if (smb_fname_base == NULL) {
1750                 errno = ENOMEM;
1751                 rc = -1;
1752                 goto exit;
1753         }
1754
1755         /*
1756          * We use baseflags to turn off nasty side-effects when opening the
1757          * underlying file.
1758          */
1759         baseflags = flags;
1760         baseflags &= ~O_TRUNC;
1761         baseflags &= ~O_EXCL;
1762         baseflags &= ~O_CREAT;
1763
1764         hostfd = SMB_VFS_OPEN(handle->conn, smb_fname_base, fsp,
1765                               baseflags, mode);
1766
1767         /*
1768          * It is legit to open a stream on a directory, but the base
1769          * fd has to be read-only.
1770          */
1771         if ((hostfd == -1) && (errno == EISDIR)) {
1772                 baseflags &= ~O_ACCMODE;
1773                 baseflags |= O_RDONLY;
1774                 hostfd = SMB_VFS_OPEN(handle->conn, smb_fname_base, fsp,
1775                                       baseflags, mode);
1776         }
1777
1778         TALLOC_FREE(smb_fname_base);
1779
1780         if (hostfd == -1) {
1781                 rc = -1;
1782                 goto exit;
1783         }
1784
1785         if (flags & (O_CREAT | O_TRUNC)) {
1786                 /*
1787                  * The attribute does not exist or needs to be truncated,
1788                  * create an AppleDouble EA
1789                  */
1790                 ad = ad_init(VFS_MEMCTX_FSP_EXTENSION(handle, fsp),
1791                              handle, ADOUBLE_META, fsp);
1792                 if (ad == NULL) {
1793                         rc = -1;
1794                         goto exit;
1795                 }
1796
1797                 rc = ad_write(ad, smb_fname->base_name);
1798                 if (rc != 0) {
1799                         rc = -1;
1800                         goto exit;
1801                 }
1802         } else {
1803                 ad = ad_alloc(VFS_MEMCTX_FSP_EXTENSION(handle, fsp),
1804                               handle, ADOUBLE_META, fsp);
1805                 if (ad == NULL) {
1806                         rc = -1;
1807                         goto exit;
1808                 }
1809                 if (ad_read(ad, smb_fname->base_name) == -1) {
1810                         rc = -1;
1811                         goto exit;
1812                 }
1813         }
1814
1815 exit:
1816         DEBUG(10, ("fruit_open meta rc=%d, fd=%d\n", rc, hostfd));
1817         if (rc != 0) {
1818                 int saved_errno = errno;
1819                 if (hostfd >= 0) {
1820                         /*
1821                          * BUGBUGBUG -- we would need to call
1822                          * fd_close_posix here, but we don't have a
1823                          * full fsp yet
1824                          */
1825                         fsp->fh->fd = hostfd;
1826                         SMB_VFS_CLOSE(fsp);
1827                 }
1828                 hostfd = -1;
1829                 errno = saved_errno;
1830         }
1831         return hostfd;
1832 }
1833
1834 static int fruit_open_rsrc(vfs_handle_struct *handle,
1835                            struct smb_filename *smb_fname,
1836                            files_struct *fsp, int flags, mode_t mode)
1837 {
1838         int rc = 0;
1839         struct fruit_config_data *config = NULL;
1840         struct adouble *ad = NULL;
1841         struct smb_filename *smb_fname_base = NULL;
1842         char *adpath = NULL;
1843         int hostfd = -1;
1844
1845         DEBUG(10, ("fruit_open_rsrc for %s\n", smb_fname_str_dbg(smb_fname)));
1846
1847         SMB_VFS_HANDLE_GET_DATA(handle, config,
1848                                 struct fruit_config_data, return -1);
1849
1850         switch (config->rsrc) {
1851         case FRUIT_RSRC_STREAM:
1852                 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
1853         case FRUIT_RSRC_XATTR:
1854 #ifdef HAVE_ATTROPEN
1855                 hostfd = attropen(smb_fname->base_name,
1856                                   AFPRESOURCE_EA_NETATALK, flags, mode);
1857                 if (hostfd == -1) {
1858                         return -1;
1859                 }
1860                 ad = ad_init(VFS_MEMCTX_FSP_EXTENSION(handle, fsp),
1861                              handle, ADOUBLE_RSRC, fsp);
1862                 if (ad == NULL) {
1863                         rc = -1;
1864                         goto exit;
1865                 }
1866                 goto exit;
1867 #else
1868                 errno = ENOTSUP;
1869                 return -1;
1870 #endif
1871         default:
1872                 break;
1873         }
1874
1875         if (!(flags & O_CREAT) && !VALID_STAT(smb_fname->st)) {
1876                 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
1877                 if (rc != 0) {
1878                         rc = -1;
1879                         goto exit;
1880                 }
1881         }
1882
1883         if (VALID_STAT(smb_fname->st) && S_ISDIR(smb_fname->st.st_ex_mode)) {
1884                 /* sorry, but directories don't habe a resource fork */
1885                 rc = -1;
1886                 goto exit;
1887         }
1888
1889         rc = adouble_path(talloc_tos(), smb_fname->base_name, &adpath);
1890         if (rc != 0) {
1891                 goto exit;
1892         }
1893
1894         /* Create an smb_filename with stream_name == NULL. */
1895         smb_fname_base = synthetic_smb_fname(talloc_tos(),
1896                                              adpath, NULL, NULL);
1897         if (smb_fname_base == NULL) {
1898                 errno = ENOMEM;
1899                 rc = -1;
1900                 goto exit;
1901         }
1902
1903         /* Sanitize flags */
1904         if (flags & O_WRONLY) {
1905                 /* We always need read access for the metadata header too */
1906                 flags &= ~O_WRONLY;
1907                 flags |= O_RDWR;
1908         }
1909
1910         hostfd = SMB_VFS_OPEN(handle->conn, smb_fname_base, fsp,
1911                               flags, mode);
1912         if (hostfd == -1) {
1913                 rc = -1;
1914                 goto exit;
1915         }
1916
1917         /* REVIEW: we need this in ad_write() */
1918         fsp->fh->fd = hostfd;
1919
1920         if (flags & (O_CREAT | O_TRUNC)) {
1921                 ad = ad_init(VFS_MEMCTX_FSP_EXTENSION(handle, fsp),
1922                              handle, ADOUBLE_RSRC, fsp);
1923                 if (ad == NULL) {
1924                         rc = -1;
1925                         goto exit;
1926                 }
1927                 rc = ad_write(ad, smb_fname->base_name);
1928                 if (rc != 0) {
1929                         rc = -1;
1930                         goto exit;
1931                 }
1932         } else {
1933                 ad = ad_alloc(VFS_MEMCTX_FSP_EXTENSION(handle, fsp),
1934                               handle, ADOUBLE_RSRC, fsp);
1935                 if (ad == NULL) {
1936                         rc = -1;
1937                         goto exit;
1938                 }
1939                 if (ad_read(ad, smb_fname->base_name) == -1) {
1940                         rc = -1;
1941                         goto exit;
1942                 }
1943         }
1944
1945 exit:
1946
1947         TALLOC_FREE(adpath);
1948         TALLOC_FREE(smb_fname_base);
1949
1950         DEBUG(10, ("fruit_open resource fork: rc=%d, fd=%d\n", rc, hostfd));
1951         if (rc != 0) {
1952                 int saved_errno = errno;
1953                 if (hostfd >= 0) {
1954                         /*
1955                          * BUGBUGBUG -- we would need to call
1956                          * fd_close_posix here, but we don't have a
1957                          * full fsp yet
1958                          */
1959                         fsp->fh->fd = hostfd;
1960                         SMB_VFS_CLOSE(fsp);
1961                 }
1962                 hostfd = -1;
1963                 errno = saved_errno;
1964         }
1965         return hostfd;
1966 }
1967
1968 static int fruit_open(vfs_handle_struct *handle,
1969                       struct smb_filename *smb_fname,
1970                       files_struct *fsp, int flags, mode_t mode)
1971 {
1972         DEBUG(10, ("fruit_open called for %s\n",
1973                    smb_fname_str_dbg(smb_fname)));
1974
1975         if (!is_ntfs_stream_smb_fname(smb_fname)) {
1976                 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
1977         }
1978
1979         if (is_afpinfo_stream(smb_fname)) {
1980                 return fruit_open_meta(handle, smb_fname, fsp, flags, mode);
1981         } else if (is_afpresource_stream(smb_fname)) {
1982                 return fruit_open_rsrc(handle, smb_fname, fsp, flags, mode);
1983         }
1984
1985         return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
1986 }
1987
1988 static int fruit_rename(struct vfs_handle_struct *handle,
1989                         const struct smb_filename *smb_fname_src,
1990                         const struct smb_filename *smb_fname_dst)
1991 {
1992         int rc = -1;
1993         char *src_adouble_path = NULL;
1994         char *dst_adouble_path = NULL;
1995         struct fruit_config_data *config = NULL;
1996
1997         rc = SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
1998
1999         if (!VALID_STAT(smb_fname_src->st)
2000             || !S_ISREG(smb_fname_src->st.st_ex_mode)) {
2001                 return rc;
2002         }
2003
2004         SMB_VFS_HANDLE_GET_DATA(handle, config,
2005                                 struct fruit_config_data, return -1);
2006
2007         if (config->rsrc == FRUIT_RSRC_XATTR) {
2008                 return rc;
2009         }
2010
2011         rc = adouble_path(talloc_tos(), smb_fname_src->base_name,
2012                           &src_adouble_path);
2013         if (rc != 0) {
2014                 goto done;
2015         }
2016         rc = adouble_path(talloc_tos(), smb_fname_dst->base_name,
2017                           &dst_adouble_path);
2018         if (rc != 0) {
2019                 goto done;
2020         }
2021
2022         DEBUG(10, ("fruit_rename: %s -> %s\n",
2023                    src_adouble_path, dst_adouble_path));
2024
2025         rc = rename(src_adouble_path, dst_adouble_path);
2026         if (errno == ENOENT) {
2027                 rc = 0;
2028         }
2029
2030         TALLOC_FREE(src_adouble_path);
2031         TALLOC_FREE(dst_adouble_path);
2032
2033 done:
2034         return rc;
2035 }
2036
2037 static int fruit_unlink(vfs_handle_struct *handle,
2038                         const struct smb_filename *smb_fname)
2039 {
2040         int rc = -1;
2041         struct fruit_config_data *config = NULL;
2042         char *adp = NULL;
2043
2044         if (!is_ntfs_stream_smb_fname(smb_fname)) {
2045                 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
2046         }
2047
2048         SMB_VFS_HANDLE_GET_DATA(handle, config,
2049                                 struct fruit_config_data, return -1);
2050
2051         if (is_afpinfo_stream(smb_fname)) {
2052                 if (config->meta == FRUIT_META_STREAM) {
2053                         rc = SMB_VFS_NEXT_UNLINK(handle, smb_fname);
2054                 } else {
2055                         rc = SMB_VFS_REMOVEXATTR(handle->conn,
2056                                                  smb_fname->base_name,
2057                                                  AFPINFO_EA_NETATALK);
2058                 }
2059         } else if (is_afpresource_stream(smb_fname)) {
2060                 if (config->rsrc == FRUIT_RSRC_ADFILE) {
2061                         rc = adouble_path(talloc_tos(),
2062                                           smb_fname->base_name, &adp);
2063                         if (rc != 0) {
2064                                 return -1;
2065                         }
2066                         /* FIXME: direct unlink(), missing smb_fname */
2067                         rc = unlink(adp);
2068                         if ((rc == -1) && (errno == ENOENT)) {
2069                                 rc = 0;
2070                         }
2071                 } else {
2072                         rc = SMB_VFS_REMOVEXATTR(handle->conn,
2073                                      smb_fname->base_name,
2074                                      AFPRESOURCE_EA_NETATALK);
2075                 }
2076         } else {
2077                 rc = SMB_VFS_NEXT_UNLINK(handle, smb_fname);
2078         }
2079
2080         TALLOC_FREE(adp);
2081         return rc;
2082 }
2083
2084 static int fruit_chmod(vfs_handle_struct *handle,
2085                        const char *path,
2086                        mode_t mode)
2087 {
2088         int rc = -1;
2089         char *adp = NULL;
2090         struct fruit_config_data *config = NULL;
2091         SMB_STRUCT_STAT sb;
2092
2093         rc = SMB_VFS_NEXT_CHMOD(handle, path, mode);
2094         if (rc != 0) {
2095                 return rc;
2096         }
2097
2098         SMB_VFS_HANDLE_GET_DATA(handle, config,
2099                                 struct fruit_config_data, return -1);
2100
2101         if (config->rsrc == FRUIT_RSRC_XATTR) {
2102                 return 0;
2103         }
2104
2105         /* FIXME: direct sys_lstat(), missing smb_fname */
2106         rc = sys_lstat(path, &sb, false);
2107         if (rc != 0 || !S_ISREG(sb.st_ex_mode)) {
2108                 return rc;
2109         }
2110
2111         rc = adouble_path(talloc_tos(), path, &adp);
2112         if (rc != 0) {
2113                 return -1;
2114         }
2115
2116         DEBUG(10, ("fruit_chmod: %s\n", adp));
2117
2118         rc = SMB_VFS_NEXT_CHMOD(handle, adp, mode);
2119         if (errno == ENOENT) {
2120                 rc = 0;
2121         }
2122
2123         TALLOC_FREE(adp);
2124         return rc;
2125 }
2126
2127 static int fruit_chown(vfs_handle_struct *handle,
2128                        const char *path,
2129                        uid_t uid,
2130                        gid_t gid)
2131 {
2132         int rc = -1;
2133         char *adp = NULL;
2134         struct fruit_config_data *config = NULL;
2135         SMB_STRUCT_STAT sb;
2136
2137         rc = SMB_VFS_NEXT_CHOWN(handle, path, uid, gid);
2138         if (rc != 0) {
2139                 return rc;
2140         }
2141
2142         SMB_VFS_HANDLE_GET_DATA(handle, config,
2143                                 struct fruit_config_data, return -1);
2144
2145         if (config->rsrc == FRUIT_RSRC_XATTR) {
2146                 return rc;
2147         }
2148
2149         /* FIXME: direct sys_lstat(), missing smb_fname */
2150         rc = sys_lstat(path, &sb, false);
2151         if (rc != 0 || !S_ISREG(sb.st_ex_mode)) {
2152                 return rc;
2153         }
2154
2155         rc = adouble_path(talloc_tos(), path, &adp);
2156         if (rc != 0) {
2157                 goto done;
2158         }
2159
2160         DEBUG(10, ("fruit_chown: %s\n", adp));
2161
2162         rc = SMB_VFS_NEXT_CHOWN(handle, adp, uid, gid);
2163         if (errno == ENOENT) {
2164                 rc = 0;
2165         }
2166
2167  done:
2168         TALLOC_FREE(adp);
2169         return rc;
2170 }
2171
2172 static int fruit_rmdir(struct vfs_handle_struct *handle, const char *path)
2173 {
2174         DIR *dh = NULL;
2175         struct dirent *de;
2176         struct fruit_config_data *config;
2177
2178         SMB_VFS_HANDLE_GET_DATA(handle, config,
2179                                 struct fruit_config_data, return -1);
2180
2181         if (!handle->conn->cwd || !path || (config->rsrc == FRUIT_RSRC_XATTR)) {
2182                 goto exit_rmdir;
2183         }
2184
2185         /*
2186          * Due to there is no way to change bDeleteVetoFiles variable
2187          * from this module, need to clean up ourselves
2188          */
2189         dh = opendir(path);
2190         if (dh == NULL) {
2191                 goto exit_rmdir;
2192         }
2193
2194         while ((de = readdir(dh)) != NULL) {
2195                 if ((strncmp(de->d_name,
2196                              ADOUBLE_NAME_PREFIX,
2197                              strlen(ADOUBLE_NAME_PREFIX))) == 0) {
2198                         char *p = talloc_asprintf(talloc_tos(),
2199                                                   "%s/%s",
2200                                                   path, de->d_name);
2201                         if (p == NULL) {
2202                                 goto exit_rmdir;
2203                         }
2204                         DEBUG(10, ("fruit_rmdir: delete %s\n", p));
2205                         (void)unlink(p);
2206                         TALLOC_FREE(p);
2207                 }
2208         }
2209
2210 exit_rmdir:
2211         if (dh) {
2212                 closedir(dh);
2213         }
2214         return SMB_VFS_NEXT_RMDIR(handle, path);
2215 }
2216
2217 static ssize_t fruit_pread(vfs_handle_struct *handle,
2218                            files_struct *fsp, void *data,
2219                            size_t n, off_t offset)
2220 {
2221         int rc = 0;
2222         struct adouble *ad = (struct adouble *)VFS_FETCH_FSP_EXTENSION(
2223                 handle, fsp);
2224         struct fruit_config_data *config = NULL;
2225         AfpInfo *ai = NULL;
2226         ssize_t len;
2227         char *name = NULL;
2228         char *tmp_base_name = NULL;
2229         NTSTATUS status;
2230
2231         DEBUG(10, ("fruit_pread: offset=%d, size=%d\n", (int)offset, (int)n));
2232
2233         if (!fsp->base_fsp) {
2234                 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
2235         }
2236
2237         SMB_VFS_HANDLE_GET_DATA(handle, config,
2238                                 struct fruit_config_data, return -1);
2239
2240         /* fsp_name is not converted with vfs_catia */
2241         tmp_base_name = fsp->base_fsp->fsp_name->base_name;
2242         status = SMB_VFS_TRANSLATE_NAME(handle->conn,
2243                                         fsp->base_fsp->fsp_name->base_name,
2244                                         vfs_translate_to_unix,
2245                                         talloc_tos(), &name);
2246         if (!NT_STATUS_IS_OK(status)) {
2247                 errno = map_errno_from_nt_status(status);
2248                 rc = -1;
2249                 goto exit;
2250         }
2251         fsp->base_fsp->fsp_name->base_name = name;
2252
2253         if (ad == NULL) {
2254                 len = SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
2255                 if (len == -1) {
2256                         rc = -1;
2257                         goto exit;
2258                 }
2259                 goto exit;
2260         }
2261
2262         if (!fruit_fsp_recheck(ad, fsp)) {
2263                 rc = -1;
2264                 goto exit;
2265         }
2266
2267         if (ad->ad_type == ADOUBLE_META) {
2268                 ai = afpinfo_new(talloc_tos());
2269                 if (ai == NULL) {
2270                         rc = -1;
2271                         goto exit;
2272                 }
2273
2274                 len = ad_read(ad, fsp->base_fsp->fsp_name->base_name);
2275                 if (len == -1) {
2276                         rc = -1;
2277                         goto exit;
2278                 }
2279
2280                 memcpy(&ai->afpi_FinderInfo[0],
2281                        ad_entry(ad, ADEID_FINDERI),
2282                        ADEDLEN_FINDERI);
2283                 len = afpinfo_pack(ai, data);
2284                 if (len != AFP_INFO_SIZE) {
2285                         rc = -1;
2286                         goto exit;
2287                 }
2288         } else {
2289                 len = SMB_VFS_NEXT_PREAD(
2290                         handle, fsp, data, n,
2291                         offset + ad_getentryoff(ad, ADEID_RFORK));
2292                 if (len == -1) {
2293                         rc = -1;
2294                         goto exit;
2295                 }
2296         }
2297 exit:
2298         fsp->base_fsp->fsp_name->base_name = tmp_base_name;
2299         TALLOC_FREE(name);
2300         TALLOC_FREE(ai);
2301         if (rc != 0) {
2302                 len = -1;
2303         }
2304         DEBUG(10, ("fruit_pread: rc=%d, len=%zd\n", rc, len));
2305         return len;
2306 }
2307
2308 static ssize_t fruit_pwrite(vfs_handle_struct *handle,
2309                             files_struct *fsp, const void *data,
2310                             size_t n, off_t offset)
2311 {
2312         int rc = 0;
2313         struct adouble *ad = (struct adouble *)VFS_FETCH_FSP_EXTENSION(
2314                 handle, fsp);
2315         struct fruit_config_data *config = NULL;
2316         AfpInfo *ai = NULL;
2317         ssize_t len;
2318         char *name = NULL;
2319         char *tmp_base_name = NULL;
2320         NTSTATUS status;
2321
2322         DEBUG(10, ("fruit_pwrite: offset=%d, size=%d\n", (int)offset, (int)n));
2323
2324         if (!fsp->base_fsp) {
2325                 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
2326         }
2327
2328         SMB_VFS_HANDLE_GET_DATA(handle, config,
2329                                 struct fruit_config_data, return -1);
2330
2331         tmp_base_name = fsp->base_fsp->fsp_name->base_name;
2332         status = SMB_VFS_TRANSLATE_NAME(handle->conn,
2333                                         fsp->base_fsp->fsp_name->base_name,
2334                                         vfs_translate_to_unix,
2335                                         talloc_tos(), &name);
2336         if (!NT_STATUS_IS_OK(status)) {
2337                 errno = map_errno_from_nt_status(status);
2338                 rc = -1;
2339                 goto exit;
2340         }
2341         fsp->base_fsp->fsp_name->base_name = name;
2342
2343         if (ad == NULL) {
2344                 len = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
2345                 if (len != n) {
2346                         rc = -1;
2347                         goto exit;
2348                 }
2349                 goto exit;
2350         }
2351
2352         if (!fruit_fsp_recheck(ad, fsp)) {
2353                 rc = -1;
2354                 goto exit;
2355         }
2356
2357         if (ad->ad_type == ADOUBLE_META) {
2358                 if (n != AFP_INFO_SIZE || offset != 0) {
2359                         DEBUG(1, ("unexpected offset=%jd or size=%jd\n",
2360                                   (intmax_t)offset, (intmax_t)n));
2361                         rc = -1;
2362                         goto exit;
2363                 }
2364                 ai = afpinfo_unpack(talloc_tos(), data);
2365                 if (ai == NULL) {
2366                         rc = -1;
2367                         goto exit;
2368                 }
2369                 memcpy(ad_entry(ad, ADEID_FINDERI),
2370                        &ai->afpi_FinderInfo[0], ADEDLEN_FINDERI);
2371                 rc = ad_write(ad, name);
2372         } else {
2373                 len = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n,
2374                                    offset + ad_getentryoff(ad, ADEID_RFORK));
2375                 if (len != n) {
2376                         rc = -1;
2377                         goto exit;
2378                 }
2379
2380                 if (config->rsrc == FRUIT_RSRC_ADFILE) {
2381                         rc = ad_read(ad, name);
2382                         if (rc == -1) {
2383                                 goto exit;
2384                         }
2385                         rc = 0;
2386
2387                         if ((len + offset) > ad_getentrylen(ad, ADEID_RFORK)) {
2388                                 ad_setentrylen(ad, ADEID_RFORK, len + offset);
2389                                 rc = ad_write(ad, name);
2390                         }
2391                 }
2392         }
2393
2394 exit:
2395         fsp->base_fsp->fsp_name->base_name = tmp_base_name;
2396         TALLOC_FREE(name);
2397         TALLOC_FREE(ai);
2398         if (rc != 0) {
2399                 return -1;
2400         }
2401         return n;
2402 }
2403
2404 /**
2405  * Helper to stat/lstat the base file of an smb_fname.
2406  */
2407 static int fruit_stat_base(vfs_handle_struct *handle,
2408                            struct smb_filename *smb_fname,
2409                            bool follow_links)
2410 {
2411         char *tmp_stream_name;
2412         int rc;
2413
2414         tmp_stream_name = smb_fname->stream_name;
2415         smb_fname->stream_name = NULL;
2416         if (follow_links) {
2417                 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
2418         } else {
2419                 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
2420         }
2421         smb_fname->stream_name = tmp_stream_name;
2422         return rc;
2423 }
2424
2425 static int fruit_stat_meta(vfs_handle_struct *handle,
2426                            struct smb_filename *smb_fname,
2427                            bool follow_links)
2428 {
2429         /* Populate the stat struct with info from the base file. */
2430         if (fruit_stat_base(handle, smb_fname, follow_links) == -1) {
2431                 return -1;
2432         }
2433         smb_fname->st.st_ex_size = AFP_INFO_SIZE;
2434         smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
2435                                               smb_fname->stream_name);
2436         return 0;
2437 }
2438
2439 static int fruit_stat_rsrc(vfs_handle_struct *handle,
2440                            struct smb_filename *smb_fname,
2441                            bool follow_links)
2442
2443 {
2444         struct adouble *ad = NULL;
2445
2446         DEBUG(10, ("fruit_stat_rsrc called for %s\n",
2447                    smb_fname_str_dbg(smb_fname)));
2448
2449         ad = ad_get(talloc_tos(), handle, smb_fname->base_name, ADOUBLE_RSRC);
2450         if (ad == NULL) {
2451                 errno = ENOENT;
2452                 return -1;
2453         }
2454
2455         /* Populate the stat struct with info from the base file. */
2456         if (fruit_stat_base(handle, smb_fname, follow_links) == -1) {
2457                 TALLOC_FREE(ad);
2458                 return -1;
2459         }
2460
2461         smb_fname->st.st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
2462         smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
2463                                               smb_fname->stream_name);
2464         TALLOC_FREE(ad);
2465         return 0;
2466 }
2467
2468 static int fruit_stat(vfs_handle_struct *handle,
2469                       struct smb_filename *smb_fname)
2470 {
2471         int rc = -1;
2472
2473         DEBUG(10, ("fruit_stat called for %s\n",
2474                    smb_fname_str_dbg(smb_fname)));
2475
2476         if (!is_ntfs_stream_smb_fname(smb_fname)
2477             || is_ntfs_default_stream_smb_fname(smb_fname)) {
2478                 rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
2479                 if (rc == 0) {
2480                         update_btime(handle, smb_fname);
2481                 }
2482                 return rc;
2483         }
2484
2485         /*
2486          * Note if lp_posix_paths() is true, we can never
2487          * get here as is_ntfs_stream_smb_fname() is
2488          * always false. So we never need worry about
2489          * not following links here.
2490          */
2491
2492         if (is_afpinfo_stream(smb_fname)) {
2493                 rc = fruit_stat_meta(handle, smb_fname, true);
2494         } else if (is_afpresource_stream(smb_fname)) {
2495                 rc = fruit_stat_rsrc(handle, smb_fname, true);
2496         } else {
2497                 return SMB_VFS_NEXT_STAT(handle, smb_fname);
2498         }
2499
2500         if (rc == 0) {
2501                 update_btime(handle, smb_fname);
2502                 smb_fname->st.st_ex_mode &= ~S_IFMT;
2503                 smb_fname->st.st_ex_mode |= S_IFREG;
2504                 smb_fname->st.st_ex_blocks =
2505                         smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
2506         }
2507         return rc;
2508 }
2509
2510 static int fruit_lstat(vfs_handle_struct *handle,
2511                        struct smb_filename *smb_fname)
2512 {
2513         int rc = -1;
2514
2515         DEBUG(10, ("fruit_lstat called for %s\n",
2516                    smb_fname_str_dbg(smb_fname)));
2517
2518         if (!is_ntfs_stream_smb_fname(smb_fname)
2519             || is_ntfs_default_stream_smb_fname(smb_fname)) {
2520                 rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
2521                 if (rc == 0) {
2522                         update_btime(handle, smb_fname);
2523                 }
2524                 return rc;
2525         }
2526
2527         if (is_afpinfo_stream(smb_fname)) {
2528                 rc = fruit_stat_meta(handle, smb_fname, false);
2529         } else if (is_afpresource_stream(smb_fname)) {
2530                 rc = fruit_stat_rsrc(handle, smb_fname, false);
2531         } else {
2532                 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
2533         }
2534
2535         if (rc == 0) {
2536                 update_btime(handle, smb_fname);
2537                 smb_fname->st.st_ex_mode &= ~S_IFMT;
2538                 smb_fname->st.st_ex_mode |= S_IFREG;
2539                 smb_fname->st.st_ex_blocks =
2540                         smb_fname->st.st_ex_size / STAT_ST_BLOCKSIZE + 1;
2541         }
2542         return rc;
2543 }
2544
2545 static int fruit_fstat_meta(vfs_handle_struct *handle,
2546                             files_struct *fsp,
2547                             SMB_STRUCT_STAT *sbuf)
2548 {
2549         DEBUG(10, ("fruit_fstat_meta called for %s\n",
2550                    smb_fname_str_dbg(fsp->base_fsp->fsp_name)));
2551
2552         /* Populate the stat struct with info from the base file. */
2553         if (fruit_stat_base(handle, fsp->base_fsp->fsp_name, false) == -1) {
2554                 return -1;
2555         }
2556         *sbuf = fsp->base_fsp->fsp_name->st;
2557         sbuf->st_ex_size = AFP_INFO_SIZE;
2558         sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
2559
2560         return 0;
2561 }
2562
2563 static int fruit_fstat_rsrc(vfs_handle_struct *handle, files_struct *fsp,
2564                             SMB_STRUCT_STAT *sbuf)
2565 {
2566         struct fruit_config_data *config;
2567         struct adouble *ad = (struct adouble *)VFS_FETCH_FSP_EXTENSION(
2568                 handle, fsp);
2569
2570         DEBUG(10, ("fruit_fstat_rsrc called for %s\n",
2571                    smb_fname_str_dbg(fsp->base_fsp->fsp_name)));
2572
2573         SMB_VFS_HANDLE_GET_DATA(handle, config,
2574                                 struct fruit_config_data, return -1);
2575
2576         if (config->rsrc == FRUIT_RSRC_STREAM) {
2577                 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
2578         }
2579
2580         /* Populate the stat struct with info from the base file. */
2581         if (fruit_stat_base(handle, fsp->base_fsp->fsp_name, false) == -1) {
2582                 return -1;
2583         }
2584         *sbuf = fsp->base_fsp->fsp_name->st;
2585         sbuf->st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
2586         sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
2587
2588         DEBUG(10, ("fruit_fstat_rsrc %s, size: %zd\n",
2589                    smb_fname_str_dbg(fsp->fsp_name),
2590                    (ssize_t)sbuf->st_ex_size));
2591
2592         return 0;
2593 }
2594
2595 static int fruit_fstat(vfs_handle_struct *handle, files_struct *fsp,
2596                        SMB_STRUCT_STAT *sbuf)
2597 {
2598         int rc;
2599         char *name = NULL;
2600         char *tmp_base_name = NULL;
2601         NTSTATUS status;
2602         struct adouble *ad = (struct adouble *)
2603                 VFS_FETCH_FSP_EXTENSION(handle, fsp);
2604
2605         DEBUG(10, ("fruit_fstat called for %s\n",
2606                    smb_fname_str_dbg(fsp->fsp_name)));
2607
2608         if (fsp->base_fsp) {
2609                 tmp_base_name = fsp->fsp_name->base_name;
2610                 /* fsp_name is not converted with vfs_catia */
2611                 status = SMB_VFS_TRANSLATE_NAME(
2612                         handle->conn,
2613                         fsp->base_fsp->fsp_name->base_name,
2614                         vfs_translate_to_unix,
2615                         talloc_tos(), &name);
2616
2617                 if (!NT_STATUS_IS_OK(status)) {
2618                         errno = map_errno_from_nt_status(status);
2619                         rc = -1;
2620                         goto exit;
2621                 }
2622                 fsp->base_fsp->fsp_name->base_name = name;
2623         }
2624
2625         if (ad == NULL || fsp->base_fsp == NULL) {
2626                 rc = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
2627                 goto exit;
2628         }
2629
2630         if (!fruit_fsp_recheck(ad, fsp)) {
2631                 rc = -1;
2632                 goto exit;
2633         }
2634
2635         switch (ad->ad_type) {
2636         case ADOUBLE_META:
2637                 rc = fruit_fstat_meta(handle, fsp, sbuf);
2638                 break;
2639         case ADOUBLE_RSRC:
2640                 rc = fruit_fstat_rsrc(handle, fsp, sbuf);
2641                 break;
2642         default:
2643                 DEBUG(10, ("fruit_fstat %s: bad type\n",
2644                            smb_fname_str_dbg(fsp->fsp_name)));
2645                 rc = -1;
2646                 goto exit;
2647         }
2648
2649         if (rc == 0) {
2650                 sbuf->st_ex_mode &= ~S_IFMT;
2651                 sbuf->st_ex_mode |= S_IFREG;
2652                 sbuf->st_ex_blocks = sbuf->st_ex_size / STAT_ST_BLOCKSIZE + 1;
2653         }
2654
2655 exit:
2656         DEBUG(10, ("fruit_fstat %s, size: %zd\n",
2657                    smb_fname_str_dbg(fsp->fsp_name),
2658                    (ssize_t)sbuf->st_ex_size));
2659         if (tmp_base_name) {
2660                 fsp->base_fsp->fsp_name->base_name = tmp_base_name;
2661         }
2662         TALLOC_FREE(name);
2663         return rc;
2664 }
2665
2666 static NTSTATUS fruit_streaminfo(vfs_handle_struct *handle,
2667                                  struct files_struct *fsp,
2668                                  const char *fname,
2669                                  TALLOC_CTX *mem_ctx,
2670                                  unsigned int *pnum_streams,
2671                                  struct stream_struct **pstreams)
2672 {
2673         struct fruit_config_data *config = NULL;
2674         struct smb_filename *smb_fname = NULL;
2675         struct adouble *ad = NULL;
2676
2677         SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
2678                                 return NT_STATUS_UNSUCCESSFUL);
2679         DEBUG(10, ("fruit_streaminfo called for %s\n", fname));
2680
2681         smb_fname = synthetic_smb_fname(talloc_tos(), fname, NULL, NULL);
2682         if (smb_fname == NULL) {
2683                 return NT_STATUS_NO_MEMORY;
2684         }
2685
2686         if (config->meta == FRUIT_META_NETATALK) {
2687                 ad = ad_get(talloc_tos(), handle,
2688                             smb_fname->base_name, ADOUBLE_META);
2689                 if (ad && !empty_finderinfo(ad)) {
2690                         if (!add_fruit_stream(
2691                                     mem_ctx, pnum_streams, pstreams,
2692                                     AFPINFO_STREAM_NAME, AFP_INFO_SIZE,
2693                                     smb_roundup(handle->conn,
2694                                                 AFP_INFO_SIZE))) {
2695                                 TALLOC_FREE(ad);
2696                                 TALLOC_FREE(smb_fname);
2697                                 return NT_STATUS_NO_MEMORY;
2698                         }
2699                 }
2700                 TALLOC_FREE(ad);
2701         }
2702
2703         if (config->rsrc != FRUIT_RSRC_STREAM) {
2704                 ad = ad_get(talloc_tos(), handle, smb_fname->base_name,
2705                             ADOUBLE_RSRC);
2706                 if (ad) {
2707                         if (!add_fruit_stream(
2708                                     mem_ctx, pnum_streams, pstreams,
2709                                     AFPRESOURCE_STREAM_NAME,
2710                                     ad_getentrylen(ad, ADEID_RFORK),
2711                                     smb_roundup(handle->conn,
2712                                                 ad_getentrylen(
2713                                                         ad, ADEID_RFORK)))) {
2714                                 TALLOC_FREE(ad);
2715                                 TALLOC_FREE(smb_fname);
2716                                 return NT_STATUS_NO_MEMORY;
2717                         }
2718                 }
2719                 TALLOC_FREE(ad);
2720         }
2721
2722         TALLOC_FREE(smb_fname);
2723
2724         return SMB_VFS_NEXT_STREAMINFO(handle, fsp, fname, mem_ctx,
2725                                        pnum_streams, pstreams);
2726 }
2727
2728 static int fruit_ntimes(vfs_handle_struct *handle,
2729                         const struct smb_filename *smb_fname,
2730                         struct smb_file_time *ft)
2731 {
2732         int rc = 0;
2733         struct adouble *ad = NULL;
2734
2735         if (null_timespec(ft->create_time)) {
2736                 goto exit;
2737         }
2738
2739         DEBUG(10,("set btime for %s to %s\n", smb_fname_str_dbg(smb_fname),
2740                  time_to_asc(convert_timespec_to_time_t(ft->create_time))));
2741
2742         ad = ad_get(talloc_tos(), handle, smb_fname->base_name, ADOUBLE_META);
2743         if (ad == NULL) {
2744                 goto exit;
2745         }
2746
2747         ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX,
2748                    convert_time_t_to_uint32_t(ft->create_time.tv_sec));
2749
2750         rc = ad_write(ad, smb_fname->base_name);
2751
2752 exit:
2753
2754         TALLOC_FREE(ad);
2755         if (rc != 0) {
2756                 DEBUG(1, ("fruit_ntimes: %s\n", smb_fname_str_dbg(smb_fname)));
2757                 return -1;
2758         }
2759         return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
2760 }
2761
2762 static int fruit_fallocate(struct vfs_handle_struct *handle,
2763                            struct files_struct *fsp,
2764                            enum vfs_fallocate_mode mode,
2765                            off_t offset,
2766                            off_t len)
2767 {
2768         struct adouble *ad =
2769                 (struct adouble *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
2770
2771         if (ad == NULL) {
2772                 return SMB_VFS_NEXT_FALLOCATE(handle, fsp, mode, offset, len);
2773         }
2774
2775         if (!fruit_fsp_recheck(ad, fsp)) {
2776                 return errno;
2777         }
2778
2779         /* Let the pwrite code path handle it. */
2780         return ENOSYS;
2781 }
2782
2783 static int fruit_ftruncate(struct vfs_handle_struct *handle,
2784                            struct files_struct *fsp,
2785                            off_t offset)
2786 {
2787         int rc = 0;
2788         struct adouble *ad =
2789                 (struct adouble *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
2790         struct fruit_config_data *config;
2791
2792         DEBUG(10, ("streams_xattr_ftruncate called for file %s offset %.0f\n",
2793                    fsp_str_dbg(fsp), (double)offset));
2794
2795         if (ad == NULL) {
2796                 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
2797         }
2798
2799         if (!fruit_fsp_recheck(ad, fsp)) {
2800                 return -1;
2801         }
2802
2803         SMB_VFS_HANDLE_GET_DATA(handle, config,
2804                                 struct fruit_config_data, return -1);
2805
2806         switch (ad->ad_type) {
2807         case ADOUBLE_META:
2808                 /*
2809                  * As this request hasn't been seen in the wild,
2810                  * the only sensible use I can imagine is the client
2811                  * truncating the stream to 0 bytes size.
2812                  * We simply remove the metadata on such a request.
2813                  */
2814                 if (offset == 0) {
2815                         rc = SMB_VFS_FREMOVEXATTR(fsp,
2816                                                   AFPRESOURCE_EA_NETATALK);
2817                 }
2818                 break;
2819         case ADOUBLE_RSRC:
2820                 if (config->rsrc == FRUIT_RSRC_XATTR && offset == 0) {
2821                         rc = SMB_VFS_FREMOVEXATTR(fsp,
2822                                                   AFPRESOURCE_EA_NETATALK);
2823                 } else {
2824                         rc = SMB_VFS_NEXT_FTRUNCATE(
2825                                 handle, fsp,
2826                                 offset + ad_getentryoff(ad, ADEID_RFORK));
2827                 }
2828                 break;
2829         default:
2830                 return -1;
2831         }
2832
2833         return rc;
2834 }
2835
2836 static NTSTATUS fruit_create_file(vfs_handle_struct *handle,
2837                                   struct smb_request *req,
2838                                   uint16_t root_dir_fid,
2839                                   struct smb_filename *smb_fname,
2840                                   uint32_t access_mask,
2841                                   uint32_t share_access,
2842                                   uint32_t create_disposition,
2843                                   uint32_t create_options,
2844                                   uint32_t file_attributes,
2845                                   uint32_t oplock_request,
2846                                   struct smb2_lease *lease,
2847                                   uint64_t allocation_size,
2848                                   uint32_t private_flags,
2849                                   struct security_descriptor *sd,
2850                                   struct ea_list *ea_list,
2851                                   files_struct **result,
2852                                   int *pinfo)
2853 {
2854         NTSTATUS status;
2855         struct fruit_config_data *config = NULL;
2856
2857         status = SMB_VFS_NEXT_CREATE_FILE(
2858                 handle, req, root_dir_fid, smb_fname,
2859                 access_mask, share_access,
2860                 create_disposition, create_options,
2861                 file_attributes, oplock_request,
2862                 lease,
2863                 allocation_size, private_flags,
2864                 sd, ea_list, result,
2865                 pinfo);
2866
2867         if (!NT_STATUS_IS_OK(status)) {
2868                 return status;
2869         }
2870
2871         if (is_ntfs_stream_smb_fname(smb_fname)
2872             || (*result == NULL)
2873             || ((*result)->is_directory)) {
2874                 return status;
2875         }
2876
2877         SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
2878                                 return NT_STATUS_UNSUCCESSFUL);
2879
2880         if (config->locking == FRUIT_LOCKING_NETATALK) {
2881                 status = fruit_check_access(
2882                         handle, *result,
2883                         access_mask,
2884                         map_share_mode_to_deny_mode(share_access, 0));
2885                 if (!NT_STATUS_IS_OK(status)) {
2886                         goto fail;
2887                 }
2888         }
2889
2890         return status;
2891
2892 fail:
2893         DEBUG(1, ("fruit_create_file: %s\n", nt_errstr(status)));
2894
2895         if (*result) {
2896                 close_file(req, *result, ERROR_CLOSE);
2897                 *result = NULL;
2898         }
2899
2900         return status;
2901 }
2902
2903 static struct vfs_fn_pointers vfs_fruit_fns = {
2904         .connect_fn = fruit_connect,
2905
2906         /* File operations */
2907         .chmod_fn = fruit_chmod,
2908         .chown_fn = fruit_chown,
2909         .unlink_fn = fruit_unlink,
2910         .rename_fn = fruit_rename,
2911         .rmdir_fn = fruit_rmdir,
2912         .open_fn = fruit_open,
2913         .pread_fn = fruit_pread,
2914         .pwrite_fn = fruit_pwrite,
2915         .stat_fn = fruit_stat,
2916         .lstat_fn = fruit_lstat,
2917         .fstat_fn = fruit_fstat,
2918         .streaminfo_fn = fruit_streaminfo,
2919         .ntimes_fn = fruit_ntimes,
2920         .unlink_fn = fruit_unlink,
2921         .ftruncate_fn = fruit_ftruncate,
2922         .fallocate_fn = fruit_fallocate,
2923         .create_file_fn = fruit_create_file,
2924 };
2925
2926 NTSTATUS vfs_fruit_init(void);
2927 NTSTATUS vfs_fruit_init(void)
2928 {
2929         NTSTATUS ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "fruit",
2930                                         &vfs_fruit_fns);
2931         if (!NT_STATUS_IS_OK(ret)) {
2932                 return ret;
2933         }
2934
2935         vfs_fruit_debug_level = debug_add_class("fruit");
2936         if (vfs_fruit_debug_level == -1) {
2937                 vfs_fruit_debug_level = DBGC_VFS;
2938                 DEBUG(0, ("%s: Couldn't register custom debugging class!\n",
2939                           "vfs_fruit_init"));
2940         } else {
2941                 DEBUG(10, ("%s: Debug class number of '%s': %d\n",
2942                            "vfs_fruit_init","fruit",vfs_fruit_debug_level));
2943         }
2944
2945         return ret;
2946 }