diff --git a/dlls/kernel32/sync.c b/dlls/kernel32/sync.c index 4e85aed0284..fcf1be70901 100644 --- a/dlls/kernel32/sync.c +++ b/dlls/kernel32/sync.c @@ -1715,29 +1715,41 @@ BOOL WINAPI DisconnectNamedPipe(HANDLE hPipe) /*********************************************************************** * TransactNamedPipe (KERNEL32.@) - * - * BUGS - * should be done as a single operation in the wineserver or kernel */ BOOL WINAPI TransactNamedPipe( HANDLE handle, LPVOID write_buf, DWORD write_size, LPVOID read_buf, DWORD read_size, LPDWORD bytes_read, LPOVERLAPPED overlapped) { - BOOL r; - DWORD count; + IO_STATUS_BLOCK default_iosb, *iosb = &default_iosb; + HANDLE event = NULL; + void *cvalue = NULL; + NTSTATUS status; - TRACE("%p %p %d %p %d %p %p\n", - handle, write_buf, write_size, read_buf, + TRACE("%p %p %u %p %u %p %p\n", handle, write_buf, write_size, read_buf, read_size, bytes_read, overlapped); if (overlapped) - FIXME("Doesn't support overlapped operation as yet\n"); + { + event = overlapped->hEvent; + iosb = (IO_STATUS_BLOCK *)overlapped; + if (((ULONG_PTR)event & 1) == 0) cvalue = overlapped; + } + else + { + iosb->Information = 0; + } - r = WriteFile(handle, write_buf, write_size, &count, NULL); - if (r) - r = ReadFile(handle, read_buf, read_size, bytes_read, NULL); + status = NtFsControlFile(handle, event, NULL, cvalue, iosb, FSCTL_PIPE_TRANSCEIVE, + write_buf, write_size, read_buf, read_size); - return r; + if (bytes_read) *bytes_read = overlapped && status ? 0 : iosb->Information; + + if (status) + { + SetLastError(RtlNtStatusToDosError(status)); + return FALSE; + } + return TRUE; } /*********************************************************************** diff --git a/dlls/kernel32/tests/pipe.c b/dlls/kernel32/tests/pipe.c index 22d120fc4d3..fa677cfdf9c 100644 --- a/dlls/kernel32/tests/pipe.c +++ b/dlls/kernel32/tests/pipe.c @@ -3011,6 +3011,44 @@ static void test_blocking_rw(HANDLE writer, HANDLE reader, DWORD buf_size, BOOL test_flush_done(flush_thread); } +#define overlapped_transact(a,b,c,d,e,f) _overlapped_transact(__LINE__,a,b,c,d,e,f) +static void _overlapped_transact(unsigned line, HANDLE caller, void *write_buf, DWORD write_size, + void *read_buf, DWORD read_size, OVERLAPPED *overlapped) +{ + BOOL res; + + memset(overlapped, 0, sizeof(*overlapped)); + overlapped->hEvent = CreateEventW(NULL, TRUE, FALSE, NULL); + res = TransactNamedPipe(caller, write_buf, write_size, read_buf, read_size, NULL, overlapped); + ok_(__FILE__,line)(!res && GetLastError() == ERROR_IO_PENDING, + "TransactNamedPipe returned: %x(%u)\n", res, GetLastError()); +} + +#define overlapped_transact_failure(a,b,c,d,e,f) _overlapped_transact_failure(__LINE__,a,b,c,d,e,f) +static void _overlapped_transact_failure(unsigned line, HANDLE caller, void *write_buf, DWORD write_size, + void *read_buf, DWORD read_size, DWORD expected_error) +{ + OVERLAPPED overlapped; + BOOL res; + + memset(&overlapped, 0, sizeof(overlapped)); + overlapped.hEvent = CreateEventW(NULL, TRUE, FALSE, NULL); + res = TransactNamedPipe(caller, write_buf, write_size, read_buf, read_size, NULL, &overlapped); + ok_(__FILE__,line)(!res, "TransactNamedPipe succeeded\n"); + + if (GetLastError() == ERROR_IO_PENDING) /* win8+ */ + { + _test_overlapped_failure(line, caller, &overlapped, expected_error); + } + else + { + ok_(__FILE__,line)(GetLastError() == expected_error, + "TransactNamedPipe returned error %u, expected %u\n", + GetLastError(), expected_error); + CloseHandle(overlapped.hEvent); + } +} + static void child_process_write_pipe(HANDLE pipe) { OVERLAPPED overlapped; @@ -3146,6 +3184,105 @@ static void test_overlapped_transport(BOOL msg_mode, BOOL msg_read_mode) CloseHandle(client); } +static void test_transact(HANDLE caller, HANDLE callee, DWORD write_buf_size, DWORD read_buf_size) +{ + OVERLAPPED overlapped, overlapped2, read_overlapped, write_overlapped; + char buf[10000], read_buf[10000]; + + memset(buf, 0xaa, sizeof(buf)); + + /* simple transact call */ + overlapped_transact(caller, (BYTE*)"abc", 3, read_buf, 100, &overlapped); + overlapped_write_sync(callee, (BYTE*)"test", 4); + test_overlapped_result(caller, &overlapped, 4, FALSE); + ok(!memcmp(read_buf, "test", 4), "unexpected read_buf\n"); + overlapped_read_sync(callee, read_buf, 1000, 3, FALSE); + ok(!memcmp(read_buf, "abc", 3), "unexpected read_buf\n"); + + /* transact fails if there is already data in read buffer */ + overlapped_write_sync(callee, buf, 1); + overlapped_transact_failure(caller, buf, 2, read_buf, 1, ERROR_PIPE_BUSY); + overlapped_read_sync(caller, read_buf, 1000, 1, FALSE); + + /* transact doesn't block on write */ + overlapped_write_async(caller, buf, write_buf_size+2000, &write_overlapped); + overlapped_transact(caller, buf, 2, read_buf, 1, &overlapped); + test_not_signaled(overlapped.hEvent); + overlapped_write_sync(callee, buf, 1); + test_overlapped_result(caller, &overlapped, 1, FALSE); + overlapped_read_sync(callee, read_buf, sizeof(read_buf), write_buf_size+2000, FALSE); + test_overlapped_result(caller, &write_overlapped, write_buf_size+2000, FALSE); + overlapped_read_sync(callee, read_buf, sizeof(read_buf), 2, FALSE); + + /* transact with already pending read */ + overlapped_read_async(callee, read_buf, 10, &read_overlapped); + overlapped_transact(caller, buf, 5, read_buf, 6, &overlapped); + test_overlapped_result(callee, &read_overlapped, 5, FALSE); + test_not_signaled(overlapped.hEvent); + overlapped_write_sync(callee, buf, 10); + test_overlapped_result(caller, &overlapped, 6, TRUE); + overlapped_read_sync(caller, read_buf, sizeof(read_buf), 4, FALSE); + + /* 0-size messages */ + overlapped_transact(caller, buf, 5, read_buf, 0, &overlapped); + overlapped_read_sync(callee, read_buf, sizeof(read_buf), 5, FALSE); + overlapped_write_sync(callee, buf, 0); + test_overlapped_result(caller, &overlapped, 0, FALSE); + + overlapped_transact(caller, buf, 0, read_buf, 0, &overlapped); + overlapped_read_sync(callee, read_buf, sizeof(read_buf), 0, FALSE); + test_not_signaled(overlapped.hEvent); + overlapped_write_sync(callee, buf, 0); + test_overlapped_result(caller, &overlapped, 0, FALSE); + + /* reply transact with another transact */ + overlapped_transact(caller, buf, 3, read_buf, 100, &overlapped); + overlapped_read_sync(callee, read_buf, 1000, 3, FALSE); + overlapped_transact(callee, buf, 4, read_buf, 100, &overlapped2); + test_overlapped_result(caller, &overlapped, 4, FALSE); + overlapped_write_sync(caller, buf, 1); + test_overlapped_result(caller, &overlapped2, 1, FALSE); + + if (!pCancelIoEx) return; + + /* cancel keeps written data */ + overlapped_write_async(caller, buf, write_buf_size+2000, &write_overlapped); + overlapped_transact(caller, buf, 2, read_buf, 1, &overlapped); + test_not_signaled(overlapped.hEvent); + cancel_overlapped(caller, &overlapped); + overlapped_read_sync(callee, read_buf, sizeof(read_buf), write_buf_size+2000, FALSE); + test_overlapped_result(caller, &write_overlapped, write_buf_size+2000, FALSE); + overlapped_read_sync(callee, read_buf, sizeof(read_buf), 2, FALSE); +} + +static void test_TransactNamedPipe(void) +{ + HANDLE client, server; + BYTE buf[10]; + + create_overlapped_pipe(PIPE_TYPE_BYTE, &client, &server); + overlapped_transact_failure(client, buf, 2, buf, 1, ERROR_BAD_PIPE); + CloseHandle(client); + CloseHandle(server); + + create_overlapped_pipe(PIPE_TYPE_MESSAGE | PIPE_READMODE_BYTE, &client, &server); + overlapped_transact_failure(client, buf, 2, buf, 1, ERROR_BAD_PIPE); + CloseHandle(client); + CloseHandle(server); + + trace("testing server->client transaction...\n"); + create_overlapped_pipe(PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE, &client, &server); + test_transact(server, client, 5000, 6000); + CloseHandle(client); + CloseHandle(server); + + trace("testing client->server transaction...\n"); + create_overlapped_pipe(PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE, &client, &server); + test_transact(client, server, 6000, 5000); + CloseHandle(client); + CloseHandle(server); +} + START_TEST(pipe) { char **argv; @@ -3186,4 +3323,5 @@ START_TEST(pipe) test_overlapped_transport(TRUE, FALSE); test_overlapped_transport(TRUE, TRUE); test_overlapped_transport(FALSE, FALSE); + test_TransactNamedPipe(); }