ctdb-failover: Split statd_callout add-client/del-client
[samba.git] / source3 / modules / vfs_fruit.c
index 4b78e391f25aef2a6386baf0003af9e1a2c3dffb..b63fff7f95d218b6f441692e4a18aff3031790b5 100644 (file)
 #include "../libcli/smb/smb2_create_ctx.h"
 #include "lib/util/tevent_ntstatus.h"
 #include "lib/util/tevent_unix.h"
+#include "lib/util/util_file.h"
 #include "offload_token.h"
 #include "string_replace.h"
 #include "hash_inode.h"
 #include "lib/adouble.h"
 #include "lib/util_macstreams.h"
+#include "source3/smbd/dir.h"
 
 /*
  * Enhanced OS X and Netatalk compatibility
@@ -131,8 +133,10 @@ struct fruit_config_data {
        const char *model;
        bool time_machine;
        off_t time_machine_max_size;
+       bool convert_adouble;
        bool wipe_intentionally_left_blank_rfork;
        bool delete_empty_adfiles;
+       bool validate_afpinfo;
 
        /*
         * Additional options, all enabled by default,
@@ -144,6 +148,8 @@ struct fruit_config_data {
        bool readdir_attr_rsize;
        bool readdir_attr_finder_info;
        bool readdir_attr_max_access;
+       /* Recursion guard. Will go away when we have STATX. */
+       bool in_openat_pathref_fsp;
 };
 
 static const struct enum_list fruit_rsrc[] = {
@@ -172,15 +178,20 @@ static const struct enum_list fruit_encoding[] = {
 };
 
 struct fio {
+       vfs_handle_struct *handle;
+       files_struct *fsp; /* backlink to itself */
+
        /* tcon config handle */
        struct fruit_config_data *config;
 
+       /* Backend fsp for AppleDouble file, can be NULL */
+       files_struct *ad_fsp;
+       /* link from adouble_open_from_base_fsp() to fio */
+       struct fio *real_fio;
+
        /* Denote stream type, meta or rsrc */
        adouble_type_t type;
 
-       /* Whether the create created the stream */
-       bool created;
-
        /*
         * AFP_AfpInfo stream created, but not written yet, thus still a fake
         * pipe fd. This is set to true in fruit_open_meta if there was no
@@ -197,13 +208,76 @@ struct fio {
  * Helper functions
  *****************************************************************************/
 
+static struct adouble *ad_get_meta_fsp(TALLOC_CTX *ctx,
+                                      vfs_handle_struct *handle,
+                                      const struct smb_filename *smb_fname)
+{
+       NTSTATUS status;
+       struct adouble *ad = NULL;
+       struct smb_filename *smb_fname_cp = NULL;
+       struct fruit_config_data *config = NULL;
+
+       if (smb_fname->fsp != NULL) {
+               return ad_get(ctx, handle, smb_fname, ADOUBLE_META);
+       }
+
+       SMB_VFS_HANDLE_GET_DATA(handle,
+                               config,
+                               struct fruit_config_data,
+                               return NULL);
+
+       if (config->in_openat_pathref_fsp) {
+               return NULL;
+       }
+
+       smb_fname_cp = cp_smb_filename(ctx,
+                                      smb_fname);
+       if (smb_fname_cp == NULL) {
+               return NULL;
+       }
+       TALLOC_FREE(smb_fname_cp->stream_name);
+       config->in_openat_pathref_fsp = true;
+       status = openat_pathref_fsp(handle->conn->cwd_fsp,
+                                   smb_fname_cp);
+       config->in_openat_pathref_fsp = false;
+       if (!NT_STATUS_IS_OK(status)) {
+               TALLOC_FREE(smb_fname_cp);
+               return NULL;
+       }
+
+       ad = ad_get(ctx, handle, smb_fname_cp, ADOUBLE_META);
+       TALLOC_FREE(smb_fname_cp);
+       return ad;
+}
+
+static struct fio *fruit_get_complete_fio(vfs_handle_struct *handle,
+                                         files_struct *fsp)
+{
+       struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+
+       if (fio == NULL) {
+               return NULL;
+       }
+
+       if (fio->real_fio != NULL) {
+               /*
+                * This is an fsp from adouble_open_from_base_fsp()
+                * we should just pass this to the next
+                * module.
+                */
+               return NULL;
+       }
+
+       return fio;
+}
+
 /**
  * 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;
+       int enumval = -1;
        const char *tm_size_str = NULL;
 
        config = talloc_zero(handle->conn, struct fruit_config_data);
@@ -213,25 +287,8 @@ static int init_fruit_config(vfs_handle_struct *handle)
                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);
+                              "resource", fruit_rsrc, FRUIT_RSRC_ADFILE);
        if (enumval == -1) {
                DEBUG(1, ("value for %s: resource type unknown\n",
                          FRUIT_PARAM_TYPE_NAME));
@@ -290,7 +347,7 @@ static int init_fruit_config(vfs_handle_struct *handle)
 
        config->aapl_zero_file_id =
            lp_parm_bool(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
-                        "zero_file_id", false);
+                        "zero_file_id", true);
 
        config->readdir_attr_rsize = lp_parm_bool(
                SNUM(handle->conn), "readdir_attr", "aapl_rsize", true);
@@ -311,6 +368,10 @@ static int init_fruit_config(vfs_handle_struct *handle)
                config->time_machine_max_size = conv_str_size(tm_size_str);
        }
 
+       config->convert_adouble = lp_parm_bool(
+               SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
+               "convert_adouble", true);
+
        config->wipe_intentionally_left_blank_rfork = lp_parm_bool(
                SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
                "wipe_intentionally_left_blank_rfork", false);
@@ -319,6 +380,10 @@ static int init_fruit_config(vfs_handle_struct *handle)
                SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
                "delete_empty_adfiles", false);
 
+       config->validate_afpinfo = lp_parm_bool(
+               SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
+               "validate_afpinfo", true);
+
        SMB_VFS_HANDLE_SET_DATA(handle, config,
                                NULL, struct fruit_config_data,
                                return -1);
@@ -377,11 +442,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;
 }
@@ -408,11 +469,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;
 }
@@ -467,7 +524,7 @@ static void update_btime(vfs_handle_struct *handle,
                return;
        }
 
-       ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
+       ad = ad_get_meta_fsp(talloc_tos(), handle, smb_fname);
        if (ad == NULL) {
                return;
        }
@@ -596,7 +653,7 @@ static NTSTATUS fruit_check_access(vfs_handle_struct *handle,
                  access_mask & FILE_WRITE_DATA ? "WRITE" : "-",
                  share_mode);
 
-       if (fsp->fh->fd == -1) {
+       if (fsp_get_io_fd(fsp) == -1) {
                return NT_STATUS_OK;
        }
 
@@ -787,7 +844,7 @@ static NTSTATUS check_aapl(vfs_handle_struct *handle,
 
        if (req_bitmap & SMB2_CRTCTX_AAPL_SERVER_CAPS) {
                if ((client_caps & SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR) &&
-                   (handle->conn->tcon->compat->fs_capabilities & FILE_NAMED_STREAMS)) {
+                   (handle->conn->fs_capabilities & FILE_NAMED_STREAMS)) {
                        server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR;
                        config->readdir_attr_enabled = true;
                }
@@ -813,7 +870,7 @@ static NTSTATUS check_aapl(vfs_handle_struct *handle,
        }
 
        if (req_bitmap & SMB2_CRTCTX_AAPL_VOLUME_CAPS) {
-               int val = lp_case_sensitive(SNUM(handle->conn->tcon->compat));
+               int val = lp_case_sensitive(SNUM(handle->conn));
                uint64_t caps = 0;
 
                switch (val) {
@@ -884,27 +941,25 @@ static bool readdir_attr_meta_finderi_stream(
        files_struct *fsp = NULL;
        ssize_t nread;
        NTSTATUS status;
-       int ret;
        bool ok;
        uint8_t buf[AFP_INFO_SIZE];
 
-       stream_name = synthetic_smb_fname(talloc_tos(),
-                                         smb_fname->base_name,
-                                         AFPINFO_STREAM_NAME,
-                                         NULL, smb_fname->flags);
-       if (stream_name == NULL) {
-               return false;
-       }
-
-       ret = SMB_VFS_STAT(handle->conn, stream_name);
-       if (ret != 0) {
+       status = synthetic_pathref(talloc_tos(),
+                                  handle->conn->cwd_fsp,
+                                  smb_fname->base_name,
+                                  AFPINFO_STREAM_NAME,
+                                  NULL,
+                                  smb_fname->twrp,
+                                  smb_fname->flags,
+                                  &stream_name);
+       if (!NT_STATUS_IS_OK(status)) {
                return false;
        }
 
        status = SMB_VFS_CREATE_FILE(
                handle->conn,                           /* conn */
                NULL,                                   /* req */
-               0,                                      /* root_dir_fid */
+               NULL,                                   /* dirfsp */
                stream_name,                            /* fname */
                FILE_READ_DATA,                         /* access_mask */
                (FILE_SHARE_READ | FILE_SHARE_WRITE |   /* share_access */
@@ -943,7 +998,7 @@ static bool readdir_attr_meta_finderi_stream(
 
 fail:
        if (fsp != NULL) {
-               close_file(NULL, fsp, NORMAL_CLOSE);
+               close_file_free(NULL, &fsp, NORMAL_CLOSE);
        }
 
        return ok;
@@ -957,7 +1012,7 @@ static bool readdir_attr_meta_finderi_netatalk(
        struct adouble *ad = NULL;
        char *p = NULL;
 
-       ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
+       ad = ad_get_meta_fsp(talloc_tos(), handle, smb_fname);
        if (ad == NULL) {
                return false;
        }
@@ -1065,7 +1120,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;
        }
@@ -1254,6 +1311,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"));
 
@@ -1271,7 +1330,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) {
@@ -1314,36 +1373,45 @@ static int fruit_connect(vfs_handle_struct *handle,
        return rc;
 }
 
-static int fruit_fake_fd(void)
+static void fio_ref_destroy_fn(void *p_data)
 {
-       int pipe_fds[2];
-       int fd;
-       int ret;
+       struct fio *ref_fio = (struct fio *)p_data;
+       if (ref_fio->real_fio != NULL) {
+               SMB_ASSERT(ref_fio->real_fio->ad_fsp == ref_fio->fsp);
+               ref_fio->real_fio->ad_fsp = NULL;
+               ref_fio->real_fio = NULL;
+       }
+}
 
-       /*
-        * Return a valid fd, but ensure any attempt to use it returns
-        * an error (EPIPE). Once we get a write on the handle, we open
-        * the real fd.
-        */
-       ret = pipe(pipe_fds);
-       if (ret != 0) {
-               return -1;
+static void fio_close_ad_fsp(struct fio *fio)
+{
+       if (fio->ad_fsp != NULL) {
+               fd_close(fio->ad_fsp);
+               file_free(NULL, fio->ad_fsp);
+               /* fio_ref_destroy_fn() should have cleared this */
+               SMB_ASSERT(fio->ad_fsp == NULL);
        }
-       fd = pipe_fds[0];
-       close(pipe_fds[1]);
+}
 
-       return fd;
+static void fio_destroy_fn(void *p_data)
+{
+       struct fio *fio = (struct fio *)p_data;
+       fio_close_ad_fsp(fio);
 }
 
 static int fruit_open_meta_stream(vfs_handle_struct *handle,
-                                 struct smb_filename *smb_fname,
+                                 const struct files_struct *dirfsp,
+                                 const struct smb_filename *smb_fname,
                                  files_struct *fsp,
                                  int flags,
                                  mode_t mode)
 {
        struct fruit_config_data *config = NULL;
        struct fio *fio = NULL;
-       int open_flags = flags & ~O_CREAT;
+       struct vfs_open_how how = {
+               .flags = flags & ~O_CREAT,
+               .mode = mode,
+       };
        int fd;
 
        DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
@@ -1351,11 +1419,17 @@ static int fruit_open_meta_stream(vfs_handle_struct *handle,
        SMB_VFS_HANDLE_GET_DATA(handle, config,
                                struct fruit_config_data, return -1);
 
-       fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
+       fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, fio_destroy_fn);
+       fio->handle = handle;
+       fio->fsp = fsp;
        fio->type = ADOUBLE_META;
        fio->config = config;
 
-       fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, open_flags, mode);
+       fd = SMB_VFS_NEXT_OPENAT(handle,
+                                dirfsp,
+                                smb_fname,
+                                fsp,
+                                &how);
        if (fd != -1) {
                return fd;
        }
@@ -1365,7 +1439,7 @@ static int fruit_open_meta_stream(vfs_handle_struct *handle,
                return -1;
        }
 
-       fd = fruit_fake_fd();
+       fd = vfs_fake_fd();
        if (fd == -1) {
                VFS_REMOVE_FSP_EXTENSION(handle, fsp);
                return -1;
@@ -1379,7 +1453,8 @@ static int fruit_open_meta_stream(vfs_handle_struct *handle,
 }
 
 static int fruit_open_meta_netatalk(vfs_handle_struct *handle,
-                                   struct smb_filename *smb_fname,
+                                   const struct files_struct *dirfsp,
+                                   const struct smb_filename *smb_fname,
                                    files_struct *fsp,
                                    int flags,
                                    mode_t mode)
@@ -1392,7 +1467,14 @@ static int fruit_open_meta_netatalk(vfs_handle_struct *handle,
 
        DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
 
-       ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
+       /*
+        * We know this is a stream open, so fsp->base_fsp must
+        * already be open.
+        */
+       SMB_ASSERT(fsp_is_alternate_stream(fsp));
+       SMB_ASSERT(fsp->base_fsp->fsp_name->fsp == fsp->base_fsp);
+
+       ad = ad_get(talloc_tos(), handle, fsp->base_fsp->fsp_name, ADOUBLE_META);
        if (ad != NULL) {
                meta_exists = true;
        }
@@ -1404,7 +1486,7 @@ static int fruit_open_meta_netatalk(vfs_handle_struct *handle,
                return -1;
        }
 
-       fd = fruit_fake_fd();
+       fd = vfs_fake_fd();
        if (fd == -1) {
                return -1;
        }
@@ -1412,7 +1494,9 @@ static int fruit_open_meta_netatalk(vfs_handle_struct *handle,
        SMB_VFS_HANDLE_GET_DATA(handle, config,
                                struct fruit_config_data, return -1);
 
-       fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
+       fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, fio_destroy_fn);
+       fio->handle = handle;
+       fio->fsp = fsp;
        fio->type = ADOUBLE_META;
        fio->config = config;
        fio->fake_fd = true;
@@ -1423,7 +1507,8 @@ static int fruit_open_meta_netatalk(vfs_handle_struct *handle,
 }
 
 static int fruit_open_meta(vfs_handle_struct *handle,
-                          struct smb_filename *smb_fname,
+                          const struct files_struct *dirfsp,
+                          const struct smb_filename *smb_fname,
                           files_struct *fsp, int flags, mode_t mode)
 {
        int fd;
@@ -1436,12 +1521,12 @@ static int fruit_open_meta(vfs_handle_struct *handle,
 
        switch (config->meta) {
        case FRUIT_META_STREAM:
-               fd = fruit_open_meta_stream(handle, smb_fname,
+               fd = fruit_open_meta_stream(handle, dirfsp, smb_fname,
                                            fsp, flags, mode);
                break;
 
        case FRUIT_META_NETATALK:
-               fd = fruit_open_meta_netatalk(handle, smb_fname,
+               fd = fruit_open_meta_netatalk(handle, dirfsp, smb_fname,
                                              fsp, flags, mode);
                break;
 
@@ -1456,16 +1541,19 @@ static int fruit_open_meta(vfs_handle_struct *handle,
 }
 
 static int fruit_open_rsrc_adouble(vfs_handle_struct *handle,
-                                  struct smb_filename *smb_fname,
+                                  const struct files_struct *dirfsp,
+                                  const struct smb_filename *smb_fname,
                                   files_struct *fsp,
                                   int flags,
                                   mode_t mode)
 {
        int rc = 0;
-       struct adouble *ad = NULL;
-       struct smb_filename *smb_fname_base = NULL;
        struct fruit_config_data *config = NULL;
-       int hostfd = -1;
+       struct files_struct *ad_fsp = NULL;
+       struct fio *fio = NULL;
+       struct fio *ref_fio = NULL;
+       NTSTATUS status;
+       int fd = -1;
 
        SMB_VFS_HANDLE_GET_DATA(handle, config,
                                struct fruit_config_data, return -1);
@@ -1473,69 +1561,94 @@ static int fruit_open_rsrc_adouble(vfs_handle_struct *handle,
        if ((!(flags & O_CREAT)) &&
            S_ISDIR(fsp->base_fsp->fsp_name->st.st_ex_mode))
        {
-               /* sorry, but directories don't habe a resource fork */
+               /* sorry, but directories don't have a resource fork */
+               errno = ENOENT;
                rc = -1;
                goto exit;
        }
 
-       rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_base);
-       if (rc != 0) {
+       /*
+        * We return a fake_fd to the vfs modules above,
+        * while we open an internal backend fsp for the
+        * '._' file for the next vfs modules.
+        *
+        * Note that adouble_open_from_base_fsp() recurses
+        * into fruit_openat(), but it'll just pass to
+        * the next module as just opens a flat file on
+        * disk.
+        */
+
+       fd = vfs_fake_fd();
+       if (fd == -1) {
+               rc = fd;
                goto exit;
        }
 
-       /* We always need read/write access for the metadata header too */
-       flags &= ~(O_RDONLY | O_WRONLY);
-       flags |= O_RDWR;
-
-       hostfd = SMB_VFS_NEXT_OPEN(handle, smb_fname_base, fsp,
-                                  flags, mode);
-       if (hostfd == -1) {
+       status = adouble_open_from_base_fsp(fsp->conn->cwd_fsp,
+                                           fsp->base_fsp,
+                                           ADOUBLE_RSRC,
+                                           flags,
+                                           mode,
+                                           &ad_fsp);
+       if (!NT_STATUS_IS_OK(status)) {
+               errno = map_errno_from_nt_status(status);
                rc = -1;
                goto exit;
        }
 
-       if (flags & (O_CREAT | O_TRUNC)) {
-               ad = ad_init(fsp, ADOUBLE_RSRC);
-               if (ad == NULL) {
-                       rc = -1;
-                       goto exit;
-               }
-
-               fsp->fh->fd = hostfd;
+       /*
+        * Now we need to glue both handles together,
+        * so that they automatically detach each other
+        * on close.
+        */
+       fio = fruit_get_complete_fio(handle, fsp);
+       if (fio == NULL) {
+               DBG_ERR("fio=NULL for [%s]\n", fsp_str_dbg(fsp));
+               errno = EBADF;
+               rc = -1;
+               goto exit;
+       }
 
-               rc = ad_fset(handle, ad, fsp);
-               fsp->fh->fd = -1;
-               if (rc != 0) {
-                       rc = -1;
-                       goto exit;
-               }
-               TALLOC_FREE(ad);
+       ref_fio = VFS_ADD_FSP_EXTENSION(handle, ad_fsp,
+                                       struct fio,
+                                       fio_ref_destroy_fn);
+       if (ref_fio == NULL) {
+               int saved_errno = errno;
+               fd_close(ad_fsp);
+               file_free(NULL, ad_fsp);
+               ad_fsp = NULL;
+               errno = saved_errno;
+               rc = -1;
+               goto exit;
        }
 
-exit:
+       SMB_ASSERT(ref_fio->fsp == NULL);
+       ref_fio->handle = handle;
+       ref_fio->fsp = ad_fsp;
+       ref_fio->type = ADOUBLE_RSRC;
+       ref_fio->config = config;
+       ref_fio->real_fio = fio;
+       SMB_ASSERT(fio->ad_fsp == NULL);
+       fio->ad_fsp = ad_fsp;
+       fio->fake_fd = true;
 
-       TALLOC_FREE(smb_fname_base);
+exit:
 
-       DEBUG(10, ("fruit_open resource fork: rc=%d, fd=%d\n", rc, hostfd));
+       DEBUG(10, ("fruit_open resource fork: rc=%d\n", rc));
        if (rc != 0) {
                int saved_errno = errno;
-               if (hostfd >= 0) {
-                       /*
-                        * BUGBUGBUG -- we would need to call
-                        * fd_close_posix here, but we don't have a
-                        * full fsp yet
-                        */
-                       fsp->fh->fd = hostfd;
-                       SMB_VFS_CLOSE(fsp);
+               if (fd != -1) {
+                       vfs_fake_fd_close(fd);
                }
-               hostfd = -1;
                errno = saved_errno;
+               return rc;
        }
-       return hostfd;
+       return fd;
 }
 
 static int fruit_open_rsrc_xattr(vfs_handle_struct *handle,
-                                struct smb_filename *smb_fname,
+                                const struct files_struct *dirfsp,
+                                const struct smb_filename *smb_fname,
                                 files_struct *fsp,
                                 int flags,
                                 mode_t mode)
@@ -1543,6 +1656,11 @@ static int fruit_open_rsrc_xattr(vfs_handle_struct *handle,
 #ifdef HAVE_ATTROPEN
        int fd = -1;
 
+       /*
+        * As there's no attropenat() this is only going to work with AT_FDCWD.
+        */
+       SMB_ASSERT(fsp_get_pathref_fd(dirfsp) == AT_FDCWD);
+
        fd = attropen(smb_fname->base_name,
                      AFPRESOURCE_EA_NETATALK,
                      flags,
@@ -1560,7 +1678,8 @@ static int fruit_open_rsrc_xattr(vfs_handle_struct *handle,
 }
 
 static int fruit_open_rsrc(vfs_handle_struct *handle,
-                          struct smb_filename *smb_fname,
+                          const struct files_struct *dirfsp,
+                          const struct smb_filename *smb_fname,
                           files_struct *fsp, int flags, mode_t mode)
 {
        int fd;
@@ -1572,23 +1691,38 @@ static int fruit_open_rsrc(vfs_handle_struct *handle,
        SMB_VFS_HANDLE_GET_DATA(handle, config,
                                struct fruit_config_data, return -1);
 
+       fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, fio_destroy_fn);
+       fio->handle = handle;
+       fio->fsp = fsp;
+       fio->type = ADOUBLE_RSRC;
+       fio->config = config;
+
        switch (config->rsrc) {
-       case FRUIT_RSRC_STREAM:
-               fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
+       case FRUIT_RSRC_STREAM: {
+               struct vfs_open_how how = {
+                       .flags = flags, .mode = mode,
+               };
+               fd = SMB_VFS_NEXT_OPENAT(handle,
+                                        dirfsp,
+                                        smb_fname,
+                                        fsp,
+                                        &how);
                break;
+       }
 
        case FRUIT_RSRC_ADFILE:
-               fd = fruit_open_rsrc_adouble(handle, smb_fname,
+               fd = fruit_open_rsrc_adouble(handle, dirfsp, smb_fname,
                                             fsp, flags, mode);
                break;
 
        case FRUIT_RSRC_XATTR:
-               fd = fruit_open_rsrc_xattr(handle, smb_fname,
+               fd = fruit_open_rsrc_xattr(handle, dirfsp, smb_fname,
                                           fsp, flags, mode);
                break;
 
        default:
                DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
+               errno = EINVAL;
                return -1;
        }
 
@@ -1598,35 +1732,60 @@ static int fruit_open_rsrc(vfs_handle_struct *handle,
                return -1;
        }
 
-       fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
-       fio->type = ADOUBLE_RSRC;
-       fio->config = config;
-
        return fd;
 }
 
-static int fruit_open(vfs_handle_struct *handle,
-                      struct smb_filename *smb_fname,
-                      files_struct *fsp, int flags, mode_t mode)
+static int fruit_openat(vfs_handle_struct *handle,
+                       const struct files_struct *dirfsp,
+                       const struct smb_filename *smb_fname,
+                       files_struct *fsp,
+                       const struct vfs_open_how *how)
 {
        int fd;
 
        DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
 
-       if (!is_ntfs_stream_smb_fname(smb_fname)) {
-               return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
+       if (!is_named_stream(smb_fname)) {
+               return SMB_VFS_NEXT_OPENAT(handle,
+                                          dirfsp,
+                                          smb_fname,
+                                          fsp,
+                                          how);
+       }
+
+       if (how->resolve != 0) {
+               errno = ENOSYS;
+               return -1;
        }
 
+       SMB_ASSERT(fsp_is_alternate_stream(fsp));
+
        if (is_afpinfo_stream(smb_fname->stream_name)) {
-               fd = fruit_open_meta(handle, smb_fname, fsp, flags, mode);
+               fd = fruit_open_meta(handle,
+                                    dirfsp,
+                                    smb_fname,
+                                    fsp,
+                                    how->flags,
+                                    how->mode);
        } else if (is_afpresource_stream(smb_fname->stream_name)) {
-               fd = fruit_open_rsrc(handle, smb_fname, fsp, flags, mode);
+               fd = fruit_open_rsrc(handle,
+                                    dirfsp,
+                                    smb_fname,
+                                    fsp,
+                                    how->flags,
+                                    how->mode);
        } else {
-               fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
+               fd = SMB_VFS_NEXT_OPENAT(handle,
+                                        dirfsp,
+                                        smb_fname,
+                                        fsp,
+                                        how);
        }
 
        DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
 
+       /* Prevent reopen optimisation */
+       fsp->fsp_flags.have_proc_fds = false;
        return fd;
 }
 
@@ -1641,12 +1800,22 @@ static int fruit_close_meta(vfs_handle_struct *handle,
 
        switch (config->meta) {
        case FRUIT_META_STREAM:
-               ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
+       {
+               struct fio *fio = fruit_get_complete_fio(handle, fsp);
+               if (fio == NULL) {
+                       return -1;
+               }
+               if (fio->fake_fd) {
+                       ret = vfs_fake_fd_close(fsp_get_pathref_fd(fsp));
+                       fsp_set_fd(fsp, -1);
+               } else {
+                       ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
+               }
                break;
-
+       }
        case FRUIT_META_NETATALK:
-               ret = close(fsp->fh->fd);
-               fsp->fh->fd = -1;
+               ret = vfs_fake_fd_close(fsp_get_pathref_fd(fsp));
+               fsp_set_fd(fsp, -1);
                break;
 
        default:
@@ -1669,13 +1838,24 @@ static int fruit_close_rsrc(vfs_handle_struct *handle,
 
        switch (config->rsrc) {
        case FRUIT_RSRC_STREAM:
-       case FRUIT_RSRC_ADFILE:
                ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
                break;
 
+       case FRUIT_RSRC_ADFILE:
+       {
+               struct fio *fio = fruit_get_complete_fio(handle, fsp);
+               if (fio == NULL) {
+                       return -1;
+               }
+               fio_close_ad_fsp(fio);
+               ret = vfs_fake_fd_close(fsp_get_pathref_fd(fsp));
+               fsp_set_fd(fsp, -1);
+               break;
+       }
+
        case FRUIT_RSRC_XATTR:
-               ret = close(fsp->fh->fd);
-               fsp->fh->fd = -1;
+               ret = vfs_fake_fd_close(fsp_get_pathref_fd(fsp));
+               fsp_set_fd(fsp, -1);
                break;
 
        default:
@@ -1692,11 +1872,11 @@ static int fruit_close(vfs_handle_struct *handle,
        int ret;
        int fd;
 
-       fd = fsp->fh->fd;
+       fd = fsp_get_pathref_fd(fsp);
 
        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 (!fsp_is_alternate_stream(fsp)) {
                return SMB_VFS_NEXT_CLOSE(handle, fsp);
        }
 
@@ -1788,8 +1968,9 @@ static int fruit_unlink_meta_stream(vfs_handle_struct *handle,
 static int fruit_unlink_meta_netatalk(vfs_handle_struct *handle,
                                      const struct smb_filename *smb_fname)
 {
-       return SMB_VFS_REMOVEXATTR(handle->conn,
-                                  smb_fname,
+       SMB_ASSERT(smb_fname->fsp != NULL);
+       SMB_ASSERT(fsp_is_alternate_stream(smb_fname->fsp));
+       return SMB_VFS_FREMOVEXATTR(smb_fname->fsp->base_fsp,
                                   AFPINFO_EA_NETATALK);
 }
 
@@ -1823,17 +2004,24 @@ 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;
 
        if (!force_unlink) {
-               struct smb_filename *smb_fname_cp = NULL;
+               struct smb_filename *full_fname = NULL;
                off_t size;
 
-               smb_fname_cp = cp_smb_filename(talloc_tos(), smb_fname);
-               if (smb_fname_cp == NULL) {
+               /*
+                * TODO: use SMB_VFS_STATX() once we have it.
+                */
+
+               full_fname = full_path_from_dirfsp_atname(talloc_tos(),
+                                                         dirfsp,
+                                                         smb_fname);
+               if (full_fname == NULL) {
                        return -1;
                }
 
@@ -1843,16 +2031,16 @@ static int fruit_unlink_rsrc_stream(vfs_handle_struct *handle,
                 * deletion doesn't remove the resourcefork stream.
                 */
 
-               ret = SMB_VFS_NEXT_STAT(handle, smb_fname_cp);
+               ret = SMB_VFS_NEXT_STAT(handle, full_fname);
                if (ret != 0) {
-                       TALLOC_FREE(smb_fname_cp);
+                       TALLOC_FREE(full_fname);
                        DBG_ERR("stat [%s] failed [%s]\n",
-                               smb_fname_str_dbg(smb_fname_cp), strerror(errno));
+                               smb_fname_str_dbg(full_fname), strerror(errno));
                        return -1;
                }
 
-               size = smb_fname_cp->st.st_ex_size;
-               TALLOC_FREE(smb_fname_cp);
+               size = full_fname->st.st_ex_size;
+               TALLOC_FREE(full_fname);
 
                if (size > 0) {
                        /* OS X ignores resource fork stream delete requests */
@@ -1860,7 +2048,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;
        }
@@ -1869,16 +2060,27 @@ 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;
        struct smb_filename *adp_smb_fname = NULL;
 
        if (!force_unlink) {
-               ad = ad_get(talloc_tos(), handle, smb_fname,
+               struct smb_filename *full_fname = NULL;
+
+               full_fname = full_path_from_dirfsp_atname(talloc_tos(),
+                                                         dirfsp,
+                                                         smb_fname);
+               if (full_fname == NULL) {
+                       return -1;
+               }
+
+               ad = ad_get(talloc_tos(), handle, full_fname,
                            ADOUBLE_RSRC);
+               TALLOC_FREE(full_fname);
                if (ad == NULL) {
                        errno = ENOENT;
                        return -1;
@@ -1905,9 +2107,12 @@ 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) {
+       if ((rc != 0) && (errno == ENOENT || errno == ENAMETOOLONG) && force_unlink) {
                rc = 0;
        }
 
@@ -1928,8 +2133,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;
@@ -1939,11 +2145,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:
@@ -1958,78 +2170,22 @@ static int fruit_unlink_rsrc(vfs_handle_struct *handle,
        return rc;
 }
 
-static int fruit_unlink_internal(vfs_handle_struct *handle,
-                       struct files_struct *dirfsp,
-                       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->stream_name)) {
-               return fruit_unlink_meta(handle,
-                               dirfsp,
-                               smb_fname);
-       } else if (is_afpresource_stream(smb_fname->stream_name)) {
-               return fruit_unlink_rsrc(handle, smb_fname, false);
-       } else if (is_ntfs_stream_smb_fname(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);
-       }
-
-       /*
-        * 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_UNLINKAT(handle,
-                       dirfsp,
-                       smb_fname,
-                       0);
-}
-
-static int fruit_chmod(vfs_handle_struct *handle,
-                      const struct smb_filename *smb_fname,
-                      mode_t mode)
+static int fruit_fchmod(vfs_handle_struct *handle,
+                      struct files_struct *fsp,
+                      mode_t mode)
 {
        int rc = -1;
        struct fruit_config_data *config = NULL;
        struct smb_filename *smb_fname_adp = NULL;
+       const struct smb_filename *smb_fname = NULL;
+       NTSTATUS status;
 
-       rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname, mode);
+       rc = SMB_VFS_NEXT_FCHMOD(handle, fsp, mode);
        if (rc != 0) {
                return rc;
        }
 
+       smb_fname = fsp->fsp_name;
        SMB_VFS_HANDLE_GET_DATA(handle, config,
                                struct fruit_config_data, return -1);
 
@@ -2047,161 +2203,31 @@ static int fruit_chmod(vfs_handle_struct *handle,
 
        rc = adouble_path(talloc_tos(), smb_fname, &smb_fname_adp);
        if (rc != 0) {
-               return -1;
-       }
-
-       DEBUG(10, ("fruit_chmod: %s\n", smb_fname_adp->base_name));
-
-       rc = SMB_VFS_NEXT_CHMOD(handle, smb_fname_adp, mode);
-       if (errno == ENOENT) {
-               rc = 0;
-       }
-
-       TALLOC_FREE(smb_fname_adp);
-       return rc;
-}
-
-static int fruit_chown(vfs_handle_struct *handle,
-                      const struct smb_filename *smb_fname,
-                      uid_t uid,
-                      gid_t gid)
-{
-       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;
-       }
-
-       DEBUG(10, ("fruit_chown: %s\n", adp_smb_fname->base_name));
-
-       rc = SMB_VFS_NEXT_CHOWN(handle, adp_smb_fname, uid, gid);
-       if (errno == ENOENT) {
-               rc = 0;
-       }
-
- done:
-       TALLOC_FREE(adp_smb_fname);
-       return rc;
-}
-
-static int fruit_rmdir_internal(struct vfs_handle_struct *handle,
-                       struct files_struct *dirfsp,
-                       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;
-       }
-
-       /*
-        * Due to there is no way to change bDeleteVetoFiles variable
-        * from this module, need to clean up ourselves
-        */
-
-       dh = SMB_VFS_OPENDIR(handle->conn, smb_fname, NULL, 0);
-       if (dh == NULL) {
-               goto exit_rmdir;
-       }
-
-       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_UNLINKAT(handle,
-                               dirfsp,
-                               ad_smb_fname,
-                               0);
-               if (ret != 0) {
-                       DBG_ERR("Deleting [%s] failed\n",
-                               smb_fname_str_dbg(ad_smb_fname));
-               }
-               TALLOC_FREE(ad_smb_fname);
+               return -1;
        }
 
-exit_rmdir:
-       if (dh) {
-               SMB_VFS_CLOSEDIR(handle->conn, dh);
+       status = openat_pathref_fsp(handle->conn->cwd_fsp,
+                                   smb_fname_adp);
+       if (!NT_STATUS_IS_OK(status)) {
+               /* detect ENOENT (mapped to OBJECT_NAME_NOT_FOUND) */
+               if (NT_STATUS_EQUAL(status,
+                                   NT_STATUS_OBJECT_NAME_NOT_FOUND)){
+                       rc = 0;
+                       goto out;
+               }
+               rc = -1;
+               goto out;
        }
-       return SMB_VFS_NEXT_RMDIR(handle, smb_fname);
-}
 
-static int fruit_rmdir(struct vfs_handle_struct *handle,
-                       const struct smb_filename *smb_fname)
-{
-       return fruit_rmdir_internal(handle,
-                               handle->conn->cwd_fsp,
-                               smb_fname);
-}
+       DBG_DEBUG("%s\n", smb_fname_adp->base_name);
 
-static int fruit_unlink(vfs_handle_struct *handle,
-                       const struct smb_filename *smb_fname)
-{
-       return fruit_unlink_internal(handle,
-                               handle->conn->cwd_fsp,
-                               smb_fname);
+       rc = SMB_VFS_NEXT_FCHMOD(handle, smb_fname_adp->fsp, mode);
+       if (errno == ENOENT) {
+               rc = 0;
+       }
+out:
+       TALLOC_FREE(smb_fname_adp);
+       return rc;
 }
 
 static int fruit_unlinkat(vfs_handle_struct *handle,
@@ -2209,28 +2235,84 @@ static int fruit_unlinkat(vfs_handle_struct *handle,
                        const struct smb_filename *smb_fname,
                        int flags)
 {
+       struct fruit_config_data *config = NULL;
+       struct smb_filename *rsrc_smb_fname = NULL;
        int ret;
 
-       SMB_ASSERT(dirfsp == dirfsp->conn->cwd_fsp);
        if (flags & AT_REMOVEDIR) {
-               ret = fruit_rmdir_internal(handle,
+               return SMB_VFS_NEXT_UNLINKAT(handle,
+                                            dirfsp,
+                                            smb_fname,
+                                            AT_REMOVEDIR);
+       }
+
+       SMB_VFS_HANDLE_GET_DATA(handle, config,
+                               struct fruit_config_data, return -1);
+
+       if (is_afpinfo_stream(smb_fname->stream_name)) {
+               return fruit_unlink_meta(handle,
                                dirfsp,
                                smb_fname);
-       } else {
-               ret = fruit_unlink_internal(handle,
+       } else if (is_afpresource_stream(smb_fname->stream_name)) {
+               return fruit_unlink_rsrc(handle,
                                dirfsp,
-                               smb_fname);
+                               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);
        }
-       return ret;
+
+       /*
+        * 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->twrp,
+                                            smb_fname->flags);
+       if (rsrc_smb_fname == NULL) {
+               return -1;
+       }
+
+       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);
+
+       return SMB_VFS_NEXT_UNLINKAT(handle,
+                       dirfsp,
+                       smb_fname,
+                       0);
 }
 
 static ssize_t fruit_pread_meta_stream(vfs_handle_struct *handle,
                                       files_struct *fsp, void *data,
                                       size_t n, off_t offset)
 {
+       struct fio *fio = fruit_get_complete_fio(handle, fsp);
        ssize_t nread;
        int ret;
 
+       if ((fio == NULL) || fio->fake_fd) {
+               return -1;
+       }
+
        nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
        if (nread == -1 || nread == n) {
                return nread;
@@ -2239,7 +2321,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;
@@ -2297,7 +2382,7 @@ static ssize_t fruit_pread_meta(vfs_handle_struct *handle,
                                files_struct *fsp, void *data,
                                size_t n, off_t offset)
 {
-       struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+       struct fio *fio = fruit_get_complete_fio(handle, fsp);
        ssize_t nread;
        ssize_t to_return;
 
@@ -2312,7 +2397,7 @@ static ssize_t fruit_pread_meta(vfs_handle_struct *handle,
        }
 
        if (fio == NULL) {
-               DBG_ERR("Failed to fetch fsp extension");
+               DBG_ERR("Failed to fetch fsp extension\n");
                return -1;
        }
 
@@ -2336,7 +2421,7 @@ static ssize_t fruit_pread_meta(vfs_handle_struct *handle,
                return -1;
        }
 
-       if (nread == -1 && fio->created) {
+       if (nread == -1 && fio->fake_fd) {
                AfpInfo *ai = NULL;
                char afpinfo_buf[AFP_INFO_SIZE];
 
@@ -2376,15 +2461,24 @@ static ssize_t fruit_pread_rsrc_adouble(vfs_handle_struct *handle,
                                        files_struct *fsp, void *data,
                                        size_t n, off_t offset)
 {
+       struct fio *fio = fruit_get_complete_fio(handle, fsp);
        struct adouble *ad = NULL;
        ssize_t nread;
 
-       ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
+       if (fio == NULL || fio->ad_fsp == NULL) {
+               DBG_ERR("fio/ad_fsp=NULL for [%s]\n", fsp_str_dbg(fsp));
+               errno = EBADF;
+               return -1;
+       }
+
+       ad = ad_fget(talloc_tos(), handle, fio->ad_fsp, ADOUBLE_RSRC);
        if (ad == NULL) {
+               DBG_ERR("ad_fget [%s] failed [%s]\n",
+                       fsp_str_dbg(fio->ad_fsp), strerror(errno));
                return -1;
        }
 
-       nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n,
+       nread = SMB_VFS_NEXT_PREAD(handle, fio->ad_fsp, data, n,
                                   offset + ad_getentryoff(ad, ADEID_RFORK));
 
        TALLOC_FREE(ad);
@@ -2395,7 +2489,7 @@ static ssize_t fruit_pread_rsrc(vfs_handle_struct *handle,
                                files_struct *fsp, void *data,
                                size_t n, off_t offset)
 {
-       struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+       struct fio *fio = fruit_get_complete_fio(handle, fsp);
        ssize_t nread;
 
        if (fio == NULL) {
@@ -2428,7 +2522,7 @@ static ssize_t fruit_pread(vfs_handle_struct *handle,
                           files_struct *fsp, void *data,
                           size_t n, off_t offset)
 {
-       struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+       struct fio *fio = fruit_get_complete_fio(handle, fsp);
        ssize_t nread;
 
        DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
@@ -2485,7 +2579,7 @@ static struct tevent_req *fruit_pread_send(
        struct tevent_req *req = NULL;
        struct tevent_req *subreq = NULL;
        struct fruit_pread_state *state = NULL;
-       struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+       struct fio *fio = fruit_get_complete_fio(handle, fsp);
 
        req = tevent_req_create(mem_ctx, &state,
                                struct fruit_pread_state);
@@ -2536,20 +2630,26 @@ static ssize_t fruit_pread_recv(struct tevent_req *req,
 {
        struct fruit_pread_state *state = tevent_req_data(
                req, struct fruit_pread_state);
+       ssize_t retval = -1;
 
        if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
+               tevent_req_received(req);
                return -1;
        }
 
        *vfs_aio_state = state->vfs_aio_state;
-       return state->nread;
+       retval = state->nread;
+       tevent_req_received(req);
+       return retval;
 }
 
 static ssize_t fruit_pwrite_meta_stream(vfs_handle_struct *handle,
-                                       files_struct *fsp, const void *data,
+                                       files_struct *fsp, const void *indata,
                                        size_t n, off_t offset)
 {
-       struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+       struct fio *fio = fruit_get_complete_fio(handle, fsp);
+       const void *data = indata;
+       char afpinfo_buf[AFP_INFO_SIZE];
        AfpInfo *ai = NULL;
        size_t nwritten;
        int ret;
@@ -2563,31 +2663,34 @@ static ssize_t fruit_pwrite_meta_stream(vfs_handle_struct *handle,
        }
 
        if (fio->fake_fd) {
-               int fd;
+               struct vfs_open_how how = {
+                       .flags = fio->flags, .mode = fio->mode,
+               };
+               int fd = fsp_get_pathref_fd(fsp);
 
-               ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
+               ret = vfs_fake_fd_close(fd);
+               fsp_set_fd(fsp, -1);
                if (ret != 0) {
                        DBG_ERR("Close [%s] failed: %s\n",
                                fsp_str_dbg(fsp), strerror(errno));
-                       fsp->fh->fd = -1;
                        return -1;
                }
 
-               fd = SMB_VFS_NEXT_OPEN(handle,
-                                      fsp->fsp_name,
-                                      fsp,
-                                      fio->flags,
-                                      fio->mode);
+               fd = SMB_VFS_NEXT_OPENAT(handle,
+                                        NULL, /* opening a stream */
+                                        fsp->fsp_name,
+                                        fsp,
+                                        &how);
                if (fd == -1) {
                        DBG_ERR("On-demand create [%s] in write failed: %s\n",
                                fsp_str_dbg(fsp), strerror(errno));
                        return -1;
                }
-               fsp->fh->fd = fd;
+               fsp_set_fd(fsp, fd);
                fio->fake_fd = false;
        }
 
-       ai = afpinfo_unpack(talloc_tos(), data);
+       ai = afpinfo_unpack(talloc_tos(), data, fio->config->validate_afpinfo);
        if (ai == NULL) {
                return -1;
        }
@@ -2619,6 +2722,21 @@ static ssize_t fruit_pwrite_meta_stream(vfs_handle_struct *handle,
                return n;
        }
 
+       if (!fio->config->validate_afpinfo) {
+               /*
+                * Ensure the buffer contains a valid header, so marshall
+                * the data from the afpinfo struck back into a buffer
+                * and write that instead of the possibly malformed data
+                * we got from the client.
+                */
+               nwritten = afpinfo_pack(ai, afpinfo_buf);
+               if (nwritten != AFP_INFO_SIZE) {
+                       errno = EINVAL;
+                       return -1;
+               }
+               data = afpinfo_buf;
+       }
+
        nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
        if (nwritten != n) {
                return -1;
@@ -2631,13 +2749,17 @@ static ssize_t fruit_pwrite_meta_netatalk(vfs_handle_struct *handle,
                                          files_struct *fsp, const void *data,
                                          size_t n, off_t offset)
 {
+       struct fruit_config_data *config = NULL;
        struct adouble *ad = NULL;
        AfpInfo *ai = NULL;
        char *p = NULL;
        int ret;
        bool ok;
 
-       ai = afpinfo_unpack(talloc_tos(), data);
+       SMB_VFS_HANDLE_GET_DATA(handle, config,
+                               struct fruit_config_data, return -1);
+
+       ai = afpinfo_unpack(talloc_tos(), data, config->validate_afpinfo);
        if (ai == NULL) {
                return -1;
        }
@@ -2695,7 +2817,7 @@ static ssize_t fruit_pwrite_meta(vfs_handle_struct *handle,
                                 files_struct *fsp, const void *data,
                                 size_t n, off_t offset)
 {
-       struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+       struct fio *fio = fruit_get_complete_fio(handle, fsp);
        ssize_t nwritten;
        uint8_t buf[AFP_INFO_SIZE];
        size_t to_write;
@@ -2703,7 +2825,7 @@ static ssize_t fruit_pwrite_meta(vfs_handle_struct *handle,
        int cmp;
 
        if (fio == NULL) {
-               DBG_ERR("Failed to fetch fsp extension");
+               DBG_ERR("Failed to fetch fsp extension\n");
                return -1;
        }
 
@@ -2717,10 +2839,12 @@ static ssize_t fruit_pwrite_meta(vfs_handle_struct *handle,
                return -1;
        }
 
-       cmp = memcmp(data, "AFP", 3);
-       if (cmp != 0) {
-               errno = EINVAL;
-               return -1;
+       if (fio->config->validate_afpinfo) {
+               cmp = memcmp(data, "AFP", 3);
+               if (cmp != 0) {
+                       errno = EINVAL;
+                       return -1;
+               }
        }
 
        if (n <= AFP_OFF_FinderInfo) {
@@ -2793,30 +2917,38 @@ static ssize_t fruit_pwrite_rsrc_adouble(vfs_handle_struct *handle,
                                         files_struct *fsp, const void *data,
                                         size_t n, off_t offset)
 {
+       struct fio *fio = fruit_get_complete_fio(handle, fsp);
        struct adouble *ad = NULL;
        ssize_t nwritten;
        int ret;
 
-       ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
+       if (fio == NULL || fio->ad_fsp == NULL) {
+               DBG_ERR("fio/ad_fsp=NULL for [%s]\n", fsp_str_dbg(fsp));
+               errno = EBADF;
+               return -1;
+       }
+
+       ad = ad_fget(talloc_tos(), handle, fio->ad_fsp, ADOUBLE_RSRC);
        if (ad == NULL) {
-               DBG_ERR("ad_get [%s] failed\n", fsp_str_dbg(fsp));
+               DBG_ERR("ad_fget [%s] failed [%s]\n",
+                       fsp_str_dbg(fio->ad_fsp), strerror(errno));
                return -1;
        }
 
-       nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n,
+       nwritten = SMB_VFS_NEXT_PWRITE(handle, fio->ad_fsp, data, n,
                                       offset + ad_getentryoff(ad, ADEID_RFORK));
        if (nwritten != n) {
                DBG_ERR("Short write on [%s] [%zd/%zd]\n",
-                       fsp_str_dbg(fsp), nwritten, n);
+                       fsp_str_dbg(fio->ad_fsp), nwritten, n);
                TALLOC_FREE(ad);
                return -1;
        }
 
        if ((n + offset) > ad_getentrylen(ad, ADEID_RFORK)) {
                ad_setentrylen(ad, ADEID_RFORK, n + offset);
-               ret = ad_fset(handle, ad, fsp);
+               ret = ad_fset(handle, ad, fio->ad_fsp);
                if (ret != 0) {
-                       DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fsp));
+                       DBG_ERR("ad_pwrite [%s] failed\n", fsp_str_dbg(fio->ad_fsp));
                        TALLOC_FREE(ad);
                        return -1;
                }
@@ -2830,11 +2962,11 @@ static ssize_t fruit_pwrite_rsrc(vfs_handle_struct *handle,
                                 files_struct *fsp, const void *data,
                                 size_t n, off_t offset)
 {
-       struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+       struct fio *fio = fruit_get_complete_fio(handle, fsp);
        ssize_t nwritten;
 
        if (fio == NULL) {
-               DBG_ERR("Failed to fetch fsp extension");
+               DBG_ERR("Failed to fetch fsp extension\n");
                return -1;
        }
 
@@ -2863,7 +2995,7 @@ static ssize_t fruit_pwrite(vfs_handle_struct *handle,
                            files_struct *fsp, const void *data,
                            size_t n, off_t offset)
 {
-       struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+       struct fio *fio = fruit_get_complete_fio(handle, fsp);
        ssize_t nwritten;
 
        DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
@@ -2901,7 +3033,7 @@ static struct tevent_req *fruit_pwrite_send(
        struct tevent_req *req = NULL;
        struct tevent_req *subreq = NULL;
        struct fruit_pwrite_state *state = NULL;
-       struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+       struct fio *fio = fruit_get_complete_fio(handle, fsp);
 
        req = tevent_req_create(mem_ctx, &state,
                                struct fruit_pwrite_state);
@@ -2952,13 +3084,117 @@ static ssize_t fruit_pwrite_recv(struct tevent_req *req,
 {
        struct fruit_pwrite_state *state = tevent_req_data(
                req, struct fruit_pwrite_state);
+       ssize_t retval = -1;
+
+       if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
+               tevent_req_received(req);
+               return -1;
+       }
+
+       *vfs_aio_state = state->vfs_aio_state;
+       retval = state->nwritten;
+       tevent_req_received(req);
+       return retval;
+}
+
+struct fruit_fsync_state {
+       int ret;
+       struct vfs_aio_state vfs_aio_state;
+};
+
+static void fruit_fsync_done(struct tevent_req *subreq);
+
+static struct tevent_req *fruit_fsync_send(
+       struct vfs_handle_struct *handle,
+       TALLOC_CTX *mem_ctx,
+       struct tevent_context *ev,
+       struct files_struct *fsp)
+{
+       struct tevent_req *req = NULL;
+       struct tevent_req *subreq = NULL;
+       struct fruit_fsync_state *state = NULL;
+       struct fio *fio = fruit_get_complete_fio(handle, fsp);
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct fruit_fsync_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       if (fruit_must_handle_aio_stream(fio)) {
+               struct adouble *ad = NULL;
+
+               if (fio->type == ADOUBLE_META) {
+                       /*
+                        * We must never pass a fake_fd
+                        * to lower level fsync calls.
+                        * Everything is already done
+                        * synchronously, so just return
+                        * true.
+                        */
+                       SMB_ASSERT(fio->fake_fd);
+                       tevent_req_done(req);
+                       return tevent_req_post(req, ev);
+               }
+
+               /*
+                * We know the following must be true,
+                * as it's the condition for fruit_must_handle_aio_stream()
+                * to return true if fio->type == ADOUBLE_RSRC.
+                */
+               SMB_ASSERT(fio->config->rsrc == FRUIT_RSRC_ADFILE);
+               if (fio->ad_fsp == NULL) {
+                       tevent_req_error(req, EBADF);
+                       return tevent_req_post(req, ev);
+               }
+               ad = ad_fget(talloc_tos(), handle, fio->ad_fsp, ADOUBLE_RSRC);
+               if (ad == NULL) {
+                       tevent_req_error(req, ENOMEM);
+                       return tevent_req_post(req, ev);
+               }
+               fsp = fio->ad_fsp;
+       }
+
+       subreq = SMB_VFS_NEXT_FSYNC_SEND(state, ev, handle, fsp);
+       if (tevent_req_nomem(req, subreq)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, fruit_fsync_done, req);
+       return req;
+}
+
+static void fruit_fsync_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct fruit_fsync_state *state = tevent_req_data(
+               req, struct fruit_fsync_state);
+
+       state->ret = SMB_VFS_FSYNC_RECV(subreq, &state->vfs_aio_state);
+       TALLOC_FREE(subreq);
+       if (state->ret != 0) {
+               tevent_req_error(req, errno);
+               return;
+       }
+       tevent_req_done(req);
+}
+
+static int fruit_fsync_recv(struct tevent_req *req,
+                                       struct vfs_aio_state *vfs_aio_state)
+{
+       struct fruit_fsync_state *state = tevent_req_data(
+               req, struct fruit_fsync_state);
+       int retval = -1;
 
        if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
+               tevent_req_received(req);
                return -1;
        }
 
        *vfs_aio_state = state->vfs_aio_state;
-       return state->nwritten;
+       retval = state->ret;
+       tevent_req_received(req);
+       return retval;
 }
 
 /**
@@ -3018,7 +3254,12 @@ static int fruit_stat_meta_netatalk(vfs_handle_struct *handle,
 {
        struct adouble *ad = NULL;
 
-       ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
+       /* Populate the stat struct with info from the base file. */
+       if (fruit_stat_base(handle, smb_fname, follow_links) == -1) {
+               return -1;
+       }
+
+       ad = ad_get_meta_fsp(talloc_tos(), handle, smb_fname);
        if (ad == NULL) {
                DBG_INFO("fruit_stat_meta %s: %s\n",
                         smb_fname_str_dbg(smb_fname), strerror(errno));
@@ -3027,10 +3268,6 @@ static int fruit_stat_meta_netatalk(vfs_handle_struct *handle,
        }
        TALLOC_FREE(ad);
 
-       /* Populate the stat struct with info from the base file. */
-       if (fruit_stat_base(handle, smb_fname, follow_links) == -1) {
-               return -1;
-       }
        smb_fname->st.st_ex_size = AFP_INFO_SIZE;
        smb_fname->st.st_ex_ino = hash_inode(&smb_fname->st,
                                              smb_fname->stream_name);
@@ -3189,8 +3426,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);
@@ -3231,8 +3467,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);
@@ -3262,7 +3497,7 @@ static int fruit_fstat_meta_stream(vfs_handle_struct *handle,
                                   files_struct *fsp,
                                   SMB_STRUCT_STAT *sbuf)
 {
-       struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+       struct fio *fio = fruit_get_complete_fio(handle, fsp);
        struct smb_filename smb_fname;
        ino_t ino;
        int ret;
@@ -3285,6 +3520,7 @@ static int fruit_fstat_meta_stream(vfs_handle_struct *handle,
 
        smb_fname = (struct smb_filename) {
                .base_name = fsp->fsp_name->base_name,
+               .twrp = fsp->fsp_name->twrp,
        };
 
        ret = fruit_stat_base(handle, &smb_fname, false);
@@ -3367,21 +3603,26 @@ static int fruit_fstat_rsrc_adouble(vfs_handle_struct *handle,
                                    files_struct *fsp,
                                    SMB_STRUCT_STAT *sbuf)
 {
+       struct fio *fio = fruit_get_complete_fio(handle, fsp);
        struct adouble *ad = NULL;
        int ret;
 
+       if (fio == NULL || fio->ad_fsp == NULL) {
+               DBG_ERR("fio/ad_fsp=NULL for [%s]\n", fsp_str_dbg(fsp));
+               errno = EBADF;
+               return -1;
+       }
+
        /* Populate the stat struct with info from the base file. */
        ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
        if (ret == -1) {
                return -1;
        }
 
-       ad = ad_get(talloc_tos(), handle,
-                   fsp->base_fsp->fsp_name,
-                   ADOUBLE_RSRC);
+       ad = ad_fget(talloc_tos(), handle, fio->ad_fsp, ADOUBLE_RSRC);
        if (ad == NULL) {
-               DBG_ERR("ad_get [%s] failed [%s]\n",
-                       fsp_str_dbg(fsp), strerror(errno));
+               DBG_ERR("ad_fget [%s] failed [%s]\n",
+                       fsp_str_dbg(fio->ad_fsp), strerror(errno));
                return -1;
        }
 
@@ -3422,7 +3663,7 @@ static int fruit_fstat_rsrc(vfs_handle_struct *handle, files_struct *fsp,
 static int fruit_fstat(vfs_handle_struct *handle, files_struct *fsp,
                       SMB_STRUCT_STAT *sbuf)
 {
-       struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+       struct fio *fio = fruit_get_complete_fio(handle, fsp);
        int rc;
 
        if (fio == NULL) {
@@ -3457,6 +3698,7 @@ static NTSTATUS delete_invalid_meta_stream(
        off_t size)
 {
        struct smb_filename *sname = NULL;
+       NTSTATUS status;
        int ret;
        bool ok;
 
@@ -3469,21 +3711,29 @@ static NTSTATUS delete_invalid_meta_stream(
                return NT_STATUS_OK;
        }
 
-       sname = synthetic_smb_fname(talloc_tos(),
-                                   smb_fname->base_name,
-                                   AFPINFO_STREAM_NAME,
-                                   NULL, 0);
-       if (sname == NULL) {
+       status = synthetic_pathref(talloc_tos(),
+                                  handle->conn->cwd_fsp,
+                                  smb_fname->base_name,
+                                  AFPINFO_STREAM_NAME,
+                                  NULL,
+                                  smb_fname->twrp,
+                                  0,
+                                  &sname);
+       if (!NT_STATUS_IS_OK(status)) {
                return NT_STATUS_NO_MEMORY;
        }
 
-       ret = SMB_VFS_NEXT_UNLINK(handle, sname);
-       TALLOC_FREE(sname);
+       ret = SMB_VFS_NEXT_UNLINKAT(handle,
+                       handle->conn->cwd_fsp,
+                       sname,
+                       0);
        if (ret != 0) {
                DBG_ERR("Removing [%s] failed\n", smb_fname_str_dbg(sname));
+               TALLOC_FREE(sname);
                return map_nt_error_from_unix(errno);
        }
 
+       TALLOC_FREE(sname);
        return NT_STATUS_OK;
 }
 
@@ -3568,7 +3818,7 @@ static NTSTATUS fruit_streaminfo_meta_netatalk(
                }
        }
 
-       ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
+       ad = ad_get_meta_fsp(talloc_tos(), handle, smb_fname);
        if (ad == NULL) {
                return NT_STATUS_OK;
        }
@@ -3727,6 +3977,10 @@ static NTSTATUS fruit_streaminfo_rsrc(vfs_handle_struct *handle,
        struct fruit_config_data *config = NULL;
        NTSTATUS status;
 
+       if (S_ISDIR(smb_fname->st.st_ex_mode)) {
+               return NT_STATUS_OK;
+       }
+
        SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
                                return NT_STATUS_INTERNAL_ERROR);
 
@@ -3786,22 +4040,24 @@ static void fruit_filter_empty_streams(unsigned int *pnum_streams,
        *pnum_streams = num_streams;
 }
 
-static NTSTATUS fruit_streaminfo(vfs_handle_struct *handle,
+static NTSTATUS fruit_fstreaminfo(vfs_handle_struct *handle,
                                 struct files_struct *fsp,
-                                const struct smb_filename *smb_fname,
                                 TALLOC_CTX *mem_ctx,
                                 unsigned int *pnum_streams,
                                 struct stream_struct **pstreams)
 {
        struct fruit_config_data *config = NULL;
+       const struct smb_filename *smb_fname = NULL;
        NTSTATUS status;
 
+       smb_fname = fsp->fsp_name;
+
        SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
                                return NT_STATUS_UNSUCCESSFUL);
 
        DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
 
-       status = SMB_VFS_NEXT_STREAMINFO(handle, fsp, smb_fname, mem_ctx,
+       status = SMB_VFS_NEXT_FSTREAMINFO(handle, fsp, mem_ctx,
                                         pnum_streams, pstreams);
        if (!NT_STATUS_IS_OK(status)) {
                return status;
@@ -3824,9 +4080,9 @@ static NTSTATUS fruit_streaminfo(vfs_handle_struct *handle,
        return NT_STATUS_OK;
 }
 
-static int fruit_ntimes(vfs_handle_struct *handle,
-                       const struct smb_filename *smb_fname,
-                       struct smb_file_time *ft)
+static int fruit_fntimes(vfs_handle_struct *handle,
+                        files_struct *fsp,
+                        struct smb_file_time *ft)
 {
        int rc = 0;
        struct adouble *ad = NULL;
@@ -3836,15 +4092,15 @@ 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);
+               return SMB_VFS_NEXT_FNTIMES(handle, fsp, ft);
        }
 
-       DEBUG(10,("set btime for %s to %s\n", smb_fname_str_dbg(smb_fname),
-                time_to_asc(convert_timespec_to_time_t(ft->create_time))));
+       DBG_DEBUG("set btime for %s to %s", fsp_str_dbg(fsp),
+                 time_to_asc(convert_timespec_to_time_t(ft->create_time)));
 
-       ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
+       ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
        if (ad == NULL) {
                goto exit;
        }
@@ -3852,16 +4108,16 @@ static int fruit_ntimes(vfs_handle_struct *handle,
        ad_setdate(ad, AD_DATE_CREATE | AD_DATE_UNIX,
                   convert_time_t_to_uint32_t(ft->create_time.tv_sec));
 
-       rc = ad_set(handle, ad, smb_fname);
+       rc = ad_fset(handle, ad, fsp);
 
 exit:
 
        TALLOC_FREE(ad);
        if (rc != 0) {
-               DEBUG(1, ("fruit_ntimes: %s\n", smb_fname_str_dbg(smb_fname)));
+               DBG_WARNING("%s\n", fsp_str_dbg(fsp));
                return -1;
        }
-       return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
+       return SMB_VFS_NEXT_FNTIMES(handle, fsp, ft);
 }
 
 static int fruit_fallocate(struct vfs_handle_struct *handle,
@@ -3870,7 +4126,7 @@ static int fruit_fallocate(struct vfs_handle_struct *handle,
                           off_t offset,
                           off_t len)
 {
-       struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+       struct fio *fio = fruit_get_complete_fio(handle, fsp);
 
        if (fio == NULL) {
                return SMB_VFS_NEXT_FALLOCATE(handle, fsp, mode, offset, len);
@@ -3895,20 +4151,27 @@ static int fruit_ftruncate_rsrc_adouble(struct vfs_handle_struct *handle,
                                        struct files_struct *fsp,
                                        off_t offset)
 {
+       struct fio *fio = fruit_get_complete_fio(handle, fsp);
        int rc;
        struct adouble *ad = NULL;
        off_t ad_off;
 
-       ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_RSRC);
+       if (fio == NULL || fio->ad_fsp == NULL) {
+               DBG_ERR("fio/ad_fsp=NULL for [%s]\n", fsp_str_dbg(fsp));
+               errno = EBADF;
+               return -1;
+       }
+
+       ad = ad_fget(talloc_tos(), handle, fio->ad_fsp, ADOUBLE_RSRC);
        if (ad == NULL) {
-               DBG_DEBUG("ad_get [%s] failed [%s]\n",
-                         fsp_str_dbg(fsp), strerror(errno));
+               DBG_ERR("ad_fget [%s] failed [%s]\n",
+                       fsp_str_dbg(fio->ad_fsp), strerror(errno));
                return -1;
        }
 
        ad_off = ad_getentryoff(ad, ADEID_RFORK);
 
-       rc = ftruncate(fsp->fh->fd, offset + ad_off);
+       rc = SMB_VFS_NEXT_FTRUNCATE(handle, fio->ad_fsp, offset + ad_off);
        if (rc != 0) {
                TALLOC_FREE(ad);
                return -1;
@@ -3916,10 +4179,10 @@ static int fruit_ftruncate_rsrc_adouble(struct vfs_handle_struct *handle,
 
        ad_setentrylen(ad, ADEID_RFORK, offset);
 
-       rc = ad_fset(handle, ad, fsp);
+       rc = ad_fset(handle, ad, fio->ad_fsp);
        if (rc != 0) {
                DBG_ERR("ad_fset [%s] failed [%s]\n",
-                       fsp_str_dbg(fsp), strerror(errno));
+                       fsp_str_dbg(fio->ad_fsp), strerror(errno));
                TALLOC_FREE(ad);
                return -1;
        }
@@ -3939,11 +4202,11 @@ static int fruit_ftruncate_rsrc(struct vfs_handle_struct *handle,
                                struct files_struct *fsp,
                                off_t offset)
 {
-       struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+       struct fio *fio = fruit_get_complete_fio(handle, fsp);
        int ret;
 
        if (fio == NULL) {
-               DBG_ERR("Failed to fetch fsp extension");
+               DBG_ERR("Failed to fetch fsp extension\n");
                return -1;
        }
 
@@ -3974,7 +4237,7 @@ static int fruit_ftruncate_meta(struct vfs_handle_struct *handle,
                                off_t offset)
 {
        if (offset > 60) {
-               DBG_WARNING("ftruncate %s to %jd",
+               DBG_WARNING("ftruncate %s to %jd\n",
                            fsp_str_dbg(fsp), (intmax_t)offset);
                /* OS X returns NT_STATUS_ALLOTTED_SPACE_EXCEEDED  */
                errno = EOVERFLOW;
@@ -3991,7 +4254,7 @@ static int fruit_ftruncate(struct vfs_handle_struct *handle,
                           struct files_struct *fsp,
                           off_t offset)
 {
-       struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+       struct fio *fio = fruit_get_complete_fio(handle, fsp);
        int ret;
 
        DBG_DEBUG("Path [%s] offset [%"PRIdMAX"]\n", fsp_str_dbg(fsp),
@@ -4013,7 +4276,7 @@ static int fruit_ftruncate(struct vfs_handle_struct *handle,
 
 static NTSTATUS fruit_create_file(vfs_handle_struct *handle,
                                  struct smb_request *req,
-                                 uint16_t root_dir_fid,
+                                 struct files_struct *dirfsp,
                                  struct smb_filename *smb_fname,
                                  uint32_t access_mask,
                                  uint32_t share_access,
@@ -4034,7 +4297,6 @@ static NTSTATUS fruit_create_file(vfs_handle_struct *handle,
        NTSTATUS status;
        struct fruit_config_data *config = NULL;
        files_struct *fsp = NULL;
-       struct fio *fio = NULL;
        bool internal_open = (oplock_request & INTERNAL_OPEN_ONLY);
        int ret;
 
@@ -4046,7 +4308,10 @@ 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->stream_name) && !internal_open) {
+       if (is_apple_stream(smb_fname->stream_name) &&
+           !internal_open &&
+           config->convert_adouble)
+       {
                uint32_t conv_flags  = 0;
 
                if (config->wipe_intentionally_left_blank_rfork) {
@@ -4061,13 +4326,13 @@ static NTSTATUS fruit_create_file(vfs_handle_struct *handle,
                                 macos_string_replace_map,
                                 conv_flags);
                if (ret != 0) {
-                       DBG_ERR("ad_convert() failed\n");
-                       return NT_STATUS_UNSUCCESSFUL;
+                       DBG_ERR("ad_convert(\"%s\") failed\n",
+                               smb_fname_str_dbg(smb_fname));
                }
        }
 
        status = SMB_VFS_NEXT_CREATE_FILE(
-               handle, req, root_dir_fid, smb_fname,
+               handle, req, dirfsp, smb_fname,
                access_mask, share_access,
                create_disposition, create_options,
                file_attributes, oplock_request,
@@ -4082,7 +4347,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
                         */
@@ -4100,25 +4365,19 @@ 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;
        }
 
-       fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
-       if (fio != NULL && pinfo != NULL && *pinfo == FILE_WAS_CREATED) {
-               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) &&
-           (fsp->op != NULL))
+           (fsp->op != NULL) &&
+           !fsp->fsp_flags.is_pathref)
        {
                status = fruit_check_access(
                        handle, *result,
@@ -4135,17 +4394,17 @@ fail:
        DEBUG(10, ("fruit_create_file: %s\n", nt_errstr(status)));
 
        if (fsp) {
-               close_file(req, fsp, ERROR_CLOSE);
-               *result = fsp = NULL;
+               close_file_free(req, &fsp, ERROR_CLOSE);
+               *result = NULL;
        }
 
        return status;
 }
 
-static NTSTATUS fruit_readdir_attr(struct vfs_handle_struct *handle,
-                                  const struct smb_filename *fname,
-                                  TALLOC_CTX *mem_ctx,
-                                  struct readdir_attr_data **pattr_data)
+static NTSTATUS fruit_freaddir_attr(struct vfs_handle_struct *handle,
+                                   struct files_struct *fsp,
+                                   TALLOC_CTX *mem_ctx,
+                                   struct readdir_attr_data **pattr_data)
 {
        struct fruit_config_data *config = NULL;
        struct readdir_attr_data *attr_data;
@@ -4158,27 +4417,35 @@ static NTSTATUS fruit_readdir_attr(struct vfs_handle_struct *handle,
                                return NT_STATUS_UNSUCCESSFUL);
 
        if (!global_fruit_config.nego_aapl) {
-               return SMB_VFS_NEXT_READDIR_ATTR(handle, fname, mem_ctx, pattr_data);
+               return SMB_VFS_NEXT_FREADDIR_ATTR(handle,
+                                                 fsp,
+                                                 mem_ctx,
+                                                 pattr_data);
        }
 
-       DEBUG(10, ("fruit_readdir_attr %s\n", fname->base_name));
+       DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
 
-       if (config->wipe_intentionally_left_blank_rfork) {
-               conv_flags |= AD_CONV_WIPE_BLANK;
-       }
-       if (config->delete_empty_adfiles) {
-               conv_flags |= AD_CONV_DELETE;
-       }
+       if (config->convert_adouble) {
+               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, fname, macos_string_replace_map, conv_flags);
-       if (ret != 0) {
-               DBG_ERR("ad_convert() failed\n");
-               return NT_STATUS_UNSUCCESSFUL;
+               ret = ad_convert(handle,
+                                fsp->fsp_name,
+                                macos_string_replace_map,
+                                conv_flags);
+               if (ret != 0) {
+                       DBG_ERR("ad_convert(\"%s\") failed\n",
+                               fsp_str_dbg(fsp));
+               }
        }
 
        *pattr_data = talloc_zero(mem_ctx, struct readdir_attr_data);
        if (*pattr_data == NULL) {
-               return NT_STATUS_UNSUCCESSFUL;
+               return NT_STATUS_NO_MEMORY;
        }
        attr_data = *pattr_data;
        attr_data->type = RDATTR_AAPL;
@@ -4187,7 +4454,7 @@ static NTSTATUS fruit_readdir_attr(struct vfs_handle_struct *handle,
         * Mac metadata: compressed FinderInfo, resource fork length
         * and creation date
         */
-       status = readdir_attr_macmeta(handle, fname, attr_data);
+       status = readdir_attr_macmeta(handle, fsp->fsp_name, attr_data);
        if (!NT_STATUS_IS_OK(status)) {
                /*
                 * Error handling is tricky: if we return failure from
@@ -4204,7 +4471,8 @@ static NTSTATUS fruit_readdir_attr(struct vfs_handle_struct *handle,
         * UNIX mode
         */
        if (config->unix_info_enabled) {
-               attr_data->attr_data.aapl.unix_mode = fname->st.st_ex_mode;
+               attr_data->attr_data.aapl.unix_mode =
+                       fsp->fsp_name->st.st_ex_mode;
        }
 
        /*
@@ -4213,9 +4481,8 @@ static NTSTATUS fruit_readdir_attr(struct vfs_handle_struct *handle,
        if (!config->readdir_attr_max_access) {
                attr_data->attr_data.aapl.max_access = FILE_GENERIC_ALL;
        } else {
-               status = smbd_calculate_access_mask(
-                       handle->conn,
-                       fname,
+               status = smbd_calculate_access_mask_fsp(fsp->conn->cwd_fsp,
+                       fsp,
                        false,
                        SEC_FLAG_MAXIMUM_ALLOWED,
                        &attr_data->attr_data.aapl.max_access);
@@ -4227,8 +4494,8 @@ static NTSTATUS fruit_readdir_attr(struct vfs_handle_struct *handle,
        return NT_STATUS_OK;
 
 fail:
-       DEBUG(1, ("fruit_readdir_attr %s, error: %s\n",
-                 fname->base_name, nt_errstr(status)));
+       DBG_WARNING("Path [%s], error: %s\n", fsp_str_dbg(fsp),
+                  nt_errstr(status));
        TALLOC_FREE(*pattr_data);
        return status;
 }
@@ -4379,6 +4646,8 @@ struct fruit_offload_read_state {
        struct tevent_context *ev;
        files_struct *fsp;
        uint32_t fsctl;
+       uint32_t flags;
+       uint64_t xferlen;
        DATA_BLOB token;
 };
 
@@ -4430,6 +4699,8 @@ static void fruit_offload_read_done(struct tevent_req *subreq)
        status = SMB_VFS_NEXT_OFFLOAD_READ_RECV(subreq,
                                                state->handle,
                                                state,
+                                               &state->flags,
+                                               &state->xferlen,
                                                &state->token);
        TALLOC_FREE(subreq);
        if (tevent_req_nterror(req, status)) {
@@ -4461,6 +4732,8 @@ static void fruit_offload_read_done(struct tevent_req *subreq)
 static NTSTATUS fruit_offload_read_recv(struct tevent_req *req,
                                        struct vfs_handle_struct *handle,
                                        TALLOC_CTX *mem_ctx,
+                                       uint32_t *flags,
+                                       uint64_t *xferlen,
                                        DATA_BLOB *token)
 {
        struct fruit_offload_read_state *state = tevent_req_data(
@@ -4472,6 +4745,8 @@ static NTSTATUS fruit_offload_read_recv(struct tevent_req *req,
                return status;
        }
 
+       *flags = state->flags;
+       *xferlen = state->xferlen;
        token->length = state->token.length;
        token->data = talloc_move(mem_ctx, &state->token.data);
 
@@ -4602,8 +4877,7 @@ static void fruit_offload_write_done(struct tevent_req *subreq)
         * streams, because we're in vfs_fruit. We don't do this async
         * because streams are few and small.
         */
-       status = vfs_streaminfo(state->handle->conn, state->src_fsp,
-                               state->src_fsp->fsp_name,
+       status = vfs_fstreaminfo(state->src_fsp,
                                req, &num_streams, &streams);
        if (tevent_req_nterror(req, status)) {
                return;
@@ -4624,6 +4898,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;
@@ -4639,6 +4914,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);
@@ -4649,8 +4925,7 @@ static void fruit_offload_write_done(struct tevent_req *subreq)
                                   state->handle->conn,
                                   src_fname_tmp,
                                   dst_fname_tmp,
-                                  OPENX_FILE_CREATE_IF_NOT_EXIST,
-                                  0, false);
+                                  FILE_CREATE);
                if (!NT_STATUS_IS_OK(status)) {
                        DEBUG(1, ("%s: copy %s to %s failed: %s\n", __func__,
                                  smb_fname_str_dbg(src_fname_tmp),
@@ -4798,7 +5073,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;
@@ -4822,7 +5102,7 @@ static bool fruit_get_bandsize(vfs_handle_struct *handle,
        status = SMB_VFS_NEXT_CREATE_FILE(
                handle,                         /* conn */
                NULL,                           /* req */
-               0,                              /* root_dir_fid */
+               NULL,                           /* dirfsp */
                smb_fname,                      /* fname */
                FILE_GENERIC_READ,              /* access_mask */
                FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
@@ -4845,7 +5125,9 @@ static bool fruit_get_bandsize(vfs_handle_struct *handle,
                goto out;
        }
 
-       file_data = talloc_array(talloc_tos(), uint8_t, plist_file_size);
+       file_data = talloc_zero_array(talloc_tos(),
+                                     uint8_t,
+                                     plist_file_size + 1);
        if (file_data == NULL) {
                ok = false;
                goto out;
@@ -4860,8 +5142,7 @@ static bool fruit_get_bandsize(vfs_handle_struct *handle,
 
        }
 
-       status = close_file(NULL, fsp, NORMAL_CLOSE);
-       fsp = NULL;
+       status = close_file_free(NULL, &fsp, NORMAL_CLOSE);
        if (!NT_STATUS_IS_OK(status)) {
                DBG_ERR("close_file failed: %s\n", nt_errstr(status));
                ok = false;
@@ -4895,11 +5176,10 @@ static bool fruit_get_bandsize(vfs_handle_struct *handle,
 
 out:
        if (fsp != NULL) {
-               status = close_file(NULL, fsp, NORMAL_CLOSE);
+               status = close_file_free(NULL, &fsp, NORMAL_CLOSE);
                if (!NT_STATUS_IS_OK(status)) {
                        DBG_ERR("close_file failed: %s\n", nt_errstr(status));
                }
-               fsp = NULL;
        }
        TALLOC_FREE(plist);
        TALLOC_FREE(smb_fname);
@@ -4913,15 +5193,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;
        size_t nbands;
-       int ret;
+       NTSTATUS status;
 
        path = talloc_asprintf(talloc_tos(),
                               "%s/%s/bands",
@@ -4935,35 +5216,34 @@ 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) {
+       status = OpenDir(talloc_tos(),
+                        handle->conn,
+                        bands_dir,
+                        NULL,
+                        0,
+                        &dir_hnd);
+       if (!NT_STATUS_IS_OK(status)) {
                TALLOC_FREE(bands_dir);
+               errno = map_errno_from_nt_status(status);
                return false;
        }
 
        nbands = 0;
 
-       for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
-            e != NULL;
-            e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
-       {
-               if (ISDOT(e->d_name) || ISDOTDOT(e->d_name)) {
+       while ((dname = ReadDirName(dir_hnd, &talloced)) != NULL) {
+               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));
 
@@ -4975,7 +5255,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;
@@ -4984,7 +5264,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;
        }
@@ -4993,39 +5273,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;
        }
@@ -5033,7 +5319,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;
 }
@@ -5057,12 +5343,13 @@ 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;
        uint64_t dfree;
        uint64_t dsize;
-       int ret;
        bool ok;
+       NTSTATUS status;
 
        SMB_VFS_HANDLE_GET_DATA(handle, config,
                                struct fruit_config_data,
@@ -5078,26 +5365,28 @@ static uint64_t fruit_disk_free(vfs_handle_struct *handle,
                                              _dsize);
        }
 
-       d = SMB_VFS_NEXT_OPENDIR(handle, smb_fname, NULL, 0);
-       if (d == NULL) {
+       status = OpenDir(talloc_tos(),
+                        handle->conn,
+                        smb_fname,
+                        NULL,
+                        0,
+                        &dir_hnd);
+       if (!NT_STATUS_IS_OK(status)) {
+               errno = map_errno_from_nt_status(status);
                return UINT64_MAX;
        }
 
-       for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
-            e != NULL;
-            e = SMB_VFS_NEXT_READDIR(handle, d, NULL))
-       {
-               ok = fruit_tmsize_do_dirent(handle, &state, e);
+       while ((dname = ReadDirName(dir_hnd, &talloced)) != NULL) {
+               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);
@@ -5134,13 +5423,10 @@ static struct vfs_fn_pointers vfs_fruit_fns = {
        .disk_free_fn = fruit_disk_free,
 
        /* File operations */
-       .chmod_fn = fruit_chmod,
-       .chown_fn = fruit_chown,
-       .unlink_fn = fruit_unlink,
+       .fchmod_fn = fruit_fchmod,
        .unlinkat_fn = fruit_unlinkat,
        .renameat_fn = fruit_renameat,
-       .rmdir_fn = fruit_rmdir,
-       .open_fn = fruit_open,
+       .openat_fn = fruit_openat,
        .close_fn = fruit_close,
        .pread_fn = fruit_pread,
        .pwrite_fn = fruit_pwrite,
@@ -5148,15 +5434,17 @@ static struct vfs_fn_pointers vfs_fruit_fns = {
        .pread_recv_fn = fruit_pread_recv,
        .pwrite_send_fn = fruit_pwrite_send,
        .pwrite_recv_fn = fruit_pwrite_recv,
+       .fsync_send_fn = fruit_fsync_send,
+       .fsync_recv_fn = fruit_fsync_recv,
        .stat_fn = fruit_stat,
        .lstat_fn = fruit_lstat,
        .fstat_fn = fruit_fstat,
-       .streaminfo_fn = fruit_streaminfo,
-       .ntimes_fn = fruit_ntimes,
+       .fstreaminfo_fn = fruit_fstreaminfo,
+       .fntimes_fn = fruit_fntimes,
        .ftruncate_fn = fruit_ftruncate,
        .fallocate_fn = fruit_fallocate,
        .create_file_fn = fruit_create_file,
-       .readdir_attr_fn = fruit_readdir_attr,
+       .freaddir_attr_fn = fruit_freaddir_attr,
        .offload_read_send_fn = fruit_offload_read_send,
        .offload_read_recv_fn = fruit_offload_read_recv,
        .offload_write_send_fn = fruit_offload_write_send,