vfs_fruit: move AppleDouble functionality to a dedicated subsystem
authorRalph Boehme <slow@samba.org>
Tue, 9 Jul 2019 11:10:00 +0000 (13:10 +0200)
committerJeremy Allison <jra@samba.org>
Fri, 12 Jul 2019 21:31:29 +0000 (21:31 +0000)
It's still tied to the VFS, therefor it lives in source3/modules/. Reduces code
bloat in vfs_fruit and makes it possible to use it from other parts of the
codebase in future.

Signed-off-by: Ralph Boehme <slow@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
source3/lib/adouble.c [new file with mode: 0644]
source3/lib/adouble.h [new file with mode: 0644]
source3/modules/vfs_fruit.c
source3/modules/wscript_build
source3/wscript_build

diff --git a/source3/lib/adouble.c b/source3/lib/adouble.c
new file mode 100644 (file)
index 0000000..78ef187
--- /dev/null
@@ -0,0 +1,1884 @@
+/*
+ * Samba AppleDouble helpers
+ *
+ * Copyright (C) Ralph Boehme, 2019
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "adouble.h"
+#include "MacExtensions.h"
+#include "string_replace.h"
+#include "smbd/smbd.h"
+#include "system/filesys.h"
+#include "libcli/security/security.h"
+
+/* Number of actually used entries */
+#define ADEID_NUM_XATTR      8
+#define ADEID_NUM_DOT_UND    2
+#define ADEID_NUM_RSRC_XATTR 1
+
+/* Sizes of relevant entry bits */
+#define ADEDLEN_MAGIC       4
+#define ADEDLEN_VERSION     4
+#define ADEDLEN_FILLER      16
+#define AD_FILLER_TAG       "Netatalk        " /* should be 16 bytes */
+#define AD_FILLER_TAG_OSX   "Mac OS X        " /* should be 16 bytes */
+#define ADEDLEN_NENTRIES    2
+#define AD_HEADER_LEN       (ADEDLEN_MAGIC + ADEDLEN_VERSION + \
+                            ADEDLEN_FILLER + ADEDLEN_NENTRIES) /* 26 */
+#define AD_ENTRY_LEN_EID    4
+#define AD_ENTRY_LEN_OFF    4
+#define AD_ENTRY_LEN_LEN    4
+#define AD_ENTRY_LEN (AD_ENTRY_LEN_EID + AD_ENTRY_LEN_OFF + AD_ENTRY_LEN_LEN)
+
+/* Offsets */
+#define ADEDOFF_MAGIC         0
+#define ADEDOFF_VERSION       (ADEDOFF_MAGIC + ADEDLEN_MAGIC)
+#define ADEDOFF_FILLER        (ADEDOFF_VERSION + ADEDLEN_VERSION)
+#define ADEDOFF_NENTRIES      (ADEDOFF_FILLER + ADEDLEN_FILLER)
+
+#define ADEDOFF_FINDERI_XATTR    (AD_HEADER_LEN + \
+                                 (ADEID_NUM_XATTR * AD_ENTRY_LEN))
+#define ADEDOFF_COMMENT_XATTR    (ADEDOFF_FINDERI_XATTR    + ADEDLEN_FINDERI)
+#define ADEDOFF_FILEDATESI_XATTR (ADEDOFF_COMMENT_XATTR    + ADEDLEN_COMMENT)
+#define ADEDOFF_AFPFILEI_XATTR   (ADEDOFF_FILEDATESI_XATTR + \
+                                 ADEDLEN_FILEDATESI)
+#define ADEDOFF_PRIVDEV_XATTR    (ADEDOFF_AFPFILEI_XATTR   + ADEDLEN_AFPFILEI)
+#define ADEDOFF_PRIVINO_XATTR    (ADEDOFF_PRIVDEV_XATTR    + ADEDLEN_PRIVDEV)
+#define ADEDOFF_PRIVSYN_XATTR    (ADEDOFF_PRIVINO_XATTR    + ADEDLEN_PRIVINO)
+#define ADEDOFF_PRIVID_XATTR     (ADEDOFF_PRIVSYN_XATTR    + ADEDLEN_PRIVSYN)
+
+#define ADEDOFF_FINDERI_DOT_UND  (AD_HEADER_LEN + \
+                                 (ADEID_NUM_DOT_UND * AD_ENTRY_LEN))
+#define ADEDOFF_RFORK_DOT_UND    (ADEDOFF_FINDERI_DOT_UND + ADEDLEN_FINDERI)
+
+#define AD_DATASZ_XATTR (AD_HEADER_LEN + \
+                        (ADEID_NUM_XATTR * AD_ENTRY_LEN) + \
+                        ADEDLEN_FINDERI + ADEDLEN_COMMENT + \
+                        ADEDLEN_FILEDATESI + ADEDLEN_AFPFILEI + \
+                        ADEDLEN_PRIVDEV + ADEDLEN_PRIVINO + \
+                        ADEDLEN_PRIVSYN + ADEDLEN_PRIVID)
+
+#if AD_DATASZ_XATTR != 402
+#error bad size for AD_DATASZ_XATTR
+#endif
+
+#define AD_DATASZ_DOT_UND (AD_HEADER_LEN + \
+                          (ADEID_NUM_DOT_UND * AD_ENTRY_LEN) + \
+                          ADEDLEN_FINDERI)
+#if AD_DATASZ_DOT_UND != 82
+#error bad size for AD_DATASZ_DOT_UND
+#endif
+
+#define AD_XATTR_HDR_MAGIC    0x41545452 /* 'ATTR' */
+#define AD_XATTR_MAX_ENTRIES  1024 /* Some arbitrarily enforced limit */
+#define AD_XATTR_HDR_SIZE     36
+#define AD_XATTR_MAX_HDR_SIZE 65536
+
+/*
+ * Both struct ad_xattr_header and struct ad_xattr_entry describe the in memory
+ * representation as well as the on-disk format.
+ *
+ * The ad_xattr_header follows the FinderInfo data in the FinderInfo entry if
+ * the length of the FinderInfo entry is larger then 32 bytes. It is then
+ * preceeded with 2 bytes padding.
+ *
+ * Cf: https://opensource.apple.com/source/xnu/xnu-4570.1.46/bsd/vfs/vfs_xattr.c
+ */
+
+struct ad_xattr_header {
+       uint32_t adx_magic;        /* ATTR_HDR_MAGIC */
+       uint32_t adx_debug_tag;    /* for debugging == file id of owning file */
+       uint32_t adx_total_size;   /* file offset of end of attribute header + entries + data */
+       uint32_t adx_data_start;   /* file offset to attribute data area */
+       uint32_t adx_data_length;  /* length of attribute data area */
+       uint32_t adx_reserved[3];
+       uint16_t adx_flags;
+       uint16_t adx_num_attrs;
+};
+
+/* On-disk entries are aligned on 4 byte boundaries */
+struct ad_xattr_entry {
+       uint32_t adx_offset;    /* file offset to data */
+       uint32_t adx_length;    /* size of attribute data */
+       uint16_t adx_flags;
+       uint8_t  adx_namelen;   /* included the NULL terminator */
+       char    *adx_name;      /* NULL-terminated UTF-8 name */
+};
+
+struct ad_entry {
+       size_t ade_off;
+       size_t ade_len;
+};
+
+struct adouble {
+       files_struct             *ad_fsp;
+       bool                      ad_opened;
+       adouble_type_t            ad_type;
+       uint32_t                  ad_magic;
+       uint32_t                  ad_version;
+       uint8_t                   ad_filler[ADEDLEN_FILLER];
+       struct ad_entry           ad_eid[ADEID_MAX];
+       char                     *ad_data;
+       struct ad_xattr_header    adx_header;
+       struct ad_xattr_entry    *adx_entries;
+};
+
+struct ad_entry_order {
+       uint32_t id, offset, len;
+};
+
+/* Netatalk AppleDouble metadata xattr */
+static const
+struct ad_entry_order entry_order_meta_xattr[ADEID_NUM_XATTR + 1] = {
+       {ADEID_FINDERI,    ADEDOFF_FINDERI_XATTR,    ADEDLEN_FINDERI},
+       {ADEID_COMMENT,    ADEDOFF_COMMENT_XATTR,    0},
+       {ADEID_FILEDATESI, ADEDOFF_FILEDATESI_XATTR, ADEDLEN_FILEDATESI},
+       {ADEID_AFPFILEI,   ADEDOFF_AFPFILEI_XATTR,   ADEDLEN_AFPFILEI},
+       {ADEID_PRIVDEV,    ADEDOFF_PRIVDEV_XATTR,    0},
+       {ADEID_PRIVINO,    ADEDOFF_PRIVINO_XATTR,    0},
+       {ADEID_PRIVSYN,    ADEDOFF_PRIVSYN_XATTR,    0},
+       {ADEID_PRIVID,     ADEDOFF_PRIVID_XATTR,     0},
+       {0, 0, 0}
+};
+
+/* AppleDouble resource fork file (the ones prefixed by "._") */
+static const
+struct ad_entry_order entry_order_dot_und[ADEID_NUM_DOT_UND + 1] = {
+       {ADEID_FINDERI,    ADEDOFF_FINDERI_DOT_UND,  ADEDLEN_FINDERI},
+       {ADEID_RFORK,      ADEDOFF_RFORK_DOT_UND,    0},
+       {0, 0, 0}
+};
+
+/* Conversion from enumerated id to on-disk AppleDouble id */
+#define AD_EID_DISK(a) (set_eid[a])
+static const uint32_t set_eid[] = {
+       0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+       AD_DEV, AD_INO, AD_SYN, AD_ID
+};
+
+static char empty_resourcefork[] = {
+       0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
+       0x54, 0x68, 0x69, 0x73, 0x20, 0x72, 0x65, 0x73,
+       0x6F, 0x75, 0x72, 0x63, 0x65, 0x20, 0x66, 0x6F,
+       0x72, 0x6B, 0x20, 0x69, 0x6E, 0x74, 0x65, 0x6E,
+       0x74, 0x69, 0x6F, 0x6E, 0x61, 0x6C, 0x6C, 0x79,
+       0x20, 0x6C, 0x65, 0x66, 0x74, 0x20, 0x62, 0x6C,
+       0x61, 0x6E, 0x6B, 0x20, 0x20, 0x20, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x1C, 0x00, 0x1E, 0xFF, 0xFF
+};
+
+size_t ad_getentrylen(const struct adouble *ad, int eid)
+{
+       return ad->ad_eid[eid].ade_len;
+}
+
+size_t ad_getentryoff(const struct adouble *ad, int eid)
+{
+       return ad->ad_eid[eid].ade_off;
+}
+
+size_t ad_setentrylen(struct adouble *ad, int eid, size_t len)
+{
+       return ad->ad_eid[eid].ade_len = len;
+}
+
+size_t ad_setentryoff(struct adouble *ad, int eid, size_t off)
+{
+       return ad->ad_eid[eid].ade_off = off;
+}
+
+/**
+ * Return a pointer to an AppleDouble entry
+ *
+ * Returns NULL if the entry is not present
+ **/
+char *ad_get_entry(const struct adouble *ad, int eid)
+{
+       off_t off = ad_getentryoff(ad, eid);
+       size_t len = ad_getentrylen(ad, eid);
+
+       if (off == 0 || len == 0) {
+               return NULL;
+       }
+
+       return ad->ad_data + off;
+}
+
+/**
+ * Get a date
+ **/
+int ad_getdate(const struct adouble *ad, unsigned int dateoff, uint32_t *date)
+{
+       bool xlate = (dateoff & AD_DATE_UNIX);
+       char *p = NULL;
+
+       dateoff &= AD_DATE_MASK;
+       p = ad_get_entry(ad, ADEID_FILEDATESI);
+       if (p == NULL) {
+               return -1;
+       }
+
+       if (dateoff > AD_DATE_ACCESS) {
+           return -1;
+       }
+
+       memcpy(date, p + dateoff, sizeof(uint32_t));
+
+       if (xlate) {
+               *date = AD_DATE_TO_UNIX(*date);
+       }
+       return 0;
+}
+
+/**
+ * Set a date
+ **/
+int ad_setdate(struct adouble *ad, unsigned int dateoff, uint32_t date)
+{
+       bool xlate = (dateoff & AD_DATE_UNIX);
+       char *p = NULL;
+
+       p = ad_get_entry(ad, ADEID_FILEDATESI);
+       if (p == NULL) {
+               return -1;
+       }
+
+       dateoff &= AD_DATE_MASK;
+       if (xlate) {
+               date = AD_DATE_FROM_UNIX(date);
+       }
+
+       if (dateoff > AD_DATE_ACCESS) {
+               return -1;
+       }
+
+       memcpy(p + dateoff, &date, sizeof(date));
+
+       return 0;
+}
+
+
+/**
+ * Map on-disk AppleDouble id to enumerated id
+ **/
+static uint32_t get_eid(uint32_t eid)
+{
+       if (eid <= 15) {
+               return eid;
+       }
+
+       switch (eid) {
+       case AD_DEV:
+               return ADEID_PRIVDEV;
+       case AD_INO:
+               return ADEID_PRIVINO;
+       case AD_SYN:
+               return ADEID_PRIVSYN;
+       case AD_ID:
+               return ADEID_PRIVID;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+/**
+ * Pack AppleDouble structure into data buffer
+ **/
+static bool ad_pack(struct adouble *ad)
+{
+       uint32_t       eid;
+       uint16_t       nent;
+       uint32_t       bufsize;
+       uint32_t       offset = 0;
+
+       bufsize = talloc_get_size(ad->ad_data);
+       if (bufsize < AD_DATASZ_DOT_UND) {
+               DBG_ERR("bad buffer size [0x%" PRIx32 "]\n", bufsize);
+               return false;
+       }
+
+       if (offset + ADEDLEN_MAGIC < offset ||
+                       offset + ADEDLEN_MAGIC >= bufsize) {
+               return false;
+       }
+       RSIVAL(ad->ad_data, offset, ad->ad_magic);
+       offset += ADEDLEN_MAGIC;
+
+       if (offset + ADEDLEN_VERSION < offset ||
+                       offset + ADEDLEN_VERSION >= bufsize) {
+               return false;
+       }
+       RSIVAL(ad->ad_data, offset, ad->ad_version);
+       offset += ADEDLEN_VERSION;
+
+       if (offset + ADEDLEN_FILLER < offset ||
+                       offset + ADEDLEN_FILLER >= bufsize) {
+               return false;
+       }
+       if (ad->ad_type == ADOUBLE_RSRC) {
+               memcpy(ad->ad_data + offset, AD_FILLER_TAG, ADEDLEN_FILLER);
+       }
+       offset += ADEDLEN_FILLER;
+
+       if (offset + ADEDLEN_NENTRIES < offset ||
+                       offset + ADEDLEN_NENTRIES >= bufsize) {
+               return false;
+       }
+       offset += ADEDLEN_NENTRIES;
+
+       for (eid = 0, nent = 0; eid < ADEID_MAX; eid++) {
+               if (ad->ad_eid[eid].ade_off == 0) {
+                       /*
+                        * ade_off is also used as indicator whether a
+                        * specific entry is used or not
+                        */
+                       continue;
+               }
+
+               if (offset + AD_ENTRY_LEN_EID < offset ||
+                               offset + AD_ENTRY_LEN_EID >= bufsize) {
+                       return false;
+               }
+               RSIVAL(ad->ad_data, offset, AD_EID_DISK(eid));
+               offset += AD_ENTRY_LEN_EID;
+
+               if (offset + AD_ENTRY_LEN_OFF < offset ||
+                               offset + AD_ENTRY_LEN_OFF >= bufsize) {
+                       return false;
+               }
+               RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_off);
+               offset += AD_ENTRY_LEN_OFF;
+
+               if (offset + AD_ENTRY_LEN_LEN < offset ||
+                               offset + AD_ENTRY_LEN_LEN >= bufsize) {
+                       return false;
+               }
+               RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_len);
+               offset += AD_ENTRY_LEN_LEN;
+
+               nent++;
+       }
+
+       if (ADEDOFF_NENTRIES + 2 >= bufsize) {
+               return false;
+       }
+       RSSVAL(ad->ad_data, ADEDOFF_NENTRIES, nent);
+
+       return true;
+}
+
+static bool ad_unpack_xattrs(struct adouble *ad)
+{
+       struct ad_xattr_header *h = &ad->adx_header;
+       const char *p = ad->ad_data;
+       uint32_t hoff;
+       uint32_t i;
+
+       if (ad_getentrylen(ad, ADEID_FINDERI) <= ADEDLEN_FINDERI) {
+               return true;
+       }
+
+       /* 2 bytes padding */
+       hoff = ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI + 2;
+
+       h->adx_magic       = RIVAL(p, hoff + 0);
+       h->adx_debug_tag   = RIVAL(p, hoff + 4); /* Not used -> not checked */
+       h->adx_total_size  = RIVAL(p, hoff + 8);
+       h->adx_data_start  = RIVAL(p, hoff + 12);
+       h->adx_data_length = RIVAL(p, hoff + 16);
+       h->adx_flags       = RSVAL(p, hoff + 32); /* Not used -> not checked */
+       h->adx_num_attrs   = RSVAL(p, hoff + 34);
+
+       if (h->adx_magic != AD_XATTR_HDR_MAGIC) {
+               DBG_ERR("Bad magic: 0x%" PRIx32 "\n", h->adx_magic);
+               return false;
+       }
+
+       if (h->adx_total_size > ad_getentryoff(ad, ADEID_RFORK)) {
+               DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size);
+               return false;
+       }
+       if (h->adx_total_size > AD_XATTR_MAX_HDR_SIZE) {
+               DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size);
+               return false;
+       }
+
+       if (h->adx_data_start < (hoff + AD_XATTR_HDR_SIZE)) {
+               DBG_ERR("Bad start: 0x%" PRIx32 "\n", h->adx_data_start);
+               return false;
+       }
+
+       if ((h->adx_data_start + h->adx_data_length) < h->adx_data_start) {
+               DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length);
+               return false;
+       }
+       if ((h->adx_data_start + h->adx_data_length) >
+           ad->adx_header.adx_total_size)
+       {
+               DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length);
+               return false;
+       }
+
+       if (h->adx_num_attrs > AD_XATTR_MAX_ENTRIES) {
+               DBG_ERR("Bad num xattrs: %" PRIu16 "\n", h->adx_num_attrs);
+               return false;
+       }
+
+       if (h->adx_num_attrs == 0) {
+               return true;
+       }
+
+       ad->adx_entries = talloc_zero_array(
+               ad, struct ad_xattr_entry, h->adx_num_attrs);
+       if (ad->adx_entries == NULL) {
+               return false;
+       }
+
+       hoff += AD_XATTR_HDR_SIZE;
+
+       for (i = 0; i < h->adx_num_attrs; i++) {
+               struct ad_xattr_entry *e = &ad->adx_entries[i];
+
+               hoff = (hoff + 3) & ~3;
+
+               e->adx_offset  = RIVAL(p, hoff + 0);
+               e->adx_length  = RIVAL(p, hoff + 4);
+               e->adx_flags   = RSVAL(p, hoff + 8);
+               e->adx_namelen = *(p + hoff + 10);
+
+               if (e->adx_offset >= ad->adx_header.adx_total_size) {
+                       DBG_ERR("Bad adx_offset: %" PRIx32 "\n",
+                               e->adx_offset);
+                       return false;
+               }
+
+               if ((e->adx_offset + e->adx_length) < e->adx_offset) {
+                       DBG_ERR("Bad adx_length: %" PRIx32 "\n",
+                               e->adx_length);
+                       return false;
+               }
+
+               if ((e->adx_offset + e->adx_length) >
+                   ad->adx_header.adx_total_size)
+               {
+                       DBG_ERR("Bad adx_length: %" PRIx32 "\n",
+                               e->adx_length);
+                       return false;
+               }
+
+               if (e->adx_namelen == 0) {
+                       DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
+                               e->adx_namelen);
+                       return false;
+               }
+               if ((hoff + 11 + e->adx_namelen) < hoff + 11) {
+                       DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
+                               e->adx_namelen);
+                       return false;
+               }
+               if ((hoff + 11 + e->adx_namelen) >
+                   ad->adx_header.adx_data_start)
+               {
+                       DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
+                               e->adx_namelen);
+                       return false;
+               }
+
+               e->adx_name = talloc_strndup(ad->adx_entries,
+                                            p + hoff + 11,
+                                            e->adx_namelen);
+               if (e->adx_name == NULL) {
+                       return false;
+               }
+
+               DBG_DEBUG("xattr [%s] offset [0x%x] size [0x%x]\n",
+                         e->adx_name, e->adx_offset, e->adx_length);
+               dump_data(10, (uint8_t *)(ad->ad_data + e->adx_offset),
+                         e->adx_length);
+
+               hoff += 11 + e->adx_namelen;
+       }
+
+       return true;
+}
+
+/**
+ * Unpack an AppleDouble blob into a struct adoble
+ **/
+static bool ad_unpack(struct adouble *ad, const size_t nentries,
+                     size_t filesize)
+{
+       size_t bufsize = talloc_get_size(ad->ad_data);
+       size_t adentries, i;
+       uint32_t eid, len, off;
+       bool ok;
+
+       /*
+        * The size of the buffer ad->ad_data is checked when read, so
+        * we wouldn't have to check our own offsets, a few extra
+        * checks won't hurt though. We have to check the offsets we
+        * read from the buffer anyway.
+        */
+
+       if (bufsize < (AD_HEADER_LEN + (AD_ENTRY_LEN * nentries))) {
+               DEBUG(1, ("bad size\n"));
+               return false;
+       }
+
+       ad->ad_magic = RIVAL(ad->ad_data, 0);
+       ad->ad_version = RIVAL(ad->ad_data, ADEDOFF_VERSION);
+       if ((ad->ad_magic != AD_MAGIC) || (ad->ad_version != AD_VERSION)) {
+               DEBUG(1, ("wrong magic or version\n"));
+               return false;
+       }
+
+       memcpy(ad->ad_filler, ad->ad_data + ADEDOFF_FILLER, ADEDLEN_FILLER);
+
+       adentries = RSVAL(ad->ad_data, ADEDOFF_NENTRIES);
+       if (adentries != nentries) {
+               DEBUG(1, ("invalid number of entries: %zu\n",
+                         adentries));
+               return false;
+       }
+
+       /* now, read in the entry bits */
+       for (i = 0; i < adentries; i++) {
+               eid = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN));
+               eid = get_eid(eid);
+               off = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 4);
+               len = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 8);
+
+               if (!eid || eid >= ADEID_MAX) {
+                       DEBUG(1, ("bogus eid %d\n", eid));
+                       return false;
+               }
+
+               /*
+                * All entries other than the resource fork are
+                * expected to be read into the ad_data buffer, so
+                * ensure the specified offset is within that bound
+                */
+               if ((off > bufsize) && (eid != ADEID_RFORK)) {
+                       DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
+                                 eid, off, len));
+                       return false;
+               }
+
+               /*
+                * All entries besides FinderInfo and resource fork
+                * must fit into the buffer. FinderInfo is special as
+                * it may be larger then the default 32 bytes (if it
+                * contains marshalled xattrs), but we will fixup that
+                * in ad_convert(). And the resource fork is never
+                * accessed directly by the ad_data buf (also see
+                * comment above) anyway.
+                */
+               if ((eid != ADEID_RFORK) &&
+                   (eid != ADEID_FINDERI) &&
+                   ((off + len) > bufsize)) {
+                       DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
+                                 eid, off, len));
+                       return false;
+               }
+
+               /*
+                * That would be obviously broken
+                */
+               if (off > filesize) {
+                       DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
+                                 eid, off, len));
+                       return false;
+               }
+
+               /*
+                * Check for any entry that has its end beyond the
+                * filesize.
+                */
+               if (off + len < off) {
+                       DEBUG(1, ("offset wrap in eid %d: off: %" PRIu32
+                                 ", len: %" PRIu32 "\n",
+                                 eid, off, len));
+                       return false;
+
+               }
+               if (off + len > filesize) {
+                       /*
+                        * If this is the resource fork entry, we fix
+                        * up the length, for any other entry we bail
+                        * out.
+                        */
+                       if (eid != ADEID_RFORK) {
+                               DEBUG(1, ("bogus eid %d: off: %" PRIu32
+                                         ", len: %" PRIu32 "\n",
+                                         eid, off, len));
+                               return false;
+                       }
+
+                       /*
+                        * Fixup the resource fork entry by limiting
+                        * the size to entryoffset - filesize.
+                        */
+                       len = filesize - off;
+                       DEBUG(1, ("Limiting ADEID_RFORK: off: %" PRIu32
+                                 ", len: %" PRIu32 "\n", off, len));
+               }
+
+               ad->ad_eid[eid].ade_off = off;
+               ad->ad_eid[eid].ade_len = len;
+       }
+
+       ok = ad_unpack_xattrs(ad);
+       if (!ok) {
+               return false;
+       }
+
+       return true;
+}
+
+static bool ad_convert_move_reso(vfs_handle_struct *handle,
+                                struct adouble *ad,
+                                const struct smb_filename *smb_fname)
+{
+       char *buf = NULL;
+       size_t rforklen;
+       size_t rforkoff;
+       ssize_t n;
+       int ret;
+       bool ok;
+
+       rforklen = ad_getentrylen(ad, ADEID_RFORK);
+       if (rforklen == 0) {
+               return true;
+       }
+
+       buf = talloc_size(ad, rforklen);
+       if (buf == NULL) {
+               /*
+                * This allocates a buffer for reading the resource fork data in
+                * one big swoop. Resource forks won't be larger then, say, 64
+                * MB, I swear, so just doing the allocation with the talloc
+                * limit as safeguard seems safe.
+                */
+               DBG_ERR("Failed to allocate %zu bytes for rfork\n",
+                       rforklen);
+               return false;
+       }
+
+       rforkoff = ad_getentryoff(ad, ADEID_RFORK);
+
+       n = SMB_VFS_PREAD(ad->ad_fsp, buf, rforklen, rforkoff);
+       if (n != rforklen) {
+               DBG_ERR("Reading %zu bytes from rfork [%s] failed: %s\n",
+                       rforklen, fsp_str_dbg(ad->ad_fsp), strerror(errno));
+               return false;
+       }
+
+       rforkoff = ADEDOFF_RFORK_DOT_UND;
+
+       n = SMB_VFS_PWRITE(ad->ad_fsp, buf, rforklen, rforkoff);
+       if (n != rforklen) {
+               DBG_ERR("Writing %zu bytes to rfork [%s] failed: %s\n",
+                       rforklen, fsp_str_dbg(ad->ad_fsp), strerror(errno));
+               return false;
+       }
+
+       ad_setentryoff(ad, ADEID_RFORK, ADEDOFF_RFORK_DOT_UND);
+       ok = ad_pack(ad);
+       if (!ok) {
+               DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name);
+               return false;
+       }
+
+       ret = ad_fset(handle, ad, ad->ad_fsp);
+       if (ret != 0) {
+               DBG_ERR("ad_fset on [%s] failed\n", fsp_str_dbg(ad->ad_fsp));
+               return false;
+       }
+
+       return true;
+}
+
+static bool ad_convert_xattr(vfs_handle_struct *handle,
+                            struct adouble *ad,
+                            const struct smb_filename *smb_fname,
+                            const char *catia_mappings,
+                            bool *converted_xattr)
+{
+       static struct char_mappings **string_replace_cmaps = NULL;
+       uint16_t i;
+       int saved_errno = 0;
+       NTSTATUS status;
+       int rc;
+       bool ok;
+
+       *converted_xattr = false;
+
+       if (ad_getentrylen(ad, ADEID_FINDERI) == ADEDLEN_FINDERI) {
+               return true;
+       }
+
+       if (string_replace_cmaps == NULL) {
+               const char **mappings = NULL;
+
+               mappings = str_list_make_v3_const(
+                       talloc_tos(), catia_mappings, NULL);
+               if (mappings == NULL) {
+                       return false;
+               }
+               string_replace_cmaps = string_replace_init_map(mappings);
+               TALLOC_FREE(mappings);
+       }
+
+       for (i = 0; i < ad->adx_header.adx_num_attrs; i++) {
+               struct ad_xattr_entry *e = &ad->adx_entries[i];
+               char *mapped_name = NULL;
+               char *tmp = NULL;
+               struct smb_filename *stream_name = NULL;
+               files_struct *fsp = NULL;
+               ssize_t nwritten;
+
+               status = string_replace_allocate(handle->conn,
+                                                e->adx_name,
+                                                string_replace_cmaps,
+                                                talloc_tos(),
+                                                &mapped_name,
+                                                vfs_translate_to_windows);
+               if (!NT_STATUS_IS_OK(status) &&
+                   !NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))
+               {
+                       DBG_ERR("string_replace_allocate failed\n");
+                       ok = false;
+                       goto fail;
+               }
+
+               tmp = mapped_name;
+               mapped_name = talloc_asprintf(talloc_tos(), ":%s", tmp);
+               TALLOC_FREE(tmp);
+               if (mapped_name == NULL) {
+                       ok = false;
+                       goto fail;
+               }
+
+               stream_name = synthetic_smb_fname(talloc_tos(),
+                                                 smb_fname->base_name,
+                                                 mapped_name,
+                                                 NULL,
+                                                 smb_fname->flags);
+               TALLOC_FREE(mapped_name);
+               if (stream_name == NULL) {
+                       DBG_ERR("synthetic_smb_fname failed\n");
+                       ok = false;
+                       goto fail;
+               }
+
+               DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
+
+               status = SMB_VFS_CREATE_FILE(
+                       handle->conn,                   /* conn */
+                       NULL,                           /* req */
+                       0,                              /* root_dir_fid */
+                       stream_name,                    /* fname */
+                       FILE_GENERIC_WRITE,             /* access_mask */
+                       FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
+                       FILE_OPEN_IF,                   /* create_disposition */
+                       0,                              /* create_options */
+                       0,                              /* file_attributes */
+                       INTERNAL_OPEN_ONLY,             /* oplock_request */
+                       NULL,                           /* lease */
+                       0,                              /* allocation_size */
+                       0,                              /* private_flags */
+                       NULL,                           /* sd */
+                       NULL,                           /* ea_list */
+                       &fsp,                           /* result */
+                       NULL,                           /* psbuf */
+                       NULL, NULL);                    /* create context */
+               TALLOC_FREE(stream_name);
+               if (!NT_STATUS_IS_OK(status)) {
+                       DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
+                       ok = false;
+                       goto fail;
+               }
+
+               nwritten = SMB_VFS_PWRITE(fsp,
+                                         ad->ad_data + e->adx_offset,
+                                         e->adx_length,
+                                         0);
+               if (nwritten == -1) {
+                       DBG_ERR("SMB_VFS_PWRITE failed\n");
+                       saved_errno = errno;
+                       close_file(NULL, fsp, ERROR_CLOSE);
+                       errno = saved_errno;
+                       ok = false;
+                       goto fail;
+               }
+
+               status = close_file(NULL, fsp, NORMAL_CLOSE);
+               if (!NT_STATUS_IS_OK(status)) {
+                       ok = false;
+                       goto fail;
+               }
+               fsp = NULL;
+       }
+
+       ad_setentrylen(ad, ADEID_FINDERI, ADEDLEN_FINDERI);
+
+       ok = ad_pack(ad);
+       if (!ok) {
+               DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name);
+               goto fail;
+       }
+
+       rc = ad_fset(handle, ad, ad->ad_fsp);
+       if (rc != 0) {
+               DBG_ERR("ad_fset on [%s] failed: %s\n",
+                       fsp_str_dbg(ad->ad_fsp), strerror(errno));
+               ok = false;
+               goto fail;
+       }
+
+       ok = ad_convert_move_reso(handle, ad, smb_fname);
+       if (!ok) {
+               goto fail;
+       }
+
+       *converted_xattr = true;
+       ok = true;
+
+fail:
+       return ok;
+}
+
+static bool ad_convert_finderinfo(vfs_handle_struct *handle,
+                                 struct adouble *ad,
+                                 const struct smb_filename *smb_fname)
+{
+       char *p_ad = NULL;
+       AfpInfo *ai = NULL;
+       DATA_BLOB aiblob;
+       struct smb_filename *stream_name = NULL;
+       files_struct *fsp = NULL;
+       size_t size;
+       ssize_t nwritten;
+       NTSTATUS status;
+       int saved_errno = 0;
+       int cmp;
+
+       cmp = memcmp(ad->ad_filler, AD_FILLER_TAG_OSX, ADEDLEN_FILLER);
+       if (cmp != 0) {
+               return true;
+       }
+
+       p_ad = ad_get_entry(ad, ADEID_FINDERI);
+       if (p_ad == NULL) {
+               return false;
+       }
+
+       ai = afpinfo_new(talloc_tos());
+       if (ai == NULL) {
+               return false;
+       }
+
+       memcpy(ai->afpi_FinderInfo, p_ad, ADEDLEN_FINDERI);
+
+       aiblob = data_blob_talloc(talloc_tos(), NULL, AFP_INFO_SIZE);
+       if (aiblob.data == NULL) {
+               TALLOC_FREE(ai);
+               return false;
+       }
+
+       size = afpinfo_pack(ai, (char *)aiblob.data);
+       TALLOC_FREE(ai);
+       if (size != AFP_INFO_SIZE) {
+               return false;
+       }
+
+       stream_name = synthetic_smb_fname(talloc_tos(),
+                                         smb_fname->base_name,
+                                         AFPINFO_STREAM,
+                                         NULL,
+                                         smb_fname->flags);
+       if (stream_name == NULL) {
+               data_blob_free(&aiblob);
+               DBG_ERR("synthetic_smb_fname failed\n");
+               return false;
+       }
+
+       DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
+
+       status = SMB_VFS_CREATE_FILE(
+               handle->conn,                   /* conn */
+               NULL,                           /* req */
+               0,                              /* root_dir_fid */
+               stream_name,                    /* fname */
+               FILE_GENERIC_WRITE,             /* access_mask */
+               FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
+               FILE_OPEN_IF,                   /* create_disposition */
+               0,                              /* create_options */
+               0,                              /* file_attributes */
+               INTERNAL_OPEN_ONLY,             /* oplock_request */
+               NULL,                           /* lease */
+               0,                              /* allocation_size */
+               0,                              /* private_flags */
+               NULL,                           /* sd */
+               NULL,                           /* ea_list */
+               &fsp,                           /* result */
+               NULL,                           /* psbuf */
+               NULL, NULL);                    /* create context */
+       TALLOC_FREE(stream_name);
+       if (!NT_STATUS_IS_OK(status)) {
+               DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
+               return false;
+       }
+
+       nwritten = SMB_VFS_PWRITE(fsp,
+                                 aiblob.data,
+                                 aiblob.length,
+                                 0);
+       if (nwritten == -1) {
+               DBG_ERR("SMB_VFS_PWRITE failed\n");
+               saved_errno = errno;
+               close_file(NULL, fsp, ERROR_CLOSE);
+               errno = saved_errno;
+               return false;
+       }
+
+       status = close_file(NULL, fsp, NORMAL_CLOSE);
+       if (!NT_STATUS_IS_OK(status)) {
+               return false;
+       }
+       fsp = NULL;
+
+       return true;
+}
+
+static bool ad_convert_truncate(vfs_handle_struct *handle,
+                               struct adouble *ad,
+                               const struct smb_filename *smb_fname)
+{
+       int rc;
+       off_t newlen;
+
+       newlen = ADEDOFF_RFORK_DOT_UND + ad_getentrylen(ad, ADEID_RFORK);
+
+       rc = SMB_VFS_FTRUNCATE(ad->ad_fsp, newlen);
+       if (rc != 0) {
+               return false;
+       }
+
+       return true;
+}
+
+static bool ad_convert_blank_rfork(vfs_handle_struct *handle,
+                                  struct adouble *ad,
+                                  uint32_t flags,
+                                  bool *blank)
+{
+       size_t rforklen = sizeof(empty_resourcefork);
+       char buf[rforklen];
+       ssize_t nread;
+       int cmp;
+       int rc;
+       bool ok;
+
+       *blank = false;
+
+       if (!(flags & AD_CONV_WIPE_BLANK)) {
+               return true;
+       }
+
+       if (ad_getentrylen(ad, ADEID_RFORK) != rforklen) {
+               return true;
+       }
+
+       nread = SMB_VFS_PREAD(ad->ad_fsp, buf, rforklen, ADEDOFF_RFORK_DOT_UND);
+       if (nread != rforklen) {
+               DBG_ERR("Reading %zu bytes from rfork [%s] failed: %s\n",
+                       rforklen, fsp_str_dbg(ad->ad_fsp), strerror(errno));
+               return false;
+       }
+
+       cmp = memcmp(buf, empty_resourcefork, rforklen);
+       if (cmp != 0) {
+               return true;
+       }
+
+       ad_setentrylen(ad, ADEID_RFORK, 0);
+       ok = ad_pack(ad);
+       if (!ok) {
+               return false;
+       }
+
+       rc = ad_fset(handle, ad, ad->ad_fsp);
+       if (rc != 0) {
+               DBG_ERR("ad_fset on [%s] failed\n", fsp_str_dbg(ad->ad_fsp));
+               return false;
+       }
+
+       *blank = true;
+       return true;
+}
+
+static bool ad_convert_delete_adfile(vfs_handle_struct *handle,
+                                    struct adouble *ad,
+                                    const struct smb_filename *smb_fname,
+                                    uint32_t flags)
+{
+       struct smb_filename *ad_name = NULL;
+       int rc;
+
+       if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
+               return true;
+       }
+
+       if (!(flags & AD_CONV_DELETE)) {
+               return true;
+       }
+
+       rc = adouble_path(talloc_tos(), smb_fname, &ad_name);
+       if (rc != 0) {
+               return false;
+       }
+
+       rc = SMB_VFS_NEXT_UNLINK(handle, ad_name);
+       if (rc != 0) {
+               DBG_ERR("Unlinking [%s] failed: %s\n",
+                       smb_fname_str_dbg(ad_name), strerror(errno));
+               TALLOC_FREE(ad_name);
+               return false;
+       }
+
+       DBG_WARNING("Unlinked [%s] after conversion\n", smb_fname_str_dbg(ad_name));
+       TALLOC_FREE(ad_name);
+
+       return true;
+}
+
+/**
+ * Convert from Apple's ._ file to Netatalk
+ *
+ * Apple's AppleDouble may contain a FinderInfo entry longer then 32
+ * bytes containing packed xattrs.
+ *
+ * @return -1 in case an error occurred, 0 if no conversion was done, 1
+ * otherwise
+ **/
+int ad_convert(struct vfs_handle_struct *handle,
+              const struct smb_filename *smb_fname,
+              const char *catia_mappings,
+              uint32_t flags)
+{
+       struct adouble *ad = NULL;
+       bool ok;
+       bool converted_xattr = false;
+       bool blank;
+       int ret;
+
+       ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
+       if (ad == NULL) {
+               return 0;
+       }
+
+       ok = ad_convert_xattr(handle,
+                             ad,
+                             smb_fname,
+                             catia_mappings,
+                             &converted_xattr);
+       if (!ok) {
+               ret = -1;
+               goto done;
+       }
+
+       ok = ad_convert_blank_rfork(handle, ad, flags, &blank);
+       if (!ok) {
+               ret = -1;
+               goto done;
+       }
+
+       if (converted_xattr || blank) {
+               ok = ad_convert_truncate(handle, ad, smb_fname);
+               if (!ok) {
+                       ret = -1;
+                       goto done;
+               }
+       }
+
+       ok = ad_convert_finderinfo(handle, ad, smb_fname);
+       if (!ok) {
+               DBG_ERR("Failed to convert [%s]\n",
+                       smb_fname_str_dbg(smb_fname));
+               ret = -1;
+               goto done;
+       }
+
+       ok = ad_convert_delete_adfile(handle, ad, smb_fname, flags);
+       if (!ok) {
+               ret = -1;
+               goto done;
+       }
+
+       ret = 0;
+done:
+       TALLOC_FREE(ad);
+       return ret;
+}
+
+/**
+ * Read and parse Netatalk AppleDouble metadata xattr
+ **/
+static ssize_t ad_read_meta(vfs_handle_struct *handle,
+                           struct adouble *ad,
+                           const struct smb_filename *smb_fname)
+{
+       int      rc = 0;
+       ssize_t  ealen;
+       bool     ok;
+
+       DEBUG(10, ("reading meta xattr for %s\n", smb_fname->base_name));
+
+       ealen = SMB_VFS_GETXATTR(handle->conn, smb_fname,
+                                AFPINFO_EA_NETATALK, ad->ad_data,
+                                AD_DATASZ_XATTR);
+       if (ealen == -1) {
+               switch (errno) {
+               case ENOATTR:
+               case ENOENT:
+                       if (errno == ENOATTR) {
+                               errno = ENOENT;
+                       }
+                       rc = -1;
+                       goto exit;
+               default:
+                       DEBUG(2, ("error reading meta xattr: %s\n",
+                                 strerror(errno)));
+                       rc = -1;
+                       goto exit;
+               }
+       }
+       if (ealen != AD_DATASZ_XATTR) {
+               DEBUG(2, ("bad size %zd\n", ealen));
+               errno = EINVAL;
+               rc = -1;
+               goto exit;
+       }
+
+       /* Now parse entries */
+       ok = ad_unpack(ad, ADEID_NUM_XATTR, AD_DATASZ_XATTR);
+       if (!ok) {
+               DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
+               errno = EINVAL;
+               rc = -1;
+               goto exit;
+       }
+
+       if (!ad_getentryoff(ad, ADEID_FINDERI)
+           || !ad_getentryoff(ad, ADEID_COMMENT)
+           || !ad_getentryoff(ad, ADEID_FILEDATESI)
+           || !ad_getentryoff(ad, ADEID_AFPFILEI)
+           || !ad_getentryoff(ad, ADEID_PRIVDEV)
+           || !ad_getentryoff(ad, ADEID_PRIVINO)
+           || !ad_getentryoff(ad, ADEID_PRIVSYN)
+           || !ad_getentryoff(ad, ADEID_PRIVID)) {
+               DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
+               errno = EINVAL;
+               rc = -1;
+               goto exit;
+       }
+
+exit:
+       DEBUG(10, ("reading meta xattr for %s, rc: %d\n",
+               smb_fname->base_name, rc));
+
+       if (rc != 0) {
+               ealen = -1;
+               if (errno == EINVAL) {
+                       become_root();
+                       (void)SMB_VFS_REMOVEXATTR(handle->conn,
+                                                 smb_fname,
+                                                 AFPINFO_EA_NETATALK);
+                       unbecome_root();
+                       errno = ENOENT;
+               }
+       }
+       return ealen;
+}
+
+static int ad_open_rsrc(vfs_handle_struct *handle,
+                       const struct smb_filename *smb_fname,
+                       int flags,
+                       mode_t mode,
+                       files_struct **_fsp)
+{
+       int ret;
+       struct smb_filename *adp_smb_fname = NULL;
+       files_struct *fsp = NULL;
+       uint32_t access_mask;
+       uint32_t share_access;
+       uint32_t create_disposition;
+       NTSTATUS status;
+
+       ret = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
+       if (ret != 0) {
+               return -1;
+       }
+
+       ret = SMB_VFS_STAT(handle->conn, adp_smb_fname);
+       if (ret != 0) {
+               TALLOC_FREE(adp_smb_fname);
+               return -1;
+       }
+
+       access_mask = FILE_GENERIC_READ;
+       share_access = FILE_SHARE_READ | FILE_SHARE_WRITE;
+       create_disposition = FILE_OPEN;
+
+       if (flags & O_RDWR) {
+               access_mask |= FILE_GENERIC_WRITE;
+               share_access &= ~FILE_SHARE_WRITE;
+       }
+
+       status = SMB_VFS_CREATE_FILE(
+               handle->conn,                   /* conn */
+               NULL,                           /* req */
+               0,                              /* root_dir_fid */
+               adp_smb_fname,
+               access_mask,
+               share_access,
+               create_disposition,
+               0,                              /* create_options */
+               0,                              /* file_attributes */
+               INTERNAL_OPEN_ONLY,             /* oplock_request */
+               NULL,                           /* lease */
+               0,                              /* allocation_size */
+               0,                              /* private_flags */
+               NULL,                           /* sd */
+               NULL,                           /* ea_list */
+               &fsp,
+               NULL,                           /* psbuf */
+               NULL, NULL);                    /* create context */
+       TALLOC_FREE(adp_smb_fname);
+       if (!NT_STATUS_IS_OK(status)) {
+               DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
+               return -1;
+       }
+
+       *_fsp = fsp;
+       return 0;
+}
+
+/*
+ * Here's the deal: for ADOUBLE_META we can do without an fd as we can issue
+ * path based xattr calls. For ADOUBLE_RSRC however we need a full-fledged fd
+ * for file IO on the ._ file.
+ */
+static int ad_open(vfs_handle_struct *handle,
+                  struct adouble *ad,
+                  files_struct *fsp,
+                  const struct smb_filename *smb_fname,
+                  int flags,
+                  mode_t mode)
+{
+       int ret;
+
+       DBG_DEBUG("Path [%s] type [%s]\n", smb_fname->base_name,
+                 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
+
+       if (ad->ad_type == ADOUBLE_META) {
+               return 0;
+       }
+
+       if (fsp != NULL) {
+               ad->ad_fsp = fsp;
+               ad->ad_opened = false;
+               return 0;
+       }
+
+       ret = ad_open_rsrc(handle, smb_fname, flags, mode, &ad->ad_fsp);
+       if (ret != 0) {
+               return -1;
+       }
+       ad->ad_opened = true;
+
+       DBG_DEBUG("Path [%s] type [%s]\n",
+                 smb_fname->base_name,
+                 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
+
+       return 0;
+}
+
+static ssize_t ad_read_rsrc_adouble(vfs_handle_struct *handle,
+                                   struct adouble *ad,
+                                   const struct smb_filename *smb_fname)
+{
+       char *p_ad = NULL;
+       size_t size;
+       ssize_t len;
+       int ret;
+       bool ok;
+
+       ret = SMB_VFS_NEXT_FSTAT(handle, ad->ad_fsp, &ad->ad_fsp->fsp_name->st);
+       if (ret != 0) {
+               DBG_ERR("fstat [%s] failed: %s\n",
+                       fsp_str_dbg(ad->ad_fsp), strerror(errno));
+               return -1;
+       }
+
+       /*
+        * AppleDouble file header content and size, two cases:
+        *
+        * - without xattrs it is exactly AD_DATASZ_DOT_UND (82) bytes large
+        * - with embedded xattrs it can be larger, up to AD_XATTR_MAX_HDR_SIZE
+        *
+        * Read as much as we can up to AD_XATTR_MAX_HDR_SIZE.
+        */
+       size = ad->ad_fsp->fsp_name->st.st_ex_size;
+       if (size > talloc_array_length(ad->ad_data)) {
+               if (size > AD_XATTR_MAX_HDR_SIZE) {
+                       size = AD_XATTR_MAX_HDR_SIZE;
+               }
+               p_ad = talloc_realloc(ad, ad->ad_data, char, size);
+               if (p_ad == NULL) {
+                       return -1;
+               }
+               ad->ad_data = p_ad;
+       }
+
+       len = SMB_VFS_NEXT_PREAD(handle, ad->ad_fsp, ad->ad_data, talloc_array_length(ad->ad_data), 0);
+       if (len != talloc_array_length(ad->ad_data)) {
+               DBG_NOTICE("%s %s: bad size: %zd\n",
+                          smb_fname->base_name, strerror(errno), len);
+               return -1;
+       }
+
+       /* Now parse entries */
+       ok = ad_unpack(ad, ADEID_NUM_DOT_UND, size);
+       if (!ok) {
+               DBG_ERR("invalid AppleDouble resource %s\n",
+                       smb_fname->base_name);
+               errno = EINVAL;
+               return -1;
+       }
+
+       if ((ad_getentryoff(ad, ADEID_FINDERI) != ADEDOFF_FINDERI_DOT_UND)
+           || (ad_getentrylen(ad, ADEID_FINDERI) < ADEDLEN_FINDERI)
+           || (ad_getentryoff(ad, ADEID_RFORK) < ADEDOFF_RFORK_DOT_UND)) {
+               DBG_ERR("invalid AppleDouble resource %s\n",
+                       smb_fname->base_name);
+               errno = EINVAL;
+               return -1;
+       }
+
+       return len;
+}
+
+/**
+ * Read and parse resource fork, either ._ AppleDouble file or xattr
+ **/
+static ssize_t ad_read_rsrc(vfs_handle_struct *handle,
+                           struct adouble *ad,
+                           const struct smb_filename *smb_fname)
+{
+       return ad_read_rsrc_adouble(handle, ad, smb_fname);
+}
+
+/**
+ * Read and unpack an AppleDouble metadata xattr or resource
+ **/
+static ssize_t ad_read(vfs_handle_struct *handle,
+                      struct adouble *ad,
+                      const struct smb_filename *smb_fname)
+{
+       switch (ad->ad_type) {
+       case ADOUBLE_META:
+               return ad_read_meta(handle, ad, smb_fname);
+       case ADOUBLE_RSRC:
+               return ad_read_rsrc(handle, ad, smb_fname);
+       default:
+               return -1;
+       }
+}
+
+static int adouble_destructor(struct adouble *ad)
+{
+       NTSTATUS status;
+
+       if (!ad->ad_opened) {
+               return 0;
+       }
+
+       SMB_ASSERT(ad->ad_fsp != NULL);
+
+       status = close_file(NULL, ad->ad_fsp, NORMAL_CLOSE);
+       if (!NT_STATUS_IS_OK(status)) {
+               DBG_ERR("Closing [%s] failed: %s\n",
+                       fsp_str_dbg(ad->ad_fsp), nt_errstr(status));
+       }
+
+       return 0;
+}
+
+/**
+ * Allocate a struct adouble without initialiing it
+ *
+ * The struct is either hang of the fsp extension context or if fsp is
+ * NULL from ctx.
+ *
+ * @param[in] ctx        talloc context
+ * @param[in] handle     vfs handle
+ * @param[in] type       type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
+ *
+ * @return               adouble handle
+ **/
+static struct adouble *ad_alloc(TALLOC_CTX *ctx,
+                               adouble_type_t type)
+{
+       int rc = 0;
+       size_t adsize = 0;
+       struct adouble *ad;
+
+       switch (type) {
+       case ADOUBLE_META:
+               adsize = AD_DATASZ_XATTR;
+               break;
+       case ADOUBLE_RSRC:
+               adsize = AD_DATASZ_DOT_UND;
+               break;
+       default:
+               return NULL;
+       }
+
+       ad = talloc_zero(ctx, struct adouble);
+       if (ad == NULL) {
+               rc = -1;
+               goto exit;
+       }
+
+       if (adsize) {
+               ad->ad_data = talloc_zero_array(ad, char, adsize);
+               if (ad->ad_data == NULL) {
+                       rc = -1;
+                       goto exit;
+               }
+       }
+
+       ad->ad_type = type;
+       ad->ad_magic = AD_MAGIC;
+       ad->ad_version = AD_VERSION;
+
+       talloc_set_destructor(ad, adouble_destructor);
+
+exit:
+       if (rc != 0) {
+               TALLOC_FREE(ad);
+       }
+       return ad;
+}
+
+/**
+ * Allocate and initialize a new struct adouble
+ *
+ * @param[in] ctx        talloc context
+ * @param[in] type       type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
+ *
+ * @return               adouble handle, initialized
+ **/
+struct adouble *ad_init(TALLOC_CTX *ctx, adouble_type_t type)
+{
+       int rc = 0;
+       const struct ad_entry_order  *eid;
+       struct adouble *ad = NULL;
+       time_t t = time(NULL);
+
+       switch (type) {
+       case ADOUBLE_META:
+               eid = entry_order_meta_xattr;
+               break;
+       case ADOUBLE_RSRC:
+               eid = entry_order_dot_und;
+               break;
+       default:
+               return NULL;
+       }
+
+       ad = ad_alloc(ctx, type);
+       if (ad == NULL) {
+               return NULL;
+       }
+
+       while (eid->id) {
+               ad->ad_eid[eid->id].ade_off = eid->offset;
+               ad->ad_eid[eid->id].ade_len = eid->len;
+               eid++;
+       }
+
+       /* put something sane in the date fields */
+       ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, t);
+       ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, t);
+       ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, t);
+       ad_setdate(ad, AD_DATE_BACKUP, htonl(AD_DATE_START));
+
+       if (rc != 0) {
+               TALLOC_FREE(ad);
+       }
+       return ad;
+}
+
+static struct adouble *ad_get_internal(TALLOC_CTX *ctx,
+                                      vfs_handle_struct *handle,
+                                      files_struct *fsp,
+                                      const struct smb_filename *smb_fname,
+                                      adouble_type_t type)
+{
+       int rc = 0;
+       ssize_t len;
+       struct adouble *ad = NULL;
+       int mode;
+
+       if (fsp != NULL) {
+               smb_fname = fsp->base_fsp->fsp_name;
+       }
+
+       DEBUG(10, ("ad_get(%s) called for %s\n",
+                  type == ADOUBLE_META ? "meta" : "rsrc",
+                  smb_fname->base_name));
+
+       ad = ad_alloc(ctx, type);
+       if (ad == NULL) {
+               rc = -1;
+               goto exit;
+       }
+
+       /* Try rw first so we can use the fd in ad_convert() */
+       mode = O_RDWR;
+
+       rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
+       if (rc == -1 && ((errno == EROFS) || (errno == EACCES))) {
+               mode = O_RDONLY;
+               rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
+       }
+       if (rc == -1) {
+               DBG_DEBUG("ad_open [%s] error [%s]\n",
+                         smb_fname->base_name, strerror(errno));
+               goto exit;
+
+       }
+
+       len = ad_read(handle, ad, smb_fname);
+       if (len == -1) {
+               DEBUG(10, ("error reading AppleDouble for %s\n",
+                       smb_fname->base_name));
+               rc = -1;
+               goto exit;
+       }
+
+exit:
+       DEBUG(10, ("ad_get(%s) for %s returning %d\n",
+                 type == ADOUBLE_META ? "meta" : "rsrc",
+                 smb_fname->base_name, rc));
+
+       if (rc != 0) {
+               TALLOC_FREE(ad);
+       }
+       return ad;
+}
+
+/**
+ * Return AppleDouble data for a file
+ *
+ * @param[in] ctx      talloc context
+ * @param[in] handle   vfs handle
+ * @param[in] smb_fname pathname to file or directory
+ * @param[in] type     type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
+ *
+ * @return             talloced struct adouble or NULL on error
+ **/
+struct adouble *ad_get(TALLOC_CTX *ctx,
+                             vfs_handle_struct *handle,
+                             const struct smb_filename *smb_fname,
+                             adouble_type_t type)
+{
+       return ad_get_internal(ctx, handle, NULL, smb_fname, type);
+}
+
+/**
+ * Return AppleDouble data for a file
+ *
+ * @param[in] ctx      talloc context
+ * @param[in] handle   vfs handle
+ * @param[in] fsp      fsp to use for IO
+ * @param[in] type     type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
+ *
+ * @return             talloced struct adouble or NULL on error
+ **/
+struct adouble *ad_fget(TALLOC_CTX *ctx, vfs_handle_struct *handle,
+                       files_struct *fsp, adouble_type_t type)
+{
+       return ad_get_internal(ctx, handle, fsp, NULL, type);
+}
+
+/**
+ * Set AppleDouble metadata on a file or directory
+ *
+ * @param[in] ad      adouble handle
+ *
+ * @param[in] smb_fname    pathname to file or directory
+ *
+ * @return            status code, 0 means success
+ **/
+int ad_set(vfs_handle_struct *handle,
+          struct adouble *ad,
+          const struct smb_filename *smb_fname)
+{
+       bool ok;
+       int ret;
+
+       DBG_DEBUG("Path [%s]\n", smb_fname->base_name);
+
+       if (ad->ad_type != ADOUBLE_META) {
+               DBG_ERR("ad_set on [%s] used with ADOUBLE_RSRC\n",
+                       smb_fname->base_name);
+               return -1;
+       }
+
+       ok = ad_pack(ad);
+       if (!ok) {
+               return -1;
+       }
+
+       ret = SMB_VFS_SETXATTR(handle->conn,
+                              smb_fname,
+                              AFPINFO_EA_NETATALK,
+                              ad->ad_data,
+                              AD_DATASZ_XATTR, 0);
+
+       DBG_DEBUG("Path [%s] ret [%d]\n", smb_fname->base_name, ret);
+
+       return ret;
+}
+
+/**
+ * Set AppleDouble metadata on a file or directory
+ *
+ * @param[in] ad      adouble handle
+ * @param[in] fsp     file handle
+ *
+ * @return            status code, 0 means success
+ **/
+int ad_fset(struct vfs_handle_struct *handle,
+           struct adouble *ad,
+           files_struct *fsp)
+{
+       int rc = -1;
+       ssize_t len;
+       bool ok;
+
+       DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
+
+       if ((fsp == NULL)
+           || (fsp->fh == NULL)
+           || (fsp->fh->fd == -1))
+       {
+               smb_panic("bad fsp");
+       }
+
+       ok = ad_pack(ad);
+       if (!ok) {
+               return -1;
+       }
+
+       switch (ad->ad_type) {
+       case ADOUBLE_META:
+               rc = SMB_VFS_NEXT_SETXATTR(handle,
+                                          fsp->fsp_name,
+                                          AFPINFO_EA_NETATALK,
+                                          ad->ad_data,
+                                          AD_DATASZ_XATTR, 0);
+               break;
+
+       case ADOUBLE_RSRC:
+               len = SMB_VFS_NEXT_PWRITE(handle,
+                                         fsp,
+                                         ad->ad_data,
+                                         AD_DATASZ_DOT_UND,
+                                         0);
+               if (len != AD_DATASZ_DOT_UND) {
+                       DBG_ERR("short write on %s: %zd", fsp_str_dbg(fsp), len);
+                       return -1;
+               }
+               rc = 0;
+               break;
+
+       default:
+               return -1;
+       }
+
+       DBG_DEBUG("Path [%s] rc [%d]\n", fsp_str_dbg(fsp), rc);
+
+       return rc;
+}
+
+bool is_adouble_file(const char *path)
+{
+       const char *p = NULL;
+       int match;
+
+       p = strrchr(path, '/');
+       if (p == NULL) {
+               p = path;
+       } else {
+               p++;
+       }
+
+       match = strncmp(p,
+                       ADOUBLE_NAME_PREFIX,
+                       strlen(ADOUBLE_NAME_PREFIX));
+       if (match != 0) {
+               return false;
+       }
+       return true;
+}
+
+/**
+ * Prepend "._" to a basename
+ * Return a new struct smb_filename with stream_name == NULL.
+ **/
+int adouble_path(TALLOC_CTX *ctx,
+                const struct smb_filename *smb_fname_in,
+                struct smb_filename **pp_smb_fname_out)
+{
+       char *parent;
+       const char *base;
+       struct smb_filename *smb_fname = cp_smb_filename(ctx,
+                                               smb_fname_in);
+
+       if (smb_fname == NULL) {
+               return -1;
+       }
+
+       /* We need streamname to be NULL */
+       TALLOC_FREE(smb_fname->stream_name);
+
+       /* And we're replacing base_name. */
+       TALLOC_FREE(smb_fname->base_name);
+
+       if (!parent_dirname(smb_fname, smb_fname_in->base_name,
+                               &parent, &base)) {
+               TALLOC_FREE(smb_fname);
+               return -1;
+       }
+
+       smb_fname->base_name = talloc_asprintf(smb_fname,
+                                       "%s/._%s", parent, base);
+       if (smb_fname->base_name == NULL) {
+               TALLOC_FREE(smb_fname);
+               return -1;
+       }
+
+       *pp_smb_fname_out = smb_fname;
+
+       return 0;
+}
+
+/**
+ * Allocate and initialize an AfpInfo struct
+ **/
+AfpInfo *afpinfo_new(TALLOC_CTX *ctx)
+{
+       AfpInfo *ai = talloc_zero(ctx, AfpInfo);
+       if (ai == NULL) {
+               return NULL;
+       }
+       ai->afpi_Signature = AFP_Signature;
+       ai->afpi_Version = AFP_Version;
+       ai->afpi_BackupTime = AD_DATE_START;
+       return ai;
+}
+
+/**
+ * Pack an AfpInfo struct into a buffer
+ *
+ * Buffer size must be at least AFP_INFO_SIZE
+ * Returns size of packed buffer
+ **/
+ssize_t afpinfo_pack(const AfpInfo *ai, char *buf)
+{
+       memset(buf, 0, AFP_INFO_SIZE);
+
+       RSIVAL(buf, 0, ai->afpi_Signature);
+       RSIVAL(buf, 4, ai->afpi_Version);
+       RSIVAL(buf, 12, ai->afpi_BackupTime);
+       memcpy(buf + 16, ai->afpi_FinderInfo, sizeof(ai->afpi_FinderInfo));
+
+       return AFP_INFO_SIZE;
+}
+
+/**
+ * Unpack a buffer into a AfpInfo structure
+ *
+ * Buffer size must be at least AFP_INFO_SIZE
+ * Returns allocated AfpInfo struct
+ **/
+AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data)
+{
+       AfpInfo *ai = talloc_zero(ctx, AfpInfo);
+       if (ai == NULL) {
+               return NULL;
+       }
+
+       ai->afpi_Signature = RIVAL(data, 0);
+       ai->afpi_Version = RIVAL(data, 4);
+       ai->afpi_BackupTime = RIVAL(data, 12);
+       memcpy(ai->afpi_FinderInfo, (const char *)data + 16,
+              sizeof(ai->afpi_FinderInfo));
+
+       if (ai->afpi_Signature != AFP_Signature
+           || ai->afpi_Version != AFP_Version) {
+               DEBUG(1, ("Bad AfpInfo signature or version\n"));
+               TALLOC_FREE(ai);
+       }
+
+       return ai;
+}
diff --git a/source3/lib/adouble.h b/source3/lib/adouble.h
new file mode 100644 (file)
index 0000000..acc9a30
--- /dev/null
@@ -0,0 +1,182 @@
+/*
+ * Samba AppleDouble helpers
+ *
+ * Copyright (C) Ralph Boehme, 2019
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _ADOUBLE_H_
+#define _ADOUBLE_H_
+
+#include "MacExtensions.h"
+
+#define ADOUBLE_NAME_PREFIX "._"
+
+#define NETATALK_META_XATTR "org.netatalk.Metadata"
+#define NETATALK_RSRC_XATTR "org.netatalk.ResourceFork"
+
+#if defined(HAVE_ATTROPEN)
+#define AFPINFO_EA_NETATALK NETATALK_META_XATTR
+#define AFPRESOURCE_EA_NETATALK NETATALK_RSRC_XATTR
+#else
+#define AFPINFO_EA_NETATALK "user." NETATALK_META_XATTR
+#define AFPRESOURCE_EA_NETATALK "user." NETATALK_RSRC_XATTR
+#endif
+
+/*
+ * There are two AppleDouble blobs we deal with:
+ *
+ * - ADOUBLE_META - AppleDouble blob used by Netatalk for storing
+ *   metadata in an xattr
+ *
+ * - ADOUBLE_RSRC - AppleDouble blob used by OS X and Netatalk in
+ *   ._ files
+ */
+typedef enum {ADOUBLE_META, ADOUBLE_RSRC} adouble_type_t;
+
+/* Version info */
+#define AD_VERSION2     0x00020000
+#define AD_VERSION      AD_VERSION2
+
+/*
+ * AppleDouble entry IDs.
+ */
+#define ADEID_DFORK         1
+#define ADEID_RFORK         2
+#define ADEID_NAME          3
+#define ADEID_COMMENT       4
+#define ADEID_ICONBW        5
+#define ADEID_ICONCOL       6
+#define ADEID_FILEI         7
+#define ADEID_FILEDATESI    8
+#define ADEID_FINDERI       9
+#define ADEID_MACFILEI      10
+#define ADEID_PRODOSFILEI   11
+#define ADEID_MSDOSFILEI    12
+#define ADEID_SHORTNAME     13
+#define ADEID_AFPFILEI      14
+#define ADEID_DID           15
+
+/* Private Netatalk entries */
+#define ADEID_PRIVDEV       16
+#define ADEID_PRIVINO       17
+#define ADEID_PRIVSYN       18
+#define ADEID_PRIVID        19
+#define ADEID_MAX           (ADEID_PRIVID + 1)
+
+/*
+ * These are the real ids for the private entries,
+ * as stored in the adouble file
+ */
+#define AD_DEV              0x80444556
+#define AD_INO              0x80494E4F
+#define AD_SYN              0x8053594E
+#define AD_ID               0x8053567E
+
+/* AppleDouble magic */
+#define AD_APPLESINGLE_MAGIC 0x00051600
+#define AD_APPLEDOUBLE_MAGIC 0x00051607
+#define AD_MAGIC             AD_APPLEDOUBLE_MAGIC
+
+/* Field widths */
+#define ADEDLEN_NAME            255
+#define ADEDLEN_COMMENT         200
+#define ADEDLEN_FILEI           16
+#define ADEDLEN_FINDERI         32
+#define ADEDLEN_FILEDATESI      16
+#define ADEDLEN_SHORTNAME       12 /* length up to 8.3 */
+#define ADEDLEN_AFPFILEI        4
+#define ADEDLEN_MACFILEI        4
+#define ADEDLEN_PRODOSFILEI     8
+#define ADEDLEN_MSDOSFILEI      2
+#define ADEDLEN_DID             4
+#define ADEDLEN_PRIVDEV         8
+#define ADEDLEN_PRIVINO         8
+#define ADEDLEN_PRIVSYN         8
+#define ADEDLEN_PRIVID          4
+
+/*
+ * Sharemode locks fcntl() offsets
+ */
+#if _FILE_OFFSET_BITS == 64 || defined(HAVE_LARGEFILE)
+#define AD_FILELOCK_BASE (UINT64_C(0x7FFFFFFFFFFFFFFF) - 9)
+#else
+#define AD_FILELOCK_BASE (UINT32_C(0x7FFFFFFF) - 9)
+#endif
+#define BYTELOCK_MAX (AD_FILELOCK_BASE - 1)
+
+#define AD_FILELOCK_OPEN_WR        (AD_FILELOCK_BASE + 0)
+#define AD_FILELOCK_OPEN_RD        (AD_FILELOCK_BASE + 1)
+#define AD_FILELOCK_RSRC_OPEN_WR   (AD_FILELOCK_BASE + 2)
+#define AD_FILELOCK_RSRC_OPEN_RD   (AD_FILELOCK_BASE + 3)
+#define AD_FILELOCK_DENY_WR        (AD_FILELOCK_BASE + 4)
+#define AD_FILELOCK_DENY_RD        (AD_FILELOCK_BASE + 5)
+#define AD_FILELOCK_RSRC_DENY_WR   (AD_FILELOCK_BASE + 6)
+#define AD_FILELOCK_RSRC_DENY_RD   (AD_FILELOCK_BASE + 7)
+#define AD_FILELOCK_OPEN_NONE      (AD_FILELOCK_BASE + 8)
+#define AD_FILELOCK_RSRC_OPEN_NONE (AD_FILELOCK_BASE + 9)
+
+/* Time stuff we overload the bits a little */
+#define AD_DATE_CREATE         0
+#define AD_DATE_MODIFY         4
+#define AD_DATE_BACKUP         8
+#define AD_DATE_ACCESS        12
+#define AD_DATE_MASK          (AD_DATE_CREATE | AD_DATE_MODIFY | \
+                               AD_DATE_BACKUP | AD_DATE_ACCESS)
+#define AD_DATE_UNIX          (1 << 10)
+#define AD_DATE_START         0x80000000
+#define AD_DATE_DELTA         946684800
+#define AD_DATE_FROM_UNIX(x)  (htonl((x) - AD_DATE_DELTA))
+#define AD_DATE_TO_UNIX(x)    (ntohl(x) + AD_DATE_DELTA)
+
+#define AD_CONV_WIPE_BLANK     (1<<0)
+#define AD_CONV_DELETE         (1<<1)
+
+struct adouble;
+
+size_t ad_getentrylen(const struct adouble *ad, int eid);
+size_t ad_getentryoff(const struct adouble *ad, int eid);
+size_t ad_setentrylen(struct adouble *ad, int eid, size_t len);
+size_t ad_setentryoff(struct adouble *ad, int eid, size_t off);
+char *ad_get_entry(const struct adouble *ad, int eid);
+int ad_getdate(const struct adouble *ad, unsigned int dateoff, uint32_t *date);
+int ad_setdate(struct adouble *ad, unsigned int dateoff, uint32_t date);
+int ad_convert(struct vfs_handle_struct *handle,
+              const struct smb_filename *smb_fname,
+              const char *catia_mappings,
+              uint32_t flags);
+struct adouble *ad_init(TALLOC_CTX *ctx, adouble_type_t type);
+struct adouble *ad_get(TALLOC_CTX *ctx,
+                      vfs_handle_struct *handle,
+                      const struct smb_filename *smb_fname,
+                      adouble_type_t type);
+struct adouble *ad_fget(TALLOC_CTX *ctx, vfs_handle_struct *handle,
+                       files_struct *fsp, adouble_type_t type);
+int ad_set(vfs_handle_struct *handle,
+          struct adouble *ad,
+          const struct smb_filename *smb_fname);
+int ad_fset(struct vfs_handle_struct *handle,
+           struct adouble *ad,
+           files_struct *fsp);
+bool is_adouble_file(const char *path);
+int adouble_path(TALLOC_CTX *ctx,
+                const struct smb_filename *smb_fname_in,
+                struct smb_filename **pp_smb_fname_out);
+
+AfpInfo *afpinfo_new(TALLOC_CTX *ctx);
+ssize_t afpinfo_pack(const AfpInfo *ai, char *buf);
+AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data);
+
+#endif
index f05b6cdedc244d605d4d9cb287c3bbe46dafe10c..2dbbaefba8ff07b5de2798766f8bb99203fedc44 100644 (file)
@@ -33,6 +33,7 @@
 #include "offload_token.h"
 #include "string_replace.h"
 #include "hash_inode.h"
