From: "Erich E. Hoover" Subject: ws2_32: Add asynchronous support for TransmitFile (try 3). Message-Id: Date: Tue, 20 Oct 2015 18:37:51 -0600 This patch adds asynchronous/overlapped support for TransmitFile. Since the earlier patches have been carefully crafted to work off of NTSTATUS values, this patch just adds the necessary infrastructure for using the register_async functionality to spawn a TransmitFile operation. Since the last review the patch has been updated to use pread with an offset instead of changing the file pointer when starting the transfer. It has also been rebased, the iosb.Information used by WS2_ReadFile is explicitly initialized to zero, and the offset updating has been moved outside of WS2_ReadFile. From 5dffc483cad9695241f0d647827f3c4c40f3e978 Mon Sep 17 00:00:00 2001 From: "Erich E. Hoover" Date: Wed, 7 Oct 2015 12:19:42 -0600 Subject: ws2_32: Add asynchronous support for TransmitFile. Signed-off-by: Erich E. Hoover --- dlls/ws2_32/socket.c | 90 +++++++++++++++++++++++++++++++++++++++-------- dlls/ws2_32/tests/sock.c | 91 ++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 161 insertions(+), 20 deletions(-) diff --git a/dlls/ws2_32/socket.c b/dlls/ws2_32/socket.c index bc29f6d..404e33e 100644 --- a/dlls/ws2_32/socket.c +++ b/dlls/ws2_32/socket.c @@ -177,6 +177,8 @@ #define TCP_KEEPIDLE TCP_KEEPALIVE #endif +#define FILE_USE_FILE_POINTER_POSITION ((LONGLONG)-2) + WINE_DEFAULT_DEBUG_CHANNEL(winsock); WINE_DECLARE_DEBUG_CHANNEL(winediag); @@ -524,6 +526,7 @@ struct ws2_transmitfile_async DWORD bytes_per_send; TRANSMIT_FILE_BUFFERS buffers; DWORD flags; + LARGE_INTEGER offset; struct ws2_async write; }; @@ -2742,9 +2745,10 @@ static BOOL WINAPI WS2_AcceptEx(SOCKET listener, SOCKET acceptor, PVOID dest, DW * * Perform an APC-safe ReadFile operation */ -static NTSTATUS WS2_ReadFile(HANDLE hFile, PIO_STATUS_BLOCK io_status, char* buffer, ULONG length) +static NTSTATUS WS2_ReadFile(HANDLE hFile, PIO_STATUS_BLOCK io_status, char* buffer, ULONG length, + PLARGE_INTEGER offset) { - int result, unix_handle; + int result = -1, unix_handle; unsigned int options; NTSTATUS status; @@ -2753,8 +2757,12 @@ static NTSTATUS WS2_ReadFile(HANDLE hFile, PIO_STATUS_BLOCK io_status, char* buf status = wine_server_handle_to_fd( hFile, FILE_READ_DATA, &unix_handle, &options ); if (status) return status; - while ((result = read( unix_handle, buffer, length )) == -1) + while (result == -1) { + if (offset && offset->QuadPart != FILE_USE_FILE_POINTER_POSITION) + result = pread( unix_handle, buffer, length, offset->QuadPart ); + else + result = read( unix_handle, buffer, length ); if (errno != EINTR) break; } @@ -2808,10 +2816,13 @@ static NTSTATUS WS2_transmitfile_getbuffer( int fd, struct ws2_transmitfile_asyn IO_STATUS_BLOCK iosb; NTSTATUS status; + iosb.Information = 0; /* when the size of the transfer is limited ensure that we don't go past that limit */ if (wsa->file_bytes != 0) bytes_per_send = min(bytes_per_send, wsa->file_bytes - wsa->file_read); - status = WS2_ReadFile( wsa->file, &iosb, wsa->buffer, bytes_per_send ); + status = WS2_ReadFile( wsa->file, &iosb, wsa->buffer, bytes_per_send, &wsa->offset ); + if (wsa->offset.QuadPart != FILE_USE_FILE_POINTER_POSITION) + wsa->offset.QuadPart += iosb.Information; if (status == STATUS_END_OF_FILE) wsa->file = NULL; /* continue on to the footer */ else if (status != STATUS_SUCCESS) @@ -2860,10 +2871,15 @@ static NTSTATUS WS2_transmitfile_base( int fd, struct ws2_transmitfile_async *ws status = WS2_transmitfile_getbuffer( fd, wsa ); if (status == STATUS_PENDING) { + IO_STATUS_BLOCK *iosb = (IO_STATUS_BLOCK *)wsa->write.user_overlapped; int n; n = WS2_send( fd, &wsa->write, convert_flags(wsa->write.flags) ); - if (n == -1 && errno != EAGAIN) + if (n >= 0) + { + if (iosb) iosb->Information += n; + } + else if (errno != EAGAIN) return wsaErrStatus(); } @@ -2871,6 +2887,33 @@ static NTSTATUS WS2_transmitfile_base( int fd, struct ws2_transmitfile_async *ws } /*********************************************************************** + * WS2_async_transmitfile (INTERNAL) + * + * Asynchronous callback for overlapped TransmitFile operations. + */ +static NTSTATUS WS2_async_transmitfile( void *user, IO_STATUS_BLOCK *iosb, + NTSTATUS status, void **apc, void **arg ) +{ + struct ws2_transmitfile_async *wsa = user; + int fd; + + if (status == STATUS_ALERTED) + { + if (!(status = wine_server_handle_to_fd( wsa->write.hSocket, FILE_WRITE_DATA, &fd, NULL ))) + { + status = WS2_transmitfile_base( fd, wsa ); + wine_server_release_fd( wsa->write.hSocket, fd ); + } + if (status == STATUS_PENDING) + return status; + } + + iosb->u.Status = status; + release_async_io( &wsa->io ); + return status; +} + +/*********************************************************************** * TransmitFile */ static BOOL WINAPI WS2_TransmitFile( SOCKET s, HANDLE h, DWORD file_bytes, DWORD bytes_per_send, @@ -2883,14 +2926,6 @@ static BOOL WINAPI WS2_TransmitFile( SOCKET s, HANDLE h, DWORD file_bytes, DWORD NTSTATUS status; int fd; - if (overlapped) - { - FIXME("(%lx, %p, %d, %d, %p, %p, %d): stub !\n", s, h, file_bytes, bytes_per_send, - overlapped, buffers, flags); - WSASetLastError( WSAEOPNOTSUPP ); - return FALSE; - } - TRACE("(%lx, %p, %d, %d, %p, %p, %d)\n", s, h, file_bytes, bytes_per_send, overlapped, buffers, flags ); @@ -2937,6 +2972,7 @@ static BOOL WINAPI WS2_TransmitFile( SOCKET s, HANDLE h, DWORD file_bytes, DWORD wsa->file_bytes = file_bytes; wsa->bytes_per_send = bytes_per_send; wsa->flags = flags; + wsa->offset.QuadPart = FILE_USE_FILE_POINTER_POSITION; wsa->write.hSocket = SOCKET2HANDLE(s); wsa->write.addr = NULL; wsa->write.addrlen.val = 0; @@ -2945,7 +2981,33 @@ static BOOL WINAPI WS2_TransmitFile( SOCKET s, HANDLE h, DWORD file_bytes, DWORD wsa->write.control = NULL; wsa->write.n_iovecs = 0; wsa->write.first_iovec = 0; - wsa->write.user_overlapped = NULL; + wsa->write.user_overlapped = overlapped; + if (overlapped) + { + IO_STATUS_BLOCK *iosb = (IO_STATUS_BLOCK *)overlapped; + int status; + + wsa->offset.u.LowPart = overlapped->u.s.Offset; + wsa->offset.u.HighPart = overlapped->u.s.OffsetHigh; + iosb->u.Status = STATUS_PENDING; + iosb->Information = 0; + SERVER_START_REQ( register_async ) + { + req->type = ASYNC_TYPE_WRITE; + req->async.handle = wine_server_obj_handle( SOCKET2HANDLE(s) ); + req->async.event = wine_server_obj_handle( overlapped->hEvent ); + req->async.callback = wine_server_client_ptr( WS2_async_transmitfile ); + req->async.iosb = wine_server_client_ptr( iosb ); + req->async.arg = wine_server_client_ptr( wsa ); + status = wine_server_call( req ); + } + SERVER_END_REQ; + + if(status != STATUS_PENDING) HeapFree( GetProcessHeap(), 0, wsa ); + release_sock_fd( s, fd ); + WSASetLastError( NtStatusToWSAError(status) ); + return FALSE; + } do { diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c index 3d20c89..29e20c3 100644 --- a/dlls/ws2_32/tests/sock.c +++ b/dlls/ws2_32/tests/sock.c @@ -7429,15 +7429,15 @@ end: closesocket(connector2); } -#define compare_file(h,s) compare_file2(h,s,__FILE__,__LINE__) +#define compare_file(h,s,o) compare_file2(h,s,o,__FILE__,__LINE__) -static void compare_file2(HANDLE handle, SOCKET sock, const char *file, int line) +static void compare_file2(HANDLE handle, SOCKET sock, int offset, const char *file, int line) { char buf1[256], buf2[256]; BOOL success; int i = 0; - SetFilePointer(handle, 0, NULL, FILE_BEGIN); + SetFilePointer(handle, offset, NULL, FILE_BEGIN); while (1) { DWORD n1 = 0, n2 = 0; @@ -7457,6 +7457,7 @@ static void compare_file2(HANDLE handle, SOCKET sock, const char *file, int line static void test_TransmitFile(void) { + DWORD num_bytes, err, file_size, total_sent; GUID transmitFileGuid = WSAID_TRANSMITFILE; LPFN_TRANSMITFILE pTransmitFile = NULL; HANDLE file = INVALID_HANDLE_VALUE; @@ -7466,11 +7467,13 @@ static void test_TransmitFile(void) struct sockaddr_in bindAddress; TRANSMIT_FILE_BUFFERS buffers; SOCKET client, server, dest; - DWORD num_bytes, err; + WSAOVERLAPPED ov; char buf[256]; int iret, len; BOOL bret; + memset( &ov, 0, sizeof(ov) ); + /* Setup sockets for testing TransmitFile */ client = socket(AF_INET, SOCK_STREAM, 0); server = socket(AF_INET, SOCK_STREAM, 0); @@ -7494,6 +7497,7 @@ static void test_TransmitFile(void) skip("Unable to open a file to transmit.\n"); goto cleanup; } + file_size = GetFileSize(file, NULL); /* Test TransmitFile with an invalid socket */ bret = pTransmitFile(INVALID_SOCKET, file, 0, 0, NULL, NULL, 0); @@ -7567,7 +7571,7 @@ static void test_TransmitFile(void) /* Test TransmitFile with only file data */ bret = pTransmitFile(client, file, 0, 0, NULL, NULL, 0); ok(bret, "TransmitFile failed unexpectedly.\n"); - compare_file(file, dest); + compare_file(file, dest, 0); /* Test TransmitFile with both file and buffer data */ buffers.Head = &header_msg[0]; @@ -7580,7 +7584,81 @@ static void test_TransmitFile(void) iret = recv(dest, buf, sizeof(header_msg)+1, 0); ok(memcmp(buf, &header_msg[0], sizeof(header_msg)+1) == 0, "TransmitFile header buffer did not match!\n"); - compare_file(file, dest); + compare_file(file, dest, 0); + iret = recv(dest, buf, sizeof(footer_msg)+1, 0); + ok(memcmp(buf, &footer_msg[0], sizeof(footer_msg)+1) == 0, + "TransmitFile footer buffer did not match!\n"); + + /* Test overlapped TransmitFile */ + ov.hEvent = CreateEventW(NULL, FALSE, FALSE, NULL); + if (ov.hEvent == INVALID_HANDLE_VALUE) + { + skip("Could not create event object, some tests will be skipped. errno = %d\n", + GetLastError()); + goto cleanup; + } + SetFilePointer(file, 0, NULL, FILE_BEGIN); + bret = pTransmitFile(client, file, 0, 0, &ov, NULL, 0); + err = WSAGetLastError(); + ok(!bret, "TransmitFile succeeded unexpectedly.\n"); + ok(err == ERROR_IO_PENDING, "TransmitFile triggered unexpected errno (%d != %d)\n", + err, ERROR_IO_PENDING); + iret = WaitForSingleObject(ov.hEvent, 2000); + ok(iret == WAIT_OBJECT_0, "Overlapped TransmitFile failed.\n"); + WSAGetOverlappedResult(client, &ov, &total_sent, FALSE, NULL); + ok(total_sent == file_size, + "Overlapped TransmitFile sent an unexpected number of bytes (%d != %d).\n", + total_sent, file_size); + compare_file(file, dest, 0); + + /* Test overlapped TransmitFile w/ start offset */ + ov.hEvent = CreateEventW(NULL, FALSE, FALSE, NULL); + if (ov.hEvent == INVALID_HANDLE_VALUE) + { + skip("Could not create event object, some tests will be skipped. errno = %d\n", GetLastError()); + goto cleanup; + } + SetFilePointer(file, 0, NULL, FILE_BEGIN); + ov.Offset = 10; + bret = pTransmitFile(client, file, 0, 0, &ov, NULL, 0); + err = WSAGetLastError(); + ok(!bret, "TransmitFile succeeded unexpectedly.\n"); + ok(err == ERROR_IO_PENDING, "TransmitFile triggered unexpected errno (%d != %d)\n", err, ERROR_IO_PENDING); + iret = WaitForSingleObject(ov.hEvent, 2000); + ok(iret == WAIT_OBJECT_0, "Overlapped TransmitFile failed.\n"); + WSAGetOverlappedResult(client, &ov, &total_sent, FALSE, NULL); + ok(total_sent == (file_size - ov.Offset), + "Overlapped TransmitFile sent an unexpected number of bytes (%d != %d).\n", + total_sent, file_size - ov.Offset); + compare_file(file, dest, ov.Offset); + + /* Test overlapped TransmitFile w/ file and buffer data */ + ov.hEvent = CreateEventW(NULL, FALSE, FALSE, NULL); + if (ov.hEvent == INVALID_HANDLE_VALUE) + { + skip("Could not create event object, some tests will be skipped. errno = %d\n", GetLastError()); + goto cleanup; + } + buffers.Head = &header_msg[0]; + buffers.HeadLength = sizeof(header_msg)+1; + buffers.Tail = &footer_msg[0]; + buffers.TailLength = sizeof(footer_msg)+1; + SetFilePointer(file, 0, NULL, FILE_BEGIN); + ov.Offset = 0; + bret = pTransmitFile(client, file, 0, 0, &ov, &buffers, 0); + err = WSAGetLastError(); + ok(!bret, "TransmitFile succeeded unexpectedly.\n"); + ok(err == ERROR_IO_PENDING, "TransmitFile triggered unexpected errno (%d != %d)\n", err, ERROR_IO_PENDING); + iret = WaitForSingleObject(ov.hEvent, 2000); + ok(iret == WAIT_OBJECT_0, "Overlapped TransmitFile failed.\n"); + WSAGetOverlappedResult(client, &ov, &total_sent, FALSE, NULL); + ok(total_sent == (file_size + buffers.HeadLength + buffers.TailLength), + "Overlapped TransmitFile sent an unexpected number of bytes (%d != %d).\n", + total_sent, file_size + buffers.HeadLength + buffers.TailLength); + iret = recv(dest, buf, sizeof(header_msg)+1, 0); + ok(memcmp(buf, &header_msg[0], sizeof(header_msg)+1) == 0, + "TransmitFile header buffer did not match!\n"); + compare_file(file, dest, 0); iret = recv(dest, buf, sizeof(footer_msg)+1, 0); ok(memcmp(buf, &footer_msg[0], sizeof(footer_msg)+1) == 0, "TransmitFile footer buffer did not match!\n"); @@ -7595,6 +7673,7 @@ static void test_TransmitFile(void) cleanup: CloseHandle(file); + CloseHandle(ov.hEvent); closesocket(client); closesocket(server); } -- 1.9.1