| From stable+bounces-171678-greg=kroah.com@vger.kernel.org Tue Aug 19 01:48:19 2025 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Mon, 18 Aug 2025 19:47:23 -0400 |
| Subject: btrfs: don't ignore inode missing when replaying log tree |
| To: stable@vger.kernel.org |
| Cc: Filipe Manana <fdmanana@suse.com>, Boris Burkov <boris@bur.io>, David Sterba <dsterba@suse.com>, Sasha Levin <sashal@kernel.org> |
| Message-ID: <20250818234723.154435-1-sashal@kernel.org> |
| |
| From: Filipe Manana <fdmanana@suse.com> |
| |
| [ Upstream commit 7ebf381a69421a88265d3c49cd0f007ba7336c9d ] |
| |
| During log replay, at add_inode_ref(), we return -ENOENT if our current |
| inode isn't found on the subvolume tree or if a parent directory isn't |
| found. The error comes from btrfs_iget_logging() <- btrfs_iget() <- |
| btrfs_read_locked_inode(). |
| |
| The single caller of add_inode_ref(), replay_one_buffer(), ignores an |
| -ENOENT error because it expects that error to mean only that a parent |
| directory wasn't found and that is ok. |
| |
| Before commit 5f61b961599a ("btrfs: fix inode lookup error handling during |
| log replay") we were converting any error when getting a parent directory |
| to -ENOENT and any error when getting the current inode to -EIO, so our |
| caller would fail log replay in case we can't find the current inode. |
| After that commit however in case the current inode is not found we return |
| -ENOENT to the caller and therefore it ignores the critical fact that the |
| current inode was not found in the subvolume tree. |
| |
| Fix this by converting -ENOENT to 0 when we don't find a parent directory, |
| returning -ENOENT when we don't find the current inode and making the |
| caller, replay_one_buffer(), not ignore -ENOENT anymore. |
| |
| Fixes: 5f61b961599a ("btrfs: fix inode lookup error handling during log replay") |
| CC: stable@vger.kernel.org # 6.16 |
| Reviewed-by: Boris Burkov <boris@bur.io> |
| Signed-off-by: Filipe Manana <fdmanana@suse.com> |
| Signed-off-by: David Sterba <dsterba@suse.com> |
| [ adapted btrfs_inode pointer usage to older inode API ] |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| fs/btrfs/tree-log.c | 14 ++++++++++++-- |
| 1 file changed, 12 insertions(+), 2 deletions(-) |
| |
| --- a/fs/btrfs/tree-log.c |
| +++ b/fs/btrfs/tree-log.c |
| @@ -1422,6 +1422,8 @@ static noinline int add_inode_ref(struct |
| btrfs_dir = btrfs_iget_logging(parent_objectid, root); |
| if (IS_ERR(btrfs_dir)) { |
| ret = PTR_ERR(btrfs_dir); |
| + if (ret == -ENOENT) |
| + ret = 0; |
| dir = NULL; |
| goto out; |
| } |
| @@ -1455,6 +1457,15 @@ static noinline int add_inode_ref(struct |
| if (IS_ERR(btrfs_dir)) { |
| ret = PTR_ERR(btrfs_dir); |
| dir = NULL; |
| + /* |
| + * A new parent dir may have not been |
| + * logged and not exist in the subvolume |
| + * tree, see the comment above before |
| + * the loop when getting the first |
| + * parent dir. |
| + */ |
| + if (ret == -ENOENT) |
| + ret = 0; |
| goto out; |
| } |
| dir = &btrfs_dir->vfs_inode; |
| @@ -2623,9 +2634,8 @@ static int replay_one_buffer(struct btrf |
| key.type == BTRFS_INODE_EXTREF_KEY) { |
| ret = add_inode_ref(wc->trans, root, log, path, |
| eb, i, &key); |
| - if (ret && ret != -ENOENT) |
| + if (ret) |
| break; |
| - ret = 0; |
| } else if (key.type == BTRFS_EXTENT_DATA_KEY) { |
| ret = replay_one_extent(wc->trans, root, path, |
| eb, i, &key); |