+#include "lib/adouble.h"
 
 /*
  * Enhanced OS X and Netatalk compatibility
@@ -105,1989 +106,104 @@ static struct global_fruit_config {
 #define DBGC_CLASS vfs_fruit_debug_level
 
 #define FRUIT_PARAM_TYPE_NAME "fruit"
-#define ADOUBLE_NAME_PREFIX "._"
 
-#define NETATALK_META_XATTR "org.netatalk.Metadata"
-#define NETATALK_RSRC_XATTR "org.netatalk.ResourceFork"
-
-#if defined(HAVE_ATTROPEN)
-#define AFPINFO_EA_NETATALK NETATALK_META_XATTR
-#define AFPRESOURCE_EA_NETATALK NETATALK_RSRC_XATTR
-#else
-#define AFPINFO_EA_NETATALK "user." NETATALK_META_XATTR
-#define AFPRESOURCE_EA_NETATALK "user." NETATALK_RSRC_XATTR
-#endif
-
-enum apple_fork {APPLE_FORK_DATA, APPLE_FORK_RSRC};
-
-enum fruit_rsrc {FRUIT_RSRC_STREAM, FRUIT_RSRC_ADFILE, FRUIT_RSRC_XATTR};
-enum fruit_meta {FRUIT_META_STREAM, FRUIT_META_NETATALK};
-enum fruit_locking {FRUIT_LOCKING_NETATALK, FRUIT_LOCKING_NONE};
-enum fruit_encoding {FRUIT_ENC_NATIVE, FRUIT_ENC_PRIVATE};
-
-struct fruit_config_data {
-       enum fruit_rsrc rsrc;
-       enum fruit_meta meta;
-       enum fruit_locking locking;
-       enum fruit_encoding encoding;
-       bool use_aapl;          /* config from smb.conf */
-       bool use_copyfile;
-       bool readdir_attr_enabled;
-       bool unix_info_enabled;
-       bool copyfile_enabled;
-       bool veto_appledouble;
-       bool posix_rename;
-       bool aapl_zero_file_id;
-       const char *model;
-       bool time_machine;
-       off_t time_machine_max_size;
-       bool wipe_intentionally_left_blank_rfork;
-       bool delete_empty_adfiles;
-
-       /*
-        * Additional options, all enabled by default,
-        * possibly useful for analyzing performance. The associated
-        * operations with each of them may be expensive, so having
-        * the chance to disable them individually gives a chance
-        * tweaking the setup for the particular usecase.
-        */
-       bool readdir_attr_rsize;
-       bool readdir_attr_finder_info;
-       bool readdir_attr_max_access;
-};
-
-static const struct enum_list fruit_rsrc[] = {
-       {FRUIT_RSRC_STREAM, "stream"}, /* pass on to vfs_streams_xattr */
-       {FRUIT_RSRC_ADFILE, "file"}, /* ._ AppleDouble file */
-       {FRUIT_RSRC_XATTR, "xattr"}, /* Netatalk compatible xattr (ZFS only) */
-       { -1, NULL}
-};
-
-static const struct enum_list fruit_meta[] = {
-       {FRUIT_META_STREAM, "stream"}, /* pass on to vfs_streams_xattr */
-       {FRUIT_META_NETATALK, "netatalk"}, /* Netatalk compatible xattr */
-       { -1, NULL}
-};
-
-static const struct enum_list fruit_locking[] = {
-       {FRUIT_LOCKING_NETATALK, "netatalk"}, /* synchronize locks with Netatalk */
-       {FRUIT_LOCKING_NONE, "none"},
-       { -1, NULL}
-};
-
-static const struct enum_list fruit_encoding[] = {
-       {FRUIT_ENC_NATIVE, "native"}, /* map unicode private chars to ASCII */
-       {FRUIT_ENC_PRIVATE, "private"}, /* keep unicode private chars */
-       { -1, NULL}
-};
-
-static const char *fruit_catia_maps =
-       "0x01:0xf001,0x02:0xf002,0x03:0xf003,0x04:0xf004,"
-       "0x05:0xf005,0x06:0xf006,0x07:0xf007,0x08:0xf008,"
-       "0x09:0xf009,0x0a:0xf00a,0x0b:0xf00b,0x0c:0xf00c,"
-       "0x0d:0xf00d,0x0e:0xf00e,0x0f:0xf00f,0x10:0xf010,"
-       "0x11:0xf011,0x12:0xf012,0x13:0xf013,0x14:0xf014,"
-       "0x15:0xf015,0x16:0xf016,0x17:0xf017,0x18:0xf018,"
-       "0x19:0xf019,0x1a:0xf01a,0x1b:0xf01b,0x1c:0xf01c,"
-       "0x1d:0xf01d,0x1e:0xf01e,0x1f:0xf01f,"
-       "0x22:0xf020,0x2a:0xf021,0x3a:0xf022,0x3c:0xf023,"
-       "0x3e:0xf024,0x3f:0xf025,0x5c:0xf026,0x7c:0xf027,"
-       "0x0d:0xf00d";
-
-/*****************************************************************************
- * Defines, functions and data structures that deal with AppleDouble
- *****************************************************************************/
-
-/*
- * There are two AppleDouble blobs we deal with:
- *
- * - ADOUBLE_META - AppleDouble blob used by Netatalk for storing
- *   metadata in an xattr
- *
- * - ADOUBLE_RSRC - AppleDouble blob used by OS X and Netatalk in
- *   ._ files
- */
-typedef enum {ADOUBLE_META, ADOUBLE_RSRC} adouble_type_t;
-
-/* Version info */
-#define AD_VERSION2     0x00020000
-#define AD_VERSION      AD_VERSION2
-
-/*
- * AppleDouble entry IDs.
- */
-#define ADEID_DFORK         1
-#define ADEID_RFORK         2
-#define ADEID_NAME          3
-#define ADEID_COMMENT       4
-#define ADEID_ICONBW        5
-#define ADEID_ICONCOL       6
-#define ADEID_FILEI         7
-#define ADEID_FILEDATESI    8
-#define ADEID_FINDERI       9
-#define ADEID_MACFILEI      10
-#define ADEID_PRODOSFILEI   11
-#define ADEID_MSDOSFILEI    12
-#define ADEID_SHORTNAME     13
-#define ADEID_AFPFILEI      14
-#define ADEID_DID           15
-
-/* Private Netatalk entries */
-#define ADEID_PRIVDEV       16
-#define ADEID_PRIVINO       17
-#define ADEID_PRIVSYN       18
-#define ADEID_PRIVID        19
-#define ADEID_MAX           (ADEID_PRIVID + 1)
-
-/*
- * These are the real ids for the private entries,
- * as stored in the adouble file
- */
-#define AD_DEV              0x80444556
-#define AD_INO              0x80494E4F
-#define AD_SYN              0x8053594E
-#define AD_ID               0x8053567E
-
-/* Number of actually used entries */
-#define ADEID_NUM_XATTR      8
-#define ADEID_NUM_DOT_UND    2
-#define ADEID_NUM_RSRC_XATTR 1
-
-/* AppleDouble magic */
-#define AD_APPLESINGLE_MAGIC 0x00051600
-#define AD_APPLEDOUBLE_MAGIC 0x00051607
-#define AD_MAGIC             AD_APPLEDOUBLE_MAGIC
-
-/* Sizes of relevant entry bits */
-#define ADEDLEN_MAGIC       4
-#define ADEDLEN_VERSION     4
-#define ADEDLEN_FILLER      16
-#define AD_FILLER_TAG       "Netatalk        " /* should be 16 bytes */
-#define AD_FILLER_TAG_OSX   "Mac OS X        " /* should be 16 bytes */
-#define ADEDLEN_NENTRIES    2
-#define AD_HEADER_LEN       (ADEDLEN_MAGIC + ADEDLEN_VERSION + \
-                            ADEDLEN_FILLER + ADEDLEN_NENTRIES) /* 26 */
-#define AD_ENTRY_LEN_EID    4
-#define AD_ENTRY_LEN_OFF    4
-#define AD_ENTRY_LEN_LEN    4
-#define AD_ENTRY_LEN (AD_ENTRY_LEN_EID + AD_ENTRY_LEN_OFF + AD_ENTRY_LEN_LEN)
-
-/* Field widths */
-#define ADEDLEN_NAME            255
-#define ADEDLEN_COMMENT         200
-#define ADEDLEN_FILEI           16
-#define ADEDLEN_FINDERI         32
-#define ADEDLEN_FILEDATESI      16
-#define ADEDLEN_SHORTNAME       12 /* length up to 8.3 */
-#define ADEDLEN_AFPFILEI        4
-#define ADEDLEN_MACFILEI        4
-#define ADEDLEN_PRODOSFILEI     8
-#define ADEDLEN_MSDOSFILEI      2
-#define ADEDLEN_DID             4
-#define ADEDLEN_PRIVDEV         8
-#define ADEDLEN_PRIVINO         8
-#define ADEDLEN_PRIVSYN         8
-#define ADEDLEN_PRIVID          4
-
-/* Offsets */
-#define ADEDOFF_MAGIC         0
-#define ADEDOFF_VERSION       (ADEDOFF_MAGIC + ADEDLEN_MAGIC)
-#define ADEDOFF_FILLER        (ADEDOFF_VERSION + ADEDLEN_VERSION)
-#define ADEDOFF_NENTRIES      (ADEDOFF_FILLER + ADEDLEN_FILLER)
-
-#define ADEDOFF_FINDERI_XATTR    (AD_HEADER_LEN + \
-                                 (ADEID_NUM_XATTR * AD_ENTRY_LEN))
-#define ADEDOFF_COMMENT_XATTR    (ADEDOFF_FINDERI_XATTR    + ADEDLEN_FINDERI)
-#define ADEDOFF_FILEDATESI_XATTR (ADEDOFF_COMMENT_XATTR    + ADEDLEN_COMMENT)
-#define ADEDOFF_AFPFILEI_XATTR   (ADEDOFF_FILEDATESI_XATTR + \
-                                 ADEDLEN_FILEDATESI)
-#define ADEDOFF_PRIVDEV_XATTR    (ADEDOFF_AFPFILEI_XATTR   + ADEDLEN_AFPFILEI)
-#define ADEDOFF_PRIVINO_XATTR    (ADEDOFF_PRIVDEV_XATTR    + ADEDLEN_PRIVDEV)
-#define ADEDOFF_PRIVSYN_XATTR    (ADEDOFF_PRIVINO_XATTR    + ADEDLEN_PRIVINO)
-#define ADEDOFF_PRIVID_XATTR     (ADEDOFF_PRIVSYN_XATTR    + ADEDLEN_PRIVSYN)
-
-#define ADEDOFF_FINDERI_DOT_UND  (AD_HEADER_LEN + \
-                                 (ADEID_NUM_DOT_UND * AD_ENTRY_LEN))
-#define ADEDOFF_RFORK_DOT_UND    (ADEDOFF_FINDERI_DOT_UND + ADEDLEN_FINDERI)
-
-#define AD_DATASZ_XATTR (AD_HEADER_LEN + \
-                        (ADEID_NUM_XATTR * AD_ENTRY_LEN) + \
-                        ADEDLEN_FINDERI + ADEDLEN_COMMENT + \
-                        ADEDLEN_FILEDATESI + ADEDLEN_AFPFILEI + \
-                        ADEDLEN_PRIVDEV + ADEDLEN_PRIVINO + \
-                        ADEDLEN_PRIVSYN + ADEDLEN_PRIVID)
-
-#if AD_DATASZ_XATTR != 402
-#error bad size for AD_DATASZ_XATTR
-#endif
-
-#define AD_DATASZ_DOT_UND (AD_HEADER_LEN + \
-                          (ADEID_NUM_DOT_UND * AD_ENTRY_LEN) + \
-                          ADEDLEN_FINDERI)
-#if AD_DATASZ_DOT_UND != 82
-#error bad size for AD_DATASZ_DOT_UND
-#endif
-
-/*
- * Sharemode locks fcntl() offsets
- */
-#if _FILE_OFFSET_BITS == 64 || defined(HAVE_LARGEFILE)
-#define AD_FILELOCK_BASE (UINT64_C(0x7FFFFFFFFFFFFFFF) - 9)
-#else
-#define AD_FILELOCK_BASE (UINT32_C(0x7FFFFFFF) - 9)
-#endif
-#define BYTELOCK_MAX (AD_FILELOCK_BASE - 1)
-
-#define AD_FILELOCK_OPEN_WR        (AD_FILELOCK_BASE + 0)
-#define AD_FILELOCK_OPEN_RD        (AD_FILELOCK_BASE + 1)
-#define AD_FILELOCK_RSRC_OPEN_WR   (AD_FILELOCK_BASE + 2)
-#define AD_FILELOCK_RSRC_OPEN_RD   (AD_FILELOCK_BASE + 3)
-#define AD_FILELOCK_DENY_WR        (AD_FILELOCK_BASE + 4)
-#define AD_FILELOCK_DENY_RD        (AD_FILELOCK_BASE + 5)
-#define AD_FILELOCK_RSRC_DENY_WR   (AD_FILELOCK_BASE + 6)
-#define AD_FILELOCK_RSRC_DENY_RD   (AD_FILELOCK_BASE + 7)
-#define AD_FILELOCK_OPEN_NONE      (AD_FILELOCK_BASE + 8)
-#define AD_FILELOCK_RSRC_OPEN_NONE (AD_FILELOCK_BASE + 9)
-
-/* Time stuff we overload the bits a little */
-#define AD_DATE_CREATE         0
-#define AD_DATE_MODIFY         4
-#define AD_DATE_BACKUP         8
-#define AD_DATE_ACCESS        12
-#define AD_DATE_MASK          (AD_DATE_CREATE | AD_DATE_MODIFY | \
-                               AD_DATE_BACKUP | AD_DATE_ACCESS)
-#define AD_DATE_UNIX          (1 << 10)
-#define AD_DATE_START         0x80000000
-#define AD_DATE_DELTA         946684800
-#define AD_DATE_FROM_UNIX(x)  (htonl((x) - AD_DATE_DELTA))
-#define AD_DATE_TO_UNIX(x)    (ntohl(x) + AD_DATE_DELTA)
-
-#define AD_XATTR_HDR_MAGIC    0x41545452 /* 'ATTR' */
-#define AD_XATTR_MAX_ENTRIES  1024 /* Some arbitrarily enforced limit */
-#define AD_XATTR_HDR_SIZE     36
-#define AD_XATTR_MAX_HDR_SIZE 65536
-
-#define AD_CONV_WIPE_BLANK     (1<<0)
-#define AD_CONV_DELETE         (1<<1)
-
-/*
- * Both struct ad_xattr_header and struct ad_xattr_entry describe the in memory
- * representation as well as the on-disk format.
- *
- * The ad_xattr_header follows the FinderInfo data in the FinderInfo entry if
- * the length of the FinderInfo entry is larger then 32 bytes. It is then
- * preceeded with 2 bytes padding.
- *
- * Cf: https://opensource.apple.com/source/xnu/xnu-4570.1.46/bsd/vfs/vfs_xattr.c
- */
-
-struct ad_xattr_header {
-       uint32_t adx_magic;        /* ATTR_HDR_MAGIC */
-       uint32_t adx_debug_tag;    /* for debugging == file id of owning file */
-       uint32_t adx_total_size;   /* file offset of end of attribute header + entries + data */
-       uint32_t adx_data_start;   /* file offset to attribute data area */
-       uint32_t adx_data_length;  /* length of attribute data area */
-       uint32_t adx_reserved[3];
-       uint16_t adx_flags;
-       uint16_t adx_num_attrs;
-};
-
-/* On-disk entries are aligned on 4 byte boundaries */
-struct ad_xattr_entry {
-       uint32_t adx_offset;    /* file offset to data */
-       uint32_t adx_length;    /* size of attribute data */
-       uint16_t adx_flags;
-       uint8_t  adx_namelen;   /* included the NULL terminator */
-       char    *adx_name;      /* NULL-terminated UTF-8 name */
-};
-
-struct ad_entry {
-       size_t ade_off;
-       size_t ade_len;
-};
-
-struct adouble {
-       files_struct             *ad_fsp;
-       bool                      ad_opened;
-       adouble_type_t            ad_type;
-       uint32_t                  ad_magic;
-       uint32_t                  ad_version;
-       uint8_t                   ad_filler[ADEDLEN_FILLER];
-       struct ad_entry           ad_eid[ADEID_MAX];
-       char                     *ad_data;
-       struct ad_xattr_header    adx_header;
-       struct ad_xattr_entry    *adx_entries;
-};
-
-struct ad_entry_order {
-       uint32_t id, offset, len;
-};
-
-/* Netatalk AppleDouble metadata xattr */
-static const
-struct ad_entry_order entry_order_meta_xattr[ADEID_NUM_XATTR + 1] = {
-       {ADEID_FINDERI,    ADEDOFF_FINDERI_XATTR,    ADEDLEN_FINDERI},
-       {ADEID_COMMENT,    ADEDOFF_COMMENT_XATTR,    0},
-       {ADEID_FILEDATESI, ADEDOFF_FILEDATESI_XATTR, ADEDLEN_FILEDATESI},
-       {ADEID_AFPFILEI,   ADEDOFF_AFPFILEI_XATTR,   ADEDLEN_AFPFILEI},
-       {ADEID_PRIVDEV,    ADEDOFF_PRIVDEV_XATTR,    0},
-       {ADEID_PRIVINO,    ADEDOFF_PRIVINO_XATTR,    0},
-       {ADEID_PRIVSYN,    ADEDOFF_PRIVSYN_XATTR,    0},
-       {ADEID_PRIVID,     ADEDOFF_PRIVID_XATTR,     0},
-       {0, 0, 0}
-};
-
-/* AppleDouble resource fork file (the ones prefixed by "._") */
-static const
-struct ad_entry_order entry_order_dot_und[ADEID_NUM_DOT_UND + 1] = {
-       {ADEID_FINDERI,    ADEDOFF_FINDERI_DOT_UND,  ADEDLEN_FINDERI},
-       {ADEID_RFORK,      ADEDOFF_RFORK_DOT_UND,    0},
-       {0, 0, 0}
-};
-
-/* Conversion from enumerated id to on-disk AppleDouble id */
-#define AD_EID_DISK(a) (set_eid[a])
-static const uint32_t set_eid[] = {
-       0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
-       AD_DEV, AD_INO, AD_SYN, AD_ID
-};
-
-static char empty_resourcefork[] = {
-       0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
-       0x54, 0x68, 0x69, 0x73, 0x20, 0x72, 0x65, 0x73,
-       0x6F, 0x75, 0x72, 0x63, 0x65, 0x20, 0x66, 0x6F,
-       0x72, 0x6B, 0x20, 0x69, 0x6E, 0x74, 0x65, 0x6E,
-       0x74, 0x69, 0x6F, 0x6E, 0x61, 0x6C, 0x6C, 0x79,
-       0x20, 0x6C, 0x65, 0x66, 0x74, 0x20, 0x62, 0x6C,
-       0x61, 0x6E, 0x6B, 0x20, 0x20, 0x20, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x1C, 0x00, 0x1E, 0xFF, 0xFF
-};
-
-struct fio {
-       /* tcon config handle */
-       struct fruit_config_data *config;
-
-       /* Denote stream type, meta or rsrc */
-       adouble_type_t type;
-
-       /* Whether the create created the stream */
-       bool created;
-
-       /*
-        * AFP_AfpInfo stream created, but not written yet, thus still a fake
-        * pipe fd. This is set to true in fruit_open_meta if there was no
-        * exisiting stream but the caller requested O_CREAT. It is later set to
-        * false when we get a write on the stream that then does open and
-        * create the stream.
-        */
-       bool fake_fd;
-       int flags;
-       int mode;
-};
-
-/*
- * Forward declarations
- */
-static struct adouble *ad_init(TALLOC_CTX *ctx,
-                              adouble_type_t type);
-static struct adouble *ad_get(TALLOC_CTX *ctx,
-                             vfs_handle_struct *handle,
-                             const struct smb_filename *smb_fname,
-                             adouble_type_t type);
-static int ad_set(vfs_handle_struct *handle,
-                 struct adouble *ad,
-                 const struct smb_filename *smb_fname);
-static int ad_fset(struct vfs_handle_struct *handle,
-                  struct adouble *ad,
-                  files_struct *fsp);
-static int adouble_path(TALLOC_CTX *ctx,
-                       const struct smb_filename *smb_fname__in,
-                       struct smb_filename **ppsmb_fname_out);
-static AfpInfo *afpinfo_new(TALLOC_CTX *ctx);
-static ssize_t afpinfo_pack(const AfpInfo *ai, char *buf);
-static AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data);
-
-static size_t ad_getentrylen(const struct adouble *ad, int eid)
-{
-       return ad->ad_eid[eid].ade_len;
-}
-
-static size_t ad_getentryoff(const struct adouble *ad, int eid)
-{
-       return ad->ad_eid[eid].ade_off;
-}
-
-static size_t ad_setentrylen(struct adouble *ad, int eid, size_t len)
-{
-       return ad->ad_eid[eid].ade_len = len;
-}
-
-static size_t ad_setentryoff(struct adouble *ad, int eid, size_t off)
-{
-       return ad->ad_eid[eid].ade_off = off;
-}
-
-/**
- * Return a pointer to an AppleDouble entry
- *
- * Returns NULL if the entry is not present
- **/
-static char *ad_get_entry(const struct adouble *ad, int eid)
-{
-       off_t off = ad_getentryoff(ad, eid);
-       size_t len = ad_getentrylen(ad, eid);
-
-       if (off == 0 || len == 0) {
-               return NULL;
-       }
-
-       return ad->ad_data + off;
-}
-
-/**
- * Get a date
- **/
-static int ad_getdate(const struct adouble *ad,
-                     unsigned int dateoff,
-                     uint32_t *date)
-{
-       bool xlate = (dateoff & AD_DATE_UNIX);
-       char *p = NULL;
-
-       dateoff &= AD_DATE_MASK;
-       p = ad_get_entry(ad, ADEID_FILEDATESI);
-       if (p == NULL) {
-               return -1;
-       }
-
-       if (dateoff > AD_DATE_ACCESS) {
-           return -1;
-       }
-
-       memcpy(date, p + dateoff, sizeof(uint32_t));
-
-       if (xlate) {
-               *date = AD_DATE_TO_UNIX(*date);
-       }
-       return 0;
-}
-
-/**
- * Set a date
- **/
-static int ad_setdate(struct adouble *ad, unsigned int dateoff, uint32_t date)
-{
-       bool xlate = (dateoff & AD_DATE_UNIX);
-       char *p = NULL;
-
-       p = ad_get_entry(ad, ADEID_FILEDATESI);
-       if (p == NULL) {
-               return -1;
-       }
-
-       dateoff &= AD_DATE_MASK;
-       if (xlate) {
-               date = AD_DATE_FROM_UNIX(date);
-       }
-
-       if (dateoff > AD_DATE_ACCESS) {
-               return -1;
-       }
-
-       memcpy(p + dateoff, &date, sizeof(date));
-
-       return 0;
-}
-
-
-/**
- * Map on-disk AppleDouble id to enumerated id
- **/
-static uint32_t get_eid(uint32_t eid)
-{
-       if (eid <= 15) {
-               return eid;
-       }
-
-       switch (eid) {
-       case AD_DEV:
-               return ADEID_PRIVDEV;
-       case AD_INO:
-               return ADEID_PRIVINO;
-       case AD_SYN:
-               return ADEID_PRIVSYN;
-       case AD_ID:
-               return ADEID_PRIVID;
-       default:
-               break;
-       }
-
-       return 0;
-}
-
-/**
- * Pack AppleDouble structure into data buffer
- **/
-static bool ad_pack(struct adouble *ad)
-{
-       uint32_t       eid;
-       uint16_t       nent;
-       uint32_t       bufsize;
-       uint32_t       offset = 0;
-
-       bufsize = talloc_get_size(ad->ad_data);
-       if (bufsize < AD_DATASZ_DOT_UND) {
-               DBG_ERR("bad buffer size [0x%" PRIx32 "]\n", bufsize);
-               return false;
-       }
-
-       if (offset + ADEDLEN_MAGIC < offset ||
-                       offset + ADEDLEN_MAGIC >= bufsize) {
-               return false;
-       }
-       RSIVAL(ad->ad_data, offset, ad->ad_magic);
-       offset += ADEDLEN_MAGIC;
-
-       if (offset + ADEDLEN_VERSION < offset ||
-                       offset + ADEDLEN_VERSION >= bufsize) {
-               return false;
-       }
-       RSIVAL(ad->ad_data, offset, ad->ad_version);
-       offset += ADEDLEN_VERSION;
-
-       if (offset + ADEDLEN_FILLER < offset ||
-                       offset + ADEDLEN_FILLER >= bufsize) {
-               return false;
-       }
-       if (ad->ad_type == ADOUBLE_RSRC) {
-               memcpy(ad->ad_data + offset, AD_FILLER_TAG, ADEDLEN_FILLER);
-       }
-       offset += ADEDLEN_FILLER;
-
-       if (offset + ADEDLEN_NENTRIES < offset ||
-                       offset + ADEDLEN_NENTRIES >= bufsize) {
-               return false;
-       }
-       offset += ADEDLEN_NENTRIES;
-
-       for (eid = 0, nent = 0; eid < ADEID_MAX; eid++) {
-               if (ad->ad_eid[eid].ade_off == 0) {
-                       /*
-                        * ade_off is also used as indicator whether a
-                        * specific entry is used or not
-                        */
-                       continue;
-               }
-
-               if (offset + AD_ENTRY_LEN_EID < offset ||
-                               offset + AD_ENTRY_LEN_EID >= bufsize) {
-                       return false;
-               }
-               RSIVAL(ad->ad_data, offset, AD_EID_DISK(eid));
-               offset += AD_ENTRY_LEN_EID;
-
-               if (offset + AD_ENTRY_LEN_OFF < offset ||
-                               offset + AD_ENTRY_LEN_OFF >= bufsize) {
-                       return false;
-               }
-               RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_off);
-               offset += AD_ENTRY_LEN_OFF;
-
-               if (offset + AD_ENTRY_LEN_LEN < offset ||
-                               offset + AD_ENTRY_LEN_LEN >= bufsize) {
-                       return false;
-               }
-               RSIVAL(ad->ad_data, offset, ad->ad_eid[eid].ade_len);
-               offset += AD_ENTRY_LEN_LEN;
-
-               nent++;
-       }
-
-       if (ADEDOFF_NENTRIES + 2 >= bufsize) {
-               return false;
-       }
-       RSSVAL(ad->ad_data, ADEDOFF_NENTRIES, nent);
-
-       return true;
-}
-
-static bool ad_unpack_xattrs(struct adouble *ad)
-{
-       struct ad_xattr_header *h = &ad->adx_header;
-       const char *p = ad->ad_data;
-       uint32_t hoff;
-       uint32_t i;
-
-       if (ad_getentrylen(ad, ADEID_FINDERI) <= ADEDLEN_FINDERI) {
-               return true;
-       }
-
-       /* 2 bytes padding */
-       hoff = ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI + 2;
-
-       h->adx_magic       = RIVAL(p, hoff + 0);
-       h->adx_debug_tag   = RIVAL(p, hoff + 4); /* Not used -> not checked */
-       h->adx_total_size  = RIVAL(p, hoff + 8);
-       h->adx_data_start  = RIVAL(p, hoff + 12);
-       h->adx_data_length = RIVAL(p, hoff + 16);
-       h->adx_flags       = RSVAL(p, hoff + 32); /* Not used -> not checked */
-       h->adx_num_attrs   = RSVAL(p, hoff + 34);
-
-       if (h->adx_magic != AD_XATTR_HDR_MAGIC) {
-               DBG_ERR("Bad magic: 0x%" PRIx32 "\n", h->adx_magic);
-               return false;
-       }
-
-       if (h->adx_total_size > ad_getentryoff(ad, ADEID_RFORK)) {
-               DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size);
-               return false;
-       }
-       if (h->adx_total_size > AD_XATTR_MAX_HDR_SIZE) {
-               DBG_ERR("Bad total size: 0x%" PRIx32 "\n", h->adx_total_size);
-               return false;
-       }
-
-       if (h->adx_data_start < (hoff + AD_XATTR_HDR_SIZE)) {
-               DBG_ERR("Bad start: 0x%" PRIx32 "\n", h->adx_data_start);
-               return false;
-       }
-
-       if ((h->adx_data_start + h->adx_data_length) < h->adx_data_start) {
-               DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length);
-               return false;
-       }
-       if ((h->adx_data_start + h->adx_data_length) >
-           ad->adx_header.adx_total_size)
-       {
-               DBG_ERR("Bad length: %" PRIu32 "\n", h->adx_data_length);
-               return false;
-       }
-
-       if (h->adx_num_attrs > AD_XATTR_MAX_ENTRIES) {
-               DBG_ERR("Bad num xattrs: %" PRIu16 "\n", h->adx_num_attrs);
-               return false;
-       }
-
-       if (h->adx_num_attrs == 0) {
-               return true;
-       }
-
-       ad->adx_entries = talloc_zero_array(
-               ad, struct ad_xattr_entry, h->adx_num_attrs);
-       if (ad->adx_entries == NULL) {
-               return false;
-       }
-
-       hoff += AD_XATTR_HDR_SIZE;
-
-       for (i = 0; i < h->adx_num_attrs; i++) {
-               struct ad_xattr_entry *e = &ad->adx_entries[i];
-
-               hoff = (hoff + 3) & ~3;
-
-               e->adx_offset  = RIVAL(p, hoff + 0);
-               e->adx_length  = RIVAL(p, hoff + 4);
-               e->adx_flags   = RSVAL(p, hoff + 8);
-               e->adx_namelen = *(p + hoff + 10);
-
-               if (e->adx_offset >= ad->adx_header.adx_total_size) {
-                       DBG_ERR("Bad adx_offset: %" PRIx32 "\n",
-                               e->adx_offset);
-                       return false;
-               }
-
-               if ((e->adx_offset + e->adx_length) < e->adx_offset) {
-                       DBG_ERR("Bad adx_length: %" PRIx32 "\n",
-                               e->adx_length);
-                       return false;
-               }
-
-               if ((e->adx_offset + e->adx_length) >
-                   ad->adx_header.adx_total_size)
-               {
-                       DBG_ERR("Bad adx_length: %" PRIx32 "\n",
-                               e->adx_length);
-                       return false;
-               }
-
-               if (e->adx_namelen == 0) {
-                       DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
-                               e->adx_namelen);
-                       return false;
-               }
-               if ((hoff + 11 + e->adx_namelen) < hoff + 11) {
-                       DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
-                               e->adx_namelen);
-                       return false;
-               }
-               if ((hoff + 11 + e->adx_namelen) >
-                   ad->adx_header.adx_data_start)
-               {
-                       DBG_ERR("Bad adx_namelen: %" PRIx32 "\n",
-                               e->adx_namelen);
-                       return false;
-               }
-
-               e->adx_name = talloc_strndup(ad->adx_entries,
-                                            p + hoff + 11,
-                                            e->adx_namelen);
-               if (e->adx_name == NULL) {
-                       return false;
-               }
-
-               DBG_DEBUG("xattr [%s] offset [0x%x] size [0x%x]\n",
-                         e->adx_name, e->adx_offset, e->adx_length);
-               dump_data(10, (uint8_t *)(ad->ad_data + e->adx_offset),
-                         e->adx_length);
-
-               hoff += 11 + e->adx_namelen;
-       }
-
-       return true;
-}
-
-/**
- * Unpack an AppleDouble blob into a struct adoble
- **/
-static bool ad_unpack(struct adouble *ad, const size_t nentries,
-                     size_t filesize)
-{
-       size_t bufsize = talloc_get_size(ad->ad_data);
-       size_t adentries, i;
-       uint32_t eid, len, off;
-       bool ok;
-
-       /*
-        * The size of the buffer ad->ad_data is checked when read, so
-        * we wouldn't have to check our own offsets, a few extra
-        * checks won't hurt though. We have to check the offsets we
-        * read from the buffer anyway.
-        */
-
-       if (bufsize < (AD_HEADER_LEN + (AD_ENTRY_LEN * nentries))) {
-               DEBUG(1, ("bad size\n"));
-               return false;
-       }
-
-       ad->ad_magic = RIVAL(ad->ad_data, 0);
-       ad->ad_version = RIVAL(ad->ad_data, ADEDOFF_VERSION);
-       if ((ad->ad_magic != AD_MAGIC) || (ad->ad_version != AD_VERSION)) {
-               DEBUG(1, ("wrong magic or version\n"));
-               return false;
-       }
-
-       memcpy(ad->ad_filler, ad->ad_data + ADEDOFF_FILLER, ADEDLEN_FILLER);
-
-       adentries = RSVAL(ad->ad_data, ADEDOFF_NENTRIES);
-       if (adentries != nentries) {
-               DEBUG(1, ("invalid number of entries: %zu\n",
-                         adentries));
-               return false;
-       }
-
-       /* now, read in the entry bits */
-       for (i = 0; i < adentries; i++) {
-               eid = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN));
-               eid = get_eid(eid);
-               off = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 4);
-               len = RIVAL(ad->ad_data, AD_HEADER_LEN + (i * AD_ENTRY_LEN) + 8);
-
-               if (!eid || eid >= ADEID_MAX) {
-                       DEBUG(1, ("bogus eid %d\n", eid));
-                       return false;
-               }
-
-               /*
-                * All entries other than the resource fork are
-                * expected to be read into the ad_data buffer, so
-                * ensure the specified offset is within that bound
-                */
-               if ((off > bufsize) && (eid != ADEID_RFORK)) {
-                       DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
-                                 eid, off, len));
-                       return false;
-               }
-
-               /*
-                * All entries besides FinderInfo and resource fork
-                * must fit into the buffer. FinderInfo is special as
-                * it may be larger then the default 32 bytes (if it
-                * contains marshalled xattrs), but we will fixup that
-                * in ad_convert(). And the resource fork is never
-                * accessed directly by the ad_data buf (also see
-                * comment above) anyway.
-                */
-               if ((eid != ADEID_RFORK) &&
-                   (eid != ADEID_FINDERI) &&
-                   ((off + len) > bufsize)) {
-                       DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
-                                 eid, off, len));
-                       return false;
-               }
-
-               /*
-                * That would be obviously broken
-                */
-               if (off > filesize) {
-                       DEBUG(1, ("bogus eid %d: off: %" PRIu32 ", len: %" PRIu32 "\n",
-                                 eid, off, len));
-                       return false;
-               }
-
-               /*
-                * Check for any entry that has its end beyond the
-                * filesize.
-                */
-               if (off + len < off) {
-                       DEBUG(1, ("offset wrap in eid %d: off: %" PRIu32
-                                 ", len: %" PRIu32 "\n",
-                                 eid, off, len));
-                       return false;
-
-               }
-               if (off + len > filesize) {
-                       /*
-                        * If this is the resource fork entry, we fix
-                        * up the length, for any other entry we bail
-                        * out.
-                        */
-                       if (eid != ADEID_RFORK) {
-                               DEBUG(1, ("bogus eid %d: off: %" PRIu32
-                                         ", len: %" PRIu32 "\n",
-                                         eid, off, len));
-                               return false;
-                       }
-
-                       /*
-                        * Fixup the resource fork entry by limiting
-                        * the size to entryoffset - filesize.
-                        */
-                       len = filesize - off;
-                       DEBUG(1, ("Limiting ADEID_RFORK: off: %" PRIu32
-                                 ", len: %" PRIu32 "\n", off, len));
-               }
-
-               ad->ad_eid[eid].ade_off = off;
-               ad->ad_eid[eid].ade_len = len;
-       }
-
-       ok = ad_unpack_xattrs(ad);
-       if (!ok) {
-               return false;
-       }
-
-       return true;
-}
-
-static bool ad_convert_move_reso(vfs_handle_struct *handle,
-                                struct adouble *ad,
-                                const struct smb_filename *smb_fname)
-{
-       char *buf = NULL;
-       size_t rforklen;
-       size_t rforkoff;
-       ssize_t n;
-       int ret;
-       bool ok;
-
-       rforklen = ad_getentrylen(ad, ADEID_RFORK);
-       if (rforklen == 0) {
-               return true;
-       }
-
-       buf = talloc_size(ad, rforklen);
-       if (buf == NULL) {
-               /*
-                * This allocates a buffer for reading the resource fork data in
-                * one big swoop. Resource forks won't be larger then, say, 64
-                * MB, I swear, so just doing the allocation with the talloc
-                * limit as safeguard seems safe.
-                */
-               DBG_ERR("Failed to allocate %zu bytes for rfork\n",
-                       rforklen);
-               return false;
-       }
-
-       rforkoff = ad_getentryoff(ad, ADEID_RFORK);
-
-       n = SMB_VFS_PREAD(ad->ad_fsp, buf, rforklen, rforkoff);
-       if (n != rforklen) {
-               DBG_ERR("Reading %zu bytes from rfork [%s] failed: %s\n",
-                       rforklen, fsp_str_dbg(ad->ad_fsp), strerror(errno));
-               return false;
-       }
-
-       rforkoff = ADEDOFF_RFORK_DOT_UND;
-
-       n = SMB_VFS_PWRITE(ad->ad_fsp, buf, rforklen, rforkoff);
-       if (n != rforklen) {
-               DBG_ERR("Writing %zu bytes to rfork [%s] failed: %s\n",
-                       rforklen, fsp_str_dbg(ad->ad_fsp), strerror(errno));
-               return false;
-       }
-
-       ad_setentryoff(ad, ADEID_RFORK, ADEDOFF_RFORK_DOT_UND);
-       ok = ad_pack(ad);
-       if (!ok) {
-               DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name);
-               return false;
-       }
-
-       ret = ad_fset(handle, ad, ad->ad_fsp);
-       if (ret != 0) {
-               DBG_ERR("ad_fset on [%s] failed\n", fsp_str_dbg(ad->ad_fsp));
-               return false;
-       }
-
-       return true;
-}
-
-static bool ad_convert_xattr(vfs_handle_struct *handle,
-                            struct adouble *ad,
-                            const struct smb_filename *smb_fname,
-                            const char *catia_mappings,
-                            bool *converted_xattr)
-{
-       static struct char_mappings **string_replace_cmaps = NULL;
-       uint16_t i;
-       int saved_errno = 0;
-       NTSTATUS status;
-       int rc;
-       bool ok;
-
-       *converted_xattr = false;
-
-       if (ad_getentrylen(ad, ADEID_FINDERI) == ADEDLEN_FINDERI) {
-               return true;
-       }
-
-       if (string_replace_cmaps == NULL) {
-               const char **mappings = NULL;
-
-               mappings = str_list_make_v3_const(
-                       talloc_tos(), catia_mappings, NULL);
-               if (mappings == NULL) {
-                       return false;
-               }
-               string_replace_cmaps = string_replace_init_map(mappings);
-               TALLOC_FREE(mappings);
-       }
-
-       for (i = 0; i < ad->adx_header.adx_num_attrs; i++) {
-               struct ad_xattr_entry *e = &ad->adx_entries[i];
-               char *mapped_name = NULL;
-               char *tmp = NULL;
-               struct smb_filename *stream_name = NULL;
-               files_struct *fsp = NULL;
-               ssize_t nwritten;
-
-               status = string_replace_allocate(handle->conn,
-                                                e->adx_name,
-                                                string_replace_cmaps,
-                                                talloc_tos(),
-                                                &mapped_name,
-                                                vfs_translate_to_windows);
-               if (!NT_STATUS_IS_OK(status) &&
-                   !NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))
-               {
-                       DBG_ERR("string_replace_allocate failed\n");
-                       ok = false;
-                       goto fail;
-               }
-
-               tmp = mapped_name;
-               mapped_name = talloc_asprintf(talloc_tos(), ":%s", tmp);
-               TALLOC_FREE(tmp);
-               if (mapped_name == NULL) {
-                       ok = false;
-                       goto fail;
-               }
-
-               stream_name = synthetic_smb_fname(talloc_tos(),
-                                                 smb_fname->base_name,
-                                                 mapped_name,
-                                                 NULL,
-                                                 smb_fname->flags);
-               TALLOC_FREE(mapped_name);
-               if (stream_name == NULL) {
-                       DBG_ERR("synthetic_smb_fname failed\n");
-                       ok = false;
-                       goto fail;
-               }
-
-               DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
-
-               status = SMB_VFS_CREATE_FILE(
-                       handle->conn,                   /* conn */
-                       NULL,                           /* req */
-                       0,                              /* root_dir_fid */
-                       stream_name,                    /* fname */
-                       FILE_GENERIC_WRITE,             /* access_mask */
-                       FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
-                       FILE_OPEN_IF,                   /* create_disposition */
-                       0,                              /* create_options */
-                       0,                              /* file_attributes */
-                       INTERNAL_OPEN_ONLY,             /* oplock_request */
-                       NULL,                           /* lease */
-                       0,                              /* allocation_size */
-                       0,                              /* private_flags */
-                       NULL,                           /* sd */
-                       NULL,                           /* ea_list */
-                       &fsp,                           /* result */
-                       NULL,                           /* psbuf */
-                       NULL, NULL);                    /* create context */
-               TALLOC_FREE(stream_name);
-               if (!NT_STATUS_IS_OK(status)) {
-                       DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
-                       ok = false;
-                       goto fail;
-               }
-
-               nwritten = SMB_VFS_PWRITE(fsp,
-                                         ad->ad_data + e->adx_offset,
-                                         e->adx_length,
-                                         0);
-               if (nwritten == -1) {
-                       DBG_ERR("SMB_VFS_PWRITE failed\n");
-                       saved_errno = errno;
-                       close_file(NULL, fsp, ERROR_CLOSE);
-                       errno = saved_errno;
-                       ok = false;
-                       goto fail;
-               }
-
-               status = close_file(NULL, fsp, NORMAL_CLOSE);
-               if (!NT_STATUS_IS_OK(status)) {
-                       ok = false;
-                       goto fail;
-               }
-               fsp = NULL;
-       }
-
-       ad_setentrylen(ad, ADEID_FINDERI, ADEDLEN_FINDERI);
-
-       ok = ad_pack(ad);
-       if (!ok) {
-               DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name);
-               goto fail;
-       }
-
-       rc = ad_fset(handle, ad, ad->ad_fsp);
-       if (rc != 0) {
-               DBG_ERR("ad_fset on [%s] failed: %s\n",
-                       fsp_str_dbg(ad->ad_fsp), strerror(errno));
-               ok = false;
-               goto fail;
-       }
-
-       ok = ad_convert_move_reso(handle, ad, smb_fname);
-       if (!ok) {
-               goto fail;
-       }
-
-       *converted_xattr = true;
-       ok = true;
-
-fail:
-       return ok;
-}
-
-static bool ad_convert_finderinfo(vfs_handle_struct *handle,
-                                 struct adouble *ad,
-                                 const struct smb_filename *smb_fname)
-{
-       char *p_ad = NULL;
-       AfpInfo *ai = NULL;
-       DATA_BLOB aiblob;
-       struct smb_filename *stream_name = NULL;
-       files_struct *fsp = NULL;
-       size_t size;
-       ssize_t nwritten;
-       NTSTATUS status;
-       int saved_errno = 0;
-       int cmp;
-
-       cmp = memcmp(ad->ad_filler, AD_FILLER_TAG_OSX, ADEDLEN_FILLER);
-       if (cmp != 0) {
-               return true;
-       }
-
-       p_ad = ad_get_entry(ad, ADEID_FINDERI);
-       if (p_ad == NULL) {
-               return false;
-       }
-
-       ai = afpinfo_new(talloc_tos());
-       if (ai == NULL) {
-               return false;
-       }
-
-       memcpy(ai->afpi_FinderInfo, p_ad, ADEDLEN_FINDERI);
-
-       aiblob = data_blob_talloc(talloc_tos(), NULL, AFP_INFO_SIZE);
-       if (aiblob.data == NULL) {
-               TALLOC_FREE(ai);
-               return false;
-       }
-
-       size = afpinfo_pack(ai, (char *)aiblob.data);
-       TALLOC_FREE(ai);
-       if (size != AFP_INFO_SIZE) {
-               return false;
-       }
-
-       stream_name = synthetic_smb_fname(talloc_tos(),
-                                         smb_fname->base_name,
-                                         AFPINFO_STREAM,
-                                         NULL,
-                                         smb_fname->flags);
-       if (stream_name == NULL) {
-               data_blob_free(&aiblob);
-               DBG_ERR("synthetic_smb_fname failed\n");
-               return false;
-       }
-
-       DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
-
-       status = SMB_VFS_CREATE_FILE(
-               handle->conn,                   /* conn */
-               NULL,                           /* req */
-               0,                              /* root_dir_fid */
-               stream_name,                    /* fname */
-               FILE_GENERIC_WRITE,             /* access_mask */
-               FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
-               FILE_OPEN_IF,                   /* create_disposition */
-               0,                              /* create_options */
-               0,                              /* file_attributes */
-               INTERNAL_OPEN_ONLY,             /* oplock_request */
-               NULL,                           /* lease */
-               0,                              /* allocation_size */
-               0,                              /* private_flags */
-               NULL,                           /* sd */
-               NULL,                           /* ea_list */
-               &fsp,                           /* result */
-               NULL,                           /* psbuf */
-               NULL, NULL);                    /* create context */
-       TALLOC_FREE(stream_name);
-       if (!NT_STATUS_IS_OK(status)) {
-               DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
-               return false;
-       }
-
-       nwritten = SMB_VFS_PWRITE(fsp,
-                                 aiblob.data,
-                                 aiblob.length,
-                                 0);
-       if (nwritten == -1) {
-               DBG_ERR("SMB_VFS_PWRITE failed\n");
-               saved_errno = errno;
-               close_file(NULL, fsp, ERROR_CLOSE);
-               errno = saved_errno;
-               return false;
-       }
-
-       status = close_file(NULL, fsp, NORMAL_CLOSE);
-       if (!NT_STATUS_IS_OK(status)) {
-               return false;
-       }
-       fsp = NULL;
-
-       return true;
-}
-
-static bool ad_convert_truncate(vfs_handle_struct *handle,
-                               struct adouble *ad,
-                               const struct smb_filename *smb_fname)
-{
-       int rc;
-       off_t newlen;
-
-       newlen = ADEDOFF_RFORK_DOT_UND + ad_getentrylen(ad, ADEID_RFORK);
-
-       rc = SMB_VFS_FTRUNCATE(ad->ad_fsp, newlen);
-       if (rc != 0) {
-               return false;
-       }
-
-       return true;
-}
-
-static bool ad_convert_blank_rfork(vfs_handle_struct *handle,
-                                  struct adouble *ad,
-                                  uint32_t flags,
-                                  bool *blank)
-{
-       size_t rforklen = sizeof(empty_resourcefork);
-       char buf[rforklen];
-       ssize_t nread;
-       int cmp;
-       int rc;
-       bool ok;
-
-       *blank = false;
-
-       if (!(flags & AD_CONV_WIPE_BLANK)) {
-               return true;
-       }
-
-       if (ad_getentrylen(ad, ADEID_RFORK) != rforklen) {
-               return true;
-       }
-
-       nread = SMB_VFS_PREAD(ad->ad_fsp, buf, rforklen, ADEDOFF_RFORK_DOT_UND);
-       if (nread != rforklen) {
-               DBG_ERR("Reading %zu bytes from rfork [%s] failed: %s\n",
-                       rforklen, fsp_str_dbg(ad->ad_fsp), strerror(errno));
-               return false;
-       }
-
-       cmp = memcmp(buf, empty_resourcefork, rforklen);
-       if (cmp != 0) {
-               return true;
-       }
-
-       ad_setentrylen(ad, ADEID_RFORK, 0);
-       ok = ad_pack(ad);
-       if (!ok) {
-               return false;
-       }
-
-       rc = ad_fset(handle, ad, ad->ad_fsp);
-       if (rc != 0) {
-               DBG_ERR("ad_fset on [%s] failed\n", fsp_str_dbg(ad->ad_fsp));
-               return false;
-       }
-
-       *blank = true;
-       return true;
-}
-
-static bool ad_convert_delete_adfile(vfs_handle_struct *handle,
-                                    struct adouble *ad,
-                                    const struct smb_filename *smb_fname,
-                                    uint32_t flags)
-{
-       struct smb_filename *ad_name = NULL;
-       int rc;
-
-       if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
-               return true;
-       }
-
-       if (!(flags & AD_CONV_DELETE)) {
-               return true;
-       }
-
-       rc = adouble_path(talloc_tos(), smb_fname, &ad_name);
-       if (rc != 0) {
-               return false;
-       }
-
-       rc = SMB_VFS_NEXT_UNLINK(handle, ad_name);
-       if (rc != 0) {
-               DBG_ERR("Unlinking [%s] failed: %s\n",
-                       smb_fname_str_dbg(ad_name), strerror(errno));
-               TALLOC_FREE(ad_name);
-               return false;
-       }
-
-       DBG_WARNING("Unlinked [%s] after conversion\n", smb_fname_str_dbg(ad_name));
-       TALLOC_FREE(ad_name);
-
-       return true;
-}
-
-/**
- * Convert from Apple's ._ file to Netatalk
- *
- * Apple's AppleDouble may contain a FinderInfo entry longer then 32
- * bytes containing packed xattrs.
- *
- * @return -1 in case an error occurred, 0 if no conversion was done, 1
- * otherwise
- **/
-static int ad_convert(struct vfs_handle_struct *handle,
-                     const struct smb_filename *smb_fname,
-                     const char *catia_mappings,
-                     uint32_t flags)
-{
-       struct adouble *ad = NULL;
-       bool ok;
-       bool converted_xattr = false;
-       bool blank;
-       int ret;
-
-       ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_RSRC);
-       if (ad == NULL) {
-               return 0;
-       }
-
-       ok = ad_convert_xattr(handle,
-                             ad,
-                             smb_fname,
-                             catia_mappings,
-                             &converted_xattr);
-       if (!ok) {
-               ret = -1;
-               goto done;
-       }
-
-       ok = ad_convert_blank_rfork(handle, ad, flags, &blank);
-       if (!ok) {
-               ret = -1;
-               goto done;
-       }
-
-       if (converted_xattr || blank) {
-               ok = ad_convert_truncate(handle, ad, smb_fname);
-               if (!ok) {
-                       ret = -1;
-                       goto done;
-               }
-       }
-
-       ok = ad_convert_finderinfo(handle, ad, smb_fname);
-       if (!ok) {
-               DBG_ERR("Failed to convert [%s]\n",
-                       smb_fname_str_dbg(smb_fname));
-               ret = -1;
-               goto done;
-       }
-
-       ok = ad_convert_delete_adfile(handle, ad, smb_fname, flags);
-       if (!ok) {
-               ret = -1;
-               goto done;
-       }
-
-       ret = 0;
-done:
-       TALLOC_FREE(ad);
-       return ret;
-}
-
-/**
- * Read and parse Netatalk AppleDouble metadata xattr
- **/
-static ssize_t ad_read_meta(vfs_handle_struct *handle,
-                           struct adouble *ad,
-                           const struct smb_filename *smb_fname)
-{
-       int      rc = 0;
-       ssize_t  ealen;
-       bool     ok;
-
-       DEBUG(10, ("reading meta xattr for %s\n", smb_fname->base_name));
-
-       ealen = SMB_VFS_GETXATTR(handle->conn, smb_fname,
-                                AFPINFO_EA_NETATALK, ad->ad_data,
-                                AD_DATASZ_XATTR);
-       if (ealen == -1) {
-               switch (errno) {
-               case ENOATTR:
-               case ENOENT:
-                       if (errno == ENOATTR) {
-                               errno = ENOENT;
-                       }
-                       rc = -1;
-                       goto exit;
-               default:
-                       DEBUG(2, ("error reading meta xattr: %s\n",
-                                 strerror(errno)));
-                       rc = -1;
-                       goto exit;
-               }
-       }
-       if (ealen != AD_DATASZ_XATTR) {
-               DEBUG(2, ("bad size %zd\n", ealen));
-               errno = EINVAL;
-               rc = -1;
-               goto exit;
-       }
-
-       /* Now parse entries */
-       ok = ad_unpack(ad, ADEID_NUM_XATTR, AD_DATASZ_XATTR);
-       if (!ok) {
-               DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
-               errno = EINVAL;
-               rc = -1;
-               goto exit;
-       }
-
-       if (!ad_getentryoff(ad, ADEID_FINDERI)
-           || !ad_getentryoff(ad, ADEID_COMMENT)
-           || !ad_getentryoff(ad, ADEID_FILEDATESI)
-           || !ad_getentryoff(ad, ADEID_AFPFILEI)
-           || !ad_getentryoff(ad, ADEID_PRIVDEV)
-           || !ad_getentryoff(ad, ADEID_PRIVINO)
-           || !ad_getentryoff(ad, ADEID_PRIVSYN)
-           || !ad_getentryoff(ad, ADEID_PRIVID)) {
-               DEBUG(2, ("invalid AppleDouble metadata xattr\n"));
-               errno = EINVAL;
-               rc = -1;
-               goto exit;
-       }
-
-exit:
-       DEBUG(10, ("reading meta xattr for %s, rc: %d\n",
-               smb_fname->base_name, rc));
-
-       if (rc != 0) {
-               ealen = -1;
-               if (errno == EINVAL) {
-                       become_root();
-                       (void)SMB_VFS_REMOVEXATTR(handle->conn,
-                                                 smb_fname,
-                                                 AFPINFO_EA_NETATALK);
-                       unbecome_root();
-                       errno = ENOENT;
-               }
-       }
-       return ealen;
-}
-
-static int ad_open_rsrc(vfs_handle_struct *handle,
-                       const struct smb_filename *smb_fname,
-                       int flags,
-                       mode_t mode,
-                       files_struct **_fsp)
-{
-       int ret;
-       struct smb_filename *adp_smb_fname = NULL;
-       files_struct *fsp = NULL;
-       uint32_t access_mask;
-       uint32_t share_access;
-       uint32_t create_disposition;
-       NTSTATUS status;
-
-       ret = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
-       if (ret != 0) {
-               return -1;
-       }
-
-       ret = SMB_VFS_STAT(handle->conn, adp_smb_fname);
-       if (ret != 0) {
-               TALLOC_FREE(adp_smb_fname);
-               return -1;
-       }
-
-       access_mask = FILE_GENERIC_READ;
-       share_access = FILE_SHARE_READ | FILE_SHARE_WRITE;
-       create_disposition = FILE_OPEN;
-
-       if (flags & O_RDWR) {
-               access_mask |= FILE_GENERIC_WRITE;
-               share_access &= ~FILE_SHARE_WRITE;
-       }
-
-       status = SMB_VFS_CREATE_FILE(
-               handle->conn,                   /* conn */
-               NULL,                           /* req */
-               0,                              /* root_dir_fid */
-               adp_smb_fname,
-               access_mask,
-               share_access,
-               create_disposition,
-               0,                              /* create_options */
-               0,                              /* file_attributes */
-               INTERNAL_OPEN_ONLY,             /* oplock_request */
-               NULL,                           /* lease */
-               0,                              /* allocation_size */
-               0,                              /* private_flags */
-               NULL,                           /* sd */
-               NULL,                           /* ea_list */
-               &fsp,
-               NULL,                           /* psbuf */
-               NULL, NULL);                    /* create context */
-       TALLOC_FREE(adp_smb_fname);
-       if (!NT_STATUS_IS_OK(status)) {
-               DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
-               return -1;
-       }
-
-       *_fsp = fsp;
-       return 0;
-}
-
-/*
- * Here's the deal: for ADOUBLE_META we can do without an fd as we can issue
- * path based xattr calls. For ADOUBLE_RSRC however we need a full-fledged fd
- * for file IO on the ._ file.
- */
-static int ad_open(vfs_handle_struct *handle,
-                  struct adouble *ad,
-                  files_struct *fsp,
-                  const struct smb_filename *smb_fname,
-                  int flags,
-                  mode_t mode)
-{
-       int ret;
-
-       DBG_DEBUG("Path [%s] type [%s]\n", smb_fname->base_name,
-                 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
-
-       if (ad->ad_type == ADOUBLE_META) {
-               return 0;
-       }
-
-       if (fsp != NULL) {
-               ad->ad_fsp = fsp;
-               ad->ad_opened = false;
-               return 0;
-       }
-
-       ret = ad_open_rsrc(handle, smb_fname, flags, mode, &ad->ad_fsp);
-       if (ret != 0) {
-               return -1;
-       }
-       ad->ad_opened = true;
-
-       DBG_DEBUG("Path [%s] type [%s]\n",
-                 smb_fname->base_name,
-                 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc");
-
-       return 0;
-}
-
-static ssize_t ad_read_rsrc_adouble(vfs_handle_struct *handle,
-                                   struct adouble *ad,
-                                   const struct smb_filename *smb_fname)
-{
-       char *p_ad = NULL;
-       size_t size;
-       ssize_t len;
-       int ret;
-       bool ok;
-
-       ret = SMB_VFS_NEXT_FSTAT(handle, ad->ad_fsp, &ad->ad_fsp->fsp_name->st);
-       if (ret != 0) {
-               DBG_ERR("fstat [%s] failed: %s\n",
-                       fsp_str_dbg(ad->ad_fsp), strerror(errno));
-               return -1;
-       }
-
-       /*
-        * AppleDouble file header content and size, two cases:
-        *
-        * - without xattrs it is exactly AD_DATASZ_DOT_UND (82) bytes large
-        * - with embedded xattrs it can be larger, up to AD_XATTR_MAX_HDR_SIZE
-        *
-        * Read as much as we can up to AD_XATTR_MAX_HDR_SIZE.
-        */
-       size = ad->ad_fsp->fsp_name->st.st_ex_size;
-       if (size > talloc_array_length(ad->ad_data)) {
-               if (size > AD_XATTR_MAX_HDR_SIZE) {
-                       size = AD_XATTR_MAX_HDR_SIZE;
-               }
-               p_ad = talloc_realloc(ad, ad->ad_data, char, size);
-               if (p_ad == NULL) {
-                       return -1;
-               }
-               ad->ad_data = p_ad;
-       }
-
-       len = SMB_VFS_NEXT_PREAD(handle, ad->ad_fsp, ad->ad_data, talloc_array_length(ad->ad_data), 0);
-       if (len != talloc_array_length(ad->ad_data)) {
-               DBG_NOTICE("%s %s: bad size: %zd\n",
-                          smb_fname->base_name, strerror(errno), len);
-               return -1;
-       }
-
-       /* Now parse entries */
-       ok = ad_unpack(ad, ADEID_NUM_DOT_UND, size);
-       if (!ok) {
-               DBG_ERR("invalid AppleDouble resource %s\n",
-                       smb_fname->base_name);
-               errno = EINVAL;
-               return -1;
-       }
-
-       if ((ad_getentryoff(ad, ADEID_FINDERI) != ADEDOFF_FINDERI_DOT_UND)
-           || (ad_getentrylen(ad, ADEID_FINDERI) < ADEDLEN_FINDERI)
-           || (ad_getentryoff(ad, ADEID_RFORK) < ADEDOFF_RFORK_DOT_UND)) {
-               DBG_ERR("invalid AppleDouble resource %s\n",
-                       smb_fname->base_name);
-               errno = EINVAL;
-               return -1;
-       }
-
-       return len;
-}
-
-/**
- * Read and parse resource fork, either ._ AppleDouble file or xattr
- **/
-static ssize_t ad_read_rsrc(vfs_handle_struct *handle,
-                           struct adouble *ad,
-                           const struct smb_filename *smb_fname)
-{
-       return ad_read_rsrc_adouble(handle, ad, smb_fname);
-}
-
-/**
- * Read and unpack an AppleDouble metadata xattr or resource
- **/
-static ssize_t ad_read(vfs_handle_struct *handle,
-                      struct adouble *ad,
-                      const struct smb_filename *smb_fname)
-{
-       switch (ad->ad_type) {
-       case ADOUBLE_META:
-               return ad_read_meta(handle, ad, smb_fname);
-       case ADOUBLE_RSRC:
-               return ad_read_rsrc(handle, ad, smb_fname);
-       default:
-               return -1;
-       }
-}
-
-static int adouble_destructor(struct adouble *ad)
-{
-       NTSTATUS status;
-
-       if (!ad->ad_opened) {
-               return 0;
-       }
-
-       SMB_ASSERT(ad->ad_fsp != NULL);
-
-       status = close_file(NULL, ad->ad_fsp, NORMAL_CLOSE);
-       if (!NT_STATUS_IS_OK(status)) {
-               DBG_ERR("Closing [%s] failed: %s\n",
-                       fsp_str_dbg(ad->ad_fsp), nt_errstr(status));
-       }
-
-       return 0;
-}
-
-/**
- * Allocate a struct adouble without initialiing it
- *
- * The struct is either hang of the fsp extension context or if fsp is
- * NULL from ctx.
- *
- * @param[in] ctx        talloc context
- * @param[in] handle     vfs handle
- * @param[in] type       type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
- *
- * @return               adouble handle
- **/
-static struct adouble *ad_alloc(TALLOC_CTX *ctx,
-                               adouble_type_t type)
-{
-       int rc = 0;
-       size_t adsize = 0;
-       struct adouble *ad;
-
-       switch (type) {
-       case ADOUBLE_META:
-               adsize = AD_DATASZ_XATTR;
-               break;
-       case ADOUBLE_RSRC:
-               adsize = AD_DATASZ_DOT_UND;
-               break;
-       default:
-               return NULL;
-       }
-
-       ad = talloc_zero(ctx, struct adouble);
-       if (ad == NULL) {
-               rc = -1;
-               goto exit;
-       }
-
-       if (adsize) {
-               ad->ad_data = talloc_zero_array(ad, char, adsize);
-               if (ad->ad_data == NULL) {
-                       rc = -1;
-                       goto exit;
-               }
-       }
-
-       ad->ad_type = type;
-       ad->ad_magic = AD_MAGIC;
-       ad->ad_version = AD_VERSION;
-
-       talloc_set_destructor(ad, adouble_destructor);
-
-exit:
-       if (rc != 0) {
-               TALLOC_FREE(ad);
-       }
-       return ad;
-}
-
-/**
- * Allocate and initialize a new struct adouble
- *
- * @param[in] ctx        talloc context
- * @param[in] type       type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
- *
- * @return               adouble handle, initialized
- **/
-static struct adouble *ad_init(TALLOC_CTX *ctx,
-                              adouble_type_t type)
-{
-       int rc = 0;
-       const struct ad_entry_order  *eid;
-       struct adouble *ad = NULL;
-       time_t t = time(NULL);
-
-       switch (type) {
-       case ADOUBLE_META:
-               eid = entry_order_meta_xattr;
-               break;
-       case ADOUBLE_RSRC:
-               eid = entry_order_dot_und;
-               break;
-       default:
-               return NULL;
-       }
-
-       ad = ad_alloc(ctx, type);
-       if (ad == NULL) {
-               return NULL;
-       }
-
-       while (eid->id) {
-               ad->ad_eid[eid->id].ade_off = eid->offset;
-               ad->ad_eid[eid->id].ade_len = eid->len;
-               eid++;
-       }
-
-       /* put something sane in the date fields */
-       ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX, t);
-       ad_setdate(ad, AD_DATE_MODIFY | AD_DATE_UNIX, t);
-       ad_setdate(ad, AD_DATE_ACCESS | AD_DATE_UNIX, t);
-       ad_setdate(ad, AD_DATE_BACKUP, htonl(AD_DATE_START));
-
-       if (rc != 0) {
-               TALLOC_FREE(ad);
-       }
-       return ad;
-}
-
-static struct adouble *ad_get_internal(TALLOC_CTX *ctx,
-                                      vfs_handle_struct *handle,
-                                      files_struct *fsp,
-                                      const struct smb_filename *smb_fname,
-                                      adouble_type_t type)
-{
-       int rc = 0;
-       ssize_t len;
-       struct adouble *ad = NULL;
-       int mode;
-
-       if (fsp != NULL) {
-               smb_fname = fsp->base_fsp->fsp_name;
-       }
-
-       DEBUG(10, ("ad_get(%s) called for %s\n",
-                  type == ADOUBLE_META ? "meta" : "rsrc",
-                  smb_fname->base_name));
-
-       ad = ad_alloc(ctx, type);
-       if (ad == NULL) {
-               rc = -1;
-               goto exit;
-       }
-
-       /* Try rw first so we can use the fd in ad_convert() */
-       mode = O_RDWR;
-
-       rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
-       if (rc == -1 && ((errno == EROFS) || (errno == EACCES))) {
-               mode = O_RDONLY;
-               rc = ad_open(handle, ad, fsp, smb_fname, mode, 0);
-       }
-       if (rc == -1) {
-               DBG_DEBUG("ad_open [%s] error [%s]\n",
-                         smb_fname->base_name, strerror(errno));
-               goto exit;
-
-       }
-
-       len = ad_read(handle, ad, smb_fname);
-       if (len == -1) {
-               DEBUG(10, ("error reading AppleDouble for %s\n",
-                       smb_fname->base_name));
-               rc = -1;
-               goto exit;
-       }
-
-exit:
-       DEBUG(10, ("ad_get(%s) for %s returning %d\n",
-                 type == ADOUBLE_META ? "meta" : "rsrc",
-                 smb_fname->base_name, rc));
-
-       if (rc != 0) {
-               TALLOC_FREE(ad);
-       }
-       return ad;
-}
-
-/**
- * Return AppleDouble data for a file
- *
- * @param[in] ctx      talloc context
- * @param[in] handle   vfs handle
- * @param[in] smb_fname pathname to file or directory
- * @param[in] type     type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
- *
- * @return             talloced struct adouble or NULL on error
- **/
-static struct adouble *ad_get(TALLOC_CTX *ctx,
-                             vfs_handle_struct *handle,
-                             const struct smb_filename *smb_fname,
-                             adouble_type_t type)
-{
-       return ad_get_internal(ctx, handle, NULL, smb_fname, type);
-}
-
-/**
- * Return AppleDouble data for a file
- *
- * @param[in] ctx      talloc context
- * @param[in] handle   vfs handle
- * @param[in] fsp      fsp to use for IO
- * @param[in] type     type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
- *
- * @return             talloced struct adouble or NULL on error
- **/
-static struct adouble *ad_fget(TALLOC_CTX *ctx, vfs_handle_struct *handle,
-                              files_struct *fsp, adouble_type_t type)
-{
-       return ad_get_internal(ctx, handle, fsp, NULL, type);
-}
-
-/**
- * Set AppleDouble metadata on a file or directory
- *
- * @param[in] ad      adouble handle
- *
- * @param[in] smb_fname    pathname to file or directory
- *
- * @return            status code, 0 means success
- **/
-static int ad_set(vfs_handle_struct *handle,
-                 struct adouble *ad,
-                 const struct smb_filename *smb_fname)
-{
-       bool ok;
-       int ret;
-
-       DBG_DEBUG("Path [%s]\n", smb_fname->base_name);
-
-       if (ad->ad_type != ADOUBLE_META) {
-               DBG_ERR("ad_set on [%s] used with ADOUBLE_RSRC\n",
-                       smb_fname->base_name);
-               return -1;
-       }
-
-       ok = ad_pack(ad);
-       if (!ok) {
-               return -1;
-       }
+enum apple_fork {APPLE_FORK_DATA, APPLE_FORK_RSRC};
 
-       ret = SMB_VFS_SETXATTR(handle->conn,
-                              smb_fname,
-                              AFPINFO_EA_NETATALK,
-                              ad->ad_data,
-                              AD_DATASZ_XATTR, 0);
+enum fruit_rsrc {FRUIT_RSRC_STREAM, FRUIT_RSRC_ADFILE, FRUIT_RSRC_XATTR};
+enum fruit_meta {FRUIT_META_STREAM, FRUIT_META_NETATALK};
+enum fruit_locking {FRUIT_LOCKING_NETATALK, FRUIT_LOCKING_NONE};
+enum fruit_encoding {FRUIT_ENC_NATIVE, FRUIT_ENC_PRIVATE};
 
-       DBG_DEBUG("Path [%s] ret [%d]\n", smb_fname->base_name, ret);
+struct fruit_config_data {
+       enum fruit_rsrc rsrc;
+       enum fruit_meta meta;
+       enum fruit_locking locking;
+       enum fruit_encoding encoding;
+       bool use_aapl;          /* config from smb.conf */
+       bool use_copyfile;
+       bool readdir_attr_enabled;
+       bool unix_info_enabled;
+       bool copyfile_enabled;
+       bool veto_appledouble;
+       bool posix_rename;
+       bool aapl_zero_file_id;
+       const char *model;
+       bool time_machine;
+       off_t time_machine_max_size;
+       bool wipe_intentionally_left_blank_rfork;
+       bool delete_empty_adfiles;
 
-       return ret;
-}
+       /*
+        * Additional options, all enabled by default,
+        * possibly useful for analyzing performance. The associated
+        * operations with each of them may be expensive, so having
+        * the chance to disable them individually gives a chance
+        * tweaking the setup for the particular usecase.
+        */
+       bool readdir_attr_rsize;
+       bool readdir_attr_finder_info;
+       bool readdir_attr_max_access;
+};
 
-/**
- * Set AppleDouble metadata on a file or directory
- *
- * @param[in] ad      adouble handle
- * @param[in] fsp     file handle
- *
- * @return            status code, 0 means success
- **/
-static int ad_fset(struct vfs_handle_struct *handle,
-                  struct adouble *ad,
-                  files_struct *fsp)
-{
-       int rc = -1;
-       ssize_t len;
-       bool ok;
+static const struct enum_list fruit_rsrc[] = {
+       {FRUIT_RSRC_STREAM, "stream"}, /* pass on to vfs_streams_xattr */
+       {FRUIT_RSRC_ADFILE, "file"}, /* ._ AppleDouble file */
+       {FRUIT_RSRC_XATTR, "xattr"}, /* Netatalk compatible xattr (ZFS only) */
+       { -1, NULL}
+};
 
-       DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
+static const struct enum_list fruit_meta[] = {
+       {FRUIT_META_STREAM, "stream"}, /* pass on to vfs_streams_xattr */
+       {FRUIT_META_NETATALK, "netatalk"}, /* Netatalk compatible xattr */
+       { -1, NULL}
+};
 
-       if ((fsp == NULL)
-           || (fsp->fh == NULL)
-           || (fsp->fh->fd == -1))
-       {
-               smb_panic("bad fsp");
-       }
+static const struct enum_list fruit_locking[] = {
+       {FRUIT_LOCKING_NETATALK, "netatalk"}, /* synchronize locks with Netatalk */
+       {FRUIT_LOCKING_NONE, "none"},
+       { -1, NULL}
+};
 
-       ok = ad_pack(ad);
-       if (!ok) {
-               return -1;
-       }
+static const struct enum_list fruit_encoding[] = {
+       {FRUIT_ENC_NATIVE, "native"}, /* map unicode private chars to ASCII */
+       {FRUIT_ENC_PRIVATE, "private"}, /* keep unicode private chars */
+       { -1, NULL}
+};
 
-       switch (ad->ad_type) {
-       case ADOUBLE_META:
-               rc = SMB_VFS_NEXT_SETXATTR(handle,
-                                          fsp->fsp_name,
-                                          AFPINFO_EA_NETATALK,
-                                          ad->ad_data,
-                                          AD_DATASZ_XATTR, 0);
-               break;
+static const char *fruit_catia_maps =
+       "0x01:0xf001,0x02:0xf002,0x03:0xf003,0x04:0xf004,"
+       "0x05:0xf005,0x06:0xf006,0x07:0xf007,0x08:0xf008,"
+       "0x09:0xf009,0x0a:0xf00a,0x0b:0xf00b,0x0c:0xf00c,"
+       "0x0d:0xf00d,0x0e:0xf00e,0x0f:0xf00f,0x10:0xf010,"
+       "0x11:0xf011,0x12:0xf012,0x13:0xf013,0x14:0xf014,"
+       "0x15:0xf015,0x16:0xf016,0x17:0xf017,0x18:0xf018,"
+       "0x19:0xf019,0x1a:0xf01a,0x1b:0xf01b,0x1c:0xf01c,"
+       "0x1d:0xf01d,0x1e:0xf01e,0x1f:0xf01f,"
+       "0x22:0xf020,0x2a:0xf021,0x3a:0xf022,0x3c:0xf023,"
+       "0x3e:0xf024,0x3f:0xf025,0x5c:0xf026,0x7c:0xf027,"
+       "0x0d:0xf00d";
 
-       case ADOUBLE_RSRC:
-               len = SMB_VFS_NEXT_PWRITE(handle,
-                                         fsp,
-                                         ad->ad_data,
-                                         AD_DATASZ_DOT_UND,
-                                         0);
-               if (len != AD_DATASZ_DOT_UND) {
-                       DBG_ERR("short write on %s: %zd", fsp_str_dbg(fsp), len);
-                       return -1;
-               }
-               rc = 0;
-               break;
+struct fio {
+       /* tcon config handle */
+       struct fruit_config_data *config;
 
-       default:
-               return -1;
-       }
+       /* Denote stream type, meta or rsrc */
+       adouble_type_t type;
 
-       DBG_DEBUG("Path [%s] rc [%d]\n", fsp_str_dbg(fsp), rc);
+       /* Whether the create created the stream */
+       bool created;
 
-       return rc;
-}
+       /*
+        * AFP_AfpInfo stream created, but not written yet, thus still a fake
+        * pipe fd. This is set to true in fruit_open_meta if there was no
+        * exisiting stream but the caller requested O_CREAT. It is later set to
+        * false when we get a write on the stream that then does open and
+        * create the stream.
+        */
+       bool fake_fd;
+       int flags;
+       int mode;
+};
 
 /*****************************************************************************
  * Helper functions
@@ -2127,27 +243,6 @@ static bool is_apple_stream(const struct smb_filename *smb_fname)
        return false;
 }
 
-static bool is_adouble_file(const char *path)
-{
-       const char *p = NULL;
-       int match;
-
-       p = strrchr(path, '/');
-       if (p == NULL) {
-               p = path;
-       } else {
-               p++;
-       }
-
-       match = strncmp(p,
-                       ADOUBLE_NAME_PREFIX,
-                       strlen(ADOUBLE_NAME_PREFIX));
-       if (match != 0) {
-               return false;
-       }
-       return true;
-}
-
 /**
  * Initialize config struct from our smb.conf config parameters
  **/
@@ -2277,108 +372,6 @@ static int init_fruit_config(vfs_handle_struct *handle)
        return 0;
 }
 
