smbd: add twrp arg to synthetic_smb_fname()
[amitay/samba.git] / source3 / modules / vfs_fruit.c
index 2bd4fbadf37af4ea6deb9b07c074682f40389c5b..5579988217a7f22864a30f1c24106b81885ce956 100644 (file)
 #include "messages.h"
 #include "libcli/security/security.h"
 #include "../libcli/smb/smb2_create_ctx.h"
-#include "lib/util/sys_rw.h"
 #include "lib/util/tevent_ntstatus.h"
 #include "lib/util/tevent_unix.h"
 #include "offload_token.h"
 #include "string_replace.h"
-
-#include <gnutls/gnutls.h>
-#include <gnutls/crypto.h>
+#include "hash_inode.h"
+#include "lib/adouble.h"
+#include "lib/util_macstreams.h"
 
 /*
  * Enhanced OS X and Netatalk compatibility
@@ -52,7 +51,7 @@
  * streams are deferred to vfs_streams_xattr.
  *
  * The OS X client maps all NTFS illegal characters to the Unicode
- * private range. This module optionally stores the charcters using
+ * private range. This module optionally stores the characters using
  * their native ASCII encoding using vfs_catia. If you're not enabling
  * this feature, you can skip catia from vfs modules.
  *
@@ -108,18 +107,6 @@ 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};
 
@@ -184,331 +171,6 @@ static const struct enum_list fruit_encoding[] = {
        { -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
-
-/* Accessor macros */
-#define ad_getentrylen(ad,eid)     ((ad)->ad_eid[(eid)].ade_len)
-#define ad_getentryoff(ad,eid)     ((ad)->ad_eid[(eid)].ade_off)
-#define ad_setentrylen(ad,eid,len) ((ad)->ad_eid[(eid)].ade_len = (len))
-#define ad_setentryoff(ad,eid,off) ((ad)->ad_eid[(eid)].ade_off = (off))
-
-/*
- * 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 {
-       int                       ad_fd;
-       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}
-};
-
-/*
- * Fake AppleDouble entry oder for resource fork xattr.  The xattr
- * isn't an AppleDouble file, it simply contains the resource data,
- * but in order to be able to use some API calls like ad_getentryoff()
- * we build a fake/helper struct adouble with this entry order struct.
- */
-static const
-struct ad_entry_order entry_order_rsrc_xattr[ADEID_NUM_RSRC_XATTR + 1] = {
-       {ADEID_RFORK, 0, 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;
@@ -522,7 +184,7 @@ struct fio {
        /*
         * 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
+        * existing 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.
         */
@@ -531,1956 +193,137 @@ struct fio {
        int mode;
 };
 
-/*
- * Forward declarations
- */
-static struct adouble *ad_init(TALLOC_CTX *ctx, vfs_handle_struct *handle,
-                              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);
-
-
-/**
- * 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;
-}
+/*****************************************************************************
+ * Helper functions
+ *****************************************************************************/
 
 /**
- * Get a date
+ * Initialize config struct from our smb.conf config parameters
  **/
-static int ad_getdate(const struct adouble *ad,
-                     unsigned int dateoff,
-                     uint32_t *date)
+static int init_fruit_config(vfs_handle_struct *handle)
 {
-       bool xlate = (dateoff & AD_DATE_UNIX);
-       char *p = NULL;
+       struct fruit_config_data *config;
+       int enumval;
+       const char *tm_size_str = NULL;
 
-       dateoff &= AD_DATE_MASK;
-       p = ad_get_entry(ad, ADEID_FILEDATESI);
-       if (p == NULL) {
+       config = talloc_zero(handle->conn, struct fruit_config_data);
+       if (!config) {
+               DEBUG(1, ("talloc_zero() failed\n"));
+               errno = ENOMEM;
                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) {
+       /*
+        * Versions up to Samba 4.5.x had a spelling bug in the
+        * fruit:resource option calling lp_parm_enum with
+        * "res*s*ource" (ie two s).
+        *
+        * In Samba 4.6 we accept both the wrong and the correct
+        * spelling, in Samba 4.7 the bad spelling will be removed.
+        */
+       enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
+                              "ressource", fruit_rsrc, FRUIT_RSRC_ADFILE);
+       if (enumval == -1) {
+               DEBUG(1, ("value for %s: resource type unknown\n",
+                         FRUIT_PARAM_TYPE_NAME));
                return -1;
        }
+       config->rsrc = (enum fruit_rsrc)enumval;
 
-       dateoff &= AD_DATE_MASK;
-       if (xlate) {
-               date = AD_DATE_FROM_UNIX(date);
-       }
-
-       if (dateoff > AD_DATE_ACCESS) {
+       enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
+                              "resource", fruit_rsrc, enumval);
+       if (enumval == -1) {
+               DEBUG(1, ("value for %s: resource type unknown\n",
+                         FRUIT_PARAM_TYPE_NAME));
                return -1;
        }
+       config->rsrc = (enum fruit_rsrc)enumval;
 
-       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;
+       enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
+                              "metadata", fruit_meta, FRUIT_META_NETATALK);
+       if (enumval == -1) {
+               DEBUG(1, ("value for %s: metadata type unknown\n",
+                         FRUIT_PARAM_TYPE_NAME));
+               return -1;
        }
+       config->meta = (enum fruit_meta)enumval;
 
-       ad->adx_entries = talloc_zero_array(
-               ad, struct ad_xattr_entry, h->adx_num_attrs);
-       if (ad->adx_entries == NULL) {
-               return false;
+       enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
+                              "locking", fruit_locking, FRUIT_LOCKING_NONE);
+       if (enumval == -1) {
+               DEBUG(1, ("value for %s: locking type unknown\n",
+                         FRUIT_PARAM_TYPE_NAME));
+               return -1;
        }
+       config->locking = (enum fruit_locking)enumval;
 
-       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;
+       enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
+                              "encoding", fruit_encoding, FRUIT_ENC_PRIVATE);
+       if (enumval == -1) {
+               DEBUG(1, ("value for %s: encoding type unknown\n",
+                         FRUIT_PARAM_TYPE_NAME));
+               return -1;
        }
-
-       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(struct adouble *ad,
-                                const struct smb_filename *smb_fname)
-{
-       char *map = MAP_FAILED;
-       size_t maplen;
-       ssize_t len;
-       int rc;
-       bool ok;
-
-       if (ad_getentrylen(ad, ADEID_RFORK) == 0) {
-               return true;
-       }
-
-       maplen = ad_getentryoff(ad, ADEID_RFORK) +
-               ad_getentrylen(ad, ADEID_RFORK);
-
-       /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
-       map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
-                  ad->ad_fd, 0);
-       if (map == MAP_FAILED) {
-               DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
-               return false;
-       }
-
-
-       memmove(map + ADEDOFF_RFORK_DOT_UND,
-               map + ad_getentryoff(ad, ADEID_RFORK),
-               ad_getentrylen(ad, ADEID_RFORK));
-
-       rc = munmap(map, maplen);
-       if (rc != 0) {
-               DBG_ERR("munmap failed: %s\n", 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;
-       }
-
-       len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
-       if (len != AD_DATASZ_DOT_UND) {
-               DBG_ERR("%s: bad size: %zd\n", smb_fname->base_name, len);
-               return false;
-       }
-
-       return true;
-}
-
-static bool ad_convert_xattr(vfs_handle_struct *handle,
-                            struct adouble *ad,
-                            const struct smb_filename *smb_fname,
-                            bool *converted_xattr)
-{
-       static struct char_mappings **string_replace_cmaps = NULL;
-       char *map = MAP_FAILED;
-       size_t maplen;
-       uint16_t i;
-       ssize_t len;
-       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(), fruit_catia_maps, NULL);
-               if (mappings == NULL) {
-                       return false;
-               }
-               string_replace_cmaps = string_replace_init_map(mappings);
-               TALLOC_FREE(mappings);
-       }
-
-       maplen = ad_getentryoff(ad, ADEID_RFORK) +
-               ad_getentrylen(ad, ADEID_RFORK);
-
-       /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
-       map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
-                  ad->ad_fd, 0);
-       if (map == MAP_FAILED) {
-               DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
-               return false;
-       }
-
-       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,
-                                         map + 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;
-       }
-
-       len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
-       if (len != AD_DATASZ_DOT_UND) {
-               DBG_ERR("%s: bad size: %zd\n", smb_fname->base_name, len);
-               ok = false;
-               goto fail;
-       }
-
-       ok = ad_convert_move_reso(ad, smb_fname);
-       if (!ok) {
-               goto fail;
-       }
-
-       *converted_xattr = true;
-       ok = true;
-
-fail:
-       rc = munmap(map, maplen);
-       if (rc != 0) {
-               DBG_ERR("munmap failed: %s\n", strerror(errno));
-               return false;
-       }
-
-       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(struct adouble *ad,
-                               const struct smb_filename *smb_fname)
-{
-       int rc;
-
-       /*
-        * FIXME: direct ftruncate(), but we don't have a fsp for the
-        * VFS call
-        */
-       rc = ftruncate(ad->ad_fd, ADEDOFF_RFORK_DOT_UND +
-                      ad_getentrylen(ad, ADEID_RFORK));
-       if (rc != 0) {
-               return false;
-       }
-
-       return true;
-}
-
-static bool ad_convert_blank_rfork(vfs_handle_struct *handle,
-                                  struct adouble *ad,
-                                  bool *blank)
-{
-       struct fruit_config_data *config = NULL;
-       uint8_t *map = MAP_FAILED;
-       size_t maplen;
-       int cmp;
-       ssize_t len;
-       int rc;
-       bool ok;
-
-       *blank = false;
-
-       SMB_VFS_HANDLE_GET_DATA(handle, config,
-                               struct fruit_config_data, return false);
-
-       if (!config->wipe_intentionally_left_blank_rfork) {
-               return true;
-       }
-
-       if (ad_getentrylen(ad, ADEID_RFORK) != sizeof(empty_resourcefork)) {
-               return true;
-       }
-
-       maplen = ad_getentryoff(ad, ADEID_RFORK) +
-               ad_getentrylen(ad, ADEID_RFORK);
-
-       /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
-       map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
-                  ad->ad_fd, 0);
-       if (map == MAP_FAILED) {
-               DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
-               return false;
-       }
-
-       cmp = memcmp(map + ADEDOFF_RFORK_DOT_UND,
-                    empty_resourcefork,
-                    sizeof(empty_resourcefork));
-       rc = munmap(map, maplen);
-       if (rc != 0) {
-               DBG_ERR("munmap failed: %s\n", strerror(errno));
-               return false;
-       }
-
-       if (cmp != 0) {
-               return true;
-       }
-
-       ad_setentrylen(ad, ADEID_RFORK, 0);
-
-       ok = ad_pack(ad);
-       if (!ok) {
-               return false;
-       }
-
-       len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
-       if (len != AD_DATASZ_DOT_UND) {
-               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)
-{
-       struct fruit_config_data *config = NULL;
-       struct smb_filename *ad_name = NULL;
-       int rc;
-
-       if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
-               return true;
-       }
-
-       SMB_VFS_HANDLE_GET_DATA(handle, config,
-                               struct fruit_config_data, return false);
-
-       if (!config->delete_empty_adfiles) {
-               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)
-{
-       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, &converted_xattr);
-       if (!ok) {
-               ret = -1;
-               goto done;
-       }
-
-       ok = ad_convert_blank_rfork(handle, ad, &blank);
-       if (!ok) {
-               ret = -1;
-               goto done;
-       }
-
-       if (converted_xattr || blank) {
-               ok = ad_convert_truncate(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);
-       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_xattr(const struct smb_filename *smb_fname,
-                               int flags,
-                               mode_t mode)
-{
-#ifdef HAVE_ATTROPEN
-       /* FIXME: direct Solaris xattr syscall */
-       return attropen(smb_fname->base_name,
-                       AFPRESOURCE_EA_NETATALK, flags, mode);
-#else
-       errno = ENOSYS;
-       return -1;
-#endif
-}
-
-static int ad_open_rsrc_adouble(const struct smb_filename *smb_fname,
-                               int flags,
-                               mode_t mode)
-{
-       int ret;
-       int fd;
-       struct smb_filename *adp_smb_fname = NULL;
-
-       ret = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
-       if (ret != 0) {
-               return -1;
-       }
-
-       fd = open(adp_smb_fname->base_name, flags, mode);
-       TALLOC_FREE(adp_smb_fname);
-
-       return fd;
-}
-
-static int ad_open_rsrc(vfs_handle_struct *handle,
-                       const struct smb_filename *smb_fname,
-                       int flags,
-                       mode_t mode)
-{
-       struct fruit_config_data *config = NULL;
-       int fd;
-
-       SMB_VFS_HANDLE_GET_DATA(handle, config,
-                               struct fruit_config_data, return -1);
-
-       if (config->rsrc == FRUIT_RSRC_XATTR) {
-               fd = ad_open_rsrc_xattr(smb_fname, flags, mode);
-       } else {
-               fd = ad_open_rsrc_adouble(smb_fname, flags, mode);
-       }
-
-       return fd;
-}
-
-/*
- * 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 fd;
-
-       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) && (fsp->fh != NULL) && (fsp->fh->fd != -1)) {
-               ad->ad_fd = fsp->fh->fd;
-               ad->ad_opened = false;
-               return 0;
-       }
-
-       fd = ad_open_rsrc(handle, smb_fname, flags, mode);
-       if (fd == -1) {
-               return -1;
-       }
-       ad->ad_opened = true;
-       ad->ad_fd = fd;
-
-       DBG_DEBUG("Path [%s] type [%s] fd [%d]\n",
-                 smb_fname->base_name,
-                 ad->ad_type == ADOUBLE_META ? "meta" : "rsrc", fd);
-
-       return 0;
-}
-
-static ssize_t ad_read_rsrc_xattr(vfs_handle_struct *handle,
-                                 struct adouble *ad)
-{
-       int ret;
-       SMB_STRUCT_STAT st;
-
-       /* FIXME: direct sys_fstat(), don't have an fsp */
-       ret = sys_fstat(ad->ad_fd, &st,
-                       lp_fake_directory_create_times(
-                               SNUM(handle->conn)));
-       if (ret != 0) {
-               return -1;
-       }
-
-       ad_setentrylen(ad, ADEID_RFORK, st.st_ex_size);
-       return st.st_ex_size;
-}
-
-static ssize_t ad_read_rsrc_adouble(vfs_handle_struct *handle,
-                                   struct adouble *ad,
-                                   const struct smb_filename *smb_fname)
-{
-       SMB_STRUCT_STAT sbuf;
-       char *p_ad = NULL;
-       size_t size;
-       ssize_t len;
-       int ret;
-       bool ok;
-
-       ret = sys_fstat(ad->ad_fd, &sbuf, lp_fake_directory_create_times(
-                               SNUM(handle->conn)));
-       if (ret != 0) {
-               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 = sbuf.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 = sys_pread(ad->ad_fd, 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, sbuf.st_ex_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)
-{
-       struct fruit_config_data *config = NULL;
-       ssize_t len;
-
-       SMB_VFS_HANDLE_GET_DATA(handle, config,
-                               struct fruit_config_data, return -1);
-
-       if (config->rsrc == FRUIT_RSRC_XATTR) {
-               len = ad_read_rsrc_xattr(handle, ad);
-       } else {
-               len = ad_read_rsrc_adouble(handle, ad, smb_fname);
-       }
-
-       return len;
-}
-
-/**
- * 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)
-{
-       if ((ad->ad_fd != -1) && ad->ad_opened) {
-               close(ad->ad_fd);
-               ad->ad_fd = -1;
-       }
-       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, vfs_handle_struct *handle,
-                               adouble_type_t type)
-{
-       int rc = 0;
-       size_t adsize = 0;
-       struct adouble *ad;
-       struct fruit_config_data *config;
-
-       SMB_VFS_HANDLE_GET_DATA(handle, config,
-                               struct fruit_config_data, return NULL);
-
-       switch (type) {
-       case ADOUBLE_META:
-               adsize = AD_DATASZ_XATTR;
-               break;
-       case ADOUBLE_RSRC:
-               if (config->rsrc == FRUIT_RSRC_ADFILE) {
-                       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;
-       ad->ad_fd = -1;
-
-       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] handle     vfs handle
- * @param[in] type       type of AppleDouble, ADOUBLE_META or ADOUBLE_RSRC
- *
- * @return               adouble handle, initialized
- **/
-static struct adouble *ad_init(TALLOC_CTX *ctx, vfs_handle_struct *handle,
-                              adouble_type_t type)
-{
-       int rc = 0;
-       const struct ad_entry_order  *eid;
-       struct adouble *ad = NULL;
-       struct fruit_config_data *config;
-       time_t t = time(NULL);
-
-       SMB_VFS_HANDLE_GET_DATA(handle, config,
-                               struct fruit_config_data, return NULL);
-
-       switch (type) {
-       case ADOUBLE_META:
-               eid = entry_order_meta_xattr;
-               break;
-       case ADOUBLE_RSRC:
-               if (config->rsrc == FRUIT_RSRC_ADFILE) {
-                       eid = entry_order_dot_und;
-               } else {
-                       eid = entry_order_rsrc_xattr;
-               }
-               break;
-       default:
-               return NULL;
-       }
-
-       ad = ad_alloc(ctx, handle, 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, handle, 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;
-       }
-
-       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
- **/
-static 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;
-}
-
-/*****************************************************************************
- * Helper functions
- *****************************************************************************/
-
-static bool is_afpinfo_stream(const struct smb_filename *smb_fname)
-{
-       if (strncasecmp_m(smb_fname->stream_name,
-                         AFPINFO_STREAM_NAME,
-                         strlen(AFPINFO_STREAM_NAME)) == 0) {
-               return true;
-       }
-       return false;
-}
-
-static bool is_afpresource_stream(const struct smb_filename *smb_fname)
-{
-       if (strncasecmp_m(smb_fname->stream_name,
-                         AFPRESOURCE_STREAM_NAME,
-                         strlen(AFPRESOURCE_STREAM_NAME)) == 0) {
-               return true;
-       }
-       return false;
-}
-
-/**
- * Test whether stream is an Apple stream.
- **/
-static bool is_apple_stream(const struct smb_filename *smb_fname)
-{
-       if (is_afpinfo_stream(smb_fname)) {
-               return true;
-       }
-       if (is_afpresource_stream(smb_fname)) {
-               return true;
-       }
-       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
- **/
-static int init_fruit_config(vfs_handle_struct *handle)
-{
-       struct fruit_config_data *config;
-       int enumval;
-       const char *tm_size_str = NULL;
-
-       config = talloc_zero(handle->conn, struct fruit_config_data);
-       if (!config) {
-               DEBUG(1, ("talloc_zero() failed\n"));
-               errno = ENOMEM;
-               return -1;
-       }
-
-       /*
-        * Versions up to Samba 4.5.x had a spelling bug in the
-        * fruit:resource option calling lp_parm_enum with
-        * "res*s*ource" (ie two s).
-        *
-        * In Samba 4.6 we accept both the wrong and the correct
-        * spelling, in Samba 4.7 the bad spelling will be removed.
-        */
-       enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
-                              "ressource", fruit_rsrc, FRUIT_RSRC_ADFILE);
-       if (enumval == -1) {
-               DEBUG(1, ("value for %s: resource type unknown\n",
-                         FRUIT_PARAM_TYPE_NAME));
-               return -1;
-       }
-       config->rsrc = (enum fruit_rsrc)enumval;
-
-       enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
-                              "resource", fruit_rsrc, enumval);
-       if (enumval == -1) {
-               DEBUG(1, ("value for %s: resource type unknown\n",
-                         FRUIT_PARAM_TYPE_NAME));
-               return -1;
-       }
-       config->rsrc = (enum fruit_rsrc)enumval;
-
-       enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
-                              "metadata", fruit_meta, FRUIT_META_NETATALK);
-       if (enumval == -1) {
-               DEBUG(1, ("value for %s: metadata type unknown\n",
-                         FRUIT_PARAM_TYPE_NAME));
-               return -1;
-       }
-       config->meta = (enum fruit_meta)enumval;
-
-       enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
-                              "locking", fruit_locking, FRUIT_LOCKING_NONE);
-       if (enumval == -1) {
-               DEBUG(1, ("value for %s: locking type unknown\n",
-                         FRUIT_PARAM_TYPE_NAME));
-               return -1;
-       }
-       config->locking = (enum fruit_locking)enumval;
-
-       enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
-                              "encoding", fruit_encoding, FRUIT_ENC_PRIVATE);
-       if (enumval == -1) {
-               DEBUG(1, ("value for %s: encoding type unknown\n",
-                         FRUIT_PARAM_TYPE_NAME));
-               return -1;
-       }
-       config->encoding = (enum fruit_encoding)enumval;
+       config->encoding = (enum fruit_encoding)enumval;
 
        if (config->rsrc == FRUIT_RSRC_ADFILE) {
                config->veto_appledouble = lp_parm_bool(SNUM(handle->conn),
                                                        FRUIT_PARAM_TYPE_NAME,
                                                        "veto_appledouble",
-                                                       true);
-       }
-
-       config->use_aapl = lp_parm_bool(
-               -1, FRUIT_PARAM_TYPE_NAME, "aapl", true);
-
-       config->time_machine = lp_parm_bool(
-               SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "time machine", false);
-
-       config->unix_info_enabled = lp_parm_bool(
-               -1, FRUIT_PARAM_TYPE_NAME, "nfs_aces", true);
-
-       config->use_copyfile = lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME,
-                                          "copyfile", false);
-
-       config->posix_rename = lp_parm_bool(
-               SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "posix_rename", true);
-
-       config->aapl_zero_file_id =
-           lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME, "zero_file_id", true);
-
-       config->readdir_attr_rsize = lp_parm_bool(
-               SNUM(handle->conn), "readdir_attr", "aapl_rsize", true);
-
-       config->readdir_attr_finder_info = lp_parm_bool(
-               SNUM(handle->conn), "readdir_attr", "aapl_finder_info", true);
-
-       config->readdir_attr_max_access = lp_parm_bool(
-               SNUM(handle->conn), "readdir_attr", "aapl_max_access", true);
-
-       config->model = lp_parm_const_string(
-               -1, FRUIT_PARAM_TYPE_NAME, "model", "MacSamba");
-
-       tm_size_str = lp_parm_const_string(
-               SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
-               "time machine max size", NULL);
-       if (tm_size_str != NULL) {
-               config->time_machine_max_size = conv_str_size(tm_size_str);
-       }
-
-       config->wipe_intentionally_left_blank_rfork = lp_parm_bool(
-               SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
-               "wipe_intentionally_left_blank_rfork", false);
-
-       config->delete_empty_adfiles = lp_parm_bool(
-               SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
-               "delete_empty_adfiles", false);
-
-       SMB_VFS_HANDLE_SET_DATA(handle, config,
-                               NULL, struct fruit_config_data,
-                               return -1);
-
-       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);
+                                                       true);
+       }
 
