bcachefs: Check for casefolded dirents in non casefolded dirs
authorKent Overstreet <kent.overstreet@linux.dev>
Wed, 21 May 2025 04:38:04 +0000 (00:38 -0400)
committerKent Overstreet <kent.overstreet@linux.dev>
Thu, 22 May 2025 00:13:14 +0000 (20:13 -0400)
Check for mismatches between casefold dirents and casefold directories.

A mismatch will cause lookups to fail, as we'll be doing the lookup with
the casefolded name, which won't match the non-casefolded dirent, and
vice versa.

Reported-by: Christopher Snowhill <chris@kode54.net>
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
fs/bcachefs/fsck.c
fs/bcachefs/sb-errors_format.h

index 0cf9fd4c17bc010786641158face412a6acc6437..aaf18708527644691e0c9279704a06dbb0d6115d 100644 (file)
@@ -2190,6 +2190,41 @@ static int check_dirent(struct btree_trans *trans, struct btree_iter *iter,
 
        struct bkey_s_c_dirent d = bkey_s_c_to_dirent(k);
 
+       /* check casefold */
+       if (fsck_err_on(d.v->d_casefold != !!hash_info->cf_encoding,
+                       trans, dirent_casefold_mismatch,
+                       "dirent casefold does not match dir casefold\n%s",
+                       (printbuf_reset(&buf),
+                        bch2_bkey_val_to_text(&buf, c, k),
+                        buf.buf))) {
+               struct qstr name = bch2_dirent_get_name(d);
+               u32 subvol = d.v->d_type == DT_SUBVOL
+                       ? d.v->d_parent_subvol
+                       : 0;
+               u64 target = d.v->d_type == DT_SUBVOL
+                       ? d.v->d_child_subvol
+                       : d.v->d_inum;
+               u64 dir_offset;
+
+               ret =   bch2_hash_delete_at(trans,
+                                           bch2_dirent_hash_desc, hash_info, iter,
+                                           BTREE_UPDATE_internal_snapshot_node) ?:
+                       bch2_dirent_create_snapshot(trans, subvol,
+                                                   d.k->p.inode, d.k->p.snapshot,
+                                                   hash_info,
+                                                   d.v->d_type,
+                                                   &name,
+                                                   target,
+                                                   &dir_offset,
+                                                   BTREE_ITER_with_updates|
+                                                   BTREE_UPDATE_internal_snapshot_node|
+                                                   STR_HASH_must_create) ?:
+                       bch2_trans_commit(trans, NULL, NULL, BCH_TRANS_COMMIT_no_enospc);
+
+               /* might need another check_dirents pass */
+               goto out;
+       }
+
        if (d.v->d_type == DT_SUBVOL) {
                ret = check_dirent_to_subvol(trans, iter, d);
                if (ret)
index 3b69a924086fd57398502ba1b76f72d14c39eaaf..4036a20c6adc2606ac22d9ba96b90bbb42b80a13 100644 (file)
@@ -209,6 +209,7 @@ enum bch_fsck_flags {
        x(subvol_to_missing_root,                               188,    0)              \
        x(subvol_root_wrong_bi_subvol,                          189,    FSCK_AUTOFIX)   \
        x(bkey_in_missing_snapshot,                             190,    0)              \
+       x(bkey_in_deleted_snapshot,                             315,    0)              \
        x(inode_pos_inode_nonzero,                              191,    0)              \
        x(inode_pos_blockdev_range,                             192,    0)              \
        x(inode_alloc_cursor_inode_bad,                         301,    0)              \
@@ -216,6 +217,7 @@ enum bch_fsck_flags {
        x(inode_str_hash_invalid,                               194,    0)              \
        x(inode_v3_fields_start_bad,                            195,    0)              \
        x(inode_snapshot_mismatch,                              196,    0)              \
+       x(snapshot_key_missing_inode_snapshot,                  314,    0)              \
        x(inode_unlinked_but_clean,                             197,    0)              \
        x(inode_unlinked_but_nlink_nonzero,                     198,    0)              \
        x(inode_unlinked_and_not_open,                          281,    0)              \
@@ -237,6 +239,8 @@ enum bch_fsck_flags {
        x(inode_unreachable,                                    210,    FSCK_AUTOFIX)   \
        x(inode_journal_seq_in_future,                          299,    FSCK_AUTOFIX)   \
        x(inode_i_sectors_underflow,                            312,    FSCK_AUTOFIX)   \
+       x(inode_has_case_insensitive_not_set,                   316,    FSCK_AUTOFIX)   \
+       x(inode_parent_has_case_insensitive_not_set,            317,    FSCK_AUTOFIX)   \
        x(vfs_inode_i_blocks_underflow,                         311,    FSCK_AUTOFIX)   \
        x(vfs_inode_i_blocks_not_zero_at_truncate,              313,    FSCK_AUTOFIX)   \
        x(deleted_inode_but_clean,                              211,    FSCK_AUTOFIX)   \
@@ -262,6 +266,7 @@ enum bch_fsck_flags {
        x(dirent_to_overwritten_inode,                          302,    0)              \
        x(dirent_to_missing_subvol,                             230,    0)              \
        x(dirent_to_itself,                                     231,    0)              \
+       x(dirent_casefold_mismatch,                             318,    FSCK_AUTOFIX)   \
        x(quota_type_invalid,                                   232,    0)              \
        x(xattr_val_size_too_small,                             233,    0)              \
        x(xattr_val_size_too_big,                               234,    0)              \
@@ -301,6 +306,7 @@ enum bch_fsck_flags {
        x(btree_ptr_v2_written_0,                               268,    0)              \
        x(subvol_snapshot_bad,                                  269,    0)              \
        x(subvol_inode_bad,                                     270,    0)              \
+       x(subvol_missing,                                       308,    FSCK_AUTOFIX)   \
        x(alloc_key_stripe_sectors_wrong,                       271,    FSCK_AUTOFIX)   \
        x(accounting_mismatch,                                  272,    FSCK_AUTOFIX)   \
        x(accounting_replicas_not_marked,                       273,    0)              \
@@ -322,7 +328,7 @@ enum bch_fsck_flags {
        x(dirent_stray_data_after_cf_name,                      305,    0)              \
        x(rebalance_work_incorrectly_set,                       309,    FSCK_AUTOFIX)   \
        x(rebalance_work_incorrectly_unset,                     310,    FSCK_AUTOFIX)   \
-       x(MAX,                                                  314,    0)
+       x(MAX,                                                  319,    0)
 
 enum bch_sb_error_id {
 #define x(t, n, ...) BCH_FSCK_ERR_##t = n,