[PATCH 1/5] ntdll: Introduce IOCTL_AFD_WINE_TRANSMIT.

Zebediah Figura z.figura12 at gmail.com
Thu May 27 18:57:29 CDT 2021


Signed-off-by: Zebediah Figura <z.figura12 at gmail.com>
---
 dlls/ntdll/unix/socket.c | 212 +++++++++++++++++++++++++++++++++++++++
 include/wine/afd.h       |  12 +++
 2 files changed, 224 insertions(+)

diff --git a/dlls/ntdll/unix/socket.c b/dlls/ntdll/unix/socket.c
index 3d4b98e5871..6112a1260cb 100644
--- a/dlls/ntdll/unix/socket.c
+++ b/dlls/ntdll/unix/socket.c
@@ -71,6 +71,8 @@
 
 WINE_DEFAULT_DEBUG_CHANNEL(winsock);
 
+#define FILE_USE_FILE_POINTER_POSITION ((LONGLONG)-2)
+
 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 )
 {
@@ -126,6 +128,23 @@ struct async_send_ioctl
     struct iovec iov[1];
 };
 
+struct async_transmit_ioctl
+{
+    struct async_fileio io;
+    HANDLE file;
+    char *buffer;
+    unsigned int buffer_size;   /* allocated size of buffer */
+    unsigned int read_len;      /* amount of valid data currently in the buffer */
+    unsigned int head_cursor;   /* amount of header data already sent */
+    unsigned int file_cursor;   /* amount of file data already sent */
+    unsigned int buffer_cursor; /* amount of data currently in the buffer already sent */
+    unsigned int tail_cursor;   /* amount of tail data already sent */
+    unsigned int file_len;      /* total file length to send */
+    DWORD flags;
+    TRANSMIT_FILE_BUFFERS buffers;
+    LARGE_INTEGER offset;
+};
+
 static NTSTATUS sock_errno_to_status( int err )
 {
     switch (err)
@@ -921,6 +940,182 @@ static NTSTATUS sock_send( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, voi
     return status;
 }
 
+static ssize_t do_send( int fd, const void *buffer, size_t len, int flags )
+{
+    ssize_t ret;
+    while ((ret = send( fd, buffer, len, flags )) < 0 && errno == EINTR);
+    if (ret < 0 && errno != EWOULDBLOCK) WARN( "send: %s\n", strerror( errno ) );
+    return ret;
+}
+
+static NTSTATUS try_transmit( int sock_fd, int file_fd, struct async_transmit_ioctl *async )
+{
+    ssize_t ret;
+
+    while (async->head_cursor < async->buffers.HeadLength)
+    {
+        TRACE( "sending %u bytes of header data\n", async->buffers.HeadLength - async->head_cursor );
+        ret = do_send( sock_fd, (char *)async->buffers.Head + async->head_cursor,
+                       async->buffers.HeadLength - async->head_cursor, 0 );
+        if (ret < 0) return sock_errno_to_status( errno );
+        TRACE( "send returned %zd\n", ret );
+        async->head_cursor += ret;
+    }
+
+    while (async->buffer_cursor < async->read_len)
+    {
+        TRACE( "sending %u bytes of file data\n", async->read_len - async->buffer_cursor );
+        ret = do_send( sock_fd, async->buffer + async->buffer_cursor,
+                       async->read_len - async->buffer_cursor, 0 );
+        if (ret < 0) return sock_errno_to_status( errno );
+        TRACE( "send returned %zd\n", ret );
+        async->buffer_cursor += ret;
+        async->file_cursor += ret;
+    }
+
+    if (async->file && async->buffer_cursor == async->read_len)
+    {
+        unsigned int read_size = async->buffer_size;
+
+        if (async->file_len)
+            read_size = min( read_size, async->file_len - async->file_cursor );
+
+        TRACE( "reading %u bytes of file data\n", read_size );
+        do
+        {
+            if (async->offset.QuadPart == FILE_USE_FILE_POINTER_POSITION)
+                ret = read( file_fd, async->buffer, read_size );
+            else
+                ret = pread( file_fd, async->buffer, read_size, async->offset.QuadPart );
+        } while (ret < 0 && errno == EINTR);
+        if (ret < 0) return errno_to_status( errno );
+        TRACE( "read returned %zd\n", ret );
+
+        async->read_len = ret;
+        async->buffer_cursor = 0;
+        if (async->offset.QuadPart != FILE_USE_FILE_POINTER_POSITION)
+            async->offset.QuadPart += ret;
+
+        if (ret < read_size || (async->file_len && async->file_cursor == async->file_len))
+            async->file = NULL;
+        return STATUS_PENDING; /* still more data to send */
+    }
+
+    while (async->tail_cursor < async->buffers.TailLength)
+    {
+        TRACE( "sending %u bytes of tail data\n", async->buffers.TailLength - async->tail_cursor );
+        ret = do_send( sock_fd, (char *)async->buffers.Tail + async->tail_cursor,
+                       async->buffers.TailLength - async->tail_cursor, 0 );
+        if (ret < 0) return sock_errno_to_status( errno );
+        TRACE( "send returned %zd\n", ret );
+        async->tail_cursor += ret;
+    }
+
+    return STATUS_SUCCESS;
+}
+
+static NTSTATUS async_transmit_proc( void *user, IO_STATUS_BLOCK *io, NTSTATUS status )
+{
+    int sock_fd, file_fd = -1, sock_needs_close = FALSE, file_needs_close = FALSE;
+    struct async_transmit_ioctl *async = user;
+
+    TRACE( "%#x\n", status );
+
+    if (status == STATUS_ALERTED)
+    {
+        if ((status = server_get_unix_fd( async->io.handle, 0, &sock_fd, &sock_needs_close, NULL, NULL )))
+            return status;
+
+        if (async->file && (status = server_get_unix_fd( async->file, 0, &file_fd, &file_needs_close, NULL, NULL )))
+        {
+            if (sock_needs_close) close( sock_fd );
+            return status;
+        }
+
+        status = try_transmit( sock_fd, file_fd, async );
+        TRACE( "got status %#x\n", status );
+
+        if (status == STATUS_DEVICE_NOT_READY)
+            status = STATUS_PENDING;
+
+        if (sock_needs_close) close( sock_fd );
+        if (file_needs_close) close( file_fd );
+    }
+    if (status != STATUS_PENDING)
+    {
+        io->Status = status;
+        io->Information = async->head_cursor + async->file_cursor + async->tail_cursor;
+        release_fileio( &async->io );
+    }
+    return status;
+}
+
+static NTSTATUS sock_transmit( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc_user,
+                               IO_STATUS_BLOCK *io, int fd, const struct afd_transmit_params *params )
+{
+    int file_fd, file_needs_close = FALSE;
+    struct async_transmit_ioctl *async;
+    enum server_fd_type file_type;
+    union unix_sockaddr addr;
+    socklen_t addr_len;
+    HANDLE wait_handle;
+    NTSTATUS status;
+    ULONG options;
+
+    addr_len = sizeof(addr);
+    if (getpeername( fd, &addr.addr, &addr_len ) != 0)
+        return STATUS_INVALID_CONNECTION;
+
+    if (params->file)
+    {
+        if ((status = server_get_unix_fd( params->file, 0, &file_fd, &file_needs_close, &file_type, NULL )))
+            return status;
+        if (file_needs_close) close( file_fd );
+
+        if (file_type != FD_TYPE_FILE)
+        {
+            FIXME( "unsupported file type %#x\n", file_type );
+            return STATUS_NOT_IMPLEMENTED;
+        }
+    }
+
+    if (!(async = (struct async_transmit_ioctl *)alloc_fileio( sizeof(*async), async_transmit_proc, handle )))
+        return STATUS_NO_MEMORY;
+
+    async->file = params->file;
+    async->buffer_size = params->buffer_size ? params->buffer_size : 65536;
+    if (!(async->buffer = malloc( async->buffer_size )))
+    {
+        release_fileio( &async->io );
+        return STATUS_NO_MEMORY;
+    }
+    async->read_len = 0;
+    async->head_cursor = 0;
+    async->file_cursor = 0;
+    async->buffer_cursor = 0;
+    async->tail_cursor = 0;
+    async->file_len = params->file_len;
+    async->flags = params->flags;
+    async->buffers = params->buffers;
+    async->offset = params->offset;
+
+    SERVER_START_REQ( send_socket )
+    {
+        req->status = STATUS_PENDING;
+        req->total  = 0;
+        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 )
 {
@@ -1037,6 +1232,23 @@ NTSTATUS sock_ioctl( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc
             break;
         }
 
+        case IOCTL_AFD_WINE_TRANSMIT:
+        {
+            const struct afd_transmit_params *params = in_buffer;
+
+            if ((status = server_get_unix_fd( handle, 0, &fd, &needs_close, NULL, NULL )))
+                return status;
+
+            if (in_size < sizeof(*params))
+            {
+                return STATUS_BUFFER_TOO_SMALL;
+                break;
+            }
+
+            status = sock_transmit( handle, event, apc, apc_user, io, fd, params );
+            break;
+        }
+
         case IOCTL_AFD_POLL:
             status = sock_poll( handle, event, apc, apc_user, io, in_buffer, in_size, out_buffer, out_size );
             break;
diff --git a/include/wine/afd.h b/include/wine/afd.h
index f003263bfc8..8199e0e2dbe 100644
--- a/include/wine/afd.h
+++ b/include/wine/afd.h
@@ -23,6 +23,7 @@
 
 #include <winternl.h>
 #include <winioctl.h>
+#include <mswsock.h>
 #include "wine/server_protocol.h"
 
 #ifdef USE_WS_PREFIX
@@ -93,6 +94,7 @@ struct afd_poll_params
 #define IOCTL_AFD_WINE_SHUTDOWN             CTL_CODE(FILE_DEVICE_NETWORK, 204, METHOD_BUFFERED, FILE_ANY_ACCESS)
 #define IOCTL_AFD_WINE_RECVMSG              CTL_CODE(FILE_DEVICE_NETWORK, 205, METHOD_BUFFERED, FILE_ANY_ACCESS)
 #define IOCTL_AFD_WINE_SENDMSG              CTL_CODE(FILE_DEVICE_NETWORK, 206, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_AFD_WINE_TRANSMIT             CTL_CODE(FILE_DEVICE_NETWORK, 207, METHOD_BUFFERED, FILE_ANY_ACCESS)
 
 #define IOCTL_AFD_WINE_ADDRESS_LIST_CHANGE  CTL_CODE(FILE_DEVICE_NETWORK, 323, METHOD_BUFFERED, FILE_ANY_ACCESS)
 
@@ -137,4 +139,14 @@ struct afd_sendmsg_params
     const WSABUF *buffers;
 };
 
+struct afd_transmit_params
+{
+    HANDLE file;
+    DWORD file_len;
+    DWORD buffer_size;
+    LARGE_INTEGER offset;
+    TRANSMIT_FILE_BUFFERS buffers;
+    DWORD flags;
+};
+
 #endif
-- 
2.30.2




More information about the wine-devel mailing list