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@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list