WaitCompletionPacket is a kernel object that, when associated with a target object and a completion object and when the target object is signaled, adds the completion information stored in itself to the completion object.
For React Native.
From: Zhiyi Zhang zzhang@codeweavers.com
--- dlls/ntdll/tests/om.c | 5 ++++- include/winternl.h | 6 ++++++ 2 files changed, 10 insertions(+), 1 deletion(-)
diff --git a/dlls/ntdll/tests/om.c b/dlls/ntdll/tests/om.c index df62bffe2c7..8f38b3eecd7 100644 --- a/dlls/ntdll/tests/om.c +++ b/dlls/ntdll/tests/om.c @@ -2329,6 +2329,7 @@ static void test_object_types(void) TYPE( L"Token", TOKEN, SYNCHRONIZE, 0 ), TYPE( L"Type", TYPE, SYNCHRONIZE, 0 ), TYPE( L"UserApcReserve", USER_APC_RESERVE, 0, 0 ), + TYPE( L"WaitCompletionPacket", WAIT_COMPLETION_PACKET, SYNCHRONIZE, 1), TYPE( L"WindowStation", WINSTA, 0, 0 ), #undef TYPE }; @@ -2390,7 +2391,9 @@ static void test_object_types(void) break; }
- ok( j < ARRAY_SIZE(all_types), "type %s not found\n", debugstr_w(tests[i].name) ); + todo_wine_if(!lstrcmpW(tests[i].name, L"WaitCompletionPacket")) + ok( broken(!lstrcmpW(tests[i].name, L"WaitCompletionPacket") && j == ARRAY_SIZE(all_types)) /* Win7 doesn't have WaitCompletionPacket */ + || j < ARRAY_SIZE(all_types), "type %s not found\n", debugstr_w(tests[i].name) ); } for (j = 0; j < ARRAY_SIZE(all_types); j++) { diff --git a/include/winternl.h b/include/winternl.h index 585349905f5..4926bd1bf82 100644 --- a/include/winternl.h +++ b/include/winternl.h @@ -3690,6 +3690,12 @@ typedef struct _FILE_IO_COMPLETION_INFORMATION { IO_STATUS_BLOCK IoStatusBlock; } FILE_IO_COMPLETION_INFORMATION, *PFILE_IO_COMPLETION_INFORMATION;
+#define WAIT_COMPLETION_PACKET_QUERY_STATE 0x0001 +#define WAIT_COMPLETION_PACKET_GENERIC_EXECUTE (STANDARD_RIGHTS_EXECUTE|WAIT_COMPLETION_PACKET_QUERY_STATE) +#define WAIT_COMPLETION_PACKET_GENERIC_READ (STANDARD_RIGHTS_READ|WAIT_COMPLETION_PACKET_QUERY_STATE) +#define WAIT_COMPLETION_PACKET_GENERIC_WRITE (STANDARD_RIGHTS_WRITE|WAIT_COMPLETION_PACKET_QUERY_STATE) +#define WAIT_COMPLETION_PACKET_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|WAIT_COMPLETION_PACKET_QUERY_STATE) + typedef enum _HARDERROR_RESPONSE_OPTION { OptionAbortRetryIgnore, OptionOk,
From: Zhiyi Zhang zzhang@codeweavers.com
--- dlls/ntdll/tests/file.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+)
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c index 02bdf67796d..7e006541cbd 100644 --- a/dlls/ntdll/tests/file.c +++ b/dlls/ntdll/tests/file.c @@ -55,6 +55,7 @@ static NTSTATUS (WINAPI *pNtAllocateReserveObject)( HANDLE *, const OBJECT_ATTRI static NTSTATUS (WINAPI *pNtCreateMailslotFile)( PHANDLE, ULONG, POBJECT_ATTRIBUTES, PIO_STATUS_BLOCK, ULONG, ULONG, ULONG, PLARGE_INTEGER ); static NTSTATUS (WINAPI *pNtCreateFile)(PHANDLE,ACCESS_MASK,POBJECT_ATTRIBUTES,PIO_STATUS_BLOCK,PLARGE_INTEGER,ULONG,ULONG,ULONG,ULONG,PVOID,ULONG); +static NTSTATUS (WINAPI *pNtCreateWaitCompletionPacket)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES); static NTSTATUS (WINAPI *pNtOpenFile)(PHANDLE,ACCESS_MASK,POBJECT_ATTRIBUTES,PIO_STATUS_BLOCK,ULONG,ULONG); static NTSTATUS (WINAPI *pNtDeleteFile)(POBJECT_ATTRIBUTES ObjectAttributes); static NTSTATUS (WINAPI *pNtReadFile)(HANDLE hFile, HANDLE hEvent, @@ -6036,6 +6037,42 @@ static void test_set_io_completion_ex(void) CloseHandle(completion); }
+static void test_create_wait_completion_packet(void) +{ + UNICODE_STRING name = RTL_CONSTANT_STRING(L"\BaseNamedObjects\test_create_waitcompletionpacket"); + HANDLE handle, handle2; + OBJECT_ATTRIBUTES attr; + NTSTATUS status; + + if (!pNtCreateWaitCompletionPacket) + { + todo_wine + win_skip("NtCreateWaitCompletionPacket is unavailable.\n"); + return; + } + + InitializeObjectAttributes(&attr, &name, 0, NULL, NULL); + + /* Parameter checks */ + status = pNtCreateWaitCompletionPacket(NULL, GENERIC_ALL, &attr); + ok(status == STATUS_ACCESS_VIOLATION, "Got unexpected status %#lx.\n", status); + + status = pNtCreateWaitCompletionPacket(&handle, 0, &attr); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + pNtClose(handle); + + status = pNtCreateWaitCompletionPacket(&handle, GENERIC_ALL, NULL); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + pNtClose(handle); + + status = pNtCreateWaitCompletionPacket(&handle, GENERIC_ALL, &attr); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + + status = pNtCreateWaitCompletionPacket(&handle2, GENERIC_ALL, &attr); + ok(status == STATUS_OBJECT_NAME_COLLISION, "Got unexpected status %#lx.\n", status); + pNtClose(handle); +} + START_TEST(file) { HMODULE hkernel32 = GetModuleHandleA("kernel32.dll"); @@ -6056,6 +6093,7 @@ START_TEST(file) pNtAllocateReserveObject= (void *)GetProcAddress(hntdll, "NtAllocateReserveObject"); pNtCreateMailslotFile = (void *)GetProcAddress(hntdll, "NtCreateMailslotFile"); pNtCreateFile = (void *)GetProcAddress(hntdll, "NtCreateFile"); + pNtCreateWaitCompletionPacket = (void *)GetProcAddress(hntdll, "NtCreateWaitCompletionPacket"); pNtOpenFile = (void *)GetProcAddress(hntdll, "NtOpenFile"); pNtDeleteFile = (void *)GetProcAddress(hntdll, "NtDeleteFile"); pNtReadFile = (void *)GetProcAddress(hntdll, "NtReadFile"); @@ -6119,4 +6157,5 @@ START_TEST(file) test_flush_buffers_file(); test_mailslot_name(); test_reparse_points(); + test_create_wait_completion_packet(); }
From: Zhiyi Zhang zzhang@codeweavers.com
--- dlls/ntdll/tests/file.c | 486 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 486 insertions(+)
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c index 7e006541cbd..a372bd170e1 100644 --- a/dlls/ntdll/tests/file.c +++ b/dlls/ntdll/tests/file.c @@ -52,6 +52,9 @@ static BOOL (WINAPI *pRtlDosPathNameToNtPathName_U)( LPCWSTR, PUNICODE_STRIN static NTSTATUS (WINAPI *pRtlWow64EnableFsRedirectionEx)( ULONG, ULONG * );
static NTSTATUS (WINAPI *pNtAllocateReserveObject)( HANDLE *, const OBJECT_ATTRIBUTES *, MEMORY_RESERVE_OBJECT_TYPE ); +static NTSTATUS (WINAPI *pNtAssociateWaitCompletionPacket)(HANDLE, HANDLE, HANDLE, PVOID, PVOID, + NTSTATUS, ULONG_PTR, PBOOLEAN); +static NTSTATUS (WINAPI *pNtCreateEvent)(PHANDLE, ACCESS_MASK, const OBJECT_ATTRIBUTES *, EVENT_TYPE, BOOLEAN); static NTSTATUS (WINAPI *pNtCreateMailslotFile)( PHANDLE, ULONG, POBJECT_ATTRIBUTES, PIO_STATUS_BLOCK, ULONG, ULONG, ULONG, PLARGE_INTEGER ); static NTSTATUS (WINAPI *pNtCreateFile)(PHANDLE,ACCESS_MASK,POBJECT_ATTRIBUTES,PIO_STATUS_BLOCK,PLARGE_INTEGER,ULONG,ULONG,ULONG,ULONG,PVOID,ULONG); @@ -74,6 +77,7 @@ static NTSTATUS (WINAPI *pNtFsControlFile) (HANDLE handle, HANDLE event, PIO_APC
static NTSTATUS (WINAPI *pNtCreateIoCompletion)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, ULONG); static NTSTATUS (WINAPI *pNtOpenIoCompletion)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES); +static NTSTATUS (WINAPI *pNtPulseEvent)(HANDLE, LONG *); static NTSTATUS (WINAPI *pNtQueryIoCompletion)(HANDLE, IO_COMPLETION_INFORMATION_CLASS, PVOID, ULONG, PULONG); static NTSTATUS (WINAPI *pNtRemoveIoCompletion)(HANDLE, PULONG_PTR, PULONG_PTR, PIO_STATUS_BLOCK, PLARGE_INTEGER); static NTSTATUS (WINAPI *pNtRemoveIoCompletionEx)(HANDLE,FILE_IO_COMPLETION_INFORMATION*,ULONG,ULONG*,LARGE_INTEGER*,BOOLEAN); @@ -6073,6 +6077,484 @@ static void test_create_wait_completion_packet(void) pNtClose(handle); }
+static void test_associate_wait_completion_packet(void) +{ + static const char pipe_name[] = "\\.\pipe\test_associate_wait_completion_packet"; + UNICODE_STRING packet_name = RTL_CONSTANT_STRING(L"\BaseNamedObjects\test_associate_wait_completion_packet"); + UNICODE_STRING packet_name2 = RTL_CONSTANT_STRING(L"\BaseNamedObjects\test_associate_wait_completion_packet2"); + BYTE send_buf[TEST_BUF_LEN], recv_buf[TEST_BUF_LEN]; + HANDLE completion, packet, packet2, server, client; + FILE_IO_COMPLETION_NOTIFICATION_INFORMATION info; + ULONG_PTR key_context, apc_context; + DWORD read_bytes, written_bytes; + FILE_COMPLETION_INFORMATION fci; + LARGE_INTEGER timeout = {{0}}; + OVERLAPPED overlapped = {0}; + OBJECT_ATTRIBUTES attr; + ULONG completion_count; + IO_STATUS_BLOCK iosb; + BOOLEAN signaled; + NTSTATUS status; + HANDLE event; + + if (!pNtAssociateWaitCompletionPacket) + { + todo_wine + win_skip("NtAssociateWaitCompletionPacket is unavailable.\n"); + return; + } + + status = pNtCreateIoCompletion(&completion, IO_COMPLETION_ALL_ACCESS, NULL, 0); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + + server = CreateNamedPipeA(pipe_name, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, + PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, 4, 1024, 1024, + 1000, NULL); + ok(server != INVALID_HANDLE_VALUE, "CreateNamedPipe failed, error %lu.\n", GetLastError()); + client = CreateFileA(pipe_name, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, + FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED, NULL); + ok(client != INVALID_HANDLE_VALUE, "CreateFile failed, error %lu.\n", GetLastError()); + + /* Test normal NtAssociateWaitCompletionPacket() calls */ + InitializeObjectAttributes(&attr, &packet_name, 0, NULL, NULL); + status = pNtCreateWaitCompletionPacket(&packet, GENERIC_ALL, &attr); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + /* ReadFile() should be called before NtAssociateWaitCompletionPacket(). Otherwise, the server + * object stays signaled */ + memset(send_buf, 0xa1, TEST_BUF_LEN); + memset(recv_buf, 0xb2, TEST_BUF_LEN); + ReadFile(server, recv_buf, TEST_BUF_LEN, &read_bytes, &overlapped); + + status = pNtAssociateWaitCompletionPacket(packet, completion, server, (void *)1, (void *)2, + STATUS_SUCCESS, (ULONG_PTR)3, &signaled); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + ok(signaled == FALSE, "Got unexpected signaled %d.\n", signaled); + completion_count = get_pending_msgs(completion); + ok(!completion_count, "Got unexpected completion count %ld.\n", completion_count); + + /* Associating an already associated wait completion packet */ + status = pNtAssociateWaitCompletionPacket(packet, completion, server, (void *)1, (void *)2, + STATUS_SUCCESS, (ULONG_PTR)3, &signaled); + ok(status == STATUS_INVALID_PARAMETER_1, "Got unexpected status %#lx.\n", status); + + WriteFile(client, send_buf, TEST_BUF_LEN, &written_bytes, NULL); + completion_count = get_pending_msgs(completion); + ok(completion_count == 1, "Got unexpected completion count %ld.\n", completion_count); + + /* Associating an already queued wait completion packet */ + status = pNtAssociateWaitCompletionPacket(packet, completion, server, (void *)1, (void *)2, + STATUS_SUCCESS, (ULONG_PTR)3, &signaled); + ok(status == STATUS_INVALID_PARAMETER_1, "Got unexpected status %#lx.\n", status); + + status = pNtRemoveIoCompletion(completion, &key_context, &apc_context, &iosb, &timeout); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + ok(key_context == 1, "Got unexpected completion key %Id\n", key_context); + ok(apc_context == 2, "Got unexpected completion value %Id\n", apc_context); + ok(iosb.Information == 3, "Got unexpected iosb.Information %Id\n", iosb.Information); + ok(iosb.Status == STATUS_SUCCESS, "Got unexpected iosb.Status %#lx\n", iosb.Status); + ok(!memcmp(send_buf, recv_buf, TEST_BUF_LEN), + "Receive buffer (%02x %02x %02x) did not match send buffer (%02x %02x %02x)\n", recv_buf[0], + recv_buf[1], recv_buf[2], send_buf[0], send_buf[1], send_buf[2]); + completion_count = get_pending_msgs(completion); + ok(!completion_count, "Unexpected completion count %ld.\n", completion_count); + + status = pNtRemoveIoCompletion(completion, &key_context, &apc_context, &iosb, &timeout); + ok(status == WAIT_TIMEOUT, "Got unexpected status %#lx.\n", status); + + /* Associating an already removed wait completion packet */ + memset(send_buf, 0xa2, TEST_BUF_LEN); + ReadFile(server, recv_buf, TEST_BUF_LEN, &read_bytes, &overlapped); + status = pNtAssociateWaitCompletionPacket(packet, completion, server, (void *)4, (void *)5, + STATUS_SUCCESS, (ULONG_PTR)6, &signaled); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + ok(signaled == FALSE, "Got unexpected signaled %d.\n", signaled); + completion_count = get_pending_msgs(completion); + ok(!completion_count, "Unexpected completion count %ld.\n", completion_count); + + WriteFile(client, send_buf, TEST_BUF_LEN, &written_bytes, NULL); + completion_count = get_pending_msgs(completion); + ok(completion_count == 1, "Got unexpected completion count %ld.\n", completion_count); + + status = pNtRemoveIoCompletion(completion, &key_context, &apc_context, &iosb, &timeout); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + ok(key_context == 4, "Got unexpected completion key %Id\n", key_context); + ok(apc_context == 5, "Got unexpected completion value %Id\n", apc_context); + ok(iosb.Information == 6, "Got unexpected iosb.Information %Id\n", iosb.Information); + ok(iosb.Status == STATUS_SUCCESS, "Got unexpected iosb.Status %#lx\n", iosb.Status); + ok(!memcmp(send_buf, recv_buf, TEST_BUF_LEN), + "Receive buffer (%02x %02x %02x) did not match send buffer (%02x %02x %02x)\n", recv_buf[0], + recv_buf[1], recv_buf[2], send_buf[0], send_buf[1], send_buf[2]); + completion_count = get_pending_msgs(completion); + ok(!completion_count, "Unexpected completion count %ld.\n", completion_count); + + /* Test associating wait completion packet before setting I/O completion */ + memset(send_buf, 0xa3, TEST_BUF_LEN); + ReadFile(server, recv_buf, TEST_BUF_LEN, &read_bytes, &overlapped); + + status = pNtAssociateWaitCompletionPacket(packet, completion, server, (void *)7, (void *)8, + STATUS_SUCCESS, (ULONG_PTR)9, &signaled); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + ok(signaled == FALSE, "Got unexpected signaled %d.\n", signaled); + completion_count = get_pending_msgs(completion); + ok(!completion_count, "Unexpected completion count %ld.\n", completion_count); + + fci.CompletionPort = completion; + fci.CompletionKey = CKEY_FIRST; + iosb.Status = 0xdeadbeef; + status = pNtSetInformationFile(server, &iosb, &fci, sizeof(fci), FileCompletionInformation); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + ok(iosb.Status == STATUS_SUCCESS, "Got unexpected iosb.Status %#lx.\n", iosb.Status); + completion_count = get_pending_msgs(completion); + ok(completion_count == 1, "Unexpected completion count %ld.\n", completion_count); + + WriteFile(client, send_buf, TEST_BUF_LEN, &written_bytes, NULL); + completion_count = get_pending_msgs(completion); + ok(completion_count == 2, "Got unexpected completion count %ld.\n", completion_count); + + status = pNtRemoveIoCompletion(completion, &key_context, &apc_context, &iosb, &timeout); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + ok(key_context == 7, "Got unexpected completion key %Id\n", key_context); + ok(apc_context == 8, "Got unexpected completion value %Id\n", apc_context); + ok(iosb.Information == 9, "Got unexpected iosb.Information %Id\n", iosb.Information); + ok(iosb.Status == STATUS_SUCCESS, "Got unexpected iosb.Status %#lx\n", iosb.Status); + ok(!memcmp(send_buf, recv_buf, TEST_BUF_LEN), + "Receive buffer (%02x %02x %02x) matched send buffer (%02x %02x %02x)\n", recv_buf[0], + recv_buf[1], recv_buf[2], send_buf[0], send_buf[1], send_buf[2]); + completion_count = get_pending_msgs(completion); + ok(completion_count == 1, "Unexpected completion count %ld.\n", completion_count); + + status = pNtRemoveIoCompletion(completion, &key_context, &apc_context, &iosb, &timeout); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + ok(key_context == CKEY_FIRST, "Got unexpected completion key %Id\n", key_context); + ok(apc_context == (ULONG_PTR)&overlapped, "Got unexpected completion value %Id\n", apc_context); + ok(iosb.Information == TEST_BUF_LEN, "Got unexpected iosb.Information %Id\n", iosb.Information); + ok(iosb.Status == STATUS_SUCCESS, "Got unexpected iosb.Status %#lx\n", iosb.Status); + ok(!memcmp(send_buf, recv_buf, TEST_BUF_LEN), + "Receive buffer (%02x %02x %02x) did not match send buffer (%02x %02x %02x)\n", recv_buf[0], + recv_buf[1], recv_buf[2], send_buf[0], send_buf[1], send_buf[2]); + completion_count = get_pending_msgs(completion); + ok(!completion_count, "Unexpected completion count %ld.\n", completion_count); + + /* Test associating wait completion packet after setting I/O completion for the same file */ + ReadFile(server, recv_buf, TEST_BUF_LEN, &read_bytes, &overlapped); + + signaled = FALSE; + status = pNtAssociateWaitCompletionPacket(packet, completion, server, (void *)10, (void *)11, + STATUS_SUCCESS, (ULONG_PTR)12, &signaled); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + ok(signaled == FALSE, "Got unexpected signaled %d.\n", signaled); + completion_count = get_pending_msgs(completion); + ok(!completion_count, "Unexpected completion count %ld.\n", completion_count); + + WriteFile(client, send_buf, TEST_BUF_LEN, &written_bytes, NULL); + + status = pNtRemoveIoCompletion(completion, &key_context, &apc_context, &iosb, &timeout); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + ok(key_context == CKEY_FIRST, "Got unexpected completion key %Id\n", key_context); + ok(apc_context == (ULONG_PTR)&overlapped, "Got unexpected completion value %Id\n", apc_context); + ok(iosb.Information == TEST_BUF_LEN, "Got unexpected iosb.Information %Id\n", iosb.Information); + ok(iosb.Status == STATUS_SUCCESS, "Got unexpected iosb.Status %#lx\n", iosb.Status); + ok(!memcmp(send_buf, recv_buf, TEST_BUF_LEN), + "Receive buffer (%02x %02x %02x) did not match send buffer (%02x %02x %02x)\n", recv_buf[0], + recv_buf[1], recv_buf[2], send_buf[0], send_buf[1], send_buf[2]); + completion_count = get_pending_msgs(completion); + ok(completion_count == 1, "Unexpected completion count %ld.\n", completion_count); + + status = pNtRemoveIoCompletion(completion, &key_context, &apc_context, &iosb, &timeout); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + ok(key_context == 10, "Got unexpected completion key %Id\n", key_context); + ok(apc_context == 11, "Got unexpected completion value %Id\n", apc_context); + ok(iosb.Information == 12, "Got unexpected iosb.Information %Id\n", iosb.Information); + ok(iosb.Status == STATUS_SUCCESS, "Got unexpected iosb.Status %#lx\n", iosb.Status); + ok(!memcmp(send_buf, recv_buf, TEST_BUF_LEN), + "Receive buffer (%02x %02x %02x) matched send buffer (%02x %02x %02x)\n", recv_buf[0], + recv_buf[1], recv_buf[2], send_buf[0], send_buf[1], send_buf[2]); + completion_count = get_pending_msgs(completion); + ok(!completion_count, "Unexpected completion count %ld.\n", completion_count); + + /* Test associating wait completion packet after setting I/O completion for a new file */ + status = pNtClose(client); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + status = pNtClose(server); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + server = CreateNamedPipeA(pipe_name, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, + PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, 4, 1024, 1024, + 1000, NULL); + ok(server != INVALID_HANDLE_VALUE, "CreateNamedPipe failed, error %lu.\n", GetLastError()); + client = CreateFileA(pipe_name, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, + FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED, NULL); + ok(client != INVALID_HANDLE_VALUE, "CreateFile failed, error %lu.\n", GetLastError()); + + memset(send_buf, 0xa4, TEST_BUF_LEN); + ReadFile(server, recv_buf, TEST_BUF_LEN, &read_bytes, &overlapped); + + fci.CompletionPort = completion; + fci.CompletionKey = CKEY_SECOND; + iosb.Status = 0xdeadbeef; + status = pNtSetInformationFile(server, &iosb, &fci, sizeof(fci), FileCompletionInformation); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + ok(iosb.Status == STATUS_SUCCESS, "Got unexpected iosb.Status %#lx.\n", iosb.Status); + completion_count = get_pending_msgs(completion); + ok(!completion_count, "Unexpected completion count %ld.\n", completion_count); + + signaled = FALSE; + status = pNtAssociateWaitCompletionPacket(packet, completion, server, (void *)13, (void *)14, + STATUS_SUCCESS, (ULONG_PTR)15, &signaled); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + ok(signaled == TRUE, "Got unexpected signaled %d.\n", signaled); + + /* Notice the wait completion packet gets added immediately when there is a completion set, even + * without writing to the file */ + completion_count = get_pending_msgs(completion); + ok(completion_count == 1, "Unexpected completion count %ld.\n", completion_count); + + status = pNtRemoveIoCompletion(completion, &key_context, &apc_context, &iosb, &timeout); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + ok(key_context == 13, "Got unexpected completion key %Id\n", key_context); + ok(apc_context == 14, "Got unexpected completion value %Id\n", apc_context); + ok(iosb.Information == 15, "Got unexpected iosb.Information %Id\n", iosb.Information); + ok(iosb.Status == STATUS_SUCCESS, "Got unexpected iosb.Status %#lx\n", iosb.Status); + ok(memcmp(send_buf, recv_buf, TEST_BUF_LEN), + "Receive buffer (%02x %02x %02x) matched send buffer (%02x %02x %02x)\n", recv_buf[0], + recv_buf[1], recv_buf[2], send_buf[0], send_buf[1], send_buf[2]); + completion_count = get_pending_msgs(completion); + ok(!completion_count, "Unexpected completion count %ld.\n", completion_count); + + WriteFile(client, send_buf, TEST_BUF_LEN, &written_bytes, NULL); + completion_count = get_pending_msgs(completion); + ok(completion_count == 1, "Got unexpected completion count %ld.\n", completion_count); + + status = pNtRemoveIoCompletion(completion, &key_context, &apc_context, &iosb, &timeout); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + ok(key_context == CKEY_SECOND, "Got unexpected completion key %Id\n", key_context); + ok(apc_context == (ULONG_PTR)&overlapped, "Got unexpected completion value %Id\n", apc_context); + ok(iosb.Information == TEST_BUF_LEN, "Got unexpected iosb.Information %Id\n", iosb.Information); + ok(iosb.Status == STATUS_SUCCESS, "Got unexpected iosb.Status %#lx\n", iosb.Status); + ok(!memcmp(send_buf, recv_buf, TEST_BUF_LEN), + "Receive buffer (%02x %02x %02x) did not match send buffer (%02x %02x %02x)\n", recv_buf[0], + recv_buf[1], recv_buf[2], send_buf[0], send_buf[1], send_buf[2]); + completion_count = get_pending_msgs(completion); + ok(!completion_count, "Unexpected completion count %ld.\n", completion_count); + + /* Test file with FILE_SKIP_SET_EVENT_ON_HANDLE set */ + status = pNtClose(client); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + status = pNtClose(server); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + server = CreateNamedPipeA(pipe_name, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, + PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, 4, 1024, 1024, + 1000, NULL); + ok(server != INVALID_HANDLE_VALUE, "CreateNamedPipe failed, error %lu.\n", GetLastError()); + client = CreateFileA(pipe_name, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, + FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED, NULL); + ok(client != INVALID_HANDLE_VALUE, "CreateFile failed, error %lu.\n", GetLastError()); + + info.Flags = FILE_SKIP_SET_EVENT_ON_HANDLE; + status = pNtSetInformationFile(server, &iosb, &info, sizeof(info), FileIoCompletionNotificationInformation); + ok(status == STATUS_SUCCESS, "expected STATUS_SUCCESS, got %08lx\n", status); + ok(!is_signaled(server), "Expected server not signaled\n"); + + memset(send_buf, 0xa4, TEST_BUF_LEN); + ReadFile(server, recv_buf, TEST_BUF_LEN, &read_bytes, &overlapped); + + signaled = FALSE; + status = pNtAssociateWaitCompletionPacket(packet, completion, server, (void *)16, (void *)17, + STATUS_SUCCESS, (ULONG_PTR)18, &signaled); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + ok(signaled == FALSE, "Got unexpected signaled %d.\n", signaled); + completion_count = get_pending_msgs(completion); + ok(!completion_count, "Unexpected completion count %ld.\n", completion_count); + + WriteFile(client, send_buf, TEST_BUF_LEN, &written_bytes, NULL); + completion_count = get_pending_msgs(completion); + ok(!completion_count, "Unexpected completion count %ld.\n", completion_count); + + /* Test associating two wait completion packets */ + status = pNtClose(packet); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + status = pNtClose(client); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + status = pNtClose(server); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + server = CreateNamedPipeA(pipe_name, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, + PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, 4, 1024, 1024, + 1000, NULL); + ok(server != INVALID_HANDLE_VALUE, "CreateNamedPipe failed, error %lu.\n", GetLastError()); + client = CreateFileA(pipe_name, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, + FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED, NULL); + ok(client != INVALID_HANDLE_VALUE, "CreateFile failed, error %lu.\n", GetLastError()); + status = pNtCreateWaitCompletionPacket(&packet, GENERIC_ALL, &attr); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + InitializeObjectAttributes(&attr, &packet_name2, 0, NULL, NULL); + status = pNtCreateWaitCompletionPacket(&packet2, GENERIC_ALL, &attr); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + + memset(send_buf, 0xa5, TEST_BUF_LEN); + ReadFile(server, recv_buf, TEST_BUF_LEN, &read_bytes, &overlapped); + + status = pNtAssociateWaitCompletionPacket(packet, completion, server, (void *)19, (void *)20, + STATUS_SUCCESS, (ULONG_PTR)21, &signaled); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + ok(signaled == FALSE, "Got unexpected signaled %d.\n", signaled); + completion_count = get_pending_msgs(completion); + ok(!completion_count, "Unexpected completion count %ld.\n", completion_count); + + status = pNtAssociateWaitCompletionPacket(packet2, completion, server, (void *)22, (void *)23, + STATUS_SUCCESS, (ULONG_PTR)24, &signaled); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + ok(signaled == FALSE, "Got unexpected signaled %d.\n", signaled); + completion_count = get_pending_msgs(completion); + ok(!completion_count, "Unexpected completion count %ld.\n", completion_count); + + WriteFile(client, send_buf, TEST_BUF_LEN, &written_bytes, NULL); + completion_count = get_pending_msgs(completion); + ok(completion_count == 2, "Unexpected completion count %ld.\n", completion_count); + + status = pNtRemoveIoCompletion(completion, &key_context, &apc_context, &iosb, &timeout); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + ok(key_context == 19, "Got unexpected completion key %Id\n", key_context); + ok(apc_context == 20, "Got unexpected completion value %Id\n", apc_context); + ok(iosb.Information == 21, "Got unexpected iosb.Information %Id\n", iosb.Information); + ok(iosb.Status == STATUS_SUCCESS, "Got unexpected iosb.Status %#lx\n", iosb.Status); + ok(!memcmp(send_buf, recv_buf, TEST_BUF_LEN), + "Receive buffer (%02x %02x %02x) did not match send buffer (%02x %02x %02x)\n", recv_buf[0], + recv_buf[1], recv_buf[2], send_buf[0], send_buf[1], send_buf[2]); + completion_count = get_pending_msgs(completion); + ok(completion_count == 1, "Unexpected completion count %ld.\n", completion_count); + + status = pNtRemoveIoCompletion(completion, &key_context, &apc_context, &iosb, &timeout); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + ok(key_context == 22, "Got unexpected completion key %Id\n", key_context); + ok(apc_context == 23, "Got unexpected completion value %Id\n", apc_context); + ok(iosb.Information == 24, "Got unexpected iosb.Information %Id\n", iosb.Information); + ok(iosb.Status == STATUS_SUCCESS, "Got unexpected iosb.Status %#lx\n", iosb.Status); + completion_count = get_pending_msgs(completion); + ok(!completion_count, "Unexpected completion count %ld.\n", completion_count); + + /* Test associating non-file objects */ + status = pNtCreateEvent(&event, GENERIC_ALL, NULL, NotificationEvent, 0); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + + status = pNtAssociateWaitCompletionPacket(packet, completion, event, (void *)25, (void *)26, + STATUS_SUCCESS, (ULONG_PTR)27, &signaled); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + ok(signaled == FALSE, "Got unexpected signaled %d.\n", signaled); + completion_count = get_pending_msgs(completion); + ok(!completion_count, "Got unexpected completion count %ld.\n", completion_count); + + status = pNtPulseEvent(event, NULL); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + completion_count = get_pending_msgs(completion); + ok(completion_count == 1, "Got unexpected completion count %ld.\n", completion_count); + + status = pNtRemoveIoCompletion(completion, &key_context, &apc_context, &iosb, &timeout); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + ok(key_context == 25, "Got unexpected completion key %Id\n", key_context); + ok(apc_context == 26, "Got unexpected completion value %Id\n", apc_context); + ok(iosb.Information == 27, "Got unexpected iosb.Information %Id\n", iosb.Information); + ok(iosb.Status == STATUS_SUCCESS, "Got unexpected iosb.Status %#lx\n", iosb.Status); + completion_count = get_pending_msgs(completion); + ok(!completion_count, "Unexpected completion count %ld.\n", completion_count); + + /* Test closing wait completion packets before the target object is signaled */ + status = pNtAssociateWaitCompletionPacket(packet, completion, event, (void *)28, (void *)29, + STATUS_SUCCESS, (ULONG_PTR)30, &signaled); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + ok(signaled == FALSE, "Got unexpected signaled %d.\n", signaled); + completion_count = get_pending_msgs(completion); + ok(!completion_count, "Got unexpected completion count %ld.\n", completion_count); + + status = pNtClose(packet); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + + status = pNtPulseEvent(event, NULL); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + completion_count = get_pending_msgs(completion); + ok(!completion_count, "Got unexpected completion count %ld.\n", completion_count); + + /* Test closing wait completion packets right after the target object is signaled */ + InitializeObjectAttributes(&attr, &packet_name, 0, NULL, NULL); + status = pNtCreateWaitCompletionPacket(&packet, GENERIC_ALL, &attr); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + + status = pNtAssociateWaitCompletionPacket(packet, completion, event, (void *)31, (void *)32, + STATUS_SUCCESS, (ULONG_PTR)33, &signaled); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + ok(signaled == FALSE, "Got unexpected signaled %d.\n", signaled); + completion_count = get_pending_msgs(completion); + ok(!completion_count, "Got unexpected completion count %ld.\n", completion_count); + + status = pNtPulseEvent(event, NULL); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + completion_count = get_pending_msgs(completion); + ok(completion_count == 1, "Got unexpected completion count %ld.\n", completion_count); + + status = pNtClose(packet); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + completion_count = get_pending_msgs(completion); + ok(!completion_count, "Got unexpected completion count %ld.\n", completion_count); + + /* Parameter checks */ + InitializeObjectAttributes(&attr, &packet_name, 0, NULL, NULL); + status = pNtCreateWaitCompletionPacket(&packet, GENERIC_ALL, &attr); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + + status = pNtAssociateWaitCompletionPacket(NULL, completion, server, (void *)0xa, (void *)0xb, + STATUS_SUCCESS, (ULONG_PTR)0xc, &signaled); + ok(status == STATUS_INVALID_HANDLE, "Got unexpected status %#lx.\n", status); + + status = pNtAssociateWaitCompletionPacket(INVALID_HANDLE_VALUE, completion, server, (void *)0xa, + (void *)0xb, STATUS_SUCCESS, (ULONG_PTR)0xc, &signaled); + ok(status == STATUS_OBJECT_TYPE_MISMATCH, "Got unexpected status %#lx.\n", status); + + status = pNtAssociateWaitCompletionPacket(packet, NULL, server, (void *)0xa, (void *)0xb, + STATUS_SUCCESS, (ULONG_PTR)0xc, &signaled); + ok(status == STATUS_INVALID_HANDLE, "Got unexpected status %#lx.\n", status); + + status = pNtAssociateWaitCompletionPacket(packet, INVALID_HANDLE_VALUE, server, (void *)0xa, + (void *)0xb, STATUS_SUCCESS, (ULONG_PTR)0xc, &signaled); + ok(status == STATUS_OBJECT_TYPE_MISMATCH, "Got unexpected status %#lx.\n", status); + + status = pNtAssociateWaitCompletionPacket(packet, completion, NULL, (void *)0xa, (void *)0xb, + STATUS_SUCCESS, (ULONG_PTR)0xc, &signaled); + ok(status == STATUS_INVALID_HANDLE, "Got unexpected status %#lx.\n", status); + + status = pNtAssociateWaitCompletionPacket(packet, completion, server, (void *)0xa, (void *)0xb, + 0xdeadbeef, (ULONG_PTR)0xc, NULL); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + + status = pNtRemoveIoCompletion(completion, &key_context, &apc_context, &iosb, &timeout); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + ok(key_context == 0xa, "Got unexpected completion key %Id\n", key_context); + ok(apc_context == 0xb, "Got unexpected completion value %Id\n", apc_context); + ok(iosb.Information == 0xc, "Got unexpected iosb.Information %Id\n", iosb.Information); + ok(iosb.Status == 0xdeadbeef, "Got unexpected iosb.Status %#lx\n", iosb.Status); + completion_count = get_pending_msgs(completion); + ok(!completion_count, "Unexpected completion count %ld.\n", completion_count); + + /* This test associates the wait completion packet with INVALID_HANDLE_VALUE, which will never + * be signaled */ + status = pNtAssociateWaitCompletionPacket(packet, completion, INVALID_HANDLE_VALUE, (void *)0xa, + (void *)0xb, STATUS_SUCCESS, (ULONG_PTR)0xc, &signaled); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + + status = pNtRemoveIoCompletion(completion, &key_context, &apc_context, &iosb, &timeout); + ok(status == WAIT_TIMEOUT, "Got unexpected status %#lx.\n", status); + + status = pNtClose(event); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + status = pNtClose(packet2); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + status = pNtClose(packet); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + status = pNtClose(client); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + status = pNtClose(server); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + status = pNtClose(completion); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); +} + START_TEST(file) { HMODULE hkernel32 = GetModuleHandleA("kernel32.dll"); @@ -6091,6 +6573,8 @@ START_TEST(file) pRtlDosPathNameToNtPathName_U = (void *)GetProcAddress(hntdll, "RtlDosPathNameToNtPathName_U"); pRtlWow64EnableFsRedirectionEx = (void *)GetProcAddress(hntdll, "RtlWow64EnableFsRedirectionEx"); pNtAllocateReserveObject= (void *)GetProcAddress(hntdll, "NtAllocateReserveObject"); + pNtAssociateWaitCompletionPacket = (void *)GetProcAddress(hntdll, "NtAssociateWaitCompletionPacket"); + pNtCreateEvent = (void *)GetProcAddress(hntdll, "NtCreateEvent"); pNtCreateMailslotFile = (void *)GetProcAddress(hntdll, "NtCreateMailslotFile"); pNtCreateFile = (void *)GetProcAddress(hntdll, "NtCreateFile"); pNtCreateWaitCompletionPacket = (void *)GetProcAddress(hntdll, "NtCreateWaitCompletionPacket"); @@ -6104,6 +6588,7 @@ START_TEST(file) pNtFsControlFile = (void *)GetProcAddress(hntdll, "NtFsControlFile"); pNtCreateIoCompletion = (void *)GetProcAddress(hntdll, "NtCreateIoCompletion"); pNtOpenIoCompletion = (void *)GetProcAddress(hntdll, "NtOpenIoCompletion"); + pNtPulseEvent = (void *)GetProcAddress(hntdll, "NtPulseEvent"); pNtQueryIoCompletion = (void *)GetProcAddress(hntdll, "NtQueryIoCompletion"); pNtRemoveIoCompletion = (void *)GetProcAddress(hntdll, "NtRemoveIoCompletion"); pNtRemoveIoCompletionEx = (void *)GetProcAddress(hntdll, "NtRemoveIoCompletionEx"); @@ -6158,4 +6643,5 @@ START_TEST(file) test_mailslot_name(); test_reparse_points(); test_create_wait_completion_packet(); + test_associate_wait_completion_packet(); }
From: Zhiyi Zhang zzhang@codeweavers.com
--- dlls/ntdll/tests/file.c | 108 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+)
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c index a372bd170e1..4a308f3e577 100644 --- a/dlls/ntdll/tests/file.c +++ b/dlls/ntdll/tests/file.c @@ -54,6 +54,7 @@ static NTSTATUS (WINAPI *pRtlWow64EnableFsRedirectionEx)( ULONG, ULONG * ); static NTSTATUS (WINAPI *pNtAllocateReserveObject)( HANDLE *, const OBJECT_ATTRIBUTES *, MEMORY_RESERVE_OBJECT_TYPE ); static NTSTATUS (WINAPI *pNtAssociateWaitCompletionPacket)(HANDLE, HANDLE, HANDLE, PVOID, PVOID, NTSTATUS, ULONG_PTR, PBOOLEAN); +static NTSTATUS (WINAPI *pNtCancelWaitCompletionPacket)(HANDLE, BOOLEAN); static NTSTATUS (WINAPI *pNtCreateEvent)(PHANDLE, ACCESS_MASK, const OBJECT_ATTRIBUTES *, EVENT_TYPE, BOOLEAN); static NTSTATUS (WINAPI *pNtCreateMailslotFile)( PHANDLE, ULONG, POBJECT_ATTRIBUTES, PIO_STATUS_BLOCK, ULONG, ULONG, ULONG, PLARGE_INTEGER ); @@ -6555,6 +6556,111 @@ static void test_associate_wait_completion_packet(void) ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); }
+static void test_cancel_wait_completion_packet(void) +{ + static const char pipe_name[] = "\\.\pipe\test_cancel_wait_completion_packet"; + UNICODE_STRING packet_name = RTL_CONSTANT_STRING(L"\BaseNamedObjects\test_cancel_wait_completion_packet"); + BYTE send_buf[TEST_BUF_LEN], recv_buf[TEST_BUF_LEN]; + HANDLE completion, packet, server, client; + DWORD read_bytes, written_bytes; + OVERLAPPED overlapped = {0}; + OBJECT_ATTRIBUTES attr; + ULONG completion_count; + BOOLEAN signaled; + NTSTATUS status; + + if (!pNtCancelWaitCompletionPacket) + { + todo_wine + win_skip("NtCancelWaitCompletionPacket is unavailable.\n"); + return; + } + + status = pNtCreateIoCompletion(&completion, IO_COMPLETION_ALL_ACCESS, NULL, 0); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + + server = CreateNamedPipeA(pipe_name, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, + PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, 4, 1024, 1024, + 1000, NULL); + ok(server != INVALID_HANDLE_VALUE, "CreateNamedPipe failed, error %lu.\n", GetLastError()); + client = CreateFileA(pipe_name, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, + FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED, NULL); + ok(client != INVALID_HANDLE_VALUE, "CreateFile failed, error %lu.\n", GetLastError()); + + InitializeObjectAttributes(&attr, &packet_name, 0, NULL, NULL); + status = pNtCreateWaitCompletionPacket(&packet, GENERIC_ALL, &attr); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + + /* Remove not associated packets */ + status = pNtCancelWaitCompletionPacket(packet, TRUE); + ok(status == STATUS_CANCELLED, "Got unexpected status %#lx.\n", status); + + /* ReadFile() should be called before NtAssociateWaitCompletionPacket(). Otherwise, the server + * object stays signaled */ + memset(send_buf, 0xa1, TEST_BUF_LEN); + memset(recv_buf, 0xb2, TEST_BUF_LEN); + ReadFile(server, recv_buf, TEST_BUF_LEN, &read_bytes, &overlapped); + + /* Cancel non-signaled packets */ + status = pNtAssociateWaitCompletionPacket(packet, completion, server, (void *)1, (void *)2, + STATUS_SUCCESS, (ULONG_PTR)3, &signaled); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + ok(signaled == FALSE, "Got unexpected signaled %d.\n", signaled); + completion_count = get_pending_msgs(completion); + ok(!completion_count, "Got unexpected completion count %ld.\n", completion_count); + + status = pNtCancelWaitCompletionPacket(packet, FALSE); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + + WriteFile(client, send_buf, TEST_BUF_LEN, &written_bytes, NULL); + completion_count = get_pending_msgs(completion); + ok(!completion_count, "Got unexpected completion count %ld.\n", completion_count); + + /* Cancel signaled packets */ + ReadFile(server, recv_buf, TEST_BUF_LEN, &read_bytes, &overlapped); + + status = pNtAssociateWaitCompletionPacket(packet, completion, server, (void *)1, (void *)2, + STATUS_SUCCESS, (ULONG_PTR)3, &signaled); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + ok(signaled == FALSE, "Got unexpected signaled %d.\n", signaled); + completion_count = get_pending_msgs(completion); + ok(!completion_count, "Got unexpected completion count %ld.\n", completion_count); + + WriteFile(client, send_buf, TEST_BUF_LEN, &written_bytes, NULL); + completion_count = get_pending_msgs(completion); + ok(completion_count == 1, "Got unexpected completion count %ld.\n", completion_count); + + status = pNtCancelWaitCompletionPacket(packet, FALSE); + ok(status == STATUS_PENDING, "Got unexpected status %#lx.\n", status); + completion_count = get_pending_msgs(completion); + ok(completion_count == 1, "Got unexpected completion count %ld.\n", completion_count); + + status = pNtCancelWaitCompletionPacket(packet, TRUE); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + completion_count = get_pending_msgs(completion); + ok(!completion_count, "Got unexpected completion count %ld.\n", completion_count); + + /* Remove already removed packets */ + status = pNtCancelWaitCompletionPacket(packet, TRUE); + ok(status == STATUS_CANCELLED, "Got unexpected status %#lx.\n", status); + + /* Parameter checks */ + status = pNtCancelWaitCompletionPacket(NULL, FALSE); + ok(status == STATUS_INVALID_HANDLE, "Got unexpected status %#lx.\n", status); + + status = pNtCancelWaitCompletionPacket(INVALID_HANDLE_VALUE, FALSE); + ok(status == STATUS_OBJECT_TYPE_MISMATCH, "Got unexpected status %#lx.\n", status); + + status = pNtClose(packet); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + status = pNtClose(client); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + status = pNtClose(server); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); + status = pNtClose(completion); + ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); +} + START_TEST(file) { HMODULE hkernel32 = GetModuleHandleA("kernel32.dll"); @@ -6574,6 +6680,7 @@ START_TEST(file) pRtlWow64EnableFsRedirectionEx = (void *)GetProcAddress(hntdll, "RtlWow64EnableFsRedirectionEx"); pNtAllocateReserveObject= (void *)GetProcAddress(hntdll, "NtAllocateReserveObject"); pNtAssociateWaitCompletionPacket = (void *)GetProcAddress(hntdll, "NtAssociateWaitCompletionPacket"); + pNtCancelWaitCompletionPacket = (void *)GetProcAddress(hntdll, "NtCancelWaitCompletionPacket"); pNtCreateEvent = (void *)GetProcAddress(hntdll, "NtCreateEvent"); pNtCreateMailslotFile = (void *)GetProcAddress(hntdll, "NtCreateMailslotFile"); pNtCreateFile = (void *)GetProcAddress(hntdll, "NtCreateFile"); @@ -6644,4 +6751,5 @@ START_TEST(file) test_reparse_points(); test_create_wait_completion_packet(); test_associate_wait_completion_packet(); + test_cancel_wait_completion_packet(); }
From: Zhiyi Zhang zzhang@codeweavers.com
--- dlls/ntdll/ntdll.spec | 1 + dlls/ntdll/signal_arm64ec.c | 1 + dlls/ntdll/tests/om.c | 1 - dlls/ntdll/unix/sync.c | 24 ++++++++++++ dlls/wow64/sync.c | 20 ++++++++++ include/winternl.h | 1 + server/completion.c | 75 +++++++++++++++++++++++++++++++++++++ server/directory.c | 1 + server/file.h | 5 +++ server/object.h | 1 + server/protocol.def | 9 +++++ 11 files changed, 138 insertions(+), 1 deletion(-)
diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec index 3bb20691208..e529bd623c8 100644 --- a/dlls/ntdll/ntdll.spec +++ b/dlls/ntdll/ntdll.spec @@ -199,6 +199,7 @@ @ stdcall -syscall NtCreateToken(ptr long ptr long ptr ptr ptr ptr ptr ptr ptr ptr ptr) @ stdcall -syscall NtCreateTransaction(ptr long ptr ptr long long long long ptr ptr) @ stdcall -syscall NtCreateUserProcess(ptr ptr long long ptr ptr long long ptr ptr ptr) +@ stdcall -syscall NtCreateWaitCompletionPacket(ptr long ptr) # @ stub NtCreateWaitablePort @ stdcall -arch=i386 NtCurrentTeb() @ stdcall -syscall NtDebugActiveProcess(long long) diff --git a/dlls/ntdll/signal_arm64ec.c b/dlls/ntdll/signal_arm64ec.c index a2811c91af5..00195b41e88 100644 --- a/dlls/ntdll/signal_arm64ec.c +++ b/dlls/ntdll/signal_arm64ec.c @@ -294,6 +294,7 @@ DEFINE_SYSCALL(NtCreateThreadEx, (HANDLE *handle, ACCESS_MASK access, OBJECT_ATT DEFINE_SYSCALL(NtCreateTimer, (HANDLE *handle, ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr, TIMER_TYPE type)) DEFINE_SYSCALL(NtCreateToken, (HANDLE *handle, ACCESS_MASK access, OBJECT_ATTRIBUTES *attr, TOKEN_TYPE type, LUID *token_id, LARGE_INTEGER *expire, TOKEN_USER *user, TOKEN_GROUPS *groups, TOKEN_PRIVILEGES *privs, TOKEN_OWNER *owner, TOKEN_PRIMARY_GROUP *group, TOKEN_DEFAULT_DACL *dacl, TOKEN_SOURCE *source)) DEFINE_SYSCALL(NtCreateTransaction, (HANDLE *handle, ACCESS_MASK mask, OBJECT_ATTRIBUTES *obj_attr, GUID *guid, HANDLE tm, ULONG options, ULONG isol_level, ULONG isol_flags, PLARGE_INTEGER timeout, UNICODE_STRING *description)) +DEFINE_SYSCALL(NtCreateWaitCompletionPacket, (HANDLE *handle, ACCESS_MASK mask, OBJECT_ATTRIBUTES *obj_attr)) DEFINE_SYSCALL(NtCreateUserProcess, (HANDLE *process_handle_ptr, HANDLE *thread_handle_ptr, ACCESS_MASK process_access, ACCESS_MASK thread_access, OBJECT_ATTRIBUTES *process_attr, OBJECT_ATTRIBUTES *thread_attr, ULONG process_flags, ULONG thread_flags, RTL_USER_PROCESS_PARAMETERS *params, PS_CREATE_INFO *info, PS_ATTRIBUTE_LIST *ps_attr)) DEFINE_SYSCALL(NtDebugActiveProcess, (HANDLE process, HANDLE debug)) DEFINE_SYSCALL(NtDebugContinue, (HANDLE handle, CLIENT_ID *client, NTSTATUS status)) diff --git a/dlls/ntdll/tests/om.c b/dlls/ntdll/tests/om.c index 8f38b3eecd7..55c838d066d 100644 --- a/dlls/ntdll/tests/om.c +++ b/dlls/ntdll/tests/om.c @@ -2391,7 +2391,6 @@ static void test_object_types(void) break; }
- todo_wine_if(!lstrcmpW(tests[i].name, L"WaitCompletionPacket")) ok( broken(!lstrcmpW(tests[i].name, L"WaitCompletionPacket") && j == ARRAY_SIZE(all_types)) /* Win7 doesn't have WaitCompletionPacket */ || j < ARRAY_SIZE(all_types), "type %s not found\n", debugstr_w(tests[i].name) ); } diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c index d486b50001d..b9b883205d8 100644 --- a/dlls/ntdll/unix/sync.c +++ b/dlls/ntdll/unix/sync.c @@ -2794,6 +2794,30 @@ NTSTATUS WINAPI NtCreateTransaction( HANDLE *handle, ACCESS_MASK mask, OBJECT_AT return STATUS_SUCCESS; }
+/*********************************************************************** + * NtCreateWaitCompletionPacket (NTDLL.@) + */ +NTSTATUS WINAPI NtCreateWaitCompletionPacket( HANDLE *handle, ACCESS_MASK access, OBJECT_ATTRIBUTES *attr ) +{ + struct object_attributes *objattr; + unsigned int ret; + data_size_t len; + + *handle = 0; + if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; + + SERVER_START_REQ( create_wait_completion_packet ) + { + req->access = access; + wine_server_add_data( req, objattr, len ); + if (!(ret = wine_server_call( req ))) + *handle = wine_server_ptr_handle( reply->handle ); + } + SERVER_END_REQ; + free( objattr ); + return ret; +} + /*********************************************************************** * NtCommitTransaction (NTDLL.@) */ diff --git a/dlls/wow64/sync.c b/dlls/wow64/sync.c index 54dca19dc1a..2f516b8f40c 100644 --- a/dlls/wow64/sync.c +++ b/dlls/wow64/sync.c @@ -1781,6 +1781,26 @@ NTSTATUS WINAPI wow64_NtCreateTransaction( UINT *args ) return status; }
+/********************************************************************** + * wow64_NtCreateWaitCompletionPacket + */ +NTSTATUS WINAPI wow64_NtCreateWaitCompletionPacket( UINT *args ) +{ + ULONG *handle_ptr = get_ptr( &args ); + ACCESS_MASK access = get_ulong( &args ); + OBJECT_ATTRIBUTES32 *attr32 = get_ptr( &args ); + + struct object_attr64 attr; + HANDLE handle = 0; + NTSTATUS status; + + *handle_ptr = 0; + status = NtCreateWaitCompletionPacket( &handle, access, objattr_32to64( &attr, attr32 )); + put_handle( handle_ptr, handle ); + + return status; +} +
/********************************************************************** * wow64_NtCommitTransaction diff --git a/include/winternl.h b/include/winternl.h index 4926bd1bf82..aa2427b0f6a 100644 --- a/include/winternl.h +++ b/include/winternl.h @@ -4528,6 +4528,7 @@ NTSYSAPI NTSTATUS WINAPI NtCreateThreadEx(HANDLE*,ACCESS_MASK,OBJECT_ATTRIBUTES NTSYSAPI NTSTATUS WINAPI NtCreateTimer(HANDLE*, ACCESS_MASK, const OBJECT_ATTRIBUTES*, TIMER_TYPE); NTSYSAPI NTSTATUS WINAPI NtCreateToken(PHANDLE,ACCESS_MASK,POBJECT_ATTRIBUTES,TOKEN_TYPE,PLUID,PLARGE_INTEGER,PTOKEN_USER,PTOKEN_GROUPS,PTOKEN_PRIVILEGES,PTOKEN_OWNER,PTOKEN_PRIMARY_GROUP,PTOKEN_DEFAULT_DACL,PTOKEN_SOURCE); NTSYSAPI NTSTATUS WINAPI NtCreateTransaction(PHANDLE,ACCESS_MASK,POBJECT_ATTRIBUTES,LPGUID,HANDLE,ULONG,ULONG,ULONG,PLARGE_INTEGER,PUNICODE_STRING); +NTSYSAPI NTSTATUS WINAPI NtCreateWaitCompletionPacket(HANDLE *,ACCESS_MASK,OBJECT_ATTRIBUTES *); NTSYSAPI NTSTATUS WINAPI NtCreateUserProcess(HANDLE*,HANDLE*,ACCESS_MASK,ACCESS_MASK,OBJECT_ATTRIBUTES*,OBJECT_ATTRIBUTES*,ULONG,ULONG,RTL_USER_PROCESS_PARAMETERS*,PS_CREATE_INFO*,PS_ATTRIBUTE_LIST*); NTSYSAPI NTSTATUS WINAPI NtDebugActiveProcess(HANDLE,HANDLE); NTSYSAPI NTSTATUS WINAPI NtDebugContinue(HANDLE,CLIENT_ID*,NTSTATUS); diff --git a/server/completion.c b/server/completion.c index f4fad9dbeca..9edc3d0e041 100644 --- a/server/completion.c +++ b/server/completion.c @@ -36,6 +36,61 @@ #include "handle.h" #include "request.h"
+static const WCHAR wait_completion_packet_name[] = {'W','a','i','t','C','o','m','p','l','e','t','i','o','n','P','a','c','k','e','t'}; + +struct type_descr wait_completion_packet_type = +{ + { wait_completion_packet_name, sizeof(wait_completion_packet_name) }, /* name */ + WAIT_COMPLETION_PACKET_ALL_ACCESS, /* valid_access */ + { /* mapping */ + WAIT_COMPLETION_PACKET_GENERIC_READ, + WAIT_COMPLETION_PACKET_GENERIC_WRITE, + WAIT_COMPLETION_PACKET_GENERIC_EXECUTE, + WAIT_COMPLETION_PACKET_ALL_ACCESS + }, +}; + +static void wait_completion_packet_dump( struct object *, int ); + +static const struct object_ops wait_completion_packet_ops = +{ + sizeof(struct wait_completion_packet), /* size */ + &wait_completion_packet_type, /* type */ + wait_completion_packet_dump, /* dump */ + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ + default_map_access, /* map_access */ + default_get_sd, /* get_sd */ + default_set_sd, /* set_sd */ + default_get_full_name, /* get_full_name */ + no_lookup_name, /* lookup_name */ + directory_link_name, /* link_name */ + default_unlink_name, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ + no_close_handle, /* close_handle */ + no_destroy /* destroy */ +}; + +static void wait_completion_packet_dump( struct object *obj, int verbose ) +{ + struct wait_completion_packet *packet = (struct wait_completion_packet *)obj; + + assert( obj->ops == &wait_completion_packet_ops ); + fprintf( stderr, "WaitCompletionPacket\n" ); +} + +static struct wait_completion_packet *create_wait_completion_packet( struct object *root, + const struct unicode_str *name, + unsigned int attr, + const struct security_descriptor *sd ) +{ + return create_named_object( root, &wait_completion_packet_ops, name, attr, sd ); +}
static const WCHAR completion_name[] = {'I','o','C','o','m','p','l','e','t','i','o','n'};
@@ -310,6 +365,26 @@ void add_completion( struct completion *completion, apc_param_t ckey, apc_param_ if (!list_empty( &completion->queue )) wake_up( &completion->obj, 0 ); }
+/* create a wait completion packet */ +DECL_HANDLER(create_wait_completion_packet) +{ + struct wait_completion_packet *packet; + struct unicode_str name; + struct object *root; + const struct security_descriptor *sd; + const struct object_attributes *objattr = get_req_object_attributes( &sd, &name, &root ); + + if (!objattr) return; + if ((packet = create_wait_completion_packet( root, &name, objattr->attributes, sd )) + && get_error() != STATUS_OBJECT_NAME_EXISTS) + { + reply->handle = alloc_handle( current->process, packet, req->access, objattr->attributes ); + release_object( packet ); + } + + if (root) release_object( root ); +} + /* create a completion */ DECL_HANDLER(create_completion) { diff --git a/server/directory.c b/server/directory.c index b37ec969a9e..cbf53b12f3b 100644 --- a/server/directory.c +++ b/server/directory.c @@ -163,6 +163,7 @@ static struct type_descr *types[] = &key_type, &apc_reserve_type, &completion_reserve_type, + &wait_completion_packet_type, };
static void object_type_dump( struct object *obj, int verbose ) diff --git a/server/file.h b/server/file.h index 4f5fc7b26f1..5e988376e55 100644 --- a/server/file.h +++ b/server/file.h @@ -48,6 +48,11 @@ struct async_queue struct list queue; /* queue of async objects */ };
+struct wait_completion_packet +{ + struct object obj; /* object header */ +}; + /* operations valid on file descriptor objects */ struct fd_ops { diff --git a/server/object.h b/server/object.h index 7555f564a08..e02dbdc6b71 100644 --- a/server/object.h +++ b/server/object.h @@ -333,6 +333,7 @@ extern struct type_descr mapping_type; extern struct type_descr key_type; extern struct type_descr apc_reserve_type; extern struct type_descr completion_reserve_type; +extern struct type_descr wait_completion_packet_type;
#define KEYEDEVENT_WAIT 0x0001 #define KEYEDEVENT_WAKE 0x0002 diff --git a/server/protocol.def b/server/protocol.def index bd0307c5d79..5f9431d9517 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -3849,6 +3849,15 @@ struct handle_info @END
+/* create a wait completion packet */ +@REQ(create_wait_completion_packet) + unsigned int access; /* desired access */ + VARARG(objattr,object_attributes); /* object attributes */ +@REPLY + obj_handle_t handle; /* wait completion packet handle */ +@END + + /* check for associated completion and push msg */ @REQ(add_fd_completion) obj_handle_t handle; /* async' object */
From: Zhiyi Zhang zzhang@codeweavers.com
--- dlls/ntdll/ntdll.spec | 1 + dlls/ntdll/signal_arm64ec.c | 1 + dlls/ntdll/tests/file.c | 1 - dlls/ntdll/unix/sync.c | 28 +++++++ dlls/wow64/sync.c | 20 +++++ include/winternl.h | 1 + server/async.c | 2 +- server/completion.c | 162 +++++++++++++++++++++++++++++++----- server/fd.c | 2 +- server/file.h | 12 ++- server/object.c | 1 + server/object.h | 1 + server/process.c | 2 +- server/protocol.def | 14 ++++ server/thread.c | 32 +++++++ server/thread.h | 1 + 16 files changed, 253 insertions(+), 28 deletions(-)
diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec index e529bd623c8..2085be4aaf7 100644 --- a/dlls/ntdll/ntdll.spec +++ b/dlls/ntdll/ntdll.spec @@ -152,6 +152,7 @@ @ stdcall -syscall NtAllocateVirtualMemoryEx(long ptr ptr long long ptr long) @ stdcall -syscall NtAreMappedFilesTheSame(ptr ptr) @ stdcall -syscall NtAssignProcessToJobObject(long long) +@ stdcall -syscall NtAssociateWaitCompletionPacket(ptr ptr ptr ptr ptr long ptr ptr) @ stdcall -syscall NtCallbackReturn(ptr long long) # @ stub NtCancelDeviceWakeupRequest @ stdcall -syscall NtCancelIoFile(long ptr) diff --git a/dlls/ntdll/signal_arm64ec.c b/dlls/ntdll/signal_arm64ec.c index 00195b41e88..c33757b7ec0 100644 --- a/dlls/ntdll/signal_arm64ec.c +++ b/dlls/ntdll/signal_arm64ec.c @@ -257,6 +257,7 @@ DEFINE_WRAPPED_SYSCALL(NtAllocateVirtualMemory, (HANDLE process, PVOID *ret, ULO DEFINE_WRAPPED_SYSCALL(NtAllocateVirtualMemoryEx, (HANDLE process, PVOID *ret, SIZE_T *size_ptr, ULONG type, ULONG protect, MEM_EXTENDED_PARAMETER *parameters, ULONG count)) DEFINE_SYSCALL(NtAreMappedFilesTheSame, (PVOID addr1, PVOID addr2)) DEFINE_SYSCALL(NtAssignProcessToJobObject, (HANDLE job, HANDLE process)) +DEFINE_SYSCALL(NtAssociateWaitCompletionPacket,(HANDLE packet, HANDLE completion, HANDLE target, void *key_context, void *apc_context, NTSTATUS io_status, ULONG_PTR io_status_information, BOOLEAN *already_signaled)) DEFINE_SYSCALL(NtCallbackReturn, (void *ret_ptr, ULONG ret_len, NTSTATUS status)) DEFINE_SYSCALL(NtCancelIoFile, (HANDLE handle, IO_STATUS_BLOCK *io_status)) DEFINE_SYSCALL(NtCancelIoFileEx, (HANDLE handle, IO_STATUS_BLOCK *io, IO_STATUS_BLOCK *io_status)) diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c index 4a308f3e577..056167b7309 100644 --- a/dlls/ntdll/tests/file.c +++ b/dlls/ntdll/tests/file.c @@ -6100,7 +6100,6 @@ static void test_associate_wait_completion_packet(void)
if (!pNtAssociateWaitCompletionPacket) { - todo_wine win_skip("NtAssociateWaitCompletionPacket is unavailable.\n"); return; } diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c index b9b883205d8..16337a64daa 100644 --- a/dlls/ntdll/unix/sync.c +++ b/dlls/ntdll/unix/sync.c @@ -2794,6 +2794,34 @@ NTSTATUS WINAPI NtCreateTransaction( HANDLE *handle, ACCESS_MASK mask, OBJECT_AT return STATUS_SUCCESS; }
+/*********************************************************************** + * NtAssociateWaitCompletionPacket (NTDLL.@) + */ +NTSTATUS WINAPI NtAssociateWaitCompletionPacket( HANDLE packet, HANDLE completion, HANDLE target, + void *key_context, void *apc_context, NTSTATUS status, ULONG_PTR information, + BOOLEAN *already_signaled ) +{ + NTSTATUS ret; + + TRACE( "%p, %p, %p, %p, %p, %#x, %ld, %p stub.\n", packet, completion, target, key_context, + apc_context, (int)status, information, already_signaled ); + + SERVER_START_REQ( associate_wait_completion_packet ) + { + req->packet = wine_server_obj_handle( packet ); + req->completion = wine_server_obj_handle( completion ); + req->target = wine_server_obj_handle( target ); + req->ckey = wine_server_client_ptr( key_context ); + req->cvalue = wine_server_client_ptr( apc_context ); + req->information = information; + req->status = status; + if (!(ret = wine_server_call( req )) && already_signaled) + *already_signaled = reply->signaled; + } + SERVER_END_REQ; + return ret; +} + /*********************************************************************** * NtCreateWaitCompletionPacket (NTDLL.@) */ diff --git a/dlls/wow64/sync.c b/dlls/wow64/sync.c index 2f516b8f40c..d9bea95fd5e 100644 --- a/dlls/wow64/sync.c +++ b/dlls/wow64/sync.c @@ -1781,6 +1781,26 @@ NTSTATUS WINAPI wow64_NtCreateTransaction( UINT *args ) return status; }
+ +/********************************************************************** + * wow64_NtAssociateWaitCompletionPacket + */ +NTSTATUS WINAPI wow64_NtAssociateWaitCompletionPacket( UINT *args ) +{ + HANDLE packet = get_handle( &args ); + HANDLE completion = get_handle( &args ); + HANDLE target = get_handle( &args ); + void *key_context = get_ptr( &args ); + void *apc_context = get_ptr( &args ); + LONG io_status = get_ulong( &args ); + ULONG_PTR io_status_information = get_ulong( &args ); + BOOLEAN *already_signaled = get_ptr( &args ); + + return NtAssociateWaitCompletionPacket( packet, completion, target, key_context, apc_context, + io_status, io_status_information, already_signaled ); +} + + /********************************************************************** * wow64_NtCreateWaitCompletionPacket */ diff --git a/include/winternl.h b/include/winternl.h index aa2427b0f6a..6105cef9528 100644 --- a/include/winternl.h +++ b/include/winternl.h @@ -4487,6 +4487,7 @@ NTSYSAPI NTSTATUS WINAPI NtAllocateVirtualMemory(HANDLE,PVOID*,ULONG_PTR,SIZE_T NTSYSAPI NTSTATUS WINAPI NtAllocateVirtualMemoryEx(HANDLE,PVOID*,SIZE_T*,ULONG,ULONG,MEM_EXTENDED_PARAMETER*,ULONG); NTSYSAPI NTSTATUS WINAPI NtAreMappedFilesTheSame(PVOID,PVOID); NTSYSAPI NTSTATUS WINAPI NtAssignProcessToJobObject(HANDLE,HANDLE); +NTSYSAPI NTSTATUS WINAPI NtAssociateWaitCompletionPacket(HANDLE,HANDLE,HANDLE,void *,void *,NTSTATUS,ULONG_PTR,BOOLEAN *); NTSYSAPI NTSTATUS WINAPI NtCallbackReturn(PVOID,ULONG,NTSTATUS); NTSYSAPI NTSTATUS WINAPI NtCancelIoFile(HANDLE,PIO_STATUS_BLOCK); NTSYSAPI NTSTATUS WINAPI NtCancelIoFileEx(HANDLE,PIO_STATUS_BLOCK,PIO_STATUS_BLOCK); diff --git a/server/async.c b/server/async.c index d2d929c9709..67f7e6f47c9 100644 --- a/server/async.c +++ b/server/async.c @@ -481,7 +481,7 @@ static void add_async_completion( struct async *async, apc_param_t cvalue, unsig apc_param_t information ) { if (async->fd && !async->completion) async->completion = fd_get_completion( async->fd, &async->comp_key ); - if (async->completion) add_completion( async->completion, async->comp_key, cvalue, status, information ); + if (async->completion) add_completion( async->completion, async->comp_key, cvalue, status, information, NULL ); }
/* store the result of the client-side async callback */ diff --git a/server/completion.c b/server/completion.c index 9edc3d0e041..0c5b98851a9 100644 --- a/server/completion.c +++ b/server/completion.c @@ -36,6 +36,25 @@ #include "handle.h" #include "request.h"
+struct comp_msg +{ + struct wait_completion_packet *packet; /* if this is from a wait completion packet, store its pointer here */ + struct list queue_entry; + apc_param_t ckey; + apc_param_t cvalue; + apc_param_t information; + unsigned int status; +}; + +struct completion +{ + struct object obj; + struct list queue; + struct list wait_queue; + unsigned int depth; + int closed; +}; + static const WCHAR wait_completion_packet_name[] = {'W','a','i','t','C','o','m','p','l','e','t','i','o','n','P','a','c','k','e','t'};
struct type_descr wait_completion_packet_type = @@ -51,6 +70,7 @@ struct type_descr wait_completion_packet_type = };
static void wait_completion_packet_dump( struct object *, int ); +static void wait_completion_packet_destroy( struct object * );
static const struct object_ops wait_completion_packet_ops = { @@ -73,7 +93,7 @@ static const struct object_ops wait_completion_packet_ops = no_open_file, /* open_file */ no_kernel_obj_list, /* get_kernel_obj_list */ no_close_handle, /* close_handle */ - no_destroy /* destroy */ + wait_completion_packet_destroy /* destroy */ };
static void wait_completion_packet_dump( struct object *obj, int verbose ) @@ -81,7 +101,16 @@ static void wait_completion_packet_dump( struct object *obj, int verbose ) struct wait_completion_packet *packet = (struct wait_completion_packet *)obj;
assert( obj->ops == &wait_completion_packet_ops ); - fprintf( stderr, "WaitCompletionPacket\n" ); + fprintf( stderr, "WaitCompletionPacket target=%p completion=%p ckey=%llx cvalue=%llx " + "information=%llx status=%#x in_object_packet_queue=%d in_completion_queue=%d\n", + packet->target, packet->completion, (long long unsigned int)packet->ckey, + (long long unsigned int)packet->cvalue, (long long unsigned int)packet->information, + packet->status, packet->in_object_packet_queue, packet->in_completion_queue ); +} + +struct wait_completion_packet *get_wait_completion_packet_obj( struct process *process, obj_handle_t handle, unsigned int access ) +{ + return (struct wait_completion_packet *)get_handle_obj( process, handle, access, &wait_completion_packet_ops ); }
static struct wait_completion_packet *create_wait_completion_packet( struct object *root, @@ -89,7 +118,46 @@ static struct wait_completion_packet *create_wait_completion_packet( struct obje unsigned int attr, const struct security_descriptor *sd ) { - return create_named_object( root, &wait_completion_packet_ops, name, attr, sd ); + struct wait_completion_packet *packet; + + if ((packet = create_named_object( root, &wait_completion_packet_ops, name, attr, sd )) + && get_error() != STATUS_OBJECT_NAME_EXISTS) + { + packet->target = NULL; + packet->completion = NULL; + packet->ckey = 0; + packet->cvalue = 0; + packet->information = 0; + packet->status = 0; + packet->in_object_packet_queue = 0; + packet->in_completion_queue = 0; + } + return packet; +} + +static void wait_completion_packet_destroy( struct object *obj ) +{ + struct wait_completion_packet *packet = (struct wait_completion_packet *)obj; + struct comp_msg *comp_msg; + + if (packet->in_object_packet_queue) list_remove( &packet->entry ); + + if (packet->in_completion_queue) + { + LIST_FOR_EACH_ENTRY( comp_msg, &packet->completion->queue, struct comp_msg, queue_entry ) + { + if (comp_msg->packet == packet) + { + list_remove( &comp_msg->queue_entry ); + free( comp_msg ); + packet->completion->depth--; + break; + } + } + } + + if (packet->target) release_object( packet->target ); + if (packet->completion) release_object( packet->completion ); }
static const WCHAR completion_name[] = {'I','o','C','o','m','p','l','e','t','i','o','n'}; @@ -106,15 +174,6 @@ struct type_descr completion_type = }, };
-struct comp_msg -{ - struct list queue_entry; - apc_param_t ckey; - apc_param_t cvalue; - apc_param_t information; - unsigned int status; -}; - struct completion_wait { struct object obj; @@ -125,15 +184,6 @@ struct completion_wait struct list wait_queue_entry; };
-struct completion -{ - struct object obj; - struct list queue; - struct list wait_queue; - unsigned int depth; - int closed; -}; - static void completion_wait_dump( struct object*, int ); static int completion_wait_signaled( struct object *obj, struct wait_queue_entry *entry ); static void completion_wait_satisfied( struct object *obj, struct wait_queue_entry *entry ); @@ -342,7 +392,7 @@ struct completion *get_completion_obj( struct process *process, obj_handle_t han }
void add_completion( struct completion *completion, apc_param_t ckey, apc_param_t cvalue, - unsigned int status, apc_param_t information ) + unsigned int status, apc_param_t information, struct wait_completion_packet *packet ) { struct comp_msg *msg = mem_alloc( sizeof( *msg ) ); struct completion_wait *wait; @@ -350,6 +400,7 @@ void add_completion( struct completion *completion, apc_param_t ckey, apc_param_ if (!msg) return;
+ msg->packet = packet; msg->ckey = ckey; msg->cvalue = cvalue; msg->status = status; @@ -385,6 +436,65 @@ DECL_HANDLER(create_wait_completion_packet) if (root) release_object( root ); }
+/* associate a wait completion packet */ +DECL_HANDLER(associate_wait_completion_packet) +{ + struct wait_completion_packet *packet; + struct completion *completion; + struct object *target; + + packet = get_wait_completion_packet_obj( current->process, req->packet, WAIT_COMPLETION_PACKET_QUERY_STATE ); + if (!packet) + return; + + if (packet->in_object_packet_queue || packet->in_completion_queue) + { + release_object( packet ); + set_error( STATUS_INVALID_PARAMETER_1 ); + return; + } + + completion = get_completion_obj( current->process, req->completion, IO_COMPLETION_MODIFY_STATE ); + if (!completion) + { + release_object( packet ); + return; + } + + target = get_handle_obj( current->process, req->target, 0, NULL ); + if (!target) + { + release_object( completion ); + release_object( packet ); + return; + } + + packet->completion = (struct completion *)grab_object( completion ); + packet->ckey = req->ckey; + packet->cvalue = req->cvalue; + packet->information = req->information; + packet->status = req->status; + + if (is_obj_signaled( target )) + { + add_completion( packet->completion, packet->ckey, packet->cvalue, packet->status, + packet->information, packet ); + packet->in_completion_queue = 1; + reply->signaled = 1; + } + else + { + packet->target = grab_object( target ); + list_add_tail( &target->wait_completion_packet_queue, &packet->entry ); + packet->in_object_packet_queue = 1; + reply->signaled = 0; + } + + release_object( packet ); + release_object( target ); + release_object( completion ); +} + /* create a completion */ DECL_HANDLER(create_completion) { @@ -429,7 +539,7 @@ DECL_HANDLER(add_completion) return; }
- add_completion( completion, req->ckey, req->cvalue, req->status, req->information ); + add_completion( completion, req->ckey, req->cvalue, req->status, req->information, NULL );
if (reserve) release_object( reserve ); release_object( completion ); @@ -477,6 +587,12 @@ DECL_HANDLER(remove_completion) reply->cvalue = msg->cvalue; reply->status = msg->status; reply->information = msg->information; + if (msg->packet) + { + release_object( msg->packet->completion ); + msg->packet->completion = NULL; + msg->packet->in_completion_queue = 0; + } free( msg ); reply->wait_handle = 0; } diff --git a/server/fd.c b/server/fd.c index dc2475b2d28..f5fbab6c224 100644 --- a/server/fd.c +++ b/server/fd.c @@ -2925,7 +2925,7 @@ DECL_HANDLER(add_fd_completion) if (fd) { if (fd->completion && (req->async || !(fd->comp_flags & FILE_SKIP_COMPLETION_PORT_ON_SUCCESS))) - add_completion( fd->completion, fd->comp_key, req->cvalue, req->status, req->information ); + add_completion( fd->completion, fd->comp_key, req->cvalue, req->status, req->information, NULL ); release_object( fd ); } } diff --git a/server/file.h b/server/file.h index 5e988376e55..557edaefc88 100644 --- a/server/file.h +++ b/server/file.h @@ -51,6 +51,16 @@ struct async_queue struct wait_completion_packet { struct object obj; /* object header */ + struct list entry; /* list entry in the target object packet list */ + struct object *target; /* target object */ + struct completion *completion; /* completion object */ + apc_param_t ckey; /* key context */ + apc_param_t cvalue; /* apc context */ + apc_param_t information; /* IO_STATUS_BLOCK information */ + unsigned int status; /* completion status */ + unsigned int in_object_packet_queue: 1; /* whether the packet is in the target object queue */ + unsigned int in_completion_queue: 1; /* whether the packet is in the completion queue */ + unsigned int pad: 30; /* padding */ };
/* operations valid on file descriptor objects */ @@ -244,7 +254,7 @@ extern struct dir *get_dir_obj( struct process *process, obj_handle_t handle, un extern struct completion *get_completion_obj( struct process *process, obj_handle_t handle, unsigned int access ); extern struct reserve *get_completion_reserve_obj( struct process *process, obj_handle_t handle, unsigned int access ); extern void add_completion( struct completion *completion, apc_param_t ckey, apc_param_t cvalue, - unsigned int status, apc_param_t information ); + unsigned int status, apc_param_t information, struct wait_completion_packet *packet ); extern void cleanup_thread_completion( struct thread *thread );
/* serial port functions */ diff --git a/server/object.c b/server/object.c index cd368ef724a..8c9708abf7d 100644 --- a/server/object.c +++ b/server/object.c @@ -308,6 +308,7 @@ void *alloc_object( const struct object_ops *ops ) obj->name = NULL; obj->sd = NULL; list_init( &obj->wait_queue ); + list_init( &obj->wait_completion_packet_queue ); #ifdef DEBUG_OBJECTS list_add_head( &object_list, &obj->obj_list ); #endif diff --git a/server/object.h b/server/object.h index e02dbdc6b71..d29aa4c346a 100644 --- a/server/object.h +++ b/server/object.h @@ -115,6 +115,7 @@ struct object unsigned int handle_count;/* handle count */ const struct object_ops *ops; struct list wait_queue; + struct list wait_completion_packet_queue; struct object_name *name; struct security_descriptor *sd; unsigned int is_permanent:1; diff --git a/server/process.c b/server/process.c index 737b37d9441..c0ab5ed7663 100644 --- a/server/process.c +++ b/server/process.c @@ -266,7 +266,7 @@ static struct job *get_job_obj( struct process *process, obj_handle_t handle, un static void add_job_completion( struct job *job, apc_param_t msg, apc_param_t pid ) { if (job->completion_port) - add_completion( job->completion_port, job->completion_key, pid, STATUS_SUCCESS, msg ); + add_completion( job->completion_port, job->completion_key, pid, STATUS_SUCCESS, msg, NULL ); }
static void add_job_completion_existing_processes( struct job *job, struct job *completion_job ) diff --git a/server/protocol.def b/server/protocol.def index 5f9431d9517..1c5ac39cce9 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -3858,6 +3858,20 @@ struct handle_info @END
+/* Associate a wait completion packet */ +@REQ(associate_wait_completion_packet) + obj_handle_t packet; /* wait completion packet handle */ + obj_handle_t completion; /* completion handle */ + obj_handle_t target; /* target object handle */ + apc_param_t ckey; /* completion key */ + apc_param_t cvalue; /* completion value */ + apc_param_t information; /* IO_STATUS_BLOCK information */ + unsigned int status; /* completion status */ +@REPLY + int signaled; /* whether the object is already signaled */ +@END + + /* check for associated completion and push msg */ @REQ(add_fd_completion) obj_handle_t handle; /* async' object */ diff --git a/server/thread.c b/server/thread.c index b3ce5d9ac95..2f25859d7a1 100644 --- a/server/thread.c +++ b/server/thread.c @@ -1059,12 +1059,44 @@ static int select_on( const union select_op *select_op, data_size_t op_size, cli return 0; }
+int is_obj_signaled( struct object *obj ) +{ + struct wait_queue_entry wait_entry; + struct thread_wait wait = {0}; + + if (!obj->ops->signaled) + return 0; + + wait.thread = current; + list_init( &wait_entry.entry ); + wait_entry.obj = obj; + wait_entry.wait = &wait; + return obj->ops->signaled( obj, &wait_entry ); +} + /* attempt to wake threads sleeping on the object wait queue */ void wake_up( struct object *obj, int max ) { struct list *ptr; int ret;
+ if (is_obj_signaled( obj )) + { + struct wait_completion_packet *packet; + + LIST_FOR_EACH_ENTRY( packet, &obj->wait_completion_packet_queue, struct wait_completion_packet, entry ) + { + list_remove( &packet->entry ); + release_object( packet->target ); + packet->in_object_packet_queue = 0; + packet->target = NULL; + + add_completion( packet->completion, packet->ckey, packet->cvalue, packet->status, + packet->information, packet ); + packet->in_completion_queue = 1; + } + } + LIST_FOR_EACH( ptr, &obj->wait_queue ) { struct wait_queue_entry *entry = LIST_ENTRY( ptr, struct wait_queue_entry, entry ); diff --git a/server/thread.h b/server/thread.h index 754e617b484..26eb17866e4 100644 --- a/server/thread.h +++ b/server/thread.h @@ -116,6 +116,7 @@ extern int wake_thread_queue_entry( struct wait_queue_entry *entry ); extern int add_queue( struct object *obj, struct wait_queue_entry *entry ); extern void remove_queue( struct object *obj, struct wait_queue_entry *entry ); extern void kill_thread( struct thread *thread, int violent_death ); +extern int is_obj_signaled( struct object *obj ); extern void wake_up( struct object *obj, int max ); extern int thread_queue_apc( struct process *process, struct thread *thread, struct object *owner, const union apc_call *call_data ); extern void thread_cancel_apc( struct thread *thread, struct object *owner, enum apc_type type );
From: Zhiyi Zhang zzhang@codeweavers.com
--- dlls/ntdll/ntdll.spec | 1 + dlls/ntdll/signal_arm64ec.c | 1 + dlls/ntdll/tests/file.c | 1 - dlls/ntdll/unix/sync.c | 20 ++++++++++++ dlls/wow64/sync.c | 12 +++++++ include/winternl.h | 1 + server/completion.c | 62 +++++++++++++++++++++++++++++++++++++ server/protocol.def | 7 +++++ 8 files changed, 104 insertions(+), 1 deletion(-)
diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec index 2085be4aaf7..8132ff23cea 100644 --- a/dlls/ntdll/ntdll.spec +++ b/dlls/ntdll/ntdll.spec @@ -159,6 +159,7 @@ @ stdcall -syscall NtCancelIoFileEx(long ptr ptr) @ stdcall -syscall NtCancelSynchronousIoFile(long ptr ptr) @ stdcall -syscall NtCancelTimer(long ptr) +@ stdcall -syscall NtCancelWaitCompletionPacket(ptr long) @ stdcall -syscall NtClearEvent(long) @ stdcall -syscall NtClose(long) # @ stub NtCloseObjectAuditAlarm diff --git a/dlls/ntdll/signal_arm64ec.c b/dlls/ntdll/signal_arm64ec.c index c33757b7ec0..0662bc300a6 100644 --- a/dlls/ntdll/signal_arm64ec.c +++ b/dlls/ntdll/signal_arm64ec.c @@ -263,6 +263,7 @@ DEFINE_SYSCALL(NtCancelIoFile, (HANDLE handle, IO_STATUS_BLOCK *io_status)) DEFINE_SYSCALL(NtCancelIoFileEx, (HANDLE handle, IO_STATUS_BLOCK *io, IO_STATUS_BLOCK *io_status)) DEFINE_SYSCALL(NtCancelSynchronousIoFile, (HANDLE handle, IO_STATUS_BLOCK *io, IO_STATUS_BLOCK *io_status)) DEFINE_SYSCALL(NtCancelTimer, (HANDLE handle, BOOLEAN *state)) +DEFINE_SYSCALL(NtCancelWaitCompletionPacket, (HANDLE packet, BOOLEAN remove_signaled)) DEFINE_SYSCALL(NtClearEvent, (HANDLE handle)) DEFINE_SYSCALL(NtClose, (HANDLE handle)) DEFINE_SYSCALL(NtCommitTransaction, (HANDLE transaction, BOOLEAN wait)) diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c index 056167b7309..2cb0c02a83d 100644 --- a/dlls/ntdll/tests/file.c +++ b/dlls/ntdll/tests/file.c @@ -6570,7 +6570,6 @@ static void test_cancel_wait_completion_packet(void)
if (!pNtCancelWaitCompletionPacket) { - todo_wine win_skip("NtCancelWaitCompletionPacket is unavailable.\n"); return; } diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c index 16337a64daa..6fa28225cb9 100644 --- a/dlls/ntdll/unix/sync.c +++ b/dlls/ntdll/unix/sync.c @@ -2822,6 +2822,26 @@ NTSTATUS WINAPI NtAssociateWaitCompletionPacket( HANDLE packet, HANDLE completio return ret; }
+/*********************************************************************** + * NtCancelWaitCompletionPacket (NTDLL.@) + */ +NTSTATUS WINAPI NtCancelWaitCompletionPacket( HANDLE packet, BOOLEAN remove_signaled ) +{ + NTSTATUS status; + + TRACE("packet %p, remove_signaled %d.\n", packet, remove_signaled); + + SERVER_START_REQ( cancel_wait_completion_packet ) + { + req->packet = wine_server_obj_handle( packet ); + req->remove_signaled = remove_signaled; + status = wine_server_call( req ); + } + SERVER_END_REQ; + return status; +} + + /*********************************************************************** * NtCreateWaitCompletionPacket (NTDLL.@) */ diff --git a/dlls/wow64/sync.c b/dlls/wow64/sync.c index d9bea95fd5e..7362adde7a4 100644 --- a/dlls/wow64/sync.c +++ b/dlls/wow64/sync.c @@ -1801,6 +1801,18 @@ NTSTATUS WINAPI wow64_NtAssociateWaitCompletionPacket( UINT *args ) }
+/********************************************************************** + * wow64_NtCancelWaitCompletionPacket + */ +NTSTATUS WINAPI wow64_NtCancelWaitCompletionPacket( UINT *args ) +{ + HANDLE packet = get_handle( &args ); + BOOLEAN remove_signaled = get_ulong( &args ); + + return NtCancelWaitCompletionPacket( packet, remove_signaled ); +} + + /********************************************************************** * wow64_NtCreateWaitCompletionPacket */ diff --git a/include/winternl.h b/include/winternl.h index 6105cef9528..3f74a18c4aa 100644 --- a/include/winternl.h +++ b/include/winternl.h @@ -4493,6 +4493,7 @@ NTSYSAPI NTSTATUS WINAPI NtCancelIoFile(HANDLE,PIO_STATUS_BLOCK); NTSYSAPI NTSTATUS WINAPI NtCancelIoFileEx(HANDLE,PIO_STATUS_BLOCK,PIO_STATUS_BLOCK); NTSYSAPI NTSTATUS WINAPI NtCancelSynchronousIoFile(HANDLE,PIO_STATUS_BLOCK,PIO_STATUS_BLOCK); NTSYSAPI NTSTATUS WINAPI NtCancelTimer(HANDLE, BOOLEAN*); +NTSYSAPI NTSTATUS WINAPI NtCancelWaitCompletionPacket(HANDLE, BOOLEAN); NTSYSAPI NTSTATUS WINAPI NtClearEvent(HANDLE); NTSYSAPI NTSTATUS WINAPI NtClose(HANDLE); NTSYSAPI NTSTATUS WINAPI NtCloseObjectAuditAlarm(PUNICODE_STRING,HANDLE,BOOLEAN); diff --git a/server/completion.c b/server/completion.c index 0c5b98851a9..8de1d8f6d72 100644 --- a/server/completion.c +++ b/server/completion.c @@ -495,6 +495,68 @@ DECL_HANDLER(associate_wait_completion_packet) release_object( completion ); }
+/* cancel a wait completion packet */ +DECL_HANDLER(cancel_wait_completion_packet) +{ + struct wait_completion_packet *packet; + + packet = get_wait_completion_packet_obj( current->process, req->packet, WAIT_COMPLETION_PACKET_QUERY_STATE ); + if (!packet) + return; + + if (!packet->in_object_packet_queue && !packet->in_completion_queue) + { + set_error( STATUS_CANCELLED ); + release_object( packet ); + return; + } + + if (packet->in_completion_queue && !req->remove_signaled) + { + set_error( STATUS_PENDING ); + release_object( packet ); + return; + } + + if (packet->in_object_packet_queue) + { + list_remove( &packet->entry ); + packet->in_object_packet_queue = 0; + } + + if (packet->in_completion_queue) + { + struct comp_msg *comp_msg; + + LIST_FOR_EACH_ENTRY( comp_msg, &packet->completion->queue, struct comp_msg, queue_entry ) + { + if (comp_msg->packet == packet) + { + list_remove( &comp_msg->queue_entry ); + free( comp_msg ); + packet->completion->depth--; + break; + } + } + + packet->in_completion_queue = 0; + } + + if (packet->target) + { + release_object( packet->target ); + packet->target = NULL; + } + + if (packet->completion) + { + release_object( packet->completion ); + packet->completion = NULL; + } + + release_object( packet ); +} + /* create a completion */ DECL_HANDLER(create_completion) { diff --git a/server/protocol.def b/server/protocol.def index 1c5ac39cce9..cca69edef17 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -3872,6 +3872,13 @@ struct handle_info @END
+/* Cancel a wait completion packet */ +@REQ(cancel_wait_completion_packet) + obj_handle_t packet; /* wait completion packet handle */ + int remove_signaled;/* whether to remove signaled packet */ +@END + + /* check for associated completion and push msg */ @REQ(add_fd_completion) obj_handle_t handle; /* async' object */
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=149982
Your paranoid android.
=== debian11 (build log) ===
/usr/bin/i686-w64-mingw32-ld: tmp677dd032/ntdll-00000000.spec.o:fake:(.edata+0x218): undefined reference to `NtAssociateWaitCompletionPacket@32' /usr/bin/i686-w64-mingw32-ld: tmp677dd032/ntdll-00000000.spec.o:fake:(.edata+0x230): undefined reference to `NtCancelWaitCompletionPacket@8' /usr/bin/i686-w64-mingw32-ld: tmp677dd032/ntdll-00000000.spec.o:fake:(.edata+0x2b8): undefined reference to `NtCreateWaitCompletionPacket@12' collect2: error: ld returned 1 exit status Task: The win32 Wine build failed
=== debian11b (build log) ===
/usr/bin/x86_64-w64-mingw32-ld: tmp6774308b/ntdll-00000000.spec.o:fake:(.edata+0x218): undefined reference to `NtAssociateWaitCompletionPacket' /usr/bin/x86_64-w64-mingw32-ld: tmp6774308b/ntdll-00000000.spec.o:fake:(.edata+0x230): undefined reference to `NtCancelWaitCompletionPacket' /usr/bin/x86_64-w64-mingw32-ld: tmp6774308b/ntdll-00000000.spec.o:fake:(.edata+0x2b8): undefined reference to `NtCreateWaitCompletionPacket' collect2: error: ld returned 1 exit status Task: The wow64 Wine build failed