| From f134dab9a5d291e0d47c92ad2064c342f21cf7f9 Mon Sep 17 00:00:00 2001 |
| From: Jan Kara <jack@suse.cz> |
| Date: Wed, 5 Sep 2012 15:48:23 +0200 |
| Subject: [PATCH] udf: Fix data corruption for files in ICB |
| |
| commit 9c2fc0de1a6e638fe58c354a463f544f42a90a09 upstream. |
| |
| When a file is stored in ICB (inode), we overwrite part of the file, and |
| the page containing file's data is not in page cache, we end up corrupting |
| file's data by overwriting them with zeros. The problem is we use |
| simple_write_begin() which simply zeroes parts of the page which are not |
| written to. The problem has been introduced by be021ee4 (udf: convert to |
| new aops). |
| |
| Fix the problem by providing a ->write_begin function which makes the page |
| properly uptodate. |
| |
| Reported-by: Ian Abbott <abbotti@mev.co.uk> |
| Signed-off-by: Jan Kara <jack@suse.cz> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| --- |
| fs/udf/file.c | 35 +++++++++++++++++++++++++++++------ |
| 1 file changed, 29 insertions(+), 6 deletions(-) |
| |
| diff --git a/fs/udf/file.c b/fs/udf/file.c |
| index 4b6a46ccbf46..af6f30ac228f 100644 |
| --- a/fs/udf/file.c |
| +++ b/fs/udf/file.c |
| @@ -41,20 +41,24 @@ |
| #include "udf_i.h" |
| #include "udf_sb.h" |
| |
| -static int udf_adinicb_readpage(struct file *file, struct page *page) |
| +static void __udf_adinicb_readpage(struct page *page) |
| { |
| struct inode *inode = page->mapping->host; |
| char *kaddr; |
| struct udf_inode_info *iinfo = UDF_I(inode); |
| |
| - BUG_ON(!PageLocked(page)); |
| - |
| kaddr = kmap(page); |
| - memset(kaddr, 0, PAGE_CACHE_SIZE); |
| memcpy(kaddr, iinfo->i_ext.i_data + iinfo->i_lenEAttr, inode->i_size); |
| + memset(kaddr + inode->i_size, 0, PAGE_CACHE_SIZE - inode->i_size); |
| flush_dcache_page(page); |
| SetPageUptodate(page); |
| kunmap(page); |
| +} |
| + |
| +static int udf_adinicb_readpage(struct file *file, struct page *page) |
| +{ |
| + BUG_ON(!PageLocked(page)); |
| + __udf_adinicb_readpage(page); |
| unlock_page(page); |
| |
| return 0; |
| @@ -79,6 +83,25 @@ static int udf_adinicb_writepage(struct page *page, |
| return 0; |
| } |
| |
| +static int udf_adinicb_write_begin(struct file *file, |
| + struct address_space *mapping, loff_t pos, |
| + unsigned len, unsigned flags, struct page **pagep, |
| + void **fsdata) |
| +{ |
| + struct page *page; |
| + |
| + if (WARN_ON_ONCE(pos >= PAGE_CACHE_SIZE)) |
| + return -EIO; |
| + page = grab_cache_page_write_begin(mapping, 0, flags); |
| + if (!page) |
| + return -ENOMEM; |
| + *pagep = page; |
| + |
| + if (!PageUptodate(page) && len != PAGE_CACHE_SIZE) |
| + __udf_adinicb_readpage(page); |
| + return 0; |
| +} |
| + |
| static int udf_adinicb_write_end(struct file *file, |
| struct address_space *mapping, |
| loff_t pos, unsigned len, unsigned copied, |
| @@ -101,8 +124,8 @@ const struct address_space_operations udf_adinicb_aops = { |
| .readpage = udf_adinicb_readpage, |
| .writepage = udf_adinicb_writepage, |
| .sync_page = block_sync_page, |
| - .write_begin = simple_write_begin, |
| - .write_end = udf_adinicb_write_end, |
| + .write_begin = udf_adinicb_write_begin, |
| + .write_end = udf_adinicb_write_end, |
| }; |
| |
| static ssize_t udf_file_aio_write(struct kiocb *iocb, const struct iovec *iov, |
| -- |
| 1.8.5.2 |
| |