[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: Ingo Molnar <mingo@elte.hu> Signed-off-by: Jens Axboe <axboe@suse.de>
This commit is contained in:
Родитель
3a326a2ce8
Коммит
529565dcb1
54
fs/splice.c
54
fs/splice.c
|
@ -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,
|
||||||
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,
|
||||||
/*
|
/*
|
||||||
* 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,
|
||||||
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,
|
||||||
/*
|
/*
|
||||||
* 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)
|
||||||
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,
|
||||||
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);
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче