Skip to content

Commit f2a701f

Browse files
author
Kent Overstreet
committed
bcachefs: fsck: Improve check_key_has_inode()
Print out more info when we find a key (extent, dirent, xattr) for a missing inode - was there a good inode in an older snapshot, full(ish) list of keys for that missing inode, so we can make better decisions on how to repair. If it looks like it should've been deleted, autofix it. If we ever hit the non-autofix cases, we'll want to write more repair code (possibly reconstituting the inode). Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
1 parent 03208bd commit f2a701f

2 files changed

Lines changed: 88 additions & 22 deletions

File tree

fs/bcachefs/fsck.c

Lines changed: 87 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1436,6 +1436,7 @@ static int check_key_has_inode(struct btree_trans *trans,
14361436
{
14371437
struct bch_fs *c = trans->c;
14381438
struct printbuf buf = PRINTBUF;
1439+
struct btree_iter iter2 = {};
14391440
int ret = PTR_ERR_OR_ZERO(i);
14401441
if (ret)
14411442
return ret;
@@ -1445,40 +1446,105 @@ static int check_key_has_inode(struct btree_trans *trans,
14451446

14461447
bool have_inode = i && !i->whiteout;
14471448

1448-
if (!have_inode && (c->sb.btrees_lost_data & BIT_ULL(BTREE_ID_inodes))) {
1449-
ret = reconstruct_inode(trans, iter->btree_id, k.k->p.snapshot, k.k->p.inode) ?:
1450-
bch2_trans_commit(trans, NULL, NULL, BCH_TRANS_COMMIT_no_enospc);
1451-
if (ret)
1452-
goto err;
1449+
if (!have_inode && (c->sb.btrees_lost_data & BIT_ULL(BTREE_ID_inodes)))
1450+
goto reconstruct;
14531451

1454-
inode->last_pos.inode--;
1455-
ret = bch_err_throw(c, transaction_restart_nested);
1456-
goto err;
1452+
if (have_inode && btree_matches_i_mode(iter->btree_id, i->inode.bi_mode))
1453+
goto out;
1454+
1455+
prt_printf(&buf, ", ");
1456+
1457+
bool have_old_inode = false;
1458+
darray_for_each(inode->inodes, i2)
1459+
if (!i2->whiteout &&
1460+
bch2_snapshot_is_ancestor(c, k.k->p.snapshot, i2->inode.bi_snapshot) &&
1461+
btree_matches_i_mode(iter->btree_id, i2->inode.bi_mode)) {
1462+
prt_printf(&buf, "but found good inode in older snapshot\n");
1463+
bch2_inode_unpacked_to_text(&buf, &i2->inode);
1464+
prt_newline(&buf);
1465+
have_old_inode = true;
1466+
break;
1467+
}
1468+
1469+
struct bkey_s_c k2;
1470+
unsigned nr_keys = 0;
1471+
1472+
prt_printf(&buf, "found keys:\n");
1473+
1474+
for_each_btree_key_max_norestart(trans, iter2, iter->btree_id,
1475+
SPOS(k.k->p.inode, 0, k.k->p.snapshot),
1476+
POS(k.k->p.inode, U64_MAX),
1477+
0, k2, ret) {
1478+
nr_keys++;
1479+
if (nr_keys <= 10) {
1480+
bch2_bkey_val_to_text(&buf, c, k2);
1481+
prt_newline(&buf);
1482+
}
1483+
if (nr_keys >= 100)
1484+
break;
14571485
}
14581486

1459-
if (fsck_err_on(!have_inode,
1460-
trans, key_in_missing_inode,
1461-
"key in missing inode:\n%s",
1462-
(printbuf_reset(&buf),
1463-
bch2_bkey_val_to_text(&buf, c, k), buf.buf)))
1464-
goto delete;
1487+
if (ret)
1488+
goto err;
14651489

1466-
if (fsck_err_on(have_inode && !btree_matches_i_mode(iter->btree_id, i->inode.bi_mode),
1467-
trans, key_in_wrong_inode_type,
1468-
"key for wrong inode mode %o:\n%s",
1469-
i->inode.bi_mode,
1470-
(printbuf_reset(&buf),
1471-
bch2_bkey_val_to_text(&buf, c, k), buf.buf)))
1472-
goto delete;
1490+
if (nr_keys > 100)
1491+
prt_printf(&buf, "found > %u keys for this missing inode\n", nr_keys);
1492+
else if (nr_keys > 10)
1493+
prt_printf(&buf, "found %u keys for this missing inode\n", nr_keys);
1494+
1495+
if (!have_inode) {
1496+
if (fsck_err_on(!have_inode,
1497+
trans, key_in_missing_inode,
1498+
"key in missing inode%s", buf.buf)) {
1499+
/*
1500+
* Maybe a deletion that raced with data move, or something
1501+
* weird like that? But if we know the inode was deleted, or
1502+
* it's just a few keys, we can safely delete them.
1503+
*
1504+
* If it's many keys, we should probably recreate the inode
1505+
*/
1506+
if (have_old_inode || nr_keys <= 2)
1507+
goto delete;
1508+
else
1509+
goto reconstruct;
1510+
}
1511+
} else {
1512+
/*
1513+
* not autofix, this one would be a giant wtf - bit error in the
1514+
* inode corrupting i_mode?
1515+
*
1516+
* may want to try repairing inode instead of deleting
1517+
*/
1518+
if (fsck_err_on(!btree_matches_i_mode(iter->btree_id, i->inode.bi_mode),
1519+
trans, key_in_wrong_inode_type,
1520+
"key for wrong inode mode %o%s",
1521+
i->inode.bi_mode, buf.buf))
1522+
goto delete;
1523+
}
14731524
out:
14741525
err:
14751526
fsck_err:
1527+
bch2_trans_iter_exit(trans, &iter2);
14761528
printbuf_exit(&buf);
14771529
bch_err_fn(c, ret);
14781530
return ret;
14791531
delete:
1532+
/*
1533+
* XXX: print out more info
1534+
* count up extents for this inode, check if we have different inode in
1535+
* an older snapshot version, perhaps decide if we want to reconstitute
1536+
*/
14801537
ret = bch2_btree_delete_at(trans, iter, BTREE_UPDATE_internal_snapshot_node);
14811538
goto out;
1539+
reconstruct:
1540+
ret = reconstruct_inode(trans, iter->btree_id, k.k->p.snapshot, k.k->p.inode) ?:
1541+
bch2_trans_commit(trans, NULL, NULL, BCH_TRANS_COMMIT_no_enospc);
1542+
if (ret)
1543+
goto err;
1544+
1545+
inode->last_pos.inode--;
1546+
ret = bch_err_throw(c, transaction_restart_nested);
1547+
goto out;
14821548
}
14831549

14841550
static int check_i_sectors_notnested(struct btree_trans *trans, struct inode_walker *w)

fs/bcachefs/sb-errors_format.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ enum bch_fsck_flags {
251251
x(deleted_inode_not_unlinked, 214, FSCK_AUTOFIX) \
252252
x(deleted_inode_has_child_snapshots, 288, FSCK_AUTOFIX) \
253253
x(extent_overlapping, 215, 0) \
254-
x(key_in_missing_inode, 216, 0) \
254+
x(key_in_missing_inode, 216, FSCK_AUTOFIX) \
255255
x(key_in_wrong_inode_type, 217, 0) \
256256
x(extent_past_end_of_inode, 218, FSCK_AUTOFIX) \
257257
x(dirent_empty_name, 219, 0) \

0 commit comments

Comments
 (0)