This patch introduces a function that will allow us to resolve a
relative difference between two elements of a disk backing chain. This
fucntion will be used to allow relative block commit and block pull
where we need to specify the new relative name of the image to qemu.
This patch also adds unit tests for the function to verify that it works
correctly.
---
src/libvirt_private.syms | 1 +
src/util/virstoragefile.c | 45 +++++++++++++++++++++
src/util/virstoragefile.h | 4 ++
tests/virstoragetest.c | 101 ++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 151 insertions(+)
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index cb1fd72..6787b51 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -1866,6 +1866,7 @@ virStorageFileGetLVMKey;
virStorageFileGetMetadataFromBuf;
virStorageFileGetMetadataFromFD;
virStorageFileGetMetadataInternal;
+virStorageFileGetRelativeBackingPath;
virStorageFileGetSCSIKey;
virStorageFileIsClusterFS;
virStorageFileParseChainIndex;
diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c
index 21d71c4..a390b1c 100644
--- a/src/util/virstoragefile.c
+++ b/src/util/virstoragefile.c
@@ -2019,3 +2019,48 @@ virStorageFileSimplifyPath(const char *path,
return ret;
}
+
+
+int
+virStorageFileGetRelativeBackingPath(virStorageSourcePtr from,
+ virStorageSourcePtr to,
+ char **relpath)
+{
+ virBuffer buf = VIR_BUFFER_INITIALIZER;
+ virStorageSourcePtr next;
+ char *tmp = NULL;
+ char ret = -1;
+
+ *relpath = NULL;
+
+ for (next = from; next; next = next->backingStore) {
+ if (!next->backingRelative || !next->relPath) {
+ ret = 1;
+ goto cleanup;
+ }
+
+ if (next != from)
+ virBufferAddLit(&buf, "/../");
+
+ virBufferAdd(&buf, next->relPath, -1);
+
+ if (next == to)
+ break;
+ }
+
+ if (next != to)
+ goto cleanup;
+
+ if (!(tmp = virBufferContentAndReset(&buf)))
+ goto cleanup;
+
+ if (!(*relpath = virStorageFileSimplifyPath(tmp, true)))
+ goto cleanup;
+
+ ret = 0;
+
+ cleanup:
+ virBufferFreeAndReset(&buf);
+ VIR_FREE(tmp);
+ return ret;
+}
diff --git a/src/util/virstoragefile.h b/src/util/virstoragefile.h
index 3708c5e..8b23f95 100644
--- a/src/util/virstoragefile.h
+++ b/src/util/virstoragefile.h
@@ -327,4 +327,8 @@ virStorageSourcePtr virStorageSourceNewFromBacking(virStorageSourcePtr
parent);
char *virStorageFileSimplifyPath(const char *path,
bool allow_relative);
+int virStorageFileGetRelativeBackingPath(virStorageSourcePtr from,
+ virStorageSourcePtr to,
+ char **relpath);
+
#endif /* __VIR_STORAGE_FILE_H__ */
diff --git a/tests/virstoragetest.c b/tests/virstoragetest.c
index 57f16ca..80d73ca 100644
--- a/tests/virstoragetest.c
+++ b/tests/virstoragetest.c
@@ -560,6 +560,85 @@ testPathSimplify(const void *args)
}
+virStorageSource backingchain[9];
+
+static void
+testPathRelativePrepare(void)
+{
+ size_t i;
+
+ for (i = 0; i < ARRAY_CARDINALITY(backingchain) - 1; i++) {
+ backingchain[i].backingStore = &backingchain[i+1];
+ }
+
+ backingchain[0].relPath = (char *) "/path/to/some/img";
+ backingchain[0].backingRelative = false;
+
+ backingchain[1].relPath = (char *) "asdf";
+ backingchain[1].backingRelative = true;
+
+ backingchain[2].relPath = (char *) "test";
+ backingchain[2].backingRelative = true;
+
+ backingchain[3].relPath = (char *) "blah";
+ backingchain[3].backingRelative = true;
+
+ backingchain[4].relPath = (char *) "/path/to/some/other/img";
+ backingchain[4].backingRelative = false;
+
+ backingchain[5].relPath = (char *) "../relative/in/other/path";
+ backingchain[5].backingRelative = true;
+
+ backingchain[6].relPath = (char *) "test";
+ backingchain[6].backingRelative = true;
+
+ backingchain[7].relPath = (char *) "../../../../../below";
+ backingchain[7].backingRelative = true;
+
+ backingchain[8].relPath = (char *) "a/little/more/upwards";
+ backingchain[8].backingRelative = true;
+}
+
+
+struct testPathRelativeBacking
+{
+ virStorageSourcePtr from;
+ virStorageSourcePtr to;
+
+ const char *expect;
+};
+
+static int
+testPathRelative(const void *args)
+{
+ const struct testPathRelativeBacking *data = args;
+ char *actual = NULL;
+ int ret = -1;
+
+ if (virStorageFileGetRelativeBackingPath(data->from,
+ data->to,
+ &actual) < 0) {
+ fprintf(stderr, "relative backing path resolution failed\n");
+ goto cleanup;
+ }
+
+ if (STRNEQ_NULLABLE(data->expect, actual)) {
+ fprintf(stderr, "relative path resolution from '%s' to '%s':
"
+ "expected '%s', got '%s'\n",
+ data->from->relPath, data->to->relPath,
+ NULLSTR(data->expect), NULLSTR(actual));
+ goto cleanup;
+ }
+
+ ret = 0;
+
+ cleanup:
+ VIR_FREE(actual);
+
+ return ret;
+}
+
+
static int
mymain(void)
{
@@ -567,6 +646,7 @@ mymain(void)
virCommandPtr cmd = NULL;
struct testChainData data;
struct testPathSimplifyData data3;
+ struct testPathRelativeBacking data4;
virStorageSourcePtr chain = NULL;
/* Prep some files with qemu-img; if that is not found on PATH, or
@@ -1099,6 +1179,27 @@ mymain(void)
TEST_SIMPLIFY(21,
"some/path/to/image.qcow/../image2.qcow/../image3.qcow/",
"some/path/to/image3.qcow",
"some/path/to/image3.qcow");
+#define TEST_RELATIVE_BACKING(id, FROM, TO, EXPECT) \
+ do { \
+ data4.from = &FROM; \
+ data4.to = &TO; \
+ data4.expect = EXPECT; \
+ if (virtTestRun("Path relative resolve " #id, \
+ testPathRelative, &data4) < 0) \
+ ret = -1; \
+ } while (0)
+
+ testPathRelativePrepare();
+
+ TEST_RELATIVE_BACKING(1, backingchain[0], backingchain[1], NULL);
+ TEST_RELATIVE_BACKING(2, backingchain[1], backingchain[2], "test");
+ TEST_RELATIVE_BACKING(3, backingchain[2], backingchain[3], "blah");
+ TEST_RELATIVE_BACKING(4, backingchain[1], backingchain[3], "blah");
+ TEST_RELATIVE_BACKING(5, backingchain[1], backingchain[4], NULL);
+ TEST_RELATIVE_BACKING(6, backingchain[5], backingchain[6],
"../relative/in/other/test");
+ TEST_RELATIVE_BACKING(7, backingchain[5], backingchain[7],
"../../../below");
+ TEST_RELATIVE_BACKING(8, backingchain[5], backingchain[8],
"../../../a/little/more/upwards");
+
cleanup:
/* Final cleanup */
virStorageSourceFree(chain);
--
1.9.3