Commit 529565dc authored by Ingo Molnar's avatar Ingo Molnar Committed by Jens Axboe

[PATCH] splice: add optional input and output offsets

add optional input and output offsets to sys_splice(), for seekable file
descriptors:

 asmlinkage long sys_splice(int fd_in, loff_t __user *off_in,
                            int fd_out, loff_t __user *off_out,
                            size_t len, unsigned int flags);

semantics are straightforward: f_pos will be updated with the offset
provided by user-space, before the splice transfer is about to begin.
Providing a NULL offset pointer means the existing f_pos will be used
(and updated in situ).  Providing an offset for a pipe results in
-ESPIPE. Providing an invalid offset pointer results in -EFAULT.
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
Signed-off-by: default avatarJens Axboe <axboe@suse.de>
parent 3a326a2c
...@@ -680,7 +680,8 @@ EXPORT_SYMBOL(generic_splice_sendpage); ...@@ -680,7 +680,8 @@ EXPORT_SYMBOL(generic_splice_sendpage);
* Attempt to initiate a splice from pipe to file. * Attempt to initiate a splice from pipe to file.
*/ */
static long do_splice_from(struct pipe_inode_info *pipe, struct file *out, static long do_splice_from(struct pipe_inode_info *pipe, struct file *out,
size_t len, unsigned int flags) loff_t __user *off_out, size_t len,
unsigned int flags)
{ {
loff_t pos; loff_t pos;
int ret; int ret;
...@@ -691,7 +692,11 @@ static long do_splice_from(struct pipe_inode_info *pipe, struct file *out, ...@@ -691,7 +692,11 @@ static long do_splice_from(struct pipe_inode_info *pipe, struct file *out,
if (!(out->f_mode & FMODE_WRITE)) if (!(out->f_mode & FMODE_WRITE))
return -EBADF; return -EBADF;
if (off_out && copy_from_user(&out->f_pos, off_out, sizeof(loff_t)))
return -EFAULT;
pos = out->f_pos; pos = out->f_pos;
ret = rw_verify_area(WRITE, out, &pos, len); ret = rw_verify_area(WRITE, out, &pos, len);
if (unlikely(ret < 0)) if (unlikely(ret < 0))
return ret; return ret;
...@@ -702,8 +707,9 @@ static long do_splice_from(struct pipe_inode_info *pipe, struct file *out, ...@@ -702,8 +707,9 @@ static long do_splice_from(struct pipe_inode_info *pipe, struct file *out,
/* /*
* Attempt to initiate a splice from a file to a pipe. * Attempt to initiate a splice from a file to a pipe.
*/ */
static long do_splice_to(struct file *in, struct pipe_inode_info *pipe, static long do_splice_to(struct file *in, loff_t __user *off_in,
size_t len, unsigned int flags) struct pipe_inode_info *pipe, size_t len,
unsigned int flags)
{ {
loff_t pos, isize, left; loff_t pos, isize, left;
int ret; int ret;
...@@ -714,7 +720,11 @@ static long do_splice_to(struct file *in, struct pipe_inode_info *pipe, ...@@ -714,7 +720,11 @@ static long do_splice_to(struct file *in, struct pipe_inode_info *pipe,
if (!(in->f_mode & FMODE_READ)) if (!(in->f_mode & FMODE_READ))
return -EBADF; return -EBADF;
if (off_in && copy_from_user(&in->f_pos, off_in, sizeof(loff_t)))
return -EFAULT;
pos = in->f_pos; pos = in->f_pos;
ret = rw_verify_area(READ, in, &pos, len); ret = rw_verify_area(READ, in, &pos, len);
if (unlikely(ret < 0)) if (unlikely(ret < 0))
return ret; return ret;
...@@ -733,23 +743,39 @@ static long do_splice_to(struct file *in, struct pipe_inode_info *pipe, ...@@ -733,23 +743,39 @@ static long do_splice_to(struct file *in, struct pipe_inode_info *pipe,
/* /*
* Determine where to splice to/from. * Determine where to splice to/from.
*/ */
static long do_splice(struct file *in, struct file *out, size_t len, static long do_splice(struct file *in, loff_t __user *off_in,
unsigned int flags) struct file *out, loff_t __user *off_out,
size_t len, unsigned int flags)
{ {
struct pipe_inode_info *pipe; struct pipe_inode_info *pipe;
if (off_out && out->f_op->llseek == no_llseek)
return -EINVAL;
if (off_in && in->f_op->llseek == no_llseek)
return -EINVAL;
pipe = in->f_dentry->d_inode->i_pipe; pipe = in->f_dentry->d_inode->i_pipe;
if (pipe) if (pipe) {
return do_splice_from(pipe, out, len, flags); if (off_in)
return -ESPIPE;
return do_splice_from(pipe, out, off_out, len, flags);
}
pipe = out->f_dentry->d_inode->i_pipe; pipe = out->f_dentry->d_inode->i_pipe;
if (pipe) if (pipe) {
return do_splice_to(in, pipe, len, flags); if (off_out)
return -ESPIPE;
return do_splice_to(in, off_in, pipe, len, flags);
}
return -EINVAL; return -EINVAL;
} }
asmlinkage long sys_splice(int fdin, int fdout, size_t len, unsigned int flags) asmlinkage long sys_splice(int fd_in, loff_t __user *off_in,
int fd_out, loff_t __user *off_out,
size_t len, unsigned int flags)
{ {
long error; long error;
struct file *in, *out; struct file *in, *out;
...@@ -759,13 +785,15 @@ asmlinkage long sys_splice(int fdin, int fdout, size_t len, unsigned int flags) ...@@ -759,13 +785,15 @@ asmlinkage long sys_splice(int fdin, int fdout, size_t len, unsigned int flags)
return 0; return 0;
error = -EBADF; error = -EBADF;
in = fget_light(fdin, &fput_in); in = fget_light(fd_in, &fput_in);
if (in) { if (in) {
if (in->f_mode & FMODE_READ) { if (in->f_mode & FMODE_READ) {
out = fget_light(fdout, &fput_out); out = fget_light(fd_out, &fput_out);
if (out) { if (out) {
if (out->f_mode & FMODE_WRITE) if (out->f_mode & FMODE_WRITE)
error = do_splice(in, out, len, flags); error = do_splice(in, off_in,
out, off_out,
len, flags);
fput_light(out, fput_out); fput_light(out, fput_out);
} }
} }
......
...@@ -569,8 +569,11 @@ asmlinkage long compat_sys_newfstatat(unsigned int dfd, char __user * filename, ...@@ -569,8 +569,11 @@ asmlinkage long compat_sys_newfstatat(unsigned int dfd, char __user * filename,
asmlinkage long compat_sys_openat(unsigned int dfd, const char __user *filename, asmlinkage long compat_sys_openat(unsigned int dfd, const char __user *filename,
int flags, int mode); int flags, int mode);
asmlinkage long sys_unshare(unsigned long unshare_flags); asmlinkage long sys_unshare(unsigned long unshare_flags);
asmlinkage long sys_splice(int fdin, int fdout, size_t len,
unsigned int flags); asmlinkage long sys_splice(int fd_in, loff_t __user *off_in,
int fd_out, loff_t __user *off_out,
size_t len, unsigned int flags);
asmlinkage long sys_sync_file_range(int fd, loff_t offset, loff_t nbytes, asmlinkage long sys_sync_file_range(int fd, loff_t offset, loff_t nbytes,
int flags); int flags);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment