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

Attachment: a.out
Description: Binary data

Reply via email to