s3:smbd: let default_sys_recvfile() and sys_recvfile() cope with non-blocking sockets.
authorJeremy Allison <jra@samba.org>
Tue, 15 Apr 2014 19:43:06 +0000 (12:43 -0700)
committerMichael Adam <obnox@samba.org>
Sat, 24 May 2014 11:48:14 +0000 (13:48 +0200)
default_sys_recvfile() and splice() recvfile were not
written to cope with non-blocking sockets.

When either the socket read() or splice() return
-1 with errno EWOULDBLOCK or EAGAIN, if no bytes have been
processed yet, return -1 and let the caller set
blocking and retry. If bytes have been processed,
just return them as a short read and let the
caller retry with the remaining needed reads.

Signed-off-by: Jeremy Allison <jra@samba.org>
Reviewed-by: Michael Adam <obnox@samba.org>
source3/lib/recvfile.c

index 500a7e43399f003bc7f60bd1de41ba35037dd50f..bffe07f75cd4c54fdcc79649f8f8dc8a5926b4ef 100644 (file)
@@ -75,8 +75,33 @@ static ssize_t default_sys_recvfile(int fromfd,
                ssize_t read_ret;
                size_t toread = MIN(bufsize,count - total);
 
-               /* Read from socket - ignore EINTR. */
-               read_ret = sys_read(fromfd, buffer, toread);
+               /*
+                * Read from socket - ignore EINTR.
+                * Can't use sys_read() as that also
+                * ignores EAGAIN and EWOULDBLOCK.
+                */
+               do {
+                       read_ret = read(fromfd, buffer, toread);
+               } while (read_ret == -1 && errno == EINTR);
+
+#if defined(EWOULDBLOCK)
+               if (read_ret == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
+#else
+               if (read_ret == -1 && (errno == EAGAIN)) {
+#endif
+                       /*
+                        * fromfd socket is in non-blocking mode.
+                        * If we already read some and wrote
+                        * it successfully, return that.
+                        * Only return -1 if this is the first read
+                        * attempt. Caller will handle both cases.
+                        */
+                       if (total_written != 0) {
+                               return total_written;
+                       }
+                       return -1;
+               }
+
                if (read_ret <= 0) {
                        /* EOF or socket error. */
                        return -1;
@@ -184,6 +209,23 @@ ssize_t sys_recvfile(int fromfd,
                                return default_sys_recvfile(fromfd, tofd,
                                                            offset, count);
                        }
+#if defined(EWOULDBLOCK)
+                       if (errno == EAGAIN || errno == EWOULDBLOCK) {
+#else
+                       if (errno == EAGAIN) {
+#endif
+                               /*
+                                * fromfd socket is in non-blocking mode.
+                                * If we already read some and wrote
+                                * it successfully, return that.
+                                * Only return -1 if this is the first read
+                                * attempt. Caller will handle both cases.
+                                */
+                               if (total_written != 0) {
+                                       return total_written;
+                               }
+                               return -1;
+                       }
                        break;
                }