summaryrefslogtreecommitdiff
path: root/fs
diff options
context:
space:
mode:
authorChristoph Hellwig <hch@sb.bsdonline.org>2002-07-27 21:24:07 +0200
committerChristoph Hellwig <hch@sb.bsdonline.org>2002-07-27 21:24:07 +0200
commit6b1ca206c66dbeeec380813cd183a09de839b4bd (patch)
tree266e666065c2d8da058f34856786be48fd8cce4e /fs
parent46979afd7f43d0ef8d03c09023fb6ebf5196fc90 (diff)
VFS: implement sendfile file operation
Currently the sendfile syscalls hardcode assumptions about the implementation of the read file operations implementation. Although it checks for the presence of a readpage address-space operation filesystems in Linux are free to implement read differently from the generic version (generic_file_read). Many filesystems such as tmpfs, smbfs or xfs chose to implement it differently and need additional locking, revalidation or checks.
Diffstat (limited to 'fs')
-rw-r--r--fs/adfs/file.c1
-rw-r--r--fs/affs/file.c1
-rw-r--r--fs/bfs/file.c9
-rw-r--r--fs/block_dev.c1
-rw-r--r--fs/ext2/file.c1
-rw-r--r--fs/ext3/file.c1
-rw-r--r--fs/freevxfs/vxfs_inode.c1
-rw-r--r--fs/hpfs/inode.c1
-rw-r--r--fs/jffs/inode-v23.c15
-rw-r--r--fs/jffs2/file.c1
-rw-r--r--fs/jfs/file.c1
-rw-r--r--fs/minix/file.c1
-rw-r--r--fs/ntfs/file.c1
-rw-r--r--fs/qnx4/file.c1
-rw-r--r--fs/ramfs/inode.c1
-rw-r--r--fs/read_write.c111
-rw-r--r--fs/reiserfs/file.c1
-rw-r--r--fs/sysv/file.c1
-rw-r--r--fs/udf/file.c1
-rw-r--r--fs/ufs/file.c1
20 files changed, 141 insertions, 11 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 = {