107 lines
3.4 KiB
Diff
107 lines
3.4 KiB
Diff
From 631ee666394a36ec36abf1042956cdb2e6058b86 Mon Sep 17 00:00:00 2001
|
|
From: Qu Wenruo <wqu@suse.com>
|
|
Date: Fri, 12 Jan 2024 16:13:27 +1030
|
|
Subject: [PATCH] btrfs-progs: convert: for ext2, fix possible tree-checker
|
|
error when converting a large fs
|
|
|
|
[BUG]
|
|
There is a report about failed btrfs-convert, which shows the following
|
|
error:
|
|
|
|
corrupt leaf: root=5 block=5001928998912 slot=1 ino=89911763, invalid previous key objectid, have 89911762 expect 89911763
|
|
ERROR: failed to copy ext2 inode 89911320: -5
|
|
ERROR: error during copy_inodes -5
|
|
WARNING: error during conversion, the original filesystem is not modified
|
|
|
|
[CAUSE]
|
|
Above error is triggered when checking the following items inside a
|
|
subvolume:
|
|
|
|
- inode ref
|
|
- dir item/index
|
|
- file extent
|
|
- xattr
|
|
|
|
This is to make sure these items have correct previous key.
|
|
|
|
However btrfs-convert is not following this requirement, it always
|
|
inserts those items first, then creates a btrfs_inode for it.
|
|
|
|
Thus it can lead to the error.
|
|
|
|
This can only happen for large fs, as for most cases we have all these
|
|
modified tree blocks cached, thus tree-checker won't be triggered.
|
|
But when the tree block cache is not hit, and we have to read from disk,
|
|
then such behavior can lead to above tree-checker error.
|
|
|
|
[FIX]
|
|
Make sure we insert the inode item first, then the file extents/dir
|
|
items/xattrs. And after the file extents/dir items/xattrs inserted, we
|
|
update the existing inode (to update its size and bytes).
|
|
|
|
Signed-off-by: Qu Wenruo <wqu@suse.com>
|
|
Signed-off-by: David Sterba <dsterba@suse.com>
|
|
---
|
|
convert/source-ext2.c | 31 ++++++++++++++++++++++++++++++-
|
|
1 file changed, 30 insertions(+), 1 deletion(-)
|
|
|
|
diff --git a/convert/source-ext2.c b/convert/source-ext2.c
|
|
index cfffc9e..ad7aeda 100644
|
|
--- a/convert/source-ext2.c
|
|
+++ b/convert/source-ext2.c
|
|
@@ -854,6 +854,8 @@ static int ext2_copy_single_inode(struct btrfs_trans_handle *trans,
|
|
int ret;
|
|
int s_inode_size;
|
|
struct btrfs_inode_item btrfs_inode;
|
|
+ struct btrfs_key inode_key;
|
|
+ struct btrfs_path path = { 0 };
|
|
|
|
if (ext2_inode->i_links_count == 0)
|
|
return 0;
|
|
@@ -875,6 +877,15 @@ static int ext2_copy_single_inode(struct btrfs_trans_handle *trans,
|
|
}
|
|
ext2_convert_inode_flags(&btrfs_inode, ext2_inode);
|
|
|
|
+ /*
|
|
+ * The inode item must be inserted before any file extents/dir items/xattrs,
|
|
+ * or we may trigger tree-checker. File extents/dir items/xattrs require
|
|
+ * the previous item has the same key objectid.
|
|
+ */
|
|
+ ret = btrfs_insert_inode(trans, root, objectid, &btrfs_inode);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
switch (ext2_inode->i_mode & S_IFMT) {
|
|
case S_IFREG:
|
|
ret = ext2_create_file_extents(trans, root, objectid,
|
|
@@ -901,7 +912,25 @@ static int ext2_copy_single_inode(struct btrfs_trans_handle *trans,
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
- return btrfs_insert_inode(trans, root, objectid, &btrfs_inode);
|
|
+
|
|
+ /*
|
|
+ * Update the inode item, as above insert never updates the inode's
|
|
+ * nbytes and size.
|
|
+ */
|
|
+ inode_key.objectid = objectid;
|
|
+ inode_key.type = BTRFS_INODE_ITEM_KEY;
|
|
+ inode_key.offset = 0;
|
|
+
|
|
+ ret = btrfs_lookup_inode(trans, root, &path, &inode_key, 1);
|
|
+ if (ret > 0)
|
|
+ ret = -ENOENT;
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+ write_extent_buffer(path.nodes[0], &btrfs_inode,
|
|
+ btrfs_item_ptr_offset(path.nodes[0], path.slots[0]),
|
|
+ sizeof(btrfs_inode));
|
|
+ btrfs_release_path(&path);
|
|
+ return 0;
|
|
}
|
|
|
|
static bool ext2_is_special_inode(ext2_filsys ext2_fs, ext2_ino_t ino)
|
|
--
|
|
2.43.0
|
|
|