diff options
-rw-r--r-- | fs/splice.c | 54 | ||||
-rw-r--r-- | include/linux/syscalls.h | 7 |
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 | */ |
682 | static long do_splice_from(struct pipe_inode_info *pipe, struct file *out, | 682 | 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) | ||
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 | */ |
705 | static long do_splice_to(struct file *in, struct pipe_inode_info *pipe, | 710 | static 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 | */ |
736 | static long do_splice(struct file *in, struct file *out, size_t len, | 746 | static 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 | ||
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) | ||
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, | |||
569 | asmlinkage long compat_sys_openat(unsigned int dfd, const char __user *filename, | 569 | asmlinkage long compat_sys_openat(unsigned int dfd, const char __user *filename, |
570 | int flags, int mode); | 570 | int flags, int mode); |
571 | asmlinkage long sys_unshare(unsigned long unshare_flags); | 571 | asmlinkage long sys_unshare(unsigned long unshare_flags); |
572 | asmlinkage long sys_splice(int fdin, int fdout, size_t len, | 572 | |
573 | unsigned int flags); | 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 | |||
574 | asmlinkage long sys_sync_file_range(int fd, loff_t offset, loff_t nbytes, | 577 | asmlinkage long sys_sync_file_range(int fd, loff_t offset, loff_t nbytes, |
575 | int flags); | 578 | int flags); |
576 | 579 | ||