summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authoralfred <alfred@FreeBSD.org>2001-12-12 08:02:24 +0000
committeralfred <alfred@FreeBSD.org>2001-12-12 08:02:24 +0000
commit13d622545cac749139a5a9f7c3febc2735379d9d (patch)
treea62c5be08d2944707571a326148fd2128b4911ac /lib
parent84bcda9834c9bc93e56049f35b5a8085f0169335 (diff)
downloadFreeBSD-src-13d622545cac749139a5a9f7c3febc2735379d9d.zip
FreeBSD-src-13d622545cac749139a5a9f7c3febc2735379d9d.tar.gz
Fix a number of subtle and evil bugs in the libc_r wrapping of sendfile(2).
o) Since we unwrap the sendfile syscall, check the return value of writev(2) to see if it didn't complete all the data. Previously if only a partial writev() succeeded, it would proceed to sendfile(2) even though the headers weren't completely sent. o) Properly adjust the "bytes to send" to take into account sendfile(2)'s behaviour of counting the headers against the bytes to be transfered from the file. o) Correct the problem where EAGAIN was being returned from _sys_sendfile(2) however the wrapper didn't update the 'sent bytes' parameter to take into account for it. This is because sendfile can return EAGAIN even though it has actually transfered data. Special thanks to Justin Erenkrantz <jerenkrantz@apache.org> for bringing this to my attention and giving an excellent way to reproduce the problem. PR: kern/32684 MFC After: 1 week
Diffstat (limited to 'lib')
-rw-r--r--lib/libc_r/uthread/uthread_sendfile.c54
1 files changed, 50 insertions, 4 deletions
diff --git a/lib/libc_r/uthread/uthread_sendfile.c b/lib/libc_r/uthread/uthread_sendfile.c
index 3df24f1..d22f4ef 100644
--- a/lib/libc_r/uthread/uthread_sendfile.c
+++ b/lib/libc_r/uthread/uthread_sendfile.c
@@ -48,13 +48,25 @@ _sendfile(int fd, int s, off_t offset, size_t nbytes, struct sf_hdtr *hdtr,
ssize_t wvret, num = 0;
off_t n, nwritten = 0;
- /* Write the headers if any. */
+ /*
+ * Write the headers if any.
+ * If some data is written but not all we must return here.
+ */
if ((hdtr != NULL) && (hdtr->headers != NULL)) {
if ((wvret = writev(s, hdtr->headers, hdtr->hdr_cnt)) == -1) {
ret = -1;
goto ERROR;
- } else
+ } else {
+ int i;
+ ssize_t hdrtot;
+
nwritten += wvret;
+
+ for (i = 0, hdrtot = 0; i < hdtr->hdr_cnt; i++)
+ hdrtot += hdtr->headers[i].iov_len;
+ if (wvret < hdrtot)
+ goto SHORT_WRITE;
+ }
}
/* Lock the descriptors. */
@@ -87,7 +99,18 @@ _sendfile(int fd, int s, off_t offset, size_t nbytes, struct sf_hdtr *hdtr,
/* Check if file operations are to block */
blocking = ((_thread_fd_table[s]->flags & O_NONBLOCK) == 0);
-
+
+ /*
+ * Emulate sendfile(2) weirdness, sendfile doesn't actually send
+ * nbytes of the file, it really sends (nbytes - headers_size) of
+ * the file. If (nbytes - headers_size) == 0 we just send trailers.
+ */
+ if (nbytes != 0) {
+ nbytes -= nwritten;
+ if (nbytes <= 0)
+ goto ERROR_2;
+ }
+
/*
* Loop while no error occurs and until the expected number of bytes are
* written.
@@ -97,11 +120,24 @@ _sendfile(int fd, int s, off_t offset, size_t nbytes, struct sf_hdtr *hdtr,
ret = __sys_sendfile(fd, s, offset + num, nbytes - num,
NULL, &n, flags);
+ /*
+ * We have to handle the sideways return path of sendfile.
+ *
+ * If the result is 0, we're done.
+ * If the result is anything else check the errno.
+ * If the errno is not EGAIN return the error.
+ * Otherwise, take into account how much
+ * sendfile may have written for us because sendfile can
+ * return EAGAIN even though it has written data.
+ *
+ * We don't clear 'ret' because the sendfile(2) syscall
+ * would not have either.
+ */
if (ret == 0) {
/* Writing completed. */
num += n;
break;
- } else if ((blocking) && (ret == -1) && (errno == EAGAIN)) {
+ } else if ((ret == -1) && (errno == EAGAIN)) {
/*
* Some bytes were written but there are still more to
* write.
@@ -110,6 +146,15 @@ _sendfile(int fd, int s, off_t offset, size_t nbytes, struct sf_hdtr *hdtr,
/* Update the count of bytes written. */
num += n;
+ /*
+ * If we're not blocking then return.
+ */
+ if (!blocking)
+ goto SHORT_WRITE;
+
+ /*
+ * Otherwise wait on the fd.
+ */
curthread->data.fd.fd = fd;
_thread_kern_set_timeout(NULL);
@@ -144,6 +189,7 @@ _sendfile(int fd, int s, off_t offset, size_t nbytes, struct sf_hdtr *hdtr,
nwritten += wvret;
}
}
+ SHORT_WRITE:
if (sbytes != NULL) {
/*
* Number of bytes written in headers/trailers, plus in the main
OpenPOWER on IntegriCloud