Description:

When using base images (ie difference disks), the path of the backing image is 
interpreted correctly as being relative to the path of the difference image 
(which is very nice). However, at creation time, the path is copied verbatim 
from the command line, which can lead to incorrect results if the "qemu-img 
create" is issued from a different location than the final location of the 
difference image.

For example:

if we have /home/cdman/qemu/winxp.img and we want to create 
/home/cdman/Desktop/diff.img and we are currently in the /home/cdman directory, 
we would say:
qemu-img create -f qcow2 -b qemu/winxp.img Desktop/diff.img

With the current source, this would write "qemu/winxp.img" in the diff.img 
file, however the correct thing to write would be "../qemu/winxp.img" (because 
the path should be relative to the location of diff.img). The attached patch 
fixes this. 

Warning! It contains string manipulation in C (which no sane person should do 
:)) and was not tested on other platforms than Linux, although I tried to code 
in some Windows compatibility.

PS. If there are no objections to the packet capturing patch 
(http://lists.gnu.org/archive/html/qemu-devel/2007-12/msg00501.html), could 
somebody please apply it? Or if there are, please let me know so that I can 
make any necessary changes.

Best regards




      ___________________________________________________________
Support the World Aids Awareness campaign this month with Yahoo! For Good 
http://uk.promotions.yahoo.com/forgood/
Index: block.c
===================================================================
RCS file: /sources/qemu/qemu/block.c,v
retrieving revision 1.53
diff -u -r1.53 block.c
--- block.c	24 Dec 2007 16:10:43 -0000	1.53
+++ block.c	30 Dec 2007 09:49:41 -0000
@@ -35,6 +35,11 @@
 #include <sys/disk.h>
 #endif
 
+#include <limits.h>
+#include <stdlib.h>
+#include <libgen.h>
+#include <unistd.h>
+
 #define SECTOR_BITS 9
 #define SECTOR_SIZE (1 << SECTOR_BITS)
 
@@ -123,6 +128,132 @@
     }
 }
 
