diff options
author | Ludovic Courtès <ludo@gnu.org> | 2013-04-07 23:43:21 +0200 |
---|---|---|
committer | Ludovic Courtès <ludo@gnu.org> | 2013-04-07 23:47:38 +0200 |
commit | e0886e0780fc1f3ce1c80d0692c11adf3b68f682 (patch) | |
tree | 662ee84dfa4c708a3c703493929755d3d779ad50 /libguile/filesys.c | |
parent | 254d313a21f06739930032062678ff5360d248fd (diff) | |
download | guile-e0886e0780fc1f3ce1c80d0692c11adf3b68f682.tar.gz |
Change `sendfile' to loop until everything has been sent.
* libguile/filesys.c (scm_sendfile)[HAVE_SYS_SENDFILE_H &&
HAVE_SENDFILE]: Compare RESULT with C_COUNT. Loop until C_COUNT bytes
have been sent.
* doc/ref/posix.texi (File System): Update the description. Explain the
new semantics.
* test-suite/tests/filesys.test ("sendfile"): Rewrite using
`pass-if-equal'. Check the return value for all the tests.
["file with offset past the end", "file with offset near the end"]:
New tests.
Diffstat (limited to 'libguile/filesys.c')
-rw-r--r-- | libguile/filesys.c | 47 |
1 files changed, 34 insertions, 13 deletions
diff --git a/libguile/filesys.c b/libguile/filesys.c index d318ae793..d2e565bf8 100644 --- a/libguile/filesys.c +++ b/libguile/filesys.c @@ -1111,9 +1111,10 @@ SCM_DEFINE (scm_copy_file, "copy-file", 2, 0, 0, SCM_DEFINE (scm_sendfile, "sendfile", 3, 1, 0, (SCM out, SCM in, SCM count, SCM offset), "Send @var{count} bytes from @var{in} to @var{out}, both of which " - "are either open file ports or file descriptors. When " + "must be either open file ports or file descriptors. When " "@var{offset} is omitted, start reading from @var{in}'s current " - "position; otherwise, start reading at @var{offset}.") + "position; otherwise, start reading at @var{offset}. Return " + "the number of bytes actually sent.") #define FUNC_NAME s_scm_sendfile { #define VALIDATE_FD_OR_PORT(cvar, svar, pos) \ @@ -1126,9 +1127,9 @@ SCM_DEFINE (scm_sendfile, "sendfile", 3, 1, 0, cvar = SCM_FPORT_FDES (svar); \ } - size_t c_count; + ssize_t result SCM_UNUSED; + size_t c_count, total = 0; scm_t_off c_offset; - ssize_t result; int in_fd, out_fd; VALIDATE_FD_OR_PORT (out_fd, out, 1); @@ -1139,9 +1140,30 @@ SCM_DEFINE (scm_sendfile, "sendfile", 3, 1, 0, #if defined HAVE_SYS_SENDFILE_H && defined HAVE_SENDFILE /* The Linux-style sendfile(2), which is different from the BSD-style. */ - result = sendfile_or_sendfile64 (out_fd, in_fd, - SCM_UNBNDP (offset) ? NULL : &c_offset, - c_count); + { + off_t *offset_ptr; + + offset_ptr = SCM_UNBNDP (offset) ? NULL : &c_offset; + + /* On Linux, when OUT_FD is a file, everything is transferred at once and + RESULT == C_COUNT. However, when OUT_FD is a pipe or other "slow" + device, fewer bytes may be transferred, hence the loop. RESULT == 0 + means EOF on IN_FD, so leave the loop in that case. */ + do + { + result = sendfile_or_sendfile64 (out_fd, in_fd, offset_ptr, + c_count - total); + if (result > 0) + /* At this point, either OFFSET_PTR is non-NULL and it has been + updated to the current offset in IN_FD, or it is NULL and IN_FD's + offset has been updated. */ + total += result; + else if (result < 0 && (errno == EINTR || errno == EAGAIN)) + /* Keep going. */ + result = 0; + } + while (total < c_count && result > 0); + } /* Quoting the Linux man page: "In Linux kernels before 2.6.33, out_fd must refer to a socket. Since Linux 2.6.33 it can be any file." @@ -1152,12 +1174,12 @@ SCM_DEFINE (scm_sendfile, "sendfile", 3, 1, 0, #endif { char buf[8192]; - size_t result, left; + size_t left; if (!SCM_UNBNDP (offset)) { if (SCM_PORTP (in)) - scm_seek (in, offset, scm_from_int (SEEK_SET)); + scm_seek (in, scm_from_off_t (c_offset), scm_from_int (SEEK_SET)); else { if (lseek_or_lseek64 (in_fd, c_offset, SEEK_SET) < 0) @@ -1165,7 +1187,7 @@ SCM_DEFINE (scm_sendfile, "sendfile", 3, 1, 0, } } - for (result = 0, left = c_count; result < c_count; ) + for (total = 0, left = c_count; total < c_count; ) { size_t asked, obtained; @@ -1180,13 +1202,12 @@ SCM_DEFINE (scm_sendfile, "sendfile", 3, 1, 0, if (obtained < asked) SCM_SYSERROR; - result += obtained; + total += obtained; } - return scm_from_size_t (result); } - return scm_from_ssize_t (result); + return scm_from_size_t (total); #undef VALIDATE_FD_OR_PORT } |