aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/splice.c54
-rw-r--r--include/linux/syscalls.h7
2 files changed, 46 insertions, 15 deletions
diff --git a/fs/splice.c b/fs/splice.c
index ed91a62402e0..a5326127aad5 100644
--- a/fs/splice.c
+++ b/fs/splice.c
@@ -680,7 +680,8 @@ EXPORT_SYMBOL(generic_splice_sendpage);
680 * Attempt to initiate a splice from pipe to file. 680 * Attempt to initiate a splice from pipe to file.
681 */ 681 */
682static long do_splice_from(struct pipe_inode_info *pipe, struct file *out, 682static 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)
684{ 685{
685 loff_t pos; 686 loff_t pos;
686 int ret; 687 int ret;
@@ -691,7 +692,11 @@ static long do_splice_from(struct pipe_inode_info *pipe, struct file *out,
691 if (!(out->f_mode & FMODE_WRITE)) 692 if (!(out->f_mode & FMODE_WRITE))
692 return -EBADF; 693 return -EBADF;
693 694
695 if (off_out && copy_from_user(&out->f_pos, off_out, sizeof(loff_t)))
696 return -EFAULT;
697
694 pos = out->f_pos; 698 pos = out->f_pos;
699
695 ret = rw_verify_area(WRITE, out, &pos, len); 700 ret = rw_verify_area(WRITE, out, &pos, len);
696 if (unlikely(ret < 0)) 701 if (unlikely(ret < 0))
697 return ret; 702 return ret;
@@ -702,8 +707,9 @@ static long do_splice_from(struct pipe_inode_info *pipe, struct file *out,
702/* 707/*
703 * Attempt to initiate a splice from a file to a pipe. 708 * Attempt to initiate a splice from a file to a pipe.
704 */ 709 */
705static long do_splice_to(struct file *in, struct pipe_inode_info *pipe, 710static long do_splice_to(struct file *in, loff_t __user *off_in,
706 size_t len, unsigned int flags) 711 struct pipe_inode_info *pipe, size_t len,
712 unsigned int flags)
707{ 713{
708 loff_t pos, isize, left; 714 loff_t pos, isize, left;
709 int ret; 715 int ret;
@@ -714,7 +720,11 @@ static long do_splice_to(struct file *in, struct pipe_inode_info *pipe,
714 if (!(in->f_mode & FMODE_READ)) 720 if (!(in->f_mode & FMODE_READ))
715 return -EBADF; 721 return -EBADF;
716 722
723 if (off_in && copy_from_user(&in->f_pos, off_in, sizeof(loff_t)))
724 return -EFAULT;
725
717 pos = in->f_pos; 726 pos = in->f_pos;
727
718 ret = rw_verify_area(READ, in, &pos, len); 728 ret = rw_verify_area(READ, in, &pos, len);
719 if (unlikely(ret < 0)) 729 if (unlikely(ret < 0))
720 return ret; 730 return ret;
@@ -733,23 +743,39 @@ static long do_splice_to(struct file *in, struct pipe_inode_info *pipe,
733/* 743/*
734 * Determine where to splice to/from. 744 * Determine where to splice to/from.
735 */ 745 */
736static long do_splice(struct file *in, struct file *out, size_t len, 746static long do_splice(struct file *in, loff_t __user *off_in,
737 unsigned int flags) 747 struct file *out, loff_t __user *off_out,
748 size_t len, unsigned int flags)
738{ 749{
739 struct pipe_inode_info *pipe; 750 struct pipe_inode_info *pipe;
740 751
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
741 pipe = in->f_dentry->d_inode->i_pipe; 757 pipe = in->f_dentry->d_inode->i_pipe;
742 if (pipe) 758 if (pipe) {
743 return do_splice_from(pipe, out, len, flags); 759 if (off_in)
760 return -ESPIPE;
761
762 return do_splice_from(pipe, out, off_out, len, flags);
763 }
744 764
745 pipe = out->f_dentry->d_inode->i_pipe; 765 pipe = out->f_dentry->d_inode->i_pipe;
746 if (pipe) 766 if (pipe) {
747 return do_splice_to(in, pipe, len, flags); 767 if (off_out)
768 return -ESPIPE;
769
770 return do_splice_to(in, off_in, pipe, len, flags);
771 }
748 772
749 return -EINVAL; 773 return -EINVAL;
750} 774}
751 775
752asmlinkage long sys_splice(int fdin, int fdout, size_t len, unsigned int flags) 776asmlinkage 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)
753{ 779{
754 long error; 780 long error;
755 struct file *in, *out; 781 struct file *in, *out;
@@ -759,13 +785,15 @@ asmlinkage long sys_splice(int fdin, int fdout, size_t len, unsigned int flags)
759 return 0; 785 return 0;
760 786
761 error = -EBADF; 787 error = -EBADF;
762 in = fget_light(fdin, &fput_in); 788 in = fget_light(fd_in, &fput_in);
763 if (in) { 789 if (in) {
764 if (in->f_mode & FMODE_READ) { 790 if (in->f_mode & FMODE_READ) {
765 out = fget_light(fdout, &fput_out); 791 out = fget_light(fd_out, &fput_out);
766 if (out) { 792 if (out) {
767 if (out->f_mode & FMODE_WRITE) 793 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);
769 fput_light(out, fput_out); 797 fput_light(out, fput_out);
770 } 798 }
771 } 799 }
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
index 5717147596b6..4c292faa70c9 100644
--- a/include/linux/syscalls.h
+++ b/include/linux/syscalls.h
@@ -569,8 +569,11 @@ asmlinkage long compat_sys_newfstatat(unsigned int dfd, char __user * filename,
569asmlinkage long compat_sys_openat(unsigned int dfd, const char __user *filename, 569asmlinkage long compat_sys_openat(unsigned int dfd, const char __user *filename,
570 int flags, int mode); 570 int flags, int mode);
571asmlinkage long sys_unshare(unsigned long unshare_flags); 571asmlinkage long sys_unshare(unsigned long unshare_flags);
572asmlinkage long sys_splice(int fdin, int fdout, size_t len, 572
573 unsigned int flags); 573asmlinkage 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
574asmlinkage long sys_sync_file_range(int fd, loff_t offset, loff_t nbytes, 577asmlinkage long sys_sync_file_range(int fd, loff_t offset, loff_t nbytes,
575 int flags); 578 int flags);
576 579