summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/bcachefs/bcachefs_format.h3
-rw-r--r--fs/bcachefs/fsck.c41
-rw-r--r--fs/bcachefs/sb-downgrade.c5
-rw-r--r--fs/bcachefs/sb-errors_format.h3
4 files changed, 49 insertions, 3 deletions
diff --git a/fs/bcachefs/bcachefs_format.h b/fs/bcachefs/bcachefs_format.h
index 0680930508a3..f70f0108401f 100644
--- a/fs/bcachefs/bcachefs_format.h
+++ b/fs/bcachefs/bcachefs_format.h
@@ -685,7 +685,8 @@ struct bch_sb_field_ext {
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,
diff --git a/fs/bcachefs/fsck.c b/fs/bcachefs/fsck.c
index 3917d75f3c98..8fcf7c8e5ede 100644
--- a/fs/bcachefs/fsck.c
+++ b/fs/bcachefs/fsck.c
@@ -1116,6 +1116,37 @@ 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,
@@ -1304,6 +1335,16 @@ 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);
diff --git a/fs/bcachefs/sb-downgrade.c b/fs/bcachefs/sb-downgrade.c
index 051214fdc735..14f6b6a5fb38 100644
--- a/fs/bcachefs/sb-downgrade.c
+++ b/fs/bcachefs/sb-downgrade.c
@@ -90,7 +90,10 @@
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, \
diff --git a/fs/bcachefs/sb-errors_format.h b/fs/bcachefs/sb-errors_format.h
index 80b6d589808b..0b4fe899209b 100644
--- a/fs/bcachefs/sb-errors_format.h
+++ b/fs/bcachefs/sb-errors_format.h
@@ -313,7 +313,8 @@ enum bch_fsck_flags {
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,