qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH 2/3] block: fix relative backing filenames for block


From: Jeff Cody
Subject: [Qemu-devel] [PATCH 2/3] block: fix relative backing filenames for block streaming
Date: Tue, 17 Apr 2012 09:17:13 -0400

When block streaming an image, if a base name is passed in that
is a relative name, but not accessible from the top-level snapshot,
then the relative name is stored incorrectly in the image file.

For instance, given a snapshot case of:

/tmp/a/base.raw
/tmp/a/snap1.qcow2
/tmp/b/snap2.qcow2

if they are all chained with relative filenames, like so:
base(bak:"") <- snap1(bak:"base.raw") <- snap2(bak:"../a/snap1.qcow2")

Then the merged top-layer will point to an inaccessible path for the
base file:

base(bak:"") <- snap2(bak:"base.raw")

This patch checks for a relative path for a basename, and fixes it
so that it is stored in the top-layer image relative to the top-layer
image.

Signed-off-by: Jeff Cody <address@hidden>
---
 block.c        |    5 +++
 block.h        |    1 +
 block/stream.c |   12 +++++++-
 cutils.c       |   92 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 qemu-common.h  |    2 +
 5 files changed, 111 insertions(+), 1 deletions(-)

diff --git a/block.c b/block.c
index 42f0953..98a28ee 100644
--- a/block.c
+++ b/block.c
@@ -206,6 +206,11 @@ static int path_has_protocol(const char *path)
     return strchr(path, ':') != NULL;
 }
 
+bool bdrv_has_protocol(BlockDriverState *bs)
+{
+    return(bs->drv && bs->drv->protocol_name);
+}
+
 int path_is_absolute(const char *path)
 {
     const char *p;
diff --git a/block.h b/block.h
index 415bb17..f2d6de2 100644
--- a/block.h
+++ b/block.h
@@ -340,6 +340,7 @@ void bdrv_disable_copy_on_read(BlockDriverState *bs);
 
 void bdrv_set_in_use(BlockDriverState *bs, int in_use);
 int bdrv_in_use(BlockDriverState *bs);
+bool bdrv_has_protocol(BlockDriverState *bs);
 
 enum BlockAcctType {
     BDRV_ACCT_READ,
diff --git a/block/stream.c b/block/stream.c
index 5c939c7..0335c78 100644
--- a/block/stream.c
+++ b/block/stream.c
@@ -172,6 +172,7 @@ static void coroutine_fn stream_run(void *opaque)
     int ret = 0;
     int n;
     void *buf;
+    char *base_filename = NULL;
 
     s->common.len = bdrv_getlength(bs);
     if (s->common.len < 0) {
@@ -240,13 +241,22 @@ retry:
     if (sector_num == end && ret == 0) {
         const char *base_id = NULL;
         if (base) {
-            base_id = s->backing_file_id;
+            /* fix up relative paths, if any */
+            if (!bdrv_has_protocol(bs) &&
+                !path_is_absolute(s->backing_file_id)) {
+                base_filename = path_find_relative(bs->filename,
+                                                   s->base->filename);
+                base_id = base_filename;
+            } else {
+                base_id = s->backing_file_id;
+            }
         }
         ret = bdrv_change_backing_file(bs, base_id, NULL);
         close_unused_images(bs, base, base_id);
     }
 
     qemu_vfree(buf);
+    g_free(base_filename);
     block_job_complete(&s->common, ret);
 }
 
diff --git a/cutils.c b/cutils.c
index dd82375..62fc0c6 100644
--- a/cutils.c
+++ b/cutils.c
@@ -553,6 +553,98 @@ int qemu_sendv(int sockfd, struct iovec *iov, int len, int 
iov_offset)
     return do_sendv_recvv(sockfd, iov, len, iov_offset, 1);
 }
 
+
+static inline bool is_path_separator(char c)
+{
+#ifdef _WIN32
+    return (c == '/' || c == '\\');
+#else
+    return (c == '/');
+#endif
+}
+
+/*
+ * Returns a newly allocated string containing the relative path to access
+ * backing, from current.  Caller is responsible for freeing this string.
+ *
+ * On error, returns the absolute canonical path of backing, if available; NULL
+ * otherwise.
+
+ * Note: it is expected that both 'current' and 'backing' are strings
+ * containing a path (relative or absolute) to a file, not just a directory.
+ * E.g., bs->filename, bs->backing_file.
+ */
+char *path_find_relative(char *current, char *backing)
+{
+    char *src = NULL;
+    char *dest = NULL;
+    char *src_tmp = NULL;
+    char *rel_backing = NULL;
+    char relpath[PATH_MAX] = {0};
+    int common_idx = 0;
+    int src_bytes_max = 0;
+
+
+    src = qemu_file_get_full_path(current, 0, NULL);
+    dest = qemu_file_get_full_path(backing, 0, NULL);
+
+    if (src == NULL || dest == NULL) {
+        goto free_and_exit;
+    }
+
+    src_tmp = g_strdup(src);
+    src_bytes_max = strlen(src_tmp)+1;
+
+    g_strlcpy(src_tmp, dirname(src), src_bytes_max);
+    g_strlcpy(src, src_tmp, src_bytes_max);
+
+    for (;;) {
+        char c;
+        if (!strncmp(src_tmp, dest, strlen(src_tmp))) {
+            /* common_idx is the marker of the first unique character of
+             * src_tmp and dest
+             */
+            common_idx = strlen(src_tmp);
+            c = '/';
+            if (common_idx > 1) {
+                c = dest[common_idx];
+                /* dirname() will return '/' for base directory, if we get
+                 * that far. That is the only dirname() result that will end
+                 * with a '/' or '\'. Account for this by moving the index
+                 * marker forward.
+                 */
+                common_idx++;
+            }
+            if (common_idx < strlen(dest)) {
+                /* we are looking for the commonality of directory paths,
+                 * not just characters - the common part must denote a full
+                 * directory.  Look for directory separator.
+                 */
+                if (is_path_separator(c)) {
+                    /* success */
+                    rel_backing = g_strconcat(relpath, &dest[common_idx], 
NULL);
+                    break;
+                }
+            } else {
+                rel_backing = g_strdup(dest);
+                break;
+            }
+        } else if (strlen(src_tmp) <= 1) {
+            break;
+        }
+        g_strlcpy(src_tmp, dirname(src), src_bytes_max);
+        g_strlcpy(src, src_tmp, src_bytes_max);
+        g_strlcat(relpath, "../", sizeof(relpath));
+    }
+
+free_and_exit:
+    g_free(src_tmp);
+    free(src);
+    free(dest);
+    return rel_backing;
+}
+
+
 /**
  * g_get_full_path:
  * @program: a file name in the GLib file name encoding
diff --git a/qemu-common.h b/qemu-common.h
index 3c2af73..88ada73 100644
--- a/qemu-common.h
+++ b/qemu-common.h
@@ -360,6 +360,8 @@ gchar *qemu_file_get_full_path(const gchar *path,
                                QEMUFileFullPathFlags flags,
                                GError **error);
 
+char *path_find_relative(char *current, char *backing);
+
 #define QEMU_FILE_TYPE_BIOS   0
 #define QEMU_FILE_TYPE_KEYMAP 1
 char *qemu_find_file(int type, const char *name);
-- 
1.7.9.rc2.1.g69204




reply via email to

[Prev in Thread] Current Thread [Next in Thread]