-/**
- * Prepend "._" to a basename
- * Return a new struct smb_filename with stream_name == NULL.
- **/
-static int adouble_path(TALLOC_CTX *ctx,
-                       const struct smb_filename *smb_fname_in,
-                       struct smb_filename **pp_smb_fname_out)
-{
-       char *parent;
-       const char *base;
-       struct smb_filename *smb_fname = cp_smb_filename(ctx,
-                                               smb_fname_in);
-
-       if (smb_fname == NULL) {
-               return -1;
-       }
-
-       /* We need streamname to be NULL */
-       TALLOC_FREE(smb_fname->stream_name);
-
-       /* And we're replacing base_name. */
-       TALLOC_FREE(smb_fname->base_name);
-
-       if (!parent_dirname(smb_fname, smb_fname_in->base_name,
-                               &parent, &base)) {
-               TALLOC_FREE(smb_fname);
-               return -1;
-       }
-
-       smb_fname->base_name = talloc_asprintf(smb_fname,
-                                       "%s/._%s", parent, base);
-       if (smb_fname->base_name == NULL) {
-               TALLOC_FREE(smb_fname);
-               return -1;
-       }
-
-       *pp_smb_fname_out = smb_fname;
-
-       return 0;
-}
-
-/**
- * Allocate and initialize an AfpInfo struct
- **/
-static AfpInfo *afpinfo_new(TALLOC_CTX *ctx)
-{
-       AfpInfo *ai = talloc_zero(ctx, AfpInfo);
-       if (ai == NULL) {
-               return NULL;
-       }
-       ai->afpi_Signature = AFP_Signature;
-       ai->afpi_Version = AFP_Version;
-       ai->afpi_BackupTime = AD_DATE_START;
-       return ai;
-}
-
-/**
- * Pack an AfpInfo struct into a buffer
- *
- * Buffer size must be at least AFP_INFO_SIZE
- * Returns size of packed buffer
- **/
-static ssize_t afpinfo_pack(const AfpInfo *ai, char *buf)
-{
-       memset(buf, 0, AFP_INFO_SIZE);
-
-       RSIVAL(buf, 0, ai->afpi_Signature);
-       RSIVAL(buf, 4, ai->afpi_Version);
-       RSIVAL(buf, 12, ai->afpi_BackupTime);
-       memcpy(buf + 16, ai->afpi_FinderInfo, sizeof(ai->afpi_FinderInfo));
-
-       return AFP_INFO_SIZE;
-}
-
-/**
- * Unpack a buffer into a AfpInfo structure
- *
- * Buffer size must be at least AFP_INFO_SIZE
- * Returns allocated AfpInfo struct
- **/
-static AfpInfo *afpinfo_unpack(TALLOC_CTX *ctx, const void *data)
-{
-       AfpInfo *ai = talloc_zero(ctx, AfpInfo);
-       if (ai == NULL) {
-               return NULL;
-       }
-
-       ai->afpi_Signature = RIVAL(data, 0);
-       ai->afpi_Version = RIVAL(data, 4);
-       ai->afpi_BackupTime = RIVAL(data, 12);
-       memcpy(ai->afpi_FinderInfo, (const char *)data + 16,
-              sizeof(ai->afpi_FinderInfo));
-
-       if (ai->afpi_Signature != AFP_Signature
-           || ai->afpi_Version != AFP_Version) {
-               DEBUG(1, ("Bad AfpInfo signature or version\n"));
-               TALLOC_FREE(ai);
-       }
-
-       return ai;
-}
-
 static bool add_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
                             struct stream_struct **streams,
                             const char *name, off_t size,
