Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Btrfs: fix an oops of log replay

When btrfs recovers from a crash, it may hit the oops below:

------------[ cut here ]------------
kernel BUG at fs/btrfs/inode.c:4580!
[...]
RIP: 0010:[<ffffffffa03df251>] [<ffffffffa03df251>] btrfs_add_link+0x161/0x1c0 [btrfs]
[...]
Call Trace:
[<ffffffffa03e7b31>] ? btrfs_inode_ref_index+0x31/0x80 [btrfs]
[<ffffffffa04054e9>] add_inode_ref+0x319/0x3f0 [btrfs]
[<ffffffffa0407087>] replay_one_buffer+0x2c7/0x390 [btrfs]
[<ffffffffa040444a>] walk_down_log_tree+0x32a/0x480 [btrfs]
[<ffffffffa0404695>] walk_log_tree+0xf5/0x240 [btrfs]
[<ffffffffa0406cc0>] btrfs_recover_log_trees+0x250/0x350 [btrfs]
[<ffffffffa0406dc0>] ? btrfs_recover_log_trees+0x350/0x350 [btrfs]
[<ffffffffa03d18b2>] open_ctree+0x1442/0x17d0 [btrfs]
[...]

This comes from that while replaying an inode ref item, we forget to
check those old conflicting DIR_ITEM and DIR_INDEX items in fs/file tree,
then we will come to conflict corners which lead to BUG_ON().

Signed-off-by: Liu Bo <liubo2009@cn.fujitsu.com>
Tested-by: Andy Lutomirski <luto@mit.edu>
Signed-off-by: Chris Mason <chris.mason@oracle.com>

authored by

liubo and committed by
Chris Mason
34f3e4f2 d5e2003c

+24 -4
+24 -4
fs/btrfs/tree-log.c
··· 799 799 struct extent_buffer *eb, int slot, 800 800 struct btrfs_key *key) 801 801 { 802 - struct inode *dir; 803 - int ret; 804 802 struct btrfs_inode_ref *ref; 803 + struct btrfs_dir_item *di; 804 + struct inode *dir; 805 805 struct inode *inode; 806 - char *name; 807 - int namelen; 808 806 unsigned long ref_ptr; 809 807 unsigned long ref_end; 808 + char *name; 809 + int namelen; 810 + int ret; 810 811 int search_done = 0; 811 812 812 813 /* ··· 907 906 * coresponding ref, it does not need to check again. 908 907 */ 909 908 search_done = 1; 909 + } 910 + btrfs_release_path(path); 911 + 912 + /* look for a conflicting sequence number */ 913 + di = btrfs_lookup_dir_index_item(trans, root, path, btrfs_ino(dir), 914 + btrfs_inode_ref_index(eb, ref), 915 + name, namelen, 0); 916 + if (di && !IS_ERR(di)) { 917 + ret = drop_one_dir_item(trans, root, path, dir, di); 918 + BUG_ON(ret); 919 + } 920 + btrfs_release_path(path); 921 + 922 + /* look for a conflicing name */ 923 + di = btrfs_lookup_dir_item(trans, root, path, btrfs_ino(dir), 924 + name, namelen, 0); 925 + if (di && !IS_ERR(di)) { 926 + ret = drop_one_dir_item(trans, root, path, dir, di); 927 + BUG_ON(ret); 910 928 } 911 929 btrfs_release_path(path); 912 930