sendfile
Rev.1を表示中。最新版はこちら。
sendfileは、current->splice_pipeに設定したpipe介して、do_splice_to()でin_fdデータをpipeへ、do_splice_from()でそのpipeからout_fdへ出力します。従ってsendfile()実装はsplice()に準じます。current->splice_pipe設定されたpipeはプロセスがexit()するまで保持されます。offsetのサイズはlongで、do_sendfile()の最大位置はMAX_NON_LFS( ((1UL<<31) - 1))で、NULLの時はデバイスの最大サイズとなり、O_LARGEFILEのファイル/ブロックデバイス(ブロックデバイスはOKです。)の、offset指定してのMAX_NON_LFSを超えた領域は読書きできません。
サンプル
#include <stdio.h> #include <fcntl.h> int main(int argc, char *argv[]) { int fdr, fdw; char buff[2]; loff_t off_in = -1; fdr = open("babakaka1.txt", O_RDONLY); fdw = open("babakaka2.txt", O_CREAT | O_RDWR); if (argc != 2) { sendfile(fdw, fdr, NULL, 1); } else { off_in = atol(argv[1]); sendfile(fdw, fdr, &off_in, 1); } buff[1] = 0; read(fdr, buff, 1); printf("babakaka1.txt:%s [off_in:%d]\n", buff, off_in); lseek(fdw, 0, SEEK_SET); buff[1] = 0; read(fdw, buff, 1); printf("babakaka2.txt:%s\n", buff); close(fdr); close(fdw); }
[root@localhost c]# cat babakaka1.txt 123456789
[root@localhost c]# ./a.out babakaka1.txt:2 [off_in:-1] babakaka2.txt:1
[root@localhost c]# ./a.out 0 babakaka1.txt:1 [off_in:1] babakaka2.txt:1
[root@localhost c]# ./a.out 5 babakaka1.txt:1 [off_in:6] babakaka2.txt:6
実装
SYSCALL_DEFINE4(sendfile, int, out_fd, int, in_fd, off_t __user *, offset, size_t, count) { loff_t pos; off_t off; ssize_t ret; if (offset) { if (unlikely(get_user(off, offset))) return -EFAULT; pos = off; ret = do_sendfile(out_fd, in_fd, &pos, count, MAX_NON_LFS); if (unlikely(put_user(pos, offset))) return -EFAULT; return ret; } return do_sendfile(out_fd, in_fd, NULL, count, 0); } static ssize_t do_sendfile(int out_fd, int in_fd, loff_t *ppos, size_t count, loff_t max) { struct file * in_file, * out_file; struct inode * in_inode, * out_inode; loff_t pos; ssize_t retval; int fput_needed_in, fput_needed_out, fl; retval = -EBADF; in_file = fget_light(in_fd, &fput_needed_in); if (!in_file) goto out; if (!(in_file->f_mode & FMODE_READ)) goto fput_in; retval = -ESPIPE; if (!ppos) ppos = &in_file->f_pos; else if (!(in_file->f_mode & FMODE_PREAD)) goto fput_in; retval = rw_verify_area(READ, in_file, ppos, count); if (retval < 0) goto fput_in; count = retval; retval = -EBADF; out_file = fget_light(out_fd, &fput_needed_out); if (!out_file) goto fput_in; if (!(out_file->f_mode & FMODE_WRITE)) goto fput_out; retval = -EINVAL; in_inode = in_file->f_path.dentry->d_inode; out_inode = out_file->f_path.dentry->d_inode; retval = rw_verify_area(WRITE, out_file, &out_file->f_pos, count); if (retval < 0) goto fput_out; count = retval; if (!max) max = min(in_inode->i_sb->s_maxbytes, out_inode->i_sb->s_maxbytes); pos = *ppos; if (unlikely(pos + count > max)) { retval = -EOVERFLOW; if (pos >= max) goto fput_out; count = max - pos; } fl = 0; #if 0 if (in_file->f_flags & O_NONBLOCK) fl = SPLICE_F_NONBLOCK; #endif retval = do_splice_direct(in_file, ppos, out_file, count, fl); if (retval > 0) { add_rchar(current, retval); add_wchar(current, retval); } inc_syscr(current); inc_syscw(current); if (*ppos > max) retval = -EOVERFLOW; fput_out: fput_light(out_file, fput_needed_out); fput_in: fput_light(in_file, fput_needed_in); out: return retval; } long do_splice_direct(struct file *in, loff_t *ppos, struct file *out, size_t len, unsigned int flags) { struct splice_desc sd = { .len = len, .total_len = len, .flags = flags, .pos = *ppos, .u.file = out, }; long ret; ret = splice_direct_to_actor(in, &sd, direct_splice_actor); if (ret > 0) *ppos = sd.pos; return ret; } ssize_t splice_direct_to_actor(struct file *in, struct splice_desc *sd, splice_direct_actor *actor) { struct pipe_inode_info *pipe; long ret, bytes; umode_t i_mode; size_t len; int i, flags; i_mode = in->f_path.dentry->d_inode->i_mode; if (unlikely(!S_ISREG(i_mode) && !S_ISBLK(i_mode))) return -EINVAL; pipe = current->splice_pipe; if (unlikely(!pipe)) { pipe = alloc_pipe_info(NULL); if (!pipe) return -ENOMEM; pipe->readers = 1; current->splice_pipe = pipe; } ret = 0; bytes = 0; len = sd->total_len; flags = sd->flags; sd->flags &= ~SPLICE_F_NONBLOCK; while (len) { size_t read_len; loff_t pos = sd->pos, prev_pos = pos; ret = do_splice_to(in, &pos, pipe, len, flags); if (unlikely(ret <= 0)) goto out_release; read_len = ret; sd->total_len = read_len; ret = actor(pipe, sd); if (unlikely(ret <= 0)) { sd->pos = prev_pos; goto out_release; } bytes += ret; len -= ret; sd->pos = pos; if (ret < read_len) { sd->pos = prev_pos + ret; goto out_release; } } done: pipe->nrbufs = pipe->curbuf = 0; file_accessed(in); return bytes; out_release: for (i = 0; i < pipe->buffers; i++) { struct pipe_buffer *buf = pipe->bufs + i; if (buf->ops) { buf->ops->release(pipe, buf); buf->ops = NULL; } } if (!bytes) bytes = ret; goto done; } static int direct_splice_actor(struct pipe_inode_info *pipe, struct splice_desc *sd) { struct file *file = sd->u.file; return do_splice_from(pipe, file, &file->f_pos, sd->total_len, sd->flags); }
補足
sendfile64だとoffsetはlong longで、offset指定してのデバイスのファイル最大値領域での読書きが可能となります。SYSCALL_DEFINE4(sendfile64, int, out_fd, int, in_fd, loff_t __user *, offset, size_t, count) { loff_t pos; ssize_t ret; if (offset) { if (unlikely(copy_from_user(&pos, offset, sizeof(loff_t)))) return -EFAULT; ret = do_sendfile(out_fd, in_fd, &pos, count, 0); if (unlikely(put_user(pos, offset))) return -EFAULT; return ret; } return do_sendfile(out_fd, in_fd, NULL, count, 0); }