Skip to content

Commit

Permalink
udf: Fix data corruption for files in ICB
Browse files Browse the repository at this point in the history
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 be021ee (udf: convert to
new aops).

Fix the problem by providing a ->write_begin function which makes the page
properly uptodate.

CC: <[email protected]> # >= 2.6.24
Reported-by: Ian Abbott <[email protected]>
Signed-off-by: Jan Kara <[email protected]>
  • Loading branch information
jankara committed Sep 5, 2012
1 parent 156bddd commit 9c2fc0d
Showing 1 changed file with 29 additions and 6 deletions.
35 changes: 29 additions & 6 deletions fs/udf/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,20 +39,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;
Expand All @@ -77,6 +81,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,
Expand All @@ -98,8 +121,8 @@ static int udf_adinicb_write_end(struct file *file,
const struct address_space_operations udf_adinicb_aops = {
.readpage = udf_adinicb_readpage,
.writepage = udf_adinicb_writepage,
.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,
Expand Down

0 comments on commit 9c2fc0d

Please sign in to comment.