Search code examples
linuxlinux-kernelsplice

How to splice onto socketfd?


The manual mentioned splice() can transfer data between two arbitrary filedescriptors, also onto a socketfd. This works if the file is send at once. Therefore the filesize has to be lower than PIPE_BUF_SZ (=65536).

But, how to handle bigger files? I want to understand the difference to sendfile() syscall. How would you rewrite the sendfile() syscall?

The second splice returns with Invalid argument. I guess it is because the socketfd is not seekable.

size_t len = 800000; //e.g.
static int do_copy(int in_fd, int out_fd)
{
    loff_t in_off = 0, out_off = 0;
    static int buf_size = 65536; 
    off_t len;
    int filedes[2];
    int err = -1;

    if(pipe(filedes) < 0) {
        perror("pipe:");
        goto out;
    }

    while(len > 0) {
        if(buf_size > len) buf_size = len;
        /* move to pipe buffer. */
        err = splice(in_fd, &in_off, filedes[1], NULL, buf_size, SPLICE_F_MOVE | SPLICE_F_MORE);
        if(err < 0) {
            perror("splice:");
            goto out_close;
        }
        /* move from pipe buffer to out_fd */
        err = splice(filedes[0], NULL, out_fd, &out_off, buf_size, SPLICE_F_MOVE | SPLICE_F_MORE);
        if(err < 0) {
            perror("splice2:");
            goto out_close;
        }
        len -= buf_size;
    }
    err = 0;
    out_close:
    close(filedes[0]);
    close(filedes[1]);

    out:
    return err;
}

Solution

  • sendfile() systemcall does not check if the filedescriptor is seekable. The only check onto that fd is, if you can read (FMODE_READ) onto the fd.

    splice() does some more checks. Among others, if the fd is seekable (FMODE_PREAD) / (FMODE_PWRITE).

    That's why sendfile works, but splice won't.