Currently the two routines tries to "understand" and skip <protocol>: prefix in path arguments are path_combine() and path_is_absolute() (the latter isn't used anywhere but in the former). This is wrong, since notion of absolute path is, at least, protocol-specific.
The implementation is more wrong on windows where even non-absolute paths but with drive name (d:foo) should be treated as absolute, as in, one can't combine, say, c:\bar with d:foo forming c:\foo as path_combine() currently does. Introduce isslash() macro that works correctly on both windows and unix, use it in is_windows_drive_prefix() (since any form of slash can be used in constructs like //./), remove path_is_absolute() and hardcode the trivial (but right) condition in path_combine(), and simplify path_combine() further by removing <protocol>: handling and unifying shash searching. Signed-off-by: Michael Tokarev <m...@tls.msk.ru> --- block.c | 72 +++++++++++++++++++++----------------------------------------- 1 files changed, 25 insertions(+), 47 deletions(-) diff --git a/block.c b/block.c index 42d6ff1..31a821d 100644 --- a/block.c +++ b/block.c @@ -71,6 +71,9 @@ static BlockDriverState *bs_snapshots; static int use_bdrv_whitelist; #ifdef _WIN32 + +#define isslash(c) ((c) == '/' || (c) == '\\') + static int is_windows_drive_prefix(const char *filename) { return (((filename[0] >= 'a' && filename[0] <= 'z') || @@ -83,11 +86,17 @@ int is_windows_drive(const char *filename) if (is_windows_drive_prefix(filename) && filename[2] == '\0') return 1; - if (strstart(filename, "\\\\.\\", NULL) || - strstart(filename, "//./", NULL)) - return 1; + if (isslash(filename[0] && isslash(filename[1]) && + filename[2] == '.' && isslash(filename[3])) + return 1; /* special case: windows device "//./" */ return 0; } + +#else + +#define isslash(c) ((c) == '/') +#define is_windows_drive_prefix(filename) (0) + #endif /* check if the path starts with "<protocol>:" @@ -115,61 +124,30 @@ static char *path_has_protocol(const char *path) (char*)p : NULL; } -int path_is_absolute(const char *path) -{ - const char *p; -#ifdef _WIN32 - /* specific case for names like: "\\.\d:" */ - if (*path == '/' || *path == '\\') - return 1; -#endif - p = strchr(path, ':'); - if (p) - p++; - else - p = path; -#ifdef _WIN32 - return (*p == '/' || *p == '\\'); -#else - return (*p == '/'); -#endif -} - /* if filename is absolute, just copy it to dest. Otherwise, build a - path to it by considering it is relative to base_path. URL are - supported. */ + path to it by considering it is relative to base_path. + This is really about filenames not URLs - we don't support + <protocol>: prefix in filename since it makes no sense, especially + if the protocol in base_path is not the same as in filename. + */ void path_combine(char *dest, int dest_size, const char *base_path, const char *filename) { - const char *p, *p1; + const char *p; int len; if (dest_size <= 0) return; - if (path_is_absolute(filename)) { + /* on windows, d:filename should be treated as absolute too */ + if (isslash(filename[0]) || is_windows_drive_prefix(filename)) { pstrcpy(dest, dest_size, filename); } else { - p = strchr(base_path, ':'); - if (p) - p++; - else - p = base_path; - p1 = strrchr(base_path, '/'); -#ifdef _WIN32 - { - const char *p2; - p2 = strrchr(base_path, '\\'); - if (!p1 || p2 > p1) - p1 = p2; - } -#endif - if (p1) - p1++; - else - p1 = base_path; - if (p1 > p) - p = p1; + /* find last slash */ + p = base_path + strlen(base_path); + while(p >= base_path && !isslash(*p)) + --p; + p++; len = p - base_path; if (len > dest_size - 1) len = dest_size - 1; -- 1.7.2.3