| From 4751dc99627e4d1465c5bfa8cb7ab31ed418eff5 Mon Sep 17 00:00:00 2001 |
| From: Filipe Manana <fdmanana@suse.com> |
| Date: Mon, 28 Feb 2022 16:29:28 +0000 |
| Subject: btrfs: add missing run of delayed items after unlink during log replay |
| |
| From: Filipe Manana <fdmanana@suse.com> |
| |
| commit 4751dc99627e4d1465c5bfa8cb7ab31ed418eff5 upstream. |
| |
| During log replay, whenever we need to check if a name (dentry) exists in |
| a directory we do searches on the subvolume tree for inode references or |
| or directory entries (BTRFS_DIR_INDEX_KEY keys, and BTRFS_DIR_ITEM_KEY |
| keys as well, before kernel 5.17). However when during log replay we |
| unlink a name, through btrfs_unlink_inode(), we may not delete inode |
| references and dir index keys from a subvolume tree and instead just add |
| the deletions to the delayed inode's delayed items, which will only be |
| run when we commit the transaction used for log replay. This means that |
| after an unlink operation during log replay, if we attempt to search for |
| the same name during log replay, we will not see that the name was already |
| deleted, since the deletion is recorded only on the delayed items. |
| |
| We run delayed items after every unlink operation during log replay, |
| except at unlink_old_inode_refs() and at add_inode_ref(). This was due |
| to an overlook, as delayed items should be run after evert unlink, for |
| the reasons stated above. |
| |
| So fix those two cases. |
| |
| Fixes: 0d836392cadd5 ("Btrfs: fix mount failure after fsync due to hard link recreation") |
| Fixes: 1f250e929a9c9 ("Btrfs: fix log replay failure after unlink and link combination") |
| CC: stable@vger.kernel.org # 4.19+ |
| Signed-off-by: Filipe Manana <fdmanana@suse.com> |
| Signed-off-by: David Sterba <dsterba@suse.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| fs/btrfs/tree-log.c | 18 ++++++++++++++++++ |
| 1 file changed, 18 insertions(+) |
| |
| --- a/fs/btrfs/tree-log.c |
| +++ b/fs/btrfs/tree-log.c |
| @@ -1308,6 +1308,15 @@ again: |
| inode, name, namelen); |
| kfree(name); |
| iput(dir); |
| + /* |
| + * Whenever we need to check if a name exists or not, we |
| + * check the subvolume tree. So after an unlink we must |
| + * run delayed items, so that future checks for a name |
| + * during log replay see that the name does not exists |
| + * anymore. |
| + */ |
| + if (!ret) |
| + ret = btrfs_run_delayed_items(trans); |
| if (ret) |
| goto out; |
| goto again; |
| @@ -1559,6 +1568,15 @@ static noinline int add_inode_ref(struct |
| */ |
| if (!ret && inode->i_nlink == 0) |
| inc_nlink(inode); |
| + /* |
| + * Whenever we need to check if a name exists or |
| + * not, we check the subvolume tree. So after an |
| + * unlink we must run delayed items, so that future |
| + * checks for a name during log replay see that the |
| + * name does not exists anymore. |
| + */ |
| + if (!ret) |
| + ret = btrfs_run_delayed_items(trans); |
| } |
| if (ret < 0) |
| goto out; |