Signed-off-by: Zhiyi Zhang zzhang@codeweavers.com --- .../api-ms-win-core-path-l1-1-0.spec | 2 +- dlls/kernelbase/kernelbase.spec | 2 +- dlls/kernelbase/path.c | 182 +++++++++++ dlls/kernelbase/tests/path.c | 306 ++++++++++++++++++ include/pathcch.h | 2 + 5 files changed, 492 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 725f16448f..9bce0ace95 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 @@ -1,4 +1,4 @@ -@ stub PathAllocCanonicalize +@ stdcall PathAllocCanonicalize(wstr long ptr) kernelbase.PathAllocCanonicalize @ stub PathAllocCombine @ stdcall PathCchAddBackslash(wstr long) kernelbase.PathCchAddBackslash @ stdcall PathCchAddBackslashEx(wstr long ptr ptr) kernelbase.PathCchAddBackslashEx diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec index e7bb62d75c..8273d5f1b1 100644 --- a/dlls/kernelbase/kernelbase.spec +++ b/dlls/kernelbase/kernelbase.spec @@ -1023,7 +1023,7 @@ @ stdcall PathAddBackslashW(wstr) shlwapi.PathAddBackslashW @ stdcall PathAddExtensionA(str str) shlwapi.PathAddExtensionA @ stdcall PathAddExtensionW(wstr wstr) shlwapi.PathAddExtensionW -# @ stub PathAllocCanonicalize +@ stdcall PathAllocCanonicalize(wstr long ptr) # @ stub PathAllocCombine @ stdcall PathAppendA(str str) shlwapi.PathAppendA @ stdcall PathAppendW(wstr wstr) shlwapi.PathAppendW diff --git a/dlls/kernelbase/path.c b/dlls/kernelbase/path.c index c3fca8b9cf..659a17bd09 100644 --- a/dlls/kernelbase/path.c +++ b/dlls/kernelbase/path.c @@ -126,6 +126,188 @@ static const WCHAR *get_root_end(const WCHAR *path) return NULL; }
+HRESULT WINAPI PathAllocCanonicalize(const WCHAR *path_in, DWORD flags, WCHAR **path_out) +{ + WCHAR *buffer, *dst; + const WCHAR *src; + const WCHAR *root_end; + SIZE_T buffer_size, length; + + TRACE("%s %#x %p\n", debugstr_w(path_in), flags, path_out); + + if (!path_in || !path_out + || ((flags & PATHCCH_FORCE_ENABLE_LONG_NAME_PROCESS) && (flags & PATHCCH_FORCE_DISABLE_LONG_NAME_PROCESS)) + || (flags & (PATHCCH_FORCE_ENABLE_LONG_NAME_PROCESS | PATHCCH_FORCE_DISABLE_LONG_NAME_PROCESS) + && !(flags & PATHCCH_ALLOW_LONG_PATHS)) + || ((flags & PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH) && (flags & PATHCCH_ALLOW_LONG_PATHS))) + { + if (path_out) *path_out = NULL; + return E_INVALIDARG; + } + + length = strlenW(path_in); + if ((length + 1 > MAX_PATH && !(flags & (PATHCCH_ALLOW_LONG_PATHS | PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH))) + || (length + 1 > PATHCCH_MAX_CCH)) + { + *path_out = NULL; + return HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE); + } + + /* PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH implies PATHCCH_DO_NOT_NORMALIZE_SEGMENTS */ + if (flags & PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH) flags |= PATHCCH_DO_NOT_NORMALIZE_SEGMENTS; + + /* path length + possible \?\ addition + possible \ addition + NUL */ + buffer_size = (length + 6) * sizeof(WCHAR); + buffer = LocalAlloc(LMEM_ZEROINIT, buffer_size); + if (!buffer) + { + *path_out = NULL; + return E_OUTOFMEMORY; + } + + src = path_in; + dst = buffer; + + root_end = get_root_end(path_in); + if (root_end) root_end = buffer + (root_end - path_in); + + /* Copy path root */ + if (root_end) + { + memcpy(dst, src, (root_end - buffer + 1) * sizeof(WCHAR)); + src += root_end - buffer + 1; + if(PathCchStripPrefix(dst, length + 6) == S_OK) + { + /* Fill in \ in X:\ if the \ is missing */ + if(isalphaW(dst[0]) && dst[1] == ':' && dst[2]!= '\') + { + dst[2] = '\'; + dst[3] = 0; + } + dst = buffer + strlenW(buffer); + root_end = dst; + } + else + dst += root_end - buffer + 1; + } + + while (*src) + { + if (src[0] == '.') + { + if (src[1] == '.') + { + /* Keep one . after * */ + if (dst > buffer && dst[-1] == '*') + { + *dst++ = *src++; + continue; + } + + /* Keep the . if one of the following is true: + * 1. PATHCCH_DO_NOT_NORMALIZE_SEGMENTS + * 2. in form of a..b + */ + if (dst > buffer + && (((flags & PATHCCH_DO_NOT_NORMALIZE_SEGMENTS) && dst[-1] != '\') + || (dst[-1] != '\' && src[2] != '\' && src[2]))) + { + *dst++ = *src++; + *dst++ = *src++; + continue; + } + + /* Remove the \ before .. if the \ is not part of root */ + if (dst > buffer && dst[-1] == '\' && (!root_end || dst - 1 > root_end)) + { + *--dst = '\0'; + /* Remove characters until a \ is encountered */ + while (dst > buffer) + { + if (dst[-1] == '\') + { + *--dst = 0; + break; + } + else + *--dst = 0; + } + } + /* Remove the extra \ after .. if the \ before .. wasn't deleted */ + else if (src[2] == '\') + src++; + + src += 2; + } + else + { + /* Keep the . if one of the following is true: + * 1. PATHCCH_DO_NOT_NORMALIZE_SEGMENTS + * 2. in form of a.b, which is used in domain names + * 3. *. + */ + if (dst > buffer + && ((flags & PATHCCH_DO_NOT_NORMALIZE_SEGMENTS && dst[-1] != '\') + || (dst[-1] != '\' && src[1] != '\' && src[1]) || (dst[-1] == '*'))) + { + *dst++ = *src++; + continue; + } + + /* Remove the \ before . if the \ is not part of root */ + if (dst > buffer && dst[-1] == '\' && (!root_end || dst - 1 > root_end)) dst--; + /* Remove the extra \ after . if the \ before . wasn't deleted */ + else if (src[1] == '\') + src++; + + src++; + } + + /* If X:\ is not complete, then complete it */ + if (isalphaW(buffer[0]) && buffer[1] == ':' && buffer[2] != '\') + { + root_end = buffer + 2; + dst = buffer + 3; + buffer[2] = '\'; + /* If next character is , use the \ to fill in */ + if (src[0] == '\') src++; + } + } + /* Copy over */ + else + *dst++ = *src++; + } + /* End the path */ + *dst = 0; + + /* If result path is empty, fill in \ */ + if (!*buffer) + { + buffer[0] = '\'; + buffer[1] = 0; + } + + /* Extend the path if needed */ + length = strlenW(buffer); + if (((length + 1 > MAX_PATH && isalphaW(buffer[0]) && buffer[1] == ':') + || (isalphaW(buffer[0]) && buffer[1] == ':' && flags & PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH)) + && !(flags & PATHCCH_FORCE_ENABLE_LONG_NAME_PROCESS)) + { + memmove(buffer + 4, buffer, (length + 1) * sizeof(WCHAR)); + buffer[0] = '\'; + buffer[1] = '\'; + buffer[2] = '?'; + buffer[3] = '\'; + } + + /* Add a trailing backslash to the path if needed */ + if (flags & PATHCCH_ENSURE_TRAILING_SLASH) + PathCchAddBackslash(buffer, buffer_size); + + *path_out = buffer; + return S_OK; +} + HRESULT WINAPI PathCchAddBackslash(WCHAR *path, SIZE_T size) { return PathCchAddBackslashEx(path, size, NULL, NULL); diff --git a/dlls/kernelbase/tests/path.c b/dlls/kernelbase/tests/path.c index 8981b3a08a..0a55369d99 100644 --- a/dlls/kernelbase/tests/path.c +++ b/dlls/kernelbase/tests/path.c @@ -30,6 +30,7 @@
#include "wine/test.h"
+HRESULT (WINAPI *pPathAllocCanonicalize)(const WCHAR *path_in, DWORD flags, WCHAR **path_out); HRESULT (WINAPI *pPathCchAddBackslash)(WCHAR *out, SIZE_T size); HRESULT (WINAPI *pPathCchAddBackslashEx)(WCHAR *out, SIZE_T size, WCHAR **endptr, SIZE_T *remaining); HRESULT (WINAPI *pPathCchAddExtension)(WCHAR *path, SIZE_T size, const WCHAR *extension); @@ -46,6 +47,309 @@ HRESULT (WINAPI *pPathCchStripPrefix)(WCHAR *path, SIZE_T size); HRESULT (WINAPI *pPathCchStripToRoot)(WCHAR *path, SIZE_T size); BOOL (WINAPI *pPathIsUNCEx)(const WCHAR *path, const WCHAR **server);
+struct alloccanonicalize_test +{ + const CHAR *path_in; + const CHAR *path_out; + DWORD flags; + HRESULT hr; +}; + +static const struct alloccanonicalize_test alloccanonicalize_tests[] = +{ + /* Malformed path */ + {"C:a", "C:a", 0, S_OK}, + {"\\?\C:", "C:\", 0, S_OK}, + {"\\?C:\a", "\\?C:\a", 0, S_OK}, + {"\\?UNC\a", "\\?UNC\a", 0, S_OK}, + {"\\?\UNCa", "\\?\UNCa", 0, S_OK}, + {"\\?C:a", "\\?C:a", 0, S_OK}, + + /* No . */ + {"", "\", 0, S_OK}, + {"C:", "C:", 0, S_OK}, + {"C:\", "C:\", 0, S_OK}, + {"\\?\C:\a", "C:\a", 0, S_OK}, + {"\\?\UNC\a", "\\a", 0, S_OK}, + + /* . */ + {".", "\", 0, S_OK}, + {"..", "\", 0, S_OK}, + {"...", "\", 0, S_OK}, + {"*.", "*.", 0, S_OK}, + {"*..", "*.", 0, S_OK}, + {"*...", "*.", 0, S_OK}, + {"a.", "a", 0, S_OK}, + {"a.b", "a.b", 0, S_OK}, + {"a\.", "a", 0, S_OK}, + {"a\.\b", "a\b", 0, S_OK}, + {"C:.", "C:\", 0, S_OK}, + {"C:\.", "C:\", 0, S_OK}, + {"C:\.\", "C:\", 0, S_OK}, + {"C:\a.", "C:\a", 0, S_OK}, + {"C:\a\.", "C:\a", 0, S_OK}, + {"C:\a\\.", "C:\a\", 0, S_OK}, + {"C:\a\\\.", "C:\a\\", 0, S_OK}, + {"\.", "\", 0, S_OK}, + {"\\.", "\\", 0, S_OK}, + {"\\.\", "\\", 0, S_OK}, + {"\\\.", "\\", 0, S_OK}, + {"\\.\\", "\\\", 0, S_OK}, + {"\\\\.", "\\\", 0, S_OK}, + {"\?\.", "\?", 0, S_OK}, + {"\\?\.", "\\?", 0, S_OK}, + {"\192.168.1.1\a", "\192.168.1.1\a", 0, S_OK}, + {"\a.168.1.1\a", "\a.168.1.1\a", 0, S_OK}, + {"\\192.168.1.1\a", "\\192.168.1.1\a", 0, S_OK}, + {"\\a.168.1.1\b", "\\a.168.1.1\b", 0, S_OK}, + {"\\?\C:.", "C:\", 0, S_OK}, + {"\\?\C:\.", "C:\", 0, S_OK}, + {"\\?\UNC\.", "\\", 0, S_OK}, + {"\\?\Volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}\.", + "\\?\Volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}\", 0, S_OK}, + + /* .. */ + {"a..", "a", 0, S_OK}, + {"a..b", "a..b", 0, S_OK}, + {"a\..", "\", 0, S_OK}, + {"a\..\", "\", 0, S_OK}, + {"a\..\b", "\b", 0, S_OK}, + {"C:..", "C:\", 0, S_OK}, + {"C:\..", "C:\", 0, S_OK}, + {"C:\\..", "C:\", 0, S_OK}, + {"C:\..\", "C:\", 0, S_OK}, + {"C:\a\..", "C:\", 0, S_OK}, + {"C:\a\\..", "C:\a", 0, S_OK}, + {"C:\a\\\..", "C:\a\", 0, S_OK}, + {"C:\a\..\b", "C:\b", 0, S_OK}, + {"C:\a\..\\b", "C:\\b", 0, S_OK}, + {"\..", "\", 0, S_OK}, + {"\\..", "\\", 0, S_OK}, + {"\\\..", "\", 0, S_OK}, + {"\\..\", "\\", 0, S_OK}, + {"\\\..", "\", 0, S_OK}, + {"\\..\\", "\\\", 0, S_OK}, + {"\\\\..", "\\", 0, S_OK}, + {"\?\..", "\", 0, S_OK}, + {"\a\..", "\", 0, S_OK}, + {"\\?\..", "\", 0, S_OK}, + {"\\a\..", "\", 0, S_OK}, + {"\a\..\b", "\b", 0, S_OK}, + {"\a\b\..", "\a", 0, S_OK}, + {"\?\UNC\..", "\?", 0, S_OK}, + {"\?\C:\..", "\?", 0, S_OK}, + {"\\?\C:..", "C:\", 0, S_OK}, + {"\\?\C:\..", "C:\", 0, S_OK}, + {"\\?\UNC\..", "\\", 0, S_OK}, + {"\\?\Volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}..", + "\\?\Volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}", 0, S_OK}, + {"\\?\Volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}\..", + "\\?\Volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}\", 0, S_OK}, + {"\\?\UNC\a\b\..", "\\a", 0, S_OK}, + {"\\?\Volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}\a\b\..", + "\\?\Volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}\a", 0, S_OK}, + + /* . and .. */ + {"C:\a\.\b\..\", "C:\a\", 0, S_OK}, + {"\a\.\b\..\", "\a\", 0, S_OK}, + {"\?\a\.\b\..\", "\?\a\", 0, S_OK}, + {"\\.\a\.\b\..\", "\\a\", 0, S_OK}, + {"\\?\a\.\b\..\", "\\?\a\", 0, S_OK}, + {"\\.\..", "\\", 0, S_OK}, + + /* PATHCCH_ALLOW_LONG_PATHS */ + /* Input path with prefix \?\ and length of MAXPATH + 1, HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE) = 0x800700ce */ + {"\\?\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", NULL, 0, 0x800700ce}, + /* Input path with prefix C:\ and length of MAXPATH + 1 */ + {"C:\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", NULL, 0, 0x800700ce}, + {"C:\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "\\?\C:\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", PATHCCH_ALLOW_LONG_PATHS, S_OK}, + /* Input path with prefix C: and length of MAXPATH + 1 */ + {"C:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "\\?\C:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", PATHCCH_ALLOW_LONG_PATHS, S_OK}, + /* Input path with prefix C:\ and length of MAXPATH + 1 and with .. */ + {"C:\..\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "C:\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", PATHCCH_ALLOW_LONG_PATHS, S_OK}, + /* Input path with prefix \?\ and length of MAXPATH + 1 */ + {"\\?\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "\\?\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", PATHCCH_ALLOW_LONG_PATHS, S_OK}, + /* Input path with prefix \ and length of MAXPATH + 1 */ + {"\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", PATHCCH_ALLOW_LONG_PATHS, S_OK}, + /* Input path with length of MAXPATH with PATHCCH_ALLOW_LONG_PATHS disabled*/ + {"C:\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "C:\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 0, S_OK}, + /* Input path with length of MAXPATH */ + {"C:\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "C:\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", PATHCCH_ALLOW_LONG_PATHS, S_OK}, + + /* Flags added after Windows 10 1709 */ + /* PATHCCH_FORCE_ENABLE_LONG_NAME_PROCESS */ + /* PATHCCH_FORCE_ENABLE_LONG_NAME_PROCESS without PATHCCH_ALLOW_LONG_PATHS */ + {"", NULL, PATHCCH_FORCE_ENABLE_LONG_NAME_PROCESS, E_INVALIDARG}, + /* Input path with prefix C:\ and length of MAXPATH + 1 and PATHCCH_FORCE_ENABLE_LONG_NAME_PROCESS */ + {"C:\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "C:\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + PATHCCH_ALLOW_LONG_PATHS | PATHCCH_FORCE_ENABLE_LONG_NAME_PROCESS, S_OK}, + + /* PATHCCH_FORCE_DISABLE_LONG_NAME_PROCESS */ + /* PATHCCH_FORCE_DISABLE_LONG_NAME_PROCESS without PATHCCH_ALLOW_LONG_PATHS */ + {"", NULL, PATHCCH_FORCE_DISABLE_LONG_NAME_PROCESS, E_INVALIDARG}, + /* Both PATHCCH_FORCE_ENABLE_LONG_NAME_PROCESS and PATHCCH_FORCE_DISABLE_LONG_NAME_PROCESS */ + {"", "\", PATHCCH_ALLOW_LONG_PATHS | PATHCCH_FORCE_ENABLE_LONG_NAME_PROCESS | PATHCCH_FORCE_DISABLE_LONG_NAME_PROCESS, + E_INVALIDARG}, + /* Input path with prefix C:\ and length of MAXPATH + 1 and PATHCCH_FORCE_DISABLE_LONG_NAME_PROCESS */ + {"C:\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "\\?\C:\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + PATHCCH_ALLOW_LONG_PATHS | PATHCCH_FORCE_DISABLE_LONG_NAME_PROCESS, S_OK}, + /* Input path with prefix C:\ and length of MAXPATH and PATHCCH_FORCE_DISABLE_LONG_NAME_PROCESS */ + {"C:\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "C:\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + PATHCCH_ALLOW_LONG_PATHS | PATHCCH_FORCE_DISABLE_LONG_NAME_PROCESS, S_OK}, + + /* PATHCCH_DO_NOT_NORMALIZE_SEGMENTS */ + /* No effect for spaces */ + {"C:\a \", "C:\a \", PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, S_OK}, + {"C:\a\ ", "C:\a\ ", PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, S_OK}, + {"C:\a ", "C:\a ", PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, S_OK}, + {"C:\a ", "C:\a ", PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, S_OK}, + {"C:\a. ", "C:\a. ", PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, S_OK}, + {"\a \", "\a \", PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, S_OK}, + {"\a\ ", "\a\ ", PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, S_OK}, + {"\\a \", "\\a \", PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, S_OK}, + {"\\a\ ", "\\a\ ", PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, S_OK}, + {"\\?\ ", "\\?\ ", PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, S_OK}, + /* Keep trailing dot */ + {"*..", "*..", PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, S_OK}, + {".", "\", PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, S_OK}, + {"..", "\", PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, S_OK}, + {"C:.", "C:.", PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, S_OK}, + {"C:..", "C:..", PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, S_OK}, + {"C:\a\.", "C:\a", PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, S_OK}, + {"C:\a\..", "C:\", PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, S_OK}, + {"C:\a.", "C:\a.", PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, S_OK}, + {"C:\a..", "C:\a..", PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, S_OK}, + + /* PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH */ + {"C:\a\", "\\?\C:\a\", PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH, S_OK}, + {"", NULL, PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH | PATHCCH_ALLOW_LONG_PATHS, E_INVALIDARG}, + {"\a\", "\a\", PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH, S_OK}, + {"\\?\C:\a\", "\\?\C:\a\", PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH, S_OK}, + /* Implication of PATHCCH_DO_NOT_NORMALIZE_SEGMENTS by PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH */ + {"\a.", "\a.", PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH, S_OK}, + + /* PATHCCH_ENSURE_TRAILING_SLASH */ + {"\", "\", PATHCCH_ENSURE_TRAILING_SLASH, S_OK}, + {"C:\", "C:\", PATHCCH_ENSURE_TRAILING_SLASH, S_OK}, + {"C:\a\.", "C:\a\", PATHCCH_ENSURE_TRAILING_SLASH, S_OK}, + {"C:\a", "C:\a\", PATHCCH_ENSURE_TRAILING_SLASH, S_OK} +}; + +static void test_PathAllocCanonicalize(void) +{ + WCHAR path_inW[1024], path_maxW[PATHCCH_MAX_CCH + 1]; + WCHAR *path_outW; + CHAR path_outA[1024]; + BOOL skip_new_flags = TRUE; + HRESULT hr; + INT i; + + if (!pPathAllocCanonicalize) + { + win_skip("PathAllocCanonicalize() is not available.\n"); + return; + } + + /* No NULL check for path on Windows */ + if (0) + { + hr = pPathAllocCanonicalize(NULL, 0, &path_outW); + ok(hr == E_INVALIDARG, "expect hr %#x, got %#x\n", E_INVALIDARG, hr); + } + + MultiByteToWideChar(CP_ACP, 0, "C:\", -1, path_inW, ARRAY_SIZE(path_inW)); + hr = pPathAllocCanonicalize(path_inW, 0, NULL); + ok(hr == E_INVALIDARG, "expect hr %#x, got %#x\n", E_INVALIDARG, hr); + + /* Test longest path */ + for (i = 0; i < ARRAY_SIZE(path_maxW) - 1; i++) path_maxW[i] = 'a'; + path_maxW[PATHCCH_MAX_CCH] = '\0'; + path_outW = (WCHAR *)0xdeadbeef; + hr = pPathAllocCanonicalize(path_maxW, PATHCCH_ALLOW_LONG_PATHS, &path_outW); + ok(hr == HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE), "expect hr %#x, got %#x\n", + HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE), hr); + ok(path_outW == NULL, "expect path_outW null, got %p\n", path_outW); + + path_maxW[PATHCCH_MAX_CCH - 1] = '\0'; + hr = pPathAllocCanonicalize(path_maxW, PATHCCH_ALLOW_LONG_PATHS, &path_outW); + ok(hr == S_OK, "expect hr %#x, got %#x\n", S_OK, hr); + + /* Check if flags added after Windows 10 1709 are supported */ + MultiByteToWideChar(CP_ACP, 0, "C:\", -1, path_inW, ARRAY_SIZE(path_inW)); + hr = pPathAllocCanonicalize(path_inW, PATHCCH_FORCE_ENABLE_LONG_NAME_PROCESS, &path_outW); + if (hr == E_INVALIDARG) skip_new_flags = FALSE; + + for (i = 0; i < ARRAY_SIZE(alloccanonicalize_tests); i++) + { + const struct alloccanonicalize_test *t = alloccanonicalize_tests + i; + + if (((PATHCCH_FORCE_ENABLE_LONG_NAME_PROCESS | PATHCCH_FORCE_DISABLE_LONG_NAME_PROCESS + | PATHCCH_DO_NOT_NORMALIZE_SEGMENTS | PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH + | PATHCCH_ENSURE_TRAILING_SLASH) + & t->flags) + && skip_new_flags) + { + win_skip("Skip testing new flags added after Windows 10 1709\n"); + return; + } + + MultiByteToWideChar(CP_ACP, 0, t->path_in, -1, path_inW, ARRAY_SIZE(path_inW)); + hr = pPathAllocCanonicalize(path_inW, t->flags, &path_outW); + ok(hr == t->hr, "path %s expect result %#x, got %#x\n", t->path_in, t->hr, hr); + if (SUCCEEDED(hr)) + { + WideCharToMultiByte(CP_ACP, 0, path_outW, -1, path_outA, ARRAY_SIZE(path_outA), NULL, NULL); + ok(!lstrcmpA(path_outA, t->path_out), "path "%s" expect output path "%s", got "%s"\n", t->path_in, + t->path_out, path_outA); + LocalFree(path_outW); + } + } +} + static const struct { const char *path1; @@ -1386,6 +1690,7 @@ START_TEST(path) { HMODULE hmod = LoadLibraryA("kernelbase.dll");
+ pPathAllocCanonicalize = (void *)GetProcAddress(hmod, "PathAllocCanonicalize"); pPathCchCombineEx = (void *)GetProcAddress(hmod, "PathCchCombineEx"); pPathCchAddBackslash = (void *)GetProcAddress(hmod, "PathCchAddBackslash"); pPathCchAddBackslashEx = (void *)GetProcAddress(hmod, "PathCchAddBackslashEx"); @@ -1402,6 +1707,7 @@ START_TEST(path) pPathCchStripToRoot = (void *)GetProcAddress(hmod, "PathCchStripToRoot"); pPathIsUNCEx = (void *)GetProcAddress(hmod, "PathIsUNCEx");
+ test_PathAllocCanonicalize(); test_PathCchCombineEx(); test_PathCchAddBackslash(); test_PathCchAddBackslashEx(); diff --git a/include/pathcch.h b/include/pathcch.h index 12a6fc5511..b1d6be249d 100644 --- a/include/pathcch.h +++ b/include/pathcch.h @@ -22,9 +22,11 @@ #define PATHCCH_FORCE_DISABLE_LONG_NAME_PROCESS 0x04 #define PATHCCH_DO_NOT_NORMALIZE_SEGMENTS 0x08 #define PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH 0x10 +#define PATHCCH_ENSURE_TRAILING_SLASH 0x20
#define PATHCCH_MAX_CCH 0x8000
+HRESULT WINAPI PathAllocCanonicalize(const WCHAR *path_in, DWORD flags, WCHAR **path_out); HRESULT WINAPI PathCchAddBackslash(WCHAR *path, SIZE_T size); HRESULT WINAPI PathCchAddBackslashEx(WCHAR *path, SIZE_T size, WCHAR **end, SIZE_T *remaining); HRESULT WINAPI PathCchAddExtension(WCHAR *path, SIZE_T size, const WCHAR *extension);