diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c index 1a02ce5eaa5..5053eb123d1 100644 --- a/dlls/ntdll/tests/file.c +++ b/dlls/ntdll/tests/file.c @@ -3261,6 +3261,137 @@ static void test_file_all_name_information(void) HeapFree( GetProcessHeap(), 0, file_name ); } +static void test_file_completion_information(void) +{ + static const char buf[] = "testdata"; + FILE_IO_COMPLETION_NOTIFICATION_INFORMATION info; + OVERLAPPED ov, *pov; + IO_STATUS_BLOCK io; + NTSTATUS status; + DWORD num_bytes; + HANDLE port, h; + ULONG_PTR key; + BOOL ret; + int i; + + if (!(h = create_temp_file(0))) return; + + status = pNtSetInformationFile(h, &io, &info, sizeof(info) - 1, FileIoCompletionNotificationInformation); + todo_wine + ok(status == STATUS_INFO_LENGTH_MISMATCH || status == STATUS_INVALID_INFO_CLASS /* XP */, + "expected STATUS_INFO_LENGTH_MISMATCH, got %08x\n", status); + if (status == STATUS_INVALID_INFO_CLASS || status == STATUS_NOT_IMPLEMENTED) + { + skip("FileIoCompletionNotificationInformation class not supported\n"); + CloseHandle(h); + return; + } + + info.Flags = FILE_SKIP_COMPLETION_PORT_ON_SUCCESS; + status = pNtSetInformationFile(h, &io, &info, sizeof(info), FileIoCompletionNotificationInformation); + ok(status == STATUS_INVALID_PARAMETER, "expected STATUS_INVALID_PARAMETER, got %08x\n", status); + + CloseHandle(h); + if (!(h = create_temp_file(FILE_FLAG_OVERLAPPED))) return; + + info.Flags = FILE_SKIP_SET_EVENT_ON_HANDLE; + status = pNtSetInformationFile(h, &io, &info, sizeof(info), FileIoCompletionNotificationInformation); + ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08x\n", status); + + info.Flags = FILE_SKIP_SET_USER_EVENT_ON_FAST_IO; + status = pNtSetInformationFile(h, &io, &info, sizeof(info), FileIoCompletionNotificationInformation); + ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08x\n", status); + + CloseHandle(h); + if (!(h = create_temp_file(FILE_FLAG_OVERLAPPED))) return; + + memset(&ov, 0, sizeof(ov)); + ov.hEvent = CreateEventA(NULL, TRUE, FALSE, NULL); + port = CreateIoCompletionPort(h, NULL, 0xdeadbeef, 0); + ok(port != NULL, "CreateIoCompletionPort failed, error %u\n", GetLastError()); + + for (i = 0; i < 10; i++) + { + SetLastError(0xdeadbeef); + ret = WriteFile(h, buf, sizeof(buf), &num_bytes, &ov); + if (ret || GetLastError() != ERROR_IO_PENDING) break; + ret = GetOverlappedResult(h, &ov, &num_bytes, TRUE); + ok(ret, "GetOverlappedResult failed, error %u\n", GetLastError()); + ret = GetQueuedCompletionStatus(port, &num_bytes, &key, &pov, 1000); + ok(ret, "GetQueuedCompletionStatus failed, error %u\n", GetLastError()); + ret = FALSE; + } + if (ret) + { + ok(num_bytes == sizeof(buf), "expected sizeof(buf), got %u\n", num_bytes); + + key = 0; + pov = NULL; + ret = GetQueuedCompletionStatus(port, &num_bytes, &key, &pov, 1000); + ok(ret, "GetQueuedCompletionStatus failed, error %u\n", GetLastError()); + ok(key == 0xdeadbeef, "expected 0xdeadbeef, got %lx\n", key); + ok(pov == &ov, "expected %p, got %p\n", &ov, pov); + } + else + win_skip("WriteFile never returned TRUE\n"); + + info.Flags = FILE_SKIP_COMPLETION_PORT_ON_SUCCESS; + status = pNtSetInformationFile(h, &io, &info, sizeof(info), FileIoCompletionNotificationInformation); + ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08x\n", status); + + for (i = 0; i < 10; i++) + { + SetLastError(0xdeadbeef); + ret = WriteFile(h, buf, sizeof(buf), &num_bytes, &ov); + if (ret || GetLastError() != ERROR_IO_PENDING) break; + ret = GetOverlappedResult(h, &ov, &num_bytes, TRUE); + ok(ret, "GetOverlappedResult failed, error %u\n", GetLastError()); + ret = FALSE; + } + if (ret) + { + ok(num_bytes == sizeof(buf), "expected sizeof(buf), got %u\n", num_bytes); + + pov = (void *)0xdeadbeef; + ret = GetQueuedCompletionStatus(port, &num_bytes, &key, &pov, 500); + ok(!ret, "GetQueuedCompletionStatus succeeded\n"); + ok(pov == NULL, "expected NULL, got %p\n", pov); + } + else + win_skip("WriteFile never returned TRUE\n"); + + info.Flags = 0; + status = pNtSetInformationFile(h, &io, &info, sizeof(info), FileIoCompletionNotificationInformation); + ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08x\n", status); + + for (i = 0; i < 10; i++) + { + SetLastError(0xdeadbeef); + ret = WriteFile(h, buf, sizeof(buf), &num_bytes, &ov); + if (ret || GetLastError() != ERROR_IO_PENDING) break; + ret = GetOverlappedResult(h, &ov, &num_bytes, TRUE); + ok(ret, "GetOverlappedResult failed, error %u\n", GetLastError()); + ret = GetQueuedCompletionStatus(port, &num_bytes, &key, &pov, 1000); + ok(ret, "GetQueuedCompletionStatus failed, error %u\n", GetLastError()); + ret = FALSE; + } + if (ret) + { + ok(num_bytes == sizeof(buf), "expected sizeof(buf), got %u\n", num_bytes); + + pov = (void *)0xdeadbeef; + ret = GetQueuedCompletionStatus(port, &num_bytes, &key, &pov, 1000); + ok(!ret, "GetQueuedCompletionStatus succeeded\n"); + ok(pov == NULL, "expected NULL, got %p\n", pov); + } + else + win_skip("WriteFile never returned TRUE\n"); + + CloseHandle(ov.hEvent); + CloseHandle(port); + CloseHandle(h); +} + static void test_file_id_information(void) { BY_HANDLE_FILE_INFORMATION info; @@ -4299,6 +4430,7 @@ START_TEST(file) test_file_rename_information(); test_file_link_information(); test_file_disposition_information(); + test_file_completion_information(); test_file_id_information(); test_query_volume_information_file(); test_query_attribute_information_file(); diff --git a/include/winternl.h b/include/winternl.h index f6308f5715e..5cd1664f1fe 100644 --- a/include/winternl.h +++ b/include/winternl.h @@ -741,6 +741,14 @@ typedef struct _FILE_ALL_INFORMATION { FILE_NAME_INFORMATION NameInformation; } FILE_ALL_INFORMATION, *PFILE_ALL_INFORMATION; +typedef struct _FILE_IO_COMPLETION_NOTIFICATION_INFORMATION { + ULONG Flags; +} FILE_IO_COMPLETION_NOTIFICATION_INFORMATION, *PFILE_IO_COMPLETION_NOTIFICATION_INFORMATION; + +#define FILE_SKIP_COMPLETION_PORT_ON_SUCCESS 0x1 +#define FILE_SKIP_SET_EVENT_ON_HANDLE 0x2 +#define FILE_SKIP_SET_USER_EVENT_ON_FAST_IO 0x4 + typedef enum _FSINFOCLASS { FileFsVolumeInformation = 1, FileFsLabelInformation,