Skip to content

Commit

Permalink
btrfs-progs: sync tree-checker.[ch] from kernel
Browse files Browse the repository at this point in the history
This syncs tree-checker.c from the kernel.  The main modification was to
add a open ctree flag to skip the deeper leaf checks, and plumbing this
through tree-checker.c.  We need this for things like fsck or
btrfs-image that need to work with slightly corrupted file systems, and
these checks simply make us unable to look at the corrupted blocks.

Signed-off-by: Josef Bacik <[email protected]>
Signed-off-by: David Sterba <[email protected]>
  • Loading branch information
josefbacik authored and kdave committed May 10, 2023
1 parent ad5c224 commit c8593f6
Show file tree
Hide file tree
Showing 16 changed files with 2,186 additions and 304 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ objects = \
kernel-shared/print-tree.o \
kernel-shared/root-tree.o \
kernel-shared/transaction.o \
kernel-shared/tree-checker.o \
kernel-shared/ulist.o \
kernel-shared/uuid-tree.o \
kernel-shared/volumes.o \
Expand Down
4 changes: 3 additions & 1 deletion check/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
#include "kernel-shared/backref.h"
#include "kernel-shared/ulist.h"
#include "kernel-shared/file-item.h"
#include "kernel-shared/tree-checker.h"
#include "common/defs.h"
#include "common/extent-cache.h"
#include "common/internal.h"
Expand Down Expand Up @@ -9999,7 +10000,8 @@ static int cmd_check(const struct cmd_struct *cmd, int argc, char **argv)
int qgroups_repaired = 0;
int qgroup_verify_ret;
unsigned ctree_flags = OPEN_CTREE_EXCLUSIVE |
OPEN_CTREE_ALLOW_TRANSID_MISMATCH;
OPEN_CTREE_ALLOW_TRANSID_MISMATCH |
OPEN_CTREE_SKIP_LEAF_ITEM_CHECKS;
int force = 0;

