diff options
| -rw-r--r-- | fs/adfs/file.c | 1 | ||||
| -rw-r--r-- | fs/affs/file.c | 1 | ||||
| -rw-r--r-- | fs/bfs/file.c | 9 | ||||
| -rw-r--r-- | fs/block_dev.c | 1 | ||||
| -rw-r--r-- | fs/ext2/file.c | 1 | ||||
| -rw-r--r-- | fs/ext3/file.c | 1 | ||||
| -rw-r--r-- | fs/freevxfs/vxfs_inode.c | 1 | ||||
| -rw-r--r-- | fs/hpfs/inode.c | 1 | ||||
| -rw-r--r-- | fs/jffs/inode-v23.c | 15 | ||||
| -rw-r--r-- | fs/jffs2/file.c | 1 | ||||
| -rw-r--r-- | fs/jfs/file.c | 1 | ||||
| -rw-r--r-- | fs/minix/file.c | 1 | ||||
| -rw-r--r-- | fs/ntfs/file.c | 1 | ||||
| -rw-r--r-- | fs/qnx4/file.c | 1 | ||||
| -rw-r--r-- | fs/ramfs/inode.c | 1 | ||||
| -rw-r--r-- | fs/read_write.c | 111 | ||||
| -rw-r--r-- | fs/reiserfs/file.c | 1 | ||||
| -rw-r--r-- | fs/sysv/file.c | 1 | ||||
| -rw-r--r-- | fs/udf/file.c | 1 | ||||
| -rw-r--r-- | fs/ufs/file.c | 1 | ||||
| -rw-r--r-- | include/linux/fs.h | 2 | ||||
| -rw-r--r-- | kernel/ksyms.c | 1 | ||||
| -rw-r--r-- | mm/filemap.c | 130 |
23 files changed, 157 insertions, 128 deletions
diff --git a/fs/adfs/file.c b/fs/adfs/file.c index 10696080dff9..e5422259c256 100644 --- a/fs/adfs/file.c +++ b/fs/adfs/file.c @@ -36,6 +36,7 @@ struct file_operations adfs_file_operations = { mmap: generic_file_mmap, fsync: file_fsync, write: generic_file_write, + sendfile: generic_file_sendfile, }; struct inode_operations adfs_file_inode_operations = { diff --git a/fs/affs/file.c b/fs/affs/file.c index c76ca76d47d3..6568b35895e9 100644 --- a/fs/affs/file.c +++ b/fs/affs/file.c @@ -50,6 +50,7 @@ struct file_operations affs_file_operations = { open: affs_file_open, release: affs_file_release, fsync: file_fsync, + sendfile: generic_file_sendfile, }; struct inode_operations affs_file_inode_operations = { diff --git a/fs/bfs/file.c b/fs/bfs/file.c index 93c83bbe868f..bf79feea09b9 100644 --- a/fs/bfs/file.c +++ b/fs/bfs/file.c @@ -18,10 +18,11 @@ #endif struct file_operations bfs_file_operations = { - llseek: generic_file_llseek, - read: generic_file_read, - write: generic_file_write, - mmap: generic_file_mmap, + llseek: generic_file_llseek, + read: generic_file_read, + write: generic_file_write, + mmap: generic_file_mmap, + sendfile: generic_file_sendfile, }; static int bfs_move_block(unsigned long from, unsigned long to, struct super_block *sb) diff --git a/fs/block_dev.c b/fs/block_dev.c index 2a0304c00c77..2c1e6f35fae1 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -774,6 +774,7 @@ struct file_operations def_blk_fops = { mmap: generic_file_mmap, fsync: block_fsync, ioctl: blkdev_ioctl, + sendfile: generic_file_sendfile, }; int ioctl_by_bdev(struct block_device *bdev, unsigned cmd, unsigned long arg) diff --git a/fs/ext2/file.c b/fs/ext2/file.c index e005965f6182..e2ce546bf80e 100644 --- a/fs/ext2/file.c +++ b/fs/ext2/file.c @@ -46,6 +46,7 @@ struct file_operations ext2_file_operations = { open: generic_file_open, release: ext2_release_file, fsync: ext2_sync_file, + sendfile: generic_file_sendfile, }; struct inode_operations ext2_file_inode_operations = { diff --git a/fs/ext3/file.c b/fs/ext3/file.c index d5040f1cdbcd..f7e5783b6cbd 100644 --- a/fs/ext3/file.c +++ b/fs/ext3/file.c @@ -84,6 +84,7 @@ struct file_operations ext3_file_operations = { open: ext3_open_file, /* BKL not held. Don't need */ release: ext3_release_file, /* BKL not held. Don't need */ fsync: ext3_sync_file, /* BKL held */ + sendfile: generic_file_sendfile, /* BKL not held. Don't need */ }; struct inode_operations ext3_file_inode_operations = { diff --git a/fs/freevxfs/vxfs_inode.c b/fs/freevxfs/vxfs_inode.c index af7c59ff068d..3e7288372ad7 100644 --- a/fs/freevxfs/vxfs_inode.c +++ b/fs/freevxfs/vxfs_inode.c @@ -53,6 +53,7 @@ static struct file_operations vxfs_file_operations = { .llseek = generic_file_llseek, .read = generic_file_read, .mmap = generic_file_mmap, + .sendfile = generic_file_sendfile, }; diff --git a/fs/hpfs/inode.c b/fs/hpfs/inode.c index 1a8ce91c00c8..cdcfb294fc49 100644 --- a/fs/hpfs/inode.c +++ b/fs/hpfs/inode.c @@ -21,6 +21,7 @@ static struct file_operations hpfs_file_ops = open: hpfs_open, release: hpfs_file_release, fsync: hpfs_file_fsync, + sendfile: generic_file_sendfile, }; static struct inode_operations hpfs_file_iops = diff --git a/fs/jffs/inode-v23.c b/fs/jffs/inode-v23.c index 839108f3f21a..aca27e701f64 100644 --- a/fs/jffs/inode-v23.c +++ b/fs/jffs/inode-v23.c @@ -1631,13 +1631,14 @@ extern loff_t generic_file_llseek(struct file *, loff_t, int) __attribute__((wea static struct file_operations jffs_file_operations = { - open: generic_file_open, - llseek: generic_file_llseek, - read: generic_file_read, - write: generic_file_write, - ioctl: jffs_ioctl, - mmap: generic_file_mmap, - fsync: jffs_fsync, + open: generic_file_open, + llseek: generic_file_llseek, + read: generic_file_read, + write: generic_file_write, + ioctl: jffs_ioctl, + mmap: generic_file_mmap, + fsync: jffs_fsync, + sendfile: generic_file_sendfile, }; diff --git a/fs/jffs2/file.c b/fs/jffs2/file.c index 96064cdffd83..02530413f8fd 100644 --- a/fs/jffs2/file.c +++ b/fs/jffs2/file.c @@ -82,6 +82,7 @@ struct file_operations jffs2_file_operations = ioctl: jffs2_ioctl, mmap: generic_file_mmap, fsync: jffs2_fsync + sendfile: generic_file_sendfile, }; /* jffs2_file_inode_operations */ diff --git a/fs/jfs/file.c b/fs/jfs/file.c index ceb7da87fbba..ddda09abfe13 100644 --- a/fs/jfs/file.c +++ b/fs/jfs/file.c @@ -98,5 +98,6 @@ struct file_operations jfs_file_operations = { .write = generic_file_write, .read = generic_file_read, .mmap = generic_file_mmap, + .sendfile = generic_file_sendfile, .fsync = jfs_fsync, }; diff --git a/fs/minix/file.c b/fs/minix/file.c index 14cccf700845..2a22c0ec950f 100644 --- a/fs/minix/file.c +++ b/fs/minix/file.c @@ -21,6 +21,7 @@ struct file_operations minix_file_operations = { write: generic_file_write, mmap: generic_file_mmap, fsync: minix_sync_file, + sendfile: generic_file_sendfile, }; struct inode_operations minix_file_inode_operations = { diff --git a/fs/ntfs/file.c b/fs/ntfs/file.c index 698aef36db6f..898bea24826e 100644 --- a/fs/ntfs/file.c +++ b/fs/ntfs/file.c @@ -52,6 +52,7 @@ struct file_operations ntfs_file_ops = { llseek: generic_file_llseek, /* Seek inside file. */ read: generic_file_read, /* Read from file. */ mmap: generic_file_mmap, /* Mmap file. */ + sendfile: generic_file_sendfile, /* Zero-copy data send */ open: ntfs_file_open, /* Open file. */ }; diff --git a/fs/qnx4/file.c b/fs/qnx4/file.c index ecf6d050697d..9755fd2ccbf4 100644 --- a/fs/qnx4/file.c +++ b/fs/qnx4/file.c @@ -33,6 +33,7 @@ struct file_operations qnx4_file_operations = #ifdef CONFIG_QNX4FS_RW fsync: qnx4_sync_file, #endif + sendfile: generic_file_sendfile, }; struct inode_operations qnx4_file_inode_operations = diff --git a/fs/ramfs/inode.c b/fs/ramfs/inode.c index 1ed057e963f2..1beb8ecb05f8 100644 --- a/fs/ramfs/inode.c +++ b/fs/ramfs/inode.c @@ -276,6 +276,7 @@ static struct file_operations ramfs_file_operations = { write: generic_file_write, mmap: generic_file_mmap, fsync: ramfs_sync_file, + sendfile: generic_file_sendfile, }; static struct inode_operations ramfs_dir_inode_operations = { diff --git a/fs/read_write.c b/fs/read_write.c index 0e94cdc214bb..ee0a945611a0 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -19,6 +19,7 @@ struct file_operations generic_ro_fops = { llseek: generic_file_llseek, read: generic_file_read, mmap: generic_file_mmap, + sendfile: generic_file_sendfile, }; loff_t generic_file_llseek(struct file *file, loff_t offset, int origin) @@ -436,3 +437,113 @@ asmlinkage ssize_t sys_writev(unsigned long fd, const struct iovec * vector, bad_file: return ret; } + +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; + + /* + * Get input file, and verify that it is ok.. + */ + retval = -EBADF; + in_file = fget(in_fd); + if (!in_file) + goto out; + if (!(in_file->f_mode & FMODE_READ)) + goto fput_in; + retval = -EINVAL; + in_inode = in_file->f_dentry->d_inode; + if (!in_inode) + goto fput_in; + if (!in_file->f_op || !in_file->f_op->sendfile) + goto fput_in; + retval = locks_verify_area(FLOCK_VERIFY_READ, in_inode, in_file, in_file->f_pos, count); + if (retval) + goto fput_in; + + /* + * Get output file, and verify that it is ok.. + */ + retval = -EBADF; + out_file = fget(out_fd); + if (!out_file) + goto fput_in; + if (!(out_file->f_mode & FMODE_WRITE)) + goto fput_out; + retval = -EINVAL; + if (!out_file->f_op || !out_file->f_op->sendpage) + goto fput_out; + out_inode = out_file->f_dentry->d_inode; + retval = locks_verify_area(FLOCK_VERIFY_WRITE, out_inode, out_file, out_file->f_pos, count); + if (retval) + goto fput_out; + + if (!ppos) + ppos = &in_file->f_pos; + + if (!max) + max = min(in_inode->i_sb->s_maxbytes, out_inode->i_sb->s_maxbytes); + + pos = *ppos; + retval = -EINVAL; + if (unlikely(pos < 0)) + goto fput_out; + if (unlikely(pos + count > max)) { + retval = -EOVERFLOW; + if (pos >= max) + goto fput_out; + count = max - pos; + } + + retval = in_file->f_op->sendfile(out_file, in_file, ppos, count); + + if (*ppos > max) + retval = -EOVERFLOW; + +fput_out: + fput(out_file); +fput_in: + fput(in_file); +out: + return retval; +} + +asmlinkage ssize_t sys_sendfile(int out_fd, int in_fd, off_t *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, MAX_NON_LFS); +} + +asmlinkage ssize_t sys_sendfile64(int out_fd, int in_fd, loff_t *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); +} diff --git a/fs/reiserfs/file.c b/fs/reiserfs/file.c index d4a7e346ad9c..a08cd5456567 100644 --- a/fs/reiserfs/file.c +++ b/fs/reiserfs/file.c @@ -147,6 +147,7 @@ struct file_operations reiserfs_file_operations = { mmap: generic_file_mmap, release: reiserfs_file_release, fsync: reiserfs_sync_file, + sendfile: generic_file_sendfile, }; diff --git a/fs/sysv/file.c b/fs/sysv/file.c index ab323a17ae37..dd7dc5d0fae3 100644 --- a/fs/sysv/file.c +++ b/fs/sysv/file.c @@ -25,6 +25,7 @@ struct file_operations sysv_file_operations = { write: generic_file_write, mmap: generic_file_mmap, fsync: sysv_sync_file, + sendfile: generic_file_sendfile, }; struct inode_operations sysv_file_inode_operations = { diff --git a/fs/udf/file.c b/fs/udf/file.c index 7afe02cfd1af..8113c6ed6145 100644 --- a/fs/udf/file.c +++ b/fs/udf/file.c @@ -365,6 +365,7 @@ struct file_operations udf_file_operations = { write: udf_file_write, release: udf_release_file, fsync: udf_fsync_file, + sendfile: generic_file_sendfile, }; struct inode_operations udf_file_inode_operations = { diff --git a/fs/ufs/file.c b/fs/ufs/file.c index f282ea559c80..01447f9e9c4b 100644 --- a/fs/ufs/file.c +++ b/fs/ufs/file.c @@ -47,6 +47,7 @@ struct file_operations ufs_file_operations = { write: generic_file_write, mmap: generic_file_mmap, open: generic_file_open, + sendfile: generic_file_sendfile, }; struct inode_operations ufs_file_inode_operations = { diff --git a/include/linux/fs.h b/include/linux/fs.h index 21aa43cc5c4d..1d3334ca66d9 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -757,6 +757,7 @@ struct file_operations { int (*lock) (struct file *, int, struct file_lock *); ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *); ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *); + ssize_t (*sendfile) (struct file *, struct file *, loff_t *, size_t); ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int); unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long); }; @@ -1234,6 +1235,7 @@ extern int generic_file_mmap(struct file *, struct vm_area_struct *); extern int file_read_actor(read_descriptor_t * desc, struct page *page, unsigned long offset, unsigned long size); extern ssize_t generic_file_read(struct file *, char *, size_t, loff_t *); extern ssize_t generic_file_write(struct file *, const char *, size_t, loff_t *); +extern ssize_t generic_file_sendfile(struct file *, struct file *, loff_t *, size_t); extern void do_generic_file_read(struct file *, loff_t *, read_descriptor_t *, read_actor_t); ssize_t generic_file_direct_IO(int rw, struct inode *inode, char *buf, loff_t offset, size_t count); diff --git a/kernel/ksyms.c b/kernel/ksyms.c index f18263655052..af4e73ca23c0 100644 --- a/kernel/ksyms.c +++ b/kernel/ksyms.c @@ -218,6 +218,7 @@ EXPORT_SYMBOL(generic_commit_write); EXPORT_SYMBOL(block_truncate_page); EXPORT_SYMBOL(generic_block_bmap); EXPORT_SYMBOL(generic_file_read); +EXPORT_SYMBOL(generic_file_sendfile); EXPORT_SYMBOL(do_generic_file_read); EXPORT_SYMBOL(generic_file_write); EXPORT_SYMBOL(generic_file_mmap); diff --git a/mm/filemap.c b/mm/filemap.c index 6b19b188f265..bdf5d1fd9015 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -1119,127 +1119,23 @@ static int file_send_actor(read_descriptor_t * desc, struct page *page, unsigned return written; } -static ssize_t common_sendfile(int out_fd, int in_fd, loff_t *offset, size_t count, loff_t max) +ssize_t generic_file_sendfile(struct file *in_file, struct file *out_file, + loff_t *ppos, size_t count) { - ssize_t retval; - struct file * in_file, * out_file; - struct inode * in_inode, * out_inode; + read_descriptor_t desc; - /* - * Get input file, and verify that it is ok.. - */ - retval = -EBADF; - in_file = fget(in_fd); - if (!in_file) - goto out; - if (!(in_file->f_mode & FMODE_READ)) - goto fput_in; - retval = -EINVAL; - in_inode = in_file->f_dentry->d_inode; - if (!in_inode) - goto fput_in; - if (!in_inode->i_mapping->a_ops->readpage) - goto fput_in; - retval = locks_verify_area(FLOCK_VERIFY_READ, in_inode, in_file, in_file->f_pos, count); - if (retval) - goto fput_in; - - retval = security_ops->file_permission (in_file, MAY_READ); - if (retval) - goto fput_in; + if (!count) + return 0; - /* - * Get output file, and verify that it is ok.. - */ - retval = -EBADF; - out_file = fget(out_fd); - if (!out_file) - goto fput_in; - if (!(out_file->f_mode & FMODE_WRITE)) - goto fput_out; - retval = -EINVAL; - if (!out_file->f_op || !out_file->f_op->sendpage) - goto fput_out; - out_inode = out_file->f_dentry->d_inode; - retval = locks_verify_area(FLOCK_VERIFY_WRITE, out_inode, out_file, out_file->f_pos, count); - if (retval) - goto fput_out; - - retval = security_ops->file_permission (out_file, MAY_WRITE); - if (retval) - goto fput_out; - - retval = 0; - if (count) { - read_descriptor_t desc; - loff_t pos; + desc.written = 0; + desc.count = count; + desc.buf = (char *)out_file; + desc.error = 0; - if (!offset) - offset = &in_file->f_pos; - - pos = *offset; - retval = -EINVAL; - if (unlikely(pos < 0)) - goto fput_out; - if (unlikely(pos + count > max)) { - retval = -EOVERFLOW; - if (pos >= max) - goto fput_out; - count = max - pos; - } - - desc.written = 0; - desc.count = count; - desc.buf = (char *) out_file; - desc.error = 0; - do_generic_file_read(in_file, offset, &desc, file_send_actor); - - retval = desc.written; - if (!retval) - retval = desc.error; - pos = *offset; - if (pos > max) - retval = -EOVERFLOW; - } - -fput_out: - fput(out_file); -fput_in: - fput(in_file); -out: - return retval; -} - -asmlinkage ssize_t sys_sendfile(int out_fd, int in_fd, off_t *offset, size_t count) -{ - loff_t pos, *ppos = NULL; - ssize_t ret; - if (offset) { - off_t off; - if (unlikely(get_user(off, offset))) - return -EFAULT; - pos = off; - ppos = &pos; - } - ret = common_sendfile(out_fd, in_fd, ppos, count, MAX_NON_LFS); - if (offset && put_user(pos, offset)) - ret = -EFAULT; - return ret; -} - -asmlinkage ssize_t sys_sendfile64(int out_fd, int in_fd, loff_t *offset, size_t count) -{ - loff_t pos, *ppos = NULL; - ssize_t ret; - if (offset) { - if (unlikely(copy_from_user(&pos, offset, sizeof(loff_t)))) - return -EFAULT; - ppos = &pos; - } - ret = common_sendfile(out_fd, in_fd, ppos, count, MAX_LFS_FILESIZE); - if (offset && put_user(pos, offset)) - ret = -EFAULT; - return ret; + do_generic_file_read(in_file, ppos, &desc, file_send_actor); + if (desc.written) + return desc.written; + return desc.error; } static ssize_t |
