aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/splice.c40
1 files changed, 30 insertions, 10 deletions
diff --git a/fs/splice.c b/fs/splice.c
index 8b5efcc906d..50c43a1e092 100644
--- a/fs/splice.c
+++ b/fs/splice.c
@@ -84,26 +84,43 @@ static void *page_cache_pipe_buf_map(struct file *file,
struct pipe_buffer *buf)
{
struct page *page = buf->page;
-
- lock_page(page);
+ int err;
if (!PageUptodate(page)) {
- unlock_page(page);
- return ERR_PTR(-EIO);
- }
+ lock_page(page);
+
+ /*
+ * Page got truncated/unhashed. This will cause a 0-byte
+ * splice, if this is the first page
+ */
+ if (!page->mapping) {
+ err = -ENODATA;
+ goto error;
+ }
+
+ /*
+ * uh oh, read-error from disk
+ */
+ if (!PageUptodate(page)) {
+ err = -EIO;
+ goto error;
+ }
- if (!page->mapping) {
+ /*
+ * page is ok afterall, fall through to mapping
+ */
unlock_page(page);
- return ERR_PTR(-ENODATA);
}
- return kmap(buf->page);
+ return kmap(page);
+error:
+ unlock_page(page);
+ return ERR_PTR(err);
}
static void page_cache_pipe_buf_unmap(struct pipe_inode_info *info,
struct pipe_buffer *buf)
{
- unlock_page(buf->page);
kunmap(buf->page);
}
@@ -379,7 +396,7 @@ static int pipe_to_file(struct pipe_inode_info *info, struct pipe_buffer *buf,
int ret;
/*
- * after this, page will be locked and unmapped
+ * make sure the data in this buffer is uptodate
*/
src = buf->ops->map(file, info, buf);
if (IS_ERR(src))
@@ -399,6 +416,9 @@ static int pipe_to_file(struct pipe_inode_info *info, struct pipe_buffer *buf,
if (buf->ops->steal(info, buf))
goto find_page;
+ /*
+ * this will also set the page locked
+ */
page = buf->page;
if (add_to_page_cache(page, mapping, index, gfp_mask))
goto find_page;