| From stable-bounces@linux.kernel.org Sun Nov 5 03:41:17 2006 |
| Date: Sun, 5 Nov 2006 12:39:51 +0100 |
| From: Jens Axboe <jens.axboe@oracle.com> |
| To: stable@kernel.org |
| Subject: splice: fix problem introduced with inode diet |
| |
| After the inode slimming patch that unionised i_pipe/i_bdev/i_cdev, it's |
| no longer enough to check for existance of ->i_pipe to verify that this |
| is a pipe. |
| |
| Original patch from Eric Dumazet <dada1@cosmosbay.com> |
| Final solution suggested by Linus. |
| |
| Signed-off-by: Jens Axboe <jens.axboe@oracle.com> |
| Signed-off-by: Linus Torvalds <torvalds@osdl.org> |
| Signed-off-by: Chris Wright <chrisw@sous-sol.org> |
| --- |
| |
| fs/splice.c | 26 ++++++++++++++++++++------ |
| 1 file changed, 20 insertions(+), 6 deletions(-) |
| |
| --- linux-2.6.18.2.orig/fs/splice.c |
| +++ linux-2.6.18.2/fs/splice.c |
| @@ -1042,6 +1042,19 @@ out_release: |
| EXPORT_SYMBOL(do_splice_direct); |
| |
| /* |
| + * After the inode slimming patch, i_pipe/i_bdev/i_cdev share the same |
| + * location, so checking ->i_pipe is not enough to verify that this is a |
| + * pipe. |
| + */ |
| +static inline struct pipe_inode_info *pipe_info(struct inode *inode) |
| +{ |
| + if (S_ISFIFO(inode->i_mode)) |
| + return inode->i_pipe; |
| + |
| + return NULL; |
| +} |
| + |
| +/* |
| * Determine where to splice to/from. |
| */ |
| static long do_splice(struct file *in, loff_t __user *off_in, |
| @@ -1052,7 +1065,7 @@ static long do_splice(struct file *in, l |
| loff_t offset, *off; |
| long ret; |
| |
| - pipe = in->f_dentry->d_inode->i_pipe; |
| + pipe = pipe_info(in->f_dentry->d_inode); |
| if (pipe) { |
| if (off_in) |
| return -ESPIPE; |
| @@ -1073,7 +1086,7 @@ static long do_splice(struct file *in, l |
| return ret; |
| } |
| |
| - pipe = out->f_dentry->d_inode->i_pipe; |
| + pipe = pipe_info(out->f_dentry->d_inode); |
| if (pipe) { |
| if (off_out) |
| return -ESPIPE; |
| @@ -1231,7 +1244,7 @@ static int get_iovec_page_array(const st |
| static long do_vmsplice(struct file *file, const struct iovec __user *iov, |
| unsigned long nr_segs, unsigned int flags) |
| { |
| - struct pipe_inode_info *pipe = file->f_dentry->d_inode->i_pipe; |
| + struct pipe_inode_info *pipe; |
| struct page *pages[PIPE_BUFFERS]; |
| struct partial_page partial[PIPE_BUFFERS]; |
| struct splice_pipe_desc spd = { |
| @@ -1241,7 +1254,8 @@ static long do_vmsplice(struct file *fil |
| .ops = &user_page_pipe_buf_ops, |
| }; |
| |
| - if (unlikely(!pipe)) |
| + pipe = pipe_info(file->f_dentry->d_inode); |
| + if (!pipe) |
| return -EBADF; |
| if (unlikely(nr_segs > UIO_MAXIOV)) |
| return -EINVAL; |
| @@ -1475,8 +1489,8 @@ static int link_pipe(struct pipe_inode_i |
| static long do_tee(struct file *in, struct file *out, size_t len, |
| unsigned int flags) |
| { |
| - struct pipe_inode_info *ipipe = in->f_dentry->d_inode->i_pipe; |
| - struct pipe_inode_info *opipe = out->f_dentry->d_inode->i_pipe; |
| + struct pipe_inode_info *ipipe = pipe_info(in->f_dentry->d_inode); |
| + struct pipe_inode_info *opipe = pipe_info(out->f_dentry->d_inode); |
| int ret = -EINVAL; |
| |
| /* |