smbd: Fix a typo in a few places
[samba.git] / source3 / modules / vfs_shadow_copy2.c
index f8f0f82e79ea9df61ec2170420581d5bbed09c4b..9d3f5843f43f1ee7210028db25d92646b27ffe47 100644 (file)
@@ -24,7 +24,7 @@
  */
 
 /*
- * This is a second implemetation of a shadow copy module for exposing
+ * This is a second implementation of a shadow copy module for exposing
  * file system snapshots to windows clients as shadow copies.
  *
  * See the manual page for documentation.
@@ -69,7 +69,6 @@ struct shadow_copy2_snaplist_info {
        time_t fetch_time; /* snaplist update time */
 };
 
-
 /*
  * shadow_copy2 private structure. This structure will be
  * used to keep module specific information
@@ -90,7 +89,7 @@ static int shadow_copy2_get_shadow_copy_data(
        bool labels);
 
 /**
- *This function will create a new snapshot list entry and
+ * This function will create a new snapshot list entry and
  * return to the caller. This entry will also be added to
  * the global snapshot list.
  *
@@ -115,7 +114,7 @@ static struct shadow_copy2_snapentry *shadow_copy2_create_snapentry(
 }
 
 /**
- *This function will delete the entire snaplist and reset
+ * This function will delete the entire snaplist and reset
  * priv->snaps->snaplist to NULL.
  *
  * @param[in] priv shadow_copye specific data structure
@@ -369,7 +368,7 @@ static char *shadow_copy2_insert_string(TALLOC_CTX *mem_ctx,
                                         config->snapdir, snaptime_string);
        }
        if (result == NULL) {
-               DEBUG(1, (__location__ " talloc_asprintf failed\n"));
+               DBG_WARNING("talloc_asprintf failed\n");
        }
 
        return result;
@@ -406,7 +405,7 @@ static char *shadow_copy2_snapshot_path(TALLOC_CTX *mem_ctx,
        result = talloc_asprintf(mem_ctx, "%s/%s",
                                 priv->config->snapshot_basepath, snaptime_string);
        if (result == NULL) {
-               DEBUG(1, (__location__ " talloc_asprintf failed\n"));
+               DBG_WARNING("talloc_asprintf failed\n");
        }
 
        return result;
@@ -572,13 +571,13 @@ static int check_for_converted_path(TALLOC_CTX *mem_ctx,
  * This function does two things.
  *
  * 1). Checks if an incoming filename is already a
- * snapshot converted pathname.
+ *     snapshot converted pathname.
  *     If so, it returns the pathname truncated
  *     at the snapshot point which will be used
  *     as the connectpath, and then does an early return.
  *
  * 2). Checks if an incoming filename contains an
- * SMB-layer @GMT- style timestamp.
+ *     SMB-layer @GMT- style timestamp.
  *     If so, it strips the timestamp, and returns
  *     both the timestamp and the stripped path
  *     (making it cwd-relative).
@@ -813,8 +812,9 @@ static char *shadow_copy2_do_convert(TALLOC_CTX *mem_ctx,
                        goto fail;
                }
 
-               ZERO_STRUCT(converted_fname);
-               converted_fname.base_name = converted;
+               converted_fname = (struct smb_filename) {
+                       .base_name = converted,
+               };
 
                ret = SMB_VFS_NEXT_LSTAT(handle, &converted_fname);
                DEBUG(10, ("Trying[not snapdirseverywhere] %s: %d (%s)\n",
@@ -864,7 +864,7 @@ static char *shadow_copy2_do_convert(TALLOC_CTX *mem_ctx,
        insertlen = talloc_get_size(insert)-1;
 
        /*
-        * Note: We deliberatly don't expensively initialize the
+        * Note: We deliberately don't expensively initialize the
         * array with talloc_zero here: Putting zero into
         * converted[pathlen+insertlen] below is sufficient, because
         * in the following for loop, the insert string is inserted
@@ -901,8 +901,9 @@ static char *shadow_copy2_do_convert(TALLOC_CTX *mem_ctx,
        memcpy(converted, path, pathlen+1);
        converted[pathlen+insertlen] = '\0';
 
-       ZERO_STRUCT(converted_fname);
-       converted_fname.base_name = converted;
+       converted_fname = (struct smb_filename) {
+               .base_name = converted,
+       };
 
        for (i = num_slashes-1; i>=0; i--) {
                int ret;
@@ -1000,7 +1001,7 @@ static void convert_sbuf(vfs_handle_struct *handle, const char *fname,
                                return);
 
        if (priv->config->fixinodes) {
-               /* some snapshot systems, like GPFS, return the name
+               /* some snapshot systems, like GPFS, return the same
                   device:inode for the snapshot files as the current
                   files. That breaks the 'restore' button in the shadow copy
                   GUI, as the client gets a sharing violation.
@@ -1168,19 +1169,45 @@ static int shadow_copy2_linkat(vfs_handle_struct *handle,
 static int shadow_copy2_stat(vfs_handle_struct *handle,
                             struct smb_filename *smb_fname)
 {
+       struct shadow_copy2_private *priv = NULL;
        time_t timestamp = 0;
        char *stripped = NULL;
+       bool converted = false;
+       char *abspath = NULL;
        char *tmp;
-       int saved_errno = 0;
-       int ret;
+       int ret = 0;
 
-       if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
-                                        smb_fname,
-                                        &timestamp, &stripped)) {
+       SMB_VFS_HANDLE_GET_DATA(handle, priv, struct shadow_copy2_private,
+                               return -1);
+
+       if (!shadow_copy2_strip_snapshot_converted(talloc_tos(),
+                                                  handle,
+                                                  smb_fname,
+                                                  &timestamp,
+                                                  &stripped,
+                                                  &converted)) {
                return -1;
        }
        if (timestamp == 0) {
-               return SMB_VFS_NEXT_STAT(handle, smb_fname);
+               TALLOC_FREE(stripped);
+               ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
+               if (ret != 0) {
+                       return ret;
+               }
+               if (!converted) {
+                       return 0;
+               }
+
+               abspath = make_path_absolute(talloc_tos(),
+                                            priv,
+                                            smb_fname->base_name);
+               if (abspath == NULL) {
+                       return -1;
+               }
+
+               convert_sbuf(handle, abspath, &smb_fname->st);
+               TALLOC_FREE(abspath);
+               return 0;
        }
 
        tmp = smb_fname->base_name;
@@ -1194,38 +1221,70 @@ static int shadow_copy2_stat(vfs_handle_struct *handle,
        }
 
        ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
-       if (ret == -1) {
-               saved_errno = errno;
+       if (ret != 0) {
+               goto out;
        }
 
+       abspath = make_path_absolute(talloc_tos(),
+                                    priv,
+                                    smb_fname->base_name);
+       if (abspath == NULL) {
+               ret = -1;
+               goto out;
+       }
+
+       convert_sbuf(handle, abspath, &smb_fname->st);
+       TALLOC_FREE(abspath);
+
+out:
        TALLOC_FREE(smb_fname->base_name);
        smb_fname->base_name = tmp;
 
-       if (ret == 0) {
-               convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
-       }
-       if (saved_errno != 0) {
-               errno = saved_errno;
-       }
        return ret;
 }
 
 static int shadow_copy2_lstat(vfs_handle_struct *handle,
                              struct smb_filename *smb_fname)
 {
+       struct shadow_copy2_private *priv = NULL;
        time_t timestamp = 0;
        char *stripped = NULL;
+       bool converted = false;
+       char *abspath = NULL;
        char *tmp;
-       int saved_errno = 0;
-       int ret;
+       int ret = 0;
 
-       if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
-                                        smb_fname,
-                                        &timestamp, &stripped)) {
+       SMB_VFS_HANDLE_GET_DATA(handle, priv, struct shadow_copy2_private,
+                               return -1);
+
+       if (!shadow_copy2_strip_snapshot_converted(talloc_tos(),
+                                                  handle,
+                                                  smb_fname,
+                                                  &timestamp,
+                                                  &stripped,
+                                                  &converted)) {
                return -1;
        }
        if (timestamp == 0) {
-               return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
+               TALLOC_FREE(stripped);
+               ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
+               if (ret != 0) {
+                       return ret;
+               }
+               if (!converted) {
+                       return 0;
+               }
+
+               abspath = make_path_absolute(talloc_tos(),
+                                            priv,
+                                            smb_fname->base_name);
+               if (abspath == NULL) {
+                       return -1;
+               }
+
+               convert_sbuf(handle, abspath, &smb_fname->st);
+               TALLOC_FREE(abspath);
+               return 0;
        }
 
        tmp = smb_fname->base_name;
@@ -1239,45 +1298,76 @@ static int shadow_copy2_lstat(vfs_handle_struct *handle,
        }
 
        ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
-       if (ret == -1) {
-               saved_errno = errno;
+       if (ret != 0) {
+               goto out;
+       }
+
+       abspath = make_path_absolute(talloc_tos(),
+                                    priv,
+                                    smb_fname->base_name);
+       if (abspath == NULL) {
+               ret = -1;
+               goto out;
        }
 
+       convert_sbuf(handle, abspath, &smb_fname->st);
+       TALLOC_FREE(abspath);
+
+out:
        TALLOC_FREE(smb_fname->base_name);
        smb_fname->base_name = tmp;
 
-       if (ret == 0) {
-               convert_sbuf(handle, smb_fname->base_name, &smb_fname->st);
-       }
-       if (saved_errno != 0) {
-               errno = saved_errno;
-       }
        return ret;
 }
 
 static int shadow_copy2_fstat(vfs_handle_struct *handle, files_struct *fsp,
                              SMB_STRUCT_STAT *sbuf)
 {
+       struct shadow_copy2_private *priv = NULL;
        time_t timestamp = 0;
        struct smb_filename *orig_smb_fname = NULL;
        struct smb_filename vss_smb_fname;
        struct smb_filename *orig_base_smb_fname = NULL;
        struct smb_filename vss_base_smb_fname;
        char *stripped = NULL;
-       int saved_errno = 0;
+       char *abspath = NULL;
+       bool converted = false;
        bool ok;
        int ret;
 
-       ok = shadow_copy2_strip_snapshot(talloc_tos(), handle,
-                                        fsp->fsp_name,
-                                        &timestamp, &stripped);
+       SMB_VFS_HANDLE_GET_DATA(handle, priv, struct shadow_copy2_private,
+                               return -1);
+
+       ok = shadow_copy2_strip_snapshot_converted(talloc_tos(),
+                                                  handle,
+                                                  fsp->fsp_name,
+                                                  &timestamp,
+                                                  &stripped,
+                                                  &converted);
        if (!ok) {
                return -1;
        }
 
        if (timestamp == 0) {
                TALLOC_FREE(stripped);
-               return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
+               ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
+               if (ret != 0) {
+                       return ret;
+               }
+               if (!converted) {
+                       return 0;
+               }
+
+               abspath = make_path_absolute(talloc_tos(),
+                                            priv,
+                                            fsp->fsp_name->base_name);
+               if (abspath == NULL) {
+                       return -1;
+               }
+
+               convert_sbuf(handle, abspath, sbuf);
+               TALLOC_FREE(abspath);
+               return 0;
        }
 
        vss_smb_fname = *fsp->fsp_name;
@@ -1293,7 +1383,7 @@ static int shadow_copy2_fstat(vfs_handle_struct *handle, files_struct *fsp,
        orig_smb_fname = fsp->fsp_name;
        fsp->fsp_name = &vss_smb_fname;
 
-       if (fsp->base_fsp != NULL) {
+       if (fsp_is_alternate_stream(fsp)) {
                vss_base_smb_fname = *fsp->base_fsp->fsp_name;
                vss_base_smb_fname.base_name = vss_smb_fname.base_name;
                orig_base_smb_fname = fsp->base_fsp->fsp_name;
@@ -1301,79 +1391,229 @@ static int shadow_copy2_fstat(vfs_handle_struct *handle, files_struct *fsp,
        }
 
        ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
+       if (ret != 0) {
+               goto out;
+       }
+
+       abspath = make_path_absolute(talloc_tos(),
+                                    priv,
+                                    fsp->fsp_name->base_name);
+       if (abspath == NULL) {
+               ret = -1;
+               goto out;
+       }
+
+       convert_sbuf(handle, abspath, sbuf);
+       TALLOC_FREE(abspath);
+
+out:
        fsp->fsp_name = orig_smb_fname;
-       if (fsp->base_fsp != NULL) {
+       if (fsp_is_alternate_stream(fsp)) {
                fsp->base_fsp->fsp_name = orig_base_smb_fname;
        }
-       if (ret == -1) {
-               saved_errno = errno;
+
+       return ret;
+}
+
+static int shadow_copy2_fstatat(
+       struct vfs_handle_struct *handle,
+       const struct files_struct *dirfsp,
+       const struct smb_filename *smb_fname_in,
+       SMB_STRUCT_STAT *sbuf,
+       int flags)
+{
+       struct shadow_copy2_private *priv = NULL;
+       struct smb_filename *smb_fname = NULL;
+       time_t timestamp = 0;
+       char *stripped = NULL;
+       char *abspath = NULL;
+       bool converted = false;
+       int ret;
+       bool ok;
+
+       SMB_VFS_HANDLE_GET_DATA(handle, priv, struct shadow_copy2_private,
+                               return -1);
+
+       smb_fname = full_path_from_dirfsp_atname(talloc_tos(),
+                                                dirfsp,
+                                                smb_fname_in);
+       if (smb_fname == NULL) {
+               errno = ENOMEM;
+               return -1;
        }
 
-       if (ret == 0) {
-               convert_sbuf(handle, fsp->fsp_name->base_name, sbuf);
+       ok = shadow_copy2_strip_snapshot_converted(talloc_tos(),
+                                                  handle,
+                                                  smb_fname,
+                                                  &timestamp,
+                                                  &stripped,
+                                                  &converted);
+       if (!ok) {
+               return -1;
        }
-       if (saved_errno != 0) {
+       if (timestamp == 0) {
+               TALLOC_FREE(stripped);
+               ret = SMB_VFS_NEXT_FSTATAT(
+                       handle, dirfsp, smb_fname_in, sbuf, flags);
+               if (ret != 0) {
+                       return ret;
+               }
+               if (!converted) {
+                       return 0;
+               }
+
+               abspath = make_path_absolute(
+                       talloc_tos(), priv, smb_fname->base_name);
+               if (abspath == NULL) {
+                       errno = ENOMEM;
+                       return -1;
+               }
+
+               convert_sbuf(handle, abspath, sbuf);
+               TALLOC_FREE(abspath);
+               return 0;
+       }
+
+       smb_fname->base_name = shadow_copy2_convert(
+               smb_fname, handle, stripped, timestamp);
+       TALLOC_FREE(stripped);
+       if (smb_fname->base_name == NULL) {
+               TALLOC_FREE(smb_fname);
+               errno = ENOMEM;
+               return -1;
+       }
+
+       ret = SMB_VFS_NEXT_FSTATAT(handle,
+                                  dirfsp,
+                                  smb_fname,
+                                  sbuf,
+                                  flags);
+       if (ret != 0) {
+               int saved_errno = errno;
+               TALLOC_FREE(smb_fname);
                errno = saved_errno;
+               return -1;
        }
-       return ret;
+
+       abspath = make_path_absolute(
+               talloc_tos(), priv, smb_fname->base_name);
+       if (abspath == NULL) {
+               TALLOC_FREE(smb_fname);
+               errno = ENOMEM;
+               return -1;
+       }
+
+       convert_sbuf(handle, abspath, sbuf);
+       TALLOC_FREE(abspath);
+
+       TALLOC_FREE(smb_fname);
+
+       return 0;
+}
+
+static struct smb_filename *shadow_copy2_openat_name(
+       TALLOC_CTX *mem_ctx,
+       const struct files_struct *dirfsp,
+       const struct files_struct *fsp,
+       const struct smb_filename *smb_fname_in)
+{
+       struct smb_filename *result = NULL;
+
+       if (fsp->base_fsp != NULL) {
+               struct smb_filename *base_fname = fsp->base_fsp->fsp_name;
+
+               if (smb_fname_in->base_name[0] == '/') {
+                       /*
+                        * Special-case stream names from streams_depot
+                        */
+                       result = cp_smb_filename(mem_ctx, smb_fname_in);
+               } else {
+
+                       SMB_ASSERT(is_named_stream(smb_fname_in));
+
+                       result = synthetic_smb_fname(mem_ctx,
+                                                    base_fname->base_name,
+                                                    smb_fname_in->stream_name,
+                                                    &smb_fname_in->st,
+                                                    smb_fname_in->twrp,
+                                                    smb_fname_in->flags);
+               }
+       } else {
+               result = full_path_from_dirfsp_atname(
+                       mem_ctx, dirfsp, smb_fname_in);
+       }
+
+       return result;
 }
 
