bcachefs: opts.casefold_disabled
authorKent Overstreet <kent.overstreet@linux.dev>
Thu, 26 Jun 2025 13:46:35 +0000 (09:46 -0400)
committerKent Overstreet <kent.overstreet@linux.dev>
Tue, 1 Jul 2025 23:33:46 +0000 (19:33 -0400)
Add an option for completely disabling casefolding on a filesystem, as a
workaround for overlayfs.

This should only be needed as a temporary workaround, until the
overlayfs fix arrives.

Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
fs/bcachefs/bcachefs.h
fs/bcachefs/dirent.c
fs/bcachefs/dirent.h
fs/bcachefs/fs.c
fs/bcachefs/fsck.c
fs/bcachefs/inode.c
fs/bcachefs/opts.h
fs/bcachefs/str_hash.c
fs/bcachefs/str_hash.h
fs/bcachefs/super.c

index 8043943cdf6ac591a5ee8ecf40b30da1f3c44d77..ddfacad0f70cf433526b61b44bd1f9183f1731cf 100644 (file)
@@ -863,9 +863,7 @@ struct bch_fs {
        DARRAY(enum bcachefs_metadata_version)
                                incompat_versions_requested;
 
-#ifdef CONFIG_UNICODE
        struct unicode_map      *cf_encoding;
-#endif
 
        struct bch_sb_handle    disk_sb;
 
@@ -1285,4 +1283,13 @@ static inline bool bch2_discard_opt_enabled(struct bch_fs *c, struct bch_dev *ca
                : ca->mi.discard;
 }
 
+static inline bool bch2_fs_casefold_enabled(struct bch_fs *c)
+{
+#ifdef CONFIG_UNICODE
+       return !c->opts.casefold_disabled;
+#else
+       return false;
+#endif
+}
+
 #endif /* _BCACHEFS_H */
index 300f7cc8abdfc161398aef0b05322a7f9da8d388..a18d0f78704de7ffb2c7d8964939270de5327697 100644 (file)
@@ -18,7 +18,9 @@ int bch2_casefold(struct btree_trans *trans, const struct bch_hash_info *info,
 {
        *out_cf = (struct qstr) QSTR_INIT(NULL, 0);
 
-#ifdef CONFIG_UNICODE
+       if (!bch2_fs_casefold_enabled(trans->c))
+               return -EOPNOTSUPP;
+
        unsigned char *buf = bch2_trans_kmalloc(trans, BCH_NAME_MAX + 1);
        int ret = PTR_ERR_OR_ZERO(buf);
        if (ret)
@@ -30,9 +32,6 @@ int bch2_casefold(struct btree_trans *trans, const struct bch_hash_info *info,
 
        *out_cf = (struct qstr) QSTR_INIT(buf, ret);
        return 0;
-#else
-       return -EOPNOTSUPP;
-#endif
 }
 
 static unsigned bch2_dirent_name_bytes(struct bkey_s_c_dirent d)
@@ -231,7 +230,8 @@ void bch2_dirent_to_text(struct printbuf *out, struct bch_fs *c, struct bkey_s_c
        prt_printf(out, " type %s", bch2_d_type_str(d.v->d_type));
 }
 
-int bch2_dirent_init_name(struct bkey_i_dirent *dirent,
+int bch2_dirent_init_name(struct bch_fs *c,
+                         struct bkey_i_dirent *dirent,
                          const struct bch_hash_info *hash_info,
                          const struct qstr *name,
                          const struct qstr *cf_name)
@@ -251,7 +251,9 @@ int bch2_dirent_init_name(struct bkey_i_dirent *dirent,
                       offsetof(struct bch_dirent, d_name) -
                       name->len);
        } else {
-#ifdef CONFIG_UNICODE
+               if (!bch2_fs_casefold_enabled(c))
+                       return -EOPNOTSUPP;
+
                memcpy(&dirent->v.d_cf_name_block.d_names[0], name->name, name->len);
 
                char *cf_out = &dirent->v.d_cf_name_block.d_names[name->len];
@@ -277,9 +279,6 @@ int bch2_dirent_init_name(struct bkey_i_dirent *dirent,
                dirent->v.d_cf_name_block.d_cf_name_len = cpu_to_le16(cf_len);
 
                EBUG_ON(bch2_dirent_get_casefold_name(dirent_i_to_s_c(dirent)).len != cf_len);
-#else
-       return -EOPNOTSUPP;
-#endif
        }
 
        unsigned u64s = dirent_val_u64s(name->len, cf_len);
@@ -313,7 +312,7 @@ struct bkey_i_dirent *bch2_dirent_create_key(struct btree_trans *trans,
        dirent->v.d_type = type;
        dirent->v.d_unused = 0;
 
-       int ret = bch2_dirent_init_name(dirent, hash_info, name, cf_name);
+       int ret = bch2_dirent_init_name(trans->c, dirent, hash_info, name, cf_name);
        if (ret)
                return ERR_PTR(ret);
 
index 70fb0b581221f491bf0d8131e8b70390615033f3..1e17199cc5c756154bf126d1e61665cc4a296766 100644 (file)
@@ -59,7 +59,8 @@ static inline void dirent_copy_target(struct bkey_i_dirent *dst,
        dst->v.d_type = src.v->d_type;
 }
 
-int bch2_dirent_init_name(struct bkey_i_dirent *,
+int bch2_dirent_init_name(struct bch_fs *,
+                         struct bkey_i_dirent *,
                          const struct bch_hash_info *,
                          const struct qstr *,
                          const struct qstr *);
index db24a76563f823ecb3d1c79abc0ec6fc50666193..e54e4f255b221a5a14c1d19127b04485ec72a748 100644 (file)
@@ -722,7 +722,6 @@ static struct dentry *bch2_lookup(struct inode *vdir, struct dentry *dentry,
        if (IS_ERR(inode))
                inode = NULL;
 
-#ifdef CONFIG_UNICODE
        if (!inode && IS_CASEFOLDED(vdir)) {
                /*
                 * Do not cache a negative dentry in casefolded directories
@@ -737,7 +736,6 @@ static struct dentry *bch2_lookup(struct inode *vdir, struct dentry *dentry,
                 */
                return NULL;
        }
-#endif
 
        return d_splice_alias(&inode->v, dentry);
 }
@@ -2566,9 +2564,10 @@ got_sb:
        sb->s_shrink->seeks = 0;
 
 #ifdef CONFIG_UNICODE
-       sb->s_encoding = c->cf_encoding;
-#endif
+       if (bch2_fs_casefold_enabled(c))
+               sb->s_encoding = c->cf_encoding;
        generic_set_sb_d_ops(sb);
+#endif
 
        vinode = bch2_vfs_inode_get(c, BCACHEFS_ROOT_SUBVOL_INUM);
        ret = PTR_ERR_OR_ZERO(vinode);
index 63093def85e35ea617ae68869c3dcbe744b8c315..dbf161e4311aead7db7d876d7494626f3ee0a6dd 100644 (file)
@@ -2302,9 +2302,7 @@ static int check_dirent(struct btree_trans *trans, struct btree_iter *iter,
                *hash_info = bch2_hash_info_init(c, &i->inode);
        dir->first_this_inode = false;
 
-#ifdef CONFIG_UNICODE
        hash_info->cf_encoding = bch2_inode_casefold(c, &i->inode) ? c->cf_encoding : NULL;
-#endif
 
        ret = bch2_str_hash_check_key(trans, s, &bch2_dirent_hash_desc, hash_info,
                                      iter, k, need_second_pass);
index 53e5dc1f6ac1884e4e045272966669ae9f06feb3..ef4cc7395b86b205877b27d508e972581b476e93 100644 (file)
@@ -1265,7 +1265,14 @@ int bch2_inode_set_casefold(struct btree_trans *trans, subvol_inum inum,
 {
        struct bch_fs *c = trans->c;
 
-#ifdef CONFIG_UNICODE
+#ifndef CONFIG_UNICODE
+       bch_err(c, "Cannot use casefolding on a kernel without CONFIG_UNICODE");
+       return -EOPNOTSUPP;
+#endif
+
+       if (c->opts.casefold_disabled)
+               return -EOPNOTSUPP;
+
        int ret = 0;
        /* Not supported on individual files. */
        if (!S_ISDIR(bi->bi_mode))
@@ -1289,10 +1296,6 @@ int bch2_inode_set_casefold(struct btree_trans *trans, subvol_inum inum,
        bi->bi_fields_set |= BIT(Inode_opt_casefold);
 
        return bch2_maybe_propagate_has_case_insensitive(trans, inum, bi);
-#else
-       bch_err(c, "Cannot use casefolding on a kernel without CONFIG_UNICODE");
-       return -EOPNOTSUPP;
-#endif
 }
 
 static noinline int __bch2_inode_rm_snapshot(struct btree_trans *trans, u64 inum, u32 snapshot)
index b0a76bd6d6f54975f4fed7a8dbec3d317e5eb434..63f8e254495cbd04cc290591a10f4fb11647a953 100644 (file)
@@ -234,6 +234,11 @@ enum fsck_err_opts {
          OPT_BOOL(),                                                   \
          BCH_SB_CASEFOLD,              false,                          \
          NULL,         "Dirent lookups are casefolded")                \
+       x(casefold_disabled,                    u8,                     \
+         OPT_FS|OPT_MOUNT,                                             \
+         OPT_BOOL(),                                                   \
+         BCH2_NO_SB_OPT,               false,                          \
+         NULL,         "Disable casefolding filesystem wide")          \
        x(inodes_32bit,                 u8,                             \
          OPT_FS|OPT_INODE|OPT_FORMAT|OPT_MOUNT|OPT_RUNTIME,            \
          OPT_BOOL(),                                                   \
index 71b735a85026741462464fdedae92e03a05d6c1a..3e9f59226bdf2b8d0f2545cfa0c15c2fbe130286 100644 (file)
@@ -38,6 +38,7 @@ static int bch2_fsck_rename_dirent(struct btree_trans *trans,
                                   struct bkey_s_c_dirent old,
                                   bool *updated_before_k_pos)
 {
+       struct bch_fs *c = trans->c;
        struct qstr old_name = bch2_dirent_get_name(old);
        struct bkey_i_dirent *new = bch2_trans_kmalloc(trans, BKEY_U64s_MAX * sizeof(u64));
        int ret = PTR_ERR_OR_ZERO(new);
@@ -60,7 +61,7 @@ static int bch2_fsck_rename_dirent(struct btree_trans *trans,
                                        sprintf(renamed_buf, "%.*s.fsck_renamed-%u",
                                                old_name.len, old_name.name, i));
 
-               ret = bch2_dirent_init_name(new, hash_info, &renamed_name, NULL);
+               ret = bch2_dirent_init_name(c, new, hash_info, &renamed_name, NULL);
                if (ret)
                        return ret;
 
@@ -79,7 +80,7 @@ static int bch2_fsck_rename_dirent(struct btree_trans *trans,
        }
 
        ret = ret ?: bch2_fsck_update_backpointers(trans, s, desc, hash_info, &new->k_i);
-       bch_err_fn(trans->c, ret);
+       bch_err_fn(c, ret);
        return ret;
 }
 
index 79d51aef70aa68797c327925a4a0571852154f74..8979ac2d7a3beddbad7745fafd9b4112f97464eb 100644 (file)
@@ -48,9 +48,7 @@ bch2_hash_info_init(struct bch_fs *c, const struct bch_inode_unpacked *bi)
        struct bch_hash_info info = {
                .inum_snapshot  = bi->bi_snapshot,
                .type           = INODE_STR_HASH(bi),
-#ifdef CONFIG_UNICODE
                .cf_encoding    = bch2_inode_casefold(c, bi) ? c->cf_encoding : NULL,
-#endif
                .siphash_key    = { .k0 = bi->bi_hash_seed }
        };
 
index 69c097ff54e7803d10441cf07badc4dc0ca1d846..c46b1053a02c9024624c7395160f34afc43e4d55 100644 (file)
@@ -1025,15 +1025,17 @@ static struct bch_fs *bch2_fs_alloc(struct bch_sb *sb, struct bch_opts *opts,
        }
 
 #ifdef CONFIG_UNICODE
-       /* Default encoding until we can potentially have more as an option. */
-       c->cf_encoding = utf8_load(BCH_FS_DEFAULT_UTF8_ENCODING);
-       if (IS_ERR(c->cf_encoding)) {
-               printk(KERN_ERR "Cannot load UTF-8 encoding for filesystem. Version: %u.%u.%u",
-                       unicode_major(BCH_FS_DEFAULT_UTF8_ENCODING),
-                       unicode_minor(BCH_FS_DEFAULT_UTF8_ENCODING),
-                       unicode_rev(BCH_FS_DEFAULT_UTF8_ENCODING));
-               ret = -EINVAL;
-               goto err;
+       if (bch2_fs_casefold_enabled(c)) {
+               /* Default encoding until we can potentially have more as an option. */
+               c->cf_encoding = utf8_load(BCH_FS_DEFAULT_UTF8_ENCODING);
+               if (IS_ERR(c->cf_encoding)) {
+                       printk(KERN_ERR "Cannot load UTF-8 encoding for filesystem. Version: %u.%u.%u",
+                              unicode_major(BCH_FS_DEFAULT_UTF8_ENCODING),
+                              unicode_minor(BCH_FS_DEFAULT_UTF8_ENCODING),
+                              unicode_rev(BCH_FS_DEFAULT_UTF8_ENCODING));
+                       ret = -EINVAL;
+                       goto err;
+               }
        }
 #else
        if (c->sb.features & BIT_ULL(BCH_FEATURE_casefolding)) {
@@ -1160,12 +1162,11 @@ int bch2_fs_start(struct bch_fs *c)
 
        print_mount_opts(c);
 
-#ifdef CONFIG_UNICODE
-       bch_info(c, "Using encoding defined by superblock: utf8-%u.%u.%u",
-                unicode_major(BCH_FS_DEFAULT_UTF8_ENCODING),
-                unicode_minor(BCH_FS_DEFAULT_UTF8_ENCODING),
-                unicode_rev(BCH_FS_DEFAULT_UTF8_ENCODING));
-#endif
+       if (c->cf_encoding)
+               bch_info(c, "Using encoding defined by superblock: utf8-%u.%u.%u",
+                        unicode_major(BCH_FS_DEFAULT_UTF8_ENCODING),
+                        unicode_minor(BCH_FS_DEFAULT_UTF8_ENCODING),
+                        unicode_rev(BCH_FS_DEFAULT_UTF8_ENCODING));
 
        if (!bch2_fs_may_start(c))
                return bch_err_throw(c, insufficient_devices_to_start);