Skip to content

Commit f396c03

Browse files
maharmstoneadam900710
authored andcommitted
btrfs-progs: add rudimentary log checking
Currently the transaction log is more or less ignored by btrfs check, meaning that it's possible for a FS with a corrupt log to pass btrfs check, but be immediately corrupted by the kernel when it's mounted. This patch adds a check that if there's an inode in the log, any pending non-inlined csumed writes also have corresponding csum entries. Signed-off-by: Mark Harmstone <[email protected]> [ Small commit message update. ] Signed-off-by: Qu Wenruo <[email protected]>
1 parent 261f25b commit f396c03

File tree

3 files changed

+298
-12
lines changed

3 files changed

+298
-12
lines changed

check/main.c

Lines changed: 284 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9670,6 +9670,266 @@ static int zero_log_tree(struct btrfs_root *root)
96709670
return ret;
96719671
}
96729672

9673+
static int check_log_csum(struct btrfs_root *root, u64 addr, u64 length)
9674+
{
9675+
struct btrfs_path path = { 0 };
9676+
struct btrfs_key key;
9677+
struct extent_buffer *leaf;
9678+
u16 csum_size = gfs_info->csum_size;
9679+
u16 num_entries;
9680+
u64 data_len;
9681+
int ret;
9682+
9683+
key.objectid = BTRFS_EXTENT_CSUM_OBJECTID;
9684+
key.type = BTRFS_EXTENT_CSUM_KEY;
9685+
key.offset = addr;
9686+
9687+
ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
9688+
if (ret < 0)
9689+
return ret;
9690+
9691+
if (ret > 0 && path.slots[0])
9692+
path.slots[0]--;
9693+
9694+
ret = 0;
9695+
9696+
while (1) {
9697+
leaf = path.nodes[0];
9698+
if (path.slots[0] >= btrfs_header_nritems(leaf)) {
9699+
ret = btrfs_next_leaf(root, &path);
9700+
if (ret) {
9701+
if (ret > 0)
9702+
ret = 0;
9703+
9704+
break;
9705+
}
9706+
leaf = path.nodes[0];
9707+
}
9708+
9709+
btrfs_item_key_to_cpu(leaf, &key, path.slots[0]);
9710+
9711+
if (key.objectid > BTRFS_EXTENT_CSUM_OBJECTID)
9712+
break;
9713+
9714+
if (key.objectid != BTRFS_EXTENT_CSUM_OBJECTID ||
9715+
key.type != BTRFS_EXTENT_CSUM_KEY)
9716+
goto next;
9717+
9718+
if (key.offset >= addr + length)
9719+
break;
9720+
9721+
num_entries = btrfs_item_size(leaf, path.slots[0]) / csum_size;
9722+
data_len = num_entries * gfs_info->sectorsize;
9723+
9724+
if (addr >= key.offset && addr <= key.offset + data_len) {
9725+
u64 end = min(addr + length, key.offset + data_len);
9726+
9727+
length = addr + length - end;
9728+
addr = end;
9729+
9730+
if (length == 0)
9731+
break;
9732+
}
9733+
9734+
next:
9735+
path.slots[0]++;
9736+
}
9737+
9738+
btrfs_release_path(&path);
9739+
9740+
if (ret >= 0)
9741+
ret = length == 0 ? 0 : 1;
9742+
9743+
return ret;
9744+
}
9745+
9746+
static int check_log_root(struct btrfs_root *root, struct cache_tree *root_cache)
9747+
{
9748+
struct btrfs_path path = { 0 };
9749+
struct btrfs_key key;
9750+
struct extent_buffer *leaf;
9751+
int ret, err = 0;
9752+
u64 last_csum_inode = 0;
9753+
9754+
key.objectid = BTRFS_FIRST_FREE_OBJECTID;
9755+
key.type = BTRFS_INODE_ITEM_KEY;
9756+
key.offset = 0;
9757+
ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
9758+
if (ret < 0)
9759+
return 1;
9760+
9761+
while (1) {
9762+
leaf = path.nodes[0];
9763+
if (path.slots[0] >= btrfs_header_nritems(leaf)) {
9764+
ret = btrfs_next_leaf(root, &path);
9765+
if (ret) {
9766+
if (ret < 0)
9767+
err = 1;
9768+
9769+
break;
9770+
}
9771+
leaf = path.nodes[0];
9772+
}
9773+
btrfs_item_key_to_cpu(leaf, &key, path.slots[0]);
9774+
9775+
if (key.objectid == BTRFS_EXTENT_CSUM_OBJECTID)
9776+
break;
9777+
9778+
if (key.type == BTRFS_INODE_ITEM_KEY) {
9779+
struct btrfs_inode_item *item;
9780+
9781+
item = btrfs_item_ptr(leaf, path.slots[0],
9782+
struct btrfs_inode_item);
9783+
9784+
if (!(btrfs_inode_flags(leaf, item) & BTRFS_INODE_NODATASUM))
9785+
last_csum_inode = key.objectid;
9786+
} else if (key.type == BTRFS_EXTENT_DATA_KEY &&
9787+
key.objectid == last_csum_inode) {
9788+
struct btrfs_file_extent_item *fi;
9789+
u64 addr, length;
9790+
9791+
fi = btrfs_item_ptr(leaf, path.slots[0],
9792+
struct btrfs_file_extent_item);
9793+
9794+
if (btrfs_file_extent_type(leaf, fi) != BTRFS_FILE_EXTENT_REG)
9795+
goto next;
9796+
9797+
addr = btrfs_file_extent_disk_bytenr(leaf, fi) +
9798+
btrfs_file_extent_offset(leaf, fi);
9799+
length = btrfs_file_extent_num_bytes(leaf, fi);
9800+
9801+
ret = check_log_csum(root, addr, length);
9802+
if (ret < 0) {
9803+
err = 1;
9804+
break;
9805+
}
9806+
9807+
if (ret) {
9808+
error("csum missing in log (root %llu, inode %llu, "
9809+
"offset %llu, address 0x%llx, length %llu)",
9810+
root->objectid, last_csum_inode, key.offset,
9811+
addr, length);
9812+
err = 1;
9813+
}
9814+
}
9815+
9816+
next:
9817+
path.slots[0]++;
9818+
}
9819+
9820+
btrfs_release_path(&path);
9821+
9822+
return err;
9823+
}
9824+
9825+
static int load_log_root(u64 root_id, struct btrfs_path *path,
9826+
struct btrfs_root *tmp_root)
9827+
{
9828+
struct extent_buffer *l;
9829+
struct btrfs_tree_parent_check check = { 0 };
9830+
int ret;
9831+
9832+
btrfs_setup_root(tmp_root, gfs_info, root_id);
9833+
9834+
l = path->nodes[0];
9835+
read_extent_buffer(l, &tmp_root->root_item,
9836+
btrfs_item_ptr_offset(l, path->slots[0]),
9837+
sizeof(tmp_root->root_item));
9838+
9839+
tmp_root->root_key.objectid = root_id;
9840+
tmp_root->root_key.type = BTRFS_ROOT_ITEM_KEY;
9841+
tmp_root->root_key.offset = 0;
9842+
9843+
check.owner_root = btrfs_root_id(tmp_root);
9844+
check.transid = btrfs_root_generation(&tmp_root->root_item);
9845+
check.level = btrfs_root_level(&tmp_root->root_item);
9846+
9847+
tmp_root->node = read_tree_block(gfs_info,
9848+
btrfs_root_bytenr(&tmp_root->root_item),
9849+
&check);
9850+
if (IS_ERR(tmp_root->node)) {
9851+
ret = PTR_ERR(tmp_root->node);
9852+
tmp_root->node = NULL;
9853+
return ret;
9854+
}
9855+
9856+
if (btrfs_header_level(tmp_root->node) != btrfs_root_level(&tmp_root->root_item)) {
9857+
error("root [%llu %llu] level %d does not match %d",
9858+
tmp_root->root_key.objectid,
9859+
tmp_root->root_key.offset,
9860+
btrfs_header_level(tmp_root->node),
9861+
btrfs_root_level(&tmp_root->root_item));
9862+
return -EIO;
9863+
}
9864+
9865+
return 0;
9866+
}
9867+
9868+
static int check_log(struct cache_tree *root_cache)
9869+
{
9870+
struct btrfs_path path = { 0 };
9871+
struct btrfs_key key;
9872+
struct extent_buffer *leaf;
9873+
struct btrfs_root *log_root = gfs_info->log_root_tree;
9874+
int ret;
9875+
int err = 0;
9876+
9877+
key.objectid = BTRFS_TREE_LOG_OBJECTID;
9878+
key.type = BTRFS_ROOT_ITEM_KEY;
9879+
key.offset = 0;
9880+
ret = btrfs_search_slot(NULL, log_root, &key, &path, 0, 0);
9881+
if (ret < 0) {
9882+
err = 1;
9883+
goto out;
9884+
}
9885+
9886+
while (1) {
9887+
leaf = path.nodes[0];
9888+
if (path.slots[0] >= btrfs_header_nritems(leaf)) {
9889+
ret = btrfs_next_leaf(log_root, &path);
9890+
if (ret) {
9891+
if (ret < 0)
9892+
err = 1;
9893+
break;
9894+
}
9895+
leaf = path.nodes[0];
9896+
}
9897+
btrfs_item_key_to_cpu(leaf, &key, path.slots[0]);
9898+
9899+
if (key.objectid > BTRFS_TREE_LOG_OBJECTID ||
9900+
key.type > BTRFS_ROOT_ITEM_KEY)
9901+
break;
9902+
9903+
if (key.objectid == BTRFS_TREE_LOG_OBJECTID &&
9904+
key.type == BTRFS_ROOT_ITEM_KEY &&
9905+
fs_root_objectid(key.offset)) {
9906+
struct btrfs_root tmp_root;
9907+
9908+
memset(&tmp_root, 0, sizeof(tmp_root));
9909+
9910+
ret = load_log_root(key.offset, &path, &tmp_root);
9911+
if (ret) {
9912+
err = 1;
9913+
goto next;
9914+
}
9915+
9916+
ret = check_log_root(&tmp_root, root_cache);
9917+
if (ret)
9918+
err = 1;
9919+
9920+
next:
9921+
if (tmp_root.node)
9922+
free_extent_buffer(tmp_root.node);
9923+
}
9924+
9925+
path.slots[0]++;
9926+
}
9927+
out:
9928+
btrfs_release_path(&path);
9929+
9930+
return err;
9931+
}
9932+
96739933
static void free_roots_info_cache(void)
96749934
{
96759935
if (!roots_info_cache)
@@ -10468,9 +10728,21 @@ static int cmd_check(const struct cmd_struct *cmd, int argc, char **argv)
1046810728
goto close_out;
1046910729
}
1047010730

