Skip to content

Commit 83e98b0

Browse files
fdmananakdave
authored andcommitted
btrfs: don't skip remaining extrefs if dir not found during log replay
During log replay, at add_inode_ref(), if we have an extref item that contains multiple extrefs and one of them points to a directory that does not exist in the subvolume tree, we are supposed to ignore it and process the remaining extrefs encoded in the extref item, since each extref can point to a different parent inode. However when that happens we just return from the function and ignore the remaining extrefs. The problem has been around since extrefs were introduced, in commit f186373 ("btrfs: extended inode refs"), but it's hard to hit in practice because getting extref items encoding multiple extref requires getting a hash collision when computing the offset of the extref's key. The offset if computed like this: key.offset = btrfs_extref_hash(dir_ino, name->name, name->len); and btrfs_extref_hash() is just a wrapper around crc32c(). Fix this by moving to next iteration of the loop when we don't find the parent directory that an extref points to. Fixes: f186373 ("btrfs: extended inode refs") Reviewed-by: Boris Burkov <[email protected]> Signed-off-by: Filipe Manana <[email protected]> Signed-off-by: David Sterba <[email protected]>
1 parent 5f6a0ce commit 83e98b0

File tree

1 file changed

+14
-4
lines changed

1 file changed

+14
-4
lines changed

fs/btrfs/tree-log.c

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1433,6 +1433,8 @@ static noinline int add_inode_ref(struct btrfs_trans_handle *trans,
14331433
if (log_ref_ver) {
14341434
ret = extref_get_fields(eb, ref_ptr, &name,
14351435
&ref_index, &parent_objectid);
1436+
if (ret)
1437+
goto out;
14361438
/*
14371439
* parent object can change from one array
14381440
* item to another.
@@ -1449,16 +1451,23 @@ static noinline int add_inode_ref(struct btrfs_trans_handle *trans,
14491451
* the loop when getting the first
14501452
* parent dir.
14511453
*/
1452-
if (ret == -ENOENT)
1454+
if (ret == -ENOENT) {
1455+
/*
1456+
* The next extref may refer to
1457+
* another parent dir that
1458+
* exists, so continue.
1459+
*/
14531460
ret = 0;
1461+
goto next;
1462+
}
14541463
goto out;
14551464
}
14561465
}
14571466
} else {
14581467
ret = ref_get_fields(eb, ref_ptr, &name, &ref_index);
1468+
if (ret)
1469+
goto out;
14591470
}
1460-
if (ret)
1461-
goto out;
14621471

14631472
ret = inode_in_dir(root, path, btrfs_ino(dir), btrfs_ino(inode),
14641473
ref_index, &name);
@@ -1492,10 +1501,11 @@ static noinline int add_inode_ref(struct btrfs_trans_handle *trans,
14921501
}
14931502
/* Else, ret == 1, we already have a perfect match, we're done. */
14941503

1504+
next:
14951505
ref_ptr = (unsigned long)(ref_ptr + ref_struct_size) + name.len;
14961506
kfree(name.name);
14971507
name.name = NULL;
1498-
if (log_ref_ver) {
1508+
if (log_ref_ver && dir) {
14991509
iput(&dir->vfs_inode);
15001510
dir = NULL;
15011511
}

0 commit comments

Comments
 (0)