Here is a small proof of concept which
- detects whether sendfile(64) are available on the platform
at run-time
- uses sendfile up to its maximum, if sendfile64 does not
exist
The attached a.out was built from ap_sendfile.c on a
x86/glibc-2.2 box.
strace with glibc-2.3:
open("testfile", O_RDWR|O_CREAT|O_TRUNC|O_LARGEFILE, 0600) = 3
pwrite(3, "a", 1, 2147483647) = 1
open("/dev/null", O_WRONLY) = 4
sendfile64(4, 3, [2147463648], 10000) = 10000
sendfile64(4, 3, [2147473648], 10000) = 10000
and on glibc-2.2:
open("testfile", O_RDWR|O_CREAT|O_TRUNC|O_LARGEFILE, 0600) = 3
pwrite(3, "a", 1, 2147483647) = 1
open("/dev/null", O_WRONLY) = 4
sendfile(4, 3, [2147463648], 10000) = 10000
pread(3, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"...,
16384, 2147473648) = 10000
write(4, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"...,
10000) = 10000
- Sascha#define _XOPEN_SOURCE 500
#define _LARGEFILE_SOURCE 1
#define _LARGEFILE64_SOURCE 1
#include <sys/types.h>
#include <dlfcn.h>
#include <unistd.h>
#include <features.h>
#include <limits.h>
#include <errno.h>
#include <fcntl.h>
static ssize_t (*ap_sendfile) (int __out_fd, int __in_fd, off_t *__offset,
size_t __count) ;
/* we use __off64_t here, because it is always defined in glibc */
static ssize_t (*ap_sendfile64) (int __out_fd, int __in_fd, __off64_t *__offset,
size_t __count);
static int detected;
static void detect(void)
{
ap_sendfile = dlsym(NULL, "sendfile");
ap_sendfile64 = dlsym(NULL, "sendfile64");
detected = 1;
}
ssize_t my_sendfile64(int outfd, int infd, __off64_t *offset, size_t count)
{
if (!detected)
detect();
if (ap_sendfile64) {
return ap_sendfile64(outfd, infd, offset, count);
} else if (ap_sendfile) {
if (*offset + count < (long long) LONG_MAX) {
return ap_sendfile(outfd, infd, (off_t *) offset,
count);
}
}
errno = EFBIG;
return -1;
}
ssize_t wrap_sendfile(int outfd, int infd, __off64_t *offset, size_t count)
{
ssize_t n;
n = my_sendfile64(outfd, infd, offset, count);
if (n == -1 && (errno == EIO || errno == ENOMEM || errno == EFBIG)) {
char buf[16384];
ssize_t w;
__off64_t off = *offset;
while (count > 0) {
n = pread(infd, buf, sizeof(buf), *offset);
if (n == -1) return n;
if (n == 0) break;
do {
w = write(outfd, buf, n);
} while (w == -1 && errno == EINTR);
if (w == -1) return w;
if (w == 0) break;
*offset += w;
count -= w;
}
n = *offset - off;
}
return n;
}
#ifndef NO_DRIVER
#include <stdio.h>
int main(void) {
int fd, outfd;
__off64_t off;
ssize_t a, b;
/* just so that we can print the pointers early */
detect();
printf("sendfile at %p handles up to %ld bytes\n", ap_sendfile,
LONG_MAX);
printf("sendfile64 is also defined at %p\n", ap_sendfile64);
printf("testing sendfile on 4GB file\n");
fd = open("testfile", O_CREAT | O_TRUNC | O_LARGEFILE | O_RDWR, 0600);
pwrite64(fd, "a", 1, 2147483647);
outfd = open("/dev/null", O_WRONLY);
off = 2147483647 - 19999;
a = wrap_sendfile(outfd, fd, &off, 10000);
b = wrap_sendfile(outfd, fd, &off, 10000);
printf("below 2GB: %d (should be 10000)\n", a);
printf("above 2GB: %d (should be 10000)\n", b);
return 0;
}
#endif
a.out
Description: Binary data
