From 03d19f3e255bfd2b99e71a7f22119e1205ce7ed0 Mon Sep 17 00:00:00 2001 From: Zhiyi Zhang Date: Thu, 22 Nov 2018 14:46:39 +0800 Subject: [PATCH] kernelbase: Implement PathCchSkipRoot. Signed-off-by: Zhiyi Zhang Signed-off-by: Alexandre Julliard --- .../api-ms-win-core-path-l1-1-0.spec | 2 +- dlls/kernelbase/kernelbase.spec | 2 +- dlls/kernelbase/path.c | 108 +++++++++++++++ dlls/kernelbase/tests/path.c | 128 ++++++++++++++++++ include/pathcch.h | 1 + 5 files changed, 239 insertions(+), 2 deletions(-) diff --git a/dlls/api-ms-win-core-path-l1-1-0/api-ms-win-core-path-l1-1-0.spec b/dlls/api-ms-win-core-path-l1-1-0/api-ms-win-core-path-l1-1-0.spec index 53f7b987ca4..e07fe02a512 100644 --- a/dlls/api-ms-win-core-path-l1-1-0/api-ms-win-core-path-l1-1-0.spec +++ b/dlls/api-ms-win-core-path-l1-1-0/api-ms-win-core-path-l1-1-0.spec @@ -16,7 +16,7 @@ @ stdcall PathCchRemoveExtension(wstr long) kernelbase.PathCchRemoveExtension @ stub PathCchRemoveFileSpec @ stdcall PathCchRenameExtension(wstr long wstr) kernelbase.PathCchRenameExtension -@ stub PathCchSkipRoot +@ stdcall PathCchSkipRoot(wstr ptr) kernelbase.PathCchSkipRoot @ stdcall PathCchStripPrefix(wstr long) kernelbase.PathCchStripPrefix @ stub PathCchStripToRoot @ stdcall PathIsUNCEx(wstr ptr) kernelbase.PathIsUNCEx diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec index 8f4136eb6bc..eae67be1474 100644 --- a/dlls/kernelbase/kernelbase.spec +++ b/dlls/kernelbase/kernelbase.spec @@ -1045,7 +1045,7 @@ @ stdcall PathCchRemoveExtension(wstr long) # @ stub PathCchRemoveFileSpec @ stdcall PathCchRenameExtension(wstr long wstr) -# @ stub PathCchSkipRoot +@ stdcall PathCchSkipRoot(wstr ptr) @ stdcall PathCchStripPrefix(wstr long) # @ stub PathCchStripToRoot @ stdcall PathCombineA(ptr str str) shlwapi.PathCombineA diff --git a/dlls/kernelbase/path.c b/dlls/kernelbase/path.c index d0ec7e027cc..0b1eb91e9e5 100644 --- a/dlls/kernelbase/path.c +++ b/dlls/kernelbase/path.c @@ -50,6 +50,82 @@ static BOOL is_prefixed_disk(const WCHAR *string) return !strncmpW(string, prefix, ARRAY_SIZE(prefix)) && isalphaW(string[4]) && string[5] == ':'; } +static BOOL is_prefixed_volume(const WCHAR *string) +{ + static const WCHAR prefixed_volume[] = {'\\', '\\', '?', '\\', 'V', 'o', 'l', 'u', 'm', 'e'}; + const WCHAR *guid; + INT i = 0; + + if (strncmpiW(string, prefixed_volume, ARRAY_SIZE(prefixed_volume))) return FALSE; + + guid = string + ARRAY_SIZE(prefixed_volume); + + 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 (!isalnumW(guid[i])) return FALSE; + break; + } + i++; + } + + return TRUE; +} + +/* 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; + } +} + +/* 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 (isalphaW(path[0]) && path[1] == ':') + return path[2] == '\\' ? path + 2 : path + 1; + else + return NULL; +} + HRESULT WINAPI PathCchAddBackslash(WCHAR *path, SIZE_T size) { return PathCchAddBackslashEx(path, size, NULL, NULL); @@ -196,6 +272,38 @@ HRESULT WINAPI PathCchRenameExtension(WCHAR *path, SIZE_T size, const WCHAR *ext return FAILED(hr) ? hr : S_OK; } +HRESULT WINAPI PathCchSkipRoot(const WCHAR *path, const WCHAR **root_end) +{ + static const WCHAR unc_prefix[] = {'\\', '\\', '?'}; + + TRACE("%s %p\n", debugstr_w(path), root_end); + + if (!path || !path[0] || !root_end + || (!memicmpW(unc_prefix, path, ARRAY_SIZE(unc_prefix)) && !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; +} + HRESULT WINAPI PathCchStripPrefix(WCHAR *path, SIZE_T size) { TRACE("%s %lu\n", wine_dbgstr_w(path), size); diff --git a/dlls/kernelbase/tests/path.c b/dlls/kernelbase/tests/path.c index 5fc3cc8398b..c4499cc7908 100644 --- a/dlls/kernelbase/tests/path.c +++ b/dlls/kernelbase/tests/path.c @@ -37,6 +37,7 @@ HRESULT (WINAPI *pPathCchCombineEx)(WCHAR *out, SIZE_T size, const WCHAR *path1, HRESULT (WINAPI *pPathCchFindExtension)(const WCHAR *path, SIZE_T size, const WCHAR **extension); HRESULT (WINAPI *pPathCchRemoveExtension)(WCHAR *path, SIZE_T size); HRESULT (WINAPI *pPathCchRenameExtension)(WCHAR *path, SIZE_T size, const WCHAR *extension); +HRESULT (WINAPI *pPathCchSkipRoot)(const WCHAR *path, const WCHAR **root_end); HRESULT (WINAPI *pPathCchStripPrefix)(WCHAR *path, SIZE_T size); BOOL (WINAPI *pPathIsUNCEx)(const WCHAR *path, const WCHAR **server); @@ -634,6 +635,131 @@ static void test_PathCchRenameExtension(void) } } +struct skiproot_test +{ + const char *path; + int root_offset; + HRESULT hr; +}; + +static const struct skiproot_test skiproot_tests [] = +{ + /* Basic combination */ + {"", 0, E_INVALIDARG}, + {"C:\\", 3, S_OK}, + {"\\", 1, S_OK}, + {"\\\\.\\", 4, S_OK}, + {"\\\\?\\UNC\\", 8, S_OK}, + {"\\\\?\\C:\\", 7, S_OK}, + + /* Basic + \ */ + {"C:\\\\", 3, S_OK}, + {"\\\\", 2, S_OK}, + {"\\\\.\\\\", 4, S_OK}, + {"\\\\?\\UNC\\\\", 9, S_OK}, + {"\\\\?\\C:\\\\", 7, S_OK}, + + /* Basic + a */ + {"a", 0, E_INVALIDARG}, + {"C:\\a", 3, S_OK}, + {"\\a", 1, S_OK}, + {"\\\\.\\a", 5, S_OK}, + {"\\\\?\\UNC\\a", 9, S_OK}, + + /* Basic + \a */ + {"\\a", 1, S_OK}, + {"C:\\\\a", 3, S_OK}, + {"\\\\a", 3, S_OK}, + {"\\\\.\\\\a", 4, S_OK}, + {"\\\\?\\UNC\\\\a", 10, S_OK}, + {"\\\\?\\C:\\\\a", 7, S_OK}, + + /* Basic + a\ */ + {"a\\", 0, E_INVALIDARG}, + {"C:\\a\\", 3, S_OK}, + {"\\a\\", 1, S_OK}, + {"\\\\.\\a\\", 6, S_OK}, + {"\\\\?\\UNC\\a\\", 10, S_OK}, + {"\\\\?\\C:\\a\\", 7, S_OK}, + + /* Network share */ + {"\\\\\\\\", 3, S_OK}, + {"\\\\a\\", 4, S_OK}, + {"\\\\a\\b", 5, S_OK}, + {"\\\\a\\b\\", 6, S_OK}, + {"\\\\a\\b\\\\", 6, S_OK}, + {"\\\\a\\b\\\\c", 6, S_OK}, + {"\\\\a\\b\\c", 6, S_OK}, + {"\\\\a\\b\\c\\", 6, S_OK}, + {"\\\\a\\b\\c\\d", 6, S_OK}, + {"\\\\a\\\\b\\c\\", 4, S_OK}, + {"\\\\aa\\bb\\cc\\", 8, S_OK}, + + /* UNC */ + {"\\\\?\\UNC\\\\", 9, S_OK}, + {"\\\\?\\UNC\\a\\b", 11, S_OK}, + {"\\\\?\\UNC\\a\\b", 11, S_OK}, + {"\\\\?\\UNC\\a\\b\\", 12, S_OK}, + {"\\\\?\\UNC\\a\\b\\c", 12, S_OK}, + {"\\\\?\\UNC\\a\\b\\c\\", 12, S_OK}, + {"\\\\?\\UNC\\a\\b\\c\\d", 12, S_OK}, + {"\\\\?\\C:", 6, S_OK}, + {"\\\\?\\Volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}", 48, S_OK}, + {"\\\\?\\Volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}\\", 49, S_OK}, + {"\\\\?\\unc\\a\\b", 11, S_OK}, + {"\\\\?\\volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}\\", 49, S_OK}, + {"\\\\?\\volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}\\a", 49, S_OK}, + + /* Malformed */ + {"C:", 2, S_OK}, + {":", 0, E_INVALIDARG}, + {":\\", 0, E_INVALIDARG}, + {"C\\", 0, E_INVALIDARG}, + {"\\?", 1, S_OK}, + {"\\?\\UNC", 1, S_OK}, + {"\\\\?\\", 0, E_INVALIDARG}, + {"\\\\?\\UNC", 0, E_INVALIDARG}, + {"\\\\?\\::\\", 0, E_INVALIDARG}, + {"\\\\?\\Volume", 0, E_INVALIDARG}, + {"\\.", 1, S_OK}, + {"\\\\..", 4, S_OK}, + {"\\\\..a", 5, S_OK} +}; + +static void test_PathCchSkipRoot(void) +{ + WCHAR pathW[MAX_PATH]; + const WCHAR *root_end; + HRESULT hr; + INT i; + + if (!pPathCchSkipRoot) + { + win_skip("PathCchSkipRoot() is not available.\n"); + return; + } + + root_end = (const WCHAR *)0xdeadbeef; + hr = pPathCchSkipRoot(NULL, &root_end); + ok(hr == E_INVALIDARG, "Expect result %#x, got %#x\n", E_INVALIDARG, hr); + ok(root_end == (const WCHAR *)0xdeadbeef, "Expect root_end 0xdeadbeef, got %p\n", root_end); + + MultiByteToWideChar(CP_ACP, 0, "C:\\", -1, pathW, ARRAY_SIZE(pathW)); + hr = pPathCchSkipRoot(pathW, NULL); + ok(hr == E_INVALIDARG, "Expect result %#x, got %#x\n", E_INVALIDARG, hr); + + for (i = 0; i < ARRAY_SIZE(skiproot_tests); i++) + { + const struct skiproot_test *t = skiproot_tests + i; + MultiByteToWideChar(CP_ACP, 0, t->path, -1, pathW, ARRAY_SIZE(pathW)); + hr = pPathCchSkipRoot(pathW, &root_end); + ok(hr == t->hr, "path %s expect result %#x, got %#x\n", t->path, t->hr, hr); + if (SUCCEEDED(hr)) + ok(root_end - pathW == t->root_offset, "path %s expect root offset %d, got %ld\n", t->path, t->root_offset, + (INT_PTR)(root_end - pathW)); + } +} + struct stripprefix_test { const CHAR *path; @@ -789,6 +915,7 @@ START_TEST(path) pPathCchFindExtension = (void *)GetProcAddress(hmod, "PathCchFindExtension"); pPathCchRemoveExtension = (void *)GetProcAddress(hmod, "PathCchRemoveExtension"); pPathCchRenameExtension = (void *)GetProcAddress(hmod, "PathCchRenameExtension"); + pPathCchSkipRoot = (void *)GetProcAddress(hmod, "PathCchSkipRoot"); pPathCchStripPrefix = (void *)GetProcAddress(hmod, "PathCchStripPrefix"); pPathIsUNCEx = (void *)GetProcAddress(hmod, "PathIsUNCEx"); @@ -799,6 +926,7 @@ START_TEST(path) test_PathCchFindExtension(); test_PathCchRemoveExtension(); test_PathCchRenameExtension(); + test_PathCchSkipRoot(); test_PathCchStripPrefix(); test_PathIsUNCEx(); } diff --git a/include/pathcch.h b/include/pathcch.h index 1ff9e246481..42bae4edd0f 100644 --- a/include/pathcch.h +++ b/include/pathcch.h @@ -32,5 +32,6 @@ HRESULT WINAPI PathCchCombineEx(WCHAR *out, SIZE_T size, const WCHAR *path1, con HRESULT WINAPI PathCchFindExtension(const WCHAR *path, SIZE_T size, const WCHAR **extension); HRESULT WINAPI PathCchRemoveExtension(WCHAR *path, SIZE_T size); HRESULT WINAPI PathCchRenameExtension(WCHAR *path, SIZE_T size, const WCHAR *extension); +HRESULT WINAPI PathCchSkipRoot(const WCHAR *path, const WCHAR **root_end); HRESULT WINAPI PathCchStripPrefix(WCHAR *path, SIZE_T size); BOOL WINAPI PathIsUNCEx(const WCHAR *path, const WCHAR **server);