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);
}

最終更新 2016/06/03 22:31:39 - north
(2016/06/03 22:31:39 作成)


検索

アクセス数
3598126
最近のコメント
コアダンプファイル - sakaia
list_head構造体 - yocto_no_yomikata
勧告ロックと強制ロック - wataash
LKMからのファイル出力 - 重松 宏昌
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
Adsense
広告情報が設定されていません。