Skip to content

Commit 9ddc959

Browse files
josefbacikkdave
authored andcommitted
btrfs: use the file extent tree infrastructure
We want to use this everywhere we modify the file extent items permanently. These include: 1) Inserting new file extents for writes and prealloc extents. 2) Truncating inode items. 3) btrfs_cont_expand(). 4) Insert inline extents. 5) Insert new extents from log replay. 6) Insert a new extent for clone, as it could be past i_size. 7) Hole punching For hole punching in particular it might seem it's not necessary because anybody extending would use btrfs_cont_expand, however there is a corner that still can give us trouble. Start with an empty file and fallocate KEEP_SIZE 1M-2M We now have a 0 length file, and a hole file extent from 0-1M, and a prealloc extent from 1M-2M. Now punch 1M-1.5M Because this is past i_size we have [HOLE EXTENT][ NOTHING ][PREALLOC] [0 1M][1M 1.5M][1.5M 2M] with an i_size of 0. Now if we pwrite 0-1.5M we'll increas our i_size to 1.5M, but our disk_i_size is still 0 until the ordered extent completes. However if we now immediately truncate 2M on the file we'll just call btrfs_cont_expand(inode, 1.5M, 2M), since our old i_size is 1.5M. If we commit the transaction here and crash we'll expose the gap. To fix this we need to clear the file extent mapping for the range that we punched but didn't insert a corresponding file extent for. This will mean the truncate will only get an disk_i_size set to 1M if we crash before the finish ordered io happens. I've written an xfstest to reproduce the problem and validate this fix. Reviewed-by: Filipe Manana <[email protected]> Signed-off-by: Josef Bacik <[email protected]> Signed-off-by: David Sterba <[email protected]>
1 parent 41a2ee7 commit 9ddc959

File tree

4 files changed

+94
-1
lines changed

4 files changed

+94
-1
lines changed

