]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
vfs_shadow_copy2: implement case canonicalisation in shadow_copy2_get_real_filename()
authorRalph Boehme <slow@samba.org>
Thu, 23 Apr 2020 14:10:23 +0000 (16:10 +0200)
committerKarolin Seeger <kseeger@samba.org>
Wed, 13 May 2020 14:44:57 +0000 (14:44 +0000)
unix_convert() can't do this for us in snapdirseverywhere mode, so we do it
ourselves.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=14350

Signed-off-by: Ralph Boehme <slow@samba.org>
(Similar to commit a3d1ac2a597e2441d6855db566306298ae5db99b)

Autobuild-User(v4-11-test): Karolin Seeger <kseeger@samba.org>
Autobuild-Date(v4-11-test): Wed May 13 14:44:57 UTC 2020 on sn-devel-184

selftest/knownfail.d/samba3.blackbox.shadow_copy2 [deleted file]
source3/modules/vfs_shadow_copy2.c

diff --git a/selftest/knownfail.d/samba3.blackbox.shadow_copy2 b/selftest/knownfail.d/samba3.blackbox.shadow_copy2
deleted file mode 100644 (file)
index f869dc8..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-^samba3.blackbox.shadow_copy2.NT1.fetch a previous version of a regular file via non-canonical parent path\(fileserver_smb1_done\)$
-^samba3.blackbox.shadow_copy2.SMB3.fetch a previous version of a regular file via non-canonical parent path\(fileserver\)$
-^samba3.blackbox.shadow_copy2.NT1.fetch a previous version of a regular file via non-canonical path\(fileserver_smb1_done\)$
-^samba3.blackbox.shadow_copy2.SMB3.fetch a previous version of a regular file via non-canonical path\(fileserver\)$
index e5def9669f48593200d845ceb7d384b47530d50b..056fa9e80cc628eda0173360e36ca8f09bfb4e50 100644 (file)
@@ -852,6 +852,11 @@ static char *shadow_copy2_do_convert(TALLOC_CTX *mem_ctx,
 
        config = priv->config;
 
+       /*
+        * Note that stripped may be an empty string "" if the outer level VFS
+        * function got passed a single @GMT-only token path.
+        */
+
        DEBUG(10, ("converting '%s'\n", name));
 
        if (!config->snapdirseverywhere) {
@@ -2707,15 +2712,35 @@ static int shadow_copy2_get_real_filename(struct vfs_handle_struct *handle,
                                          TALLOC_CTX *mem_ctx,
                                          char **found_name)
 {
+       struct shadow_copy2_private *priv = NULL;
+       struct shadow_copy2_config *config = NULL;
+       bool name_is_gmt_token = is_gmt_token(name);
        time_t timestamp = 0;
        char *stripped = NULL;
        ssize_t ret;
        int saved_errno = 0;
        char *conv;
 
+       SMB_VFS_HANDLE_GET_DATA(handle, priv, struct shadow_copy2_private,
+                               return -1);
+       config = priv->config;
+
        DEBUG(10, ("shadow_copy2_get_real_filename called for path=[%s], "
                   "name=[%s]\n", path, name));
 
+       if (ISDOT(path) && name_is_gmt_token) {
+               /*
+                * This happens in the first round of looping over the path in
+                * unix_convert(). Eg for a snapshot path "@GMT-.../foo/bar" we
+                * would be called with path="." and name="@GMT-...".
+                */
+               *found_name = talloc_strdup(mem_ctx, name);
+               if (*found_name == NULL) {
+                       return -1;
+               }
+               return 0;
+       }
+
        if (!shadow_copy2_strip_snapshot(talloc_tos(), handle, path,
                                         &timestamp, &stripped)) {
                DEBUG(10, ("shadow_copy2_strip_snapshot failed\n"));
@@ -2726,25 +2751,75 @@ static int shadow_copy2_get_real_filename(struct vfs_handle_struct *handle,
                return SMB_VFS_NEXT_GET_REAL_FILENAME(handle, path, name,
                                                      mem_ctx, found_name);
        }
+
+       /*
+        * Note that stripped may be an empty string "" if path was a single
+        * @GMT-only token. 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);
-       TALLOC_FREE(stripped);
        if (conv == NULL) {
-               DEBUG(10, ("shadow_copy2_convert failed\n"));
-               return -1;
+               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;
+               }
        }
+       TALLOC_FREE(stripped);
+
        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, name,
                                             mem_ctx, found_name);
        DEBUG(10, ("NEXT_REAL_FILE_NAME returned %d\n", (int)ret));
-       if (ret == -1) {
-               saved_errno = errno;
+       if (ret == 0) {
+               return 0;
        }
-       TALLOC_FREE(conv);
-       if (saved_errno != 0) {
+       if (errno != EOPNOTSUPP) {
+               TALLOC_FREE(conv);
+               errno = EOPNOTSUPP;
+               return -1;
+       }
+
+       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;
        }
-       return ret;
+
+       DBG_DEBUG("Scan [%s] for [%s] returned [%s]\n",
+                 conv, name, *found_name);
+
+       TALLOC_FREE(conv);
+       return 0;
 }
 
 static const char *shadow_copy2_connectpath(struct vfs_handle_struct *handle,