Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/ws2_32/tests/afd.c | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+)
diff --git a/dlls/ws2_32/tests/afd.c b/dlls/ws2_32/tests/afd.c index da42f878367..a02b4f3da27 100644 --- a/dlls/ws2_32/tests/afd.c +++ b/dlls/ws2_32/tests/afd.c @@ -39,6 +39,43 @@ static void set_blocking(SOCKET s, ULONG blocking) ok(!ret, "got error %u\n", WSAGetLastError()); }
+static void test_open_device(void) +{ + OBJECT_BASIC_INFORMATION info; + OBJECT_ATTRIBUTES attr; + UNICODE_STRING string; + IO_STATUS_BLOCK io; + HANDLE handle; + SOCKET s; + int ret; + + RtlInitUnicodeString(&string, L"\Device\Afd"); + InitializeObjectAttributes(&attr, &string, 0, NULL, NULL); + ret = NtOpenFile(&handle, SYNCHRONIZE, &attr, &io, 0, 0); + ok(!ret, "got %#x\n", ret); + CloseHandle(handle); + + RtlInitUnicodeString(&string, L"\Device\Afd\"); + InitializeObjectAttributes(&attr, &string, 0, NULL, NULL); + ret = NtOpenFile(&handle, SYNCHRONIZE, &attr, &io, 0, 0); + ok(!ret, "got %#x\n", ret); + CloseHandle(handle); + + RtlInitUnicodeString(&string, L"\Device\Afd\foobar"); + InitializeObjectAttributes(&attr, &string, 0, NULL, NULL); + ret = NtOpenFile(&handle, SYNCHRONIZE, &attr, &io, 0, 0); + todo_wine ok(!ret, "got %#x\n", ret); + CloseHandle(handle); + + s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + ret = NtQueryObject((HANDLE)s, ObjectBasicInformation, &info, sizeof(info), NULL); + ok(!ret, "got %#x\n", ret); + todo_wine ok(info.Attributes == OBJ_INHERIT, "got attributes %#x\n", info.Attributes); + todo_wine ok(info.GrantedAccess == (FILE_GENERIC_READ | FILE_GENERIC_WRITE | WRITE_DAC), "got access %#x\n", info.GrantedAccess); + + closesocket(s); +} + static void test_recv(void) { const struct sockaddr_in bind_addr = {.sin_family = AF_INET, .sin_addr.s_addr = htonl(INADDR_LOOPBACK)}; @@ -397,6 +434,7 @@ START_TEST(afd)
WSAStartup(MAKEWORD(2, 2), &data);
+ test_open_device(); test_recv();
WSACleanup();
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=50974 Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/ws2_32/tests/afd.c | 2 +- server/sock.c | 1 + 2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/dlls/ws2_32/tests/afd.c b/dlls/ws2_32/tests/afd.c index a02b4f3da27..48b177ee845 100644 --- a/dlls/ws2_32/tests/afd.c +++ b/dlls/ws2_32/tests/afd.c @@ -64,7 +64,7 @@ static void test_open_device(void) RtlInitUnicodeString(&string, L"\Device\Afd\foobar"); InitializeObjectAttributes(&attr, &string, 0, NULL, NULL); ret = NtOpenFile(&handle, SYNCHRONIZE, &attr, &io, 0, 0); - todo_wine ok(!ret, "got %#x\n", ret); + ok(!ret, "got %#x\n", ret); CloseHandle(handle);
s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); diff --git a/server/sock.c b/server/sock.c index 7f8d9760afb..ab5aa57a6cd 100644 --- a/server/sock.c +++ b/server/sock.c @@ -1981,6 +1981,7 @@ static void socket_device_dump( struct object *obj, int verbose ) static struct object *socket_device_lookup_name( struct object *obj, struct unicode_str *name, unsigned int attr, struct object *root ) { + if (name) name->len = 0; return NULL; }
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=50975 Signed-off-by: Zebediah Figura z.figura12@gmail.com --- As described in a comment, it's possible to implement this entirely on the server side. I found it to be difficult and fragile due to the difference in structure size between 32-bit and 64-bit processes, and elected to simplify the server's job by handling that in ntdll. I'm open to moving the whole ioctl to the server, though.
dlls/ntdll/unix/socket.c | 157 ++++++++++++++++++++ include/wine/afd.h | 30 ++++ server/protocol.def | 24 +++ server/sock.c | 306 ++++++++++++++++++++++++++++++++++++++- server/trace.c | 32 ++++ 5 files changed, 546 insertions(+), 3 deletions(-)
diff --git a/dlls/ntdll/unix/socket.c b/dlls/ntdll/unix/socket.c index 0e262b26611..0812ec4970f 100644 --- a/dlls/ntdll/unix/socket.c +++ b/dlls/ntdll/unix/socket.c @@ -235,6 +235,159 @@ static NTSTATUS sock_recv( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, voi return status; }
+ +struct async_poll_ioctl +{ + struct async_fileio io; + unsigned int count; + struct afd_poll_params *input, *output; + struct poll_socket_output sockets[1]; +}; + +static ULONG_PTR fill_poll_output( struct async_poll_ioctl *async, NTSTATUS status ) +{ + struct afd_poll_params *input = async->input, *output = async->output; + unsigned int i, count = 0; + + memcpy( output, input, offsetof( struct afd_poll_params, sockets[0] ) ); + + if (!status) + { + for (i = 0; i < async->count; ++i) + { + if (async->sockets[i].flags) + { + output->sockets[count].socket = input->sockets[i].socket; + output->sockets[count].flags = async->sockets[i].flags; + output->sockets[count].status = async->sockets[i].status; + ++count; + } + } + } + output->count = count; + return offsetof( struct afd_poll_params, sockets[count] ); +} + +static NTSTATUS async_poll_proc( void *user, IO_STATUS_BLOCK *io, NTSTATUS status ) +{ + struct async_poll_ioctl *async = user; + ULONG_PTR information = 0; + + if (status == STATUS_ALERTED) + { + SERVER_START_REQ( get_async_result ) + { + req->user_arg = wine_server_client_ptr( async ); + wine_server_set_reply( req, async->sockets, async->count * sizeof(async->sockets[0]) ); + status = wine_server_call( req ); + } + SERVER_END_REQ; + + information = fill_poll_output( async, status ); + } + + if (status != STATUS_PENDING) + { + io->Status = status; + io->Information = information; + free( async->input ); + release_fileio( &async->io ); + } + return status; +} + + +/* we could handle this ioctl entirely on the server side, but the differing + * structure size makes it painful */ +static NTSTATUS sock_poll( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc_user, IO_STATUS_BLOCK *io, + void *in_buffer, ULONG in_size, void *out_buffer, ULONG out_size ) +{ + const struct afd_poll_params *params = in_buffer; + struct poll_socket_input *input; + struct async_poll_ioctl *async; + HANDLE wait_handle; + DWORD async_size; + NTSTATUS status; + unsigned int i; + ULONG options; + + if (in_size < sizeof(*params) || out_size < in_size || !params->count + || in_size < offsetof( struct afd_poll_params, sockets[params->count] )) + return STATUS_INVALID_PARAMETER; + + TRACE( "timeout %s, count %u, unknown %#x, padding (%#x, %#x, %#x), sockets[0] {%04lx, %#x}\n", + wine_dbgstr_longlong(params->timeout), params->count, params->unknown, + params->padding[0], params->padding[1], params->padding[2], + params->sockets[0].socket, params->sockets[0].flags ); + + if (params->unknown) FIXME( "unknown boolean is %#x\n", params->unknown ); + if (params->padding[0]) FIXME( "padding[0] is %#x\n", params->padding[0] ); + if (params->padding[1]) FIXME( "padding[1] is %#x\n", params->padding[1] ); + if (params->padding[2]) FIXME( "padding[2] is %#x\n", params->padding[2] ); + for (i = 0; i < params->count; ++i) + { + if (params->sockets[i].flags & ~0x1ff) + FIXME( "unknown socket flags %#x\n", params->sockets[i].flags ); + } + + if (!(input = malloc( params->count * sizeof(*input) ))) + return STATUS_NO_MEMORY; + + async_size = offsetof( struct async_poll_ioctl, sockets[params->count] ); + + if (!(async = (struct async_poll_ioctl *)alloc_fileio( async_size, async_poll_proc, handle ))) + { + free( input ); + return STATUS_NO_MEMORY; + } + + if (!(async->input = malloc( in_size ))) + { + release_fileio( &async->io ); + free( input ); + return STATUS_NO_MEMORY; + } + memcpy( async->input, in_buffer, in_size ); + + async->count = params->count; + async->output = out_buffer; + + for (i = 0; i < params->count; ++i) + { + input[i].socket = params->sockets[i].socket; + input[i].flags = params->sockets[i].flags; + } + + SERVER_START_REQ( poll_socket ) + { + req->async = server_async( handle, &async->io, event, apc, apc_user, io ); + req->timeout = params->timeout; + wine_server_add_data( req, input, params->count * sizeof(*input) ); + wine_server_set_reply( req, async->sockets, params->count * sizeof(async->sockets[0]) ); + status = wine_server_call( req ); + wait_handle = wine_server_ptr_handle( reply->wait ); + options = reply->options; + if (wait_handle && status != STATUS_PENDING) + { + io->Status = status; + io->Information = fill_poll_output( async, status ); + } + } + SERVER_END_REQ; + + free( input ); + + if (status != STATUS_PENDING) + { + free( async->input ); + release_fileio( &async->io ); + } + + if (wait_handle) status = wait_async( wait_handle, (options & FILE_SYNCHRONOUS_IO_ALERT) ); + return status; +} + + NTSTATUS sock_ioctl( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc_user, IO_STATUS_BLOCK *io, ULONG code, void *in_buffer, ULONG in_size, void *out_buffer, ULONG out_size ) { @@ -299,6 +452,10 @@ NTSTATUS sock_ioctl( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc break; }
+ case IOCTL_AFD_POLL: + status = sock_poll( handle, event, apc, apc_user, io, in_buffer, in_size, out_buffer, out_size ); + break; + default: { FIXME( "Unknown ioctl %#x (device %#x, access %#x, function %#x, method %#x)\n", diff --git a/include/wine/afd.h b/include/wine/afd.h index a83ce7c1bac..264e76ef69a 100644 --- a/include/wine/afd.h +++ b/include/wine/afd.h @@ -27,6 +27,20 @@
#define IOCTL_AFD_LISTEN CTL_CODE(FILE_DEVICE_BEEP, 0x802, METHOD_NEITHER, FILE_ANY_ACCESS) #define IOCTL_AFD_RECV CTL_CODE(FILE_DEVICE_BEEP, 0x805, METHOD_NEITHER, FILE_ANY_ACCESS) +#define IOCTL_AFD_POLL CTL_CODE(FILE_DEVICE_BEEP, 0x809, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define AFD_POLL_READ 0x0001 +#define AFD_POLL_OOB 0x0002 +#define AFD_POLL_WRITE 0x0004 +#define AFD_POLL_HUP 0x0008 +#define AFD_POLL_RESET 0x0010 +#define AFD_POLL_CLOSE 0x0020 +#define AFD_POLL_CONNECT 0x0040 +#define AFD_POLL_ACCEPT 0x0080 +#define AFD_POLL_CONNECT_ERR 0x0100 +/* I have never seen these reported, but StarCraft Remastered polls for them. */ +#define AFD_POLL_UNK1 0x0200 +#define AFD_POLL_UNK2 0x0400
struct afd_listen_params { @@ -50,6 +64,22 @@ struct afd_recv_params int msg_flags; };
+#include <pshpack4.h> +struct afd_poll_params +{ + LONGLONG timeout; + unsigned int count; + BOOLEAN unknown; + BOOLEAN padding[3]; + struct + { + SOCKET socket; + int flags; + NTSTATUS status; + } sockets[1]; +}; +#include <poppack.h> + #define IOCTL_AFD_WINE_CREATE CTL_CODE(FILE_DEVICE_NETWORK, 200, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_AFD_WINE_ACCEPT CTL_CODE(FILE_DEVICE_NETWORK, 201, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_AFD_WINE_ACCEPT_INTO CTL_CODE(FILE_DEVICE_NETWORK, 202, METHOD_BUFFERED, FILE_ANY_ACCESS) diff --git a/server/protocol.def b/server/protocol.def index 62a584e4cc9..db50e03c691 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -1490,6 +1490,30 @@ enum server_fd_type @END
+struct poll_socket_input +{ + obj_handle_t socket; /* socket handle */ + int flags; /* events to poll for */ +}; + +struct poll_socket_output +{ + int flags; /* events signaled */ + unsigned int status; /* socket status */ +}; + +/* Perform an async poll on a socket */ +@REQ(poll_socket) + async_data_t async; /* async I/O parameters */ + timeout_t timeout; /* timeout */ + VARARG(sockets,poll_socket_input); /* list of sockets to poll */ +@REPLY + obj_handle_t wait; /* handle to wait on for blocking poll */ + unsigned int options; /* file open options */ + VARARG(sockets,poll_socket_output); /* data returned */ +@END + + /* Retrieve the next pending console ioctl request */ @REQ(get_next_console_request) obj_handle_t handle; /* console server handle */ diff --git a/server/sock.c b/server/sock.c index ab5aa57a6cd..3925018c7a5 100644 --- a/server/sock.c +++ b/server/sock.c @@ -96,6 +96,23 @@ #include "request.h" #include "user.h"
+static struct list poll_list = LIST_INIT( poll_list ); + +struct poll_req +{ + struct list entry; + struct async *async; + struct iosb *iosb; + struct timeout_user *timeout; + unsigned int count; + struct poll_socket_output *output; + struct + { + struct sock *sock; + int flags; + } sockets[1]; +}; + struct accept_req { struct list entry; @@ -151,6 +168,7 @@ struct sock struct async_queue ifchange_q; /* queue for interface change notifications */ struct async_queue accept_q; /* queue for asynchronous accepts */ struct async_queue connect_q; /* queue for asynchronous connects */ + struct async_queue poll_q; /* queue for asynchronous polls */ struct object *ifchange_obj; /* the interface change notification object */ struct list ifchange_entry; /* entry in ifchange notification list */ struct list accept_list; /* list of pending accept requests */ @@ -621,6 +639,106 @@ static void complete_async_connect( struct sock *sock ) } }
+static void free_poll_req( void *private ) +{ + struct poll_req *req = private; + unsigned int i; + + if (req->timeout) remove_timeout_user( req->timeout ); + + for (i = 0; i < req->count; ++i) + release_object( req->sockets[i].sock ); + release_object( req->async ); + release_object( req->iosb ); + list_remove( &req->entry ); + free( req ); +} + +static int is_oobinline( struct sock *sock ) +{ + int oobinline; + socklen_t len = sizeof(oobinline); + return !getsockopt( get_unix_fd( sock->fd ), SOL_SOCKET, SO_OOBINLINE, (char *)&oobinline, &len ) && oobinline; +} + +static int get_poll_flags( struct sock *sock, int event ) +{ + int flags = 0; + + /* A connection-mode socket which has never been connected does not return + * write or hangup events, but Linux reports POLLOUT | POLLHUP. */ + if (sock->type == WS_SOCK_STREAM && !(sock->state & (FD_CONNECT | FD_WINE_CONNECTED | FD_WINE_LISTENING))) + event &= ~(POLLOUT | POLLHUP); + + if (event & POLLIN) + { + if (sock->state & FD_WINE_LISTENING) + flags |= AFD_POLL_ACCEPT; + else + flags |= AFD_POLL_READ; + } + if (event & POLLPRI) + flags |= is_oobinline( sock ) ? AFD_POLL_READ : AFD_POLL_OOB; + if (event & POLLOUT) + flags |= AFD_POLL_WRITE; + if (sock->state & FD_WINE_CONNECTED) + flags |= AFD_POLL_CONNECT; + if (event & POLLHUP) + flags |= AFD_POLL_HUP; + if (event & POLLERR) + flags |= AFD_POLL_CONNECT_ERR; + + return flags; +} + +static void complete_async_polls( struct sock *sock, int event, int error ) +{ + int flags = get_poll_flags( sock, event ); + struct poll_req *req, *next; + + LIST_FOR_EACH_ENTRY_SAFE( req, next, &poll_list, struct poll_req, entry ) + { + struct iosb *iosb = req->iosb; + unsigned int i; + + if (iosb->status != STATUS_PENDING) continue; + + for (i = 0; i < req->count; ++i) + { + if (req->sockets[i].sock != sock) continue; + if (!(req->sockets[i].flags & flags)) continue; + + if (debug_level) + fprintf( stderr, "completing poll for socket %p, wanted %#x got %#x\n", + sock, req->sockets[i].flags, flags ); + + req->output[i].flags = req->sockets[i].flags & flags; + req->output[i].status = sock_get_ntstatus( error ); + + iosb->status = STATUS_SUCCESS; + iosb->out_data = req->output; + iosb->out_size = req->count * sizeof(*req->output); + async_terminate( req->async, STATUS_ALERTED ); + break; + } + } +} + +static void async_poll_timeout( void *private ) +{ + struct poll_req *req = private; + struct iosb *iosb = req->iosb; + + req->timeout = NULL; + + if (iosb->status != STATUS_PENDING) return; + + iosb->status = STATUS_TIMEOUT; + iosb->out_data = req->output; + iosb->out_size = req->count * sizeof(*req->output); + async_terminate( req->async, STATUS_ALERTED ); +} + static int sock_dispatch_asyncs( struct sock *sock, int event, int error ) { if (event & (POLLIN | POLLPRI)) @@ -820,6 +938,8 @@ static void sock_poll_event( struct fd *fd, int event ) event |= POLLHUP; }
+ complete_async_polls( sock, event, error ); + event = sock_dispatch_asyncs( sock, event, error ); sock_dispatch_events( sock, prevstate, event, error );
@@ -835,11 +955,34 @@ static void sock_dump( struct object *obj, int verbose ) sock->mask, sock->pending_events, sock->reported_events ); }
+static int poll_flags_from_afd( struct sock *sock, int flags ) +{ + int ev = 0; + + /* A connection-mode socket which has never been connected does + * not return write or hangup events, but Linux returns + * POLLOUT | POLLHUP. */ + if (sock->type == WS_SOCK_STREAM && !(sock->state & (FD_CONNECT | FD_WINE_CONNECTED | FD_WINE_LISTENING))) + return -1; + + if (flags & (AFD_POLL_READ | AFD_POLL_ACCEPT)) + ev |= POLLIN; + if ((flags & AFD_POLL_HUP) && sock->type == WS_SOCK_STREAM) + ev |= POLLIN; + if (flags & AFD_POLL_OOB) + ev |= is_oobinline( sock ) ? POLLIN : POLLPRI; + if (flags & AFD_POLL_WRITE) + ev |= POLLOUT; + + return ev; +} + static int sock_get_poll_events( struct fd *fd ) { struct sock *sock = get_fd_user( fd ); unsigned int mask = sock->mask & ~sock->reported_events; unsigned int smask = sock->state & mask; + struct poll_req *req; int ev = 0;
assert( sock->obj.ops == &sock_ops ); @@ -869,6 +1012,18 @@ static int sock_get_poll_events( struct fd *fd ) else if (smask & FD_WRITE) ev |= POLLOUT;
+ LIST_FOR_EACH_ENTRY( req, &poll_list, struct poll_req, entry ) + { + unsigned int i; + + for (i = 0; i < req->count; ++i) + { + if (req->sockets[i].sock != sock) continue; + + ev |= poll_flags_from_afd( sock, req->sockets[i].flags ); + } + } + return ev; }
@@ -933,18 +1088,45 @@ static struct fd *sock_get_fd( struct object *obj ) static int sock_close_handle( struct object *obj, struct process *process, obj_handle_t handle ) { struct sock *sock = (struct sock *)obj; - struct accept_req *req, *next;
if (sock->obj.handle_count == 1) /* last handle */ { + struct accept_req *accept_req, *accept_next; + struct poll_req *poll_req, *poll_next; + if (sock->accept_recv_req) async_terminate( sock->accept_recv_req->async, STATUS_CANCELLED );
- LIST_FOR_EACH_ENTRY_SAFE( req, next, &sock->accept_list, struct accept_req, entry ) - async_terminate( req->async, STATUS_CANCELLED ); + LIST_FOR_EACH_ENTRY_SAFE( accept_req, accept_next, &sock->accept_list, struct accept_req, entry ) + async_terminate( accept_req->async, STATUS_CANCELLED );
if (sock->connect_req) async_terminate( sock->connect_req->async, STATUS_CANCELLED ); + + LIST_FOR_EACH_ENTRY_SAFE( poll_req, poll_next, &poll_list, struct poll_req, entry ) + { + struct iosb *iosb = poll_req->iosb; + unsigned int i; + + if (iosb->status != STATUS_PENDING) continue; + + for (i = 0; i < poll_req->count; ++i) + { + if (poll_req->sockets[i].sock == sock) + { + iosb->status = STATUS_SUCCESS; + poll_req->output[i].flags = AFD_POLL_CLOSE; + poll_req->output[i].status = 0; + } + } + + if (iosb->status != STATUS_PENDING) + { + iosb->out_data = poll_req->output; + iosb->out_size = poll_req->count * sizeof(*poll_req->output); + async_terminate( poll_req->async, STATUS_ALERTED ); + } + } }
return 1; @@ -968,6 +1150,7 @@ static void sock_destroy( struct object *obj ) free_async_queue( &sock->ifchange_q ); free_async_queue( &sock->accept_q ); free_async_queue( &sock->connect_q ); + free_async_queue( &sock->poll_q ); if (sock->event) release_object( sock->event ); if (sock->fd) { @@ -1007,6 +1190,7 @@ static struct sock *create_socket(void) init_async_queue( &sock->ifchange_q ); init_async_queue( &sock->accept_q ); init_async_queue( &sock->connect_q ); + init_async_queue( &sock->poll_q ); memset( sock->errors, 0, sizeof(sock->errors) ); list_init( &sock->accept_list ); return sock; @@ -1710,6 +1894,101 @@ static int sock_ioctl( struct fd *fd, ioctl_code_t code, struct async *async ) } }
+static int poll_socket( struct sock *poll_sock, struct async *async, timeout_t timeout, + unsigned int count, const struct poll_socket_input *input ) +{ + struct poll_socket_output *output; + struct poll_req *req; + unsigned int i, j; + + if (!(output = mem_alloc( count * sizeof(*output) ))) + return 0; + memset( output, 0, count * sizeof(*output) ); + + if (!(req = mem_alloc( offsetof( struct poll_req, sockets[count] ) ))) + { + free( output ); + return 0; + } + + req->timeout = NULL; + if (timeout && timeout != TIMEOUT_INFINITE && + !(req->timeout = add_timeout_user( timeout, async_poll_timeout, req ))) + { + free( req ); + free( output ); + return 0; + } + + for (i = 0; i < count; ++i) + { + req->sockets[i].sock = (struct sock *)get_handle_obj( current->process, input[i].socket, 0, &sock_ops ); + if (!req->sockets[i].sock) + { + for (j = 0; j < i; ++j) release_object( req->sockets[i].sock ); + if (req->timeout) remove_timeout_user( req->timeout ); + free( req ); + free( output ); + return 0; + } + req->sockets[i].flags = input[i].flags; + } + + req->count = count; + req->async = (struct async *)grab_object( async ); + req->iosb = async_get_iosb( async ); + req->output = output; + + list_add_tail( &poll_list, &req->entry ); + async_set_completion_callback( async, free_poll_req, req ); + queue_async( &poll_sock->poll_q, async ); + + if (!timeout) req->iosb->status = STATUS_SUCCESS; + + for (i = 0; i < count; ++i) + { + struct sock *sock = req->sockets[i].sock; + struct pollfd pollfd; + int flags; + + pollfd.fd = get_unix_fd( sock->fd ); + pollfd.events = poll_flags_from_afd( sock, req->sockets[i].flags ); + if (pollfd.events < 0 || poll( &pollfd, 1, 0 ) < 0) continue; + + if ((req->sockets[i].flags & AFD_POLL_HUP) && (pollfd.revents & POLLIN) && + sock->type == WS_SOCK_STREAM) + { + char dummy; + + if (!recv( get_unix_fd( sock->fd ), &dummy, 1, MSG_PEEK )) + { + pollfd.revents &= ~POLLIN; + pollfd.revents |= POLLHUP; + } + } + + flags = get_poll_flags( sock, pollfd.revents ) & req->sockets[i].flags; + if (flags) + { + req->iosb->status = STATUS_SUCCESS; + output[i].flags = flags; + output[i].status = sock_get_ntstatus( sock_error( sock->fd ) ); + } + } + + if (req->iosb->status != STATUS_PENDING) + { + req->iosb->out_data = output; + req->iosb->out_size = count * sizeof(*output); + async_terminate( req->async, STATUS_ALERTED ); + } + + for (i = 0; i < req->count; ++i) + sock_reselect( req->sockets[i].sock ); + set_error( STATUS_PENDING ); + return 1; +} + #ifdef HAVE_LINUX_RTNETLINK_H
/* only keep one ifchange object around, all sockets waiting for wakeups will look to it */ @@ -2201,3 +2480,24 @@ DECL_HANDLER(recv_socket) } release_object( sock ); } + +DECL_HANDLER(poll_socket) +{ + struct sock *sock = (struct sock *)get_handle_obj( current->process, req->async.handle, 0, &sock_ops ); + const struct poll_socket_input *input = get_req_data(); + struct async *async; + unsigned int count; + + if (!sock) return; + + count = get_req_data_size() / sizeof(*input); + + if ((async = create_request_async( sock->fd, get_fd_comp_flags( sock->fd ), &req->async ))) + { + reply->wait = async_handoff( async, poll_socket( sock, async, req->timeout, count, input ), NULL, 0 ); + reply->options = get_fd_options( sock->fd ); + release_object( async ); + } + + release_object( sock ); +} diff --git a/server/trace.c b/server/trace.c index ab805a05234..d8082d54cdb 100644 --- a/server/trace.c +++ b/server/trace.c @@ -1372,6 +1372,38 @@ static void dump_varargs_handle_infos( const char *prefix, data_size_t size ) fputc( '}', stderr ); }
+static void dump_varargs_poll_socket_input( const char *prefix, data_size_t size ) +{ + const struct poll_socket_input *input; + + fprintf( stderr, "%s{", prefix ); + while (size >= sizeof(*input)) + { + input = cur_data; + fprintf( stderr, "{socket=%04x,flags=%08x}", input->socket, input->flags ); + size -= sizeof(*input); + remove_data( sizeof(*input) ); + if (size) fputc( ',', stderr ); + } + fputc( '}', stderr ); +} + +static void dump_varargs_poll_socket_output( const char *prefix, data_size_t size ) +{ + const struct poll_socket_output *output; + + fprintf( stderr, "%s{", prefix ); + while (size >= sizeof(*output)) + { + output = cur_data; + fprintf( stderr, "{flags=%08x,status=%s}", output->flags, get_status_name( output->status ) ); + size -= sizeof(*output); + remove_data( sizeof(*output) ); + if (size) fputc( ',', stderr ); + } + fputc( '}', stderr ); +} + typedef void (*dump_func)( const void *req );
/* Everything below this line is generated automatically by tools/make_requests */
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/ws2_32/tests/afd.c | 683 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 683 insertions(+)
diff --git a/dlls/ws2_32/tests/afd.c b/dlls/ws2_32/tests/afd.c index 48b177ee845..13d0f3942d4 100644 --- a/dlls/ws2_32/tests/afd.c +++ b/dlls/ws2_32/tests/afd.c @@ -31,6 +31,41 @@ #include "wine/afd.h" #include "wine/test.h"
+static void tcp_socketpair(SOCKET *src, SOCKET *dst) +{ + SOCKET server = INVALID_SOCKET; + struct sockaddr_in addr; + int len, ret; + + *src = WSASocketW(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED); + ok(*src != INVALID_SOCKET, "failed to create socket, error %u\n", WSAGetLastError()); + + server = WSASocketW(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED); + ok(server != INVALID_SOCKET, "failed to create socket, error %u\n", WSAGetLastError()); + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + ret = bind(server, (struct sockaddr *)&addr, sizeof(addr)); + ok(!ret, "failed to bind socket, error %u\n", WSAGetLastError()); + + len = sizeof(addr); + ret = getsockname(server, (struct sockaddr *)&addr, &len); + ok(!ret, "failed to get address, error %u\n", WSAGetLastError()); + + ret = listen(server, 1); + ok(!ret, "failed to listen, error %u\n", WSAGetLastError()); + + ret = connect(*src, (struct sockaddr *)&addr, sizeof(addr)); + ok(!ret, "failed to connect, error %u\n", WSAGetLastError()); + + len = sizeof(addr); + *dst = accept(server, (struct sockaddr *)&addr, &len); + ok(*dst != INVALID_SOCKET, "failed to accept socket, error %u\n", WSAGetLastError()); + + closesocket(server); +} + static void set_blocking(SOCKET s, ULONG blocking) { int ret; @@ -76,6 +111,652 @@ static void test_open_device(void) closesocket(s); }
+#define check_poll(a, b, c) check_poll_(__LINE__, a, b, c, FALSE) +#define check_poll_todo(a, b, c) check_poll_(__LINE__, a, b, c, TRUE) +static void check_poll_(int line, SOCKET s, HANDLE event, int expect, BOOL todo) +{ + struct afd_poll_params in_params = {0}, out_params = {0}; + IO_STATUS_BLOCK io; + NTSTATUS ret; + + in_params.timeout = -1000 * 10000; + in_params.count = 1; + in_params.sockets[0].socket = s; + in_params.sockets[0].flags = ~0; + in_params.sockets[0].status = 0xdeadbeef; + + ret = NtDeviceIoControlFile((HANDLE)s, event, NULL, NULL, &io, + IOCTL_AFD_POLL, &in_params, sizeof(in_params), &out_params, sizeof(out_params)); + ok_(__FILE__, line)(!ret, "got %#x\n", ret); + ok_(__FILE__, line)(!io.Status, "got %#x\n", io.Status); + ok_(__FILE__, line)(io.Information == sizeof(out_params), "got %#Ix\n", io.Information); + ok_(__FILE__, line)(out_params.timeout == in_params.timeout, "got timeout %I64d\n", out_params.timeout); + ok_(__FILE__, line)(out_params.count == 1, "got count %u\n", out_params.count); + ok_(__FILE__, line)(out_params.sockets[0].socket == s, "got socket %#Ix\n", out_params.sockets[0].socket); + todo_wine_if (todo) ok_(__FILE__, line)(out_params.sockets[0].flags == expect, "got flags %#x\n", out_params.sockets[0].flags); + ok_(__FILE__, line)(!out_params.sockets[0].status, "got status %#x\n", out_params.sockets[0].status); +} + +static void test_poll(void) +{ + const struct sockaddr_in bind_addr = {.sin_family = AF_INET, .sin_addr.s_addr = htonl(INADDR_LOOPBACK)}; + char in_buffer[offsetof(struct afd_poll_params, sockets[3])]; + char out_buffer[offsetof(struct afd_poll_params, sockets[3])]; + struct afd_poll_params *in_params = (struct afd_poll_params *)in_buffer; + struct afd_poll_params *out_params = (struct afd_poll_params *)out_buffer; + int large_buffer_size = 1024 * 1024; + SOCKET client, server, listener; + struct sockaddr_in addr; + char *large_buffer; + IO_STATUS_BLOCK io; + LARGE_INTEGER now; + ULONG params_size; + HANDLE event; + int ret, len; + + large_buffer = malloc(large_buffer_size); + memset(in_buffer, 0, sizeof(in_buffer)); + memset(out_buffer, 0, sizeof(out_buffer)); + event = CreateEventW(NULL, TRUE, FALSE, NULL); + + listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + ret = bind(listener, (const struct sockaddr *)&bind_addr, sizeof(bind_addr)); + ok(!ret, "got error %u\n", WSAGetLastError()); + ret = listen(listener, 1); + ok(!ret, "got error %u\n", WSAGetLastError()); + len = sizeof(addr); + ret = getsockname(listener, (struct sockaddr *)&addr, &len); + ok(!ret, "got error %u\n", WSAGetLastError()); + + params_size = offsetof(struct afd_poll_params, sockets[1]); + in_params->count = 1; + + /* out_size must be at least as large as in_size. */ + + ret = NtDeviceIoControlFile((HANDLE)listener, event, NULL, NULL, &io, + IOCTL_AFD_POLL, in_params, params_size, NULL, 0); + ok(ret == STATUS_INVALID_PARAMETER, "got %#x\n", ret); + + ret = NtDeviceIoControlFile((HANDLE)listener, event, NULL, NULL, &io, + IOCTL_AFD_POLL, NULL, 0, out_params, params_size); + ok(ret == STATUS_INVALID_PARAMETER, "got %#x\n", ret); + + ret = NtDeviceIoControlFile((HANDLE)listener, event, NULL, NULL, &io, + IOCTL_AFD_POLL, in_params, params_size, out_params, params_size + 1); + ok(ret == STATUS_INVALID_HANDLE, "got %#x\n", ret); + + ret = NtDeviceIoControlFile((HANDLE)listener, event, NULL, NULL, &io, + IOCTL_AFD_POLL, in_params, params_size + 1, out_params, params_size); + ok(ret == STATUS_INVALID_PARAMETER, "got %#x\n", ret); + + ret = NtDeviceIoControlFile((HANDLE)listener, event, NULL, NULL, &io, + IOCTL_AFD_POLL, in_params, params_size - 1, out_params, params_size - 1); + ok(ret == STATUS_INVALID_PARAMETER, "got %#x\n", ret); + + ret = NtDeviceIoControlFile((HANDLE)listener, event, NULL, NULL, &io, + IOCTL_AFD_POLL, in_params, params_size + 1, out_params, params_size + 1); + ok(ret == STATUS_INVALID_HANDLE, "got %#x\n", ret); + + in_params->count = 0; + ret = NtDeviceIoControlFile((HANDLE)listener, event, NULL, NULL, &io, + IOCTL_AFD_POLL, in_params, params_size, out_params, params_size); + ok(ret == STATUS_INVALID_PARAMETER, "got %#x\n", ret); + + /* Basic semantics of the ioctl. */ + + in_params->timeout = 0; + in_params->count = 1; + in_params->sockets[0].socket = listener; + in_params->sockets[0].flags = ~0; + in_params->sockets[0].status = 0xdeadbeef; + + memset(out_params, 0, params_size); + ret = NtDeviceIoControlFile((HANDLE)listener, event, NULL, NULL, &io, + IOCTL_AFD_POLL, in_params, params_size, out_params, params_size); + ok(!ret, "got %#x\n", ret); + ok(!io.Status, "got %#x\n", io.Status); + ok(io.Information == offsetof(struct afd_poll_params, sockets[0]), "got %#Ix\n", io.Information); + ok(!out_params->timeout, "got timeout %#I64x\n", out_params->timeout); + ok(!out_params->count, "got count %u\n", out_params->count); + ok(!out_params->sockets[0].socket, "got socket %#Ix\n", out_params->sockets[0].socket); + ok(!out_params->sockets[0].flags, "got flags %#x\n", out_params->sockets[0].flags); + ok(!out_params->sockets[0].status, "got status %#x\n", out_params->sockets[0].status); + + NtQuerySystemTime(&now); + in_params->timeout = now.QuadPart; + + ret = NtDeviceIoControlFile((HANDLE)listener, event, NULL, NULL, &io, + IOCTL_AFD_POLL, in_params, params_size, out_params, params_size); + ok(ret == STATUS_PENDING, "got %#x\n", ret); + ret = WaitForSingleObject(event, 100); + ok(!ret, "got %#x\n", ret); + ok(io.Status == STATUS_TIMEOUT, "got %#x\n", io.Status); + ok(io.Information == offsetof(struct afd_poll_params, sockets[0]), "got %#Ix\n", io.Information); + ok(out_params->timeout == now.QuadPart, "got timeout %#I64x\n", out_params->timeout); + ok(!out_params->count, "got count %u\n", out_params->count); + + in_params->timeout = -1000 * 10000; + + ret = NtDeviceIoControlFile((HANDLE)listener, event, NULL, NULL, &io, + IOCTL_AFD_POLL, in_params, params_size, out_params, params_size); + ok(ret == STATUS_PENDING, "got %#x\n", ret); + + client = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + set_blocking(client, FALSE); + ret = connect(client, (struct sockaddr *)&addr, sizeof(addr)); + ok(!ret || WSAGetLastError() == WSAEWOULDBLOCK, "got error %u\n", WSAGetLastError()); + + ret = WaitForSingleObject(event, 100); + ok(!ret, "got %#x\n", ret); + ok(!io.Status, "got %#x\n", io.Status); + ok(io.Information == offsetof(struct afd_poll_params, sockets[1]), "got %#Ix\n", io.Information); + ok(out_params->timeout == -1000 * 10000, "got timeout %#I64x\n", out_params->timeout); + ok(out_params->count == 1, "got count %u\n", out_params->count); + ok(out_params->sockets[0].socket == listener, "got socket %#Ix\n", out_params->sockets[0].socket); + ok(out_params->sockets[0].flags == AFD_POLL_ACCEPT, "got flags %#x\n", out_params->sockets[0].flags); + ok(!out_params->sockets[0].status, "got status %#x\n", out_params->sockets[0].status); + + ret = NtDeviceIoControlFile((HANDLE)listener, event, NULL, NULL, &io, + IOCTL_AFD_POLL, in_params, params_size, out_params, params_size); + ok(!ret, "got %#x\n", ret); + ok(!io.Status, "got %#x\n", io.Status); + ok(io.Information == offsetof(struct afd_poll_params, sockets[1]), "got %#Ix\n", io.Information); + ok(out_params->timeout == -1000 * 10000, "got timeout %#I64x\n", out_params->timeout); + ok(out_params->count == 1, "got count %u\n", out_params->count); + ok(out_params->sockets[0].socket == listener, "got socket %#Ix\n", out_params->sockets[0].socket); + ok(out_params->sockets[0].flags == AFD_POLL_ACCEPT, "got flags %#x\n", out_params->sockets[0].flags); + ok(!out_params->sockets[0].status, "got status %#x\n", out_params->sockets[0].status); + + in_params->timeout = now.QuadPart; + in_params->sockets[0].flags = (~0) & ~AFD_POLL_ACCEPT; + + ret = NtDeviceIoControlFile((HANDLE)listener, event, NULL, NULL, &io, + IOCTL_AFD_POLL, in_params, params_size, out_params, params_size); + ok(ret == STATUS_PENDING, "got %#x\n", ret); + ret = WaitForSingleObject(event, 100); + ok(!ret, "got %#x\n", ret); + ok(io.Status == STATUS_TIMEOUT, "got %#x\n", io.Status); + ok(io.Information == offsetof(struct afd_poll_params, sockets[0]), "got %#Ix\n", io.Information); + ok(!out_params->count, "got count %u\n", out_params->count); + + server = accept(listener, NULL, NULL); + ok(server != -1, "got error %u\n", WSAGetLastError()); + set_blocking(server, FALSE); + + /* Test flags exposed by connected sockets. */ + + check_poll(client, event, AFD_POLL_WRITE | AFD_POLL_CONNECT); + check_poll(server, event, AFD_POLL_WRITE | AFD_POLL_CONNECT); + + /* It is valid to poll on a socket other than the one passed to + * NtDeviceIoControlFile(). */ + + in_params->count = 1; + in_params->sockets[0].socket = server; + in_params->sockets[0].flags = ~0; + + ret = NtDeviceIoControlFile((HANDLE)listener, event, NULL, NULL, &io, + IOCTL_AFD_POLL, in_params, params_size, out_params, params_size); + ok(!ret, "got %#x\n", ret); + ok(!io.Status, "got %#x\n", io.Status); + ok(io.Information == offsetof(struct afd_poll_params, sockets[1]), "got %#Ix\n", io.Information); + ok(out_params->count == 1, "got count %u\n", out_params->count); + ok(out_params->sockets[0].socket == server, "got socket %#Ix\n", out_params->sockets[0].socket); + ok(out_params->sockets[0].flags == (AFD_POLL_WRITE | AFD_POLL_CONNECT), + "got flags %#x\n", out_params->sockets[0].flags); + ok(!out_params->sockets[0].status, "got status %#x\n", out_params->sockets[0].status); + + /* Test sending data. */ + + ret = send(server, "data", 5, 0); + ok(ret == 5, "got %d\n", ret); + + check_poll(client, event, AFD_POLL_WRITE | AFD_POLL_CONNECT | AFD_POLL_READ); + check_poll(server, event, AFD_POLL_WRITE | AFD_POLL_CONNECT); + + while (send(server, large_buffer, large_buffer_size, 0) == large_buffer_size); + + check_poll(client, event, AFD_POLL_WRITE | AFD_POLL_CONNECT | AFD_POLL_READ); + check_poll(server, event, AFD_POLL_CONNECT); + + /* Test sending out-of-band data. */ + + ret = send(client, "a", 1, MSG_OOB); + ok(ret == 1, "got %d\n", ret); + + check_poll(client, event, AFD_POLL_WRITE | AFD_POLL_CONNECT | AFD_POLL_READ); + check_poll(server, event, AFD_POLL_CONNECT | AFD_POLL_OOB); + + ret = recv(server, large_buffer, 1, MSG_OOB); + ok(ret == 1, "got %d\n", ret); + + check_poll(client, event, AFD_POLL_WRITE | AFD_POLL_CONNECT | AFD_POLL_READ); + check_poll(server, event, AFD_POLL_CONNECT); + + ret = 1; + ret = setsockopt(server, SOL_SOCKET, SO_OOBINLINE, (char *)&ret, sizeof(ret)); + ok(!ret, "got error %u\n", WSAGetLastError()); + + ret = send(client, "a", 1, MSG_OOB); + ok(ret == 1, "got %d\n", ret); + + check_poll(client, event, AFD_POLL_WRITE | AFD_POLL_CONNECT | AFD_POLL_READ); + check_poll(server, event, AFD_POLL_CONNECT | AFD_POLL_READ); + + closesocket(client); + closesocket(server); + + /* Test shutdown. */ + + client = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + ret = connect(client, (struct sockaddr *)&addr, sizeof(addr)); + ok(!ret, "got error %u\n", WSAGetLastError()); + server = accept(listener, NULL, NULL); + ok(server != -1, "got error %u\n", WSAGetLastError()); + + ret = shutdown(client, SD_RECEIVE); + ok(!ret, "got error %u\n", WSAGetLastError()); + + check_poll(client, event, AFD_POLL_WRITE | AFD_POLL_CONNECT); + check_poll(server, event, AFD_POLL_WRITE | AFD_POLL_CONNECT); + + ret = shutdown(client, SD_SEND); + ok(!ret, "got error %u\n", WSAGetLastError()); + + check_poll(client, event, AFD_POLL_WRITE | AFD_POLL_CONNECT); + check_poll(server, event, AFD_POLL_WRITE | AFD_POLL_CONNECT | AFD_POLL_HUP); + + closesocket(client); + closesocket(server); + + /* Test shutdown with data in the pipe. */ + + client = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + ret = connect(client, (struct sockaddr *)&addr, sizeof(addr)); + ok(!ret, "got error %u\n", WSAGetLastError()); + server = accept(listener, NULL, NULL); + ok(server != -1, "got error %u\n", WSAGetLastError()); + + ret = send(client, "data", 5, 0); + ok(ret == 5, "got %d\n", ret); + + check_poll(client, event, AFD_POLL_WRITE | AFD_POLL_CONNECT); + check_poll(server, event, AFD_POLL_WRITE | AFD_POLL_CONNECT | AFD_POLL_READ); + + ret = shutdown(client, SD_SEND); + ok(!ret, "got error %u\n", WSAGetLastError()); + + check_poll(client, event, AFD_POLL_WRITE | AFD_POLL_CONNECT); + check_poll_todo(server, event, AFD_POLL_WRITE | AFD_POLL_CONNECT | AFD_POLL_READ | AFD_POLL_HUP); + + /* Test closing a socket while polling on it. Note that AFD_POLL_CLOSE + * is always returned, regardless of whether it's polled for. */ + + in_params->timeout = -1000 * 10000; + in_params->count = 1; + in_params->sockets[0].socket = client; + in_params->sockets[0].flags = 0; + + ret = NtDeviceIoControlFile((HANDLE)client, event, NULL, NULL, &io, + IOCTL_AFD_POLL, in_params, params_size, out_params, params_size); + ok(ret == STATUS_PENDING, "got %#x\n", ret); + + closesocket(client); + + ret = WaitForSingleObject(event, 100); + ok(!ret, "got %#x\n", ret); + ok(!io.Status, "got %#x\n", io.Status); + ok(io.Information == offsetof(struct afd_poll_params, sockets[1]), "got %#Ix\n", io.Information); + ok(out_params->count == 1, "got count %u\n", out_params->count); + ok(out_params->sockets[0].socket == client, "got socket %#Ix\n", out_params->sockets[0].socket); + ok(out_params->sockets[0].flags == AFD_POLL_CLOSE, + "got flags %#x\n", out_params->sockets[0].flags); + ok(!out_params->sockets[0].status, "got status %#x\n", out_params->sockets[0].status); + + closesocket(server); + + /* Test a failed connection. + * + * The following poll works even where the equivalent WSAPoll() call fails. + * However, it can take over 2 seconds to complete on the testbot. */ + + if (winetest_interactive) + { + client = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + set_blocking(client, FALSE); + + in_params->timeout = -10000 * 10000; + in_params->count = 1; + in_params->sockets[0].socket = client; + in_params->sockets[0].flags = ~0; + + ret = NtDeviceIoControlFile((HANDLE)client, event, NULL, NULL, &io, + IOCTL_AFD_POLL, in_params, params_size, out_params, params_size); + ok(ret == STATUS_PENDING, "got %#x\n", ret); + + addr.sin_port = 255; + ret = connect(client, (struct sockaddr *)&addr, sizeof(addr)); + ok(!ret || WSAGetLastError() == WSAEWOULDBLOCK, "got error %u\n", WSAGetLastError()); + + ret = WaitForSingleObject(event, 10000); + ok(!ret, "got %#x\n", ret); + ok(!io.Status, "got %#x\n", io.Status); + ok(io.Information == offsetof(struct afd_poll_params, sockets[1]), "got %#Ix\n", io.Information); + ok(out_params->count == 1, "got count %u\n", out_params->count); + ok(out_params->sockets[0].socket == client, "got socket %#Ix\n", out_params->sockets[0].socket); + ok(out_params->sockets[0].flags == AFD_POLL_CONNECT_ERR, "got flags %#x\n", out_params->sockets[0].flags); + ok(out_params->sockets[0].status == STATUS_CONNECTION_REFUSED, "got status %#x\n", out_params->sockets[0].status); + + closesocket(client); + } + + /* Test supplying multiple handles to the ioctl. */ + + len = sizeof(addr); + ret = getsockname(listener, (struct sockaddr *)&addr, &len); + ok(!ret, "got error %u\n", WSAGetLastError()); + + client = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + ret = connect(client, (struct sockaddr *)&addr, sizeof(addr)); + ok(!ret, "got error %u\n", WSAGetLastError()); + server = accept(listener, NULL, NULL); + ok(server != -1, "got error %u\n", WSAGetLastError()); + + in_params->count = 2; + in_params->sockets[0].socket = client; + in_params->sockets[0].flags = AFD_POLL_READ; + in_params->sockets[1].socket = server; + in_params->sockets[1].flags = AFD_POLL_READ; + + ret = NtDeviceIoControlFile((HANDLE)client, event, NULL, NULL, &io, + IOCTL_AFD_POLL, in_params, params_size, out_params, params_size); + ok(ret == STATUS_INVALID_PARAMETER, "got %#x\n", ret); + + params_size = offsetof(struct afd_poll_params, sockets[2]); + + ret = NtDeviceIoControlFile((HANDLE)client, event, NULL, NULL, &io, + IOCTL_AFD_POLL, in_params, params_size, out_params, params_size); + ok(ret == STATUS_PENDING, "got %#x\n", ret); + + ret = send(client, "data", 5, 0); + ok(ret == 5, "got %d\n", ret); + + ret = WaitForSingleObject(event, 100); + ok(!ret, "got %#x\n", ret); + ok(!io.Status, "got %#x\n", io.Status); + ok(io.Information == offsetof(struct afd_poll_params, sockets[1]), "got %#Ix\n", io.Information); + ok(out_params->count == 1, "got count %u\n", out_params->count); + ok(out_params->sockets[0].socket == server, "got socket %#Ix\n", out_params->sockets[0].socket); + ok(out_params->sockets[0].flags == AFD_POLL_READ, "got flags %#x\n", out_params->sockets[0].flags); + ok(!out_params->sockets[0].status, "got status %#x\n", out_params->sockets[0].status); + + in_params->count = 2; + in_params->sockets[0].socket = client; + in_params->sockets[0].flags = AFD_POLL_READ | AFD_POLL_WRITE; + in_params->sockets[1].socket = server; + in_params->sockets[1].flags = AFD_POLL_READ | AFD_POLL_WRITE; + + ret = NtDeviceIoControlFile((HANDLE)client, event, NULL, NULL, &io, + IOCTL_AFD_POLL, in_params, params_size, out_params, params_size); + ok(!ret, "got %#x\n", ret); + ok(!io.Status, "got %#x\n", io.Status); + ok(io.Information == offsetof(struct afd_poll_params, sockets[2]), "got %#Ix\n", io.Information); + ok(out_params->count == 2, "got count %u\n", out_params->count); + ok(out_params->sockets[0].socket == client, "got socket %#Ix\n", out_params->sockets[0].socket); + ok(out_params->sockets[0].flags == AFD_POLL_WRITE, "got flags %#x\n", out_params->sockets[0].flags); + ok(!out_params->sockets[0].status, "got status %#x\n", out_params->sockets[0].status); + ok(out_params->sockets[1].socket == server, "got socket %#Ix\n", out_params->sockets[1].socket); + ok(out_params->sockets[1].flags == (AFD_POLL_READ | AFD_POLL_WRITE), + "got flags %#x\n", out_params->sockets[1].flags); + ok(!out_params->sockets[1].status, "got status %#x\n", out_params->sockets[1].status); + + in_params->count = 2; + in_params->sockets[0].socket = client; + in_params->sockets[0].flags = AFD_POLL_READ | AFD_POLL_WRITE; + in_params->sockets[1].socket = server; + in_params->sockets[1].flags = AFD_POLL_READ | AFD_POLL_WRITE; + + ret = NtDeviceIoControlFile((HANDLE)client, event, NULL, NULL, &io, + IOCTL_AFD_POLL, in_params, params_size, out_params, params_size); + ok(!ret, "got %#x\n", ret); + ok(!io.Status, "got %#x\n", io.Status); + ok(io.Information == offsetof(struct afd_poll_params, sockets[2]), "got %#Ix\n", io.Information); + ok(out_params->count == 2, "got count %u\n", out_params->count); + ok(out_params->sockets[0].socket == client, "got socket %#Ix\n", out_params->sockets[0].socket); + ok(out_params->sockets[0].flags == AFD_POLL_WRITE, "got flags %#x\n", out_params->sockets[0].flags); + ok(!out_params->sockets[0].status, "got status %#x\n", out_params->sockets[0].status); + ok(out_params->sockets[1].socket == server, "got socket %#Ix\n", out_params->sockets[1].socket); + ok(out_params->sockets[1].flags == (AFD_POLL_READ | AFD_POLL_WRITE), + "got flags %#x\n", out_params->sockets[1].flags); + ok(!out_params->sockets[1].status, "got status %#x\n", out_params->sockets[1].status); + + /* Close a socket while polling on another. */ + + in_params->timeout = -100 * 10000; + in_params->count = 1; + in_params->sockets[0].socket = client; + in_params->sockets[0].flags = AFD_POLL_READ; + params_size = offsetof(struct afd_poll_params, sockets[1]); + + ret = NtDeviceIoControlFile((HANDLE)server, event, NULL, NULL, &io, + IOCTL_AFD_POLL, in_params, params_size, out_params, params_size); + ok(ret == STATUS_PENDING, "got %#x\n", ret); + + closesocket(server); + + ret = WaitForSingleObject(event, 1000); + ok(!ret, "got %#x\n", ret); + todo_wine ok(io.Status == STATUS_TIMEOUT, "got %#x\n", io.Status); + todo_wine ok(io.Information == offsetof(struct afd_poll_params, sockets[0]), "got %#Ix\n", io.Information); + todo_wine ok(!out_params->count, "got count %u\n", out_params->count); + + closesocket(client); + + closesocket(listener); + + /* Test UDP sockets. */ + + client = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + server = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + + check_poll(client, event, AFD_POLL_WRITE); + check_poll(server, event, AFD_POLL_WRITE); + + ret = bind(client, (const struct sockaddr *)&bind_addr, sizeof(bind_addr)); + ok(!ret, "got error %u\n", WSAGetLastError()); + len = sizeof(addr); + ret = getsockname(listener, (struct sockaddr *)&addr, &len); + ok(!ret, "got error %u\n", WSAGetLastError()); + + check_poll(client, event, AFD_POLL_WRITE); + check_poll(server, event, AFD_POLL_WRITE); + + in_params->timeout = -1000 * 10000; + in_params->count = 1; + in_params->sockets[0].socket = client; + in_params->sockets[0].flags = (~0) & ~AFD_POLL_WRITE; + params_size = offsetof(struct afd_poll_params, sockets[1]); + + ret = NtDeviceIoControlFile((HANDLE)client, event, NULL, NULL, &io, + IOCTL_AFD_POLL, in_params, params_size, out_params, params_size); + ok(ret == STATUS_PENDING, "got %#x\n", ret); + + ret = sendto(server, "data", 5, 0, (struct sockaddr *)&addr, sizeof(addr)); + ok(ret == 5, "got %d\n", ret); + + ret = WaitForSingleObject(event, 100); + ok(!ret, "got %#x\n", ret); + ok(!io.Status, "got %#x\n", io.Status); + ok(io.Information == offsetof(struct afd_poll_params, sockets[1]), "got %#Ix\n", io.Information); + ok(out_params->count == 1, "got count %u\n", out_params->count); + ok(out_params->sockets[0].socket == client, "got socket %#Ix\n", out_params->sockets[0].socket); + ok(out_params->sockets[0].flags == AFD_POLL_READ, "got flags %#x\n", out_params->sockets[0].flags); + ok(!out_params->sockets[0].status, "got status %#x\n", out_params->sockets[0].status); + + closesocket(client); + closesocket(server); + + /* Passing any invalid sockets yields STATUS_INVALID_HANDLE. + * + * Note however that WSAPoll() happily accepts invalid sockets. It seems + * user-side cached data is used: closing a handle with CloseHandle() before + * passing it to WSAPoll() yields ENOTSOCK. */ + + tcp_socketpair(&client, &server); + + in_params->count = 2; + in_params->sockets[0].socket = 0xabacab; + in_params->sockets[0].flags = AFD_POLL_READ | AFD_POLL_WRITE; + in_params->sockets[1].socket = client; + in_params->sockets[1].flags = AFD_POLL_READ | AFD_POLL_WRITE; + params_size = offsetof(struct afd_poll_params, sockets[2]); + + memset(&io, 0, sizeof(io)); + ret = NtDeviceIoControlFile((HANDLE)client, event, NULL, NULL, &io, + IOCTL_AFD_POLL, in_params, params_size, out_params, params_size); + ok(ret == STATUS_INVALID_HANDLE, "got %#x\n", ret); + todo_wine ok(!io.Status, "got %#x\n", io.Status); + ok(!io.Information, "got %#Ix\n", io.Information); + + /* Test passing the same handle twice. */ + + in_params->count = 3; + in_params->sockets[0].socket = client; + in_params->sockets[0].flags = AFD_POLL_READ | AFD_POLL_WRITE; + in_params->sockets[1].socket = client; + in_params->sockets[1].flags = AFD_POLL_READ | AFD_POLL_WRITE; + in_params->sockets[2].socket = client; + in_params->sockets[2].flags = AFD_POLL_READ | AFD_POLL_WRITE | AFD_POLL_CONNECT; + params_size = offsetof(struct afd_poll_params, sockets[3]); + + ret = NtDeviceIoControlFile((HANDLE)client, event, NULL, NULL, &io, + IOCTL_AFD_POLL, in_params, params_size, out_params, params_size); + ok(!ret, "got %#x\n", ret); + ok(!io.Status, "got %#x\n", io.Status); + ok(io.Information == offsetof(struct afd_poll_params, sockets[3]), "got %#Ix\n", io.Information); + ok(out_params->count == 3, "got count %u\n", out_params->count); + ok(out_params->sockets[0].socket == client, "got socket %#Ix\n", out_params->sockets[0].socket); + ok(out_params->sockets[0].flags == AFD_POLL_WRITE, "got flags %#x\n", out_params->sockets[0].flags); + ok(!out_params->sockets[0].status, "got status %#x\n", out_params->sockets[0].status); + ok(out_params->sockets[1].socket == client, "got socket %#Ix\n", out_params->sockets[1].socket); + ok(out_params->sockets[1].flags == AFD_POLL_WRITE, "got flags %#x\n", out_params->sockets[1].flags); + ok(!out_params->sockets[1].status, "got status %#x\n", out_params->sockets[1].status); + ok(out_params->sockets[2].socket == client, "got socket %#Ix\n", out_params->sockets[2].socket); + ok(out_params->sockets[2].flags == (AFD_POLL_WRITE | AFD_POLL_CONNECT), + "got flags %#x\n", out_params->sockets[2].flags); + ok(!out_params->sockets[2].status, "got status %#x\n", out_params->sockets[2].status); + + in_params->count = 2; + in_params->sockets[0].socket = client; + in_params->sockets[0].flags = AFD_POLL_READ; + in_params->sockets[1].socket = client; + in_params->sockets[1].flags = AFD_POLL_READ; + params_size = offsetof(struct afd_poll_params, sockets[2]); + + ret = NtDeviceIoControlFile((HANDLE)client, event, NULL, NULL, &io, + IOCTL_AFD_POLL, in_params, params_size, out_params, params_size); + ok(ret == STATUS_PENDING, "got %#x\n", ret); + + ret = send(server, "data", 5, 0); + ok(ret == 5, "got %d\n", ret); + + ret = WaitForSingleObject(event, 100); + ok(!ret, "got %#x\n", ret); + ok(!io.Status, "got %#x\n", io.Status); + ok(io.Information == offsetof(struct afd_poll_params, sockets[1]), "got %#Ix\n", io.Information); + ok(out_params->count == 1, "got count %u\n", out_params->count); + ok(out_params->sockets[0].socket == client, "got socket %#Ix\n", out_params->sockets[0].socket); + ok(out_params->sockets[0].flags == AFD_POLL_READ, "got flags %#x\n", out_params->sockets[0].flags); + ok(!out_params->sockets[0].status, "got status %#x\n", out_params->sockets[0].status); + + closesocket(client); + closesocket(server); + + CloseHandle(event); + free(large_buffer); +} + +static void test_poll_completion_port(void) +{ + struct afd_poll_params params = {0}; + LARGE_INTEGER zero = {0}; + SOCKET client, server; + ULONG_PTR key, value; + IO_STATUS_BLOCK io; + HANDLE event, port; + int ret; + + event = CreateEventW(NULL, TRUE, FALSE, NULL); + tcp_socketpair(&client, &server); + port = CreateIoCompletionPort((HANDLE)client, NULL, 0, 0); + + params.timeout = -100 * 10000; + params.count = 1; + params.sockets[0].socket = client; + params.sockets[0].flags = AFD_POLL_WRITE; + params.sockets[0].status = 0xdeadbeef; + + ret = NtDeviceIoControlFile((HANDLE)client, event, NULL, NULL, &io, + IOCTL_AFD_POLL, ¶ms, sizeof(params), ¶ms, sizeof(params)); + ok(!ret, "got %#x\n", ret); + + ret = NtRemoveIoCompletion(port, &key, &value, &io, &zero); + ok(ret == STATUS_TIMEOUT, "got %#x\n", ret); + + ret = NtDeviceIoControlFile((HANDLE)client, event, NULL, (void *)0xdeadbeef, &io, + IOCTL_AFD_POLL, ¶ms, sizeof(params), ¶ms, sizeof(params)); + ok(!ret, "got %#x\n", ret); + + ret = NtRemoveIoCompletion(port, &key, &value, &io, &zero); + ok(!ret, "got %#x\n", ret); + ok(!key, "got key %#Ix\n", key); + ok(value == 0xdeadbeef, "got value %#Ix\n", value); + + params.timeout = 0; + params.count = 1; + params.sockets[0].socket = client; + params.sockets[0].flags = AFD_POLL_READ; + params.sockets[0].status = 0xdeadbeef; + + ret = NtDeviceIoControlFile((HANDLE)client, event, NULL, (void *)0xdeadbeef, &io, + IOCTL_AFD_POLL, ¶ms, sizeof(params), ¶ms, sizeof(params)); + ok(!ret, "got %#x\n", ret); + + ret = NtRemoveIoCompletion(port, &key, &value, &io, &zero); + ok(!ret, "got %#x\n", ret); + ok(!key, "got key %#Ix\n", key); + ok(value == 0xdeadbeef, "got value %#Ix\n", value); + + /* Close a socket while polling on another. */ + + params.timeout = -100 * 10000; + params.count = 1; + params.sockets[0].socket = server; + params.sockets[0].flags = AFD_POLL_READ; + params.sockets[0].status = 0xdeadbeef; + + ret = NtDeviceIoControlFile((HANDLE)client, event, NULL, (void *)0xdeadbeef, &io, + IOCTL_AFD_POLL, ¶ms, sizeof(params), ¶ms, sizeof(params)); + ok(ret == STATUS_PENDING, "got %#x\n", ret); + + closesocket(client); + + ret = WaitForSingleObject(event, 1000); + ok(!ret, "got %#x\n", ret); + todo_wine ok(io.Status == STATUS_TIMEOUT, "got %#x\n", io.Status); + todo_wine ok(io.Information == offsetof(struct afd_poll_params, sockets[0]), "got %#Ix\n", io.Information); + todo_wine ok(!params.count, "got count %u\n", params.count); + + ret = NtRemoveIoCompletion(port, &key, &value, &io, &zero); + ok(!ret, "got %#x\n", ret); + ok(!key, "got key %#Ix\n", key); + ok(value == 0xdeadbeef, "got value %#Ix\n", value); + + CloseHandle(port); + closesocket(server); + CloseHandle(event); +} + static void test_recv(void) { const struct sockaddr_in bind_addr = {.sin_family = AF_INET, .sin_addr.s_addr = htonl(INADDR_LOOPBACK)}; @@ -435,6 +1116,8 @@ START_TEST(afd) WSAStartup(MAKEWORD(2, 2), &data);
test_open_device(); + test_poll(); + test_poll_completion_port(); test_recv();
WSACleanup();
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=91139
Your paranoid android.
=== build (build log) ===
error: patch failed: dlls/user32/cursoricon.c:1570 error: patch failed: dlls/user32/tests/cursoricon.c:709 error: patch failed: dlls/user32/cursoricon.c:2776 error: patch failed: dlls/user32/tests/cursoricon.c:486 error: patch failed: dlls/user32/cursoricon.c:1667 error: patch failed: dlls/ws2_32/tests/afd.c:76 Task: Patch failed to apply
=== debiant2 (build log) ===
error: patch failed: dlls/user32/cursoricon.c:1570 error: patch failed: dlls/user32/tests/cursoricon.c:709 error: patch failed: dlls/user32/cursoricon.c:2776 error: patch failed: dlls/user32/tests/cursoricon.c:486 error: patch failed: dlls/user32/cursoricon.c:1667 error: patch failed: dlls/ws2_32/tests/afd.c:76 Task: Patch failed to apply
=== debiant2 (build log) ===
error: patch failed: dlls/user32/cursoricon.c:1570 error: patch failed: dlls/user32/tests/cursoricon.c:709 error: patch failed: dlls/user32/cursoricon.c:2776 error: patch failed: dlls/user32/tests/cursoricon.c:486 error: patch failed: dlls/user32/cursoricon.c:1667 error: patch failed: dlls/ws2_32/tests/afd.c:76 Task: Patch failed to apply