s3:utils: let smbstatus report anonymous signing/encryption explicitly
[samba.git] / source3 / modules / vfs_fruit.c
index 7d9f4f7a1130711066cac33e4612450cf1fa25da..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[] = {
@@ -202,6 +208,48 @@ 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)
 {
@@ -229,7 +277,7 @@ static struct fio *fruit_get_complete_fio(vfs_handle_struct *handle,
 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);
@@ -239,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));
@@ -316,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);
@@ -337,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);
@@ -345,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);
@@ -485,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;
        }
@@ -805,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;
                }
@@ -831,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) {
@@ -920,6 +959,7 @@ static bool readdir_attr_meta_finderi_stream(
        status = SMB_VFS_CREATE_FILE(
                handle->conn,                           /* conn */
                NULL,                                   /* req */
+               NULL,                                   /* dirfsp */
                stream_name,                            /* fname */
                FILE_READ_DATA,                         /* access_mask */
                (FILE_SHARE_READ | FILE_SHARE_WRITE |   /* share_access */
@@ -958,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;
@@ -972,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;
        }
@@ -1368,7 +1408,10 @@ static int fruit_open_meta_stream(vfs_handle_struct *handle,
 {
        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));
@@ -1386,8 +1429,7 @@ static int fruit_open_meta_stream(vfs_handle_struct *handle,
                                 dirfsp,
                                 smb_fname,
                                 fsp,
-                                open_flags,
-                                mode);
+                                &how);
        if (fd != -1) {
                return fd;
        }
@@ -1429,10 +1471,10 @@ static int fruit_open_meta_netatalk(vfs_handle_struct *handle,
         * We know this is a stream open, so fsp->base_fsp must
         * already be open.
         */
-       SMB_ASSERT(fsp->base_fsp != NULL);
+       SMB_ASSERT(fsp_is_alternate_stream(fsp));
        SMB_ASSERT(fsp->base_fsp->fsp_name->fsp == fsp->base_fsp);
 
-       ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
+       ad = ad_get(talloc_tos(), handle, fsp->base_fsp->fsp_name, ADOUBLE_META);
        if (ad != NULL) {
                meta_exists = true;
        }
@@ -1519,8 +1561,8 @@ 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 */
-               errno = EISDIR;
+               /* sorry, but directories don't have a resource fork */
+               errno = ENOENT;
                rc = -1;
                goto exit;
        }
@@ -1542,7 +1584,7 @@ static int fruit_open_rsrc_adouble(vfs_handle_struct *handle,
                goto exit;
        }
 
-       status = adouble_open_from_base_fsp(dirfsp,
+       status = adouble_open_from_base_fsp(fsp->conn->cwd_fsp,
                                            fsp->base_fsp,
                                            ADOUBLE_RSRC,
                                            flags,
@@ -1560,6 +1602,12 @@ static int fruit_open_rsrc_adouble(vfs_handle_struct *handle,
         * 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;
+       }
 
        ref_fio = VFS_ADD_FSP_EXTENSION(handle, ad_fsp,
                                        struct fio,
@@ -1650,14 +1698,17 @@ static int fruit_open_rsrc(vfs_handle_struct *handle,
        fio->config = config;
 
        switch (config->rsrc) {
-       case FRUIT_RSRC_STREAM:
+       case FRUIT_RSRC_STREAM: {
+               struct vfs_open_how how = {
+                       .flags = flags, .mode = mode,
+               };
                fd = SMB_VFS_NEXT_OPENAT(handle,
                                         dirfsp,
                                         smb_fname,
                                         fsp,
-                                        flags,
-                                        mode);
+                                        &how);
                break;
+       }
 
        case FRUIT_RSRC_ADFILE:
                fd = fruit_open_rsrc_adouble(handle, dirfsp, smb_fname,
@@ -1671,6 +1722,7 @@ static int fruit_open_rsrc(vfs_handle_struct *handle,
 
        default:
                DBG_ERR("Unexpected rsrc config [%d]\n", config->rsrc);
+               errno = EINVAL;
                return -1;
        }
 
@@ -1687,8 +1739,7 @@ static int fruit_openat(vfs_handle_struct *handle,
                        const struct files_struct *dirfsp,
                        const struct smb_filename *smb_fname,
                        files_struct *fsp,
-                       int flags,
-                       mode_t mode)
+                       const struct vfs_open_how *how)
 {
        int fd;
 
@@ -1699,31 +1750,36 @@ static int fruit_openat(vfs_handle_struct *handle,
                                           dirfsp,
                                           smb_fname,
                                           fsp,
-                                          flags,
-                                          mode);
+                                          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,
                                     dirfsp,
                                     smb_fname,
                                     fsp,
-                                    flags,
-                                    mode);
+                                    how->flags,
+                                    how->mode);
        } else if (is_afpresource_stream(smb_fname->stream_name)) {
                fd = fruit_open_rsrc(handle,
                                     dirfsp,
                                     smb_fname,
                                     fsp,
-                                    flags,
-                                    mode);
+                                    how->flags,
+                                    how->mode);
        } else {
                fd = SMB_VFS_NEXT_OPENAT(handle,
                                         dirfsp,
                                         smb_fname,
                                         fsp,
-                                        flags,
-                                        mode);
+                                        how);
        }
 
        DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
@@ -1736,19 +1792,19 @@ static int fruit_openat(vfs_handle_struct *handle,
 static int fruit_close_meta(vfs_handle_struct *handle,
                            files_struct *fsp)
 {
-       struct fio *fio = fruit_get_complete_fio(handle, fsp);
        int ret;
        struct fruit_config_data *config = NULL;
 
        SMB_VFS_HANDLE_GET_DATA(handle, config,
                                struct fruit_config_data, return -1);
 
-       if (fio == NULL) {
-               return -1;
-       }
-
        switch (config->meta) {
        case FRUIT_META_STREAM:
+       {
+               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);
@@ -1756,7 +1812,7 @@ static int fruit_close_meta(vfs_handle_struct *handle,
                        ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
                }
                break;
-
+       }
        case FRUIT_META_NETATALK:
                ret = vfs_fake_fd_close(fsp_get_pathref_fd(fsp));
                fsp_set_fd(fsp, -1);
@@ -1774,7 +1830,6 @@ static int fruit_close_meta(vfs_handle_struct *handle,
 static int fruit_close_rsrc(vfs_handle_struct *handle,
                            files_struct *fsp)
 {
-       struct fio *fio = fruit_get_complete_fio(handle, fsp);
        int ret;
        struct fruit_config_data *config = NULL;
 
@@ -1787,10 +1842,16 @@ static int fruit_close_rsrc(vfs_handle_struct *handle,
                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 = vfs_fake_fd_close(fsp_get_pathref_fd(fsp));
@@ -1815,7 +1876,7 @@ static int fruit_close(vfs_handle_struct *handle,
 
        DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(fsp->fsp_name), fd);
 
-       if (!is_named_stream(fsp->fsp_name)) {
+       if (!fsp_is_alternate_stream(fsp)) {
                return SMB_VFS_NEXT_CLOSE(handle, fsp);
        }
 
@@ -1908,7 +1969,7 @@ static int fruit_unlink_meta_netatalk(vfs_handle_struct *handle,
                                      const struct smb_filename *smb_fname)
 {
        SMB_ASSERT(smb_fname->fsp != NULL);
-       SMB_ASSERT(smb_fname->fsp->base_fsp != NULL);
+       SMB_ASSERT(fsp_is_alternate_stream(smb_fname->fsp));
        return SMB_VFS_FREMOVEXATTR(smb_fname->fsp->base_fsp,
                                   AFPINFO_EA_NETATALK);
 }
@@ -2051,7 +2112,7 @@ static int fruit_unlink_rsrc_adouble(vfs_handle_struct *handle,
                        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;
        }
 
@@ -2336,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;
        }
 
@@ -2404,8 +2465,8 @@ static ssize_t fruit_pread_rsrc_adouble(vfs_handle_struct *handle,
        struct adouble *ad = NULL;
        ssize_t nread;
 
-       if (fio->ad_fsp == NULL) {
-               DBG_ERR("ad_fsp=NULL for [%s]\n", fsp_str_dbg(fsp));
+       if (fio == NULL || fio->ad_fsp == NULL) {
+               DBG_ERR("fio/ad_fsp=NULL for [%s]\n", fsp_str_dbg(fsp));
                errno = EBADF;
                return -1;
        }
@@ -2569,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 = fruit_get_complete_fio(handle, fsp);
+       const void *data = indata;
+       char afpinfo_buf[AFP_INFO_SIZE];
        AfpInfo *ai = NULL;
        size_t nwritten;
        int ret;
@@ -2596,6 +2663,9 @@ static ssize_t fruit_pwrite_meta_stream(vfs_handle_struct *handle,
        }
 
        if (fio->fake_fd) {
+               struct vfs_open_how how = {
+                       .flags = fio->flags, .mode = fio->mode,
+               };
                int fd = fsp_get_pathref_fd(fsp);
 
                ret = vfs_fake_fd_close(fd);
@@ -2607,11 +2677,10 @@ static ssize_t fruit_pwrite_meta_stream(vfs_handle_struct *handle,
                }
 
                fd = SMB_VFS_NEXT_OPENAT(handle,
-                                        fsp->conn->cwd_fsp,
+                                        NULL, /* opening a stream */
                                         fsp->fsp_name,
                                         fsp,
-                                        fio->flags,
-                                        fio->mode);
+                                        &how);
                if (fd == -1) {
                        DBG_ERR("On-demand create [%s] in write failed: %s\n",
                                fsp_str_dbg(fsp), strerror(errno));
@@ -2621,7 +2690,7 @@ static ssize_t fruit_pwrite_meta_stream(vfs_handle_struct *handle,
                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;
        }
@@ -2653,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;
@@ -2665,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;
        }
@@ -2737,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;
        }
 
@@ -2751,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) {
@@ -2832,8 +2922,8 @@ static ssize_t fruit_pwrite_rsrc_adouble(vfs_handle_struct *handle,
        ssize_t nwritten;
        int ret;
 
-       if (fio->ad_fsp == NULL) {
-               DBG_ERR("ad_fsp=NULL for [%s]\n", fsp_str_dbg(fsp));
+       if (fio == NULL || fio->ad_fsp == NULL) {
+               DBG_ERR("fio/ad_fsp=NULL for [%s]\n", fsp_str_dbg(fsp));
                errno = EBADF;
                return -1;
        }
@@ -2876,7 +2966,7 @@ static ssize_t fruit_pwrite_rsrc(vfs_handle_struct *handle,
        ssize_t nwritten;
 
        if (fio == NULL) {
-               DBG_ERR("Failed to fetch fsp extension");
+               DBG_ERR("Failed to fetch fsp extension\n");
                return -1;
        }
 
@@ -2994,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;
-       return state->nwritten;
+       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;
+       retval = state->ret;
+       tevent_req_received(req);
+       return retval;
 }
 
 /**
@@ -3060,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));
@@ -3069,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);
@@ -3412,8 +3607,8 @@ static int fruit_fstat_rsrc_adouble(vfs_handle_struct *handle,
        struct adouble *ad = NULL;
        int ret;
 
-       if (fio->ad_fsp == NULL) {
-               DBG_ERR("ad_fsp=NULL for [%s]\n", fsp_str_dbg(fsp));
+       if (fio == NULL || fio->ad_fsp == NULL) {
+               DBG_ERR("fio/ad_fsp=NULL for [%s]\n", fsp_str_dbg(fsp));
                errno = EBADF;
                return -1;
        }
@@ -3623,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;
        }
@@ -3782,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);
 
@@ -3898,7 +4097,7 @@ static int fruit_fntimes(vfs_handle_struct *handle,
                return SMB_VFS_NEXT_FNTIMES(handle, fsp, ft);
        }
 
-       DBG_DEBUG("set btime for %s to %s\n", fsp_str_dbg(fsp),
+       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_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
@@ -3957,8 +4156,8 @@ static int fruit_ftruncate_rsrc_adouble(struct vfs_handle_struct *handle,
        struct adouble *ad = NULL;
        off_t ad_off;
 
-       if (fio->ad_fsp == NULL) {
-               DBG_ERR("ad_fsp=NULL for [%s]\n", fsp_str_dbg(fsp));
+       if (fio == NULL || fio->ad_fsp == NULL) {
+               DBG_ERR("fio/ad_fsp=NULL for [%s]\n", fsp_str_dbg(fsp));
                errno = EBADF;
                return -1;
        }
@@ -4007,7 +4206,7 @@ static int fruit_ftruncate_rsrc(struct vfs_handle_struct *handle,
        int ret;
 
        if (fio == NULL) {
-               DBG_ERR("Failed to fetch fsp extension");
+               DBG_ERR("Failed to fetch fsp extension\n");
                return -1;
        }
 
@@ -4038,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;
@@ -4077,6 +4276,7 @@ static int fruit_ftruncate(struct vfs_handle_struct *handle,
 
 static NTSTATUS fruit_create_file(vfs_handle_struct *handle,
                                  struct smb_request *req,
+                                 struct files_struct *dirfsp,
                                  struct smb_filename *smb_fname,
                                  uint32_t access_mask,
                                  uint32_t share_access,
@@ -4108,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) {
@@ -4123,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, smb_fname,
+               handle, req, dirfsp, smb_fname,
                access_mask, share_access,
                create_disposition, create_options,
                file_attributes, oplock_request,
@@ -4191,8 +4394,8 @@ 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;
@@ -4222,20 +4425,22 @@ static NTSTATUS fruit_freaddir_attr(struct vfs_handle_struct *handle,
 
        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,
-                        fsp->fsp_name,
-                        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);
@@ -4441,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;
 };
 
@@ -4492,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)) {
@@ -4523,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(
@@ -4534,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);
 
@@ -4712,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),
@@ -4890,6 +5102,7 @@ static bool fruit_get_bandsize(vfs_handle_struct *handle,
        status = SMB_VFS_NEXT_CREATE_FILE(
                handle,                         /* conn */
                NULL,                           /* req */
+               NULL,                           /* dirfsp */
                smb_fname,                      /* fname */
                FILE_GENERIC_READ,              /* access_mask */
                FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
@@ -4929,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;
@@ -4964,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);
@@ -4990,8 +5201,8 @@ static bool fruit_get_num_bands(vfs_handle_struct *handle,
        struct smb_Dir *dir_hnd = NULL;
        const char *dname = NULL;
        char *talloced = NULL;
-       long offset = 0;
        size_t nbands;
+       NTSTATUS status;
 
        path = talloc_asprintf(talloc_tos(),
                               "%s/%s/bands",
@@ -5012,17 +5223,21 @@ static bool fruit_get_num_bands(vfs_handle_struct *handle,
                return false;
        }
 
-       dir_hnd = OpenDir(talloc_tos(), handle->conn, bands_dir, NULL, 0);
-       if (dir_hnd == 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;
 
-        while ((dname = ReadDirName(dir_hnd, &offset, NULL, &talloced))
-              != NULL)
-       {
+       while ((dname = ReadDirName(dir_hnd, &talloced)) != NULL) {
                if (ISDOT(dname) || ISDOTDOT(dname)) {
                        continue;
                }
@@ -5131,10 +5346,10 @@ static uint64_t fruit_disk_free(vfs_handle_struct *handle,
        struct smb_Dir *dir_hnd = NULL;
        const char *dname = NULL;
        char *talloced = NULL;
-       long offset = 0;
        uint64_t dfree;
        uint64_t dsize;
        bool ok;
+       NTSTATUS status;
 
        SMB_VFS_HANDLE_GET_DATA(handle, config,
                                struct fruit_config_data,
@@ -5150,14 +5365,18 @@ static uint64_t fruit_disk_free(vfs_handle_struct *handle,
                                              _dsize);
        }
 
-       dir_hnd = OpenDir(talloc_tos(), handle->conn, smb_fname, NULL, 0);
-       if (dir_hnd == 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;
        }
 
-        while ((dname = ReadDirName(dir_hnd, &offset, NULL, &talloced))
-              != NULL)
-       {
+       while ((dname = ReadDirName(dir_hnd, &talloced)) != NULL) {
                ok = fruit_tmsize_do_dirent(handle, &state, dname);
                if (!ok) {
                        TALLOC_FREE(talloced);
@@ -5215,6 +5434,8 @@ 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,