As gnulib's canonicalize_filename_mode isn't LGPL and relative snapshot
resolution code will need to clean paths with relative path components
this patch adds a libvirt's own implementation of that functionality and
tests to make sure everything works well.
---
 src/util/virstoragefile.c | 95 +++++++++++++++++++++++++++++++++++++++++++++++
 src/util/virstoragefile.h |  2 +
 tests/virstoragetest.c    | 83 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 180 insertions(+)

diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c
index db71cf3..21d71c4 100644
--- a/src/util/virstoragefile.c
+++ b/src/util/virstoragefile.c
@@ -40,6 +40,7 @@
 #include "virutil.h"
 #include "viruri.h"
 #include "dirname.h"
+#include "virbuffer.h"
 #if HAVE_SYS_SYSCALL_H
 # include <sys/syscall.h>
 #endif
@@ -1924,3 +1925,97 @@ virStorageSourceNewFromBacking(virStorageSourcePtr 
parent)

     return ret;
 }
+
+
+static char *
+virStorageFileExportPath(char **components,
+                         size_t ncomponents,
+                         bool beginSlash)
+{
+    virBuffer buf = VIR_BUFFER_INITIALIZER;
+    size_t i;
+    char *ret = NULL;
+
+    if (beginSlash)
+        virBufferAddLit(&buf, "/");
+
+    for (i = 0; i < ncomponents; i++) {
+        if (i != 0)
+            virBufferAddLit(&buf, "/");
+
+        virBufferAdd(&buf, components[i], -1);
+    }
+
+    if (virBufferError(&buf) != 0) {
+        virReportOOMError();
+        return NULL;
+    }
+
+    /* if the output string is empty ... wel just return an empty string */
+    if (!(ret = virBufferContentAndReset(&buf)))
+        ignore_value(VIR_STRDUP(ret, ""));
+
+    return ret;
+}
+
+
+char *
+virStorageFileSimplifyPath(const char *path,
+                           bool allow_relative)
+{
+    bool beginSlash = false;
+    char **components = NULL;
+    size_t ncomponents = 0;
+    size_t i;
+    char *ret = NULL;
+
+    /* special cases are "" and "//", return them as they are */
+    if (STREQ(path, "") || STREQ(path, "//")) {
+        ignore_value(VIR_STRDUP(ret, path));
+        goto cleanup;
+    }
+
+    if (path[0] == '/')
+        beginSlash = true;
+
+    if (!(components = virStringSplitCount(path, "/", 0, &ncomponents)))
+        goto cleanup;
+
+    i = 0;
+    while (i < ncomponents) {
+        if (STREQ(components[i], "") ||
+            STREQ(components[i], ".")) {
+            VIR_FREE(components[i]);
+            VIR_DELETE_ELEMENT(components, i, ncomponents);
+            continue;
+        }
+
+        if (STREQ(components[i], "..")) {
+            if (allow_relative && !beginSlash &&
+                (i == 0 || STREQ(components[i - 1], ".."))) {
+                i++;
+                continue;
+            }
+
+            VIR_FREE(components[i]);
+            VIR_DELETE_ELEMENT(components, i, ncomponents);
+
+            if (i != 0) {
+                VIR_FREE(components[i - 1]);
+                VIR_DELETE_ELEMENT(components, i - 1, ncomponents);
+                i--;
+            }
+
+            continue;
+        }
+
+        i++;
+    }
+
+    ret = virStorageFileExportPath(components, ncomponents, beginSlash);
+
+ cleanup:
+    virStringFreeListCount(components, ncomponents);
+
+    return ret;
+}
diff --git a/src/util/virstoragefile.h b/src/util/virstoragefile.h
index 0d0f958..3708c5e 100644
--- a/src/util/virstoragefile.h
+++ b/src/util/virstoragefile.h
@@ -324,5 +324,7 @@ void virStorageSourceFree(virStorageSourcePtr def);
 void virStorageSourceClearBackingStore(virStorageSourcePtr def);
 virStorageSourcePtr virStorageSourceNewFromBacking(virStorageSourcePtr parent);

+char *virStorageFileSimplifyPath(const char *path,
+                                 bool allow_relative);

 #endif /* __VIR_STORAGE_FILE_H__ */
diff --git a/tests/virstoragetest.c b/tests/virstoragetest.c
index 29297ef..57f16ca 100644
--- a/tests/virstoragetest.c
+++ b/tests/virstoragetest.c
@@ -512,12 +512,61 @@ testStorageLookup(const void *args)
     return ret;
 }

