[H. S. Teoh] > Hi, the fix for #396631 to libapr1 does not work. Apache2 still > serves 0 bytes when running on a 2.4 kernel (on my virtual colo > host). Please look into this problem. Thanks! My old patch is slightly buggy - can you try rebuilding apr 1.2.7-8.1 with this updated version of debian/patches/015_sendfile_lfs.dpatch? Thanks, Peter
#! /bin/sh /usr/share/dpatch/dpatch-run ## 015_sendfile_lfs.dpatch by <thom@debian.org> <peter@p12n.org> ## ## DP: Fix up sendfile: ## DP: - Detect sendfile64() at runtime (not present on 2.4 kernels.) ## DP: - Ensure that we only send 2GB chunks on 64bit platforms thanks ## DP: to extreme linux kernel bustage. @DPATCH@ Index: network_io/unix/sendrecv.c --- a/network_io/unix/sendrecv.c +++ b/network_io/unix/sendrecv.c @@ -240,31 +240,77 @@ #if defined(__linux__) && defined(HAVE_WRITEV) +/* 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) +{ +#if !APR_HAS_LARGE_FILES + ssize_t ret; + do + ret = sendfile(out, in, off, len); + while (ret == -1 && errno == EINTR); + return ret; +#else + +#ifdef HAVE_SENDFILE64 + static int sendfile64_enosys; /* sendfile64() syscall not found */ +#endif + off_t offtmp; + ssize_t ret; + + /* 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; + } + + /* 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); + + 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; - -#if APR_HAS_LARGE_FILES && defined(HAVE_SENDFILE64) apr_off_t off = *offset; -#define sendfile sendfile64 - -#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; - } - - off = *offset; - -#else - off_t off = *offset; -#endif if (!hdtr) { hdtr = &no_hdtr; @@ -310,12 +356,10 @@ 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)) { @@ -326,12 +370,10 @@ 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); } }
Attachment:
signature.asc
Description: Digital signature