kernelbase: Implement PathAllocCanonicalize.

Signed-off-by: Zhiyi Zhang <zzhang@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Zhiyi Zhang 2018-11-23 11:18:05 +08:00 committed by Alexandre Julliard
parent d381263ae5
commit 165f777c38
5 changed files with 492 additions and 2 deletions

View file

@ -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

View file

@ -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

View file

@ -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);

View file

@ -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();

View file

@ -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);