]> xenbits.xensource.com Git - people/liuw/libxenctrl-split/libvirt.git/commitdiff
util: storage: Add helper to resolve relative path difference
authorPeter Krempa <pkrempa@redhat.com>
Tue, 13 May 2014 08:10:56 +0000 (10:10 +0200)
committerPeter Krempa <pkrempa@redhat.com>
Wed, 25 Jun 2014 07:27:16 +0000 (09:27 +0200)
This patch introduces a function that will allow us to resolve a
relative difference between two elements of a disk backing chain. This
function 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
src/util/virstoragefile.c
src/util/virstoragefile.h
tests/virstoragetest.c

index 981dc879f0bfc68c82662da558008e01f40c8b0f..a793b4c93b0dd27eebf523c3d939d8a2782d6c86 100644 (file)
@@ -1890,6 +1890,7 @@ virStorageFileGetLVMKey;
 virStorageFileGetMetadataFromBuf;
 virStorageFileGetMetadataFromFD;
 virStorageFileGetMetadataInternal;
+virStorageFileGetRelativeBackingPath;
 virStorageFileGetSCSIKey;
 virStorageFileIsClusterFS;
 virStorageFileParseChainIndex;
index 613ba3cf49268d327c69892976a545b4916323c5..cd763c508ee6bd70e017d39d1f2777a6863d2b83 100644 (file)
@@ -2176,3 +2176,89 @@ virStorageFileCanonicalizePath(const char *path,
 
     return ret;
 }
+
+
+/**
+ * virStorageFileRemoveLastPathComponent:
+ *
+ * @path: Path string to remove the last component from
+ *
+ * Removes the last path component of a path. This function is designed to be
+ * called on file paths only (no trailing slashes in @path). Caller is
+ * responsible to free the returned string.
+ */
+static char *
+virStorageFileRemoveLastPathComponent(const char *path)
+{
+    char *tmp;
+    char *ret;
+
+    if (VIR_STRDUP(ret, path ? path : "") < 0)
+        return NULL;
+
+    if ((tmp = strrchr(ret, '/')))
+        tmp[1] = '\0';
+    else
+        ret[0] = '\0';
+
+    return ret;
+}
+
+
+/*
+ * virStorageFileGetRelativeBackingPath:
+ *
+ * Resolve relative path to be written to the overlay of @top image when
+ * collapsing the backing chain between @top and @base.
+ *
+ * Returns 0 on success; 1 if backing chain isn't relative and -1 on error.
+ */
+int
+virStorageFileGetRelativeBackingPath(virStorageSourcePtr top,
+                                     virStorageSourcePtr base,
+                                     char **relpath)
+{
+    virStorageSourcePtr next;
+    char *tmp = NULL;
+    char *path = NULL;
+    char ret = -1;
+
+    *relpath = NULL;
+
+    for (next = top; next; next = next->backingStore) {
+        if (!next->backingRelative || !next->relPath) {
+            ret = 1;
+            goto cleanup;
+        }
+
+        if (!(tmp = virStorageFileRemoveLastPathComponent(path)))
+            goto cleanup;
+
+        VIR_FREE(path);
+
+        if (virAsprintf(&path, "%s%s", tmp, next->relPath) < 0)
+            goto cleanup;
+
+        VIR_FREE(tmp);
+
+        if (next == base)
+            break;
+    }
+
+    if (next != base) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("failed to resolve relative backing name: "
+                         "base image is not in backing chain"));
+        goto cleanup;
+    }
+
+    *relpath = path;
+    path = NULL;
+
+    ret = 0;
+
+ cleanup:
+    VIR_FREE(path);
+    VIR_FREE(tmp);
+    return ret;
+}
index ff53a860c9bf35e75dda66f8485b21520ddd4f5d..71f2a46d08868a376158ea8dc4c8db637f6deef7 100644 (file)
@@ -337,4 +337,9 @@ char *virStorageFileCanonicalizePath(const char *path,
                                      virStorageFileSimplifyPathReadlinkCallback cb,
                                      void *cbdata);
 
