From 6cc3535dcbb8bca4df496b9beac8f4c664958fce Mon Sep 17 00:00:00 2001 From: Kent Overstreet Date: Thu, 19 Sep 2019 18:05:04 -0400 Subject: [PATCH] bcachefs: Don't write past eof When converting from PAGE_SIZE to block_size, the .mkwrite path was missed Signed-off-by: Kent Overstreet --- fs/bcachefs/fs-io.c | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/fs/bcachefs/fs-io.c b/fs/bcachefs/fs-io.c index 4a016c19dcbd1..f05950da69577 100644 --- a/fs/bcachefs/fs-io.c +++ b/fs/bcachefs/fs-io.c @@ -801,6 +801,8 @@ vm_fault_t bch2_page_mkwrite(struct vm_fault *vmf) struct address_space *mapping = file->f_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); @@ -817,21 +819,27 @@ vm_fault_t bch2_page_mkwrite(struct vm_fault *vmf) bch2_pagecache_add_get(&inode->ei_pagecache_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: bch2_pagecache_add_put(&inode->ei_pagecache_lock); @@ -1433,6 +1441,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; @@ -2518,6 +2530,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, -- 2.30.2