[PATCH] kernelbase: Fix PathAllocCanonicalize handling segments that contain dots.
Zhiyi Zhang
zzhang at codeweavers.com
Wed Sep 18 04:40:52 CDT 2019
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=47766
Signed-off-by: Zhiyi Zhang <zzhang at codeweavers.com>
---
dlls/kernelbase/path.c | 168 ++++++++++++++++++++++-------------
dlls/kernelbase/tests/path.c | 34 +++++++
2 files changed, 138 insertions(+), 64 deletions(-)
diff --git a/dlls/kernelbase/path.c b/dlls/kernelbase/path.c
index 82708be544..e2cff64f5a 100644
--- a/dlls/kernelbase/path.c
+++ b/dlls/kernelbase/path.c
@@ -200,8 +200,9 @@ static const WCHAR *get_root_end(const WCHAR *path)
HRESULT WINAPI PathAllocCanonicalize(const WCHAR *path_in, DWORD flags, WCHAR **path_out)
{
- WCHAR *buffer, *dst;
- const WCHAR *src;
+ WCHAR *buffer, *dst, *old_dst;
+ const WCHAR *src, *old_src;
+ const WCHAR *segment_start;
const WCHAR *root_end;
SIZE_T buffer_size, length;
@@ -263,78 +264,89 @@ HRESULT WINAPI PathAllocCanonicalize(const WCHAR *path_in, DWORD flags, WCHAR **
dst += root_end - buffer + 1;
}
- while (*src)
+ /* If segment normalization is on, remove trailing dots in the root segment */
+ if (!(flags & PATHCCH_DO_NOT_NORMALIZE_SEGMENTS))
{
- if (src[0] == '.')
+ old_src = src;
+ while (*src && *src == '.')
+ ++src;
+
+ /* Not trailing dots, don't remove */
+ if (*src)
+ {
+ src = old_src;
+ }
+ /* At least have one dot */
+ else if (src > old_src)
{
- if (src[1] == '.')
+ /* If X:\ is incomplete, complete it */
+ if (iswalpha(buffer[0]) && buffer[1] == ':' && buffer[2] != '\\')
{
- /* Keep one . after * */
- if (dst > buffer && dst[-1] == '*')
- {
- *dst++ = *src++;
- continue;
- }
+ root_end = buffer + 2;
+ dst = buffer + 3;
+ buffer[2] = '\\';
+ /* If the next character is \, skip it to avoid duplicated \ */
+ if (src[0] == '\\')
+ ++src;
+ }
+ }
+ }
- /* 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;
- }
+ /* Dots after X: are part of the root segment */
+ if (iswalpha(buffer[0]) && buffer[1] == ':' && !buffer[2])
+ {
+ while (*src && *src == '.')
+ *dst++ = *src++;
+ }
- /* Remove the \ before .. if the \ is not part of root */
- if (dst > buffer && dst[-1] == '\\' && (!root_end || dst - 1 > root_end))
+ while (*src)
+ {
+ /* .. up one level */
+ if (src[0] == '.' && src[1] == '.' && (src[2] == '\\' || !src[2]))
+ {
+ /* 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)
{
- *--dst = '\0';
- /* Remove characters until a \ is encountered */
- while (dst > buffer)
+ if (dst[-1] == '\\')
{
- if (dst[-1] == '\\')
- {
- *--dst = 0;
- break;
- }
- else
- *--dst = 0;
+ *--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++;
+ /* Remove the extra \ after .. if the \ before .. wasn't deleted */
+ else if (src[2] == '\\')
+ ++src;
- src++;
+ src += 2;
+ /* If X:\ is not complete, then complete it */
+ if (iswalpha(buffer[0]) && buffer[1] == ':' && buffer[2] != '\\')
+ {
+ root_end = buffer + 2;
+ dst = buffer + 3;
+ buffer[2] = '\\';
+ /* If the next character is \, skip it to avoid duplicated \ */
+ if (src[0] == '\\')
+ ++src;
}
-
+ }
+ /* . current level */
+ else if (src[0] == '.' && (src[1] == '\\' || !src[1]))
+ {
+ /* Remove the \ before . if the \ is not part of root */
+ if (dst > buffer && dst[-1] == '\\' && (!root_end || dst - 1 > root_end))
+ *--dst = 0;
+ /* 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 (iswalpha(buffer[0]) && buffer[1] == ':' && buffer[2] != '\\')
{
@@ -345,9 +357,37 @@ HRESULT WINAPI PathAllocCanonicalize(const WCHAR *path_in, DWORD flags, WCHAR **
if (src[0] == '\\') src++;
}
}
- /* Copy over */
- else
+ /* *\.+ */
+ else if (!(flags & PATHCCH_DO_NOT_NORMALIZE_SEGMENTS) && src[0] == '*' && src[1] == '.')
+ {
+ /* Keep only one . after * */
*dst++ = *src++;
+ *dst++ = *src++;
+ while (*src == '.')
+ ++src;
+ }
+ /* Normal segments */
+ else
+ {
+ segment_start = dst;
+ /* Copy the whole segment */
+ while (*src && *src != '\\')
+ *dst++ = *src++;
+
+ /* Remove trailing dots, aka segment normalization */
+ if (!(flags & PATHCCH_DO_NOT_NORMALIZE_SEGMENTS))
+ {
+ old_dst = dst;
+ while (dst > segment_start && (dst[-1] == '.'))
+ --dst;
+ /* Not trailing dots, don't remove */
+ if (dst == segment_start)
+ dst = old_dst;
+ }
+
+ if (*src == '\\')
+ *dst++ = *src++;
+ }
}
/* End the path */
*dst = 0;
diff --git a/dlls/kernelbase/tests/path.c b/dlls/kernelbase/tests/path.c
index b5da303629..d7492a1e9f 100644
--- a/dlls/kernelbase/tests/path.c
+++ b/dlls/kernelbase/tests/path.c
@@ -72,6 +72,7 @@ static const struct alloccanonicalize_test alloccanonicalize_tests[] =
{"\\\\?C:a", "\\\\?C:a", 0, S_OK},
/* No . */
+ {"*", "*", 0, S_OK},
{"", "\\", 0, S_OK},
{"C:", "C:", 0, S_OK},
{"C:\\", "C:\\", 0, S_OK},
@@ -85,17 +86,26 @@ static const struct alloccanonicalize_test alloccanonicalize_tests[] =
{"*.", "*.", 0, S_OK},
{"*..", "*.", 0, S_OK},
{"*...", "*.", 0, S_OK},
+ {"*....", "*.", 0, S_OK},
+ {".a", ".a", 0, S_OK},
{"a.", "a", 0, S_OK},
+ {".a.", ".a", 0, S_OK},
{"a.b", "a.b", 0, S_OK},
+ {".a.b.", ".a.b", 0, S_OK},
{"a\\.", "a", 0, S_OK},
{"a\\.\\b", "a\\b", 0, S_OK},
+ {":.", ":", 0, S_OK},
{"C:.", "C:\\", 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:\\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},
{"C:\\a\\\\\\.", "C:\\a\\\\", 0, S_OK},
+ {".\\", "\\", 0, S_OK},
{"\\.", "\\", 0, S_OK},
{"\\\\.", "\\\\", 0, S_OK},
{"\\\\.\\", "\\\\", 0, S_OK},
@@ -115,21 +125,42 @@ static const struct alloccanonicalize_test alloccanonicalize_tests[] =
"\\\\?\\Volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}\\", 0, S_OK},
/* .. */
+ {"..a", "..a", 0, S_OK},
+ {"...a", "...a", 0, S_OK},
+ {"....a", "....a", 0, S_OK},
{"a..", "a", 0, S_OK},
+ {"a...", "a", 0, S_OK},
+ {"a....", "a", 0, S_OK},
+ {"..a..", "..a", 0, S_OK},
{"a..b", "a..b", 0, S_OK},
+ {"..a..b..", "..a..b", 0, S_OK},
{"a\\..", "\\", 0, S_OK},
{"a\\..\\", "\\", 0, S_OK},
{"a\\..\\b", "\\b", 0, S_OK},
+ {":..", ":", 0, S_OK},
{"C:..", "C:\\", 0, S_OK},
+ {"C:...", "C:\\", 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:\\..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},
{"C:\\\\..", "C:\\", 0, S_OK},
{"C:\\..\\", "C:\\", 0, S_OK},
+ {"C:\\...\\", "C:\\...\\", 0, S_OK},
{"C:\\a\\..", "C:\\", 0, S_OK},
+ {"C:\\a\\b..", "C:\\a\\b", 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},
@@ -249,6 +280,8 @@ static const struct alloccanonicalize_test alloccanonicalize_tests[] =
/* PATHCCH_DO_NOT_NORMALIZE_SEGMENTS */
/* No effect for spaces */
+ {"a ", "a ", 0, S_OK},
+ {"C:\\a ", "C:\\a ", 0, 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},
@@ -265,6 +298,7 @@ static const struct alloccanonicalize_test alloccanonicalize_tests[] =
{"..", "\\", 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:...", "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},
--
2.20.1
More information about the wine-devel
mailing list