]> git.dujemihanovic.xyz Git - linux.git/commitdiff
bcachefs: Check for unlinked inodes with dirents
authorKent Overstreet <kent.overstreet@linux.dev>
Mon, 30 Sep 2024 02:38:04 +0000 (22:38 -0400)
committerKent Overstreet <kent.overstreet@linux.dev>
Sat, 5 Oct 2024 00:25:32 +0000 (20:25 -0400)
link count works differently in bcachefs - it's only nonzero for files
with multiple hardlinks, which means we can also avoid checking it
except for files that are known to have hardlinks.

That means we need a few different checks instead; in particular, we
don't want fsck to delet a file that has a dirent pointing to it.

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

index d2aa6a5bc973e6ad744647a652a31cc335d1a158..09f890539ded4eb47e99f04c0f961058c2264560 100644 (file)
@@ -1029,6 +1029,7 @@ static int check_inode(struct btree_trans *trans,
                       bool full)
 {
        struct bch_fs *c = trans->c;
+       struct printbuf buf = PRINTBUF;
        struct bch_inode_unpacked u;
        bool do_update = false;
        int ret;
@@ -1062,7 +1063,24 @@ static int check_inode(struct btree_trans *trans,
                        trans, inode_snapshot_mismatch,
                        "inodes in different snapshots don't match")) {
                bch_err(c, "repair not implemented yet");
-               return -BCH_ERR_fsck_repair_unimplemented;
+               ret = -BCH_ERR_fsck_repair_unimplemented;
+               goto err_noprint;
+       }
+
+       if (u.bi_dir || u.bi_dir_offset) {
+               ret = check_inode_dirent_inode(trans, &u, &do_update);
+               if (ret)
+                       goto err;
+       }
+
+       if (fsck_err_on(u.bi_dir && (u.bi_flags & BCH_INODE_unlinked),
+                       trans, inode_unlinked_but_has_dirent,
+                       "inode unlinked but has dirent\n%s",
+                       (printbuf_reset(&buf),
+                        bch2_inode_unpacked_to_text(&buf, &u),
+                        buf.buf))) {
+               u.bi_flags &= ~BCH_INODE_unlinked;
+               do_update = true;
        }
 
        if ((u.bi_flags & (BCH_INODE_i_size_dirty|BCH_INODE_unlinked)) &&
@@ -1079,11 +1097,11 @@ static int check_inode(struct btree_trans *trans,
 
                bch_err_msg(c, ret, "in fsck updating inode");
                if (ret)
-                       return ret;
+                       goto err_noprint;
 
                if (!bpos_eq(new_min_pos, POS_MIN))
                        bch2_btree_iter_set_pos(iter, bpos_predecessor(new_min_pos));
-               return 0;
+               goto err_noprint;
        }
 
        if (u.bi_flags & BCH_INODE_unlinked) {
@@ -1100,7 +1118,7 @@ static int check_inode(struct btree_trans *trans,
                         */
                        ret = check_inode_deleted_list(trans, k.k->p);
                        if (ret < 0)
-                               return ret;
+                               goto err_noprint;
 
                        fsck_err_on(!ret,
                                    trans, unlinked_inode_not_on_deleted_list,
@@ -1117,7 +1135,7 @@ static int check_inode(struct btree_trans *trans,
                                      u.bi_inum, u.bi_snapshot)) {
                                ret = bch2_inode_rm_snapshot(trans, u.bi_inum, iter->pos.snapshot);
                                bch_err_msg(c, ret, "in fsck deleting inode");
-                               return ret;
+                               goto err_noprint;
                        }
                }
        }
@@ -1182,12 +1200,6 @@ static int check_inode(struct btree_trans *trans,
                do_update = true;
        }
 
-       if (u.bi_dir || u.bi_dir_offset) {
-               ret = check_inode_dirent_inode(trans, &u, &do_update);
-               if (ret)
-                       goto err;
-       }
-
        if (fsck_err_on(u.bi_parent_subvol &&
                        (u.bi_subvol == 0 ||
                         u.bi_subvol == BCACHEFS_ROOT_SUBVOL),
@@ -1232,11 +1244,13 @@ do_update:
                ret = __bch2_fsck_write_inode(trans, &u, iter->pos.snapshot);
                bch_err_msg(c, ret, "in fsck updating inode");
                if (ret)
-                       return ret;
+                       goto err_noprint;
        }
 err:
 fsck_err:
        bch_err_fn(c, ret);
+err_noprint:
+       printbuf_exit(&buf);
        return ret;
 }
 
@@ -1831,11 +1845,22 @@ static int check_dirent_inode_dirent(struct btree_trans *trans,
                fsck_err_on(S_ISDIR(target->bi_mode),
                            trans, inode_dir_missing_backpointer,
                            "directory with missing backpointer\n%s",
-                           (bch2_bkey_val_to_text(&buf, c, d.s_c),
-                            prt_printf(&buf, "\n  "),
+                           (printbuf_reset(&buf),
+                            bch2_bkey_val_to_text(&buf, c, d.s_c),
+                            prt_printf(&buf, "\n"),
                             bch2_inode_unpacked_to_text(&buf, target),
                             buf.buf));
 
+               fsck_err_on(target->bi_flags & BCH_INODE_unlinked,
+                           trans, inode_unlinked_but_has_dirent,
+                           "inode unlinked but has dirent\n%s",
+                           (printbuf_reset(&buf),
+                            bch2_bkey_val_to_text(&buf, c, d.s_c),
+                            prt_printf(&buf, "\n"),
+                            bch2_inode_unpacked_to_text(&buf, target),
+                            buf.buf));
+
+               target->bi_flags &= ~BCH_INODE_unlinked;
                target->bi_dir          = d.k->p.inode;
                target->bi_dir_offset   = d.k->p.offset;
                return __bch2_fsck_write_inode(trans, target, target_snapshot);
index fc3d31ed5975c8a7ee2d4ef60cb3cff577dac29a..4c8ff4cc17c239d726cd9841ff7c563e2d7c9d10 100644 (file)
@@ -211,6 +211,7 @@ enum bch_fsck_flags {
        x(inode_unlinked_but_clean,                             197,    0)              \
        x(inode_unlinked_but_nlink_nonzero,                     198,    0)              \
        x(inode_unlinked_and_not_open,                          281,    0)              \
+       x(inode_unlinked_but_has_dirent,                        285,    0)              \
        x(inode_checksum_type_invalid,                          199,    0)              \
        x(inode_compression_type_invalid,                       200,    0)              \
        x(inode_subvol_root_but_not_dir,                        201,    0)              \
@@ -296,7 +297,7 @@ enum bch_fsck_flags {
        x(accounting_key_replicas_devs_unsorted,                280,    FSCK_AUTOFIX)   \
        x(accounting_key_version_0,                             282,    FSCK_AUTOFIX)   \
        x(logged_op_but_clean,                                  283,    FSCK_AUTOFIX)   \
-       x(MAX,                                                  285,    0)
+       x(MAX,                                                  286,    0)
 
 enum bch_sb_error_id {
 #define x(t, n, ...) BCH_FSCK_ERR_##t = n,