]> www.infradead.org Git - users/hch/misc.git/commitdiff
bcachefs: Fix fsck directory i_size checking
authorKent Overstreet <kent.overstreet@linux.dev>
Thu, 13 Feb 2025 17:43:42 +0000 (12:43 -0500)
committerKent Overstreet <kent.overstreet@linux.dev>
Wed, 19 Feb 2025 18:52:27 +0000 (13:52 -0500)
Error handling was wrong, causing unhandled transaction restart errors.

check_directory_size() was also inefficient, since keys in multiple
snapshots would be iterated over once for every snapshot. Convert it to
the same scheme used for i_sectors and subdir count checking.

Cc: Hongbo Li <lihongbo22@huawei.com>
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
fs/bcachefs/fsck.c
fs/bcachefs/sb-downgrade.c

index 53a421ff136d3759e17df86c3c7e9d46577b6e53..9bf316e7b845d156010d9adbad7130eb97895948 100644 (file)
@@ -823,6 +823,7 @@ struct inode_walker_entry {
        struct bch_inode_unpacked inode;
        u32                     snapshot;
        u64                     count;
+       u64                     i_size;
 };
 
 struct inode_walker {
@@ -910,8 +911,9 @@ found:
        if (k.k->p.snapshot != i->snapshot && !is_whiteout) {
                struct inode_walker_entry new = *i;
 
-               new.snapshot = k.k->p.snapshot;
-               new.count = 0;
+               new.snapshot    = k.k->p.snapshot;
+               new.count       = 0;
+               new.i_size      = 0;
 
                struct printbuf buf = PRINTBUF;
                bch2_bkey_val_to_text(&buf, c, k);
@@ -1116,37 +1118,6 @@ err:
        return ret;
 }
 
-static int check_directory_size(struct btree_trans *trans,
-                               struct bch_inode_unpacked *inode_u,
-                               struct bkey_s_c inode_k, bool *write_inode)
-{
-       struct btree_iter iter;
-       struct bkey_s_c k;
-       u64 new_size = 0;
-       int ret;
-
-       for_each_btree_key_max_norestart(trans, iter, BTREE_ID_dirents,
-                       SPOS(inode_k.k->p.offset, 0, inode_k.k->p.snapshot),
-                       POS(inode_k.k->p.offset, U64_MAX),
-                       0, k, ret) {
-               if (k.k->type != KEY_TYPE_dirent)
-                       continue;
-
-               struct bkey_s_c_dirent dirent = bkey_s_c_to_dirent(k);
-               struct qstr name = bch2_dirent_get_name(dirent);
-
-               new_size += dirent_occupied_size(&name);
-       }
-       bch2_trans_iter_exit(trans, &iter);
-
-       if (!ret && inode_u->bi_size != new_size) {
-               inode_u->bi_size = new_size;
-               *write_inode = true;
-       }
-
-       return ret;
-}
-
 static int check_inode(struct btree_trans *trans,
                       struct btree_iter *iter,
                       struct bkey_s_c k,
@@ -1335,16 +1306,6 @@ static int check_inode(struct btree_trans *trans,
                u.bi_journal_seq = journal_cur_seq(&c->journal);
                do_update = true;
        }
-
-       if (S_ISDIR(u.bi_mode)) {
-               ret = check_directory_size(trans, &u, k, &do_update);
-
-               fsck_err_on(ret,
-                           trans, directory_size_mismatch,
-                           "directory inode %llu:%u with the mismatch directory size",
-                           u.bi_inum, k.k->p.snapshot);
-               ret = 0;
-       }
 do_update:
        if (do_update) {
                ret = __bch2_fsck_write_inode(trans, &u);
@@ -2017,10 +1978,31 @@ fsck_err:
        return ret;
 }
 
-static int check_subdir_count(struct btree_trans *trans, struct inode_walker *w)
+static int check_dir_i_size_notnested(struct btree_trans *trans, struct inode_walker *w)
+{
+       struct bch_fs *c = trans->c;
+       int ret = 0;
+
+       darray_for_each(w->inodes, i)
+               if (fsck_err_on(i->inode.bi_size != i->i_size,
+                               trans, inode_dir_wrong_nlink,
+                               "directory %llu:%u with wrong i_size: got %llu, should be %llu",
+                               w->last_pos.inode, i->snapshot, i->inode.bi_size, i->i_size)) {
+                       i->inode.bi_size = i->i_size;
+                       ret = bch2_fsck_write_inode(trans, &i->inode);
+                       if (ret)
+                               break;
+               }
+fsck_err:
+       bch_err_fn(c, ret);
+       return ret;
+}
+
+static int check_subdir_dirents_count(struct btree_trans *trans, struct inode_walker *w)
 {
        u32 restart_count = trans->restart_count;
        return check_subdir_count_notnested(trans, w) ?:
+               check_dir_i_size_notnested(trans, w) ?:
                trans_was_restarted(trans, restart_count);
 }
 
@@ -2367,7 +2349,7 @@ static int check_dirent(struct btree_trans *trans, struct btree_iter *iter,
                goto out;
 
        if (dir->last_pos.inode != k.k->p.inode && dir->have_inodes) {
-               ret = check_subdir_count(trans, dir);
+               ret = check_subdir_dirents_count(trans, dir);
                if (ret)
                        goto err;
        }
@@ -2457,9 +2439,11 @@ static int check_dirent(struct btree_trans *trans, struct btree_iter *iter,
        if (ret)
                goto err;
 
-       if (d.v->d_type == DT_DIR)
-               for_each_visible_inode(c, s, dir, d.k->p.snapshot, i)
+       for_each_visible_inode(c, s, dir, d.k->p.snapshot, i) {
+               if (d.v->d_type == DT_DIR)
                        i->count++;
+               i->i_size += bkey_bytes(d.k);
+       }
 out:
 err:
 fsck_err:
index 14f6b6a5fb38389de442706a572f99b01107208b..35e07bc8fbd3445e71773cc047e39327dc212d0a 100644 (file)
@@ -92,7 +92,7 @@
          BCH_FSCK_ERR_accounting_key_replicas_nr_devs_0,       \
          BCH_FSCK_ERR_accounting_key_junk_at_end)              \
        x(directory_size,                                       \
-         BIT_ULL(BCH_RECOVERY_PASS_check_inodes),              \
+         BIT_ULL(BCH_RECOVERY_PASS_check_dirents),             \
          BCH_FSCK_ERR_directory_size_mismatch)                 \
 
 #define DOWNGRADE_TABLE()                                      \