diff options
author | Kent Overstreet <kent.overstreet@gmail.com> | 2019-09-19 18:05:04 -0400 |
---|---|---|
committer | Kent Overstreet <kent.overstreet@gmail.com> | 2019-09-19 18:05:04 -0400 |
commit | f6c4443f514dd9b13e30e44418119aa04420b1ac (patch) | |
tree | 53c779539defd4823a0aaab039e338b6d55153b3 | |
parent | 009ad49a7cb5fabcd8a1efe761e5ef52683101f3 (diff) |
bcachefs: Don't write past eof
When converting from PAGE_SIZE to block_size, the .mkwrite path was
missed
Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
-rw-r--r-- | fs/bcachefs/fs-io.c | 32 |
1 files changed, 27 insertions, 5 deletions
diff --git a/fs/bcachefs/fs-io.c b/fs/bcachefs/fs-io.c index 1e26a458c9e1..24fe58f90e76 100644 --- a/fs/bcachefs/fs-io.c +++ b/fs/bcachefs/fs-io.c @@ -780,6 +780,8 @@ vm_fault_t bch2_page_mkwrite(struct vm_fault *vmf) struct address_space *mapping = inode->v.i_mapping; struct bch_fs *c = inode->v.i_sb->s_fs_info; struct bch2_page_reservation res; + unsigned len; + loff_t isize; int ret = VM_FAULT_LOCKED; bch2_page_reservation_init(c, inode, &res); @@ -797,21 +799,27 @@ vm_fault_t bch2_page_mkwrite(struct vm_fault *vmf) pagecache_add_get(&mapping->add_lock); lock_page(page); - if (page->mapping != mapping || - page_offset(page) > i_size_read(&inode->v)) { + isize = i_size_read(&inode->v); + + if (page->mapping != mapping || page_offset(page) >= isize) { unlock_page(page); ret = VM_FAULT_NOPAGE; goto out; } - if (bch2_page_reservation_get(c, inode, page, &res, - 0, PAGE_SIZE, true)) { + /* page is wholly or partially inside EOF */ + if (((page->index + 1) << PAGE_SHIFT) <= isize) + len = PAGE_SIZE; + else + len = offset_in_page(isize); + + if (bch2_page_reservation_get(c, inode, page, &res, 0, len, true)) { unlock_page(page); ret = VM_FAULT_SIGBUS; goto out; } - bch2_set_page_dirty(c, inode, page, &res, 0, PAGE_SIZE); + bch2_set_page_dirty(c, inode, page, &res, 0, len); wait_for_stable_page(page); out: if (current->pagecache_lock != &mapping->add_lock) @@ -1490,6 +1498,10 @@ do_io: BUG_ON(!bio_add_page(&w->io->op.op.wbio.bio, page, sectors << 9, offset << 9)); + /* Check for writing past i_size: */ + BUG_ON((bio_end_sector(&w->io->op.op.wbio.bio) << 9) > + round_up(i_size, block_bytes(c))); + w->io->op.op.res.sectors += reserved_sectors; w->io->op.new_i_size = i_size; @@ -2530,6 +2542,16 @@ int bch2_truncate(struct bch_inode_info *inode, struct iattr *iattr) if (unlikely(ret)) goto err; + /* + * When extending, we're going to write the new i_size to disk + * immediately so we need to flush anything above the current on disk + * i_size first: + * + * Also, when extending we need to flush the page that i_size currently + * straddles - if it's mapped to userspace, we need to ensure that + * userspace has to redirty it and call .mkwrite -> set_page_dirty + * again to allocate the part of the page that was extended. + */ if (iattr->ia_size > inode->ei_inode.bi_size) ret = filemap_write_and_wait_range(mapping, inode->ei_inode.bi_size, |