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 #endif +#include +#include +#include +#include + #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