fs/btrfs/delayed-inode.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1760,6 +1760,7 @@ static void fill_stack_inode_item(struct btrfs_trans_handle *trans,
17601760

17611761
int btrfs_fill_inode(struct inode *inode, u32 *rdev)
17621762
{
1763+
struct btrfs_fs_info *fs_info = BTRFS_I(inode)->root->fs_info;
17631764
struct btrfs_delayed_node *delayed_node;
17641765
struct btrfs_inode_item *inode_item;
17651766

@@ -1779,6 +1780,8 @@ int btrfs_fill_inode(struct inode *inode, u32 *rdev)
17791780
i_uid_write(inode, btrfs_stack_inode_uid(inode_item));
17801781
i_gid_write(inode, btrfs_stack_inode_gid(inode_item));
17811782
btrfs_i_size_write(BTRFS_I(inode), btrfs_stack_inode_size(inode_item));
1783+
btrfs_inode_set_file_extent_range(BTRFS_I(inode), 0,
1784+
round_up(i_size_read(inode), fs_info->sectorsize));
17821785
inode->i_mode = btrfs_stack_inode_mode(inode_item);
17831786
set_nlink(inode, btrfs_stack_inode_nlink(inode_item));
17841787
inode_set_bytes(inode, btrfs_stack_inode_nbytes(inode_item));

fs/btrfs/file.c

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2486,6 +2486,11 @@ static int btrfs_insert_clone_extent(struct btrfs_trans_handle *trans,
24862486
btrfs_mark_buffer_dirty(leaf);
24872487
btrfs_release_path(path);
24882488

2489+
ret = btrfs_inode_set_file_extent_range(BTRFS_I(inode),
2490+
clone_info->file_offset, clone_len);
2491+
if (ret)
2492+
return ret;
2493+
24892494
/* If it's a hole, nothing more needs to be done. */
24902495
if (clone_info->disk_offset == 0)
24912496
return 0;
@@ -2596,6 +2601,24 @@ int btrfs_punch_hole_range(struct inode *inode, struct btrfs_path *path,
25962601
btrfs_abort_transaction(trans, ret);
25972602
break;
25982603
}
2604+
} else if (!clone_info && cur_offset < drop_end) {
2605+
/*
2606+
* We are past the i_size here, but since we didn't
2607+
* insert holes we need to clear the mapped area so we
2608+
* know to not set disk_i_size in this area until a new
2609+
* file extent is inserted here.
2610+
*/
2611+
ret = btrfs_inode_clear_file_extent_range(BTRFS_I(inode),
2612+
cur_offset, drop_end - cur_offset);
2613+
if (ret) {
2614+
/*
2615+
* We couldn't clear our area, so we could
2616+
* presumably adjust up and corrupt the fs, so
2617+
* we need to abort.
2618+
*/
2619+
btrfs_abort_transaction(trans, ret);
2620+
break;
2621+
}
25992622
}
26002623

26012624
if (clone_info && drop_end > clone_info->file_offset) {
@@ -2686,6 +2709,15 @@ int btrfs_punch_hole_range(struct inode *inode, struct btrfs_path *path,
26862709
btrfs_abort_transaction(trans, ret);
26872710
goto out_trans;
26882711
}
2712+
} else if (!clone_info && cur_offset < drop_end) {
2713+
/* See the comment in the loop above for the reasoning here. */
2714+
ret = btrfs_inode_clear_file_extent_range(BTRFS_I(inode),
2715+
cur_offset, drop_end - cur_offset);
2716+
if (ret) {
2717+
btrfs_abort_transaction(trans, ret);
2718+
goto out_trans;
2719+
}
2720+
26892721
}
26902722
if (clone_info) {
26912723
ret = btrfs_insert_clone_extent(trans, inode, path, clone_info,

fs/btrfs/inode.c

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,15 @@ static int insert_inline_extent(struct btrfs_trans_handle *trans,
241241
btrfs_mark_buffer_dirty(leaf);
242242
btrfs_release_path(path);
243243

244+
/*
245+
* We align size to sectorsize for inline extents just for simplicity
246+
* sake.
247+
*/
248+
size = ALIGN(size, root->fs_info->sectorsize);
249+
ret = btrfs_inode_set_file_extent_range(BTRFS_I(inode), start, size);
250+
if (ret)
251+
goto fail;
252+
244253
/*
245254
* we're an inline extent, so nobody can
246255
* extend the file past i_size without locking
@@ -2446,6 +2455,11 @@ static int insert_reserved_file_extent(struct btrfs_trans_handle *trans,
24462455
ins.offset = disk_num_bytes;
24472456
ins.type = BTRFS_EXTENT_ITEM_KEY;
24482457

2458+
ret = btrfs_inode_set_file_extent_range(BTRFS_I(inode), file_pos,
2459+
ram_bytes);
2460+
if (ret)
2461+
goto out;
2462+
24492463
/*
24502464
* Release the reserved range from inode dirty range map, as it is
24512465
* already moved into delayed_ref_head
@@ -4160,6 +4174,8 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans,
41604174
}
41614175

41624176
while (1) {
4177+
u64 clear_start = 0, clear_len = 0;
4178+
41634179
fi = NULL;
41644180
leaf = path->nodes[0];
41654181
btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
@@ -4210,13 +4226,16 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans,
42104226

42114227
if (extent_type != BTRFS_FILE_EXTENT_INLINE) {
42124228
u64 num_dec;
4229+
4230+
clear_start = found_key.offset;
42134231
extent_start = btrfs_file_extent_disk_bytenr(leaf, fi);
42144232
if (!del_item) {
42154233
u64 orig_num_bytes =
42164234
btrfs_file_extent_num_bytes(leaf, fi);
42174235
extent_num_bytes = ALIGN(new_size -
42184236
found_key.offset,
42194237
fs_info->sectorsize);
4238+
clear_start = ALIGN(new_size, fs_info->sectorsize);
42204239
btrfs_set_file_extent_num_bytes(leaf, fi,
42214240
extent_num_bytes);
42224241
num_dec = (orig_num_bytes -
@@ -4242,6 +4261,7 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans,
42424261
inode_sub_bytes(inode, num_dec);
42434262
}
42444263
}
4264+
clear_len = num_dec;
42454265
} else if (extent_type == BTRFS_FILE_EXTENT_INLINE) {
42464266
/*
42474267
* we can't truncate inline items that have had
@@ -4263,12 +4283,33 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans,
42634283
*/
42644284
ret = NEED_TRUNCATE_BLOCK;
42654285
break;
4286+
} else {
4287+
/*
4288+
* Inline extents are special, we just treat
4289+
* them as a full sector worth in the file
4290+
* extent tree just for simplicity sake.
4291+
*/
4292+
clear_len = fs_info->sectorsize;
42664293
}
42674294

42684295
if (test_bit(BTRFS_ROOT_REF_COWS, &root->state))
42694296
inode_sub_bytes(inode, item_end + 1 - new_size);
42704297
}
42714298
delete:
4299+
/*
4300+
* We use btrfs_truncate_inode_items() to clean up log trees for
4301+
* multiple fsyncs, and in this case we don't want to clear the
4302+
* file extent range because it's just the log.
4303+
*/
4304+
if (root == BTRFS_I(inode)->root) {
4305+
ret = btrfs_inode_clear_file_extent_range(BTRFS_I(inode),
4306+
clear_start, clear_len);
4307+
if (ret) {
4308+
btrfs_abort_transaction(trans, ret);
4309+
break;
4310+
}
4311+
}
4312+
42724313
if (del_item)
42734314
last_size = found_key.offset;
42744315
else
@@ -4591,14 +4632,21 @@ int btrfs_cont_expand(struct inode *inode, loff_t oldsize, loff_t size)
45914632
}
45924633
last_byte = min(extent_map_end(em), block_end);
45934634
last_byte = ALIGN(last_byte, fs_info->sectorsize);
4635+
hole_size = last_byte - cur_offset;
4636+
45944637
if (!test_bit(EXTENT_FLAG_PREALLOC, &em->flags)) {
45954638
struct extent_map *hole_em;
4596-
hole_size = last_byte - cur_offset;
45974639

45984640
err = maybe_insert_hole(root, inode, cur_offset,
45994641
hole_size);
46004642
if (err)
46014643
break;
4644+
4645+
err = btrfs_inode_set_file_extent_range(BTRFS_I(inode),
4646+
cur_offset, hole_size);
4647+
if (err)
4648+
break;
4649+
46024650
btrfs_drop_extent_cache(BTRFS_I(inode), cur_offset,
46034651
cur_offset + hole_size - 1, 0);
46044652
hole_em = alloc_extent_map();
@@ -4630,6 +4678,11 @@ int btrfs_cont_expand(struct inode *inode, loff_t oldsize, loff_t size)
46304678
hole_size - 1, 0);
46314679
}
46324680
free_extent_map(hole_em);
4681+
} else {
4682+
err = btrfs_inode_set_file_extent_range(BTRFS_I(inode),
4683+
cur_offset, hole_size);
4684+
if (err)
4685+
break;
46334686
}
46344687
next:
46354688
free_extent_map(em);

fs/btrfs/tree-log.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -830,6 +830,11 @@ static noinline int replay_one_extent(struct btrfs_trans_handle *trans,
830830
goto out;
831831
}
832832

833+
ret = btrfs_inode_set_file_extent_range(BTRFS_I(inode), start,
834+
extent_end - start);
835+
if (ret)
836+
goto out;
837+
833838
inode_add_bytes(inode, nbytes);
834839
update_inode:
835840
ret = btrfs_update_inode(trans, root, inode);

0 commit comments

Comments
 (0)