+char *realpath_alloc(const char *path)
+{
+    int buff_len;
+    char *result;
+    
+#ifdef PATH_MAX    
+    buff_len = PATH_MAX;
+#else
+    buff_len = pathconf(path, _PC_PATH_MAX);
+    if (buff_len <= 0)
+        buff_len = 4096;
+#endif
+
+    ++buff_len;    
+    result = (char*)malloc(buff_len * sizeof(char));
+    if (!result)
+        return NULL;
+    
+    realpath(path, result);
+    return result;
+}
+
+char *get_relative_path(char *path, char *relative_to)
+{
+    char *path_real;
+    char *path_prefix;
+    char *path_real_suffix;    
+    char *path_real_to;
+    char *path_real_to_suffix;
+    char *result;
+    int prefix_len, i, slash_count;
+    char *string_end;
+    char path_separator;
+#ifdef _WIN32
+    path_separator = '\\';
+#else
+    path_separator = '/';
+#endif
+
+    if (NULL == path || NULL == relative_to)
+        return NULL;
+        
+    printf(">>>%s\n>>>%s\n", path, relative_to);
+    
+    path_real = realpath_alloc(path);
+    if (!path_real)
+	    return NULL;
+    path_real_to = realpath_alloc(relative_to);
+    if (!path_real_to)
+    {
+	    free(path_real);
+	    return NULL;
+    }
+    
+    if (0 == strcmp(path_real, path_real_to))
+    {
+        free(path_real);
+        free(path_real_to);
+    
+	    //the two directories are equal, the relative path is an empty string
+	    result = (char*)malloc(sizeof(char));
+	    *result = '\0';
+	    return result;
+    }
+    
+    result = NULL;
+    
+    //eliminate the common prefix
+    for (prefix_len = 0;
+    	path_real[prefix_len] != '\0' &&
+	    path_real_to[prefix_len] != '\0' &&
+	    path_real[prefix_len] == path_real_to[prefix_len];
+	    ++prefix_len);
+	
+    path_prefix = path_real;
+    path_real_suffix = path_real + prefix_len;
+    while ('\0' != *path_real_suffix &&
+#ifdef _WIN32
+        ('/' == *path_real_suffix || '\\' == *path_real_suffix)
+#else
+        ('/' == *path_real_suffix)
+#endif	
+        ) { *path_real_suffix++ = '\0'; }
+    
+    path_real_to_suffix = path_real_to + prefix_len;
+    while ('\0' != *path_real_to_suffix &&
+#ifdef _WIN32
+        ('/' == *path_real_to_suffix || '\\' == *path_real_to_suffix)
+#else
+        ('/' == *path_real_to_suffix)
+#endif	
+        ) { *path_real_to_suffix++ = '\0'; }
+	
+    slash_count = 0;
+    for (i = 0; '\0' != path_real_to_suffix[i]; ++i)
+#ifdef _WIN32
+        if ('/' == path_real_to_suffix[i] || '\\' == path_real_to_suffix[i])
+#else
+        if ('/' == path_real_to_suffix[i])
+#endif	    
+	        ++slash_count;
+	if ('\0' != *path_real_to_suffix) ++slash_count;
+    result = (char*)malloc(sizeof(char) * (4 + 3 * slash_count + strlen(path_real_suffix)));
+
+    string_end = result;
+    for (i = 0; i < slash_count; ++i)
+    {
+        if (i > 0)
+            *string_end++ = path_separator;
+        *string_end++ = '.';
+        *string_end++ = '.';        
+    }
+    if (0 == slash_count)
+        *string_end++ = '.';
+    if ('\0' != *path_real_suffix)
+    {
+        *string_end++ = path_separator;
+        for (i = 0; '\0' != path_real_suffix[i]; ++i)
+            *string_end++ = path_real_suffix[i];	
+    }
+    *string_end++ = '\0';
+    
+    free(path_real);
+    free(path_real_to);
+    return result;
+}
 
 static void bdrv_register(BlockDriver *bdrv)
 {
@@ -174,9 +305,94 @@
                 const char *filename, int64_t size_in_sectors,
                 const char *backing_file, int flags)
 {
+    char *filename_dir, *backing_file_dir;
+    char *backing_file_relative;
+    char *backing_file_relative_path;
+    char *backing_file_name;
+    char *temp;
+    int return_value;
+    
     if (!drv->bdrv_create)
-        return -ENOTSUP;
-    return drv->bdrv_create(filename, size_in_sectors, backing_file, flags);
+        return -ENOTSUP;    
+    
+    backing_file_relative = NULL;
+    if (backing_file && !path_is_absolute(backing_file))
+    {
+        return_value = -1;        
+        backing_file_relative_path = NULL;
+        backing_file_dir = NULL;
+        backing_file_name = NULL;
+        while (1)
+        {
+            filename_dir = realpath_alloc(filename);
+            if (!filename_dir)
+                break;
+            filename_dir = dirname(filename_dir);
+            
+            backing_file_dir = realpath_alloc(backing_file);
+            if (!backing_file_dir)
+                break;
+            asprintf(&backing_file_name, "%s", backing_file_dir);
+            if (!backing_file_name)
+                break;
+            
+            backing_file_dir = dirname(backing_file_dir);
+            asprintf(&temp, "%s", basename(backing_file_name));
+            free(backing_file_name);
+            asprintf(&backing_file_name, "%s", temp);
+            free(temp);
+            temp = NULL;
+            
+            if (!strcmp(filename_dir, backing_file_dir))
+            {
+                //the two files are in the same directory
+                //we need to keep only the filename
+                asprintf(&backing_file_relative, "%s", backing_file_name);
+            }
+            else
+            {
+                //they are in two different directories
+                //get the directory of backing_file
+                //relative to the directory of filename
+                backing_file_relative_path = get_relative_path(backing_file_dir, filename_dir);
+                if (!backing_file_relative_path)
+                    break;
+                asprintf(&backing_file_relative, "%s%c%s", backing_file_relative_path,
+#ifdef _WIN32
+                    '\\',
+#else
+                    '/',
+#endif
+                    backing_file_name);
+            }
+            
+            if (!backing_file_relative)
+                break;
+            backing_file = backing_file_relative;            
+                
+            return_value = 0;
+            break;
+        }
+        
+        if (filename_dir)
+            free(filename_dir);
+        if (backing_file_dir)
+            free(backing_file_dir);
+        if (backing_file_name)
+            free(backing_file_name);                    
+        if (backing_file_relative_path)
+            free(backing_file_relative_path);
+        if (return_value < 0)
+            //there has been an error
+            return return_value;        
+    }
+    
+    return_value = drv->bdrv_create(filename, size_in_sectors, backing_file, flags);
+    
+    if (backing_file_relative)
+        free(backing_file_relative);
+        
+    return return_value;
 }
 
 #ifdef _WIN32

Reply via email to