-static int shadow_copy2_open(vfs_handle_struct *handle,
-                            struct smb_filename *smb_fname, files_struct *fsp,
-                            int flags, mode_t mode)
+static int shadow_copy2_openat(vfs_handle_struct *handle,
+                              const struct files_struct *dirfsp,
+                              const struct smb_filename *smb_fname_in,
+                              struct files_struct *fsp,
+                              const struct vfs_open_how *_how)
 {
+       struct vfs_open_how how = *_how;
+       struct smb_filename *smb_fname = NULL;
        time_t timestamp = 0;
        char *stripped = NULL;
-       char *tmp;
-       bool is_converted = false;
        int saved_errno = 0;
        int ret;
+       bool ok;
 
-       if (!shadow_copy2_strip_snapshot_converted(talloc_tos(), handle,
+       if (how.resolve != 0) {
+               errno = ENOSYS;
+               return -1;
+       }
+
+       smb_fname = shadow_copy2_openat_name(
+               talloc_tos(), dirfsp, fsp, smb_fname_in);
+       if (smb_fname == NULL) {
+               errno = ENOMEM;
+               return -1;
+       }
+
+       ok = shadow_copy2_strip_snapshot(talloc_tos(),
+                                        handle,
                                         smb_fname,
-                                        &timestamp, &stripped,
-                                        &is_converted)) {
+                                        &timestamp,
+                                        &stripped);
+       if (!ok) {
+               TALLOC_FREE(smb_fname);
                return -1;
        }
        if (timestamp == 0) {
-               if (is_converted) {
-                       /*
-                        * Just pave over the user requested mode and use
-                        * O_RDONLY. Later attempts by the client to write on
-                        * the handle will fail in the pwrite() syscall with
-                        * EINVAL which we carefully map to EROFS. In sum, this
-                        * matches Windows behaviour.
-                        */
-                       flags = O_RDONLY;
-               }
-               return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
+               TALLOC_FREE(stripped);
+               TALLOC_FREE(smb_fname);
+               return SMB_VFS_NEXT_OPENAT(handle,
+                                          dirfsp,
+                                          smb_fname_in,
+                                          fsp,
+                                          &how);
        }
 
-       tmp = smb_fname->base_name;
-       smb_fname->base_name = shadow_copy2_convert(
-               talloc_tos(), handle, stripped, timestamp);
-       TALLOC_FREE(stripped);
-
+       smb_fname->base_name = shadow_copy2_convert(smb_fname,
+                                              handle,
+                                              stripped,
+                                              timestamp);
        if (smb_fname->base_name == NULL) {
-               smb_fname->base_name = tmp;
+               int err = errno;
+               TALLOC_FREE(stripped);
+               TALLOC_FREE(smb_fname);
+               errno = err;
                return -1;
        }
+       TALLOC_FREE(stripped);
 
-       /*
-        * Just pave over the user requested mode and use O_RDONLY. Later
-        * attempts by the client to write on the handle will fail in the
-        * pwrite() syscall with EINVAL which we carefully map to EROFS. In sum,
-        * this matches Windows behaviour.
-        */
-       flags = O_RDONLY;
-
-       ret = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
+       ret = SMB_VFS_NEXT_OPENAT(handle,
+                                 dirfsp,
+                                 smb_fname,
+                                 fsp,
+                                 &how);
        if (ret == -1) {
                saved_errno = errno;
        }
 
-       TALLOC_FREE(smb_fname->base_name);
-       smb_fname->base_name = tmp;
+       TALLOC_FREE(smb_fname);
 
        if (saved_errno != 0) {
                errno = saved_errno;
@@ -1403,24 +1643,26 @@ static int shadow_copy2_unlinkat(vfs_handle_struct *handle,
                        flags);
 }
 
-static int shadow_copy2_chmod(vfs_handle_struct *handle,
-                       const struct smb_filename *smb_fname,
-                       mode_t mode)
+static int shadow_copy2_fchmod(vfs_handle_struct *handle,
+                       struct files_struct *fsp,
+                       mode_t mode)
 {
        time_t timestamp = 0;
+       const struct smb_filename *smb_fname = NULL;
 
+       smb_fname = fsp->fsp_name;
        if (!shadow_copy2_strip_snapshot(talloc_tos(),
-                               handle,
-                               smb_fname,
-                               &timestamp,
-                               NULL)) {
+                                       handle,
+                                       smb_fname,
+                                       &timestamp,
+                                       NULL)) {
                return -1;
        }
        if (timestamp != 0) {
                errno = EROFS;
                return -1;
        }
-       return SMB_VFS_NEXT_CHMOD(handle, smb_fname, mode);
+       return SMB_VFS_NEXT_FCHMOD(handle, fsp, mode);
 }
 
 static void store_cwd_data(vfs_handle_struct *handle,
@@ -1525,26 +1767,28 @@ static int shadow_copy2_chdir(vfs_handle_struct *handle,
        return ret;
 }
 
-static int shadow_copy2_ntimes(vfs_handle_struct *handle,
-                              const struct smb_filename *smb_fname,
-                              struct smb_file_time *ft)
+static int shadow_copy2_fntimes(vfs_handle_struct *handle,
+                               files_struct *fsp,
+                               struct smb_file_time *ft)
 {
        time_t timestamp = 0;
 
-       if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
-                                        smb_fname,
-                                        &timestamp, NULL)) {
+       if (!shadow_copy2_strip_snapshot(talloc_tos(),
+                                        handle,
+                                        fsp->fsp_name,
+                                        &timestamp,
+                                        NULL)) {
                return -1;
        }
        if (timestamp != 0) {
                errno = EROFS;
                return -1;
        }
-       return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
+       return SMB_VFS_NEXT_FNTIMES(handle, fsp, ft);
 }
 
 static int shadow_copy2_readlinkat(vfs_handle_struct *handle,
-                               files_struct *dirfsp,
+                               const struct files_struct *dirfsp,
                                const struct smb_filename *smb_fname,
                                char *buf,
                                size_t bufsiz)
@@ -1553,26 +1797,43 @@ static int shadow_copy2_readlinkat(vfs_handle_struct *handle,
        char *stripped = NULL;
        int saved_errno = 0;
        int ret;
+       struct smb_filename *full_fname = NULL;
        struct smb_filename *conv = NULL;
 
-       if (!shadow_copy2_strip_snapshot(talloc_tos(), handle,
-                                        smb_fname,
-                                        &timestamp, &stripped)) {
+       full_fname = full_path_from_dirfsp_atname(talloc_tos(),
+                                                 dirfsp,
+                                                 smb_fname);
+       if (full_fname == NULL) {
+               errno = ENOMEM;
+               return -1;
+       }
+
+       if (!shadow_copy2_strip_snapshot(talloc_tos(),
+                                       handle,
+                                       full_fname,
+                                       &timestamp,
+                                       &stripped)) {
+               TALLOC_FREE(full_fname);
                return -1;
        }
+
        if (timestamp == 0) {
+               TALLOC_FREE(full_fname);
+               TALLOC_FREE(stripped);
                return SMB_VFS_NEXT_READLINKAT(handle,
                                dirfsp,
                                smb_fname,
                                buf,
                                bufsiz);
        }
-       conv = cp_smb_filename(talloc_tos(), smb_fname);
+       conv = cp_smb_filename(talloc_tos(), full_fname);
        if (conv == NULL) {
+               TALLOC_FREE(full_fname);
                TALLOC_FREE(stripped);
                errno = ENOMEM;
                return -1;
        }
+       TALLOC_FREE(full_fname);
        conv->base_name = shadow_copy2_convert(
                conv, handle, stripped, timestamp);
        TALLOC_FREE(stripped);
@@ -1580,7 +1841,7 @@ static int shadow_copy2_readlinkat(vfs_handle_struct *handle,
                return -1;
        }
        ret = SMB_VFS_NEXT_READLINKAT(handle,
-                               dirfsp,
+                               handle->conn->cwd_fsp,
                                conv,
                                buf,
                                bufsiz);
@@ -1668,6 +1929,7 @@ done:
  * otherwise return NULL.
  */
 static char *have_snapdir(struct vfs_handle_struct *handle,
+                         TALLOC_CTX *mem_ctx,
                          const char *path)
 {
        struct smb_filename smb_fname;
@@ -1677,9 +1939,10 @@ static char *have_snapdir(struct vfs_handle_struct *handle,
        SMB_VFS_HANDLE_GET_DATA(handle, priv, struct shadow_copy2_private,
                                return NULL);
 
-       ZERO_STRUCT(smb_fname);
-       smb_fname.base_name = talloc_asprintf(talloc_tos(), "%s/%s",
-                                             path, priv->config->snapdir);
+       smb_fname = (struct smb_filename) {
+               .base_name = talloc_asprintf(
+                       mem_ctx, "%s/%s", path, priv->config->snapdir),
+       };
        if (smb_fname.base_name == NULL) {
                return NULL;
        }
@@ -1692,43 +1955,6 @@ static char *have_snapdir(struct vfs_handle_struct *handle,
        return NULL;
 }
 
-static bool check_access_snapdir(struct vfs_handle_struct *handle,
-                               const char *path)
-{
-       struct smb_filename smb_fname;
-       int ret;
-       NTSTATUS status;
-
-       ZERO_STRUCT(smb_fname);
-       smb_fname.base_name = talloc_asprintf(talloc_tos(),
-                                               "%s",
-                                               path);
-       if (smb_fname.base_name == NULL) {
-               return false;
-       }
-
-       ret = SMB_VFS_NEXT_STAT(handle, &smb_fname);
-       if (ret != 0 || !S_ISDIR(smb_fname.st.st_ex_mode)) {
-               TALLOC_FREE(smb_fname.base_name);
-               return false;
-       }
-
-       status = smbd_check_access_rights(handle->conn,
-                                       handle->conn->cwd_fsp,
-                                       &smb_fname,
-                                       false,
-                                       SEC_DIR_LIST);
-       if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(0,("user does not have list permission "
-                       "on snapdir %s\n",
-                       smb_fname.base_name));
-               TALLOC_FREE(smb_fname.base_name);
-               return false;
-       }
-       TALLOC_FREE(smb_fname.base_name);
-       return true;
-}
-
 /**
  * Find the snapshot directory (if any) for the given
  * filename (which is relative to the share).
@@ -1748,7 +1974,7 @@ static const char *shadow_copy2_find_snapdir(TALLOC_CTX *mem_ctx,
        config = priv->config;
 
        /*
-        * If the non-snapdisrseverywhere mode, we should not search!
+        * If the non-snapdirseverywhere mode, we should not search!
         */
        if (!config->snapdirseverywhere) {
                return config->snapshot_basepath;
@@ -1761,7 +1987,7 @@ static const char *shadow_copy2_find_snapdir(TALLOC_CTX *mem_ctx,
                return NULL;
        }
 
-       snapdir = have_snapdir(handle, path);
+       snapdir = have_snapdir(handle, talloc_tos(), path);
        if (snapdir != NULL) {
                TALLOC_FREE(path);
                return snapdir;
@@ -1771,7 +1997,7 @@ static const char *shadow_copy2_find_snapdir(TALLOC_CTX *mem_ctx,
 
                p[0] = '\0';
 
-               snapdir = have_snapdir(handle, path);
+               snapdir = have_snapdir(handle, talloc_tos(), path);
                if (snapdir != NULL) {
                        TALLOC_FREE(path);
                        return snapdir;
@@ -1785,7 +2011,7 @@ static bool shadow_copy2_snapshot_to_gmt(vfs_handle_struct *handle,
                                         const char *name,
                                         char *gmt, size_t gmt_len)
 {
-       struct tm timestamp;
+       struct tm timestamp = { .tm_sec = 0, };
        time_t timestamp_t;
        unsigned long int timestamp_long;
        const char *fmt;
@@ -1831,7 +2057,6 @@ static bool shadow_copy2_snapshot_to_gmt(vfs_handle_struct *handle,
                }
        }
 
-       ZERO_STRUCT(timestamp);
        if (config->use_sscanf) {
                if (sscanf(name, fmt, &timestamp_long) != 1) {
                        DEBUG(10, ("shadow_copy2_snapshot_to_gmt: "
@@ -1850,7 +2075,7 @@ static bool shadow_copy2_snapshot_to_gmt(vfs_handle_struct *handle,
                }
                DEBUG(10, ("shadow_copy2_snapshot_to_gmt: match %s: %s\n",
                           fmt, name));
-               
+
                if (config->use_localtime) {
                        timestamp.tm_isdst = -1;
                        timestamp_t = mktime(&timestamp);
@@ -1920,15 +2145,19 @@ static int shadow_copy2_get_shadow_copy_data(
        const char *snapdir;
        struct smb_filename *snapdir_smb_fname = NULL;
        struct files_struct *dirfsp = NULL;
+       struct files_struct *fspcwd = NULL;
        struct dirent *d;
        TALLOC_CTX *tmp_ctx = talloc_stackframe();
        struct shadow_copy2_private *priv = NULL;
        struct shadow_copy2_snapentry *tmpentry = NULL;
        bool get_snaplist = false;
-       bool access_granted = false;
-       int open_flags = O_RDONLY;
+       struct vfs_open_how how = {
+               .flags = O_RDONLY, .mode = 0,
+       };
+       int fd;
        int ret = -1;
        NTSTATUS status;
+       int saved_errno = 0;
 
        snapdir = shadow_copy2_find_snapdir(tmp_ctx, handle, fsp->fsp_name);
        if (snapdir == NULL) {
@@ -1938,13 +2167,6 @@ static int shadow_copy2_get_shadow_copy_data(
                goto done;
        }
 
-       access_granted = check_access_snapdir(handle, snapdir);
-       if (!access_granted) {
-               DEBUG(0,("access denied on listing snapdir %s\n", snapdir));
-               errno = EACCES;
-               goto done;
-       }
-
        snapdir_smb_fname = synthetic_smb_fname(talloc_tos(),
                                        snapdir,
                                        NULL,
@@ -1956,10 +2178,9 @@ static int shadow_copy2_get_shadow_copy_data(
                goto done;
        }
 
-       status = create_internal_dirfsp_at(handle->conn,
-                                          handle->conn->cwd_fsp,
-                                          snapdir_smb_fname,
-                                          &dirfsp);
+       status = create_internal_dirfsp(handle->conn,
+                                       snapdir_smb_fname,
+                                       &dirfsp);
        if (!NT_STATUS_IS_OK(status)) {
                DBG_WARNING("create_internal_dir_fsp() failed for '%s'"
                            " - %s\n", snapdir, nt_errstr(status));
@@ -1967,27 +2188,47 @@ static int shadow_copy2_get_shadow_copy_data(
                goto done;
        }
 
+       status = vfs_at_fspcwd(talloc_tos(), handle->conn, &fspcwd);
+       if (!NT_STATUS_IS_OK(status)) {
+               errno = ENOMEM;
+               goto done;
+       }
+
 #ifdef O_DIRECTORY
-       open_flags |= O_DIRECTORY;
+       how.flags |= O_DIRECTORY;
 #endif
 
-       dirfsp->fh->fd = SMB_VFS_NEXT_OPEN(handle,
-                                          snapdir_smb_fname,
-                                          dirfsp,
-                                          open_flags,
-                                          0);
-       if (dirfsp->fh->fd == -1) {
+       fd = SMB_VFS_NEXT_OPENAT(handle,
+                                fspcwd,
+                                snapdir_smb_fname,
+                                dirfsp,
+                                &how);
+       if (fd == -1) {
                DBG_WARNING("SMB_VFS_NEXT_OPEN failed for '%s'"
                            " - %s\n", snapdir, strerror(errno));
                errno = ENOSYS;
                goto done;
        }
+       fsp_set_fd(dirfsp, fd);
 
-       p = SMB_VFS_NEXT_FDOPENDIR(handle, dirfsp, NULL, 0);
-       if (!p) {
-               DEBUG(2,("shadow_copy2: SMB_VFS_NEXT_OPENDIR() failed for '%s'"
-                        " - %s\n", snapdir, strerror(errno)));
-               errno = ENOSYS;
+       /* Now we have the handle, check access here. */
+       status = smbd_check_access_rights_fsp(fspcwd,
+                                       dirfsp,
+                                       false,
+                                       SEC_DIR_LIST);
+       if (!NT_STATUS_IS_OK(status)) {
+               DBG_ERR("user does not have list permission "
+                       "on snapdir %s\n",
+                       fsp_str_dbg(dirfsp));
+               errno = EACCES;
+               goto done;
+       }
+
+       p = SMB_VFS_NEXT_FDOPENDIR(handle, dirfsp, NULL, 0);
+       if (!p) {
+               DBG_NOTICE("shadow_copy2: SMB_VFS_NEXT_FDOPENDIR() failed for '%s'"
+                          " - %s\n", snapdir, strerror(errno));
+               errno = ENOSYS;
                goto done;
        }
 
@@ -2018,7 +2259,7 @@ static int shadow_copy2_get_shadow_copy_data(
                time(&(priv->snaps->fetch_time));
        }
 
-       while ((d = SMB_VFS_NEXT_READDIR(handle, p, NULL))) {
+       while ((d = SMB_VFS_NEXT_READDIR(handle, dirfsp, p))) {
                char snapshot[GMT_NAME_LEN+1];
                SHADOW_COPY_LABEL *tlabels;
 
@@ -2081,61 +2322,30 @@ static int shadow_copy2_get_shadow_copy_data(
        ret = 0;
 
 done:
+       if (ret != 0) {
+               saved_errno = errno;
+       }
+       TALLOC_FREE(fspcwd );
        if (p != NULL) {
                SMB_VFS_NEXT_CLOSEDIR(handle, p);
                p = NULL;
+               if (dirfsp != NULL) {
+                       /*
+                        * VFS_CLOSEDIR implicitly
+                        * closed the associated fd.
+                        */
+                       fsp_set_fd(dirfsp, -1);
+               }
        }
        if (dirfsp != NULL) {
                fd_close(dirfsp);
                file_free(NULL, dirfsp);
        }
        TALLOC_FREE(tmp_ctx);
-       return ret;
-}
-
-static NTSTATUS shadow_copy2_get_nt_acl(vfs_handle_struct *handle,
-                                       const struct smb_filename *smb_fname,
-                                       uint32_t security_info,
-                                       TALLOC_CTX *mem_ctx,
-                                       struct security_descriptor **ppdesc)
-{
-       time_t timestamp = 0;
-       char *stripped = NULL;
-       NTSTATUS status;
-       char *conv;
-       struct smb_filename *conv_smb_fname = NULL;
-
-       if (!shadow_copy2_strip_snapshot(talloc_tos(),
-                                       handle,
-                                       smb_fname,
-                                       &timestamp,
-                                       &stripped)) {
-               return map_nt_error_from_unix(errno);
-       }
-       if (timestamp == 0) {
-               return SMB_VFS_NEXT_GET_NT_ACL(handle, smb_fname, security_info,
-                                              mem_ctx, ppdesc);
-       }
-       conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
-       TALLOC_FREE(stripped);
-       if (conv == NULL) {
-               return map_nt_error_from_unix(errno);
-       }
-       conv_smb_fname = synthetic_smb_fname(talloc_tos(),
-                                       conv,
-                                       NULL,
-                                       NULL,
-                                       0,
-                                       smb_fname->flags);
-       if (conv_smb_fname == NULL) {
-               TALLOC_FREE(conv);
-               return NT_STATUS_NO_MEMORY;
+       if (saved_errno != 0) {
+               errno = saved_errno;
        }
-       status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv_smb_fname, security_info,
-                                        mem_ctx, ppdesc);
-       TALLOC_FREE(conv);
-       TALLOC_FREE(conv_smb_fname);
-       return status;
+       return ret;
 }
 
 static int shadow_copy2_mkdirat(vfs_handle_struct *handle,
@@ -2143,15 +2353,26 @@ static int shadow_copy2_mkdirat(vfs_handle_struct *handle,
                                const struct smb_filename *smb_fname,
                                mode_t mode)
 {
+       struct smb_filename *full_fname = NULL;
        time_t timestamp = 0;
 
+       full_fname = full_path_from_dirfsp_atname(talloc_tos(),
+                                                 dirfsp,
+                                                 smb_fname);
+       if (full_fname == NULL) {
+               errno = ENOMEM;
+               return -1;
+       }
+
        if (!shadow_copy2_strip_snapshot(talloc_tos(),
                                        handle,
-                                       smb_fname,
+                                       full_fname,
                                        &timestamp,
                                        NULL)) {
+               TALLOC_FREE(full_fname);
                return -1;
        }
+       TALLOC_FREE(full_fname);
        if (timestamp != 0) {
                errno = EROFS;
                return -1;
@@ -2162,15 +2383,15 @@ static int shadow_copy2_mkdirat(vfs_handle_struct *handle,
                        mode);
 }
 
-static int shadow_copy2_chflags(vfs_handle_struct *handle,
-                               const struct smb_filename *smb_fname,
+static int shadow_copy2_fchflags(vfs_handle_struct *handle,
+                               struct files_struct *fsp,
                                unsigned int flags)
 {
        time_t timestamp = 0;
 
        if (!shadow_copy2_strip_snapshot(talloc_tos(),
                                        handle,
-                                       smb_fname,
+                                       fsp->fsp_name,
                                        &timestamp,
                                        NULL)) {
                return -1;
@@ -2179,137 +2400,18 @@ static int shadow_copy2_chflags(vfs_handle_struct *handle,
                errno = EROFS;
                return -1;
        }
-       return SMB_VFS_NEXT_CHFLAGS(handle, smb_fname, flags);
-}
-
-static ssize_t shadow_copy2_getxattr(vfs_handle_struct *handle,
-                               const struct smb_filename *smb_fname,
-                               const char *aname,
-                               void *value,
-                               size_t size)
-{
-       time_t timestamp = 0;
-       char *stripped = NULL;
-       ssize_t ret;
-       int saved_errno = 0;
-       char *conv;
-       struct smb_filename *conv_smb_fname = NULL;
-
-       if (!shadow_copy2_strip_snapshot(talloc_tos(),
-                               handle,
-                               smb_fname,
-                               &timestamp,
-                               &stripped)) {
-               return -1;
-       }
-       if (timestamp == 0) {
-               return SMB_VFS_NEXT_GETXATTR(handle, smb_fname, aname, value,
-                                            size);
-       }
-       conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
-       TALLOC_FREE(stripped);
-       if (conv == NULL) {
-               return -1;
-       }
-
-       conv_smb_fname = synthetic_smb_fname(talloc_tos(),
-                                       conv,
-                                       NULL,
-                                       NULL,
-                                       0,
-                                       smb_fname->flags);
-       if (conv_smb_fname == NULL) {
-               TALLOC_FREE(conv);
-               return -1;
-       }
-
-       ret = SMB_VFS_NEXT_GETXATTR(handle, conv_smb_fname, aname, value, size);
-       if (ret == -1) {
-               saved_errno = errno;
-       }
-       TALLOC_FREE(conv_smb_fname);
-       TALLOC_FREE(conv);
-       if (saved_errno != 0) {
-               errno = saved_errno;
-       }
-       return ret;
-}
-
-static ssize_t shadow_copy2_listxattr(struct vfs_handle_struct *handle,
-                                     const struct smb_filename *smb_fname,
-                                     char *list, size_t size)
-{
-       time_t timestamp = 0;
-       char *stripped = NULL;
-       ssize_t ret;
-       int saved_errno = 0;
-       char *conv;
-       struct smb_filename *conv_smb_fname = NULL;
-
-       if (!shadow_copy2_strip_snapshot(talloc_tos(),
-                               handle,
-                               smb_fname,
-                               &timestamp,
-                               &stripped)) {
-               return -1;
-       }
-       if (timestamp == 0) {
-               return SMB_VFS_NEXT_LISTXATTR(handle, smb_fname, list, size);
-       }
-       conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
-       TALLOC_FREE(stripped);
-       if (conv == NULL) {
-               return -1;
-       }
-       conv_smb_fname = synthetic_smb_fname(talloc_tos(),
-                                       conv,
-                                       NULL,
-                                       NULL,
-                                       0,
-                                       smb_fname->flags);
-       if (conv_smb_fname == NULL) {
-               TALLOC_FREE(conv);
-               return -1;
-       }
-       ret = SMB_VFS_NEXT_LISTXATTR(handle, conv_smb_fname, list, size);
-       if (ret == -1) {
-               saved_errno = errno;
-       }
-       TALLOC_FREE(conv_smb_fname);
-       TALLOC_FREE(conv);
-       if (saved_errno != 0) {
-               errno = saved_errno;
-       }
-       return ret;
-}
-
-static int shadow_copy2_removexattr(vfs_handle_struct *handle,
-                               const struct smb_filename *smb_fname,
-                               const char *aname)
-{
-       time_t timestamp = 0;
-
-       if (!shadow_copy2_strip_snapshot(talloc_tos(),
-                               handle,
-                               smb_fname,
-                               &timestamp,
-                               NULL)) {
-               return -1;
-       }
-       if (timestamp != 0) {
-               errno = EROFS;
-               return -1;
-       }
-       return SMB_VFS_NEXT_REMOVEXATTR(handle, smb_fname, aname);
+       return SMB_VFS_NEXT_FCHFLAGS(handle, fsp, flags);
 }
 
-static int shadow_copy2_setxattr(struct vfs_handle_struct *handle,
-                                const struct smb_filename *smb_fname,
+static int shadow_copy2_fsetxattr(struct vfs_handle_struct *handle,
+                                struct files_struct *fsp,
                                 const char *aname, const void *value,
                                 size_t size, int flags)
 {
        time_t timestamp = 0;
+       const struct smb_filename *smb_fname = NULL;
 
+       smb_fname = fsp->fsp_name;
        if (!shadow_copy2_strip_snapshot(talloc_tos(),
                                handle,
                                smb_fname,
@@ -2321,7 +2423,7 @@ static int shadow_copy2_setxattr(struct vfs_handle_struct *handle,
                errno = EROFS;
                return -1;
        }
-       return SMB_VFS_NEXT_SETXATTR(handle, smb_fname,
+       return SMB_VFS_NEXT_FSETXATTR(handle, fsp,
                                aname, value, size, flags);
 }
 
@@ -2353,23 +2455,34 @@ static NTSTATUS shadow_copy2_create_dfs_pathat(struct vfs_handle_struct *handle,
 static NTSTATUS shadow_copy2_read_dfs_pathat(struct vfs_handle_struct *handle,
                                TALLOC_CTX *mem_ctx,
                                struct files_struct *dirfsp,
-                               const struct smb_filename *smb_fname,
+                               struct smb_filename *smb_fname,
                                struct referral **ppreflist,
                                size_t *preferral_count)
 {
        time_t timestamp = 0;
        char *stripped = NULL;
+       struct smb_filename *full_fname = NULL;
        struct smb_filename *conv = NULL;
        NTSTATUS status;
 
+       full_fname = full_path_from_dirfsp_atname(talloc_tos(),
+                                                 dirfsp,
+                                                 smb_fname);
+       if (full_fname == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
        if (!shadow_copy2_strip_snapshot(mem_ctx,
                                        handle,
-                                       smb_fname,
+                                       full_fname,
                                        &timestamp,
                                        &stripped)) {
+               TALLOC_FREE(full_fname);
                return NT_STATUS_NO_MEMORY;
        }
        if (timestamp == 0) {
+               TALLOC_FREE(full_fname);
+               TALLOC_FREE(stripped);
                return SMB_VFS_NEXT_READ_DFS_PATHAT(handle,
                                        mem_ctx,
                                        dirfsp,
@@ -2378,11 +2491,13 @@ static NTSTATUS shadow_copy2_read_dfs_pathat(struct vfs_handle_struct *handle,
                                        preferral_count);
        }
 
-       conv = cp_smb_filename(mem_ctx, smb_fname);
+       conv = cp_smb_filename(mem_ctx, full_fname);
        if (conv == NULL) {
+               TALLOC_FREE(full_fname);
                TALLOC_FREE(stripped);
                return NT_STATUS_NO_MEMORY;
        }
+       TALLOC_FREE(full_fname);
        conv->base_name = shadow_copy2_convert(conv,
                                        handle,
                                        stripped,
@@ -2395,126 +2510,30 @@ static NTSTATUS shadow_copy2_read_dfs_pathat(struct vfs_handle_struct *handle,
 
        status = SMB_VFS_NEXT_READ_DFS_PATHAT(handle,
                                mem_ctx,
-                               dirfsp,
+                               handle->conn->cwd_fsp,
                                conv,
                                ppreflist,
                                preferral_count);
 
-       TALLOC_FREE(conv);
-       return status;
-}
-
-static int shadow_copy2_get_real_filename(struct vfs_handle_struct *handle,
-                                         const struct smb_filename *fname,
-                                         const char *name,
-                                         TALLOC_CTX *mem_ctx,
-                                         char **found_name)
-{
-       char *path = fname->base_name;
-       struct shadow_copy2_private *priv = NULL;
-       struct shadow_copy2_config *config = NULL;
-       time_t timestamp = 0;
-       char *stripped = NULL;
-       ssize_t ret;
-       int saved_errno = 0;
-       char *conv;
-       struct smb_filename conv_fname;
-
-       SMB_VFS_HANDLE_GET_DATA(handle, priv, struct shadow_copy2_private,
-                               return -1);
-       config = priv->config;
-
-       DBG_DEBUG("Path=[%s] name=[%s]\n", smb_fname_str_dbg(fname), name);
-
-       if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, fname,
-                                        &timestamp, &stripped)) {
-               DEBUG(10, ("shadow_copy2_strip_snapshot failed\n"));
-               return -1;
-       }
-       if (timestamp == 0) {
-               DEBUG(10, ("timestamp == 0\n"));
-               return SMB_VFS_NEXT_GET_REAL_FILENAME(handle, fname, name,
-                                                     mem_ctx, found_name);
-       }
-
-       /*
-        * Note that stripped may be an empty string "" if path was ".". As
-        * shadow_copy2_convert() combines "" with the shadow-copy tree connect
-        * root fullpath and get_real_filename_full_scan() has an explicit check
-        * for "" this works.
-        */
-       DBG_DEBUG("stripped [%s]\n", stripped);
-
-       conv = shadow_copy2_convert(talloc_tos(), handle, stripped, timestamp);
-       if (conv == NULL) {
-               if (!config->snapdirseverywhere) {
-                       DBG_DEBUG("shadow_copy2_convert [%s] failed\n", stripped);
-                       return -1;
-               }
-
-               /*
-                * We're called in the path traversal loop in unix_convert()
-                * walking down the directory hierarchy. shadow_copy2_convert()
-                * will fail if the snapshot directory is futher down in the
-                * hierachy. Set conv to the original stripped path and try to
-                * look it up in the filesystem with
-                * SMB_VFS_NEXT_GET_REAL_FILENAME() or
-                * get_real_filename_full_scan().
-                */
-               DBG_DEBUG("Use stripped [%s] as conv\n", stripped);
-               conv = talloc_strdup(talloc_tos(), stripped);
-               if (conv == NULL) {
-                       TALLOC_FREE(stripped);
-                       return -1;
-               }
-       }
-
-       conv_fname = (struct smb_filename) {
-               .base_name = conv,
-       };
-
-       DEBUG(10, ("Calling NEXT_GET_REAL_FILE_NAME for conv=[%s], "
-                  "name=[%s]\n", conv, name));
-       ret = SMB_VFS_NEXT_GET_REAL_FILENAME(handle, &conv_fname, name,
-                                            mem_ctx, found_name);
-       DEBUG(10, ("NEXT_REAL_FILE_NAME returned %d\n", (int)ret));
-       if (ret == 0) {
-               return 0;
-       }
-       if (errno != EOPNOTSUPP) {
-               TALLOC_FREE(conv);
-               errno = EOPNOTSUPP;
-               return -1;
+       if (NT_STATUS_IS_OK(status)) {
+               /* Return any stat(2) info. */
+               smb_fname->st = conv->st;
        }
 
-       ret = get_real_filename_full_scan(handle->conn,
-                                         conv,
-                                         name,
-                                         false,
-                                         mem_ctx,
-                                         found_name);
-       if (ret != 0) {
-               saved_errno = errno;
-               DBG_DEBUG("Scan [%s] for [%s] failed\n",
-                         conv, name);
-               errno = saved_errno;
-               return -1;
-       }
-
-       DBG_DEBUG("Scan [%s] for [%s] returned [%s]\n",
-                 conv, name, *found_name);
-
        TALLOC_FREE(conv);
-       return 0;
+       return status;
 }
 
-static const char *shadow_copy2_connectpath(struct vfs_handle_struct *handle,
-                                       const struct smb_filename *smb_fname_in)
+static const char *shadow_copy2_connectpath(
+       struct vfs_handle_struct *handle,
+       const struct files_struct *dirfsp,
+       const struct smb_filename *smb_fname_in)
 {
        time_t timestamp = 0;
        char *stripped = NULL;
        char *tmp = NULL;
        const char *fname = smb_fname_in->base_name;
+       const struct smb_filename *full = NULL;
        struct smb_filename smb_fname = {0};
        struct smb_filename *result_fname = NULL;
        char *result = NULL;
@@ -2534,12 +2553,18 @@ static const char *shadow_copy2_connectpath(struct vfs_handle_struct *handle,
                return priv->shadow_connectpath;
        }
 
-       if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, smb_fname_in,
+       full = full_path_from_dirfsp_atname(
+               talloc_tos(), dirfsp, smb_fname_in);
+       if (full == NULL) {
+               return NULL;
+       }
+
+       if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, full,
                                         &timestamp, &stripped)) {
                goto done;
        }
        if (timestamp == 0) {
-               return SMB_VFS_NEXT_CONNECTPATH(handle, smb_fname_in);
+               return SMB_VFS_NEXT_CONNECTPATH(handle, dirfsp, smb_fname_in);
        }
 
        tmp = shadow_copy2_do_convert(talloc_tos(), handle, stripped, timestamp,
@@ -2609,6 +2634,103 @@ done:
        return result;
 }
 
+static NTSTATUS shadow_copy2_parent_pathname(vfs_handle_struct *handle,
+                                            TALLOC_CTX *ctx,
+                                            const struct smb_filename *smb_fname_in,
+                                            struct smb_filename **parent_dir_out,
+                                            struct smb_filename **atname_out)
+{
+       time_t timestamp = 0;
+       char *stripped = NULL;
+       char *converted_name = NULL;
+       struct smb_filename *smb_fname = NULL;
+       struct smb_filename *parent = NULL;
+       struct smb_filename *atname = NULL;
+       struct shadow_copy2_private *priv = NULL;
+       bool ok = false;
+       bool is_converted = false;
+       NTSTATUS status = NT_STATUS_OK;
+       TALLOC_CTX *frame = NULL;
+
+       SMB_VFS_HANDLE_GET_DATA(handle,
+                               priv,
+                               struct shadow_copy2_private,
+                               return NT_STATUS_INTERNAL_ERROR);
+
+       frame = talloc_stackframe();
+
+       smb_fname = cp_smb_filename(frame, smb_fname_in);
+       if (smb_fname == NULL) {
+               status = NT_STATUS_NO_MEMORY;
+               goto fail;
+       }
+
+       /* First, call the default PARENT_PATHNAME. */
+       status = SMB_VFS_NEXT_PARENT_PATHNAME(handle,
+                                             frame,
+                                             smb_fname,
+                                             &parent,
+                                             &atname);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto fail;
+       }
+
+       if (parent->twrp == 0) {
+               /*
+                * Parent is not a snapshot path, return
+                * the regular result.
+                */
+               status = NT_STATUS_OK;
+               goto out;
+       }
+
+       /* See if we can find a snapshot for the parent. */
+       ok = shadow_copy2_strip_snapshot_converted(frame,
+                                                  handle,
+                                                  parent,
+                                                  &timestamp,
+                                                  &stripped,
+                                                  &is_converted);
+       if (!ok) {
+               status = map_nt_error_from_unix(errno);
+               goto fail;
+       }
+
+       if (is_converted) {
+               /*
+                * Already found snapshot for parent so wipe
+                * out the twrp.
+                */
+               parent->twrp = 0;
+               goto out;
+       }
+
+       converted_name = shadow_copy2_convert(frame,
+                                             handle,
+                                             stripped,
+                                             timestamp);
+
+       if (converted_name == NULL) {
+               /*
+                * Can't find snapshot for parent so wipe
+                * out the twrp.
+                */
+               parent->twrp = 0;
+       }
+
+  out:
+
+       *parent_dir_out = talloc_move(ctx, &parent);
+       if (atname_out != NULL) {
+               *atname_out = talloc_move(*parent_dir_out, &atname);
+       }
+
+  fail:
+
+       TALLOC_FREE(frame);
+       return status;
+}
+
 static uint64_t shadow_copy2_disk_free(vfs_handle_struct *handle,
                                const struct smb_filename *smb_fname,
                                uint64_t *bsize,
@@ -2824,9 +2946,9 @@ static int shadow_copy2_connect(struct vfs_handle_struct *handle,
        const char *snapsharepath = NULL;
        const char *mount_point;
 
-       DEBUG(10, (__location__ ": cnum[%u], connectpath[%s]\n",
-                  (unsigned)handle->conn->cnum,
-                  handle->conn->connectpath));
+       DBG_DEBUG("cnum[%" PRIu32 "], connectpath[%s]\n",
+                 handle->conn->cnum,
+                 handle->conn->connectpath);
 
        ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
        if (ret < 0) {
@@ -2954,10 +3076,10 @@ static int shadow_copy2_connect(struct vfs_handle_struct *handle,
                                           "shadow", "mountpoint", NULL);
        if (mount_point != NULL) {
                if (mount_point[0] != '/') {
-                       DEBUG(1, (__location__ " Warning: 'mountpoint' is "
-                                 "relative ('%s'), but it has to be an "
-                                 "absolute path. Ignoring provided value.\n",
-                                 mount_point));
+                       DBG_WARNING("Warning: 'mountpoint' is relative "
+                                   "('%s'), but it has to be an absolute "
+                                   "path. Ignoring provided value.\n",
+                                   mount_point);
                        mount_point = NULL;
                } else {
                        char *p;
@@ -2977,7 +3099,7 @@ static int shadow_copy2_connect(struct vfs_handle_struct *handle,
        if (mount_point != NULL) {
                config->mount_point = talloc_strdup(config, mount_point);
                if (config->mount_point == NULL) {
-                       DEBUG(0, (__location__ " talloc_strdup() failed\n"));
+                       DBG_ERR("talloc_strdup() failed\n");
                        return -1;
                }
        } else {
@@ -2996,10 +3118,10 @@ static int shadow_copy2_connect(struct vfs_handle_struct *handle,
 
        if (basedir != NULL) {
                if (basedir[0] != '/') {
-                       DEBUG(1, (__location__ " Warning: 'basedir' is "
-                                 "relative ('%s'), but it has to be an "
-                                 "absolute path. Disabling basedir.\n",
-                                 basedir));
+                       DBG_WARNING("Warning: 'basedir' is "
+                                   "relative ('%s'), but it has to be an "
+                                   "absolute path. Disabling basedir.\n",
+                                   basedir);
                        basedir = NULL;
                } else {
                        char *p;
@@ -3016,8 +3138,8 @@ static int shadow_copy2_connect(struct vfs_handle_struct *handle,
        }
 
        if (config->snapdirseverywhere && basedir != NULL) {
-               DEBUG(1, (__location__ " Warning: 'basedir' is incompatible "
-                         "with 'snapdirseverywhere'. Disabling basedir.\n"));
+               DBG_WARNING("Warning: 'basedir' is incompatible "
+                           "with 'snapdirseverywhere'. Disabling basedir.\n");
                basedir = NULL;
        }
 
@@ -3072,17 +3194,18 @@ static int shadow_copy2_connect(struct vfs_handle_struct *handle,
        if (config->snapdir[0] == '/') {
                config->snapdir_absolute = true;
 
-               if (config->snapdirseverywhere == true) {
-                       DEBUG(1, (__location__ " Warning: An absolute snapdir "
-                                 "is incompatible with 'snapdirseverywhere', "
-                                 "setting 'snapdirseverywhere' to false.\n"));
+               if (config->snapdirseverywhere) {
+                       DBG_WARNING("Warning: An absolute snapdir is "
+                                   "incompatible with 'snapdirseverywhere', "
+                                   "setting 'snapdirseverywhere' to "
+                                   "false.\n");
                        config->snapdirseverywhere = false;
                }
 
-               if (config->crossmountpoints == true) {
-                       DEBUG(1, (__location__ " Warning: 'crossmountpoints' "
-                                 "is not supported with an absolute snapdir. "
-                                 "Disabling it.\n"));
+               if (config->crossmountpoints) {
+                       DBG_WARNING("Warning: 'crossmountpoints' is not "
+                                   "supported with an absolute snapdir. "
+                                   "Disabling it.\n");
                        config->crossmountpoints = false;
                }
 
@@ -3152,29 +3275,24 @@ static struct vfs_fn_pointers vfs_shadow_copy2_fns = {
        .stat_fn = shadow_copy2_stat,
        .lstat_fn = shadow_copy2_lstat,
        .fstat_fn = shadow_copy2_fstat,
-       .open_fn = shadow_copy2_open,
+       .fstatat_fn = shadow_copy2_fstatat,
+       .openat_fn = shadow_copy2_openat,
        .unlinkat_fn = shadow_copy2_unlinkat,
-       .chmod_fn = shadow_copy2_chmod,
+       .fchmod_fn = shadow_copy2_fchmod,
        .chdir_fn = shadow_copy2_chdir,
-       .ntimes_fn = shadow_copy2_ntimes,
+       .fntimes_fn = shadow_copy2_fntimes,
        .readlinkat_fn = shadow_copy2_readlinkat,
        .mknodat_fn = shadow_copy2_mknodat,
        .realpath_fn = shadow_copy2_realpath,
-       .get_nt_acl_fn = shadow_copy2_get_nt_acl,
        .get_shadow_copy_data_fn = shadow_copy2_get_shadow_copy_data,
        .mkdirat_fn = shadow_copy2_mkdirat,
-       .getxattr_fn = shadow_copy2_getxattr,
-       .getxattrat_send_fn = vfs_not_implemented_getxattrat_send,
-       .getxattrat_recv_fn = vfs_not_implemented_getxattrat_recv,
-       .listxattr_fn = shadow_copy2_listxattr,
-       .removexattr_fn = shadow_copy2_removexattr,
-       .setxattr_fn = shadow_copy2_setxattr,
-       .chflags_fn = shadow_copy2_chflags,
-       .get_real_filename_fn = shadow_copy2_get_real_filename,
+       .fsetxattr_fn = shadow_copy2_fsetxattr,
+       .fchflags_fn = shadow_copy2_fchflags,
        .pwrite_fn = shadow_copy2_pwrite,
        .pwrite_send_fn = shadow_copy2_pwrite_send,
        .pwrite_recv_fn = shadow_copy2_pwrite_recv,
        .connectpath_fn = shadow_copy2_connectpath,
+       .parent_pathname_fn = shadow_copy2_parent_pathname,
 };
 
 static_decl_vfs;