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 8e00d8c..7de04ed 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 d8b4b54..301452e 100644
--- a/src/util/virstoragefile.c
+++ b/src/util/virstoragefile.c
@@ -1962,13 +1962,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;
@@ -2012,6 +2046,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++;
}
@@ -2019,11 +2087,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 cbac30b..70deaef 100644
--- a/src/util/virstoragefile.h
+++ b/src/util/virstoragefile.h
@@ -325,6 +325,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