diff --git a/util/cutils.c b/util/cutils.c index 42364039a598..21d267066338 100644 --- a/util/cutils.c +++ b/util/cutils.c @@ -44,7 +44,126 @@ #endif #ifdef G_OS_WIN32 -#include +#include "pathcch2.h" +#include + +static BOOL is_prefixed_volume(const WCHAR* string) +{ + const WCHAR* guid; + INT i = 0; + + if (wcsnicmp(string, L"\\\\?\\Volume", 10)) return FALSE; + + guid = string + 10; + + while (i <= 37) + { + switch (i) + { + case 0: + if (guid[i] != '{') return FALSE; + break; + case 9: + case 14: + case 19: + case 24: + if (guid[i] != '-') return FALSE; + break; + case 37: + if (guid[i] != '}') return FALSE; + break; + default: + if (!isxdigit(guid[i])) return FALSE; + break; + } + i++; + } + + return TRUE; +} + +static BOOL is_prefixed_unc(const WCHAR* string) +{ + return !wcsnicmp(string, L"\\\\?\\UNC\\", 8); +} + +static BOOL is_drive_spec(const WCHAR* str) +{ + return isalpha(str[0]) && str[1] == ':'; +} + +static BOOL is_prefixed_disk(const WCHAR* string) +{ + return !wcsncmp(string, L"\\\\?\\", 4) && is_drive_spec(string + 4); +} +/* Find the last character of the root in a path, if there is one, without any segments */ +static const WCHAR* get_root_end(const WCHAR* path) +{ + /* Find path root */ + if (is_prefixed_volume(path)) + return path[48] == '\\' ? path + 48 : path + 47; + else if (is_prefixed_unc(path)) + return path + 7; + else if (is_prefixed_disk(path)) + return path[6] == '\\' ? path + 6 : path + 5; + /* \\ */ + else if (path[0] == '\\' && path[1] == '\\') + return path + 1; + /* \ */ + else if (path[0] == '\\') + return path; + /* X:\ */ + else if (is_drive_spec(path)) + return path[2] == '\\' ? path + 2 : path + 1; + else + return NULL; +} + +/* Get the next character beyond end of the segment. + Return TRUE if the last segment ends with a backslash */ +static BOOL get_next_segment(const WCHAR* next, const WCHAR** next_segment) +{ + while (*next && *next != '\\') next++; + if (*next == '\\') + { + *next_segment = next + 1; + return TRUE; + } + else + { + *next_segment = next; + return FALSE; + } +} + +HRESULT WINAPI PathCchSkipRoot2(const WCHAR* path, const WCHAR** root_end) +{ + + if (!path || !path[0] || !root_end + || (!wcsnicmp(path, L"\\\\?", 3) && !is_prefixed_volume(path) && !is_prefixed_unc(path) + && !is_prefixed_disk(path))) + return E_INVALIDARG; + + *root_end = get_root_end(path); + if (*root_end) + { + (*root_end)++; + if (is_prefixed_unc(path)) + { + get_next_segment(*root_end, root_end); + get_next_segment(*root_end, root_end); + } + else if (path[0] == '\\' && path[1] == '\\' && path[2] != '?') + { + /* Skip share server */ + get_next_segment(*root_end, root_end); + /* If mount point is empty, don't skip over mount point */ + if (**root_end != '\\') get_next_segment(*root_end, root_end); + } + } + + return *root_end ? S_OK : E_INVALIDARG; +} #include #endif @@ -1169,7 +1288,7 @@ char *get_relocated_path(const char *dir) mbsrtowcs(wdir, &src, size, &(mbstate_t){0}); PCWSTR wdir_skipped_root; - if (PathCchSkipRoot(wdir, &wdir_skipped_root) == S_OK) { + if (PathCchSkipRoot2(wdir, &wdir_skipped_root) == S_OK) { size = wcsrtombs(NULL, &wdir_skipped_root, 0, &(mbstate_t){0}); char *cursor = result->str + result->len; g_string_set_size(result, result->len + size); diff --git a/util/meson.build b/util/meson.build index 5d8bef98912a..c9c1f4ca993c 100644 --- a/util/meson.build +++ b/util/meson.build @@ -25,7 +25,6 @@ else util_ss.add(files('event_notifier-win32.c')) util_ss.add(files('oslib-win32.c')) util_ss.add(files('qemu-thread-win32.c')) - util_ss.add(winmm, pathcch) endif util_ss.add(when: linux_io_uring, if_true: files('fdmon-io_uring.c')) if glib_has_gslice diff --git a/util/pathcch2.h b/util/pathcch2.h new file mode 100644 index 000000000000..b54f39a22c15 --- /dev/null +++ b/util/pathcch2.h @@ -0,0 +1 @@ +HRESULT WINAPI PathCchSkipRoot2(const WCHAR* path, const WCHAR** root_end); \ No newline at end of file