[PATCH 4/5] ntdll: Implement IOCTL_AFD_RECV.

Zebediah Figura z.figura12 at gmail.com
Fri May 21 22:08:48 CDT 2021


Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=50366
Signed-off-by: Zebediah Figura <z.figura12 at 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 */
+ at 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 */
+ at REPLY
+    obj_handle_t wait;          /* handle to wait on for blocking recv */
+    unsigned int options;       /* device open options */
+ at 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 );
+}
-- 
2.30.2




More information about the wine-devel mailing list