Module: wine Branch: master Commit: 4d081f36efefd3ccf702cacea4d20c5616cfbae1 URL: http://source.winehq.org/git/wine.git/?a=commit;h=4d081f36efefd3ccf702cacea4...
Author: Erich E. Hoover erich.e.hoover@wine-staging.com Date: Wed Oct 7 12:14:56 2015 -0600
ws2_32: Implement a basic synchronous TransmitFile.
Signed-off-by: Erich E. Hoover erich.e.hoover@wine-staging.com Signed-off-by: Alexandre Julliard julliard@winehq.org
---
dlls/ws2_32/socket.c | 193 +++++++++++++++++++++++++++++++++++++++++++++-- dlls/ws2_32/tests/sock.c | 36 ++++++++- 2 files changed, 222 insertions(+), 7 deletions(-)
diff --git a/dlls/ws2_32/socket.c b/dlls/ws2_32/socket.c index 44926de..f107d32 100644 --- a/dlls/ws2_32/socket.c +++ b/dlls/ws2_32/socket.c @@ -514,6 +514,18 @@ struct ws2_accept_async struct ws2_async *read; };
+struct ws2_transmitfile_async +{ + struct ws2_async_io io; + char *buffer; + HANDLE file; + DWORD file_read; + DWORD file_bytes; + DWORD bytes_per_send; + DWORD flags; + struct ws2_async write; +}; + static struct ws2_async_io *async_io_freelist;
static void release_async_io( struct ws2_async_io *io ) @@ -2725,6 +2737,117 @@ static BOOL WINAPI WS2_AcceptEx(SOCKET listener, SOCKET acceptor, PVOID dest, DW }
/*********************************************************************** + * WS2_ReadFile (INTERNAL) + * + * Perform an APC-safe ReadFile operation + */ +static NTSTATUS WS2_ReadFile(HANDLE hFile, PIO_STATUS_BLOCK io_status, char* buffer, ULONG length) +{ + int result, unix_handle; + unsigned int options; + NTSTATUS status; + + TRACE( "(%p,%p,0x%08x)\n", hFile, buffer,length ); + + 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) + { + if (errno != EINTR) + break; + } + + if (!result) + status = (length ? STATUS_END_OF_FILE : STATUS_SUCCESS); + else if (result != -1) + status = STATUS_SUCCESS; + else if (errno != EAGAIN) + status = wsaErrStatus(); + else + status = STATUS_PENDING; + + close( unix_handle ); + TRACE("= 0x%08x (%d)\n", status, result); + if (status == STATUS_SUCCESS || status == STATUS_END_OF_FILE) + { + io_status->u.Status = status; + io_status->Information = result; + } + + return status; +} + +/*********************************************************************** + * WS2_transmitfile_getbuffer (INTERNAL) + * + * Pick the appropriate buffer for a TransmitFile send operation. + */ +static NTSTATUS WS2_transmitfile_getbuffer( int fd, struct ws2_transmitfile_async *wsa ) +{ + /* send any incomplete writes from a previous iteration */ + if (wsa->write.first_iovec < wsa->write.n_iovecs) + return STATUS_PENDING; + + /* process the main file */ + if (wsa->file) + { + DWORD bytes_per_send = wsa->bytes_per_send; + IO_STATUS_BLOCK iosb; + NTSTATUS status; + + /* 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 ); + if (status == STATUS_END_OF_FILE) + return STATUS_SUCCESS; + else if (status != STATUS_SUCCESS) + return status; + else + { + if (iosb.Information) + { + wsa->write.first_iovec = 0; + wsa->write.n_iovecs = 1; + wsa->write.iovec[0].iov_base = wsa->buffer; + wsa->write.iovec[0].iov_len = iosb.Information; + wsa->file_read += iosb.Information; + } + + if (wsa->file_bytes != 0 && wsa->file_read >= wsa->file_bytes) + wsa->file = NULL; + + return STATUS_PENDING; + } + } + + return STATUS_SUCCESS; +} + +/*********************************************************************** + * WS2_transmitfile_base (INTERNAL) + * + * Shared implementation for both synchronous and asynchronous TransmitFile. + */ +static NTSTATUS WS2_transmitfile_base( int fd, struct ws2_transmitfile_async *wsa ) +{ + NTSTATUS status; + + status = WS2_transmitfile_getbuffer( fd, wsa ); + if (status == STATUS_PENDING) + { + int n; + + n = WS2_send( fd, &wsa->write, convert_flags(wsa->write.flags) ); + if (n == -1 && errno != EAGAIN) + return wsaErrStatus(); + } + + return status; +} + +/*********************************************************************** * TransmitFile */ static BOOL WINAPI WS2_TransmitFile( SOCKET s, HANDLE h, DWORD file_bytes, DWORD bytes_per_send, @@ -2733,12 +2856,22 @@ static BOOL WINAPI WS2_TransmitFile( SOCKET s, HANDLE h, DWORD file_bytes, DWORD { union generic_unix_sockaddr uaddr; unsigned int uaddrlen = sizeof(uaddr); + struct ws2_transmitfile_async *wsa; + NTSTATUS status; int fd;
- FIXME("(%lx, %p, %d, %d, %p, %p, %d): stub !\n", s, h, file_bytes, bytes_per_send, overlapped, - buffers, flags ); + if (overlapped || buffers) + { + FIXME("(%lx, %p, %d, %d, %p, %p, %d): stub !\n", s, h, file_bytes, bytes_per_send, + overlapped, buffers, flags); + WSASetLastError( WSAEOPNOTSUPP ); + return FALSE; + }
- fd = get_sock_fd( s, 0, NULL ); + TRACE("(%lx, %p, %d, %d, %p, %p, %d)\n", s, h, file_bytes, bytes_per_send, overlapped, + buffers, flags ); + + fd = get_sock_fd( s, FILE_WRITE_DATA, NULL ); if (fd == -1) { WSASetLastError( WSAENOTSOCK ); @@ -2750,12 +2883,60 @@ static BOOL WINAPI WS2_TransmitFile( SOCKET s, HANDLE h, DWORD file_bytes, DWORD WSASetLastError( WSAENOTCONN ); return FALSE; } - release_sock_fd( s, fd ); if (flags) FIXME("Flags are not currently supported (0x%x).\n", flags);
- WSASetLastError( WSAEOPNOTSUPP ); - return FALSE; + if (h && GetFileType( h ) != FILE_TYPE_DISK) + { + FIXME("Non-disk file handles are not currently supported.\n"); + release_sock_fd( s, fd ); + WSASetLastError( WSAEOPNOTSUPP ); + return FALSE; + } + + /* set reasonable defaults when requested */ + if (!bytes_per_send) + bytes_per_send = (1 << 16); /* Depends on OS version: PAGE_SIZE, 2*PAGE_SIZE, or 2^16 */ + + if (!(wsa = (struct ws2_transmitfile_async *)alloc_async_io( sizeof(*wsa) + bytes_per_send ))) + { + release_sock_fd( s, fd ); + WSASetLastError( WSAEFAULT ); + return FALSE; + } + wsa->buffer = (char *)(wsa + 1); + wsa->file = h; + wsa->file_read = 0; + wsa->file_bytes = file_bytes; + wsa->bytes_per_send = bytes_per_send; + wsa->flags = flags; + wsa->write.hSocket = SOCKET2HANDLE(s); + wsa->write.addr = NULL; + wsa->write.addrlen.val = 0; + wsa->write.flags = 0; + wsa->write.lpFlags = &wsa->flags; + wsa->write.control = NULL; + wsa->write.n_iovecs = 0; + wsa->write.first_iovec = 0; + wsa->write.user_overlapped = NULL; + + do + { + status = WS2_transmitfile_base( fd, wsa ); + if (status == STATUS_PENDING) + { + /* block here */ + do_block(fd, POLLOUT, -1); + _sync_sock_state(s); /* let wineserver notice connection */ + } + } + while (status == STATUS_PENDING); + release_sock_fd( s, fd ); + + if (status != STATUS_SUCCESS) + WSASetLastError( NtStatusToWSAError(status) ); + HeapFree( GetProcessHeap(), 0, wsa ); + return (status == STATUS_SUCCESS); }
/*********************************************************************** diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c index 7fbd67d..478a5fe 100644 --- a/dlls/ws2_32/tests/sock.c +++ b/dlls/ws2_32/tests/sock.c @@ -7429,6 +7429,32 @@ end: closesocket(connector2); }
+#define compare_file(h,s) compare_file2(h,s,__FILE__,__LINE__) + +static void compare_file2(HANDLE handle, SOCKET sock, const char *file, int line) +{ + char buf1[256], buf2[256]; + BOOL success; + int i = 0; + + SetFilePointer(handle, 0, NULL, FILE_BEGIN); + while (1) + { + DWORD n1 = 0, n2 = 0; + + success = ReadFile(handle, buf1, sizeof(buf1), &n1, NULL); + ok_(file,line)(success, "Failed to read from file.\n"); + if (success && n1 == 0) + break; + else if(!success) + return; + n2 = recv(sock, buf2, n1, 0); + ok_(file,line)(n1 == n2, "Block %d size mismatch (%d != %d)\n", i, n1, n2); + ok_(file,line)(memcmp(buf1, buf2, n2) == 0, "Block %d failed\n", i); + i++; + } +} + static void test_TransmitFile(void) { GUID transmitFileGuid = WSAID_TRANSMITFILE; @@ -7438,6 +7464,7 @@ static void test_TransmitFile(void) struct sockaddr_in bindAddress; SOCKET client, server, dest; DWORD num_bytes, err; + char buf[256]; int iret, len; BOOL bret;
@@ -7515,7 +7542,14 @@ static void test_TransmitFile(void)
/* Test TransmitFile with no possible buffer */ bret = pTransmitFile(client, NULL, 0, 0, NULL, NULL, 0); - todo_wine ok(bret, "TransmitFile failed unexpectedly.\n"); + ok(bret, "TransmitFile failed unexpectedly.\n"); + iret = recv(dest, buf, sizeof(buf), 0); + ok(iret == -1, "Returned an unexpected buffer from TransmitFile (%d != -1).\n", iret); + + /* 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);
/* Test TransmitFile with a UDP datagram socket */ closesocket(client);