network_io/unix/sendrecv.c | 96 +++++++++++++++++++++++++++++-------------- 1 files changed, 65 insertions(+), 31 deletions(-) diff --git a/network_io/unix/sendrecv.c b/network_io/unix/sendrecv.c index c133a26..d15ed69 100644 --- a/network_io/unix/sendrecv.c +++ b/network_io/unix/sendrecv.c @@ -247,39 +247,77 @@ static apr_hdtr_t no_hdtr; #if defined(__linux__) && defined(HAVE_WRITEV) -apr_status_t apr_socket_sendfile(apr_socket_t *sock, apr_file_t *file, - apr_hdtr_t *hdtr, apr_off_t *offset, - apr_size_t *len, apr_int32_t flags) +/* Helper function for apr_socket_sendfile. + * Takes care of sendfile vs. sendfile64 (must be detected at runtime), + * EINTR restarting, and other details. NOTE: does not necessarily + * update 'off', as callers don't need this. + */ +static +ssize_t do_sendfile(int out, int in, apr_off_t *off, apr_size_t len) { - int rv, nbytes = 0, total_hdrbytes, i; - apr_status_t arv; +#if !APR_HAS_LARGE_FILES + ssize_t ret; + do + ret = sendfile(out, in, off, len); + while (ret == -1 && errno == EINTR); + return ret; +#else -#if APR_HAS_LARGE_FILES && defined(HAVE_SENDFILE64) - apr_off_t off = *offset; -#define sendfile sendfile64 +#ifdef HAVE_SENDFILE64 + static int sendfile64_enosys; /* sendfile64() syscall not found */ +#endif + off_t offtmp; + ssize_t ret; -#elif APR_HAS_LARGE_FILES && SIZEOF_OFF_T == 4 - /* 64-bit apr_off_t but no sendfile64(): fail if trying to send - * past the 2Gb limit. */ - off_t off; - - if ((apr_int64_t)*offset + *len > INT_MAX) { - return EINVAL; + /* Multiple reports have shown sendfile failing with EINVAL if + * passed a >=2Gb count value on some 64-bit kernels. It won't + * noticably hurt performance to limit each call to <2Gb at a time, + * so avoid that issue here. (Round down to a common page size.) */ + if (sizeof(off_t) == 8 && len > INT_MAX) + len = INT_MAX - 8191; + + /* The simple and common case: we don't cross the LFS barrier */ + if (sizeof(off_t) == 8 || (apr_int64_t)*off + len <= INT_MAX) { + offtmp = *off; + do + ret = sendfile(out, in, &offtmp, len); + while (ret == -1 && errno == EINTR); + return ret; } - - off = *offset; -#else - off_t off = *offset; + /* From here down we know it's a 32-bit runtime */ +#ifdef HAVE_SENDFILE64 + if (!sendfile64_enosys) { + do + ret = sendfile64(out, in, off, len); + while (ret == -1 && errno == EINTR); - /* Multiple reports have shown sendfile failing with EINVAL if - * passed a >=2Gb count value on some 64-bit kernels. It won't - * noticably hurt performance to limit each call to <2Gb at a - * time, so avoid that issue here: */ - if (sizeof(off_t) == 8 && *len > INT_MAX) { - *len = INT_MAX; + if (ret != -1 || errno != ENOSYS) + return ret; + + sendfile64_enosys = 1; } #endif + if (*off > INT_MAX) { + errno = EINVAL; + return -1; + } + offtmp = *off; + do + ret = sendfile(out, in, &offtmp, len); + while (ret == -1 && errno == EINTR); + return ret; +#endif /* APR_HAS_LARGE_FILES */ +} + + +apr_status_t apr_socket_sendfile(apr_socket_t *sock, apr_file_t *file, + apr_hdtr_t *hdtr, apr_off_t *offset, + apr_size_t *len, apr_int32_t flags) +{ + int rv, nbytes = 0, total_hdrbytes, i; + apr_status_t arv; + apr_off_t off = *offset; if (!hdtr) { hdtr = &no_hdtr; @@ -325,12 +363,10 @@ apr_status_t apr_socket_sendfile(apr_socket_t *sock, apr_file_t *file, goto do_select; } - do { - rv = sendfile(sock->socketdes, /* socket */ + rv = do_sendfile(sock->socketdes, /* socket */ file->filedes, /* open file descriptor of the file to be sent */ &off, /* where in the file to start */ *len); /* number of bytes to send */ - } while (rv == -1 && errno == EINTR); while ((rv == -1) && (errno == EAGAIN || errno == EWOULDBLOCK) && (sock->timeout > 0)) { @@ -341,12 +377,10 @@ do_select: return arv; } else { - do { - rv = sendfile(sock->socketdes, /* socket */ + rv = do_sendfile(sock->socketdes, /* socket */ file->filedes, /* open file descriptor of the file to be sent */ &off, /* where in the file to start */ *len); /* number of bytes to send */ - } while (rv == -1 && errno == EINTR); } }