-       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));
+       config->use_aapl = lp_parm_bool(
+               -1, FRUIT_PARAM_TYPE_NAME, "aapl", true);
 
-       return AFP_INFO_SIZE;
-}
+       config->time_machine = lp_parm_bool(
+               SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "time machine", false);
 
-/**
- * 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;
-       }
+       config->unix_info_enabled = lp_parm_bool(
+               -1, FRUIT_PARAM_TYPE_NAME, "nfs_aces", true);
 
-       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));
+       config->use_copyfile = lp_parm_bool(-1, FRUIT_PARAM_TYPE_NAME,
+                                          "copyfile", false);
 
-       if (ai->afpi_Signature != AFP_Signature
-           || ai->afpi_Version != AFP_Version) {
-               DEBUG(1, ("Bad AfpInfo signature or version\n"));
-               TALLOC_FREE(ai);
-       }
+       config->posix_rename = lp_parm_bool(
+               SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME, "posix_rename", true);
 
-       return ai;
-}
+       config->aapl_zero_file_id =
+           lp_parm_bool(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
+                        "zero_file_id", false);
 
-/**
- * Fake an inode number from the md5 hash of the (xattr) name
- **/
-static SMB_INO_T fruit_inode(const SMB_STRUCT_STAT *sbuf, const char *sname)
-{
-       gnutls_hash_hd_t hash_hnd = NULL;
-       unsigned char hash[16];
-       SMB_INO_T result = 0;
-       char *upper_sname;
-       int rc;
+       config->readdir_attr_rsize = lp_parm_bool(
+               SNUM(handle->conn), "readdir_attr", "aapl_rsize", true);
 
-       DBG_DEBUG("fruit_inode called for %ju/%ju [%s]\n",
-                 (uintmax_t)sbuf->st_ex_dev,
-                 (uintmax_t)sbuf->st_ex_ino, sname);
+       config->readdir_attr_finder_info = lp_parm_bool(
+               SNUM(handle->conn), "readdir_attr", "aapl_finder_info", true);
 
-       upper_sname = talloc_strdup_upper(talloc_tos(), sname);
-       SMB_ASSERT(upper_sname != NULL);
+       config->readdir_attr_max_access = lp_parm_bool(
+               SNUM(handle->conn), "readdir_attr", "aapl_max_access", true);
 
-       rc = gnutls_hash_init(&hash_hnd, GNUTLS_DIG_MD5);
-       if (rc < 0) {
-               goto out;
-       }
+       config->model = lp_parm_const_string(
+               -1, FRUIT_PARAM_TYPE_NAME, "model", "MacSamba");
 
-       rc = gnutls_hash(hash_hnd, &(sbuf->st_ex_dev), sizeof(sbuf->st_ex_dev));
-       if (rc < 0) {
-               gnutls_hash_deinit(hash_hnd, NULL);
-               goto out;
-       }
-       rc = gnutls_hash(hash_hnd,
-                        &(sbuf->st_ex_ino),
-                        sizeof(sbuf->st_ex_ino));
-       if (rc < 0) {
-               gnutls_hash_deinit(hash_hnd, NULL);
-               goto out;
-       }
-       rc = gnutls_hash(hash_hnd,
-                        upper_sname,
-                        talloc_get_size(upper_sname) - 1);
-       if (rc < 0) {
-               gnutls_hash_deinit(hash_hnd, NULL);
-               goto out;
+       tm_size_str = lp_parm_const_string(
+               SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
+               "time machine max size", NULL);
+       if (tm_size_str != NULL) {
+               config->time_machine_max_size = conv_str_size(tm_size_str);
        }
 
-       gnutls_hash_deinit(hash_hnd, hash);
-
-       /* Hopefully all the variation is in the lower 4 (or 8) bytes! */
-       memcpy(&result, hash, sizeof(result));
-       ZERO_ARRAY(hash);
+       config->wipe_intentionally_left_blank_rfork = lp_parm_bool(
+               SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
+               "wipe_intentionally_left_blank_rfork", false);
 
-       DBG_DEBUG("fruit_inode \"%s\": ino=%ju\n",
-                 sname, (uintmax_t)result);
+       config->delete_empty_adfiles = lp_parm_bool(
+               SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
+               "delete_empty_adfiles", false);
 
-out:
-       TALLOC_FREE(upper_sname);
+       SMB_VFS_HANDLE_SET_DATA(handle, config,
+                               NULL, struct fruit_config_data,
+                               return -1);
 
-       return result;
+       return 0;
 }
 
 static bool add_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
@@ -2534,11 +377,7 @@ static bool filter_empty_rsrc_stream(unsigned int *num_streams,
        }
 
        TALLOC_FREE(tmp[i].name);
-       if (*num_streams - 1 > i) {
-               memmove(&tmp[i], &tmp[i+1],
-                       (*num_streams - i - 1) * sizeof(struct stream_struct));
-       }
-
+       ARRAY_DEL_ELEMENT(tmp, i, *num_streams);
        *num_streams -= 1;
        return true;
 }
