From: Martin Storsjo Subject: [PATCH] kernel32: Implement a few more classes in GetFileInformationByHandleEx Message-Id: <1431810368-14673-1-git-send-email-martin@martin.st> Date: Sun, 17 May 2015 00:06:08 +0300 Implement FileBasicInfo and FileStandardInfo. The common error handling within the function is factorized and shared. Also add tests tests for them, and for FileNameInfo. Using these classes requires adding the right structs in winbase.h (even though the implementation of GetFileInformationByHandleEx don't use them). These structs are equal to the corresponding ones on the ntdll level, that already exist. The tests do some rudimentary but not exhaustive checks of the behaviour of these info structs. --- dlls/kernel32/file.c | 31 +++++++++++---------- dlls/kernel32/tests/file.c | 69 ++++++++++++++++++++++++++++++++++++++++++++-- include/winbase.h | 21 ++++++++++++++ 3 files changed, 104 insertions(+), 17 deletions(-) diff --git a/dlls/kernel32/file.c b/dlls/kernel32/file.c index 006db1c..a680ab2 100644 --- a/dlls/kernel32/file.c +++ b/dlls/kernel32/file.c @@ -897,8 +897,6 @@ BOOL WINAPI GetFileInformationByHandleEx( HANDLE handle, FILE_INFO_BY_HANDLE_CLA switch (class) { - case FileBasicInfo: - case FileStandardInfo: case FileRenameInfo: case FileDispositionInfo: case FileAllocationInfo: @@ -919,31 +917,36 @@ BOOL WINAPI GetFileInformationByHandleEx( HANDLE handle, FILE_INFO_BY_HANDLE_CLA SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); return FALSE; + case FileBasicInfo: + status = NtQueryInformationFile( handle, &io, info, size, FileBasicInformation ); + break; + + case FileStandardInfo: + status = NtQueryInformationFile( handle, &io, info, size, FileStandardInformation ); + break; + case FileNameInfo: status = NtQueryInformationFile( handle, &io, info, size, FileNameInformation ); - if (status != STATUS_SUCCESS) - { - SetLastError( RtlNtStatusToDosError( status ) ); - return FALSE; - } - return TRUE; + break; case FileIdBothDirectoryRestartInfo: case FileIdBothDirectoryInfo: status = NtQueryDirectoryFile( handle, NULL, NULL, NULL, &io, info, size, FileIdBothDirectoryInformation, FALSE, NULL, (class == FileIdBothDirectoryRestartInfo) ); - if (status != STATUS_SUCCESS) - { - SetLastError( RtlNtStatusToDosError( status ) ); - return FALSE; - } - return TRUE; + break; default: SetLastError( ERROR_INVALID_PARAMETER ); return FALSE; } + + if (status != STATUS_SUCCESS) + { + SetLastError( RtlNtStatusToDosError( status ) ); + return FALSE; + } + return TRUE; } diff --git a/dlls/kernel32/tests/file.c b/dlls/kernel32/tests/file.c index 9a6e972..66619a7 100644 --- a/dlls/kernel32/tests/file.c +++ b/dlls/kernel32/tests/file.c @@ -3764,11 +3764,15 @@ else static void test_GetFileInformationByHandleEx(void) { int i; - char tempPath[MAX_PATH], tempFileName[MAX_PATH], buffer[1024]; + char tempPath[MAX_PATH], tempFileName[MAX_PATH], buffer[1024], *strPtr; BOOL ret; - DWORD ret2; - HANDLE directory; + DWORD ret2, written; + HANDLE directory, file; FILE_ID_BOTH_DIR_INFO *bothDirInfo; + FILE_BASIC_INFO *basicInfo; + FILE_STANDARD_INFO *standardInfo; + FILE_NAME_INFO *nameInfo; + LARGE_INTEGER prevWrite; struct { FILE_INFO_BY_HANDLE_CLASS handleClass; void *ptr; @@ -3831,6 +3835,65 @@ static void test_GetFileInformationByHandleEx(void) } CloseHandle(directory); + + file = CreateFileA(tempFileName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, OPEN_EXISTING, 0, NULL); + ok(file != INVALID_HANDLE_VALUE, "GetFileInformationByHandleEx: failed to open the temp file, " + "got error %u.\n", GetLastError()); + + // Test FileBasicInfo; make sure the write time changes when a file is updated + memset(buffer, 0xff, sizeof(buffer)); + ret = pGetFileInformationByHandleEx(file, FileBasicInfo, buffer, sizeof(buffer)); + ok(ret, "GetFileInformationByHandleEx: failed to get FileBasicInfo, %u\n", GetLastError()); + basicInfo = (FILE_BASIC_INFO *)buffer; + prevWrite = basicInfo->LastWriteTime; + CloseHandle(file); + + Sleep(30); // Make sure a new write time is different from the previous + + // Write something to the file, to make sure the write time has changed + file = CreateFileA(tempFileName, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, OPEN_EXISTING, 0, NULL); + ok(file != INVALID_HANDLE_VALUE, "GetFileInformationByHandleEx: failed to open the temp file, " + "got error %u.\n", GetLastError()); + ret = WriteFile(file, tempFileName, strlen(tempFileName), &written, NULL); + ok(ret, "GetFileInformationByHandleEx: Write failed\n"); + CloseHandle(file); + + file = CreateFileA(tempFileName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, OPEN_EXISTING, 0, NULL); + ok(file != INVALID_HANDLE_VALUE, "GetFileInformationByHandleEx: failed to open the temp file, " + "got error %u.\n", GetLastError()); + + memset(buffer, 0xff, sizeof(buffer)); + ret = pGetFileInformationByHandleEx(file, FileBasicInfo, buffer, sizeof(buffer)); + ok(ret, "GetFileInformationByHandleEx: failed to get FileBasicInfo, %u\n", GetLastError()); + basicInfo = (FILE_BASIC_INFO *)buffer; + // Could also check that the creation time didn't change - on windows + // it doesn't, but on wine, it does change even if it shouldn't. + ok(basicInfo->LastWriteTime.QuadPart != prevWrite.QuadPart, "GetFileInformationByHandleEx: last write time didn't change\n"); + + // Test FileStandardInfo, check some basic parameters + memset(buffer, 0xff, sizeof(buffer)); + ret = pGetFileInformationByHandleEx(file, FileStandardInfo, buffer, sizeof(buffer)); + ok(ret, "GetFileInformationByHandleEx: failed to get FileStandardInfo, %u\n", GetLastError()); + standardInfo = (FILE_STANDARD_INFO *)buffer; + ok(standardInfo->NumberOfLinks == 1, "GetFileInformationByHandleEx: Unexpcted number of links\n"); + ok(standardInfo->DeletePending == FALSE, "GetFileInformationByHandleEx: Unexpcted pending delete\n"); + ok(standardInfo->Directory == FALSE, "GetFileInformationByHandleEx: Incorrect directory flag\n"); + + // Test FileNameInfo + memset(buffer, 0xff, sizeof(buffer)); + ret = pGetFileInformationByHandleEx(file, FileNameInfo, buffer, sizeof(buffer)); + ok(ret, "GetFileInformationByHandleEx: failed to get FileNameInfo, %u\n", GetLastError()); + nameInfo = (FILE_NAME_INFO *)buffer; + strPtr = strchr(tempFileName, '\\'); + ok(strPtr != NULL, "GetFileInformationByHandleEx: Temp filename didn't contain backslash\n"); + ok(nameInfo->FileNameLength == strlen(strPtr) * 2, "GetFileInformationByHandleEx: Incorrect file name length\n"); + for (i = 0; i < nameInfo->FileNameLength/2; i++) + ok(strPtr[i] == nameInfo->FileName[i], "Incorrect filename char %d: %c vs %c\n", i, strPtr[i], nameInfo->FileName[i]); + CloseHandle(file); + DeleteFileA(tempFileName); } diff --git a/include/winbase.h b/include/winbase.h index 9d27dcd..7a9a8c6 100644 --- a/include/winbase.h +++ b/include/winbase.h @@ -823,6 +823,27 @@ typedef struct _FILE_ID_BOTH_DIR_INFO { WCHAR FileName[1]; } FILE_ID_BOTH_DIR_INFO, *PFILE_ID_BOTH_DIR_INFO; +typedef struct _FILE_BASIC_INFO { + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + DWORD FileAttributes; +} FILE_BASIC_INFO, *PFILE_BASIC_INFO; + +typedef struct _FILE_STANDARD_INFO { + LARGE_INTEGER AllocationSize; + LARGE_INTEGER EndOfFile; + DWORD NumberOfLinks; + BOOLEAN DeletePending; + BOOLEAN Directory; +} FILE_STANDARD_INFO, *PFILE_STANDARD_INFO; + +typedef struct _FILE_NAME_INFO { + DWORD FileNameLength; + WCHAR FileName[1]; +} FILE_NAME_INFO, *PFILE_NAME_INFO; + #define PIPE_ACCESS_INBOUND 1 #define PIPE_ACCESS_OUTBOUND 2 #define PIPE_ACCESS_DUPLEX 3 -- 1.8.1.2