@@ -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;
@@ -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,
@@ -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);
}
@@ -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
@@ -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);
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 <jcody@redhat.com> --- block.c | 5 +++ block.h | 1 + block/stream.c | 12 +++++++- cutils.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ qemu-common.h | 2 + 5 files changed, 111 insertions(+), 1 deletions(-)