while(1) {
Expand Down
1 change: 1 addition & 0 deletions check/mode-lowmem.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include "kernel-shared/compression.h"
#include "kernel-shared/volumes.h"
#include "kernel-shared/file-item.h"
#include "kernel-shared/tree-checker.h"
#include "common/messages.h"
#include "common/internal.h"
#include "common/utils.h"
Expand Down
1 change: 1 addition & 0 deletions check/repair.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include "kernel-shared/transaction.h"
#include "kernel-shared/extent_io.h"
#include "kernel-shared/disk-io.h"
#include "kernel-shared/tree-checker.h"
#include "common/extent-cache.h"
#include "check/repair.h"

Expand Down
14 changes: 9 additions & 5 deletions image/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -1025,7 +1025,8 @@ static int create_metadump(const char *input, FILE *out, int num_threads,
int ret;
int err = 0;

root = open_ctree(input, 0, OPEN_CTREE_ALLOW_TRANSID_MISMATCH);
root = open_ctree(input, 0, OPEN_CTREE_ALLOW_TRANSID_MISMATCH |
OPEN_CTREE_SKIP_LEAF_ITEM_CHECKS);
if (!root) {
error("open ctree failed");
return -EIO;
Expand Down Expand Up @@ -2798,7 +2799,7 @@ static int restore_metadump(const char *input, FILE *out, int old_restore,

ocf.filename = target;
ocf.flags = OPEN_CTREE_WRITES | OPEN_CTREE_RESTORE |
OPEN_CTREE_PARTIAL;
OPEN_CTREE_PARTIAL | OPEN_CTREE_SKIP_LEAF_ITEM_CHECKS;
info = open_ctree_fs_info(&ocf);
if (!info) {
error("open ctree failed");
Expand Down Expand Up @@ -2864,7 +2865,8 @@ static int restore_metadump(const char *input, FILE *out, int old_restore,
OPEN_CTREE_PARTIAL |
OPEN_CTREE_WRITES |
OPEN_CTREE_NO_DEVICES |
OPEN_CTREE_ALLOW_TRANSID_MISMATCH);
OPEN_CTREE_ALLOW_TRANSID_MISMATCH |
OPEN_CTREE_SKIP_LEAF_ITEM_CHECKS);
if (!root) {
error("open ctree failed in %s", target);
ret = -EIO;
Expand All @@ -2883,7 +2885,8 @@ static int restore_metadump(const char *input, FILE *out, int old_restore,

if (!info) {
root = open_ctree_fd(fileno(out), target, 0,
OPEN_CTREE_ALLOW_TRANSID_MISMATCH);
OPEN_CTREE_ALLOW_TRANSID_MISMATCH |
OPEN_CTREE_SKIP_LEAF_ITEM_CHECKS);
if (!root) {
error("open ctree failed in %s", target);
ret = -EIO;
Expand Down Expand Up @@ -3226,7 +3229,8 @@ int BOX_MAIN(image)(int argc, char *argv[])
int i;

ocf.filename = target;
ocf.flags = OPEN_CTREE_PARTIAL | OPEN_CTREE_RESTORE;
ocf.flags = OPEN_CTREE_PARTIAL | OPEN_CTREE_RESTORE |
OPEN_CTREE_SKIP_LEAF_ITEM_CHECKS;
info = open_ctree_fs_info(&ocf);
if (!info) {
error("open ctree failed at %s", target);
Expand Down
10 changes: 10 additions & 0 deletions include/kerncompat.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
#define _RET_IP_ 0
#define TASK_UNINTERRUPTIBLE 0
#define SLAB_MEM_SPREAD 0
#define ALLOW_ERROR_INJECTION(a, b)

#ifndef ULONG_MAX
#define ULONG_MAX (~0UL)
Expand Down Expand Up @@ -417,6 +418,15 @@ do { \
__ret_warn_on; \
})

#define WARN(c, msg...) ({ \
int __ret_warn_on = !!(c); \
if (__ret_warn_on) \
printf(msg); \
__ret_warn_on; \
})

#define IS_ENABLED(c) 0

#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
Expand Down
1 change: 1 addition & 0 deletions kernel-lib/bitops.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#ifndef _PERF_LINUX_BITOPS_H_
#define _PERF_LINUX_BITOPS_H_

#include "kerncompat.h"
#include <endian.h>
#include "common/internal.h"

Expand Down
2 changes: 2 additions & 0 deletions kernel-lib/raid56.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
#ifndef __BTRFS_PROGS_RAID56_H__
#define __BTRFS_PROGS_RAID56_H__

#include "kerncompat.h"

void raid6_gen_syndrome(int disks, size_t bytes, void **ptrs);
int raid5_gen_result(int nr_devs, size_t stripe_len, int dest, void **data);

Expand Down
186 changes: 4 additions & 182 deletions kernel-shared/ctree.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,18 @@
* Boston, MA 021110-1307, USA.
*/

#include "kernel-lib/bitops.h"
#include "kernel-lib/sizes.h"
#include "kernel-shared/ctree.h"
#include "kernel-shared/disk-io.h"
#include "kernel-shared/transaction.h"
#include "kernel-shared/print-tree.h"
#include "kernel-lib/bitops.h"
#include "kernel-shared/tree-checker.h"
#include "kernel-shared/volumes.h"
#include "crypto/crc32c.h"
#include "common/internal.h"
#include "common/messages.h"
#include "common/utils.h"
#include "kernel-lib/sizes.h"
#include "kernel-shared/volumes.h"
#include "check/repair.h"

static int split_node(struct btrfs_trans_handle *trans, struct btrfs_root
Expand Down Expand Up @@ -602,185 +603,6 @@ static inline unsigned int leaf_data_end(const struct extent_buffer *leaf)
return btrfs_item_offset(leaf, nr - 1);
}

static void generic_err(const struct extent_buffer *buf, int slot,
const char *fmt, ...)
{
va_list args;

fprintf(stderr, "corrupt %s: root=%lld block=%llu slot=%d, ",
btrfs_header_level(buf) == 0 ? "leaf": "node",
btrfs_header_owner(buf), btrfs_header_bytenr(buf), slot);
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
fprintf(stderr, "\n");
}

enum btrfs_tree_block_status __btrfs_check_node(struct extent_buffer *node)
{
struct btrfs_fs_info *fs_info = node->fs_info;
unsigned long nr = btrfs_header_nritems(node);
struct btrfs_key key, next_key;
int slot;
int level = btrfs_header_level(node);
u64 bytenr;
enum btrfs_tree_block_status ret = BTRFS_TREE_BLOCK_INVALID_NRITEMS;

if (level <= 0 || level >= BTRFS_MAX_LEVEL) {
generic_err(node, 0,
"invalid level for node, have %d expect [1, %d]",
level, BTRFS_MAX_LEVEL - 1);
ret = BTRFS_TREE_BLOCK_INVALID_LEVEL;
goto fail;
}
if (nr == 0 || nr > BTRFS_NODEPTRS_PER_BLOCK(fs_info)) {
generic_err(node, 0,
"corrupt node: root=%llu block=%llu, nritems too %s, have %lu expect range [1,%u]",
btrfs_header_owner(node), node->start,
nr == 0 ? "small" : "large", nr,
BTRFS_NODEPTRS_PER_BLOCK(fs_info));
ret = BTRFS_TREE_BLOCK_INVALID_NRITEMS;
goto fail;
}

for (slot = 0; slot < nr - 1; slot++) {
bytenr = btrfs_node_blockptr(node, slot);
btrfs_node_key_to_cpu(node, &key, slot);
btrfs_node_key_to_cpu(node, &next_key, slot + 1);

if (!bytenr) {
generic_err(node, slot,
"invalid NULL node pointer");
ret = BTRFS_TREE_BLOCK_INVALID_BLOCKPTR;
goto fail;
}
if (!IS_ALIGNED(bytenr, fs_info->sectorsize)) {
generic_err(node, slot,
"unaligned pointer, have %llu should be aligned to %u",
bytenr, fs_info->sectorsize);
ret = BTRFS_TREE_BLOCK_INVALID_BLOCKPTR;
goto fail;
}

if (btrfs_comp_cpu_keys(&key, &next_key) >= 0) {
generic_err(node, slot,
"bad key order, current (%llu %u %llu) next (%llu %u %llu)",
key.objectid, key.type, key.offset,
next_key.objectid, next_key.type,
next_key.offset);
ret = BTRFS_TREE_BLOCK_BAD_KEY_ORDER;
goto fail;
}
}
ret = BTRFS_TREE_BLOCK_CLEAN;
fail:
return ret;
}

enum btrfs_tree_block_status __btrfs_check_leaf(struct extent_buffer *leaf)
{
struct btrfs_fs_info *fs_info = leaf->fs_info;
/* No valid key type is 0, so all key should be larger than this key */
struct btrfs_key prev_key = {0, 0, 0};
struct btrfs_key key;
u32 nritems = btrfs_header_nritems(leaf);
int slot;
int ret;

if (btrfs_header_level(leaf) != 0) {
generic_err(leaf, 0,
"invalid level for leaf, have %d expect 0",
btrfs_header_level(leaf));
ret = BTRFS_TREE_BLOCK_INVALID_LEVEL;
goto fail;
}

if (nritems == 0)
return 0;

/*
* Check the following things to make sure this is a good leaf, and
* leaf users won't need to bother with similar sanity checks:
*
* 1) key ordering
* 2) item offset and size
* No overlap, no hole, all inside the leaf.
* 3) item content
* If possible, do comprehensive sanity check.
* NOTE: All checks must only rely on the item data itself.
*/
for (slot = 0; slot < nritems; slot++) {
u32 item_end_expected;
u64 item_data_end;

btrfs_item_key_to_cpu(leaf, &key, slot);

/* Make sure the keys are in the right order */
if (btrfs_comp_cpu_keys(&prev_key, &key) >= 0) {
generic_err(leaf, slot,
"bad key order, prev (%llu %u %llu) current (%llu %u %llu)",
prev_key.objectid, prev_key.type,
prev_key.offset, key.objectid, key.type,
key.offset);
ret = BTRFS_TREE_BLOCK_BAD_KEY_ORDER;
goto fail;
}

item_data_end = (u64)btrfs_item_offset(leaf, slot) +
btrfs_item_size(leaf, slot);
/*
* Make sure the offset and ends are right, remember that the
* item data starts at the end of the leaf and grows towards the
* front.
*/
if (slot == 0)
item_end_expected = BTRFS_LEAF_DATA_SIZE(fs_info);
else
item_end_expected = btrfs_item_offset(leaf,
slot - 1);
if (item_data_end != item_end_expected) {
generic_err(leaf, slot,
"unexpected item end, have %llu expect %u",
item_data_end, item_end_expected);
ret = BTRFS_TREE_BLOCK_INVALID_OFFSETS;
goto fail;
}

/*
* Check to make sure that we don't point outside of the leaf,
* just in case all the items are consistent to each other, but
* all point outside of the leaf.
*/
if (item_data_end > BTRFS_LEAF_DATA_SIZE(fs_info)) {
generic_err(leaf, slot,
"slot end outside of leaf, have %llu expect range [0, %u]",
item_data_end, BTRFS_LEAF_DATA_SIZE(fs_info));
ret = BTRFS_TREE_BLOCK_INVALID_OFFSETS;
goto fail;
}

/* Also check if the item pointer overlaps with btrfs item. */
if (btrfs_item_ptr_offset(leaf, slot) <
btrfs_item_nr_offset(leaf, slot) + sizeof(struct btrfs_item)) {
generic_err(leaf, slot,
"slot overlaps with its data, item end %lu data start %lu",
btrfs_item_nr_offset(leaf, slot) +
sizeof(struct btrfs_item),
btrfs_item_ptr_offset(leaf, slot));
ret = BTRFS_TREE_BLOCK_INVALID_OFFSETS;
goto fail;
}

prev_key.objectid = key.objectid;
prev_key.type = key.type;
prev_key.offset = key.offset;
}

ret = BTRFS_TREE_BLOCK_CLEAN;
fail:
return ret;
}

static int noinline check_block(struct btrfs_fs_info *fs_info,
struct btrfs_path *path, int level)
{
Expand Down
14 changes: 1 addition & 13 deletions kernel-shared/ctree.h
Original file line number Diff line number Diff line change
Expand Up @@ -185,17 +185,6 @@ struct btrfs_path {
sizeof(struct btrfs_item))
#define BTRFS_MAX_EXTENT_SIZE 128UL * 1024 * 1024

enum btrfs_tree_block_status {
BTRFS_TREE_BLOCK_CLEAN,
BTRFS_TREE_BLOCK_INVALID_NRITEMS,
BTRFS_TREE_BLOCK_INVALID_PARENT_KEY,
BTRFS_TREE_BLOCK_BAD_KEY_ORDER,
BTRFS_TREE_BLOCK_INVALID_LEVEL,
BTRFS_TREE_BLOCK_INVALID_FREE_SPACE,
BTRFS_TREE_BLOCK_INVALID_OFFSETS,
BTRFS_TREE_BLOCK_INVALID_BLOCKPTR,
};

/*
* We don't want to overwrite 1M at the beginning of device, even though
* there is our 1st superblock at 64k. Some possible reasons:
Expand Down Expand Up @@ -373,6 +362,7 @@ struct btrfs_fs_info {
unsigned int finalize_on_close:1;
unsigned int hide_names:1;
unsigned int allow_transid_mismatch:1;
unsigned int skip_leaf_item_checks:1;

int transaction_aborted;
int force_csum_type;
Expand Down Expand Up @@ -958,8 +948,6 @@ int btrfs_convert_one_bg(struct btrfs_trans_handle *trans, u64 bytenr);
int btrfs_comp_cpu_keys(const struct btrfs_key *k1, const struct btrfs_key *k2);
int btrfs_del_ptr(struct btrfs_root *root, struct btrfs_path *path,
int level, int slot);
enum btrfs_tree_block_status __btrfs_check_node(struct extent_buffer *buf);
enum btrfs_tree_block_status __btrfs_check_leaf(struct extent_buffer *buf);
struct extent_buffer *read_node_slot(struct btrfs_fs_info *fs_info,
struct extent_buffer *parent, int slot);
int btrfs_previous_item(struct btrfs_root *root,
Expand Down
Loading

0 comments on commit c8593f6

Please sign in to comment.