10731+
if (gfs_info->log_root_tree) {
10732+
fprintf(stderr, "[1/8] checking log\n");
10733+
ret = check_log(&root_cache);
10734+
10735+
if (ret)
10736+
error("errors found in log");
10737+
err |= !!ret;
10738+
} else {
10739+
fprintf(stderr,
10740+
"[1/8] checking log skipped (none written)\n");
10741+
}
10742+
1047110743
if (!init_extent_tree) {
1047210744
if (!g_task_ctx.progress_enabled) {
10473-
fprintf(stderr, "[1/7] checking root items\n");
10745+
fprintf(stderr, "[2/8] checking root items\n");
1047410746
} else {
1047510747
g_task_ctx.tp = TASK_ROOT_ITEMS;
1047610748
task_start(g_task_ctx.info, &g_task_ctx.start_time,
@@ -10505,11 +10777,11 @@ static int cmd_check(const struct cmd_struct *cmd, int argc, char **argv)
1050510777
}
1050610778
}
1050710779
} else {
10508-
fprintf(stderr, "[1/7] checking root items... skipped\n");
10780+
fprintf(stderr, "[2/8] checking root items... skipped\n");
1050910781
}
1051010782

1051110783
if (!g_task_ctx.progress_enabled) {
10512-
fprintf(stderr, "[2/7] checking extents\n");
10784+
fprintf(stderr, "[3/8] checking extents\n");
1051310785
} else {
1051410786
g_task_ctx.tp = TASK_EXTENTS;
1051510787
task_start(g_task_ctx.info, &g_task_ctx.start_time, &g_task_ctx.item_count);
@@ -10527,9 +10799,9 @@ static int cmd_check(const struct cmd_struct *cmd, int argc, char **argv)
1052710799

1052810800
if (!g_task_ctx.progress_enabled) {
1052910801
if (is_free_space_tree)
10530-
fprintf(stderr, "[3/7] checking free space tree\n");
10802+
fprintf(stderr, "[4/8] checking free space tree\n");
1053110803
else
10532-
fprintf(stderr, "[3/7] checking free space cache\n");
10804+
fprintf(stderr, "[4/8] checking free space cache\n");
1053310805
} else {
1053410806
g_task_ctx.tp = TASK_FREE_SPACE;
1053510807
task_start(g_task_ctx.info, &g_task_ctx.start_time, &g_task_ctx.item_count);
@@ -10547,7 +10819,7 @@ static int cmd_check(const struct cmd_struct *cmd, int argc, char **argv)
1054710819
*/
1054810820
no_holes = btrfs_fs_incompat(gfs_info, NO_HOLES);
1054910821
if (!g_task_ctx.progress_enabled) {
10550-
fprintf(stderr, "[4/7] checking fs roots\n");
10822+
fprintf(stderr, "[5/8] checking fs roots\n");
1055110823
} else {
1055210824
g_task_ctx.tp = TASK_FS_ROOTS;
1055310825
task_start(g_task_ctx.info, &g_task_ctx.start_time, &g_task_ctx.item_count);
@@ -10563,10 +10835,10 @@ static int cmd_check(const struct cmd_struct *cmd, int argc, char **argv)
1056310835

1056410836
if (!g_task_ctx.progress_enabled) {
1056510837
if (check_data_csum)
10566-
fprintf(stderr, "[5/7] checking csums against data\n");
10838+
fprintf(stderr, "[6/8] checking csums against data\n");
1056710839
else
1056810840
fprintf(stderr,
10569-
"[5/7] checking only csums items (without verifying data)\n");
10841+
"[6/8] checking only csums items (without verifying data)\n");
1057010842
} else {
1057110843
g_task_ctx.tp = TASK_CSUMS;
1057210844
task_start(g_task_ctx.info, &g_task_ctx.start_time, &g_task_ctx.item_count);
@@ -10585,7 +10857,7 @@ static int cmd_check(const struct cmd_struct *cmd, int argc, char **argv)
1058510857
/* For low memory mode, check_fs_roots_v2 handles root refs */
1058610858
if (check_mode != CHECK_MODE_LOWMEM) {
1058710859
if (!g_task_ctx.progress_enabled) {
10588-
fprintf(stderr, "[6/7] checking root refs\n");
10860+
fprintf(stderr, "[7/8] checking root refs\n");
1058910861
} else {
1059010862
g_task_ctx.tp = TASK_ROOT_REFS;
1059110863
task_start(g_task_ctx.info, &g_task_ctx.start_time, &g_task_ctx.item_count);
@@ -10600,7 +10872,7 @@ static int cmd_check(const struct cmd_struct *cmd, int argc, char **argv)
1060010872
}
1060110873
} else {
1060210874
fprintf(stderr,
10603-
"[6/7] checking root refs done with fs roots in lowmem mode, skipping\n");
10875+
"[7/8] checking root refs done with fs roots in lowmem mode, skipping\n");
1060410876
}
1060510877

1060610878
while (opt_check_repair && !list_empty(&gfs_info->recow_ebs)) {
@@ -10632,7 +10904,7 @@ static int cmd_check(const struct cmd_struct *cmd, int argc, char **argv)
1063210904

1063310905
if (gfs_info->quota_enabled) {
1063410906
if (!g_task_ctx.progress_enabled) {
10635-
fprintf(stderr, "[7/7] checking quota groups\n");
10907+
fprintf(stderr, "[8/8] checking quota groups\n");
1063610908
} else {
1063710909
g_task_ctx.tp = TASK_QGROUPS;
1063810910
task_start(g_task_ctx.info, &g_task_ctx.start_time, &g_task_ctx.item_count);
@@ -10655,7 +10927,7 @@ static int cmd_check(const struct cmd_struct *cmd, int argc, char **argv)
1065510927
ret = 0;
1065610928
} else {
1065710929
fprintf(stderr,
10658-
"[7/7] checking quota groups skipped (not enabled on this FS)\n");
10930+
"[8/8] checking quota groups skipped (not enabled on this FS)\n");
1065910931
}
1066010932

1066110933
if (!list_empty(&gfs_info->recow_ebs)) {
Binary file not shown.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#!/bin/bash
2+
#
3+
# Verify that check can detect missing log csum items.
4+
5+
source "$TEST_TOP/common" || exit
6+
7+
check_prereq btrfs
8+
9+
check_image() {
10+
run_mustfail "missing log csum items not detected" \
11+
"$TOP/btrfs" check "$1"
12+
}
13+
14+
check_all_images

0 commit comments

Comments
 (0)