linux 2.6.16.38 w/ vs2.0.3-rc1
[linux-2.6.git] / fs / splice.c
index 8fef667..0b83166 100644 (file)
@@ -55,31 +55,43 @@ static int page_cache_pipe_buf_steal(struct pipe_inode_info *pipe,
                                     struct pipe_buffer *buf)
 {
        struct page *page = buf->page;
-       struct address_space *mapping = page_mapping(page);
+       struct address_space *mapping;
 
        lock_page(page);
 
-       WARN_ON(!PageUptodate(page));
+       mapping = page_mapping(page);
+       if (mapping) {
+               WARN_ON(!PageUptodate(page));
 
-       /*
-        * At least for ext2 with nobh option, we need to wait on writeback
-        * completing on this page, since we'll remove it from the pagecache.
-        * Otherwise truncate wont wait on the page, allowing the disk
-        * blocks to be reused by someone else before we actually wrote our
-        * data to them. fs corruption ensues.
-        */
-       wait_on_page_writeback(page);
+               /*
+                * At least for ext2 with nobh option, we need to wait on
+                * writeback completing on this page, since we'll remove it
+                * from the pagecache.  Otherwise truncate wont wait on the
+                * page, allowing the disk blocks to be reused by someone else
+                * before we actually wrote our data to them. fs corruption
+                * ensues.
+                */
+               wait_on_page_writeback(page);
 
-       if (PagePrivate(page))
-               try_to_release_page(page, mapping_gfp_mask(mapping));
+               if (PagePrivate(page))
+                       try_to_release_page(page, mapping_gfp_mask(mapping));
 
-       if (!remove_mapping(mapping, page)) {
-               unlock_page(page);
-               return 1;
+               /*
+                * If we succeeded in removing the mapping, set LRU flag
+                * and return good.
+                */
+               if (remove_mapping(mapping, page)) {
+                       buf->flags |= PIPE_BUF_FLAG_LRU;
+                       return 0;
+               }
        }
 
-       buf->flags |= PIPE_BUF_FLAG_LRU;
-       return 0;
+       /*
+        * Raced with truncate or failed to remove page from current
+        * address space, unlock and return failure.
+        */
+       unlock_page(page);
+       return 1;
 }
 
 static void page_cache_pipe_buf_release(struct pipe_inode_info *pipe,
@@ -595,7 +607,7 @@ find_page:
                        ret = -ENOMEM;
                        page = page_cache_alloc_cold(mapping);
                        if (unlikely(!page))
-                               goto out_nomem;
+                               goto out_ret;
 
                        /*
                         * This will also lock the page
@@ -654,7 +666,7 @@ find_page:
                if (sd->pos + this_len > isize)
                        vmtruncate(mapping->host, isize);
 
-               goto out;
+               goto out_ret;
        }
 
        if (buf->page != page) {
@@ -686,7 +698,7 @@ find_page:
 out:
        page_cache_release(page);
        unlock_page(page);
-out_nomem:
+out_ret:
        return ret;
 }
 
@@ -1029,6 +1041,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 int is_pipe(struct inode *inode)
+{
+       if (inode->i_pipe && S_ISFIFO(inode->i_mode))
+               return 1;
+
+       return 0;
+}
+
 /*
  * Determine where to splice to/from.
  */
@@ -1040,8 +1065,8 @@ static long do_splice(struct file *in, loff_t __user *off_in,
        loff_t offset, *off;
        long ret;
 
-       pipe = in->f_dentry->d_inode->i_pipe;
-       if (pipe) {
+       if (is_pipe(in->f_dentry->d_inode)) {
+               pipe = in->f_dentry->d_inode->i_pipe;
                if (off_in)
                        return -ESPIPE;
                if (off_out) {
@@ -1061,8 +1086,8 @@ static long do_splice(struct file *in, loff_t __user *off_in,
                return ret;
        }
 
-       pipe = out->f_dentry->d_inode->i_pipe;
-       if (pipe) {
+       if (is_pipe(out->f_dentry->d_inode)) {
+               pipe = out->f_dentry->d_inode->i_pipe;
                if (off_out)
                        return -ESPIPE;
                if (off_in) {
@@ -1219,7 +1244,7 @@ static int get_iovec_page_array(const struct iovec __user *iov,
 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 = {
@@ -1229,7 +1254,7 @@ static long do_vmsplice(struct file *file, const struct iovec __user *iov,
                .ops = &user_page_pipe_buf_ops,
        };
 
-       if (unlikely(!pipe))
+       if (!is_pipe(file->f_dentry->d_inode))
                return -EBADF;
        if (unlikely(nr_segs > UIO_MAXIOV))
                return -EINVAL;
@@ -1241,6 +1266,7 @@ static long do_vmsplice(struct file *file, const struct iovec __user *iov,
        if (spd.nr_pages <= 0)
                return spd.nr_pages;
 
+       pipe = file->f_dentry->d_inode->i_pipe;
        return splice_to_pipe(pipe, &spd);
 }
 
@@ -1389,11 +1415,11 @@ static int link_pipe(struct pipe_inode_info *ipipe,
         * could deadlock (one doing tee from A -> B, the other from B -> A).
         */
        if (ipipe->inode < opipe->inode) {
-               mutex_lock(&ipipe->inode->i_mutex);
-               mutex_lock(&opipe->inode->i_mutex);
+               mutex_lock_nested(&ipipe->inode->i_mutex, I_MUTEX_PARENT);
+               mutex_lock_nested(&opipe->inode->i_mutex, I_MUTEX_CHILD);
        } else {
-               mutex_lock(&opipe->inode->i_mutex);
-               mutex_lock(&ipipe->inode->i_mutex);
+               mutex_lock_nested(&opipe->inode->i_mutex, I_MUTEX_PARENT);
+               mutex_lock_nested(&ipipe->inode->i_mutex, I_MUTEX_CHILD);
        }
 
        do {
@@ -1463,15 +1489,20 @@ static int link_pipe(struct pipe_inode_info *ipipe,
 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;
+       struct pipe_inode_info *opipe;
        int ret = -EINVAL;
 
+       if (!is_pipe(in->f_dentry->d_inode) || !is_pipe(out->f_dentry->d_inode))
+               return ret;
+
        /*
         * Duplicate the contents of ipipe to opipe without actually
         * copying the data.
         */
-       if (ipipe && opipe && ipipe != opipe) {
+       ipipe = in->f_dentry->d_inode->i_pipe;
+       opipe = out->f_dentry->d_inode->i_pipe;
+       if (ipipe != opipe) {
                /*
                 * Keep going, unless we encounter an error. The ipipe/opipe
                 * ordering doesn't really matter.