Signed-off-by: Zebediah Figura z.figura12@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