[PATCH 1/4] ntdll: Also accept \\? as a UNC or device path in RtlDetermineDosPathNameType_U().
Signed-off-by: Zebediah Figura <z.figura12(a)gmail.com> --- dlls/ntdll/path.c | 8 ++++---- dlls/ntdll/tests/path.c | 7 +++++++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/dlls/ntdll/path.c b/dlls/ntdll/path.c index ccc95e3..8fe61da 100644 --- a/dlls/ntdll/path.c +++ b/dlls/ntdll/path.c @@ -244,10 +244,10 @@ DOS_PATHNAME_TYPE WINAPI RtlDetermineDosPathNameType_U( PCWSTR path ) if (IS_SEPARATOR(path[0])) { if (!IS_SEPARATOR(path[1])) return ABSOLUTE_PATH; /* "/foo" */ - if (path[2] != '.') return UNC_PATH; /* "//foo" */ - if (IS_SEPARATOR(path[3])) return DEVICE_PATH; /* "//./foo" */ - if (path[3]) return UNC_PATH; /* "//.foo" */ - return UNC_DOT_PATH; /* "//." */ + if (path[2] != '.' && path[2] != '?') return UNC_PATH; /* "//foo" */ + if (IS_SEPARATOR(path[3])) return DEVICE_PATH; /* "//./foo" or "//?/foo" */ + if (path[3]) return UNC_PATH; /* "//.foo" or "//?foo" */ + return UNC_DOT_PATH; /* "//." or "//?" */ } else { diff --git a/dlls/ntdll/tests/path.c b/dlls/ntdll/tests/path.c index 9369e1c..94b76c1 100644 --- a/dlls/ntdll/tests/path.c +++ b/dlls/ntdll/tests/path.c @@ -68,6 +68,13 @@ static void test_RtlDetermineDosPathNameType_U(void) { "//.foo", 1 }, { "\\\\.", 7 }, { "//.", 7 }, + { "\\\\?\\foo", 6 }, + { "//?/foo", 6 }, + { "/\\?/foo", 6 }, + { "\\\\?foo", 1 }, + { "//?foo", 1 }, + { "\\\\?", 7 }, + { "//?", 7 }, { NULL, 0 } }; -- 2.7.4
Signed-off-by: Zebediah Figura <z.figura12(a)gmail.com> --- dlls/ntdll/tests/path.c | 56 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 49 insertions(+), 7 deletions(-) diff --git a/dlls/ntdll/tests/path.c b/dlls/ntdll/tests/path.c index 94b76c1..b2a6955 100644 --- a/dlls/ntdll/tests/path.c +++ b/dlls/ntdll/tests/path.c @@ -266,6 +266,7 @@ static void test_RtlGetFullPathName_U(void) static const struct test tests[] = { { "c:/test", "c:\\test", "test"}, + { "c:/test/", "c:\\test\\", NULL}, { "c:/test ", "c:\\test", "test"}, { "c:/test.", "c:\\test", "test"}, { "c:/test .... .. ", "c:\\test", "test"}, @@ -287,22 +288,61 @@ static void test_RtlGetFullPathName_U(void) { "c:/test../file", "c:\\test.\\file", "file", "c:\\test..\\file", "file"}, /* vista */ { "c:\\test", "c:\\test", "test"}, - { NULL, NULL, NULL} + { "C:\\test", "C:\\test", "test"}, + { "c:/", "c:\\", NULL}, + { "c:.", "C:\\windows", "windows"}, + { "c:foo", "C:\\windows\\foo", "foo"}, + { "c:foo/bar", "C:\\windows\\foo\\bar", "bar"}, + { "c:./foo", "C:\\windows\\foo", "foo"}, + { "\\foo", "C:\\foo", "foo"}, + { "foo", "C:\\windows\\foo", "foo"}, + { ".", "C:\\windows", "windows"}, + { "..", "C:\\", NULL}, + { "...", "C:\\windows\\", NULL}, + { "./foo", "C:\\windows\\foo", "foo"}, + { "foo/..", "C:\\windows", "windows"}, + { "AUX", "\\\\.\\AUX", NULL}, + { "COM1", "\\\\.\\COM1", NULL}, + { "?<>*\"|:", "C:\\windows\\?<>*\"|:", "?<>*\"|:"}, + + { "\\\\foo", "\\\\foo", NULL}, + { "//foo", "\\\\foo", NULL}, + { "\\/foo", "\\\\foo", NULL}, + { "//", "\\\\", NULL}, + { "//foo/", "\\\\foo\\", NULL}, + + { "//.", "\\\\.\\", NULL}, + { "//./", "\\\\.\\", NULL}, + { "//.//", "\\\\.\\", NULL}, + { "//./foo", "\\\\.\\foo", "foo"}, + { "//./foo/", "\\\\.\\foo\\", NULL}, + { "//./foo/bar", "\\\\.\\foo\\bar", "bar"}, + { "//./foo/.", "\\\\.\\foo", "foo"}, + { "//./foo/..", "\\\\.\\", NULL}, + + { "//?/", "\\\\?\\", NULL}, + { "//?//", "\\\\?\\", NULL}, + { "//?/foo", "\\\\?\\foo", "foo"}, + { "//?/foo/", "\\\\?\\foo\\", NULL}, + { "//?/foo/bar", "\\\\?\\foo\\bar", "bar"}, + { "//?/foo/.", "\\\\?\\foo", "foo"}, + { "//?/foo/..", "\\\\?\\", NULL}, + + /* RtlGetFullPathName_U() can't understand the global namespace prefix */ + { "\\??\\foo", "C:\\??\\foo", "foo"}, + { 0 } }; const struct test *test; WCHAR pathbufW[2*MAX_PATH], rbufferW[MAX_PATH]; - CHAR rbufferA[MAX_PATH], rfileA[MAX_PATH]; + char rbufferA[MAX_PATH], rfileA[MAX_PATH], curdir[MAX_PATH]; ULONG ret; WCHAR *file_part; DWORD reslen; UINT len; - if (!pRtlGetFullPathName_U) - { - win_skip("RtlGetFullPathName_U is not available\n"); - return; - } + GetCurrentDirectoryA(sizeof(curdir), curdir); + SetCurrentDirectoryA("C:\\windows\\"); file_part = (WCHAR *)0xdeadbeef; lstrcpyW(rbufferW, deadbeefW); @@ -348,6 +388,8 @@ static void test_RtlGetFullPathName_U(void) ok( !test->rfile, "Got NULL expected \"%s\"\n", test->rfile ); } } + + SetCurrentDirectoryA(curdir); } static void test_RtlDosPathNameToNtPathName_U_WithStatus(void) -- 2.7.4
Signed-off-by: Zebediah Figura <z.figura12(a)gmail.com> --- dlls/ntdll/tests/path.c | 147 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 127 insertions(+), 20 deletions(-) diff --git a/dlls/ntdll/tests/path.c b/dlls/ntdll/tests/path.c index b2a6955..a032092 100644 --- a/dlls/ntdll/tests/path.c +++ b/dlls/ntdll/tests/path.c @@ -19,6 +19,7 @@ */ #include "ntdll_test.h" +#include "winnls.h" static NTSTATUS (WINAPI *pRtlMultiByteToUnicodeN)( LPWSTR dst, DWORD dstlen, LPDWORD reslen, LPCSTR src, DWORD srclen ); @@ -28,6 +29,7 @@ static ULONG (WINAPI *pRtlIsDosDeviceName_U)( PCWSTR dos_name ); static NTSTATUS (WINAPI *pRtlOemStringToUnicodeString)(UNICODE_STRING *, const STRING *, BOOLEAN ); static BOOLEAN (WINAPI *pRtlIsNameLegalDOS8Dot3)(const UNICODE_STRING*,POEM_STRING,PBOOLEAN); static DWORD (WINAPI *pRtlGetFullPathName_U)(const WCHAR*,ULONG,WCHAR*,WCHAR**); +static BOOLEAN (WINAPI *pRtlDosPathNameToNtPathName_U)(const WCHAR*, UNICODE_STRING*, WCHAR**, CURDIR*); static NTSTATUS (WINAPI *pRtlDosPathNameToNtPathName_U_WithStatus)(const WCHAR*, UNICODE_STRING*, WCHAR**, CURDIR*); static void test_RtlDetermineDosPathNameType_U(void) @@ -392,33 +394,137 @@ static void test_RtlGetFullPathName_U(void) SetCurrentDirectoryA(curdir); } -static void test_RtlDosPathNameToNtPathName_U_WithStatus(void) +static void test_RtlDosPathNameToNtPathName_U(void) { - static const WCHAR emptyW[] = { 0 }; + char curdir[MAX_PATH]; WCHAR path[MAX_PATH]; UNICODE_STRING nameW; + WCHAR *file_part; NTSTATUS status; + BOOL ret, expect; + int i; - if (!pRtlDosPathNameToNtPathName_U_WithStatus) + static const struct { - win_skip("RtlDosPathNameToNtPathName_U_WithStatus() is not supported.\n"); - return; + const char *dos; + const char *nt; + int file_offset; /* offset to file part */ + NTSTATUS status; + NTSTATUS alt_status; + int broken; + } + tests[] = + { + { "c:\\", "\\??\\c:\\", -1, STATUS_SUCCESS }, + { "c:/", "\\??\\c:\\", -1, STATUS_SUCCESS }, + { "c:/foo", "\\??\\c:\\foo", 7, STATUS_SUCCESS }, + { "c:/foo.", "\\??\\c:\\foo", 7, STATUS_SUCCESS }, + { "c:/foo/", "\\??\\c:\\foo\\", -1, STATUS_SUCCESS }, + { "c:/foo//", "\\??\\c:\\foo\\", -1, STATUS_SUCCESS }, + { "C:/foo", "\\??\\C:\\foo", 7, STATUS_SUCCESS }, + { "C:/foo/bar", "\\??\\C:\\foo\\bar", 11, STATUS_SUCCESS }, + { "C:/foo/bar", "\\??\\C:\\foo\\bar", 11, STATUS_SUCCESS }, + { "c:.", "\\??\\C:\\windows", 7, STATUS_SUCCESS }, + { "c:foo", "\\??\\C:\\windows\\foo", 15, STATUS_SUCCESS }, + { "c:foo/bar", "\\??\\C:\\windows\\foo\\bar", 19, STATUS_SUCCESS }, + { "c:./foo", "\\??\\C:\\windows\\foo", 15, STATUS_SUCCESS }, + { "c:/./foo", "\\??\\c:\\foo", 7, STATUS_SUCCESS }, + { "c:/foo/.", "\\??\\c:\\foo", 7, STATUS_SUCCESS }, + { "c:/foo/./bar", "\\??\\c:\\foo\\bar", 11, STATUS_SUCCESS }, + { "c:/foo/../bar", "\\??\\c:\\bar", 7, STATUS_SUCCESS }, + { "\\foo", "\\??\\C:\\foo", 7, STATUS_SUCCESS }, + { "foo", "\\??\\C:\\windows\\foo", 15, STATUS_SUCCESS }, + { ".", "\\??\\C:\\windows", 7, STATUS_SUCCESS }, + { "./", "\\??\\C:\\windows\\", -1, STATUS_SUCCESS }, + { "..", "\\??\\C:\\", -1, STATUS_SUCCESS }, + { "...", "\\??\\C:\\windows\\", -1, STATUS_SUCCESS }, + { "./foo", "\\??\\C:\\windows\\foo", 15, STATUS_SUCCESS }, + { "foo/..", "\\??\\C:\\windows", 7, STATUS_SUCCESS }, + { "AUX" , "\\??\\AUX", -1, STATUS_SUCCESS }, + { "COM1" , "\\??\\COM1", -1, STATUS_SUCCESS }, + { "?<>*\"|:", "\\??\\C:\\windows\\?<>*\"|:", 15, STATUS_SUCCESS }, + + { "", NULL, -1, STATUS_OBJECT_NAME_INVALID, STATUS_OBJECT_PATH_NOT_FOUND }, + { NULL, NULL, -1, STATUS_OBJECT_NAME_INVALID, STATUS_OBJECT_PATH_NOT_FOUND }, + { " ", NULL, -1, STATUS_OBJECT_NAME_INVALID, STATUS_OBJECT_PATH_NOT_FOUND }, + + { "\\\\foo", "\\??\\UNC\\foo", -1, STATUS_SUCCESS }, + { "//foo", "\\??\\UNC\\foo", -1, STATUS_SUCCESS }, + { "\\/foo", "\\??\\UNC\\foo", -1, STATUS_SUCCESS }, + { "//", "\\??\\UNC\\", -1, STATUS_SUCCESS }, + { "//foo/", "\\??\\UNC\\foo\\", -1, STATUS_SUCCESS }, + + { "//.", "\\??\\", -1, STATUS_SUCCESS }, + { "//./", "\\??\\", -1, STATUS_SUCCESS }, + { "//.//", "\\??\\", -1, STATUS_SUCCESS }, + { "//./foo", "\\??\\foo", 4, STATUS_SUCCESS }, + { "//./foo/", "\\??\\foo\\", -1, STATUS_SUCCESS }, + { "//./foo/bar", "\\??\\foo\\bar", 8, STATUS_SUCCESS }, + { "//./foo/.", "\\??\\foo", 4, STATUS_SUCCESS }, + { "//./foo/..", "\\??\\", -1, STATUS_SUCCESS }, + + { "//?", "\\??\\", -1, STATUS_SUCCESS }, + { "//?/", "\\??\\", -1, STATUS_SUCCESS }, + { "//?//", "\\??\\", -1, STATUS_SUCCESS }, + { "//?/foo", "\\??\\foo", 4, STATUS_SUCCESS }, + { "//?/foo/", "\\??\\foo\\", -1, STATUS_SUCCESS }, + { "//?/foo/bar", "\\??\\foo\\bar", 8, STATUS_SUCCESS }, + { "//?/foo/.", "\\??\\foo", 4, STATUS_SUCCESS }, + { "//?/foo/..", "\\??\\", -1, STATUS_SUCCESS }, + + { "\\\\?", "\\??\\", -1, STATUS_SUCCESS }, + { "\\\\?\\", "\\??\\", -1, STATUS_SUCCESS }, + + { "\\\\?\\/", "\\??\\/", 4, STATUS_SUCCESS }, + { "\\\\?\\foo", "\\??\\foo", 4, STATUS_SUCCESS }, + { "\\\\?\\foo/", "\\??\\foo/", 4, STATUS_SUCCESS }, + { "\\\\?\\foo/bar", "\\??\\foo/bar", 4, STATUS_SUCCESS }, + { "\\\\?\\foo/.", "\\??\\foo/.", 4, STATUS_SUCCESS }, + { "\\\\?\\foo/..", "\\??\\foo/..", 4, STATUS_SUCCESS }, + { "\\\\?\\\\", "\\??\\\\", -1, STATUS_SUCCESS }, + { "\\\\?\\\\\\", "\\??\\\\\\", -1, STATUS_SUCCESS }, + { "\\\\?\\foo\\", "\\??\\foo\\", -1, STATUS_SUCCESS }, + { "\\\\?\\foo\\bar", "\\??\\foo\\bar", 8, STATUS_SUCCESS }, + { "\\\\?\\foo\\.", "\\??\\foo\\.", 8, STATUS_SUCCESS }, + { "\\\\?\\foo\\..", "\\??\\foo\\..", 8, STATUS_SUCCESS }, + }; + + GetCurrentDirectoryA(sizeof(curdir), curdir); + SetCurrentDirectoryA("C:\\windows\\"); + + for (i = 0; i < ARRAY_SIZE(tests); ++i) + { + MultiByteToWideChar(CP_ACP, 0, tests[i].dos, -1, path, sizeof(path)); + ret = pRtlDosPathNameToNtPathName_U(path, &nameW, &file_part, NULL); + + if (pRtlDosPathNameToNtPathName_U_WithStatus) + { + status = pRtlDosPathNameToNtPathName_U_WithStatus(path, &nameW, &file_part, NULL); + ok(status == tests[i].status || status == tests[i].alt_status, + "%s: Expected status %#x, got %#x.\n", tests[i].dos, tests[i].status, status); + } + + expect = (tests[i].status == STATUS_SUCCESS); + ok(ret == expect, "%s: Expected %#x, got %#x.\n", tests[i].dos, expect, ret); + + if (ret != TRUE) continue; + + MultiByteToWideChar(CP_ACP, 0, tests[i].nt, -1, path, sizeof(path)); + ok(!lstrcmpW(nameW.Buffer, path), "%s: Expected %s, got %s.\n", + tests[i].dos, tests[i].nt, wine_dbgstr_w(nameW.Buffer)); + + if (tests[i].file_offset > 0) + ok(file_part == nameW.Buffer + tests[i].file_offset, + "%s: Expected file part %s, got %s.\n", tests[i].dos, + wine_dbgstr_w(nameW.Buffer + tests[i].file_offset), wine_dbgstr_w(file_part)); + else + ok(file_part == NULL, "%s: Expected NULL file part, got %s.\n", + tests[i].dos, wine_dbgstr_w(file_part)); + + RtlFreeUnicodeString(&nameW); } - GetCurrentDirectoryW( MAX_PATH, path ); - - status = pRtlDosPathNameToNtPathName_U_WithStatus( path, &nameW, NULL, NULL ); - ok(!status, "Failed convert to nt path, %#x.\n", status); - - status = pRtlDosPathNameToNtPathName_U_WithStatus( NULL, &nameW, NULL, NULL ); - ok(status == STATUS_OBJECT_NAME_INVALID || broken(status == STATUS_OBJECT_PATH_NOT_FOUND) /* W2k3 */, - "Unexpected status %#x.\n", status); - - status = pRtlDosPathNameToNtPathName_U_WithStatus( emptyW, &nameW, NULL, NULL ); - ok(status == STATUS_OBJECT_NAME_INVALID || broken(status == STATUS_OBJECT_PATH_NOT_FOUND) /* W2k3 */, - "Unexpected status %#x.\n", status); - - RtlFreeUnicodeString( &nameW ); + SetCurrentDirectoryA(curdir); } START_TEST(path) @@ -437,11 +543,12 @@ START_TEST(path) pRtlOemStringToUnicodeString = (void *)GetProcAddress(mod,"RtlOemStringToUnicodeString"); pRtlIsNameLegalDOS8Dot3 = (void *)GetProcAddress(mod,"RtlIsNameLegalDOS8Dot3"); pRtlGetFullPathName_U = (void *)GetProcAddress(mod,"RtlGetFullPathName_U"); + pRtlDosPathNameToNtPathName_U = (void *)GetProcAddress(mod, "RtlDosPathNameToNtPathName_U"); pRtlDosPathNameToNtPathName_U_WithStatus = (void *)GetProcAddress(mod, "RtlDosPathNameToNtPathName_U_WithStatus"); test_RtlDetermineDosPathNameType_U(); test_RtlIsDosDeviceName_U(); test_RtlIsNameLegalDOS8Dot3(); test_RtlGetFullPathName_U(); - test_RtlDosPathNameToNtPathName_U_WithStatus(); + test_RtlDosPathNameToNtPathName_U(); } -- 2.7.4
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45654 Signed-off-by: Zebediah Figura <z.figura12(a)gmail.com> --- dlls/ntdll/path.c | 6 ++++-- dlls/ntdll/tests/path.c | 25 +++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/dlls/ntdll/path.c b/dlls/ntdll/path.c index 8fe61da..9794b67 100644 --- a/dlls/ntdll/path.c +++ b/dlls/ntdll/path.c @@ -340,7 +340,8 @@ ULONG WINAPI RtlIsDosDeviceName_U( PCWSTR dos_name ) NTSTATUS WINAPI RtlDosPathNameToNtPathName_U_WithStatus(const WCHAR *dos_path, UNICODE_STRING *ntpath, WCHAR **file_part, CURDIR *cd) { - static const WCHAR LongFileNamePfxW[] = {'\\','\\','?','\\'}; + static const WCHAR global_prefix[] = {'\\','\\','?','\\'}; + static const WCHAR global_prefix2[] = {'\\','?','?','\\'}; ULONG sz, offset; WCHAR local[MAX_PATH]; LPWSTR ptr; @@ -356,7 +357,8 @@ NTSTATUS WINAPI RtlDosPathNameToNtPathName_U_WithStatus(const WCHAR *dos_path, U if (!dos_path || !*dos_path) return STATUS_OBJECT_NAME_INVALID; - if (!strncmpW(dos_path, LongFileNamePfxW, 4)) + if (!memcmp(dos_path, global_prefix, sizeof(global_prefix)) || + (!memcmp(dos_path, global_prefix2, sizeof(global_prefix2)) && dos_path[4])) { ntpath->Length = strlenW(dos_path) * sizeof(WCHAR); ntpath->MaximumLength = ntpath->Length + sizeof(WCHAR); diff --git a/dlls/ntdll/tests/path.c b/dlls/ntdll/tests/path.c index a032092..7baf474 100644 --- a/dlls/ntdll/tests/path.c +++ b/dlls/ntdll/tests/path.c @@ -396,6 +396,8 @@ static void test_RtlGetFullPathName_U(void) static void test_RtlDosPathNameToNtPathName_U(void) { + static const WCHAR broken_global_prefix[] = {'\\','?','?','\\','C',':','\\','?','?'}; + char curdir[MAX_PATH]; WCHAR path[MAX_PATH]; UNICODE_STRING nameW; @@ -487,6 +489,22 @@ static void test_RtlDosPathNameToNtPathName_U(void) { "\\\\?\\foo\\bar", "\\??\\foo\\bar", 8, STATUS_SUCCESS }, { "\\\\?\\foo\\.", "\\??\\foo\\.", 8, STATUS_SUCCESS }, { "\\\\?\\foo\\..", "\\??\\foo\\..", 8, STATUS_SUCCESS }, + + { "\\??", "\\??\\C:\\??", 7, STATUS_SUCCESS }, + { "\\??\\", "\\??\\C:\\??\\", -1, STATUS_SUCCESS }, + + { "\\??\\/", "\\??\\/", 4, STATUS_SUCCESS }, + { "\\??\\foo", "\\??\\foo", 4, STATUS_SUCCESS }, + { "\\??\\foo/", "\\??\\foo/", 4, STATUS_SUCCESS }, + { "\\??\\foo/bar", "\\??\\foo/bar", 4, STATUS_SUCCESS }, + { "\\??\\foo/.", "\\??\\foo/.", 4, STATUS_SUCCESS }, + { "\\??\\foo/..", "\\??\\foo/..", 4, STATUS_SUCCESS }, + { "\\??\\\\", "\\??\\\\", -1, STATUS_SUCCESS }, + { "\\??\\\\\\", "\\??\\\\\\", -1, STATUS_SUCCESS }, + { "\\??\\foo\\", "\\??\\foo\\", -1, STATUS_SUCCESS }, + { "\\??\\foo\\bar", "\\??\\foo\\bar", 8, STATUS_SUCCESS }, + { "\\??\\foo\\.", "\\??\\foo\\.", 8, STATUS_SUCCESS }, + { "\\??\\foo\\..", "\\??\\foo\\..", 8, STATUS_SUCCESS }, }; GetCurrentDirectoryA(sizeof(curdir), curdir); @@ -509,6 +527,13 @@ static void test_RtlDosPathNameToNtPathName_U(void) if (ret != TRUE) continue; + if (!strncmp(tests[i].dos, "\\??\\", 4) && tests[i].dos[4] && + broken(!memcmp(nameW.Buffer, broken_global_prefix, sizeof(broken_global_prefix)))) + { + /* Windows version prior to 2003 don't interpret the \??\ prefix */ + continue; + } + MultiByteToWideChar(CP_ACP, 0, tests[i].nt, -1, path, sizeof(path)); ok(!lstrcmpW(nameW.Buffer, path), "%s: Expected %s, got %s.\n", tests[i].dos, tests[i].nt, wine_dbgstr_w(nameW.Buffer)); -- 2.7.4
participants (1)
-
Zebediah Figura