forked from Minki/linux
udf: Fix data corruption for files in ICB
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.
CC: <stable@vger.kernel.org> # >= 2.6.24
Reported-by: Ian Abbott <abbotti@mev.co.uk>
Signed-off-by: Jan Kara <jack@suse.cz>
This commit is contained in:
parent
156bddd8e5
commit
9c2fc0de1a
@ -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;
|
||||
@ -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,
|
||||
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user