Erich E. Hoover : ws2_32: Implement a basic synchronous TransmitFile.

Alexandre Julliard julliard at wine.codeweavers.com
Mon Oct 19 11:34:21 CDT 2015


Module: wine
Branch: master
Commit: 4d081f36efefd3ccf702cacea4d20c5616cfbae1
URL:    http://source.winehq.org/git/wine.git/?a=commit;h=4d081f36efefd3ccf702cacea4d20c5616cfbae1

Author: Erich E. Hoover <erich.e.hoover at 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 at wine-staging.com>
Signed-off-by: Alexandre Julliard <julliard at 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);




More information about the wine-cvs mailing list