@@ -2565,11 +404,7 @@ static bool del_fruit_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
        }
 
        TALLOC_FREE(tmp[i].name);
-       if (*num_streams - 1 > i) {
-               memmove(&tmp[i], &tmp[i+1],
-                       (*num_streams - i - 1) * sizeof(struct stream_struct));
-       }
-
+       ARRAY_DEL_ELEMENT(tmp, i, *num_streams);
        *num_streams -= 1;
        return true;
 }
@@ -2704,7 +539,7 @@ static off_t denymode_to_netatalk_brl(enum apple_fork fork_type,
 
 /**
  * Call fcntl() with an exclusive F_GETLK request in order to
- * determine if there's an exisiting shared lock
+ * determine if there's an existing shared lock
  *
  * @return true if the requested lock was found or any error occurred
  *         false if the lock was not found
@@ -2742,6 +577,7 @@ static NTSTATUS fruit_check_access(vfs_handle_struct *handle,
        bool netatalk_already_open_for_writing = false;
        bool netatalk_already_open_with_deny_read = false;
        bool netatalk_already_open_with_deny_write = false;
+       struct GUID req_guid = GUID_random();
 
        /* FIXME: hardcoded data fork, add resource fork */
        enum apple_fork fork_type = APPLE_FORK_DATA;
@@ -2804,16 +640,19 @@ static NTSTATUS fruit_check_access(vfs_handle_struct *handle,
 
        /* Set NetAtalk locks matching our access */
        if (access_mask & FILE_READ_DATA) {
-               struct byte_range_lock *br_lck = NULL;
-
                off = access_to_netatalk_brl(fork_type, FILE_READ_DATA);
-               br_lck = do_lock(
-                       handle->conn->sconn->msg_ctx, fsp,
-                       fsp->op->global->open_persistent_id, 1, off,
-                       READ_LOCK, POSIX_LOCK, false,
-                       &status, NULL);
-
-               TALLOC_FREE(br_lck);
+               req_guid.time_hi_and_version = __LINE__;
+               status = do_lock(
+                       fsp,
+                       talloc_tos(),
+                       &req_guid,
+                       fsp->op->global->open_persistent_id,
+                       1,
+                       off,
+                       READ_LOCK,
+                       POSIX_LOCK,
+                       NULL,
+                       NULL);
 
                if (!NT_STATUS_IS_OK(status))  {
                        return status;
@@ -2821,16 +660,19 @@ static NTSTATUS fruit_check_access(vfs_handle_struct *handle,
        }
 
        if (!share_for_read) {
-               struct byte_range_lock *br_lck = NULL;
-
                off = denymode_to_netatalk_brl(fork_type, DENY_READ);
-               br_lck = do_lock(
-                       handle->conn->sconn->msg_ctx, fsp,
-                       fsp->op->global->open_persistent_id, 1, off,
-                       READ_LOCK, POSIX_LOCK, false,
-                       &status, NULL);
-
-               TALLOC_FREE(br_lck);
+               req_guid.time_hi_and_version = __LINE__;
+               status = do_lock(
+                       fsp,
+                       talloc_tos(),
+                       &req_guid,
+                       fsp->op->global->open_persistent_id,
+                       1,
+                       off,
+                       READ_LOCK,
+                       POSIX_LOCK,
+                       NULL,
+                       NULL);
 
                if (!NT_STATUS_IS_OK(status)) {
                        return status;
@@ -2838,16 +680,19 @@ static NTSTATUS fruit_check_access(vfs_handle_struct *handle,
        }
 
        if (access_mask & FILE_WRITE_DATA) {
-               struct byte_range_lock *br_lck = NULL;
-
                off = access_to_netatalk_brl(fork_type, FILE_WRITE_DATA);
-               br_lck = do_lock(
-                       handle->conn->sconn->msg_ctx, fsp,
-                       fsp->op->global->open_persistent_id, 1, off,
-                       READ_LOCK, POSIX_LOCK, false,
-                       &status, NULL);
-
-               TALLOC_FREE(br_lck);
+               req_guid.time_hi_and_version = __LINE__;
+               status = do_lock(
+                       fsp,
+                       talloc_tos(),
+                       &req_guid,
+                       fsp->op->global->open_persistent_id,
+                       1,
+                       off,
+                       READ_LOCK,
+                       POSIX_LOCK,
+                       NULL,
+                       NULL);
 
                if (!NT_STATUS_IS_OK(status)) {
                        return status;
@@ -2855,16 +700,19 @@ static NTSTATUS fruit_check_access(vfs_handle_struct *handle,
        }
 
        if (!share_for_write) {
-               struct byte_range_lock *br_lck = NULL;
-
                off = denymode_to_netatalk_brl(fork_type, DENY_WRITE);
-               br_lck = do_lock(
-                       handle->conn->sconn->msg_ctx, fsp,
-                       fsp->op->global->open_persistent_id, 1, off,
-                       READ_LOCK, POSIX_LOCK, false,
-                       &status, NULL);
-
-               TALLOC_FREE(br_lck);
+               req_guid.time_hi_and_version = __LINE__;
+               status = do_lock(
+                       fsp,
+                       talloc_tos(),
+                       &req_guid,
+                       fsp->op->global->open_persistent_id,
+                       1,
+                       off,
+                       READ_LOCK,
+                       POSIX_LOCK,
+                       NULL,
+                       NULL);
 
                if (!NT_STATUS_IS_OK(status)) {
                        return status;
@@ -3014,9 +862,6 @@ static NTSTATUS check_aapl(vfs_handle_struct *handle,
                                      blob);
        if (NT_STATUS_IS_OK(status)) {
                global_fruit_config.nego_aapl = true;
-               if (config->aapl_zero_file_id) {
-                       aapl_force_zero_file_id(handle->conn->sconn);
-               }
        }
 
        return status;
@@ -3038,7 +883,9 @@ static bool readdir_attr_meta_finderi_stream(
        stream_name = synthetic_smb_fname(talloc_tos(),
                                          smb_fname->base_name,
                                          AFPINFO_STREAM_NAME,
-                                         NULL, smb_fname->flags);
+                                         NULL,
+                                         smb_fname->twrp,
+                                         smb_fname->flags);
        if (stream_name == NULL) {
                return false;
        }
@@ -3212,7 +1059,9 @@ static uint64_t readdir_attr_rfork_size_stream(
        stream_name = synthetic_smb_fname(talloc_tos(),
                                          smb_fname->base_name,
                                          AFPRESOURCE_STREAM_NAME,
-                                         NULL, 0);
+                                         NULL,
+                                         smb_fname->twrp,
+                                         0);
        if (stream_name == NULL) {
                return 0;
        }
@@ -3241,12 +1090,12 @@ static uint64_t readdir_attr_rfork_size(struct vfs_handle_struct *handle,
 
        switch (config->rsrc) {
        case FRUIT_RSRC_ADFILE:
-       case FRUIT_RSRC_XATTR:
                rfork_size = readdir_attr_rfork_size_adouble(handle,
                                                             smb_fname);
                break;
 
-       case FRUIT_META_STREAM:
+       case FRUIT_RSRC_XATTR:
+       case FRUIT_RSRC_STREAM:
                rfork_size = readdir_attr_rfork_size_stream(handle,
                                                            smb_fname);
                break;
@@ -3401,6 +1250,8 @@ static int fruit_connect(vfs_handle_struct *handle,
        int rc;
        char *list = NULL, *newlist = NULL;
        struct fruit_config_data *config;
+       const struct loadparm_substitution *lp_sub =
+               loadparm_s3_global_substitution();
 
        DEBUG(10, ("fruit_connect\n"));
 
@@ -3418,7 +1269,7 @@ static int fruit_connect(vfs_handle_struct *handle,
                                struct fruit_config_data, return -1);
 
        if (config->veto_appledouble) {
-               list = lp_veto_files(talloc_tos(), SNUM(handle->conn));
+               list = lp_veto_files(talloc_tos(), lp_sub, SNUM(handle->conn));
 
                if (list) {
                        if (strstr(list, "/" ADOUBLE_NAME_PREFIX "*/") == NULL) {
@@ -3442,7 +1293,7 @@ static int fruit_connect(vfs_handle_struct *handle,
        if (config->encoding == FRUIT_ENC_NATIVE) {
                lp_do_parameter(SNUM(handle->conn),
                                "catia:mappings",
-                               fruit_catia_maps);
+                               macos_string_replace_map);
        }
 
        if (config->time_machine) {
@@ -3642,7 +1493,7 @@ static int fruit_open_rsrc_adouble(vfs_handle_struct *handle,
        }
 
        if (flags & (O_CREAT | O_TRUNC)) {
-               ad = ad_init(fsp, handle, ADOUBLE_RSRC);
+               ad = ad_init(fsp, ADOUBLE_RSRC);
                if (ad == NULL) {
                        rc = -1;
                        goto exit;
@@ -3760,13 +1611,13 @@ static int fruit_open(vfs_handle_struct *handle,
 
        DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
 
-       if (!is_ntfs_stream_smb_fname(smb_fname)) {
+       if (!is_named_stream(smb_fname)) {
                return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
        }
 
-       if (is_afpinfo_stream(smb_fname)) {
+       if (is_afpinfo_stream(smb_fname->stream_name)) {
                fd = fruit_open_meta(handle, smb_fname, fsp, flags, mode);
-       } else if (is_afpresource_stream(smb_fname)) {
+       } else if (is_afpresource_stream(smb_fname->stream_name)) {
                fd = fruit_open_rsrc(handle, smb_fname, fsp, flags, mode);
        } else {
                fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
@@ -3843,13 +1694,13 @@ static int fruit_close(vfs_handle_struct *handle,
 
        DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(fsp->fsp_name), fd);
 
-       if (!is_ntfs_stream_smb_fname(fsp->fsp_name)) {
+       if (!is_named_stream(fsp->fsp_name)) {
                return SMB_VFS_NEXT_CLOSE(handle, fsp);
        }
 
-       if (is_afpinfo_stream(fsp->fsp_name)) {
+       if (is_afpinfo_stream(fsp->fsp_name->stream_name)) {
                ret = fruit_close_meta(handle, fsp);
-       } else if (is_afpresource_stream(fsp->fsp_name)) {
+       } else if (is_afpresource_stream(fsp->fsp_name->stream_name)) {
                ret = fruit_close_rsrc(handle, fsp);
        } else {
                ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
@@ -3858,8 +1709,10 @@ static int fruit_close(vfs_handle_struct *handle,
        return ret;
 }
 
-static int fruit_rename(struct vfs_handle_struct *handle,
+static int fruit_renameat(struct vfs_handle_struct *handle,
+                       files_struct *srcfsp,
                        const struct smb_filename *smb_fname_src,
+                       files_struct *dstfsp,
                        const struct smb_filename *smb_fname_dst)
 {
        int rc = -1;
@@ -3876,7 +1729,11 @@ static int fruit_rename(struct vfs_handle_struct *handle,
                return -1;
        }
 
-       rc = SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
+       rc = SMB_VFS_NEXT_RENAMEAT(handle,
+                               srcfsp,
+                               smb_fname_src,
+                               dstfsp,
+                               smb_fname_dst);
        if (rc != 0) {
                return -1;
        }
@@ -3901,7 +1758,11 @@ static int fruit_rename(struct vfs_handle_struct *handle,
                  smb_fname_str_dbg(src_adp_smb_fname),
                  smb_fname_str_dbg(dst_adp_smb_fname));
 
-       rc = SMB_VFS_NEXT_RENAME(handle, src_adp_smb_fname, dst_adp_smb_fname);
+       rc = SMB_VFS_NEXT_RENAMEAT(handle,
+                       srcfsp,
+                       src_adp_smb_fname,
+                       dstfsp,
+                       dst_adp_smb_fname);
        if (errno == ENOENT) {
                rc = 0;
        }
@@ -3913,9 +1774,13 @@ done:
 }
 
 static int fruit_unlink_meta_stream(vfs_handle_struct *handle,
-                                   const struct smb_filename *smb_fname)
+                               struct files_struct *dirfsp,
+                               const struct smb_filename *smb_fname)
 {
-       return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
+       return SMB_VFS_NEXT_UNLINKAT(handle,
+                               dirfsp,
+                               smb_fname,
+                               0);
 }
 
 static int fruit_unlink_meta_netatalk(vfs_handle_struct *handle,
@@ -3927,7 +1792,8 @@ static int fruit_unlink_meta_netatalk(vfs_handle_struct *handle,
 }
 
 static int fruit_unlink_meta(vfs_handle_struct *handle,
-                            const struct smb_filename *smb_fname)
+                       struct files_struct *dirfsp,
+                       const struct smb_filename *smb_fname)
 {
        struct fruit_config_data *config = NULL;
        int rc;
@@ -3937,7 +1803,9 @@ static int fruit_unlink_meta(vfs_handle_struct *handle,
 
        switch (config->meta) {
        case FRUIT_META_STREAM:
-               rc = fruit_unlink_meta_stream(handle, smb_fname);
+               rc = fruit_unlink_meta_stream(handle,
+                               dirfsp,
+                               smb_fname);
                break;
 
        case FRUIT_META_NETATALK:
@@ -3953,8 +1821,9 @@ static int fruit_unlink_meta(vfs_handle_struct *handle,
 }
 
 static int fruit_unlink_rsrc_stream(vfs_handle_struct *handle,
-                                   const struct smb_filename *smb_fname,
-                                   bool force_unlink)
+                               struct files_struct *dirfsp,
+                               const struct smb_filename *smb_fname,
+                               bool force_unlink)
 {
        int ret;
 
@@ -3990,7 +1859,10 @@ static int fruit_unlink_rsrc_stream(vfs_handle_struct *handle,
                }
        }
 
-       ret = SMB_VFS_NEXT_UNLINK(handle, smb_fname);
+       ret = SMB_VFS_NEXT_UNLINKAT(handle,
+                       dirfsp,
+                       smb_fname,
+                       0);
        if ((ret != 0) && (errno == ENOENT) && force_unlink) {
                ret = 0;
        }
@@ -3999,8 +1871,9 @@ static int fruit_unlink_rsrc_stream(vfs_handle_struct *handle,
 }
 
 static int fruit_unlink_rsrc_adouble(vfs_handle_struct *handle,
-                                    const struct smb_filename *smb_fname,
-                                    bool force_unlink)
+                               struct files_struct *dirfsp,
+                               const struct smb_filename *smb_fname,
+                               bool force_unlink)
 {
        int rc;
        struct adouble *ad = NULL;
@@ -4035,7 +1908,10 @@ static int fruit_unlink_rsrc_adouble(vfs_handle_struct *handle,
                return -1;
        }
 
-       rc = SMB_VFS_NEXT_UNLINK(handle, adp_smb_fname);
+       rc = SMB_VFS_NEXT_UNLINKAT(handle,
+                       dirfsp,
+                       adp_smb_fname,
+                       0);
        TALLOC_FREE(adp_smb_fname);
        if ((rc != 0) && (errno == ENOENT) && force_unlink) {
                rc = 0;
@@ -4058,8 +1934,9 @@ static int fruit_unlink_rsrc_xattr(vfs_handle_struct *handle,
 }
 
 static int fruit_unlink_rsrc(vfs_handle_struct *handle,
-                            const struct smb_filename *smb_fname,
-                            bool force_unlink)
+                       struct files_struct *dirfsp,
+                       const struct smb_filename *smb_fname,
+                       bool force_unlink)
 {
        struct fruit_config_data *config = NULL;
        int rc;
@@ -4069,11 +1946,17 @@ static int fruit_unlink_rsrc(vfs_handle_struct *handle,
 
        switch (config->rsrc) {
        case FRUIT_RSRC_STREAM:
-               rc = fruit_unlink_rsrc_stream(handle, smb_fname, force_unlink);
+               rc = fruit_unlink_rsrc_stream(handle,
+                               dirfsp,
+                               smb_fname,
+                               force_unlink);
                break;
 
        case FRUIT_RSRC_ADFILE:
-               rc = fruit_unlink_rsrc_adouble(handle, smb_fname, force_unlink);
+               rc = fruit_unlink_rsrc_adouble(handle,
+                               dirfsp,
+                               smb_fname,
+                               force_unlink);
                break;
 
        case FRUIT_RSRC_XATTR:
@@ -4088,53 +1971,6 @@ static int fruit_unlink_rsrc(vfs_handle_struct *handle,
        return rc;
 }
 
-static int fruit_unlink(vfs_handle_struct *handle,
-                       const struct smb_filename *smb_fname)
-{
-       int rc;
-       struct fruit_config_data *config = NULL;
-       struct smb_filename *rsrc_smb_fname = NULL;
-
-       SMB_VFS_HANDLE_GET_DATA(handle, config,
-                               struct fruit_config_data, return -1);
-
-       if (is_afpinfo_stream(smb_fname)) {
-               return fruit_unlink_meta(handle, smb_fname);
-       } else if (is_afpresource_stream(smb_fname)) {
-               return fruit_unlink_rsrc(handle, smb_fname, false);
-       } else if (is_ntfs_stream_smb_fname(smb_fname)) {
-               return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
-       } else if (is_adouble_file(smb_fname->base_name)) {
-               return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
-       }
-
-       /*
-        * A request to delete the base file. Because 0 byte resource
-        * fork streams are not listed by fruit_streaminfo,
-        * delete_all_streams() can't remove 0 byte resource fork
-        * streams, so we have to cleanup this here.
-        */
-       rsrc_smb_fname = synthetic_smb_fname(talloc_tos(),
-                                            smb_fname->base_name,
-                                            AFPRESOURCE_STREAM_NAME,
-                                            NULL,
-                                            smb_fname->flags);
-       if (rsrc_smb_fname == NULL) {
-               return -1;
-       }
-
-       rc = fruit_unlink_rsrc(handle, rsrc_smb_fname, true);
-       if ((rc != 0) && (errno != ENOENT)) {
-               DBG_ERR("Forced unlink of [%s] failed [%s]\n",
-                       smb_fname_str_dbg(rsrc_smb_fname), strerror(errno));
-               TALLOC_FREE(rsrc_smb_fname);
-               return -1;
-       }
-       TALLOC_FREE(rsrc_smb_fname);
-
-       return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
-}
-
 static int fruit_chmod(vfs_handle_struct *handle,
                       const struct smb_filename *smb_fname,
                       mode_t mode)
@@ -4179,127 +2015,77 @@ static int fruit_chmod(vfs_handle_struct *handle,
        return rc;
 }
 
-static int fruit_chown(vfs_handle_struct *handle,
-                      const struct smb_filename *smb_fname,
-                      uid_t uid,
-                      gid_t gid)
+static int fruit_unlinkat(vfs_handle_struct *handle,
+                       struct files_struct *dirfsp,
+                       const struct smb_filename *smb_fname,
+                       int flags)
 {
-       int rc = -1;
        struct fruit_config_data *config = NULL;
-       struct smb_filename *adp_smb_fname = NULL;
-
-       rc = SMB_VFS_NEXT_CHOWN(handle, smb_fname, uid, gid);
-       if (rc != 0) {
-               return rc;
-       }
-
-       SMB_VFS_HANDLE_GET_DATA(handle, config,
-                               struct fruit_config_data, return -1);
-
-       if (config->rsrc != FRUIT_RSRC_ADFILE) {
-               return 0;
-       }
-
-       if (!VALID_STAT(smb_fname->st)) {
-               return 0;
-       }
-
-       if (!S_ISREG(smb_fname->st.st_ex_mode)) {
-               return 0;
-       }
-
-       rc = adouble_path(talloc_tos(), smb_fname, &adp_smb_fname);
-       if (rc != 0) {
-               goto done;
-       }
+       struct smb_filename *rsrc_smb_fname = NULL;
+       int ret;
 
-       DEBUG(10, ("fruit_chown: %s\n", adp_smb_fname->base_name));
+       SMB_ASSERT(dirfsp == dirfsp->conn->cwd_fsp);
 
-       rc = SMB_VFS_NEXT_CHOWN(handle, adp_smb_fname, uid, gid);
-       if (errno == ENOENT) {
-               rc = 0;
+       if (flags & AT_REMOVEDIR) {
+               return SMB_VFS_NEXT_UNLINKAT(handle,
+                                            dirfsp,
+                                            smb_fname,
+                                            AT_REMOVEDIR);
        }
 
- done:
-       TALLOC_FREE(adp_smb_fname);
-       return rc;
-}
-
-static int fruit_rmdir(struct vfs_handle_struct *handle,
-                       const struct smb_filename *smb_fname)
-{
-       DIR *dh = NULL;
-       struct dirent *de;
-       struct fruit_config_data *config;
-
        SMB_VFS_HANDLE_GET_DATA(handle, config,
                                struct fruit_config_data, return -1);
 
-       if (config->rsrc != FRUIT_RSRC_ADFILE) {
-               goto exit_rmdir;
+       if (is_afpinfo_stream(smb_fname->stream_name)) {
+               return fruit_unlink_meta(handle,
+                               dirfsp,
+                               smb_fname);
+       } else if (is_afpresource_stream(smb_fname->stream_name)) {
+               return fruit_unlink_rsrc(handle,
+                               dirfsp,
+                               smb_fname,
+                               false);
+       } else if (is_named_stream(smb_fname)) {
+               return SMB_VFS_NEXT_UNLINKAT(handle,
+                               dirfsp,
+                               smb_fname,
+                               0);
+       } else if (is_adouble_file(smb_fname->base_name)) {
+               return SMB_VFS_NEXT_UNLINKAT(handle,
+                               dirfsp,
+                               smb_fname,
+                               0);
        }
 
        /*
-        * Due to there is no way to change bDeleteVetoFiles variable
-        * from this module, need to clean up ourselves
+        * A request to delete the base file. Because 0 byte resource
+        * fork streams are not listed by fruit_streaminfo,
+        * delete_all_streams() can't remove 0 byte resource fork
+        * streams, so we have to cleanup this here.
         */
-
-       dh = SMB_VFS_OPENDIR(handle->conn, smb_fname, NULL, 0);
-       if (dh == NULL) {
-               goto exit_rmdir;
+       rsrc_smb_fname = synthetic_smb_fname(talloc_tos(),
+                                            smb_fname->base_name,
+                                            AFPRESOURCE_STREAM_NAME,
+                                            NULL,
+                                            smb_fname->twrp,
+                                            smb_fname->flags);
+       if (rsrc_smb_fname == NULL) {
+               return -1;
        }
 
-       while ((de = SMB_VFS_READDIR(handle->conn, dh, NULL)) != NULL) {
-               struct adouble *ad = NULL;
-               char *p = NULL;
-               struct smb_filename *ad_smb_fname = NULL;
-               int ret;
-
-               if (!is_adouble_file(de->d_name)) {
-                       continue;
-               }
-
-               p = talloc_asprintf(talloc_tos(), "%s/%s",
-                                   smb_fname->base_name, de->d_name);
-               if (p == NULL) {
-                       DBG_ERR("talloc_asprintf failed\n");
-                       return -1;
-               }
-
-               ad_smb_fname = synthetic_smb_fname(talloc_tos(), p,
-                                                   NULL, NULL,
-                                                   smb_fname->flags);
-               TALLOC_FREE(p);
-               if (ad_smb_fname == NULL) {
-                       DBG_ERR("synthetic_smb_fname failed\n");
-                       return -1;
-               }
-
-               /*
-                * Check whether it's a valid AppleDouble file, if
-                * yes, delete it, ignore it otherwise.
-                */
-               ad = ad_get(talloc_tos(), handle, ad_smb_fname, ADOUBLE_RSRC);
-               if (ad == NULL) {
-                       TALLOC_FREE(ad_smb_fname);
-                       TALLOC_FREE(p);
-                       continue;
-               }
-               TALLOC_FREE(ad);
-
-               ret = SMB_VFS_NEXT_UNLINK(handle, ad_smb_fname);
-               if (ret != 0) {
-                       DBG_ERR("Deleting [%s] failed\n",
-                               smb_fname_str_dbg(ad_smb_fname));
-               }
-               TALLOC_FREE(ad_smb_fname);
+       ret = fruit_unlink_rsrc(handle, dirfsp, rsrc_smb_fname, true);
+       if ((ret != 0) && (errno != ENOENT)) {
+               DBG_ERR("Forced unlink of [%s] failed [%s]\n",
+                       smb_fname_str_dbg(rsrc_smb_fname), strerror(errno));
+               TALLOC_FREE(rsrc_smb_fname);
+               return -1;
        }
+       TALLOC_FREE(rsrc_smb_fname);
 
-exit_rmdir:
-       if (dh) {
-               SMB_VFS_CLOSEDIR(handle->conn, dh);
-       }
-       return SMB_VFS_NEXT_RMDIR(handle, smb_fname);
+       return SMB_VFS_NEXT_UNLINKAT(handle,
+                       dirfsp,
+                       smb_fname,
+                       0);
 }
 
 static ssize_t fruit_pread_meta_stream(vfs_handle_struct *handle,
@@ -4317,7 +2103,10 @@ static ssize_t fruit_pread_meta_stream(vfs_handle_struct *handle,
        DBG_ERR("Removing [%s] after short read [%zd]\n",
                fsp_str_dbg(fsp), nread);
 
-       ret = SMB_VFS_NEXT_UNLINK(handle, fsp->fsp_name);
+       ret = SMB_VFS_NEXT_UNLINKAT(handle,
+                       fsp->conn->cwd_fsp,
+                       fsp->fsp_name,
+                       0);
        if (ret != 0) {
                DBG_ERR("Removing [%s] failed\n", fsp_str_dbg(fsp));
                return -1;
@@ -4722,7 +2511,7 @@ static ssize_t fruit_pwrite_meta_netatalk(vfs_handle_struct *handle,
 
        ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
        if (ad == NULL) {
-               ad = ad_init(talloc_tos(), handle, ADOUBLE_META);
+               ad = ad_init(talloc_tos(), ADOUBLE_META);
                if (ad == NULL) {
                        return -1;
                }
@@ -5077,7 +2866,7 @@ static int fruit_stat_meta_stream(vfs_handle_struct *handle,
                return -1;
        }
 
-       ino = fruit_inode(&smb_fname->st, smb_fname->stream_name);
+       ino = hash_inode(&smb_fname->st, smb_fname->stream_name);
 
        if (follow_links) {
                ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
@@ -5110,7 +2899,7 @@ static int fruit_stat_meta_netatalk(vfs_handle_struct *handle,
                return -1;
        }
        smb_fname->st.st_ex_size = AFP_INFO_SIZE;
-       smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
+       smb_fname->st.st_ex_ino = hash_inode(&smb_fname->st,
                                              smb_fname->stream_name);
        return 0;
 }
@@ -5163,7 +2952,7 @@ static int fruit_stat_rsrc_netatalk(vfs_handle_struct *handle,
        }
 
        smb_fname->st.st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
-       smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
+       smb_fname->st.st_ex_ino = hash_inode(&smb_fname->st,
                                              smb_fname->stream_name);
        TALLOC_FREE(ad);
        return 0;
@@ -5215,8 +3004,8 @@ static int fruit_stat_rsrc_xattr(vfs_handle_struct *handle,
        close(fd);
        fd = -1;
 
-       smb_fname->st.st_ex_ino = fruit_inode(&smb_fname->st,
-                                             smb_fname->stream_name);
+       smb_fname->st.st_ex_ino = hash_inode(&smb_fname->st,
+                                            smb_fname->stream_name);
 
        return ret;
 
@@ -5267,8 +3056,7 @@ static int fruit_stat(vfs_handle_struct *handle,
        DEBUG(10, ("fruit_stat called for %s\n",
                   smb_fname_str_dbg(smb_fname)));
 
-       if (!is_ntfs_stream_smb_fname(smb_fname)
-           || is_ntfs_default_stream_smb_fname(smb_fname)) {
+       if (!is_named_stream(smb_fname)) {
                rc = SMB_VFS_NEXT_STAT(handle, smb_fname);
                if (rc == 0) {
                        update_btime(handle, smb_fname);
@@ -5283,9 +3071,9 @@ static int fruit_stat(vfs_handle_struct *handle,
         * not following links here.
         */
 
-       if (is_afpinfo_stream(smb_fname)) {
+       if (is_afpinfo_stream(smb_fname->stream_name)) {
                rc = fruit_stat_meta(handle, smb_fname, true);
-       } else if (is_afpresource_stream(smb_fname)) {
+       } else if (is_afpresource_stream(smb_fname->stream_name)) {
                rc = fruit_stat_rsrc(handle, smb_fname, true);
        } else {
                return SMB_VFS_NEXT_STAT(handle, smb_fname);
@@ -5309,8 +3097,7 @@ static int fruit_lstat(vfs_handle_struct *handle,
        DEBUG(10, ("fruit_lstat called for %s\n",
                   smb_fname_str_dbg(smb_fname)));
 
-       if (!is_ntfs_stream_smb_fname(smb_fname)
-           || is_ntfs_default_stream_smb_fname(smb_fname)) {
+       if (!is_named_stream(smb_fname)) {
                rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
                if (rc == 0) {
                        update_btime(handle, smb_fname);
@@ -5318,9 +3105,9 @@ static int fruit_lstat(vfs_handle_struct *handle,
                return rc;
        }
 
-       if (is_afpinfo_stream(smb_fname)) {
+       if (is_afpinfo_stream(smb_fname->stream_name)) {
                rc = fruit_stat_meta(handle, smb_fname, false);
-       } else if (is_afpresource_stream(smb_fname)) {
+       } else if (is_afpresource_stream(smb_fname->stream_name)) {
                rc = fruit_stat_rsrc(handle, smb_fname, false);
        } else {
                return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
@@ -5357,7 +3144,7 @@ static int fruit_fstat_meta_stream(vfs_handle_struct *handle,
 
                *sbuf = fsp->base_fsp->fsp_name->st;
                sbuf->st_ex_size = AFP_INFO_SIZE;
-               sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
+               sbuf->st_ex_ino = hash_inode(sbuf, fsp->fsp_name->stream_name);
                return 0;
        }
 
@@ -5371,7 +3158,7 @@ static int fruit_fstat_meta_stream(vfs_handle_struct *handle,
        }
        *sbuf = smb_fname.st;
 
-       ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
+       ino = hash_inode(sbuf, fsp->fsp_name->stream_name);
 
        ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
        if (ret != 0) {
@@ -5395,7 +3182,7 @@ static int fruit_fstat_meta_netatalk(vfs_handle_struct *handle,
 
        *sbuf = fsp->base_fsp->fsp_name->st;
        sbuf->st_ex_size = AFP_INFO_SIZE;
-       sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
+       sbuf->st_ex_ino = hash_inode(sbuf, fsp->fsp_name->stream_name);
 
        return 0;
 }
@@ -5465,7 +3252,7 @@ static int fruit_fstat_rsrc_adouble(vfs_handle_struct *handle,
 
        *sbuf = fsp->base_fsp->fsp_name->st;
        sbuf->st_ex_size = ad_getentrylen(ad, ADEID_RFORK);
-       sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
+       sbuf->st_ex_ino = hash_inode(sbuf, fsp->fsp_name->stream_name);
 
        TALLOC_FREE(ad);
        return 0;
@@ -5550,12 +3337,17 @@ static NTSTATUS delete_invalid_meta_stream(
        sname = synthetic_smb_fname(talloc_tos(),
                                    smb_fname->base_name,
                                    AFPINFO_STREAM_NAME,
-                                   NULL, 0);
+                                   NULL,
+                                   smb_fname->twrp,
+                                   0);
        if (sname == NULL) {
                return NT_STATUS_NO_MEMORY;
        }
 
-       ret = SMB_VFS_NEXT_UNLINK(handle, sname);
+       ret = SMB_VFS_NEXT_UNLINKAT(handle,
+                       handle->conn->cwd_fsp,
+                       sname,
+                       0);
        TALLOC_FREE(sname);
        if (ret != 0) {
                DBG_ERR("Removing [%s] failed\n", smb_fname_str_dbg(sname));
@@ -5914,7 +3706,7 @@ static int fruit_ntimes(vfs_handle_struct *handle,
                                return -1);
 
        if ((config->meta != FRUIT_META_NETATALK) ||
-           null_timespec(ft->create_time))
+           is_omit_timespec(&ft->create_time))
        {
                return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
        }
@@ -6099,7 +3891,7 @@ static NTSTATUS fruit_create_file(vfs_handle_struct *handle,
                                  uint32_t create_options,
                                  uint32_t file_attributes,
                                  uint32_t oplock_request,
-                                 struct smb2_lease *lease,
+                                 const struct smb2_lease *lease,
                                  uint64_t allocation_size,
                                  uint32_t private_flags,
                                  struct security_descriptor *sd,
@@ -6124,8 +3916,21 @@ static NTSTATUS fruit_create_file(vfs_handle_struct *handle,
        SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
                                return NT_STATUS_UNSUCCESSFUL);
 
-       if (is_apple_stream(smb_fname) && !internal_open) {
-               ret = ad_convert(handle, smb_fname);
+       if (is_apple_stream(smb_fname->stream_name) && !internal_open) {
+               uint32_t conv_flags  = 0;
+
+               if (config->wipe_intentionally_left_blank_rfork) {
+                       conv_flags |= AD_CONV_WIPE_BLANK;
+               }
+               if (config->delete_empty_adfiles) {
+                       conv_flags |= AD_CONV_DELETE;
+               }
+
+               ret = ad_convert(handle,
+                                handle->conn->cwd_fsp,
+                                smb_fname,
+                                macos_string_replace_map,
+                                conv_flags);
                if (ret != 0) {
                        DBG_ERR("ad_convert() failed\n");
                        return NT_STATUS_UNSUCCESSFUL;
@@ -6148,7 +3953,7 @@ static NTSTATUS fruit_create_file(vfs_handle_struct *handle,
        fsp = *result;
 
        if (global_fruit_config.nego_aapl) {
-               if (config->posix_rename && fsp->is_directory) {
+               if (config->posix_rename && fsp->fsp_flags.is_directory) {
                        /*
                         * Enable POSIX directory rename behaviour
                         */
@@ -6166,8 +3971,7 @@ static NTSTATUS fruit_create_file(vfs_handle_struct *handle,
        if (global_fruit_config.nego_aapl &&
            create_disposition == FILE_OPEN &&
            smb_fname->st.st_ex_size == 0 &&
-           is_ntfs_stream_smb_fname(smb_fname) &&
-           !(is_ntfs_default_stream_smb_fname(smb_fname)))
+           is_named_stream(smb_fname))
        {
                status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
                goto fail;
@@ -6178,12 +3982,13 @@ static NTSTATUS fruit_create_file(vfs_handle_struct *handle,
                fio->created = true;
        }
 
-       if (is_ntfs_stream_smb_fname(smb_fname)
-           || fsp->is_directory) {
+       if (is_named_stream(smb_fname) || fsp->fsp_flags.is_directory) {
                return status;
        }
 
-       if (config->locking == FRUIT_LOCKING_NETATALK) {
+       if ((config->locking == FRUIT_LOCKING_NETATALK) &&
+           (fsp->op != NULL))
+       {
                status = fruit_check_access(
                        handle, *result,
                        access_mask,
@@ -6213,6 +4018,7 @@ static NTSTATUS fruit_readdir_attr(struct vfs_handle_struct *handle,
 {
        struct fruit_config_data *config = NULL;
        struct readdir_attr_data *attr_data;
+       uint32_t conv_flags  = 0;
        NTSTATUS status;
        int ret;
 
@@ -6226,7 +4032,18 @@ static NTSTATUS fruit_readdir_attr(struct vfs_handle_struct *handle,
 
        DEBUG(10, ("fruit_readdir_attr %s\n", fname->base_name));
 
-       ret = ad_convert(handle, fname);
+       if (config->wipe_intentionally_left_blank_rfork) {
+               conv_flags |= AD_CONV_WIPE_BLANK;
+       }
+       if (config->delete_empty_adfiles) {
+               conv_flags |= AD_CONV_DELETE;
+       }
+
+       ret = ad_convert(handle,
+                       handle->conn->cwd_fsp,
+                       fname,
+                       macos_string_replace_map,
+                       conv_flags);
        if (ret != 0) {
                DBG_ERR("ad_convert() failed\n");
                return NT_STATUS_UNSUCCESSFUL;
@@ -6271,6 +4088,7 @@ static NTSTATUS fruit_readdir_attr(struct vfs_handle_struct *handle,
        } else {
                status = smbd_calculate_access_mask(
                        handle->conn,
+                       handle->conn->cwd_fsp,
                        fname,
                        false,
                        SEC_FLAG_MAXIMUM_ALLOWED,
@@ -6411,18 +4229,13 @@ static NTSTATUS fruit_fset_nt_acl(vfs_handle_struct *handle,
        }
 
        if (do_chmod) {
-               if (fsp->fh->fd != -1) {
-                       result = SMB_VFS_FCHMOD(fsp, ms_nfs_mode);
-               } else {
-                       result = SMB_VFS_CHMOD(fsp->conn,
-                                              fsp->fsp_name,
-                                              ms_nfs_mode);
-               }
-
+               result = SMB_VFS_FCHMOD(fsp, ms_nfs_mode);
                if (result != 0) {
-                       DEBUG(1, ("chmod: %s, result: %d, %04o error %s\n", fsp_str_dbg(fsp),
-                                 result, (unsigned)ms_nfs_mode,
-                                 strerror(errno)));
+                       DBG_WARNING("%s, result: %d, %04o error %s\n",
+                               fsp_str_dbg(fsp),
+                               result,
+                               (unsigned)ms_nfs_mode,
+                               strerror(errno));
                        status = map_nt_error_from_unix(errno);
                        TALLOC_FREE(psd);
                        return status;
@@ -6685,6 +4498,7 @@ static void fruit_offload_write_done(struct tevent_req *subreq)
                        state->src_fsp->fsp_name->base_name,
                        streams[i].name,
                        NULL,
+                       state->src_fsp->fsp_name->twrp,
                        state->src_fsp->fsp_name->flags);
                if (tevent_req_nomem(src_fname_tmp, req)) {
                        return;
@@ -6700,6 +4514,7 @@ static void fruit_offload_write_done(struct tevent_req *subreq)
                        state->dst_fsp->fsp_name->base_name,
                        streams[i].name,
                        NULL,
+                       state->dst_fsp->fsp_name->twrp,
                        state->dst_fsp->fsp_name->flags);
                if (tevent_req_nomem(dst_fname_tmp, req)) {
                        TALLOC_FREE(src_fname_tmp);
@@ -6859,7 +4674,12 @@ static bool fruit_get_bandsize(vfs_handle_struct *handle,
                goto out;
        }
 
-       smb_fname = synthetic_smb_fname(talloc_tos(), plist, NULL, NULL, 0);
+       smb_fname = synthetic_smb_fname(talloc_tos(),
+                                       plist,
+                                       NULL,
+                                       NULL,
+                                       0,
+                                       0);
        if (smb_fname == NULL) {
                ok = false;
                goto out;
@@ -6974,15 +4794,16 @@ struct fruit_disk_free_state {
 };
 
 static bool fruit_get_num_bands(vfs_handle_struct *handle,
-                               char *bundle,
+                               const char *bundle,
                                size_t *_nbands)
 {
        char *path = NULL;
        struct smb_filename *bands_dir = NULL;
-       DIR *d = NULL;
-       struct dirent *e = NULL;
+       struct smb_Dir *dir_hnd = NULL;
+       const char *dname = NULL;
+       char *talloced = NULL;
+       long offset = 0;
        size_t nbands;
-       int ret;
 
        path = talloc_asprintf(talloc_tos(),
                               "%s/%s/bands",
@@ -6996,35 +4817,30 @@ static bool fruit_get_num_bands(vfs_handle_struct *handle,
                                        path,
                                        NULL,
                                        NULL,
+                                       0,
                                        0);
        TALLOC_FREE(path);
        if (bands_dir == NULL) {
                return false;
        }
 
-       d = SMB_VFS_NEXT_OPENDIR(handle, bands_dir, NULL, 0);
-       if (d == NULL) {
+       dir_hnd = OpenDir(talloc_tos(), handle->conn, bands_dir, NULL, 0);
+       if (dir_hnd == NULL) {
                TALLOC_FREE(bands_dir);
                return false;
        }
 
        nbands = 0;
 
-       for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
-            e != NULL;
-            e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
+        while ((dname = ReadDirName(dir_hnd, &offset, NULL, &talloced))
+              != NULL)
        {
-               if (ISDOT(e->d_name) || ISDOTDOT(e->d_name)) {
+               if (ISDOT(dname) || ISDOTDOT(dname)) {
                        continue;
                }
                nbands++;
        }
-
-       ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
-       if (ret != 0) {
-               TALLOC_FREE(bands_dir);
-               return false;
-       }
+       TALLOC_FREE(dir_hnd);
 
        DBG_DEBUG("%zu bands in [%s]\n", nbands, smb_fname_str_dbg(bands_dir));
 
@@ -7036,7 +4852,7 @@ static bool fruit_get_num_bands(vfs_handle_struct *handle,
 
 static bool fruit_tmsize_do_dirent(vfs_handle_struct *handle,
                                   struct fruit_disk_free_state *state,
-                                  struct dirent *e)
+                                  const char *name)
 {
        bool ok;
        char *p = NULL;
@@ -7045,7 +4861,7 @@ static bool fruit_tmsize_do_dirent(vfs_handle_struct *handle,
        size_t nbands;
        off_t tm_size;
 
-       p = strstr(e->d_name, "sparsebundle");
+       p = strstr(name, "sparsebundle");
        if (p == NULL) {
                return true;
        }
@@ -7054,39 +4870,45 @@ static bool fruit_tmsize_do_dirent(vfs_handle_struct *handle,
                return true;
        }
 
-       DBG_DEBUG("Processing sparsebundle [%s]\n", e->d_name);
+       DBG_DEBUG("Processing sparsebundle [%s]\n", name);
 
-       ok = fruit_get_bandsize(handle, e->d_name, &bandsize);
+       ok = fruit_get_bandsize(handle, name, &bandsize);
        if (!ok) {
                /*
                 * Beware of race conditions: this may be an uninitialized
                 * Info.plist that a client is just creating. We don't want let
                 * this to trigger complete failure.
                 */
-               DBG_ERR("Processing sparsebundle [%s] failed\n", e->d_name);
+               DBG_ERR("Processing sparsebundle [%s] failed\n", name);
                return true;
        }
 
-       ok = fruit_get_num_bands(handle, e->d_name, &nbands);
+       ok = fruit_get_num_bands(handle, name, &nbands);
        if (!ok) {
                /*
                 * Beware of race conditions: this may be a backup sparsebundle
                 * in an early stage lacking a bands subdirectory. We don't want
                 * let this to trigger complete failure.
                 */
-               DBG_ERR("Processing sparsebundle [%s] failed\n", e->d_name);
+               DBG_ERR("Processing sparsebundle [%s] failed\n", name);
                return true;
        }
 
+       /*
+        * Arithmetic on 32-bit systems may cause overflow, depending on
+        * size_t precision. First we check its unlikely, then we
+        * force the precision into target off_t, then we check that
+        * the total did not overflow either.
+        */
        if (bandsize > SIZE_MAX/nbands) {
-               DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
+               DBG_ERR("tmsize potential overflow: bandsize [%zu] nbands [%zu]\n",
                        bandsize, nbands);
                return false;
        }
-       tm_size = bandsize * nbands;
+       tm_size = (off_t)bandsize * (off_t)nbands;
 
        if (state->total_size + tm_size < state->total_size) {
-               DBG_ERR("tmsize overflow: bandsize [%zu] nbands [%zu]\n",
+               DBG_ERR("tm total size overflow: bandsize [%zu] nbands [%zu]\n",
                        bandsize, nbands);
                return false;
        }
@@ -7094,7 +4916,7 @@ static bool fruit_tmsize_do_dirent(vfs_handle_struct *handle,
        state->total_size += tm_size;
 
        DBG_DEBUG("[%s] tm_size [%jd] total_size [%jd]\n",
-                 e->d_name, (intmax_t)tm_size, (intmax_t)state->total_size);
+                 name, (intmax_t)tm_size, (intmax_t)state->total_size);
 
        return true;
 }
@@ -7118,11 +4940,12 @@ static uint64_t fruit_disk_free(vfs_handle_struct *handle,
 {
        struct fruit_config_data *config = NULL;
        struct fruit_disk_free_state state = {0};
-       DIR *d = NULL;
-       struct dirent *e = NULL;
+       struct smb_Dir *dir_hnd = NULL;
+       const char *dname = NULL;
+       char *talloced = NULL;
+       long offset = 0;
        uint64_t dfree;
        uint64_t dsize;
-       int ret;
        bool ok;
 
        SMB_VFS_HANDLE_GET_DATA(handle, config,
@@ -7139,26 +4962,24 @@ static uint64_t fruit_disk_free(vfs_handle_struct *handle,
                                              _dsize);
        }
 
-       d = SMB_VFS_NEXT_OPENDIR(handle, smb_fname, NULL, 0);
-       if (d == NULL) {
+       dir_hnd = OpenDir(talloc_tos(), handle->conn, smb_fname, NULL, 0);
+       if (dir_hnd == NULL) {
                return UINT64_MAX;
        }
 
-       for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
-            e != NULL;
-            e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
+        while ((dname = ReadDirName(dir_hnd, &offset, NULL, &talloced))
+              != NULL)
        {
-               ok = fruit_tmsize_do_dirent(handle, &state, e);
+               ok = fruit_tmsize_do_dirent(handle, &state, dname);
                if (!ok) {
-                       SMB_VFS_NEXT_CLOSEDIR(handle, d);
+                       TALLOC_FREE(talloced);
+                       TALLOC_FREE(dir_hnd);
                        return UINT64_MAX;
                }
+               TALLOC_FREE(talloced);
        }
 
-       ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
-       if (ret != 0) {
-               return UINT64_MAX;
-       }
+       TALLOC_FREE(dir_hnd);
 
        dsize = config->time_machine_max_size / 512;
        dfree = dsize - (state.total_size / 512);
@@ -7172,16 +4993,32 @@ static uint64_t fruit_disk_free(vfs_handle_struct *handle,
        return dfree / 2;
 }
 
+static uint64_t fruit_fs_file_id(struct vfs_handle_struct *handle,
+                                const SMB_STRUCT_STAT *psbuf)
+{
+       struct fruit_config_data *config = NULL;
+
+       SMB_VFS_HANDLE_GET_DATA(handle, config,
+                               struct fruit_config_data,
+                               return 0);
+
+       if (global_fruit_config.nego_aapl &&
+           config->aapl_zero_file_id)
+       {
+               return 0;
+       }
+
+       return SMB_VFS_NEXT_FS_FILE_ID(handle, psbuf);
+}
+
 static struct vfs_fn_pointers vfs_fruit_fns = {
        .connect_fn = fruit_connect,
        .disk_free_fn = fruit_disk_free,
 
        /* File operations */
        .chmod_fn = fruit_chmod,
-       .chown_fn = fruit_chown,
-       .unlink_fn = fruit_unlink,
-       .rename_fn = fruit_rename,
-       .rmdir_fn = fruit_rmdir,
+       .unlinkat_fn = fruit_unlinkat,
+       .renameat_fn = fruit_renameat,
        .open_fn = fruit_open,
        .close_fn = fruit_close,
        .pread_fn = fruit_pread,
@@ -7203,6 +5040,7 @@ static struct vfs_fn_pointers vfs_fruit_fns = {
        .offload_read_recv_fn = fruit_offload_read_recv,
        .offload_write_send_fn = fruit_offload_write_send,
        .offload_write_recv_fn = fruit_offload_write_recv,
+       .fs_file_id_fn = fruit_fs_file_id,
 
        /* NT ACL operations */
        .fget_nt_acl_fn = fruit_fget_nt_acl,