+
+struct testPathSimplifyData
+{
+    const char *path;
+    const char *exp_abs;
+    const char *exp_rel;
+};
+
+
+static int
+testPathSimplify(const void *args)
+{
+    const struct testPathSimplifyData *data = args;
+    char *simple;
+    int ret = -1;
+
+    if (!(simple = virStorageFileSimplifyPath(data->path, false))) {
+        fprintf(stderr, "path simplification returned NULL\n");
+        goto cleanup;
+    }
+
+    if (STRNEQ(simple, data->exp_abs)) {
+        fprintf(stderr, "simplify path (absolute) '%s': expected: '%s', got 
'%s'\n",
+                data->path, data->exp_abs, simple);
+        goto cleanup;
+    }
+
+    VIR_FREE(simple);
+
+    if (!(simple = virStorageFileSimplifyPath(data->path, true))) {
+        fprintf(stderr, "path simplification returned NULL\n");
+        goto cleanup;
+    }
+
+    if (STRNEQ(simple, data->exp_rel)) {
+        fprintf(stderr, "simplify path (relative) '%s': expected: '%s', got 
'%s'\n",
+                data->path, data->exp_rel, simple);
+        goto cleanup;
+    }
+
+    ret = 0;
+
+ cleanup:
+    VIR_FREE(simple);
+    return ret;
+}
+
+
 static int
 mymain(void)
 {
     int ret;
     virCommandPtr cmd = NULL;
     struct testChainData data;
+    struct testPathSimplifyData data3;
     virStorageSourcePtr chain = NULL;

     /* Prep some files with qemu-img; if that is not found on PATH, or
@@ -1016,6 +1065,40 @@ mymain(void)
                        chain->backingStore->path);
     TEST_LOOKUP_TARGET(33, "vda", "vda[3]", 3, NULL, NULL, NULL);

+#define TEST_SIMPLIFY(id, PATH, EXP_ABS, EXP_REL)                           \
+    do {                                                                    \
+        data3.path = PATH;                                                  \
+        data3.exp_abs = EXP_ABS;                                            \
+        data3.exp_rel = EXP_REL;                                            \
+        if (virtTestRun("Path simplify " #id,                               \
+                        testPathSimplify, &data3) < 0)                      \
+            ret = -1;                                                       \
+    } while (0)
+
+    /* PATH, absolute simplification, relative simplification */
+    TEST_SIMPLIFY(1, "/", "/", "/");
+    TEST_SIMPLIFY(2, "/path", "/path", "/path");
+    TEST_SIMPLIFY(3, "/path/to/blah", "/path/to/blah", "/path/to/blah");
+    TEST_SIMPLIFY(4, "/path/", "/path", "/path");
+    TEST_SIMPLIFY(5, "///////", "/", "/");
+    TEST_SIMPLIFY(6, "//", "//", "//");
+    TEST_SIMPLIFY(7, "", "", "");
+    TEST_SIMPLIFY(8, "../", "", "..");
+    TEST_SIMPLIFY(9, "../../", "", "../..");
+    TEST_SIMPLIFY(10, "../../blah", "blah", "../../blah");
+    TEST_SIMPLIFY(11, "/./././blah", "/blah", "/blah");
+    TEST_SIMPLIFY(12, ".././../././../blah", "blah", "../../../blah");
+    TEST_SIMPLIFY(13, "/././", "/", "/");
+    TEST_SIMPLIFY(14, "./././", "", "");
+    TEST_SIMPLIFY(15, "blah/../foo", "foo", "foo");
+    TEST_SIMPLIFY(16, "foo/bar/../blah", "foo/blah", "foo/blah");
+    TEST_SIMPLIFY(17, "foo/bar/.././blah", "foo/blah", "foo/blah");
+    TEST_SIMPLIFY(18, "/path/to/foo/bar/../../../../../../../../baz", "/baz", 
"/baz");
+    TEST_SIMPLIFY(19, "path/to/foo/bar/../../../../../../../../baz", "baz", 
"../../../../baz");
+    TEST_SIMPLIFY(20, "path/to/foo/bar", "path/to/foo/bar", "path/to/foo/bar");
+    TEST_SIMPLIFY(21, "some/path/to/image.qcow/../image2.qcow/../image3.qcow/",
+                  "some/path/to/image3.qcow", "some/path/to/image3.qcow");
+
  cleanup:
     /* Final cleanup */
     virStorageSourceFree(chain);
-- 
1.9.3

--
libvir-list mailing list
libvir-list@redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list

Reply via email to