btrfs-progs: introduce OPEN_CTREE_ALLOW_TRANSID_MISMATCH flag

[BUG]
There is a report that, btrfstune can even work while the fs has transid
mismatch problems.

  $ btrfstune -f -u /dev/sdb1
  Current fsid: b2b5ae8d-4c49-45f0-b42e-46fe7dcfcb07
  New fsid: b2b5ae8d-4c49-45f0-b42e-46fe7dcfcb07
  Set superblock flag CHANGING_FSID
  Change fsid in extents
  parent transid verify failed on 792854528 wanted 20103 found 20091
  parent transid verify failed on 792854528 wanted 20103 found 20091
  parent transid verify failed on 792854528 wanted 20103 found 20091
  Ignoring transid failure
  parent transid verify failed on 792870912 wanted 20103 found 20091
  parent transid verify failed on 792870912 wanted 20103 found 20091
  parent transid verify failed on 792870912 wanted 20103 found 20091
  Ignoring transid failure
  parent transid verify failed on 792887296 wanted 20103 found 20091
  parent transid verify failed on 792887296 wanted 20103 found 20091
  parent transid verify failed on 792887296 wanted 20103 found 20091
  Ignoring transid failure
  ERROR: child eb corrupted: parent bytenr=38010880 item=69 parent level=1 child level=1
  ERROR: failed to change UUID of metadata: -5
  ERROR: btrfstune failed

This leaves a corrupted fs even more corrupted, and due to the extra
CHANGING_FSID flag, btrfs check will not even try to run on it:

  Opening filesystem to check...
  ERROR: Filesystem UUID change in progress
  ERROR: cannot open file system

[CAUSE]
Unlike kernel, btrfs-progs has a less strict check on transid mismatch.

In read_tree_block() we will fall back to use the tree block even its
transid mismatch if we can't find any better copy.

However not all commands in btrfs-progs needs this feature, only
btrfs-check (which may fix the problem) and btrfs-restore (it just tries
to ignore any problems) really utilize this feature.

[FIX]
Introduce a new open ctree flag, OPEN_CTREE_ALLOW_TRANSID_MISMATCH, to
be explicit about whether we really want to ignore transid error.

Currently only btrfs-check and btrfs-restore will utilize this new flag.

Also add btrfs-image to allow opening such fs with transid error.

Link: https://www.reddit.com/r/btrfs/comments/pivpqk/failure_during_btrfstune_u/
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
Qu Wenruo 2021-09-08 10:05:42 +08:00 committed by David Sterba
parent 7d16785ab4
commit 60651ad9da
6 changed files with 27 additions and 8 deletions

View File