index 5e0047da9170f0a7cb2ee78f740a990e5b5cdc60..5dae8a342b62ebc1abfb3afb6b410392b025e32d 100644 (file)
@@ -123,7 +123,7 @@ bld.SAMBA3_MODULE('vfs_netatalk',
 bld.SAMBA3_MODULE('vfs_fruit',
                  subsystem='vfs',
                  source='vfs_fruit.c',
-                 deps='samba-util OFFLOAD_TOKEN STRING_REPLACE HASH_INODE',
+                 deps='samba-util OFFLOAD_TOKEN STRING_REPLACE HASH_INODE ADOUBLE',
                  init_function='',
                  internal_module=bld.SAMBA3_IS_STATIC_MODULE('vfs_fruit'),
                  enabled=bld.SAMBA3_IS_ENABLED_MODULE('vfs_fruit'))
index aa3c71752026255d363b7dab25d362534c6300fb..db29a7d2a1528997fb7b126728d27c243a208f4a 100644 (file)
@@ -1081,6 +1081,10 @@ bld.SAMBA3_SUBSYSTEM('tevent-glib-glue',
                     enabled=bld.CONFIG_SET('WITH_TEVENT_GLIB_GLUE'),
 )
 
+bld.SAMBA3_SUBSYSTEM('ADOUBLE',
+                    source='lib/adouble.c',
+                    deps='STRING_REPLACE')
+
 ########################## BINARIES #################################
 
 bld.SAMBA3_BINARY('smbd/smbd',