From: Paul Gofman pgofman@codeweavers.com
--- dlls/kernel32/tests/file.c | 4 +-- dlls/ntdll/tests/directory.c | 64 +++++++++++++++++++++++++++++------- dlls/ntdll/unix/file.c | 40 +++++++++++++++++++++- 3 files changed, 94 insertions(+), 14 deletions(-)
diff --git a/dlls/kernel32/tests/file.c b/dlls/kernel32/tests/file.c index dc9f67e2f03..e6766bbd08d 100644 --- a/dlls/kernel32/tests/file.c +++ b/dlls/kernel32/tests/file.c @@ -3077,7 +3077,7 @@ static void test_FindFirstFile_wildcards(void) {1, "<..<", ", '..a', '.a..a', 'a..a'"}, {1, "<..", ", '.', '..', 'a', '.a', '..a', 'aa', 'aaa', 'aaaa', '.aaa'"}, {1, ".<.", ", '.', '..', '.a', '.aaa'"}, - {1, "..<", ", '..a'"}, + {0, "..<", ", '..a'"}, {1, "<<", ", '.', '..', '..a', '..a.a', '.a', '.a..a', '.a.a', '.aaa', 'a', 'a..a', 'a.a', 'a.a.a', 'aa', 'aaa', 'aaaa', ' .a'"}, {1, "<<.", ", '.', '..', '..a', '..a.a', '.a', '.a..a', '.a.a', '.aaa', 'a', 'a..a', 'a.a', 'a.a.a', 'aa', 'aaa', 'aaaa', ' .a'"}, {1, "<. ", ", '.', '..', '..a', '.a', '.aaa', 'a', 'aa', 'aaa', 'aaaa'"}, @@ -3087,7 +3087,7 @@ static void test_FindFirstFile_wildcards(void) {1, "<. .", ", '.', '..', 'a', '.a', '..a', 'aa', 'aaa', 'aaaa', '.aaa'"}, {1, "< ..", ", '.', '..', 'a', '.a', '..a', 'aa', 'aaa', 'aaaa', '.aaa'"}, {0, " <..", ""}, - {1, "..< ", ", '..a'"}, + {0, "..< ", ", '..a'"},
{1, "?", ", '.', '..', 'a'"}, {0, "?.", ", '.', '..', 'a'"}, diff --git a/dlls/ntdll/tests/directory.c b/dlls/ntdll/tests/directory.c index d4028a92519..c12c5547ade 100644 --- a/dlls/ntdll/tests/directory.c +++ b/dlls/ntdll/tests/directory.c @@ -183,7 +183,7 @@ static void tally_test_file(FILE_BOTH_DIRECTORY_INFORMATION *dir_info)
static void test_flags_NtQueryDirectoryFile(OBJECT_ATTRIBUTES *attr, const char *testdirA, UNICODE_STRING *mask, - BOOLEAN single_entry, BOOLEAN restart_flag) + BOOLEAN single_entry, BOOLEAN restart_flag, BOOLEAN expect_empty) { UNICODE_STRING dummy_mask; HANDLE dirh, new_dirh; @@ -213,6 +213,12 @@ static void test_flags_NtQueryDirectoryFile(OBJECT_ATTRIBUTES *attr, const char io.Status = 0xdeadbeef; status = pNtQueryDirectoryFile( dirh, NULL, NULL, NULL, &io, data, data_size, FileBothDirectoryInformation, single_entry, mask, restart_flag ); + if (expect_empty) + { + ok( status == STATUS_NO_SUCH_FILE, "got %#lx.\n", status ); + pNtClose( dirh ); + return; + } ok (status == STATUS_SUCCESS, "failed to query directory; status %lx\n", status); ok (io.Status == STATUS_SUCCESS, "failed to query directory; status %lx\n", io.Status); data_len = io.Information; @@ -247,7 +253,7 @@ static void test_flags_NtQueryDirectoryFile(OBJECT_ATTRIBUTES *attr, const char } ok(numfiles < max_test_dir_size, "too many loops\n");
- if (mask && !wcspbrk( mask->Buffer, L"*?" )) + if (mask && !wcspbrk( mask->Buffer, L"*?<">" )) for (i = 0; i < test_dir_count; i++) ok(testfiles[i].nfound == (testfiles[i].name == mask->Buffer), "Wrong number %d of %s files found (single_entry=%d,mask=%s)\n", @@ -476,10 +482,36 @@ static void test_NtQueryDirectoryFile(void) {L"??", {0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0}}, {L"??.", {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}}, {L"??.???", {0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, + {L"<", {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1}}, + {L"<.", {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1}}, + {L"<..", {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}}, + {L".<", {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0}}, + {L"..<", {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}}, {L"..*", {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}}, {L"*..*", {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1}}, {L"*..", {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}}, {L"..?", {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}}, + {L"a.<", {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1}}, + {L"ea.tmp.tmp<", {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}}, + {L"<tmp", {1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}}, + {L"<.tmp", {1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}}, + {L"<name.tmp", {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, + {L"<nam<tmp", {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, + {L"<name.<", {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, + {L"<name<", {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, + {L"<.<", {1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1}}, + {L"<<", {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}, + {L"<a", {0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0}}, + {L"*a", {0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0}}, + {L"<aa", {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0}}, + {L"<.a", {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0}}, + {L"<..a", {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}}, + {L"<.<.<", {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1}}, + {L".<.<", {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0}}, + {L"<<.<", {1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1}}, + {L"<.<<", {1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1}}, + {L"<<<", {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}, + {L"< ..", {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, };
OBJECT_ATTRIBUTES attr; @@ -495,6 +527,7 @@ static void test_NtQueryDirectoryFile(void) FILE_POSITION_INFORMATION pos_info; FILE_NAMES_INFORMATION *names; const WCHAR *filename = fbdi->FileName; + BOOLEAN expect_empty; NTSTATUS status; HANDLE dirh, h;
@@ -512,27 +545,36 @@ static void test_NtQueryDirectoryFile(void) } InitializeObjectAttributes(&attr, &ntdirname, OBJ_CASE_INSENSITIVE, 0, NULL);
- test_flags_NtQueryDirectoryFile(&attr, testdirA, NULL, FALSE, TRUE); - test_flags_NtQueryDirectoryFile(&attr, testdirA, NULL, FALSE, FALSE); - test_flags_NtQueryDirectoryFile(&attr, testdirA, NULL, TRUE, TRUE); - test_flags_NtQueryDirectoryFile(&attr, testdirA, NULL, TRUE, FALSE); + test_flags_NtQueryDirectoryFile(&attr, testdirA, NULL, FALSE, TRUE, FALSE); + test_flags_NtQueryDirectoryFile(&attr, testdirA, NULL, FALSE, FALSE, FALSE); + test_flags_NtQueryDirectoryFile(&attr, testdirA, NULL, TRUE, TRUE, FALSE); + test_flags_NtQueryDirectoryFile(&attr, testdirA, NULL, TRUE, FALSE, FALSE);
for (i = 0; i < test_dir_count; i++) { if (testfiles[i].name[0] == '.') continue; /* . and .. as masks are broken on Windows */ mask.Buffer = testfiles[i].name; mask.Length = mask.MaximumLength = lstrlenW(testfiles[i].name) * sizeof(WCHAR); - test_flags_NtQueryDirectoryFile(&attr, testdirA, &mask, FALSE, TRUE); - test_flags_NtQueryDirectoryFile(&attr, testdirA, &mask, FALSE, FALSE); - test_flags_NtQueryDirectoryFile(&attr, testdirA, &mask, TRUE, TRUE); - test_flags_NtQueryDirectoryFile(&attr, testdirA, &mask, TRUE, FALSE); + test_flags_NtQueryDirectoryFile(&attr, testdirA, &mask, FALSE, TRUE, FALSE); + test_flags_NtQueryDirectoryFile(&attr, testdirA, &mask, FALSE, FALSE, FALSE); + test_flags_NtQueryDirectoryFile(&attr, testdirA, &mask, TRUE, TRUE, FALSE); + test_flags_NtQueryDirectoryFile(&attr, testdirA, &mask, TRUE, FALSE, FALSE); }
for (i = 0; i < ARRAY_SIZE(mask_tests); ++i) { winetest_push_context("mask %s", debugstr_w(mask_tests[i].mask)); RtlInitUnicodeString(&mask, mask_tests[i].mask); - test_flags_NtQueryDirectoryFile(&attr, testdirA, &mask, FALSE, TRUE); + expect_empty = TRUE; + for (j = 0; j < ARRAY_SIZE(mask_tests[i].found); ++j) + { + if (mask_tests[i].found[j]) + { + expect_empty = FALSE; + break; + } + } + test_flags_NtQueryDirectoryFile(&attr, testdirA, &mask, FALSE, TRUE, expect_empty); for (j = 0; j < test_dir_count; j++) ok(testfiles[j].nfound == mask_tests[i].found[j], "%S, got %d.\n", testfiles[j].name, testfiles[j].nfound); winetest_pop_context(); diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c index ab567a63d19..8694564b576 100644 --- a/dlls/ntdll/unix/file.c +++ b/dlls/ntdll/unix/file.c @@ -1428,6 +1428,8 @@ static ULONG hash_short_file_name( const WCHAR *name, int length, LPWSTR buffer */ static BOOLEAN match_filename_part( const WCHAR *name, const WCHAR *name_end, const WCHAR *mask, const WCHAR *mask_end ) { + WCHAR c; + while (name < name_end && mask < mask_end) { switch(*mask) @@ -1447,6 +1449,42 @@ static BOOLEAN match_filename_part( const WCHAR *name, const WCHAR *name_end, co ++name; } break; + case '<': + { + const WCHAR *next_dot; + BOOL had_dot = FALSE; + + ++mask; + while (name < name_end) + { + next_dot = name; + while (next_dot < name_end && *next_dot != '.') ++next_dot; + if (next_dot == name_end && had_dot) break; + if (next_dot < name_end) + { + had_dot = TRUE; + ++next_dot; + } + if (mask < mask_end) + { + while (name < next_dot) + { + c = *mask; + if (!is_wildcard(c)) + { + if (is_case_sensitive) + while (name < next_dot && (*name != c)) name++; + else + while (name < next_dot && (towupper(*name) != towupper(c))) name++; + } + if (match_filename_part( name, name_end, mask, mask_end )) return TRUE; + ++name; + } + } + name = next_dot; + } + break; + } case '?': case '>': mask++; @@ -1460,7 +1498,7 @@ static BOOLEAN match_filename_part( const WCHAR *name, const WCHAR *name_end, co break; } } - while (mask < mask_end && *mask == '*') + while (mask < mask_end && (*mask == '*' || *mask == '<')) mask++; return (name == name_end && mask == mask_end); }