@ -10384,7 +10384,8 @@ static int cmd_check(const struct cmd_struct *cmd, int argc, char **argv)
int qgroup_report = 0;
int qgroups_repaired = 0;
int qgroup_verify_ret;
unsigned ctree_flags = OPEN_CTREE_EXCLUSIVE;
unsigned ctree_flags = OPEN_CTREE_EXCLUSIVE |
OPEN_CTREE_ALLOW_TRANSID_MISMATCH;
int force = 0;
while(1) {

View File

@ -1213,7 +1213,8 @@ static struct btrfs_root *open_fs(const char *dev, u64 root_location,
ocf.filename = dev;
ocf.sb_bytenr = bytenr;
ocf.root_tree_bytenr = root_location;
ocf.flags = OPEN_CTREE_PARTIAL | OPEN_CTREE_NO_BLOCK_GROUPS;
ocf.flags = OPEN_CTREE_PARTIAL | OPEN_CTREE_NO_BLOCK_GROUPS |
OPEN_CTREE_ALLOW_TRANSID_MISMATCH;
fs_info = open_ctree_fs_info(&ocf);
if (fs_info)
break;

View File

@ -1004,7 +1004,7 @@ static int create_metadump(const char *input, FILE *out, int num_threads,
int ret;
int err = 0;
root = open_ctree(input, 0, 0);
root = open_ctree(input, 0, OPEN_CTREE_ALLOW_TRANSID_MISMATCH);
if (!root) {
error("open ctree failed");
return -EIO;
@ -2781,7 +2781,8 @@ static int restore_metadump(const char *input, FILE *out, int old_restore,
struct open_ctree_flags ocf = { 0 };
ocf.filename = target;
ocf.flags = OPEN_CTREE_WRITES | OPEN_CTREE_RESTORE | OPEN_CTREE_PARTIAL;
ocf.flags = OPEN_CTREE_WRITES | OPEN_CTREE_RESTORE |
OPEN_CTREE_PARTIAL;
info = open_ctree_fs_info(&ocf);
if (!info) {
error("open ctree failed");
@ -2846,7 +2847,8 @@ static int restore_metadump(const char *input, FILE *out, int old_restore,
root = open_ctree_fd(fileno(out), target, 0,
OPEN_CTREE_PARTIAL |
OPEN_CTREE_WRITES |
OPEN_CTREE_NO_DEVICES);
OPEN_CTREE_NO_DEVICES |
OPEN_CTREE_ALLOW_TRANSID_MISMATCH);
if (!root) {
error("open ctree failed in %s", target);
ret = -EIO;
@ -2864,7 +2866,8 @@ static int restore_metadump(const char *input, FILE *out, int old_restore,
u64 dev_size;
if (!info) {
root = open_ctree_fd(fileno(out), target, 0, 0);
root = open_ctree_fd(fileno(out), target, 0,
OPEN_CTREE_ALLOW_TRANSID_MISMATCH);
if (!root) {
error("open ctree failed in %s", target);
ret = -EIO;

View File

@ -1216,6 +1216,7 @@ struct btrfs_fs_info {
unsigned int avoid_sys_chunk_alloc:1;
unsigned int finalize_on_close:1;
unsigned int hide_names:1;
unsigned int allow_transid_mismatch:1;
int transaction_aborted;

View File

@ -421,7 +421,7 @@ struct extent_buffer* read_tree_block(struct btrfs_fs_info *fs_info, u64 bytenr,
ret = -EIO;
break;
}
if (num_copies == 1) {
if (num_copies == 1 && fs_info->allow_transid_mismatch) {
ignore = 1;
continue;
}
@ -431,6 +431,10 @@ struct extent_buffer* read_tree_block(struct btrfs_fs_info *fs_info, u64 bytenr,
}
mirror_num++;
if (mirror_num > num_copies) {
if (!fs_info->allow_transid_mismatch) {
ret = -EIO;
break;
}
if (candidate_mirror > 0)
mirror_num = candidate_mirror;
else
@ -1231,6 +1235,8 @@ static struct btrfs_fs_info *__open_ctree_fd(int fp, struct open_ctree_flags *oc
fs_info->ignore_chunk_tree_error = 1;
if (flags & OPEN_CTREE_HIDE_NAMES)
fs_info->hide_names = 1;
if (flags & OPEN_CTREE_ALLOW_TRANSID_MISMATCH)
fs_info->allow_transid_mismatch = 1;
if ((flags & OPEN_CTREE_RECOVER_SUPER)
&& (flags & OPEN_CTREE_TEMPORARY_SUPER)) {
@ -1988,7 +1994,8 @@ int btrfs_buffer_uptodate(struct extent_buffer *buf, u64 parent_transid)
return ret;
ret = verify_parent_transid(&buf->fs_info->extent_cache, buf,
parent_transid, 1);
parent_transid,
buf->fs_info->allow_transid_mismatch);
return !ret;
}

View File

@ -88,6 +88,12 @@ enum btrfs_open_ctree_flags {
/* For print-tree, print HIDDEN instead of filenames/xattrs/refs */
OPEN_CTREE_HIDE_NAMES = (1U << 14),
/*
* Allow certain commands like check/restore to ignore transid
* mismatch.
*/
OPEN_CTREE_ALLOW_TRANSID_MISMATCH = (1U << 15),
};
/*