diff options
author | Jan Kara <jack@suse.cz> | 2014-10-01 21:49:18 -0400 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2014-10-01 21:49:18 -0400 |
commit | 90a8020278c1598fafd071736a0846b38510309c (patch) | |
tree | 2ab461b549a2b5f6b933895b1e61eb98627bba94 /include/linux/mm.h | |
parent | f6e63f90809946d410c42045577cb159fedabf8c (diff) |
vfs: fix data corruption when blocksize < pagesize for mmaped data
->page_mkwrite() is used by filesystems to allocate blocks under a page
which is becoming writeably mmapped in some process' address space. This
allows a filesystem to return a page fault if there is not enough space
available, user exceeds quota or similar problem happens, rather than
silently discarding data later when writepage is called.
However VFS fails to call ->page_mkwrite() in all the cases where
filesystems need it when blocksize < pagesize. For example when
blocksize = 1024, pagesize = 4096 the following is problematic:
ftruncate(fd, 0);
pwrite(fd, buf, 1024, 0);
map = mmap(NULL, 1024, PROT_WRITE, MAP_SHARED, fd, 0);
map[0] = 'a'; ----> page_mkwrite() for index 0 is called
ftruncate(fd, 10000); /* or even pwrite(fd, buf, 1, 10000) */
mremap(map, 1024, 10000, 0);
map[4095] = 'a'; ----> no page_mkwrite() called
At the moment ->page_mkwrite() is called, filesystem can allocate only
one block for the page because i_size == 1024. Otherwise it would create
blocks beyond i_size which is generally undesirable. But later at
->writepage() time, we also need to store data at offset 4095 but we
don't have block allocated for it.
This patch introduces a helper function filesystems can use to have
->page_mkwrite() called at all the necessary moments.
Signed-off-by: Jan Kara <jack@suse.cz>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Cc: stable@vger.kernel.org
Diffstat (limited to 'include/linux/mm.h')
-rw-r--r-- | include/linux/mm.h | 1 |
1 files changed, 1 insertions, 0 deletions
diff --git a/include/linux/mm.h b/include/linux/mm.h index 8981cc882ed2..5005464fe012 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h | |||
@@ -1155,6 +1155,7 @@ static inline void unmap_shared_mapping_range(struct address_space *mapping, | |||
1155 | 1155 | ||
1156 | extern void truncate_pagecache(struct inode *inode, loff_t new); | 1156 | extern void truncate_pagecache(struct inode *inode, loff_t new); |
1157 | extern void truncate_setsize(struct inode *inode, loff_t newsize); | 1157 | extern void truncate_setsize(struct inode *inode, loff_t newsize); |
1158 | void pagecache_isize_extended(struct inode *inode, loff_t from, loff_t to); | ||
1158 | void truncate_pagecache_range(struct inode *inode, loff_t offset, loff_t end); | 1159 | void truncate_pagecache_range(struct inode *inode, loff_t offset, loff_t end); |
1159 | int truncate_inode_page(struct address_space *mapping, struct page *page); | 1160 | int truncate_inode_page(struct address_space *mapping, struct page *page); |
1160 | int generic_error_remove_page(struct address_space *mapping, struct page *page); | 1161 | int generic_error_remove_page(struct address_space *mapping, struct page *page); |