Try to fix recvfile using splice on Linux
authorVolker Lendecke <vl@samba.org>
Tue, 23 Dec 2008 21:45:03 +0000 (22:45 +0100)
committerVolker Lendecke <vl@samba.org>
Wed, 31 Dec 2008 18:33:25 +0000 (19:33 +0100)
According to the splice(2) manpage, one of the file descriptors *must* be a
pipe. With this patch I could successfully run splice(2) on a debian lenny
installation.

Jeremy, please check!

source3/lib/recvfile.c

index 513742ce8f8edfa298ab3f0721fe85588d55ebd2..41f72139437637e3b95fdf18c8aaea6b0057e276 100644 (file)
@@ -145,6 +145,7 @@ ssize_t sys_recvfile(int fromfd,
                        SMB_OFF_T offset,
                        size_t count)
 {
+       static int pipefd[2] = { -1, -1 };
        static bool try_splice_call = true;
        size_t total_written = 0;
 
@@ -171,31 +172,45 @@ ssize_t sys_recvfile(int fromfd,
                                count);
        }
 
-       while (total_written < count) {
-               ssize_t ret = splice(fromfd,
-                                       NULL,
-                                       tofd,
-                                       &offset,
-                                       count,
-                                       0);
-               if (ret == -1) {
-                       if (errno != EINTR) {
-                               if (total_written == 0 &&
-                                               (errno == EBADF || errno == EINVAL)) {
-                                       try_splice_call = false;
-                                       return default_sys_recvfile(fromfd,
-                                                               tofd,
-                                                               offset,
-                                                               count);
-                               }
-                               break;
+       if ((pipefd[0] == -1) && (pipe(pipefd) == -1)) {
+               try_splice_call = false;
+               return default_sys_recvfile(fromfd, tofd, offset, count);
+       }
+
+       while (count > 0) {
+               int nread, to_write;
+
+               nread = splice(fromfd, NULL, pipefd[1], NULL, count, 0);
+               if (nread == -1) {
+                       if (errno == EINTR) {
+                               continue;
+                       }
+                       if (total_written == 0 &&
+                           (errno == EBADF || errno == EINVAL)) {
+                               try_splice_call = false;
+                               return default_sys_recvfile(fromfd, tofd,
+                                                           offset, count);
                        }
-                       continue;
+                       break;
                }
-               total_written += ret;
-               count -= ret;
+
+               to_write = nread;
+               while (to_write > 0) {
+                       int thistime;
+                       thistime = splice(pipefd[0], NULL, tofd, &offset,
+                                         to_write, 0);
+                       if (thistime == -1) {
+                               goto done;
+                       }
+                       offset += thistime;
+                       to_write -= thistime;
+               }
+
+               total_written += nread;
+               count -= nread;
        }
 
+ done:
        if (total_written < count) {
                int saved_errno = errno;
                if (drain_socket(fromfd, count-total_written) !=