Skip to content

Commit

Permalink
bcachefs: fix initial page state after falloc
Browse files Browse the repository at this point in the history
On a new page reservation check if the backing extent was already
allocated before adding to the number of sectors we will allocate.
It is possible to write to a page that is beyond the inode i_size
that is also backed by an allocated extent. This can happen when
fallocate is used with FALLOC_FL_KEEP_SIZE.

Signed-off-by: Dan Robertson <[email protected]>
  • Loading branch information
dlrobertson committed Jun 9, 2021
1 parent 86ab563 commit 1778607
Showing 1 changed file with 30 additions and 7 deletions.
37 changes: 30 additions & 7 deletions fs/bcachefs/fs-io.c
Original file line number Diff line number Diff line change
Expand Up @@ -387,23 +387,45 @@ static void bch2_page_reservation_put(struct bch_fs *c,
static int bch2_page_reservation_get(struct bch_fs *c,
struct bch_inode_info *inode, struct page *page,
struct bch2_page_reservation *res,
unsigned offset, unsigned len, bool check_enospc)
loff_t raw_offset, unsigned len, bool check_enospc)
{
struct bch_page_state *s = bch2_page_state_create(page, 0);
unsigned i, disk_sectors = 0, quota_sectors = 0;
int ret;
struct btree_iter *iter;
struct btree_trans trans;
struct bkey_s_c k;
unsigned offset = raw_offset & (PAGE_SIZE - 1);

if (!s)
return -ENOMEM;

bch2_trans_init(&trans, c, BTREE_ITER_MAX, 0);
iter = bch2_trans_get_iter(&trans, BTREE_ID_extents,
POS(inode->v.i_ino, raw_offset >> 9),
BTREE_ITER_SLOTS|BTREE_ITER_INTENT);

for (i = round_down(offset, block_bytes(c)) >> 9;
i < round_up(offset + len, block_bytes(c)) >> 9;
i++) {
disk_sectors += sectors_to_reserve(&s->s[i],
res->disk.nr_replicas);
quota_sectors += s->s[i].state == SECTOR_UNALLOCATED;
k = bch2_btree_iter_peek_slot(iter);
ret = bkey_err(k);
if (ret) {
break;
} else if (bkey_extent_is_allocation(k.k)) {
s->s[i].state = SECTOR_ALLOCATED;
} else {
quota_sectors += s->s[i].state == SECTOR_UNALLOCATED;
disk_sectors += sectors_to_reserve(&s->s[i], res->disk.nr_replicas);
}
}

bch2_trans_iter_put(&trans, iter);
bch2_trans_exit(&trans);

if (ret)
return ret;

if (disk_sectors) {
ret = bch2_disk_reservation_add(c, &res->disk,
disk_sectors,
Expand Down Expand Up @@ -581,7 +603,8 @@ vm_fault_t bch2_page_mkwrite(struct vm_fault *vmf)

len = min_t(loff_t, PAGE_SIZE, isize - page_offset(page));

if (bch2_page_reservation_get(c, inode, page, &res, 0, len, true)) {
if (bch2_page_reservation_get(c, inode, page, &res,
page_offset(page), len, true)) {
unlock_page(page);
ret = VM_FAULT_SIGBUS;
goto out;
Expand Down Expand Up @@ -1338,7 +1361,7 @@ int bch2_write_begin(struct file *file, struct address_space *mapping,
goto err;
out:
ret = bch2_page_reservation_get(c, inode, page, res,
offset, len, true);
pos, len, true);
if (ret) {
if (!PageUptodate(page)) {
/*
Expand Down Expand Up @@ -1472,7 +1495,7 @@ static int __bch2_buffered_write(struct bch_inode_info *inode,
PAGE_SIZE - pg_offset);
retry_reservation:
ret = bch2_page_reservation_get(c, inode, page, &res,
pg_offset, pg_len, true);
pos + reserved, pg_len, true);

if (ret && !PageUptodate(page)) {
ret = bch2_read_single_page(page, mapping);
Expand Down

0 comments on commit 1778607

Please sign in to comment.