Signed-off-by: Zebediah Figura z.figura12@gmail.com --- server/async.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-)
diff --git a/server/async.c b/server/async.c index 1ac5117edb9..a5fa61dc6e5 100644 --- a/server/async.c +++ b/server/async.c @@ -154,12 +154,7 @@ void async_terminate( struct async *async, unsigned int status ) { assert( status != STATUS_PENDING );
- if (async->status != STATUS_PENDING) - { - /* already terminated, just update status */ - async->status = status; - return; - } + if (async->status != STATUS_PENDING) return; /* already terminated */
async->status = status; if (async->iosb && async->iosb->status == STATUS_PENDING) async->iosb->status = status; @@ -457,7 +452,7 @@ static int cancel_async( struct process *process, struct object *obj, struct thr restart: LIST_FOR_EACH_ENTRY( async, &process->asyncs, struct async, process_entry ) { - if (async->status == STATUS_CANCELLED) continue; + if (async->status != STATUS_PENDING) continue; if ((!obj || (async->fd && get_fd_user( async->fd ) == obj)) && (!thread || async->thread == thread) && (!iosb || async->data.iosb == iosb))
Steam uses WSASend() with completion ports, reusing OVERLAPPED structures as soon as they are returned from GetQueuedCompletionStatus(). Since completion is queued during the select request in wait_async(), the I/O status block can be reused even before the call to NtDeviceIoControl exits.
This works fine with current Wine, because WSASend() doesn't access the I/O status block after queuing completion. However, a patch that changes it to use wait_async() like other async requests causes NtDeviceIoControlFile to consistently return garbage status codes.
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- I don't love this solution, although I also don't hate it as much as I did when I initially wrote it. It strikes me as unfortunate to overload the return value of the select request in this way. Disentangling a separate "wait_async" request is another option that strikes me as potentially preferable, although it does not help the implementation very much. Returning the async status in a separate reply field requires a lot of plumbing, including adding that field to struct wake_up_reply.
dlls/ntdll/unix/file.c | 15 +++++++-------- server/async.c | 4 ++-- server/thread.c | 8 ++++++++ server/thread.h | 1 + 4 files changed, 18 insertions(+), 10 deletions(-)
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c index c033bb06fa3..8313e3c3160 100644 --- a/dlls/ntdll/unix/file.c +++ b/dlls/ntdll/unix/file.c @@ -4697,10 +4697,9 @@ static async_data_t server_async( HANDLE handle, struct async_fileio *user, HAND return async; }
-static NTSTATUS wait_async( HANDLE handle, BOOL alertable, IO_STATUS_BLOCK *io ) +static NTSTATUS wait_async( HANDLE handle, BOOL alertable ) { - if (NtWaitForSingleObject( handle, alertable, NULL )) return STATUS_PENDING; - return io->u.Status; + return NtWaitForSingleObject( handle, alertable, NULL ); }
/* callback for irp async I/O completion */ @@ -4861,7 +4860,7 @@ static NTSTATUS server_read_file( HANDLE handle, HANDLE event, PIO_APC_ROUTINE a
if (status != STATUS_PENDING) free( async );
- if (wait_handle) status = wait_async( wait_handle, (options & FILE_SYNCHRONOUS_IO_ALERT), io ); + if (wait_handle) status = wait_async( wait_handle, (options & FILE_SYNCHRONOUS_IO_ALERT) ); return status; }
@@ -4899,7 +4898,7 @@ static NTSTATUS server_write_file( HANDLE handle, HANDLE event, PIO_APC_ROUTINE
if (status != STATUS_PENDING) free( async );
- if (wait_handle) status = wait_async( wait_handle, (options & FILE_SYNCHRONOUS_IO_ALERT), io ); + if (wait_handle) status = wait_async( wait_handle, (options & FILE_SYNCHRONOUS_IO_ALERT) ); return status; }
@@ -4944,7 +4943,7 @@ static NTSTATUS server_ioctl_file( HANDLE handle, HANDLE event,
if (status != STATUS_PENDING) free( async );
- if (wait_handle) status = wait_async( wait_handle, (options & FILE_SYNCHRONOUS_IO_ALERT), io ); + if (wait_handle) status = wait_async( wait_handle, (options & FILE_SYNCHRONOUS_IO_ALERT) ); return status; }
@@ -5937,7 +5936,7 @@ NTSTATUS WINAPI NtFlushBuffersFile( HANDLE handle, IO_STATUS_BLOCK *io )
if (ret != STATUS_PENDING) free( async );
- if (wait_handle) ret = wait_async( wait_handle, FALSE, io ); + if (wait_handle) ret = wait_async( wait_handle, FALSE ); }
if (needs_close) close( fd ); @@ -6436,7 +6435,7 @@ NTSTATUS WINAPI NtQueryVolumeInformationFile( HANDLE handle, IO_STATUS_BLOCK *io } SERVER_END_REQ; if (status != STATUS_PENDING) free( async ); - if (wait_handle) status = wait_async( wait_handle, FALSE, io ); + if (wait_handle) status = wait_async( wait_handle, FALSE ); return status; } else if (io->u.Status) return io->u.Status; diff --git a/server/async.c b/server/async.c index a5fa61dc6e5..27af3be52ff 100644 --- a/server/async.c +++ b/server/async.c @@ -118,14 +118,14 @@ static void async_satisfied( struct object *obj, struct wait_queue_entry *entry async->direct_result = 0; }
+ set_wait_status( entry, async->status ); + /* close wait handle here to avoid extra server round trip */ if (async->wait_handle) { close_handle( async->thread->process, async->wait_handle ); async->wait_handle = 0; } - - if (async->status == STATUS_PENDING) make_wait_abandoned( entry ); }
static void async_destroy( struct object *obj ) diff --git a/server/thread.c b/server/thread.c index eb8b0de84b1..4077a752e4a 100644 --- a/server/thread.c +++ b/server/thread.c @@ -67,6 +67,7 @@ struct thread_wait client_ptr_t cookie; /* magic cookie to return to client */ abstime_t when; struct timeout_user *user; + int status; /* status to return (unless STATUS_PENDING) */ struct wait_queue_entry queues[1]; };
@@ -727,6 +728,11 @@ void make_wait_abandoned( struct wait_queue_entry *entry ) entry->wait->abandoned = 1; }
+void set_wait_status( struct wait_queue_entry *entry, int status ) +{ + entry->wait->status = status; +} + /* finish waiting */ static unsigned int end_wait( struct thread *thread, unsigned int status ) { @@ -739,6 +745,7 @@ static unsigned int end_wait( struct thread *thread, unsigned int status )
if (status < wait->count) /* wait satisfied, tell it to the objects */ { + wait->status = status; if (wait->select == SELECT_WAIT_ALL) { for (i = 0, entry = wait->queues; i < wait->count; i++, entry++) @@ -749,6 +756,7 @@ static unsigned int end_wait( struct thread *thread, unsigned int status ) entry = wait->queues + status; entry->obj->ops->satisfied( entry->obj, entry ); } + status = wait->status; if (wait->abandoned) status += STATUS_ABANDONED_WAIT_0; } for (i = 0, entry = wait->queues; i < wait->count; i++, entry++) diff --git a/server/thread.h b/server/thread.h index 74b828f768b..8dcf966a90a 100644 --- a/server/thread.h +++ b/server/thread.h @@ -106,6 +106,7 @@ extern struct thread *get_wait_queue_thread( struct wait_queue_entry *entry ); extern enum select_op get_wait_queue_select_op( struct wait_queue_entry *entry ); extern client_ptr_t get_wait_queue_key( struct wait_queue_entry *entry ); extern void make_wait_abandoned( struct wait_queue_entry *entry ); +extern void set_wait_status( struct wait_queue_entry *entry, int status ); extern void stop_thread( struct thread *thread ); extern int wake_thread( struct thread *thread ); extern int wake_thread_queue_entry( struct wait_queue_entry *entry );
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=91033
Your paranoid android.
=== debiant2 (32 bit report) ===
ntdll: om.c:2307: Test failed: got 87
=== debiant2 (32 bit Chinese:China report) ===
ntdll: om.c:2322: Test failed: got 89
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/ntdll/unix/file.c | 9 --------- dlls/ntdll/unix/server.c | 4 ++-- dlls/ntdll/unix/unix_private.h | 9 +++++++++ 3 files changed, 11 insertions(+), 11 deletions(-)
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c index 8313e3c3160..0db2bd4ffce 100644 --- a/dlls/ntdll/unix/file.c +++ b/dlls/ntdll/unix/file.c @@ -4610,15 +4610,6 @@ NTSTATUS WINAPI NtSetInformationFile( HANDLE handle, IO_STATUS_BLOCK *io, * Asynchronous file I/O * */
-typedef NTSTATUS async_callback_t( void *user, IO_STATUS_BLOCK *io, NTSTATUS status ); - -struct async_fileio -{ - async_callback_t *callback; /* must be the first field */ - struct async_fileio *next; - HANDLE handle; -}; - struct async_fileio_read { struct async_fileio io; diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c index d153fdce0be..10f53bea8ab 100644 --- a/dlls/ntdll/unix/server.c +++ b/dlls/ntdll/unix/server.c @@ -390,12 +390,12 @@ static void invoke_system_apc( const apc_call_t *call, apc_result_t *result, BOO case APC_ASYNC_IO: { IO_STATUS_BLOCK *iosb = wine_server_get_ptr( call->async_io.sb ); - NTSTATUS (**user)(void *, IO_STATUS_BLOCK *, NTSTATUS) = wine_server_get_ptr( call->async_io.user ); + struct async_fileio *user = wine_server_get_ptr( call->async_io.user ); void *saved_frame = get_syscall_frame(); void *frame;
result->type = call->type; - result->async_io.status = (*user)( user, iosb, call->async_io.status ); + result->async_io.status = user->callback( user, iosb, call->async_io.status );
if ((frame = get_syscall_frame()) != saved_frame) { diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h index bd00d4e9d42..3c23562b7a0 100644 --- a/dlls/ntdll/unix/unix_private.h +++ b/dlls/ntdll/unix/unix_private.h @@ -75,6 +75,15 @@ static inline struct ntdll_thread_data *ntdll_get_thread_data(void) return (struct ntdll_thread_data *)&NtCurrentTeb()->GdiTebBatch; }
+typedef NTSTATUS async_callback_t( void *user, IO_STATUS_BLOCK *io, NTSTATUS status ); + +struct async_fileio +{ + async_callback_t *callback; + struct async_fileio *next; + HANDLE handle; +}; + static const SIZE_T page_size = 0x1000; static const SIZE_T teb_size = 0x3000; /* TEB64 + TEB32 */ static const SIZE_T signal_stack_mask = 0xffff;
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=91034
Your paranoid android.
=== debiant2 (32 bit WoW report) ===
ntdll: om.c:2307: Test failed: got 87
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=50366 Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/ntdll/Makefile.in | 1 + dlls/ntdll/unix/file.c | 7 +- dlls/ntdll/unix/socket.c | 313 +++++++++++++++++++++++++++++++++ dlls/ntdll/unix/unix_private.h | 4 + include/wine/afd.h | 17 ++ server/fd.c | 6 + server/file.h | 1 + server/protocol.def | 11 ++ server/sock.c | 83 ++++++++- 9 files changed, 435 insertions(+), 8 deletions(-) create mode 100644 dlls/ntdll/unix/socket.c
diff --git a/dlls/ntdll/Makefile.in b/dlls/ntdll/Makefile.in index 0fdfb83df15..6dfe2eb12f9 100644 --- a/dlls/ntdll/Makefile.in +++ b/dlls/ntdll/Makefile.in @@ -57,6 +57,7 @@ C_SRCS = \ unix/signal_arm64.c \ unix/signal_i386.c \ unix/signal_x86_64.c \ + unix/socket.c \ unix/sync.c \ unix/system.c \ unix/tape.c \ diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c index 0db2bd4ffce..d974968d0ca 100644 --- a/dlls/ntdll/unix/file.c +++ b/dlls/ntdll/unix/file.c @@ -4645,7 +4645,7 @@ struct async_irp
static struct async_fileio *fileio_freelist;
-static void release_fileio( struct async_fileio *io ) +void release_fileio( struct async_fileio *io ) { for (;;) { @@ -4655,7 +4655,7 @@ static void release_fileio( struct async_fileio *io ) } }
-static struct async_fileio *alloc_fileio( DWORD size, async_callback_t callback, HANDLE handle ) +struct async_fileio *alloc_fileio( DWORD size, async_callback_t callback, HANDLE handle ) { /* first free remaining previous fileinfos */ struct async_fileio *io = InterlockedExchangePointer( (void **)&fileio_freelist, NULL ); @@ -5729,6 +5729,9 @@ NTSTATUS WINAPI NtDeviceIoControlFile( HANDLE handle, HANDLE event, PIO_APC_ROUT
switch (device) { + case FILE_DEVICE_BEEP: + status = sock_ioctl( handle, event, apc, apc_context, io, code, in_buffer, in_size, out_buffer, out_size ); + break; case FILE_DEVICE_DISK: case FILE_DEVICE_CD_ROM: case FILE_DEVICE_DVD: diff --git a/dlls/ntdll/unix/socket.c b/dlls/ntdll/unix/socket.c new file mode 100644 index 00000000000..0e262b26611 --- /dev/null +++ b/dlls/ntdll/unix/socket.c @@ -0,0 +1,313 @@ +/* + * Windows sockets + * + * Copyright 2021 Zebediah Figura for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#if 0 +#pragma makedep unix +#endif + +#include "config.h" +#include <errno.h> +#include <unistd.h> +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winioctl.h" +#define USE_WS_PREFIX +#include "winsock2.h" +#include "wine/afd.h" + +#include "unix_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(winsock); + +static async_data_t server_async( HANDLE handle, struct async_fileio *user, HANDLE event, + PIO_APC_ROUTINE apc, void *apc_context, IO_STATUS_BLOCK *io ) +{ + async_data_t async; + async.handle = wine_server_obj_handle( handle ); + async.user = wine_server_client_ptr( user ); + async.iosb = wine_server_client_ptr( io ); + async.event = wine_server_obj_handle( event ); + async.apc = wine_server_client_ptr( apc ); + async.apc_context = wine_server_client_ptr( apc_context ); + return async; +} + +static NTSTATUS wait_async( HANDLE handle, BOOL alertable ) +{ + return NtWaitForSingleObject( handle, alertable, NULL ); +} + +struct async_recv_ioctl +{ + struct async_fileio io; + int unix_flags; + unsigned int count; + struct iovec iov[1]; +}; + +static NTSTATUS sock_errno_to_status( int err ) +{ + switch (err) + { + case EBADF: return STATUS_INVALID_HANDLE; + case EBUSY: return STATUS_DEVICE_BUSY; + case EPERM: + case EACCES: return STATUS_ACCESS_DENIED; + case EFAULT: return STATUS_ACCESS_VIOLATION; + case EINVAL: return STATUS_INVALID_PARAMETER; + case ENFILE: + case EMFILE: return STATUS_TOO_MANY_OPENED_FILES; + case EINPROGRESS: + case EWOULDBLOCK: return STATUS_DEVICE_NOT_READY; + case EALREADY: return STATUS_NETWORK_BUSY; + case ENOTSOCK: return STATUS_OBJECT_TYPE_MISMATCH; + case EDESTADDRREQ: return STATUS_INVALID_PARAMETER; + case EMSGSIZE: return STATUS_BUFFER_OVERFLOW; + case EPROTONOSUPPORT: + case ESOCKTNOSUPPORT: + case EPFNOSUPPORT: + case EAFNOSUPPORT: + case EPROTOTYPE: return STATUS_NOT_SUPPORTED; + case ENOPROTOOPT: return STATUS_INVALID_PARAMETER; + case EOPNOTSUPP: return STATUS_NOT_SUPPORTED; + case EADDRINUSE: return STATUS_SHARING_VIOLATION; + case EADDRNOTAVAIL: return STATUS_INVALID_PARAMETER; + case ECONNREFUSED: return STATUS_CONNECTION_REFUSED; + case ESHUTDOWN: return STATUS_PIPE_DISCONNECTED; + case ENOTCONN: return STATUS_INVALID_CONNECTION; + case ETIMEDOUT: return STATUS_IO_TIMEOUT; + case ENETUNREACH: return STATUS_NETWORK_UNREACHABLE; + case EHOSTUNREACH: return STATUS_HOST_UNREACHABLE; + case ENETDOWN: return STATUS_NETWORK_BUSY; + case EPIPE: + case ECONNRESET: return STATUS_CONNECTION_RESET; + case ECONNABORTED: return STATUS_CONNECTION_ABORTED; + case EISCONN: return STATUS_CONNECTION_ACTIVE; + + case 0: return STATUS_SUCCESS; + default: + FIXME( "unknown errno %d\n", err ); + return STATUS_UNSUCCESSFUL; + } +} + +extern ssize_t CDECL __wine_locked_recvmsg( int fd, struct msghdr *hdr, int flags ); + +static NTSTATUS try_recv( int fd, struct async_recv_ioctl *async, ULONG_PTR *size ) +{ +#ifndef HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS + char control_buffer[512]; +#endif + struct msghdr hdr; + ssize_t ret; + + memset( &hdr, 0, sizeof(hdr) ); + hdr.msg_iov = async->iov; + hdr.msg_iovlen = async->count; +#ifndef HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS + hdr.msg_control = control_buffer; + hdr.msg_controllen = sizeof(control_buffer); +#endif + while ((ret = __wine_locked_recvmsg( fd, &hdr, async->unix_flags )) < 0 && errno == EINTR); + + if (ret < 0) + { + /* Unix-like systems return EINVAL when attempting to read OOB data from + * an empty socket buffer; Windows returns WSAEWOULDBLOCK. */ + if ((async->unix_flags & MSG_OOB) && errno == EINVAL) + errno = EWOULDBLOCK; + + if (errno != EWOULDBLOCK) WARN( "recvmsg: %s\n", strerror( errno ) ); + return sock_errno_to_status( errno ); + } + + *size = ret; + return (hdr.msg_flags & MSG_TRUNC) ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS; +} + +static NTSTATUS async_recv_proc( void *user, IO_STATUS_BLOCK *io, NTSTATUS status ) +{ + struct async_recv_ioctl *async = user; + ULONG_PTR information = 0; + int fd, needs_close; + + TRACE( "%#x\n", status ); + + if (status == STATUS_ALERTED) + { + if ((status = server_get_unix_fd( async->io.handle, 0, &fd, &needs_close, NULL, NULL ))) + return status; + + status = try_recv( fd, async, &information ); + TRACE( "got status %#x, %#lx bytes read\n", status, information ); + + if (status == STATUS_DEVICE_NOT_READY) + status = STATUS_PENDING; + + if (needs_close) close( fd ); + } + if (status != STATUS_PENDING) + { + io->Status = status; + io->Information = information; + release_fileio( &async->io ); + } + return status; +} + +static NTSTATUS sock_recv( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc_user, IO_STATUS_BLOCK *io, + int fd, const WSABUF *buffers, unsigned int count, int unix_flags, int force_async ) +{ + struct async_recv_ioctl *async; + ULONG_PTR information; + HANDLE wait_handle; + DWORD async_size; + NTSTATUS status; + unsigned int i; + ULONG options; + + async_size = offsetof( struct async_recv_ioctl, iov[count] ); + + if (!(async = (struct async_recv_ioctl *)alloc_fileio( async_size, async_recv_proc, handle ))) + return STATUS_NO_MEMORY; + + async->count = count; + for (i = 0; i < count; ++i) + { + async->iov[i].iov_base = buffers[i].buf; + async->iov[i].iov_len = buffers[i].len; + } + async->unix_flags = unix_flags; + + status = try_recv( fd, async, &information ); + + if (status != STATUS_SUCCESS && status != STATUS_BUFFER_OVERFLOW && status != STATUS_DEVICE_NOT_READY) + { + release_fileio( &async->io ); + return status; + } + + if (status == STATUS_DEVICE_NOT_READY && force_async) + status = STATUS_PENDING; + + if (!NT_ERROR(status)) + { + io->Status = status; + io->Information = information; + } + + SERVER_START_REQ( recv_socket ) + { + req->status = status; + req->total = information; + req->async = server_async( handle, &async->io, event, apc, apc_user, io ); + status = wine_server_call( req ); + wait_handle = wine_server_ptr_handle( reply->wait ); + options = reply->options; + } + SERVER_END_REQ; + + if (status != STATUS_PENDING) 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 ) +{ + int fd, needs_close; + NTSTATUS status; + + TRACE( "handle %p, code %#x, in_buffer %p, in_size %u, out_buffer %p, out_size %u\n", + handle, code, in_buffer, in_size, out_buffer, out_size ); + + if ((status = server_get_unix_fd( handle, 0, &fd, &needs_close, NULL, NULL ))) + return status; + + switch (code) + { + case IOCTL_AFD_LISTEN: + { + const struct afd_listen_params *params = in_buffer; + + TRACE( "backlog %u\n", params->backlog ); + if (out_size) FIXME( "unexpected output size %u\n", out_size ); + if (params->unknown1) FIXME( "listen: got unknown1 %#x\n", params->unknown1 ); + if (params->unknown2) FIXME( "listen: got unknown2 %#x\n", params->unknown2 ); + + status = STATUS_BAD_DEVICE_TYPE; + break; + } + + case IOCTL_AFD_RECV: + { + const struct afd_recv_params *params = in_buffer; + int unix_flags = 0; + + if (out_size) FIXME( "unexpected output size %u\n", out_size ); + + if (in_size < sizeof(struct afd_recv_params)) + { + status = STATUS_INVALID_PARAMETER; + break; + } + + if ((params->msg_flags & (AFD_MSG_NOT_OOB | AFD_MSG_OOB)) == 0 || + (params->msg_flags & (AFD_MSG_NOT_OOB | AFD_MSG_OOB)) == (AFD_MSG_NOT_OOB | AFD_MSG_OOB)) + { + status = STATUS_INVALID_PARAMETER; + break; + } + + if (params->msg_flags & ~(AFD_MSG_NOT_OOB | AFD_MSG_OOB | AFD_MSG_PEEK | AFD_MSG_WAITALL)) + FIXME( "unknown msg_flags %#x\n", params->msg_flags ); + if (params->recv_flags & ~AFD_RECV_FORCE_ASYNC) + FIXME( "unknown recv_flags %#x\n", params->recv_flags ); + + if (params->msg_flags & AFD_MSG_OOB) + unix_flags |= MSG_OOB; + if (params->msg_flags & AFD_MSG_PEEK) + unix_flags |= MSG_PEEK; + if (params->msg_flags & AFD_MSG_WAITALL) + FIXME( "MSG_WAITALL is not supported\n" ); + + status = sock_recv( handle, event, apc, apc_user, io, fd, params->buffers, params->count, + unix_flags, !!(params->recv_flags & AFD_RECV_FORCE_ASYNC) ); + break; + } + + default: + { + FIXME( "Unknown ioctl %#x (device %#x, access %#x, function %#x, method %#x)\n", + code, code >> 16, (code >> 14) & 3, (code >> 2) & 0xfff, code & 3 ); + status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + } + + if (needs_close) close( fd ); + return status; +} diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h index 3c23562b7a0..15a4387bdc0 100644 --- a/dlls/ntdll/unix/unix_private.h +++ b/dlls/ntdll/unix/unix_private.h @@ -257,10 +257,14 @@ extern NTSTATUS serial_DeviceIoControl( HANDLE device, HANDLE event, PIO_APC_ROU IO_STATUS_BLOCK *io, ULONG code, void *in_buffer, ULONG in_size, void *out_buffer, ULONG out_size ) DECLSPEC_HIDDEN; extern NTSTATUS serial_FlushBuffersFile( int fd ) DECLSPEC_HIDDEN; +extern 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 ) DECLSPEC_HIDDEN; extern NTSTATUS tape_DeviceIoControl( HANDLE device, 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 ) DECLSPEC_HIDDEN;
+extern struct async_fileio *alloc_fileio( DWORD size, async_callback_t callback, HANDLE handle ) DECLSPEC_HIDDEN; +extern void release_fileio( struct async_fileio *io ) DECLSPEC_HIDDEN; extern NTSTATUS errno_to_status( int err ) DECLSPEC_HIDDEN; extern BOOL get_redirect( OBJECT_ATTRIBUTES *attr, UNICODE_STRING *redir ) DECLSPEC_HIDDEN; extern NTSTATUS nt_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, char **name_ret, UINT disposition ) DECLSPEC_HIDDEN; diff --git a/include/wine/afd.h b/include/wine/afd.h index 30429b02324..a83ce7c1bac 100644 --- a/include/wine/afd.h +++ b/include/wine/afd.h @@ -21,10 +21,12 @@ #ifndef __WINE_WINE_AFD_H #define __WINE_WINE_AFD_H
+#include <winternl.h> #include <winioctl.h> #include "wine/server_protocol.h"
#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)
struct afd_listen_params { @@ -33,6 +35,21 @@ struct afd_listen_params int unknown2; };
+#define AFD_RECV_FORCE_ASYNC 0x2 + +#define AFD_MSG_NOT_OOB 0x0020 +#define AFD_MSG_OOB 0x0040 +#define AFD_MSG_PEEK 0x0080 +#define AFD_MSG_WAITALL 0x4000 + +struct afd_recv_params +{ + const WSABUF *buffers; + unsigned int count; + int recv_flags; + int msg_flags; +}; + #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/fd.c b/server/fd.c index e7253ec8f51..bfa2805d82b 100644 --- a/server/fd.c +++ b/server/fd.c @@ -2076,6 +2076,12 @@ unsigned int get_fd_options( struct fd *fd ) return fd->options; }
+/* retrieve the completion flags for the fd */ +unsigned int get_fd_comp_flags( struct fd *fd ) +{ + return fd->comp_flags; +} + /* check if fd is in overlapped mode */ int is_fd_overlapped( struct fd *fd ) { diff --git a/server/file.h b/server/file.h index 0fa66e5750a..ba97a833c71 100644 --- a/server/file.h +++ b/server/file.h @@ -89,6 +89,7 @@ extern struct fd *get_fd_object_for_mapping( struct fd *fd, unsigned int access, extern void *get_fd_user( struct fd *fd ); extern void set_fd_user( struct fd *fd, const struct fd_ops *ops, struct object *user ); extern unsigned int get_fd_options( struct fd *fd ); +extern unsigned int get_fd_comp_flags( struct fd *fd ); extern int is_fd_overlapped( struct fd *fd ); extern int get_unix_fd( struct fd *fd ); extern int is_same_file_fd( struct fd *fd1, struct fd *fd2 ); diff --git a/server/protocol.def b/server/protocol.def index 6d8208b128b..8020e201611 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -1477,6 +1477,17 @@ enum server_fd_type @END
+/* Perform a recv on a socket */ +@REQ(recv_socket) + async_data_t async; /* async I/O parameters */ + unsigned int status; /* status of initial call */ + unsigned int total; /* number of bytes already read */ +@REPLY + obj_handle_t wait; /* handle to wait on for blocking recv */ + unsigned int options; /* device open options */ +@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 b523e8db780..7f8d9760afb 100644 --- a/server/sock.c +++ b/server/sock.c @@ -653,14 +653,15 @@ static int sock_dispatch_asyncs( struct sock *sock, int event, int error ) async_terminate( sock->connect_req->async, get_error() ); }
+ if (event & (POLLIN | POLLPRI) && async_waiting( &sock->read_q )) + { + if (debug_level) fprintf( stderr, "activating read queue for socket %p\n", sock ); + async_wake_up( &sock->read_q, STATUS_ALERTED ); + event &= ~(POLLIN | POLLPRI); + } + if (is_fd_overlapped( sock->fd )) { - if (event & (POLLIN|POLLPRI) && async_waiting( &sock->read_q )) - { - if (debug_level) fprintf( stderr, "activating read queue for socket %p\n", sock ); - async_wake_up( &sock->read_q, STATUS_ALERTED ); - event &= ~(POLLIN|POLLPRI); - } if (event & POLLOUT && async_waiting( &sock->write_q )) { if (debug_level) fprintf( stderr, "activating write queue for socket %p\n", sock ); @@ -2129,3 +2130,73 @@ DECL_HANDLER(get_socket_info)
release_object( &sock->obj ); } + +DECL_HANDLER(recv_socket) +{ + struct sock *sock = (struct sock *)get_handle_obj( current->process, req->async.handle, 0, &sock_ops ); + unsigned int status = req->status; + timeout_t timeout = 0; + struct async *async; + struct fd *fd; + + if (!sock) return; + fd = sock->fd; + + /* recv() returned EWOULDBLOCK, i.e. no data available yet */ + if (status == STATUS_DEVICE_NOT_READY && !(sock->state & FD_WINE_NONBLOCKING)) + { +#ifdef SO_RCVTIMEO + struct timeval tv; + socklen_t len = sizeof(tv); + + /* Set a timeout on the async if necessary. + * + * We want to do this *only* if the client gave us STATUS_DEVICE_NOT_READY. + * If the client gave us STATUS_PENDING, it expects the async to always + * block (it was triggered by WSARecv*() with a valid OVERLAPPED + * structure) and for the timeout not to be respected. */ + if (is_fd_overlapped( fd ) && !getsockopt( get_unix_fd( fd ), SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, &len )) + timeout = tv.tv_sec * -10000000 + tv.tv_usec * -10; +#endif + + status = STATUS_PENDING; + } + + /* are we shut down? */ + if (status == STATUS_PENDING && !(sock->state & FD_READ)) status = STATUS_PIPE_DISCONNECTED; + + sock->pending_events &= ~FD_READ; + sock->reported_events &= ~FD_READ; + + if ((async = create_request_async( fd, get_fd_comp_flags( fd ), &req->async ))) + { + int success = 0; + + if (status == STATUS_SUCCESS) + { + struct iosb *iosb = async_get_iosb( async ); + iosb->result = req->total; + release_object( iosb ); + success = 1; + } + else if (status == STATUS_PENDING) + { + success = 1; + } + set_error( status ); + + if (timeout) + async_set_timeout( async, timeout, STATUS_IO_TIMEOUT ); + + if (status == STATUS_PENDING) + queue_async( &sock->read_q, async ); + + /* always reselect; we changed reported_events above */ + sock_reselect( sock ); + + reply->wait = async_handoff( async, success, NULL, 0 ); + reply->options = get_fd_options( fd ); + release_object( async ); + } + release_object( sock ); +}
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/ws2_32/tests/afd.c | 438 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 438 insertions(+) create mode 100644 dlls/ws2_32/tests/afd.c
diff --git a/dlls/ws2_32/tests/afd.c b/dlls/ws2_32/tests/afd.c new file mode 100644 index 00000000000..f79263fedb9 --- /dev/null +++ b/dlls/ws2_32/tests/afd.c @@ -0,0 +1,438 @@ +/* + * Unit tests for AFD device ioctls + * + * Copyright 2021 Zebediah Figura for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <limits.h> +#include <stdarg.h> +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winternl.h" +#include "winioctl.h" +#include "winsock2.h" +#include "ws2tcpip.h" +#include "mswsock.h" +#include "wine/afd.h" +#include "wine/test.h" + +static void tcp_socketpair_ovl(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; + blocking = !blocking; + ret = ioctlsocket(s, FIONBIO, &blocking); + ok(!ret, "got error %u\n", WSAGetLastError()); +} + +static void test_recv(void) +{ + const struct sockaddr_in bind_addr = {.sin_family = AF_INET, .sin_addr.s_addr = htonl(INADDR_LOOPBACK)}; + struct afd_recv_params params = {0}; + SOCKET client, server, listener; + struct sockaddr addr; + IO_STATUS_BLOCK io; + WSABUF wsabufs[2]; + char buffer[8]; + HANDLE event; + int ret, len; + + 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()); + + memset(&io, 0, sizeof(io)); + ret = NtDeviceIoControlFile((HANDLE)listener, event, NULL, NULL, &io, IOCTL_AFD_RECV, NULL, 0, NULL, 0); + todo_wine ok(ret == STATUS_INVALID_CONNECTION, "got %#x\n", ret); + todo_wine ok(!io.Status, "got status %#x\n", io.Status); + ok(!io.Information, "got information %#Ix\n", io.Information); + + 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()); + + memset(&io, 0, sizeof(io)); + ret = NtDeviceIoControlFile((HANDLE)client, event, NULL, NULL, &io, IOCTL_AFD_RECV, NULL, 0, NULL, 0); + ok(ret == STATUS_INVALID_PARAMETER, "got %#x\n", ret); + todo_wine ok(!io.Status, "got status %#x\n", io.Status); + ok(!io.Information, "got information %#Ix\n", io.Information); + + wsabufs[0].len = sizeof(buffer); + wsabufs[0].buf = buffer; + params.buffers = wsabufs; + params.count = 1; + params.msg_flags = AFD_MSG_NOT_OOB; + + ret = NtDeviceIoControlFile((HANDLE)client, event, NULL, NULL, &io, + IOCTL_AFD_RECV, ¶ms, sizeof(params) - 1, NULL, 0); + ok(ret == STATUS_INVALID_PARAMETER, "got %#x\n", ret); + + memset(&io, 0, sizeof(io)); + memset(buffer, 0xcc, sizeof(buffer)); + ret = NtDeviceIoControlFile((HANDLE)client, event, NULL, NULL, &io, + IOCTL_AFD_RECV, ¶ms, sizeof(params), NULL, 0); + ok(ret == STATUS_PENDING, "got %#x\n", ret); + ok(!io.Status, "got status %#x\n", io.Status); + ok(!io.Information, "got information %#Ix\n", io.Information); + + /* These structures need not remain valid. */ + memset(¶ms, 0xcc, sizeof(params)); + memset(wsabufs, 0xcc, sizeof(wsabufs)); + + ret = send(server, "data", 5, 0); + ok(ret == 5, "got %d\n", ret); + + ret = WaitForSingleObject(event, 200); + ok(!ret, "wait timed out\n"); + ok(!io.Status, "got %#x\n", io.Status); + ok(io.Information == 5, "got %#Ix\n", io.Information); + ok(!strcmp(buffer, "data"), "got %s\n", debugstr_an(buffer, io.Information)); + + /* Test with multiple buffers. */ + + wsabufs[0].len = 2; + wsabufs[0].buf = buffer; + wsabufs[1].len = 4; + wsabufs[1].buf = buffer + 3; + memset(¶ms, 0, sizeof(params)); + params.buffers = wsabufs; + params.count = 2; + params.msg_flags = AFD_MSG_NOT_OOB; + + memset(&io, 0, sizeof(io)); + memset(buffer, 0xcc, sizeof(buffer)); + ret = NtDeviceIoControlFile((HANDLE)client, event, NULL, NULL, &io, + IOCTL_AFD_RECV, ¶ms, sizeof(params), NULL, 0); + ok(ret == STATUS_PENDING, "got %#x\n", ret); + ok(!io.Status, "got status %#x\n", io.Status); + ok(!io.Information, "got information %#Ix\n", io.Information); + + ret = send(server, "data", 5, 0); + ok(ret == 5, "got %d\n", ret); + + ret = WaitForSingleObject(event, 200); + ok(!ret, "wait timed out\n"); + ok(!io.Status, "got %#x\n", io.Status); + ok(io.Information == 5, "got %#Ix\n", io.Information); + ok(!strcmp(buffer, "da\xccta"), "got %s\n", debugstr_an(buffer, io.Information)); + + /* Test synchronous return. */ + + ret = send(server, "data", 5, 0); + ok(ret == 5, "got %d\n", ret); + + memset(&io, 0xcc, sizeof(io)); + memset(buffer, 0xcc, sizeof(buffer)); + ret = NtDeviceIoControlFile((HANDLE)client, event, NULL, NULL, &io, + IOCTL_AFD_RECV, ¶ms, sizeof(params), NULL, 0); + ok(!ret, "got %#x\n", ret); + ok(!io.Status, "got %#x\n", io.Status); + ok(io.Information == 5, "got %#Ix\n", io.Information); + ok(!strcmp(buffer, "da\xccta"), "got %s\n", debugstr_an(buffer, io.Information)); + + /* Test nonblocking mode. */ + + set_blocking(client, FALSE); + + memset(&io, 0, sizeof(io)); + memset(buffer, 0xcc, sizeof(buffer)); + ret = NtDeviceIoControlFile((HANDLE)client, event, NULL, NULL, &io, + IOCTL_AFD_RECV, ¶ms, sizeof(params), NULL, 0); + ok(ret == STATUS_DEVICE_NOT_READY, "got %#x\n", ret); + todo_wine ok(!io.Status, "got status %#x\n", io.Status); + ok(!io.Information, "got information %#Ix\n", io.Information); + + ret = send(server, "data", 5, 0); + ok(ret == 5, "got %d\n", ret); + + memset(&io, 0xcc, sizeof(io)); + memset(buffer, 0xcc, sizeof(buffer)); + ret = NtDeviceIoControlFile((HANDLE)client, event, NULL, NULL, &io, + IOCTL_AFD_RECV, ¶ms, sizeof(params), NULL, 0); + ok(!ret, "got %#x\n", ret); + ok(!io.Status, "got %#x\n", io.Status); + ok(io.Information == 5, "got %#Ix\n", io.Information); + ok(!strcmp(buffer, "da\xccta"), "got %s\n", debugstr_an(buffer, io.Information)); + + params.recv_flags = AFD_RECV_FORCE_ASYNC; + + memset(&io, 0, sizeof(io)); + memset(buffer, 0xcc, sizeof(buffer)); + ret = NtDeviceIoControlFile((HANDLE)client, event, NULL, NULL, &io, + IOCTL_AFD_RECV, ¶ms, sizeof(params), NULL, 0); + ok(ret == STATUS_PENDING, "got %#x\n", ret); + todo_wine ok(!io.Status, "got status %#x\n", io.Status); + todo_wine ok(!io.Information, "got information %#Ix\n", io.Information); + + ret = send(server, "data", 5, 0); + ok(ret == 5, "got %d\n", ret); + + ret = WaitForSingleObject(event, 200); + ok(!ret, "wait timed out\n"); + ok(!io.Status, "got %#x\n", io.Status); + ok(io.Information == 5, "got %#Ix\n", io.Information); + ok(!strcmp(buffer, "da\xccta"), "got %s\n", debugstr_an(buffer, io.Information)); + + params.recv_flags = 0; + + set_blocking(client, TRUE); + + /* Test flags. */ + + ret = send(server, "a", 1, MSG_OOB); + ok(ret == 1, "got %d\n", ret); + + ret = send(server, "data", 5, 0); + ok(ret == 5, "got %d\n", ret); + + memset(&io, 0xcc, sizeof(io)); + memset(buffer, 0xcc, sizeof(buffer)); + ret = NtDeviceIoControlFile((HANDLE)client, event, NULL, NULL, &io, + IOCTL_AFD_RECV, ¶ms, sizeof(params), NULL, 0); + ok(!ret, "got %#x\n", ret); + ok(!io.Status, "got %#x\n", io.Status); + ok(io.Information == 5, "got %#Ix\n", io.Information); + ok(!strcmp(buffer, "da\xccta"), "got %s\n", debugstr_an(buffer, io.Information)); + + params.msg_flags = 0; + + io.Status = 0xdeadbeef; + ret = NtDeviceIoControlFile((HANDLE)client, event, NULL, NULL, &io, + IOCTL_AFD_RECV, ¶ms, sizeof(params), NULL, 0); + ok(ret == STATUS_INVALID_PARAMETER, "got %#x\n", ret); + todo_wine ok(io.Status == 0xdeadbeef, "got %#x\n", io.Status); + + params.msg_flags = AFD_MSG_OOB | AFD_MSG_NOT_OOB; + + io.Status = 0xdeadbeef; + ret = NtDeviceIoControlFile((HANDLE)client, event, NULL, NULL, &io, + IOCTL_AFD_RECV, ¶ms, sizeof(params), NULL, 0); + ok(ret == STATUS_INVALID_PARAMETER, "got %#x\n", ret); + todo_wine ok(io.Status == 0xdeadbeef, "got %#x\n", io.Status); + + params.msg_flags = AFD_MSG_OOB; + + memset(buffer, 0xcc, sizeof(buffer)); + ret = NtDeviceIoControlFile((HANDLE)client, event, NULL, NULL, &io, + IOCTL_AFD_RECV, ¶ms, sizeof(params), NULL, 0); + todo_wine ok(!ret, "got %#x\n", ret); + todo_wine ok(!io.Status, "got %#x\n", io.Status); + todo_wine ok(io.Information == 1, "got %#Ix\n", io.Information); + todo_wine ok(buffer[0] == 'a', "got %s\n", debugstr_an(buffer, io.Information)); + + params.msg_flags = AFD_MSG_NOT_OOB | AFD_MSG_PEEK; + + ret = send(server, "data", 4, 0); + ok(ret == 4, "got %d\n", ret); + + memset(buffer, 0xcc, sizeof(buffer)); + ret = NtDeviceIoControlFile((HANDLE)client, event, NULL, NULL, &io, + IOCTL_AFD_RECV, ¶ms, sizeof(params), NULL, 0); + ok(!ret, "got %#x\n", ret); + ok(!io.Status, "got %#x\n", io.Status); + ok(io.Information == 4, "got %#Ix\n", io.Information); + ok(!memcmp(buffer, "da\xccta", 5), "got %s\n", debugstr_an(buffer, io.Information)); + + memset(buffer, 0xcc, sizeof(buffer)); + ret = NtDeviceIoControlFile((HANDLE)client, event, NULL, NULL, &io, + IOCTL_AFD_RECV, ¶ms, sizeof(params), NULL, 0); + ok(!ret, "got %#x\n", ret); + ok(!io.Status, "got %#x\n", io.Status); + ok(io.Information == 4, "got %#Ix\n", io.Information); + ok(!memcmp(buffer, "da\xccta", 5), "got %s\n", debugstr_an(buffer, io.Information)); + + params.msg_flags = AFD_MSG_NOT_OOB | AFD_MSG_WAITALL; + + memset(buffer, 0xcc, sizeof(buffer)); + ret = NtDeviceIoControlFile((HANDLE)client, event, NULL, NULL, &io, + IOCTL_AFD_RECV, ¶ms, sizeof(params), NULL, 0); + todo_wine ok(ret == STATUS_PENDING, "got %#x\n", ret); + + if (ret == STATUS_PENDING) + { + ret = send(server, "s", 2, 0); + ok(ret == 2, "got %d\n", ret); + + ret = WaitForSingleObject(event, 200); + ok(!ret, "wait timed out\n"); + ok(!io.Status, "got %#x\n", io.Status); + ok(io.Information == 6, "got %#Ix\n", io.Information); + ok(!strcmp(buffer, "da\xcctas"), "got %s\n", debugstr_an(buffer, io.Information)); + } + + params.msg_flags = AFD_MSG_NOT_OOB; + + /* Test shutdown. */ + + memset(buffer, 0xcc, sizeof(buffer)); + ret = NtDeviceIoControlFile((HANDLE)client, event, NULL, NULL, &io, + IOCTL_AFD_RECV, ¶ms, sizeof(params), NULL, 0); + ok(ret == STATUS_PENDING, "got %#x\n", ret); + + closesocket(server); + + ret = WaitForSingleObject(event, 200); + ok(!ret, "wait timed out\n"); + ok(!io.Status, "got %#x\n", io.Status); + ok(!io.Information, "got %#Ix\n", io.Information); + + ret = shutdown(client, SD_RECEIVE); + ok(!ret, "got error %u\n", WSAGetLastError()); + + memset(&io, 0, sizeof(io)); + memset(buffer, 0xcc, sizeof(buffer)); + ret = NtDeviceIoControlFile((HANDLE)client, event, NULL, NULL, &io, + IOCTL_AFD_RECV, ¶ms, sizeof(params), NULL, 0); + todo_wine ok(ret == STATUS_PIPE_DISCONNECTED, "got %#x\n", ret); + ok(!io.Status, "got status %#x\n", io.Status); + ok(!io.Information, "got information %#Ix\n", io.Information); + + closesocket(client); + closesocket(listener); + + /* Test UDP datagrams. */ + + client = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + server = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + + memset(buffer, 0xcc, sizeof(buffer)); + ret = NtDeviceIoControlFile((HANDLE)client, event, NULL, NULL, &io, + IOCTL_AFD_RECV, ¶ms, sizeof(params), NULL, 0); + todo_wine ok(ret == STATUS_INVALID_PARAMETER, "got %#x\n", ret); + if (ret == STATUS_PENDING) + CancelIo((HANDLE)client); + + 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()); + + memset(buffer, 0xcc, sizeof(buffer)); + ret = NtDeviceIoControlFile((HANDLE)client, event, NULL, NULL, &io, + IOCTL_AFD_RECV, ¶ms, sizeof(params), NULL, 0); + 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, 200); + ok(!ret, "wait timed out\n"); + ok(!io.Status, "got %#x\n", io.Status); + ok(io.Information == 5, "got %#Ix\n", io.Information); + ok(!strcmp(buffer, "da\xccta"), "got %s\n", debugstr_an(buffer, io.Information)); + + /* Test a short read of a UDP datagram. */ + + memset(buffer, 0xcc, sizeof(buffer)); + ret = NtDeviceIoControlFile((HANDLE)client, event, NULL, NULL, &io, + IOCTL_AFD_RECV, ¶ms, sizeof(params), NULL, 0); + ok(ret == STATUS_PENDING, "got %#x\n", ret); + + ret = sendto(server, "moredata", 9, 0, (struct sockaddr *)&addr, sizeof(addr)); + ok(ret == 9, "got %d\n", ret); + + ret = WaitForSingleObject(event, 200); + ok(!ret, "wait timed out\n"); + ok(io.Status == STATUS_BUFFER_OVERFLOW, "got %#x\n", io.Status); + ok(io.Information == 6, "got %#Ix\n", io.Information); + ok(!memcmp(buffer, "mo\xccreda\xcc", 7), "got %s\n", debugstr_an(buffer, io.Information)); + + ret = sendto(server, "moredata", 9, 0, (struct sockaddr *)&addr, sizeof(addr)); + ok(ret == 9, "got %d\n", ret); + + memset(&io, 0, sizeof(io)); + memset(buffer, 0xcc, sizeof(buffer)); + ret = NtDeviceIoControlFile((HANDLE)client, event, NULL, NULL, &io, + IOCTL_AFD_RECV, ¶ms, sizeof(params), NULL, 0); + ok(ret == STATUS_BUFFER_OVERFLOW, "got %#x\n", ret); + ok(io.Status == STATUS_BUFFER_OVERFLOW, "got %#x\n", io.Status); + ok(io.Information == 6, "got %#Ix\n", io.Information); + ok(!memcmp(buffer, "mo\xccreda\xcc", 7), "got %s\n", debugstr_an(buffer, io.Information)); + + /* Test closing a socket during an async. */ + + memset(buffer, 0xcc, sizeof(buffer)); + ret = NtDeviceIoControlFile((HANDLE)client, event, NULL, NULL, &io, + IOCTL_AFD_RECV, ¶ms, sizeof(params), NULL, 0); + ok(ret == STATUS_PENDING, "got %#x\n", ret); + + closesocket(client); + + ret = WaitForSingleObject(event, 200); + ok(!ret, "wait timed out\n"); + todo_wine ok(io.Status == STATUS_CANCELLED, "got %#x\n", io.Status); + ok(!io.Information, "got %#Ix\n", io.Information); + + closesocket(server); + CloseHandle(event); +} + +START_TEST(afd) +{ + WSADATA data; + + WSAStartup(MAKEWORD(2, 2), &data); + + 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=91036
Your paranoid android.
=== w2008s64 (32 bit report) ===
ws2_32: Fatal: test 'afd' does not exist.
=== w7u_2qxl (32 bit report) ===
ws2_32: Fatal: test 'afd' does not exist.
=== w7u_adm (32 bit report) ===
ws2_32: Fatal: test 'afd' does not exist.
=== w7u_el (32 bit report) ===
ws2_32: Fatal: test 'afd' does not exist.
=== w8 (32 bit report) ===
ws2_32: Fatal: test 'afd' does not exist.
=== w8adm (32 bit report) ===
ws2_32: Fatal: test 'afd' does not exist.
=== w864 (32 bit report) ===
ws2_32: Fatal: test 'afd' does not exist.
=== w1064v1507 (32 bit report) ===
ws2_32: Fatal: test 'afd' does not exist.
=== w1064v1809 (32 bit report) ===
ws2_32: Fatal: test 'afd' does not exist.
=== w1064 (32 bit report) ===
ws2_32: Fatal: test 'afd' does not exist.
=== w1064_tsign (32 bit report) ===
ws2_32: Fatal: test 'afd' does not exist.
=== w10pro64 (32 bit report) ===
ws2_32: Fatal: test 'afd' does not exist.
=== wvistau64 (64 bit report) ===
ws2_32: Fatal: test 'afd' does not exist.
=== w2008s64 (64 bit report) ===
ws2_32: Fatal: test 'afd' does not exist.
=== w864 (64 bit report) ===
ws2_32: Fatal: test 'afd' does not exist.
=== w1064v1507 (64 bit report) ===
ws2_32: Fatal: test 'afd' does not exist.
=== w1064v1809 (64 bit report) ===
ws2_32: Fatal: test 'afd' does not exist.
=== w1064 (64 bit report) ===
ws2_32: Fatal: test 'afd' does not exist.
=== w1064_2qxl (64 bit report) ===
ws2_32: Fatal: test 'afd' does not exist.
=== w1064_tsign (64 bit report) ===
ws2_32: Fatal: test 'afd' does not exist.
=== w10pro64 (64 bit report) ===
ws2_32: Fatal: test 'afd' does not exist.
=== w10pro64_ar (64 bit report) ===
ws2_32: Fatal: test 'afd' does not exist.
=== w10pro64_he (64 bit report) ===
ws2_32: Fatal: test 'afd' does not exist.
=== w10pro64_ja (64 bit report) ===
ws2_32: Fatal: test 'afd' does not exist.
=== w10pro64_zh_CN (64 bit report) ===
ws2_32: Fatal: test 'afd' does not exist.
On Fri, 21 May 2021, Zebediah Figura wrote:
Signed-off-by: Zebediah Figura z.figura12@gmail.com
dlls/ws2_32/tests/afd.c | 438 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 438 insertions(+) create mode 100644 dlls/ws2_32/tests/afd.c
diff --git a/dlls/ws2_32/tests/afd.c b/dlls/ws2_32/tests/afd.c new file mode 100644 index 00000000000..f79263fedb9 --- /dev/null +++ b/dlls/ws2_32/tests/afd.c
This patch (and/or the ones that followed) introduced some new failures: https://test.winehq.org/data/patterns.html#ws2_32:afd
Also the TestBot was confused by the lack of a patch to ws2_32/tests/Makefile.in. Maybe it should automatically run make_makefiles or something.
On 5/28/21 12:14 PM, Francois Gouget wrote:
On Fri, 21 May 2021, Zebediah Figura wrote:
Signed-off-by: Zebediah Figura z.figura12@gmail.com
dlls/ws2_32/tests/afd.c | 438 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 438 insertions(+) create mode 100644 dlls/ws2_32/tests/afd.c
diff --git a/dlls/ws2_32/tests/afd.c b/dlls/ws2_32/tests/afd.c new file mode 100644 index 00000000000..f79263fedb9 --- /dev/null +++ b/dlls/ws2_32/tests/afd.c
This patch (and/or the ones that followed) introduced some new failures: https://test.winehq.org/data/patterns.html#ws2_32:afd
Thanks for alerting me; I've sent 206912 and 206913.
Also the TestBot was confused by the lack of a patch to ws2_32/tests/Makefile.in. Maybe it should automatically run make_makefiles or something.
That seems like a good idea. If I get a minute I will try to write a patch for that.