The recently introduced virStorageFileSimplifyPath is good at resolving relative portions of a path. To add full path canonicalization capability we need to be able to resolve symlinks in the path too. This patch adds a callback to that function so that arbitrary storage systems can use this functionality. --- src/libvirt_private.syms | 1 + src/util/virstoragefile.c | 82 +++++++++++++++++++++++++++++++++++++++++++++-- src/util/virstoragefile.h | 9 ++++++ tests/virstoragetest.c | 80 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 170 insertions(+), 2 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 6787b51..02d86a8 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1872,6 +1872,7 @@ virStorageFileIsClusterFS; virStorageFileParseChainIndex; virStorageFileProbeFormat; virStorageFileResize; +virStorageFileSimplifyPathInternal; virStorageIsFile; virStorageNetHostDefClear; virStorageNetHostDefCopy; diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c index a390b1c..330210d 100644 --- a/src/util/virstoragefile.c +++ b/src/util/virstoragefile.c @@ -1959,13 +1959,47 @@ virStorageFileExportPath(char **components, } +static int +virStorageFileExplodePath(const char *path, + size_t at, + char ***components, + size_t *ncomponents) +{ + char **tmp = NULL; + char **next; + size_t ntmp = 0; + int ret = -1; + + if (!(tmp = virStringSplitCount(path, "/", 0, &ntmp))) + goto cleanup; + + /* prepend */ + for (next = tmp; *next; next++) { + if (VIR_INSERT_ELEMENT(*components, at, *ncomponents, *next) < 0) + goto cleanup; + + at++; + } + + ret = 0; + + cleanup: + virStringFreeListCount(tmp, ntmp); + return ret; +} + + char * -virStorageFileSimplifyPath(const char *path, - bool allow_relative) +virStorageFileSimplifyPathInternal(const char *path, + bool allow_relative, + virStorageFileSimplifyPathReadlinkCallback cb, + void *cbdata) { bool beginSlash = false; char **components = NULL; size_t ncomponents = 0; + char *linkpath = NULL; + char *currentpath = NULL; size_t i; char *ret = NULL; @@ -2009,6 +2043,40 @@ virStorageFileSimplifyPath(const char *path, continue; } + /* read link and stuff */ + if (cb) { + int rc; + if (!(currentpath = virStorageFileExportPath(components, i + 1, + beginSlash))) + goto cleanup; + + if ((rc = cb(currentpath, &linkpath, cbdata)) < 0) + goto cleanup; + + if (rc == 0) { + if (linkpath[0] == '/') { + /* start from a clean slate */ + virStringFreeListCount(components, ncomponents); + ncomponents = 0; + components = NULL; + beginSlash = true; + i = 0; + } else { + VIR_FREE(components[i]); + VIR_DELETE_ELEMENT(components, i, ncomponents); + } + + if (virStorageFileExplodePath(linkpath, i, &components, + &ncomponents) < 0) + goto cleanup; + + VIR_FREE(linkpath); + VIR_FREE(currentpath); + + continue; + } + } + i++; } @@ -2016,11 +2084,21 @@ virStorageFileSimplifyPath(const char *path, cleanup: virStringFreeListCount(components, ncomponents); + VIR_FREE(linkpath); + VIR_FREE(currentpath); return ret; } +char * +virStorageFileSimplifyPath(const char *path, + bool allow_relative) +{ + return virStorageFileSimplifyPathInternal(path, allow_relative, NULL, NULL); +} + + int virStorageFileGetRelativeBackingPath(virStorageSourcePtr from, virStorageSourcePtr to, diff --git a/src/util/virstoragefile.h b/src/util/virstoragefile.h index 8b23f95..a8412a5 100644 --- a/src/util/virstoragefile.h +++ b/src/util/virstoragefile.h @@ -324,6 +324,15 @@ void virStorageSourceFree(virStorageSourcePtr def); void virStorageSourceClearBackingStore(virStorageSourcePtr def); virStorageSourcePtr virStorageSourceNewFromBacking(virStorageSourcePtr parent); +typedef int +(*virStorageFileSimplifyPathReadlinkCallback)(const char *path, + char **link, + void *data); +char *virStorageFileSimplifyPathInternal(const char *path, + bool allow_relative, + virStorageFileSimplifyPathReadlinkCallback cb, + void *cbdata); + char *virStorageFileSimplifyPath(const char *path, bool allow_relative); diff --git a/tests/virstoragetest.c b/tests/virstoragetest.c index 80d73ca..771df0b 100644 --- a/tests/virstoragetest.c +++ b/tests/virstoragetest.c @@ -639,6 +639,72 @@ testPathRelative(const void *args) } +struct testPathCanonicalizeData +{ + const char *path; + const char *expect; +}; + +static const char *testPathCanonicalizeSymlinks[][2] = +{ + {"/path/blah", "/other/path/huzah"}, + {"/path/to/relative/symlink", "../../actual/file"}, +}; + +static int +testPathCanonicalizeReadlink(const char *path, + char **link, + void *data ATTRIBUTE_UNUSED) +{ + size_t i; + + *link = NULL; + + for (i = 0; i < ARRAY_CARDINALITY(testPathCanonicalizeSymlinks); i++) { + if (STREQ(path, testPathCanonicalizeSymlinks[i][0])) { + if (VIR_STRDUP(*link, testPathCanonicalizeSymlinks[i][1]) < 0) + return -1; + + return 0; + } + } + + return 1; +} + + +static int +testPathCanonicalize(const void *args) +{ + const struct testPathCanonicalizeData *data = args; + char *canon = NULL; + int ret = -1; + + if (!(canon = virStorageFileSimplifyPathInternal(data->path, + false, + testPathCanonicalizeReadlink, + NULL))) { + fprintf(stderr, "path canonicalization failed\n"); + goto cleanup; + } + + if (STRNEQ_NULLABLE(data->expect, canon)) { + fprintf(stderr, + "path canonicalization of '%s' failed: expected '%s' got '%s'\n", + data->path, data->expect, canon); + + goto cleanup; + } + + ret = 0; + + cleanup: + VIR_FREE(canon); + + return ret; +} + + static int mymain(void) { @@ -647,6 +713,7 @@ mymain(void) struct testChainData data; struct testPathSimplifyData data3; struct testPathRelativeBacking data4; + struct testPathCanonicalizeData data5; virStorageSourcePtr chain = NULL; /* Prep some files with qemu-img; if that is not found on PATH, or @@ -1200,6 +1267,19 @@ mymain(void) TEST_RELATIVE_BACKING(7, backingchain[5], backingchain[7], "../../../below"); TEST_RELATIVE_BACKING(8, backingchain[5], backingchain[8], "../../../a/little/more/upwards"); +#define TEST_PATH_CANONICALIZE(id, PATH, EXPECT) \ + do { \ + data5.path = PATH; \ + data5.expect = EXPECT; \ + if (virtTestRun("Path canonicalize " #id, \ + testPathCanonicalize, &data5) < 0) \ + ret = -1; \ + } while (0) + + TEST_PATH_CANONICALIZE(1, "/some/full/path", "/some/full/path"); + TEST_PATH_CANONICALIZE(2, "/path/blah", "/other/path/huzah"); + TEST_PATH_CANONICALIZE(3, "/path/to/relative/symlink", "/path/actual/file"); + cleanup: /* Final cleanup */ virStorageSourceFree(chain); -- 1.9.3 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list