Skip to content

Commit 529565d

Browse files
Ingo MolnarJens Axboe
authored andcommitted
[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 <[email protected]> Signed-off-by: Jens Axboe <[email protected]>
1 parent 3a326a2 commit 529565d

File tree

2 files changed

+46
-15
lines changed

2 files changed

+46
-15
lines changed

fs/splice.c

Lines changed: 41 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -680,7 +680,8 @@ EXPORT_SYMBOL(generic_splice_sendpage);
680680
* Attempt to initiate a splice from pipe to file.
681681
*/
682682
static long do_splice_from(struct pipe_inode_info *pipe, struct file *out,
683-
size_t len, unsigned int flags)
683+
loff_t __user *off_out, size_t len,
684+
unsigned int flags)
684685
{
685686
loff_t pos;
686687
int ret;
@@ -691,7 +692,11 @@ static long do_splice_from(struct pipe_inode_info *pipe, struct file *out,
691692
if (!(out->f_mode & FMODE_WRITE))
692693
return -EBADF;
693694

695+
if (off_out && copy_from_user(&out->f_pos, off_out, sizeof(loff_t)))
696+
return -EFAULT;
697+
694698
pos = out->f_pos;
699+
695700
ret = rw_verify_area(WRITE, out, &pos, len);
696701
if (unlikely(ret < 0))
697702
return ret;
@@ -702,8 +707,9 @@ static long do_splice_from(struct pipe_inode_info *pipe, struct file *out,
702707
/*
703708
* Attempt to initiate a splice from a file to a pipe.
704709
*/
705-
static long do_splice_to(struct file *in, struct pipe_inode_info *pipe,
706-
size_t len, unsigned int flags)
710+
static long do_splice_to(struct file *in, loff_t __user *off_in,
711+
struct pipe_inode_info *pipe, size_t len,
712+
unsigned int flags)
707713
{
708714
loff_t pos, isize, left;
709715
int ret;
@@ -714,7 +720,11 @@ static long do_splice_to(struct file *in, struct pipe_inode_info *pipe,
714720
if (!(in->f_mode & FMODE_READ))
715721
return -EBADF;
716722

723+
if (off_in && copy_from_user(&in->f_pos, off_in, sizeof(loff_t)))
724+
return -EFAULT;
725+
717726
pos = in->f_pos;
727+
718728
ret = rw_verify_area(READ, in, &pos, len);
719729
if (unlikely(ret < 0))
720730
return ret;
@@ -733,23 +743,39 @@ static long do_splice_to(struct file *in, struct pipe_inode_info *pipe,
733743
/*
734744
* Determine where to splice to/from.
735745
*/
736-
static long do_splice(struct file *in, struct file *out, size_t len,
737-
unsigned int flags)
746+
static long do_splice(struct file *in, loff_t __user *off_in,
747+
struct file *out, loff_t __user *off_out,
748+
size_t len, unsigned int flags)
738749
{
739750
struct pipe_inode_info *pipe;
740751

752+
if (off_out && out->f_op->llseek == no_llseek)
753+
return -EINVAL;
754+
if (off_in && in->f_op->llseek == no_llseek)
755+
return -EINVAL;
756+
741757
pipe = in->f_dentry->d_inode->i_pipe;
742-
if (pipe)
743-
return do_splice_from(pipe, out, len, flags);
758+
if (pipe) {
759+
if (off_in)
760+
return -ESPIPE;
761+
762+
return do_splice_from(pipe, out, off_out, len, flags);
763+
}
744764

745765
pipe = out->f_dentry->d_inode->i_pipe;
746-
if (pipe)
747-
return do_splice_to(in, pipe, len, flags);
766+
if (pipe) {
767+
if (off_out)
768+
return -ESPIPE;
769+
770+
return do_splice_to(in, off_in, pipe, len, flags);
771+
}
748772

749773
return -EINVAL;
750774
}
751775

752-
asmlinkage long sys_splice(int fdin, int fdout, size_t len, unsigned int flags)
776+
asmlinkage long sys_splice(int fd_in, loff_t __user *off_in,
777+
int fd_out, loff_t __user *off_out,
778+
size_t len, unsigned int flags)
753779
{
754780
long error;
755781
struct file *in, *out;
@@ -759,13 +785,15 @@ asmlinkage long sys_splice(int fdin, int fdout, size_t len, unsigned int flags)
759785
return 0;
760786

761787
error = -EBADF;
762-
in = fget_light(fdin, &fput_in);
788+
in = fget_light(fd_in, &fput_in);
763789
if (in) {
764790
if (in->f_mode & FMODE_READ) {
765-
out = fget_light(fdout, &fput_out);
791+
out = fget_light(fd_out, &fput_out);
766792
if (out) {
767793
if (out->f_mode & FMODE_WRITE)
768-
error = do_splice(in, out, len, flags);
794+
error = do_splice(in, off_in,
795+
out, off_out,
796+
len, flags);
769797
fput_light(out, fput_out);
770798
}
771799
}

include/linux/syscalls.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -569,8 +569,11 @@ asmlinkage long compat_sys_newfstatat(unsigned int dfd, char __user * filename,
569569
asmlinkage long compat_sys_openat(unsigned int dfd, const char __user *filename,
570570
int flags, int mode);
571571
asmlinkage long sys_unshare(unsigned long unshare_flags);
572-
asmlinkage long sys_splice(int fdin, int fdout, size_t len,
573-
unsigned int flags);
572+
573+
asmlinkage long sys_splice(int fd_in, loff_t __user *off_in,
574+
int fd_out, loff_t __user *off_out,
575+
size_t len, unsigned int flags);
576+
574577
asmlinkage long sys_sync_file_range(int fd, loff_t offset, loff_t nbytes,
575578
int flags);
576579

0 commit comments

Comments
 (0)