+int virStorageFileGetRelativeBackingPath(virStorageSourcePtr from,
+                                         virStorageSourcePtr to,
+                                         char **relpath)
+    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3);
+
 #endif /* __VIR_STORAGE_FILE_H__ */
index e85dc6e8e6d032b827204e3fc93a62eb4b84cd3b..419537d96d7ff503682d139aad60a148e8b6439d 100644 (file)
@@ -589,6 +589,104 @@ testPathCanonicalize(const void *args)
     return ret;
 }
 
+static virStorageSource backingchain[12];
+
+static void
+testPathRelativePrepare(void)
+{
+    size_t i;
+
+    for (i = 0; i < ARRAY_CARDINALITY(backingchain); i++) {
+        if (i < ARRAY_CARDINALITY(backingchain) - 1)
+            backingchain[i].backingStore = &backingchain[i + 1];
+        else
+            backingchain[i].backingStore = NULL;
+
+        backingchain[i].backingRelative = true;
+    }
+
+    /* normal relative backing chain */
+    backingchain[0].path = (char *) "/path/to/some/img";
+    backingchain[0].relPath = (char *) "/path/to/some/img";
+    backingchain[0].backingRelative = false;
+
+    backingchain[1].path = (char *) "/path/to/some/asdf";
+    backingchain[1].relPath = (char *) "asdf";
+
+    backingchain[2].path = (char *) "/path/to/some/test";
+    backingchain[2].relPath = (char *) "test";
+
+    backingchain[3].path = (char *) "/path/to/some/blah";
+    backingchain[3].relPath = (char *) "blah";
+
+    /* ovirt's backing chain */
+    backingchain[4].path = (char *) "/path/to/volume/image1";
+    backingchain[4].relPath = (char *) "/path/to/volume/image1";
+    backingchain[4].backingRelative = false;
+
+    backingchain[5].path = (char *) "/path/to/volume/image2";
+    backingchain[5].relPath = (char *) "../volume/image2";
+
+    backingchain[6].path = (char *) "/path/to/volume/image3";
+    backingchain[6].relPath = (char *) "../volume/image3";
+
+    backingchain[7].path = (char *) "/path/to/volume/image4";
+    backingchain[7].relPath = (char *) "../volume/image4";
+
+    /* some arbitrarily crazy backing chains */
+    backingchain[8].path = (char *) "/crazy/base/image";
+    backingchain[8].relPath = (char *) "/crazy/base/image";
+    backingchain[8].backingRelative = false;
+
+    backingchain[9].path = (char *) "/crazy/base/directory/stuff/volumes/garbage/image2";
+    backingchain[9].relPath = (char *) "directory/stuff/volumes/garbage/image2";
+
+    backingchain[10].path = (char *) "/crazy/base/directory/image3";
+    backingchain[10].relPath = (char *) "../../../image3";
+
+    backingchain[11].path = (char *) "/crazy/base/blah/image4";
+    backingchain[11].relPath = (char *) "../blah/image4";
+}
+
+
+struct testPathRelativeBacking
+{
+    virStorageSourcePtr top;
+    virStorageSourcePtr base;
+
+    const char *expect;
+};
+
+static int
+testPathRelative(const void *args)
+{
+    const struct testPathRelativeBacking *data = args;
+    char *actual = NULL;
+    int ret = -1;
+
+    if (virStorageFileGetRelativeBackingPath(data->top,
+                                             data->base,
+                                             &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->top->path, data->base->path,
+                NULLSTR(data->expect), NULLSTR(actual));
+        goto cleanup;
+    }
+
+    ret = 0;
+
+ cleanup:
+    VIR_FREE(actual);
+
+    return ret;
+}
+
 
 static int
 mymain(void)
@@ -598,6 +696,7 @@ mymain(void)
     struct testChainData data;
     struct testLookupData data2;
     struct testPathCanonicalizeData data3;
+    struct testPathRelativeBacking data4;
     virStorageSourcePtr chain = NULL;
     virStorageSourcePtr chain2; /* short for chain->backingStore */
     virStorageSourcePtr chain3; /* short for chain2->backingStore */
@@ -1181,6 +1280,53 @@ mymain(void)
     TEST_PATH_CANONICALIZE(30, "/cycle2/link", NULL);
     TEST_PATH_CANONICALIZE(31, "///", "/");
 
+#define TEST_RELATIVE_BACKING(id, TOP, BASE, EXPECT)                        \
+    do {                                                                    \
+        data4.top = &TOP;                                                   \
+        data4.base = &BASE;                                                 \
+        data4.expect = EXPECT;                                              \
+        if (virtTestRun("Path relative resolve " #id,                       \
+                        testPathRelative, &data4) < 0)                      \
+            ret = -1;                                                       \
+    } while (0)
+
+    testPathRelativePrepare();
+
+    /* few negative tests first */
+
+    /* a non-relative image is in the backing chain span */
+    TEST_RELATIVE_BACKING(1, backingchain[0], backingchain[1], NULL);
+    TEST_RELATIVE_BACKING(2, backingchain[0], backingchain[2], NULL);
+    TEST_RELATIVE_BACKING(3, backingchain[0], backingchain[3], NULL);
+    TEST_RELATIVE_BACKING(4, backingchain[1], backingchain[5], NULL);
+
+    /* image is not in chain (specified backwards) */
+    TEST_RELATIVE_BACKING(5, backingchain[2], backingchain[1], NULL);
+
+    /* positive tests */
+    TEST_RELATIVE_BACKING(6, backingchain[1], backingchain[1], "asdf");
+    TEST_RELATIVE_BACKING(7, backingchain[1], backingchain[2], "test");
+    TEST_RELATIVE_BACKING(8, backingchain[1], backingchain[3], "blah");
+    TEST_RELATIVE_BACKING(9, backingchain[2], backingchain[2], "test");
+    TEST_RELATIVE_BACKING(10, backingchain[2], backingchain[3], "blah");
+    TEST_RELATIVE_BACKING(11, backingchain[3], backingchain[3], "blah");
+
+    /* oVirt spelling */
+    TEST_RELATIVE_BACKING(12, backingchain[5], backingchain[5], "../volume/image2");
+    TEST_RELATIVE_BACKING(13, backingchain[5], backingchain[6], "../volume/../volume/image3");
+    TEST_RELATIVE_BACKING(14, backingchain[5], backingchain[7], "../volume/../volume/../volume/image4");
+    TEST_RELATIVE_BACKING(15, backingchain[6], backingchain[6], "../volume/image3");
+    TEST_RELATIVE_BACKING(16, backingchain[6], backingchain[7], "../volume/../volume/image4");
+    TEST_RELATIVE_BACKING(17, backingchain[7], backingchain[7], "../volume/image4");
+
+    /* crazy spellings */
+    TEST_RELATIVE_BACKING(17, backingchain[9], backingchain[9], "directory/stuff/volumes/garbage/image2");
+    TEST_RELATIVE_BACKING(18, backingchain[9], backingchain[10], "directory/stuff/volumes/garbage/../../../image3");
+    TEST_RELATIVE_BACKING(19, backingchain[9], backingchain[11], "directory/stuff/volumes/garbage/../../../../blah/image4");
+    TEST_RELATIVE_BACKING(20, backingchain[10], backingchain[10], "../../../image3");
+    TEST_RELATIVE_BACKING(21, backingchain[10], backingchain[11], "../../../../blah/image4");
+    TEST_RELATIVE_BACKING(22, backingchain[11], backingchain[11], "../blah/image4");
+
  cleanup:
     /* Final cleanup */
     virStorageSourceFree(chain);