bcachefs: fsck: fix extent past end of inode repair
authorKent Overstreet <kent.overstreet@linux.dev>
Fri, 13 Jun 2025 22:58:57 +0000 (18:58 -0400)
committerKent Overstreet <kent.overstreet@linux.dev>
Mon, 16 Jun 2025 02:11:56 +0000 (22:11 -0400)
Fix the case where we're deleting in a different snapshot and need to
emit a whiteout - that requires a regular BTREE_ITER_filter_snapshots
iterator.

Also, only delete the part of the extent that extents past i_size.

Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
fs/bcachefs/fsck.c

index 28ca07c0e029b232d0dba553e5c6164e9011c024..48810a8e5d4bed7977e21d763a093efe0e22c665 100644 (file)
@@ -1820,18 +1820,39 @@ static int check_extent(struct btree_trans *trans, struct btree_iter *iter,
                            !key_visible_in_snapshot(c, s, i->inode.bi_snapshot, k.k->p.snapshot))
                                continue;
 
-                       if (fsck_err_on(k.k->p.offset > round_up(i->inode.bi_size, block_bytes(c)) >> 9 &&
+                       u64 last_block = round_up(i->inode.bi_size, block_bytes(c)) >> 9;
+
+                       if (fsck_err_on(k.k->p.offset > last_block &&
                                        !bkey_extent_is_reservation(k),
                                        trans, extent_past_end_of_inode,
                                        "extent type past end of inode %llu:%u, i_size %llu\n%s",
                                        i->inode.bi_inum, i->inode.bi_snapshot, i->inode.bi_size,
                                        (bch2_bkey_val_to_text(&buf, c, k), buf.buf))) {
-                               struct btree_iter iter2;
+                               struct bkey_i *whiteout = bch2_trans_kmalloc(trans, sizeof(*whiteout));
+                               ret = PTR_ERR_OR_ZERO(whiteout);
+                               if (ret)
+                                       goto err;
 
-                               bch2_trans_copy_iter(trans, &iter2, iter);
-                               bch2_btree_iter_set_snapshot(trans, &iter2, i->inode.bi_snapshot);
+                               bkey_init(&whiteout->k);
+                               whiteout->k.p = SPOS(k.k->p.inode,
+                                                    last_block,
+                                                    i->inode.bi_snapshot);
+                               bch2_key_resize(&whiteout->k,
+                                               min(KEY_SIZE_MAX & (~0 << c->block_bits),
+                                                   U64_MAX - whiteout->k.p.offset));
+
+
+                               /*
+                                * Need a normal (not BTREE_ITER_all_snapshots)
+                                * iterator, if we're deleting in a different
+                                * snapshot and need to emit a whiteout
+                                */
+                               struct btree_iter iter2;
+                               bch2_trans_iter_init(trans, &iter2, BTREE_ID_extents,
+                                                    bkey_start_pos(&whiteout->k),
+                                                    BTREE_ITER_intent);
                                ret =   bch2_btree_iter_traverse(trans, &iter2) ?:
-                                       bch2_btree_delete_at(trans, &iter2,
+                                       bch2_trans_update(trans, &iter2, whiteout,
                                                BTREE_UPDATE_internal_snapshot_node);
                                bch2_trans_iter_exit(trans, &iter2);
                                if (ret)