x(reflink_p_may_update_opts,    BCH_VERSION(1, 16))             \
        x(inode_depth,                  BCH_VERSION(1, 17))             \
        x(persistent_inode_cursors,     BCH_VERSION(1, 18))             \
-       x(autofix_errors,               BCH_VERSION(1, 19))
+       x(autofix_errors,               BCH_VERSION(1, 19))             \
+       x(directory_size,               BCH_VERSION(1, 20))
 
 enum bcachefs_metadata_version {
        bcachefs_metadata_version_min = 9,
 
        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,
                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);
 
          BIT_ULL(BCH_RECOVERY_PASS_check_allocations),         \
          BCH_FSCK_ERR_accounting_mismatch,                     \
          BCH_FSCK_ERR_accounting_key_replicas_nr_devs_0,       \
-         BCH_FSCK_ERR_accounting_key_junk_at_end)
+         BCH_FSCK_ERR_accounting_key_junk_at_end)              \
+       x(directory_size,                                       \
+         BIT_ULL(BCH_RECOVERY_PASS_check_inodes),              \
+         BCH_FSCK_ERR_directory_size_mismatch)                 \
 
 #define DOWNGRADE_TABLE()                                      \
        x(bucket_stripe_sectors,                                \
 
        x(logged_op_but_clean,                                  283,    FSCK_AUTOFIX)   \
        x(compression_opt_not_marked_in_sb,                     295,    FSCK_AUTOFIX)   \
        x(compression_type_not_marked_in_sb,                    296,    FSCK_AUTOFIX)   \
-       x(MAX,                                                  303,    0)
+       x(directory_size_mismatch,                              303,    FSCK_AUTOFIX)   \
+       x(MAX,                                                  304,    0)
 
 enum bch_sb_error_id {
 #define x(t, n, ...) BCH_FSCK_ERR_##t = n,