From: Marcus Meissner Subject: [PATCH] kernel32: implemented "Basic" mode of findfirstfilex (2nd try) Message-Id: <1395585206-31596-1-git-send-email-marcus@jet.franken.de> Date: Sun, 23 Mar 2014 15:33:26 +0100 FindExInfoBasic was not handled, rewrite it a bit to handle it. Added testcase, handle case where Windows OS does not support it yet. --- dlls/kernel32/file.c | 112 +++++++++++++++++++++++++++++---------------- dlls/kernel32/tests/file.c | 47 +++++++++++++++++++ 2 files changed, 120 insertions(+), 39 deletions(-) diff --git a/dlls/kernel32/file.c b/dlls/kernel32/file.c index e205050..e986110 100644 --- a/dlls/kernel32/file.c +++ b/dlls/kernel32/file.c @@ -57,6 +57,7 @@ typedef struct HANDLE handle; /* handle to directory */ CRITICAL_SECTION cs; /* crit section protecting this structure */ FINDEX_SEARCH_OPS search_op; /* Flags passed to FindFirst. */ + FILE_INFORMATION_CLASS infoclass; /* information requested */ UNICODE_STRING mask; /* file mask */ UNICODE_STRING path; /* NT path used to open the directory */ BOOL is_root; /* is directory the root of the drive? */ @@ -118,7 +119,7 @@ static HANDLE create_file_OF( LPCSTR path, INT mode ) * * Check if a dir symlink should be returned by FindNextFile. */ -static BOOL check_dir_symlink( FIND_FIRST_INFO *info, const FILE_BOTH_DIR_INFORMATION *file_info ) +static BOOL check_dir_symlink( FIND_FIRST_INFO *info, ULONG FileNameLength, WCHAR *FileName ) { UNICODE_STRING str; ANSI_STRING unix_name; @@ -126,13 +127,13 @@ static BOOL check_dir_symlink( FIND_FIRST_INFO *info, const FILE_BOTH_DIR_INFORM BOOL ret = TRUE; DWORD len; - str.MaximumLength = info->path.Length + sizeof(WCHAR) + file_info->FileNameLength; + str.MaximumLength = info->path.Length + sizeof(WCHAR) + FileNameLength; if (!(str.Buffer = HeapAlloc( GetProcessHeap(), 0, str.MaximumLength ))) return TRUE; memcpy( str.Buffer, info->path.Buffer, info->path.Length ); len = info->path.Length / sizeof(WCHAR); if (!len || str.Buffer[len-1] != '\\') str.Buffer[len++] = '\\'; - memcpy( str.Buffer + len, file_info->FileName, file_info->FileNameLength ); - str.Length = len * sizeof(WCHAR) + file_info->FileNameLength; + memcpy( str.Buffer + len, FileName, FileNameLength ); + str.Length = len * sizeof(WCHAR) + FileNameLength; unix_name.Buffer = NULL; if (!wine_nt_to_unix_file_name( &str, &unix_name, OPEN_EXISTING, FALSE ) && @@ -1858,6 +1859,7 @@ HANDLE WINAPI FindFirstFileExW( LPCWSTR filename, FINDEX_INFO_LEVELS level, IO_STATUS_BLOCK io; NTSTATUS status; DWORD device = 0; + FILE_INFORMATION_CLASS infoclass; TRACE("%s %d %p %d %p %x\n", debugstr_w(filename), level, data, search_op, filter, flags); @@ -1867,8 +1869,14 @@ HANDLE WINAPI FindFirstFileExW( LPCWSTR filename, FINDEX_INFO_LEVELS level, FIXME("options not implemented 0x%08x 0x%08x\n", search_op, flags ); return INVALID_HANDLE_VALUE; } - if (level != FindExInfoStandard) - { + switch (level) { + case FindExInfoStandard: + infoclass = FileBothDirectoryInformation; + break; + case FindExInfoBasic: + infoclass = FileFullDirectoryInformation; + break; + default: FIXME("info level %d not implemented\n", level ); return INVALID_HANDLE_VALUE; } @@ -1970,6 +1978,7 @@ HANDLE WINAPI FindFirstFileExW( LPCWSTR filename, FINDEX_INFO_LEVELS level, info->data_size = 0; info->data = NULL; info->search_op = search_op; + info->infoclass = infoclass; if (device) { @@ -1998,7 +2007,7 @@ HANDLE WINAPI FindFirstFileExW( LPCWSTR filename, FINDEX_INFO_LEVELS level, } NtQueryDirectoryFile( info->handle, 0, NULL, NULL, &io, info->data, info->data_size, - FileBothDirectoryInformation, FALSE, &info->mask, TRUE ); + infoclass, FALSE, &info->mask, TRUE ); if (io.u.Status) { FindClose( info ); @@ -2052,8 +2061,10 @@ error: BOOL WINAPI FindNextFileW( HANDLE handle, WIN32_FIND_DATAW *data ) { FIND_FIRST_INFO *info; - FILE_BOTH_DIR_INFORMATION *dir_info; + FILE_BOTH_DIR_INFORMATION *both_info; BOOL ret = FALSE; + WCHAR *filename; + ULONG filenamelength; TRACE("%p %p\n", handle, data); @@ -2080,7 +2091,7 @@ BOOL WINAPI FindNextFileW( HANDLE handle, WIN32_FIND_DATAW *data ) if (info->data_size) NtQueryDirectoryFile( info->handle, 0, NULL, NULL, &io, info->data, info->data_size, - FileBothDirectoryInformation, FALSE, &info->mask, FALSE ); + info->infoclass, FALSE, &info->mask, FALSE ); else io.u.Status = STATUS_NO_MORE_FILES; @@ -2100,41 +2111,64 @@ BOOL WINAPI FindNextFileW( HANDLE handle, WIN32_FIND_DATAW *data ) info->data_pos = 0; } - dir_info = (FILE_BOTH_DIR_INFORMATION *)(info->data + info->data_pos); + /* layout between full and both at the beginning is the same */ + both_info = (FILE_BOTH_DIR_INFORMATION *)(info->data + info->data_pos); + + /* The only differences are for FileName and ShortName */ + if (info->infoclass == FileBothDirectoryInformation) { + filename = both_info->FileName; + filenamelength = both_info->FileNameLength; + FIXME("both fn length %d, fn %s\n", filenamelength, debugstr_w(filename)); + } else if (info->infoclass == FileFullDirectoryInformation) { + FILE_FULL_DIR_INFORMATION *full_info = (FILE_FULL_DIR_INFORMATION *)(info->data + info->data_pos); + + filename = full_info->FileName; + filenamelength = full_info->FileNameLength; + FIXME("full fn length %d, fn %s\n", filenamelength, debugstr_w(filename)); + } else { + FIXME("infoclass %d, should not arrive here.\n", info->infoclass); + SetLastError( ERROR_INVALID_HANDLE ); + return FALSE; + } - if (dir_info->NextEntryOffset) info->data_pos += dir_info->NextEntryOffset; + if (both_info->NextEntryOffset) info->data_pos += both_info->NextEntryOffset; else info->data_pos = info->data_len; - /* don't return '.' and '..' in the root of the drive */ - if (info->is_root) - { - if (dir_info->FileNameLength == sizeof(WCHAR) && dir_info->FileName[0] == '.') continue; - if (dir_info->FileNameLength == 2 * sizeof(WCHAR) && - dir_info->FileName[0] == '.' && dir_info->FileName[1] == '.') continue; + /* don't return '.' and '..' in the root of the drive */ + if (info->is_root) + { + if (filenamelength == sizeof(WCHAR) && filename[0] == '.') continue; + if (filenamelength == 2 * sizeof(WCHAR) && + filename[0] == '.' && filename[1] == '.') continue; + } + + /* check for dir symlink */ + if ((both_info->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) && + (both_info->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) && + strpbrkW( info->mask.Buffer, wildcardsW )) + { + if (!check_dir_symlink( info, filenamelength, filename )) continue; + } + + data->dwFileAttributes = both_info->FileAttributes; + data->ftCreationTime = *(FILETIME *)&both_info->CreationTime; + data->ftLastAccessTime = *(FILETIME *)&both_info->LastAccessTime; + data->ftLastWriteTime = *(FILETIME *)&both_info->LastWriteTime; + data->nFileSizeHigh = both_info->EndOfFile.QuadPart >> 32; + data->nFileSizeLow = (DWORD)both_info->EndOfFile.QuadPart; + data->dwReserved0 = 0; + data->dwReserved1 = 0; + + memcpy( data->cFileName, filename, filenamelength ); + data->cFileName[filenamelength/sizeof(WCHAR)] = 0; + + if (info->infoclass == FileBothDirectoryInformation) { + memcpy( data->cAlternateFileName, both_info->ShortName, both_info->ShortNameLength ); + data->cAlternateFileName[both_info->ShortNameLength/sizeof(WCHAR)] = 0; + } else { + data->cAlternateFileName[0] = 0; } - /* check for dir symlink */ - if ((dir_info->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) && - (dir_info->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) && - strpbrkW( info->mask.Buffer, wildcardsW )) - { - if (!check_dir_symlink( info, dir_info )) continue; - } - - data->dwFileAttributes = dir_info->FileAttributes; - data->ftCreationTime = *(FILETIME *)&dir_info->CreationTime; - data->ftLastAccessTime = *(FILETIME *)&dir_info->LastAccessTime; - data->ftLastWriteTime = *(FILETIME *)&dir_info->LastWriteTime; - data->nFileSizeHigh = dir_info->EndOfFile.QuadPart >> 32; - data->nFileSizeLow = (DWORD)dir_info->EndOfFile.QuadPart; - data->dwReserved0 = 0; - data->dwReserved1 = 0; - - memcpy( data->cFileName, dir_info->FileName, dir_info->FileNameLength ); - data->cFileName[dir_info->FileNameLength/sizeof(WCHAR)] = 0; - memcpy( data->cAlternateFileName, dir_info->ShortName, dir_info->ShortNameLength ); - data->cAlternateFileName[dir_info->ShortNameLength/sizeof(WCHAR)] = 0; - TRACE("returning %s (%s)\n", debugstr_w(data->cFileName), debugstr_w(data->cAlternateFileName) ); diff --git a/dlls/kernel32/tests/file.c b/dlls/kernel32/tests/file.c index 492a34f..345136d 100644 --- a/dlls/kernel32/tests/file.c +++ b/dlls/kernel32/tests/file.c @@ -2619,6 +2619,53 @@ static void test_FindFirstFileExA(FINDEX_SEARCH_OPS search_ops) FindClose( handle ); + /* Now do it without short names */ + + handle = pFindFirstFileExA("test-dir\\*", FindExInfoBasic, &search_results, search_ops, NULL, 0); + if (handle == INVALID_HANDLE_VALUE && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) + { + win_skip("FindFirstFileExA is not implemented\n"); + goto cleanup; + } + if (handle == INVALID_HANDLE_VALUE && GetLastError() == ERROR_INVALID_PARAMETER) + { + win_skip("FindFirstFileExA FindExInfoBasic is not implemented\n"); + goto cleanup; + } + ok(handle != INVALID_HANDLE_VALUE, "FindFirstFile failed (err=%u)\n", GetLastError()); + ok(strcmp(search_results.cFileName, ".") == 0, "First entry should be '.', is %s\n", search_results.cFileName); + +#define CHECK_NAME(fn) (strcmp((fn), "file1") == 0 || strcmp((fn), "file2") == 0 || strcmp((fn), "dir1") == 0) + + ok(FindNextFileA(handle, &search_results), "Fetching second file failed\n"); + ok(strcmp(search_results.cFileName, "..") == 0, "Second entry should be '..' is %s\n", search_results.cFileName); + + ok(FindNextFileA(handle, &search_results), "Fetching third file failed\n"); + ok(CHECK_NAME(search_results.cFileName), "Invalid third entry - %s\n", search_results.cFileName); + + SetLastError(0xdeadbeef); + ret = FindNextFileA(handle, &search_results); + if (!ret && (GetLastError() == ERROR_NO_MORE_FILES) && (search_ops == FindExSearchLimitToDirectories)) + { + skip("File system supports directory filtering\n"); + /* Results from the previous call are not cleared */ + ok(strcmp(search_results.cFileName, "dir1") == 0, "Third entry should be 'dir1' is %s\n", search_results.cFileName); + FindClose( handle ); + goto cleanup; + } + + ok(ret, "Fetching fourth file failed\n"); + ok(CHECK_NAME(search_results.cFileName), "Invalid fourth entry - %s\n", search_results.cFileName); + + ok(FindNextFileA(handle, &search_results), "Fetching fifth file failed\n"); + ok(CHECK_NAME(search_results.cFileName), "Invalid fifth entry - %s\n", search_results.cFileName); + +#undef CHECK_NAME + + ok(FindNextFileA(handle, &search_results) == FALSE, "Fetching sixth file should fail\n"); + + FindClose( handle ); + cleanup: DeleteFileA("test-dir\\file1"); DeleteFileA("test-dir\\file2"); -- 1.8.4.5