From nobody Fri Dec 19 14:21:13 2025 Received: from out-172.mta0.migadu.com (out-172.mta0.migadu.com [91.218.175.172]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E5128175D53 for ; Tue, 20 May 2025 05:16:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.218.175.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747718173; cv=none; b=o3pn1ymAJa/uxBoVCtllzLpdJBtClSiRwpDSvEDELv+sdH5J7A7ADLwJ+7cfRwrXzzbXa+lAcocPGPtxXGHAvLIbjnmMeJzuo+7HgbuDMFGxXbnvU2xWOfIbk5wGOIsn8uTVjnzAyxtPku6rf53psDTcaCQSG6bc8N2QnfG5VcI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747718173; c=relaxed/simple; bh=rWn0KtQV6w7v4hIccCW5M7Zg5TVS1WdH2jUhat5RwJE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=f092yFMFqoKFtN2THY15OHULQ/4rS8hbKlo76MvmPIq5L2Usm3ex93jIxyKpoIWriY0B6IxZpNW2AJkdE1vYeqatEVTgAqw2m7UNXuG1WanMO10m4sAOZzZXyH/iBfTlHpIsPXE37M8ppMDFXNAxOxs95r8PwMR0DjUJ40tNHK0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=K2WqbMBj; arc=none smtp.client-ip=91.218.175.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="K2WqbMBj" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1747718169; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=LhOkd2WseNGnwpcnI2NXO/0YjYpHYyCLAFMWWwe1gw0=; b=K2WqbMBj/eC1Xn6H2EKWBNEEjitdHcjPoHCtgqYVSLZVRE0rA+kAf6YS5pjnFAEwOKvdK7 irfgPB30SbkHAXHuyxQPyZ1rqb0dnQUYzI581DNGoBeBAH8wDdCBC2k3lXLJRsNd7PvvdR ajRHK9cClRwZdGzBnx0gwrzTXcv7YkM= From: Kent Overstreet To: linux-fsdevel@vger.kernel.org, linux-bcachefs@vger.kernel.org, linux-kernel@vger.kernel.org, linux-unionfs@vger.kernel.org Cc: Kent Overstreet , Miklos Szeredi , Amir Goldstein , Alexander Viro , Christian Brauner , Jan Kara Subject: [PATCH 1/6] bcachefs: BCH_INODE_has_case_insensitive Date: Tue, 20 May 2025 01:15:53 -0400 Message-ID: <20250520051600.1903319-2-kent.overstreet@linux.dev> In-Reply-To: <20250520051600.1903319-1-kent.overstreet@linux.dev> References: <20250520051600.1903319-1-kent.overstreet@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Migadu-Flow: FLOW_OUT Content-Type: text/plain; charset="utf-8" Add a flag for tracking whether a directory has case-insensitive descendents - so that overlayfs can disallow mounting, even though the filesystem supports case insensitivity. This is a new on disk format version, with a (cheap) upgrade to ensure the flag is correctly set on existing inodes. Create, rename and fssetxattr are all plumbed to ensure the new flag is set, and we've got new fsck code that hooks into check_inode(0. Signed-off-by: Kent Overstreet --- fs/bcachefs/bcachefs_format.h | 3 +- fs/bcachefs/fs.c | 4 + fs/bcachefs/fsck.c | 10 +- fs/bcachefs/inode.c | 8 +- fs/bcachefs/inode.h | 2 +- fs/bcachefs/inode_format.h | 7 +- fs/bcachefs/namei.c | 166 ++++++++++++++++++++++++++++++++- fs/bcachefs/namei.h | 5 + fs/bcachefs/sb-downgrade.c | 6 +- fs/bcachefs/sb-errors_format.h | 4 +- 10 files changed, 202 insertions(+), 13 deletions(-) diff --git a/fs/bcachefs/bcachefs_format.h b/fs/bcachefs/bcachefs_format.h index 5900ff3715c6..b4a04df5ea95 100644 --- a/fs/bcachefs/bcachefs_format.h +++ b/fs/bcachefs/bcachefs_format.h @@ -699,7 +699,8 @@ struct bch_sb_field_ext { x(casefolding, BCH_VERSION(1, 24)) \ x(extent_flags, BCH_VERSION(1, 25)) \ x(snapshot_deletion_v2, BCH_VERSION(1, 26)) \ - x(fast_device_removal, BCH_VERSION(1, 27)) + x(fast_device_removal, BCH_VERSION(1, 27)) \ + x(inode_has_case_insensitive, BCH_VERSION(1, 28)) =20 enum bcachefs_metadata_version { bcachefs_metadata_version_min =3D 9, diff --git a/fs/bcachefs/fs.c b/fs/bcachefs/fs.c index c863dcae5062..0b5d52895e05 100644 --- a/fs/bcachefs/fs.c +++ b/fs/bcachefs/fs.c @@ -1681,6 +1681,10 @@ static int fssetxattr_inode_update_fn(struct btree_t= rans *trans, bi->bi_casefold =3D s->casefold + 1; bi->bi_fields_set |=3D BIT(Inode_opt_casefold); =20 + ret =3D bch2_maybe_propagate_has_case_insensitive(trans, inode_inum(inod= e), bi); + if (ret) + return ret; + #else printk(KERN_ERR "Cannot use casefolding on a kernel without CONFIG_UNICO= DE\n"); return -EOPNOTSUPP; diff --git a/fs/bcachefs/fsck.c b/fs/bcachefs/fsck.c index a0afb4c2a6f5..338309d0a570 100644 --- a/fs/bcachefs/fsck.c +++ b/fs/bcachefs/fsck.c @@ -264,7 +264,7 @@ static int lookup_lostfound(struct btree_trans *trans, = u32 snapshot, u64 cpu =3D raw_smp_processor_id(); =20 bch2_inode_init_early(c, lostfound); - bch2_inode_init_late(lostfound, now, 0, 0, S_IFDIR|0700, 0, &root_inode); + bch2_inode_init_late(c, lostfound, now, 0, 0, S_IFDIR|0700, 0, &root_inod= e); lostfound->bi_dir =3D root_inode.bi_inum; lostfound->bi_snapshot =3D le32_to_cpu(st.root_snapshot); =20 @@ -543,7 +543,7 @@ static int reconstruct_subvol(struct btree_trans *trans= , u32 snapshotid, u32 sub u64 cpu =3D raw_smp_processor_id(); =20 bch2_inode_init_early(c, &new_inode); - bch2_inode_init_late(&new_inode, bch2_current_time(c), 0, 0, S_IFDIR|075= 5, 0, NULL); + bch2_inode_init_late(c, &new_inode, bch2_current_time(c), 0, 0, S_IFDIR|= 0755, 0, NULL); =20 new_inode.bi_subvol =3D subvolid; =20 @@ -633,7 +633,7 @@ static int reconstruct_inode(struct btree_trans *trans,= enum btree_id btree, u32 =20 struct bch_inode_unpacked new_inode; bch2_inode_init_early(c, &new_inode); - bch2_inode_init_late(&new_inode, bch2_current_time(c), 0, 0, i_mode|0600,= 0, NULL); + bch2_inode_init_late(c, &new_inode, bch2_current_time(c), 0, 0, i_mode|06= 00, 0, NULL); new_inode.bi_size =3D i_size; new_inode.bi_inum =3D inum; new_inode.bi_snapshot =3D snapshot; @@ -1135,6 +1135,10 @@ static int check_inode(struct btree_trans *trans, goto err; } =20 + ret =3D bch2_check_inode_has_case_insensitive(trans, &u, &s->ids, &do_upd= ate); + if (ret) + goto err; + if (u.bi_dir || u.bi_dir_offset) { ret =3D check_inode_dirent_inode(trans, &u, &do_update); if (ret) diff --git a/fs/bcachefs/inode.c b/fs/bcachefs/inode.c index 1d7c3e6b9089..dbcb95ad1e33 100644 --- a/fs/bcachefs/inode.c +++ b/fs/bcachefs/inode.c @@ -907,7 +907,8 @@ void bch2_inode_init_early(struct bch_fs *c, get_random_bytes(&inode_u->bi_hash_seed, sizeof(inode_u->bi_hash_seed)); } =20 -void bch2_inode_init_late(struct bch_inode_unpacked *inode_u, u64 now, +void bch2_inode_init_late(struct bch_fs *c, + struct bch_inode_unpacked *inode_u, u64 now, uid_t uid, gid_t gid, umode_t mode, dev_t rdev, struct bch_inode_unpacked *parent) { @@ -934,6 +935,9 @@ void bch2_inode_init_late(struct bch_inode_unpacked *in= ode_u, u64 now, =20 if (!S_ISDIR(mode)) inode_u->bi_casefold =3D 0; + + if (bch2_inode_casefold(c, inode_u)) + inode_u->bi_flags |=3D BCH_INODE_has_case_insensitive; } =20 void bch2_inode_init(struct bch_fs *c, struct bch_inode_unpacked *inode_u, @@ -941,7 +945,7 @@ void bch2_inode_init(struct bch_fs *c, struct bch_inode= _unpacked *inode_u, struct bch_inode_unpacked *parent) { bch2_inode_init_early(c, inode_u); - bch2_inode_init_late(inode_u, bch2_current_time(c), + bch2_inode_init_late(c, inode_u, bch2_current_time(c), uid, gid, mode, rdev, parent); } =20 diff --git a/fs/bcachefs/inode.h b/fs/bcachefs/inode.h index 851721d88295..f562ba516191 100644 --- a/fs/bcachefs/inode.h +++ b/fs/bcachefs/inode.h @@ -164,7 +164,7 @@ int bch2_fsck_write_inode(struct btree_trans *, struct = bch_inode_unpacked *); =20 void bch2_inode_init_early(struct bch_fs *, struct bch_inode_unpacked *); -void bch2_inode_init_late(struct bch_inode_unpacked *, u64, +void bch2_inode_init_late(struct bch_fs *, struct bch_inode_unpacked *, u6= 4, uid_t, gid_t, umode_t, dev_t, struct bch_inode_unpacked *); void bch2_inode_init(struct bch_fs *, struct bch_inode_unpacked *, diff --git a/fs/bcachefs/inode_format.h b/fs/bcachefs/inode_format.h index 87e193e8ed25..1f00938b1bdc 100644 --- a/fs/bcachefs/inode_format.h +++ b/fs/bcachefs/inode_format.h @@ -129,6 +129,10 @@ enum inode_opt_id { Inode_opt_nr, }; =20 +/* + * BCH_INODE_has_case_insensitive is set if any descendent is case insensi= tive - + * for overlayfs + */ #define BCH_INODE_FLAGS() \ x(sync, 0) \ x(immutable, 1) \ @@ -139,7 +143,8 @@ enum inode_opt_id { x(i_sectors_dirty, 6) \ x(unlinked, 7) \ x(backptr_untrusted, 8) \ - x(has_child_snapshot, 9) + x(has_child_snapshot, 9) \ + x(has_case_insensitive, 10) =20 /* bits 20+ reserved for packed fields below: */ =20 diff --git a/fs/bcachefs/namei.c b/fs/bcachefs/namei.c index 561dc034df21..148615f6952a 100644 --- a/fs/bcachefs/namei.c +++ b/fs/bcachefs/namei.c @@ -11,6 +11,14 @@ =20 #include =20 +static inline subvol_inum parent_inum(subvol_inum inum, struct bch_inode_u= npacked *inode) +{ + return (subvol_inum) { + .subvol =3D inode->bi_parent_subvol ?: inum.subvol, + .inum =3D inode->bi_dir, + }; +} + static inline int is_subdir_for_nlink(struct bch_inode_unpacked *inode) { return S_ISDIR(inode->bi_mode) && !inode->bi_subvol; @@ -49,7 +57,7 @@ int bch2_create_trans(struct btree_trans *trans, =20 if (!(flags & BCH_CREATE_SNAPSHOT)) { /* Normal create path - allocate a new inode: */ - bch2_inode_init_late(new_inode, now, uid, gid, mode, rdev, dir_u); + bch2_inode_init_late(c, new_inode, now, uid, gid, mode, rdev, dir_u); =20 if (flags & BCH_CREATE_TMPFILE) new_inode->bi_flags |=3D BCH_INODE_unlinked; @@ -512,6 +520,13 @@ int bch2_rename_trans(struct btree_trans *trans, goto err; } =20 + ret =3D bch2_maybe_propagate_has_case_insensitive(trans, src_inum, src= _inode_u) ?: + (mode =3D=3D BCH_RENAME_EXCHANGE + ? bch2_maybe_propagate_has_case_insensitive(trans, dst_inum, dst_inode= _u) + : 0); + if (ret) + goto err; + if (is_subdir_for_nlink(src_inode_u)) { src_dir_u->bi_nlink--; dst_dir_u->bi_nlink++; @@ -613,8 +628,7 @@ int bch2_inum_to_path(struct btree_trans *trans, subvol= _inum inum, struct printb goto disconnected; } =20 - inum.subvol =3D inode.bi_parent_subvol ?: inum.subvol; - inum.inum =3D inode.bi_dir; + inum =3D parent_inum(inum, &inode); =20 u32 snapshot; ret =3D bch2_subvolume_get_snapshot(trans, inum.subvol, &snapshot); @@ -849,3 +863,149 @@ int __bch2_check_dirent_target(struct btree_trans *tr= ans, bch_err_fn(c, ret); return ret; } + +/* + * BCH_INODE_has_case_insensitive: + * We have to track whether directories have any descendent directory that= is + * casefolded - for overlayfs: + */ + +static int bch2_propagate_has_case_insensitive(struct btree_trans *trans, = subvol_inum inum) +{ + struct btree_iter iter =3D {}; + int ret =3D 0; + + while (true) { + struct bch_inode_unpacked inode; + ret =3D bch2_inode_peek(trans, &iter, &inode, inum, + BTREE_ITER_intent|BTREE_ITER_with_updates); + if (ret) + break; + + if (inode.bi_flags & BCH_INODE_has_case_insensitive) + break; + + inode.bi_flags |=3D BCH_INODE_has_case_insensitive; + ret =3D bch2_inode_write(trans, &iter, &inode); + if (ret) + break; + + bch2_trans_iter_exit(trans, &iter); + if (subvol_inum_eq(inum, BCACHEFS_ROOT_SUBVOL_INUM)) + break; + + inum =3D parent_inum(inum, &inode); + } + + bch2_trans_iter_exit(trans, &iter); + return ret; +} + +int bch2_maybe_propagate_has_case_insensitive(struct btree_trans *trans, s= ubvol_inum inum, + struct bch_inode_unpacked *inode) +{ + if (!bch2_inode_casefold(trans->c, inode)) + return 0; + + inode->bi_flags |=3D BCH_INODE_has_case_insensitive; + + return bch2_propagate_has_case_insensitive(trans, parent_inum(inum, inode= )); +} + +int bch2_check_inode_has_case_insensitive(struct btree_trans *trans, + struct bch_inode_unpacked *inode, + snapshot_id_list *snapshot_overwrites, + bool *do_update) +{ + struct printbuf buf =3D PRINTBUF; + bool repairing_parents =3D false; + int ret =3D 0; + + if (!S_ISDIR(inode->bi_mode)) { + /* + * Old versions set bi_casefold for non dirs, but that's + * unnecessary and wasteful + */ + if (inode->bi_casefold) { + inode->bi_casefold =3D 0; + *do_update =3D true; + } + return 0; + } + + if (trans->c->sb.version < bcachefs_metadata_version_inode_has_case_insen= sitive) + return 0; + + if (bch2_inode_casefold(trans->c, inode) && + !(inode->bi_flags & BCH_INODE_has_case_insensitive)) { + prt_printf(&buf, "casefolded dir with has_case_insensitive not set\ninum= %llu:%u ", + inode->bi_inum, inode->bi_snapshot); + + ret =3D bch2_inum_snapshot_to_path(trans, inode->bi_inum, inode->bi_snap= shot, + snapshot_overwrites, &buf); + if (ret) + goto err; + + if (fsck_err(trans, inode_has_case_insensitive_not_set, "%s", buf.buf)) { + inode->bi_flags |=3D BCH_INODE_has_case_insensitive; + *do_update =3D true; + } + } + + if (!(inode->bi_flags & BCH_INODE_has_case_insensitive)) + goto out; + + struct bch_inode_unpacked dir =3D *inode; + u32 snapshot =3D dir.bi_snapshot; + + while (!(dir.bi_inum =3D=3D BCACHEFS_ROOT_INO && + dir.bi_subvol =3D=3D BCACHEFS_ROOT_SUBVOL)) { + if (dir.bi_parent_subvol) { + ret =3D bch2_subvolume_get_snapshot(trans, dir.bi_parent_subvol, &snaps= hot); + if (ret) + goto err; + + snapshot_overwrites =3D NULL; + } + + ret =3D bch2_inode_find_by_inum_snapshot(trans, dir.bi_dir, snapshot, &d= ir, 0); + if (ret) + goto err; + + if (!(dir.bi_flags & BCH_INODE_has_case_insensitive)) { + prt_printf(&buf, "parent of casefolded dir with has_case_insensitive no= t set\n"); + + ret =3D bch2_inum_snapshot_to_path(trans, dir.bi_inum, dir.bi_snapshot, + snapshot_overwrites, &buf); + if (ret) + goto err; + + if (fsck_err(trans, inode_parent_has_case_insensitive_not_set, "%s", bu= f.buf)) { + dir.bi_flags |=3D BCH_INODE_has_case_insensitive; + ret =3D __bch2_fsck_write_inode(trans, &dir); + if (ret) + goto err; + } + } + + /* + * We only need to check the first parent, unless we find an + * inconsistency + */ + if (!repairing_parents) + break; + } +out: +err: +fsck_err: + printbuf_exit(&buf); + if (ret) + return ret; + + if (repairing_parents) { + return bch2_trans_commit(trans, NULL, NULL, BCH_TRANS_COMMIT_no_enospc) = ?: + -BCH_ERR_transaction_restart_nested; + } + + return 0; +} diff --git a/fs/bcachefs/namei.h b/fs/bcachefs/namei.h index d4d2d2d69517..ae6ebc2d0785 100644 --- a/fs/bcachefs/namei.h +++ b/fs/bcachefs/namei.h @@ -71,4 +71,9 @@ static inline int bch2_check_dirent_target(struct btree_t= rans *trans, return __bch2_check_dirent_target(trans, dirent_iter, d, target, in_fsck); } =20 +int bch2_maybe_propagate_has_case_insensitive(struct btree_trans *, subvol= _inum, + struct bch_inode_unpacked *); +int bch2_check_inode_has_case_insensitive(struct btree_trans *, struct bch= _inode_unpacked *, + snapshot_id_list *, bool *); + #endif /* _BCACHEFS_NAMEI_H */ diff --git a/fs/bcachefs/sb-downgrade.c b/fs/bcachefs/sb-downgrade.c index 296c6c925386..861fce1630f0 100644 --- a/fs/bcachefs/sb-downgrade.c +++ b/fs/bcachefs/sb-downgrade.c @@ -100,7 +100,11 @@ BCH_FSCK_ERR_ptr_to_missing_backpointer) \ x(stripe_backpointers, \ BIT_ULL(BCH_RECOVERY_PASS_check_extents_to_backpointers),\ - BCH_FSCK_ERR_ptr_to_missing_backpointer) + BCH_FSCK_ERR_ptr_to_missing_backpointer) \ + x(inode_has_case_insensitive, \ + BIT_ULL(BCH_RECOVERY_PASS_check_inodes), \ + BCH_FSCK_ERR_inode_has_case_insensitive_not_set, \ + BCH_FSCK_ERR_inode_parent_has_case_insensitive_not_set) =20 #define DOWNGRADE_TABLE() \ x(bucket_stripe_sectors, \ diff --git a/fs/bcachefs/sb-errors_format.h b/fs/bcachefs/sb-errors_format.h index 68d99ca70634..8f6b02571df0 100644 --- a/fs/bcachefs/sb-errors_format.h +++ b/fs/bcachefs/sb-errors_format.h @@ -239,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) \ @@ -325,7 +327,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, 316, 0) + x(MAX, 318, 0) =20 enum bch_sb_error_id { #define x(t, n, ...) BCH_FSCK_ERR_##t =3D n, --=20 2.49.0 From nobody Fri Dec 19 14:21:13 2025 Received: from out-179.mta0.migadu.com (out-179.mta0.migadu.com [91.218.175.179]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 770F72571D8 for ; Tue, 20 May 2025 05:16:12 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.218.175.179 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747718175; cv=none; b=dQqu089W510kDv2RInqX1MtbQvWIjhIWyebXrQhzx5Tc/fXs0rk4fFoU3ScpTGQP7UXpO2q85BXujM1WUSJtakhmy6q/aCU7roTQMby4OmIRlAWk/OS7Sqm2/r2AVQsqrL61RHJApbZp+K527UZPYmj4iv9GNzqiY2At6hZBagc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747718175; c=relaxed/simple; bh=qIfS6eL6bKdqoJe9XzZ2X0UHibtGO3qcUrzIKnRpzy8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=sv730nlOFUuqYqTFj30njdCgolZT0aD3fZxDitlDfKWeageyo9w3S+lFwu7BaTHS+g+bWLvHUR+GxMA0EhH35WfpynxYpe4ck2QEWtSMMdly3EPaDuyzbNLML8vIcC2BYhS5qv2E+CvWCr3ApmvOlZPWK2WP7BPwt894zCQNyY4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=Rsh9v91e; arc=none smtp.client-ip=91.218.175.179 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="Rsh9v91e" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1747718170; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=Yp+gMyCCEhWIJhMBAJi3K0ln1Cu3dcNJVw79Mw0koIs=; b=Rsh9v91eFzHL862uZejGA1jLKTDEiiuRw0LH7No5kzbp//5KU9n0WSa1w+wSJIrUJhsEUC 3auSG6nPoNZM0hIyreBSXRNPLCSV3BJQgV/Rz810y4NxOeXQhctdDZWdfPVSSvxY+pi2MI AvPlIjNIjmB3eLmjxiaxn6Q8PuHf3cc= From: Kent Overstreet To: linux-fsdevel@vger.kernel.org, linux-bcachefs@vger.kernel.org, linux-kernel@vger.kernel.org, linux-unionfs@vger.kernel.org Cc: Kent Overstreet , Miklos Szeredi , Amir Goldstein , Alexander Viro , Christian Brauner , Jan Kara Subject: [PATCH 2/6] darray: lift from bcachefs Date: Tue, 20 May 2025 01:15:54 -0400 Message-ID: <20250520051600.1903319-3-kent.overstreet@linux.dev> In-Reply-To: <20250520051600.1903319-1-kent.overstreet@linux.dev> References: <20250520051600.1903319-1-kent.overstreet@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Migadu-Flow: FLOW_OUT Content-Type: text/plain; charset="utf-8" dynamic arrays - inspired from CCAN darrays, basically c++ stl vectors. Open coding these is unwieldy. In this series they're used by the new dcache code for exclusion between case insensitive directories and overlayfs, and we've got a lot of open coded dynamic arrays that could be cleaned up with this. Signed-off-by: Kent Overstreet --- MAINTAINERS | 7 +++ fs/bcachefs/Makefile | 1 - fs/bcachefs/btree_node_scan_types.h | 2 +- fs/bcachefs/btree_types.h | 2 +- fs/bcachefs/btree_update.c | 1 + fs/bcachefs/btree_write_buffer_types.h | 2 +- fs/bcachefs/disk_accounting_types.h | 2 +- fs/bcachefs/fsck.c | 2 +- fs/bcachefs/journal_io.h | 2 +- fs/bcachefs/journal_sb.c | 2 +- fs/bcachefs/rcu_pending.c | 3 +- fs/bcachefs/sb-downgrade.c | 3 +- fs/bcachefs/sb-errors_types.h | 2 +- fs/bcachefs/sb-members.h | 3 +- fs/bcachefs/snapshot_types.h | 3 +- fs/bcachefs/subvolume.h | 1 - fs/bcachefs/thread_with_file_types.h | 2 +- fs/bcachefs/util.h | 28 +--------- {fs/bcachefs =3D> include/linux}/darray.h | 70 ++++++++++++++----------- include/linux/darray_types.h | 33 ++++++++++++ lib/Makefile | 2 +- {fs/bcachefs =3D> lib}/darray.c | 9 +++- 22 files changed, 106 insertions(+), 76 deletions(-) rename {fs/bcachefs =3D> include/linux}/darray.h (64%) create mode 100644 include/linux/darray_types.h rename {fs/bcachefs =3D> lib}/darray.c (75%) diff --git a/MAINTAINERS b/MAINTAINERS index 3cbf9ac0d83f..ae460234af14 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6515,6 +6515,13 @@ F: net/ax25/ax25_out.c F: net/ax25/ax25_timer.c F: net/ax25/sysctl_net_ax25.c =20 +DARRAY +M: Kent Overstreet +L: linux-bcachefs@vger.kernel.org +S: Maintained +F: include/linux/darray.h +F: include/linux/darray_types.h + DATA ACCESS MONITOR M: SeongJae Park L: damon@lists.linux.dev diff --git a/fs/bcachefs/Makefile b/fs/bcachefs/Makefile index 93c8ee5425c8..d71621711cfa 100644 --- a/fs/bcachefs/Makefile +++ b/fs/bcachefs/Makefile @@ -28,7 +28,6 @@ bcachefs-y :=3D \ checksum.o \ clock.o \ compress.o \ - darray.o \ data_update.o \ debug.o \ dirent.o \ diff --git a/fs/bcachefs/btree_node_scan_types.h b/fs/bcachefs/btree_node_s= can_types.h index 2811b6857c97..422d49a5c57c 100644 --- a/fs/bcachefs/btree_node_scan_types.h +++ b/fs/bcachefs/btree_node_scan_types.h @@ -2,7 +2,7 @@ #ifndef _BCACHEFS_BTREE_NODE_SCAN_TYPES_H #define _BCACHEFS_BTREE_NODE_SCAN_TYPES_H =20 -#include "darray.h" +#include =20 struct found_btree_node { bool range_updated:1; diff --git a/fs/bcachefs/btree_types.h b/fs/bcachefs/btree_types.h index 6a0b82886581..7020ceef50e7 100644 --- a/fs/bcachefs/btree_types.h +++ b/fs/bcachefs/btree_types.h @@ -2,13 +2,13 @@ #ifndef _BCACHEFS_BTREE_TYPES_H #define _BCACHEFS_BTREE_TYPES_H =20 +#include #include #include =20 #include "bbpos_types.h" #include "btree_key_cache_types.h" #include "buckets_types.h" -#include "darray.h" #include "errcode.h" #include "journal_types.h" #include "replicas_types.h" diff --git a/fs/bcachefs/btree_update.c b/fs/bcachefs/btree_update.c index 20fba8d17431..afd05c3dfd03 100644 --- a/fs/bcachefs/btree_update.c +++ b/fs/bcachefs/btree_update.c @@ -14,6 +14,7 @@ #include "snapshot.h" #include "trace.h" =20 +#include #include =20 static inline int btree_insert_entry_cmp(const struct btree_insert_entry *= l, diff --git a/fs/bcachefs/btree_write_buffer_types.h b/fs/bcachefs/btree_wri= te_buffer_types.h index e9e76e20f43b..d39d163c6ea9 100644 --- a/fs/bcachefs/btree_write_buffer_types.h +++ b/fs/bcachefs/btree_write_buffer_types.h @@ -2,7 +2,7 @@ #ifndef _BCACHEFS_BTREE_WRITE_BUFFER_TYPES_H #define _BCACHEFS_BTREE_WRITE_BUFFER_TYPES_H =20 -#include "darray.h" +#include #include "journal_types.h" =20 #define BTREE_WRITE_BUFERED_VAL_U64s_MAX 4 diff --git a/fs/bcachefs/disk_accounting_types.h b/fs/bcachefs/disk_account= ing_types.h index b1982131b206..242b3270cd5c 100644 --- a/fs/bcachefs/disk_accounting_types.h +++ b/fs/bcachefs/disk_accounting_types.h @@ -2,7 +2,7 @@ #ifndef _BCACHEFS_DISK_ACCOUNTING_TYPES_H #define _BCACHEFS_DISK_ACCOUNTING_TYPES_H =20 -#include "darray.h" +#include =20 struct accounting_mem_entry { struct bpos pos; diff --git a/fs/bcachefs/fsck.c b/fs/bcachefs/fsck.c index 338309d0a570..cc56c706d2b0 100644 --- a/fs/bcachefs/fsck.c +++ b/fs/bcachefs/fsck.c @@ -6,7 +6,6 @@ #include "btree_cache.h" #include "btree_update.h" #include "buckets.h" -#include "darray.h" #include "dirent.h" #include "error.h" #include "fs.h" @@ -21,6 +20,7 @@ #include "xattr.h" =20 #include +#include #include /* struct qstr */ =20 static int dirent_points_to_inode_nowarn(struct bkey_s_c_dirent d, diff --git a/fs/bcachefs/journal_io.h b/fs/bcachefs/journal_io.h index 12b39fcb4424..ffa543424e9e 100644 --- a/fs/bcachefs/journal_io.h +++ b/fs/bcachefs/journal_io.h @@ -2,7 +2,7 @@ #ifndef _BCACHEFS_JOURNAL_IO_H #define _BCACHEFS_JOURNAL_IO_H =20 -#include "darray.h" +#include =20 void bch2_journal_pos_from_member_info_set(struct bch_fs *); void bch2_journal_pos_from_member_info_resume(struct bch_fs *); diff --git a/fs/bcachefs/journal_sb.c b/fs/bcachefs/journal_sb.c index 62b910f2fb27..68b960e08f12 100644 --- a/fs/bcachefs/journal_sb.c +++ b/fs/bcachefs/journal_sb.c @@ -2,8 +2,8 @@ =20 #include "bcachefs.h" #include "journal_sb.h" -#include "darray.h" =20 +#include #include =20 /* BCH_SB_FIELD_journal: */ diff --git a/fs/bcachefs/rcu_pending.c b/fs/bcachefs/rcu_pending.c index bef2aa1b8bcd..2cf3d55d0bbc 100644 --- a/fs/bcachefs/rcu_pending.c +++ b/fs/bcachefs/rcu_pending.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 #define pr_fmt(fmt) "%s() " fmt "\n", __func__ =20 +#include #include #include #include @@ -9,8 +10,6 @@ #include =20 #include "rcu_pending.h" -#include "darray.h" -#include "util.h" =20 #define static_array_for_each(_a, _i) \ for (typeof(&(_a)[0]) _i =3D _a; \ diff --git a/fs/bcachefs/sb-downgrade.c b/fs/bcachefs/sb-downgrade.c index 861fce1630f0..d4e22c43eeb2 100644 --- a/fs/bcachefs/sb-downgrade.c +++ b/fs/bcachefs/sb-downgrade.c @@ -6,12 +6,13 @@ */ =20 #include "bcachefs.h" -#include "darray.h" #include "recovery_passes.h" #include "sb-downgrade.h" #include "sb-errors.h" #include "super-io.h" =20 +#include + #define RECOVERY_PASS_ALL_FSCK BIT_ULL(63) =20 /* diff --git a/fs/bcachefs/sb-errors_types.h b/fs/bcachefs/sb-errors_types.h index 40325239c3b0..3b28871d23ed 100644 --- a/fs/bcachefs/sb-errors_types.h +++ b/fs/bcachefs/sb-errors_types.h @@ -2,7 +2,7 @@ #ifndef _BCACHEFS_SB_ERRORS_TYPES_H #define _BCACHEFS_SB_ERRORS_TYPES_H =20 -#include "darray.h" +#include =20 struct bch_sb_error_entry_cpu { u64 id:16, diff --git a/fs/bcachefs/sb-members.h b/fs/bcachefs/sb-members.h index 6bd9b86aee5b..09b751a75020 100644 --- a/fs/bcachefs/sb-members.h +++ b/fs/bcachefs/sb-members.h @@ -2,10 +2,11 @@ #ifndef _BCACHEFS_SB_MEMBERS_H #define _BCACHEFS_SB_MEMBERS_H =20 -#include "darray.h" #include "bkey_types.h" #include "enumerated_ref.h" =20 +#include + extern char * const bch2_member_error_strs[]; =20 static inline struct bch_member * diff --git a/fs/bcachefs/snapshot_types.h b/fs/bcachefs/snapshot_types.h index 0ab698f13e5c..31f96d1cf5f4 100644 --- a/fs/bcachefs/snapshot_types.h +++ b/fs/bcachefs/snapshot_types.h @@ -3,9 +3,10 @@ #define _BCACHEFS_SNAPSHOT_TYPES_H =20 #include "bbpos_types.h" -#include "darray.h" #include "subvolume_types.h" =20 +#include + typedef DARRAY(u32) snapshot_id_list; =20 #define IS_ANCESTOR_BITMAP 128 diff --git a/fs/bcachefs/subvolume.h b/fs/bcachefs/subvolume.h index 075f55e25c70..771ade03a348 100644 --- a/fs/bcachefs/subvolume.h +++ b/fs/bcachefs/subvolume.h @@ -2,7 +2,6 @@ #ifndef _BCACHEFS_SUBVOLUME_H #define _BCACHEFS_SUBVOLUME_H =20 -#include "darray.h" #include "subvolume_types.h" =20 int bch2_check_subvols(struct bch_fs *); diff --git a/fs/bcachefs/thread_with_file_types.h b/fs/bcachefs/thread_with= _file_types.h index f4d484d44f63..254b8493ec4b 100644 --- a/fs/bcachefs/thread_with_file_types.h +++ b/fs/bcachefs/thread_with_file_types.h @@ -2,7 +2,7 @@ #ifndef _BCACHEFS_THREAD_WITH_FILE_TYPES_H #define _BCACHEFS_THREAD_WITH_FILE_TYPES_H =20 -#include "darray.h" +#include =20 struct stdio_buf { spinlock_t lock; diff --git a/fs/bcachefs/util.h b/fs/bcachefs/util.h index 25cf61ebd40c..3985636cbc24 100644 --- a/fs/bcachefs/util.h +++ b/fs/bcachefs/util.h @@ -5,24 +5,24 @@ #include #include #include +#include #include #include #include #include -#include #include #include #include #include #include #include +#include #include #include #include =20 #include "mean_and_variance.h" =20 -#include "darray.h" #include "time_stats.h" =20 struct closure; @@ -552,30 +552,6 @@ static inline void memset_u64s_tail(void *s, int c, un= signed bytes) memset(s + bytes, c, rem); } =20 -/* just the memmove, doesn't update @_nr */ -#define __array_insert_item(_array, _nr, _pos) \ - memmove(&(_array)[(_pos) + 1], \ - &(_array)[(_pos)], \ - sizeof((_array)[0]) * ((_nr) - (_pos))) - -#define array_insert_item(_array, _nr, _pos, _new_item) \ -do { \ - __array_insert_item(_array, _nr, _pos); \ - (_nr)++; \ - (_array)[(_pos)] =3D (_new_item); \ -} while (0) - -#define array_remove_items(_array, _nr, _pos, _nr_to_remove) \ -do { \ - (_nr) -=3D (_nr_to_remove); \ - memmove(&(_array)[(_pos)], \ - &(_array)[(_pos) + (_nr_to_remove)], \ - sizeof((_array)[0]) * ((_nr) - (_pos))); \ -} while (0) - -#define array_remove_item(_array, _nr, _pos) \ - array_remove_items(_array, _nr, _pos, 1) - static inline void __move_gap(void *array, size_t element_size, size_t nr, size_t size, size_t old_gap, size_t new_gap) diff --git a/fs/bcachefs/darray.h b/include/linux/darray.h similarity index 64% rename from fs/bcachefs/darray.h rename to include/linux/darray.h index 50ec3decfe8c..7a0c0159b319 100644 --- a/fs/bcachefs/darray.h +++ b/include/linux/darray.h @@ -1,45 +1,26 @@ /* SPDX-License-Identifier: GPL-2.0 */ -#ifndef _BCACHEFS_DARRAY_H -#define _BCACHEFS_DARRAY_H +/* + * (C) 2022-2024 Kent Overstreet + */ +#ifndef _LINUX_DARRAY_H +#define _LINUX_DARRAY_H =20 /* - * Dynamic arrays: + * Dynamic arrays * * Inspired by CCAN's darray */ =20 +#include #include =20 -#define DARRAY_PREALLOCATED(_type, _nr) \ -struct { \ - size_t nr, size; \ - _type *data; \ - _type preallocated[_nr]; \ -} - -#define DARRAY(_type) DARRAY_PREALLOCATED(_type, 0) - -typedef DARRAY(char) darray_char; -typedef DARRAY(char *) darray_str; -typedef DARRAY(const char *) darray_const_str; - -typedef DARRAY(u8) darray_u8; -typedef DARRAY(u16) darray_u16; -typedef DARRAY(u32) darray_u32; -typedef DARRAY(u64) darray_u64; - -typedef DARRAY(s8) darray_s8; -typedef DARRAY(s16) darray_s16; -typedef DARRAY(s32) darray_s32; -typedef DARRAY(s64) darray_s64; - -int __bch2_darray_resize_noprof(darray_char *, size_t, size_t, gfp_t); +int __darray_resize_slowpath(darray_char *, size_t, size_t, gfp_t); =20 #define __bch2_darray_resize(...) alloc_hooks(__bch2_darray_resize_noprof(= __VA_ARGS__)) =20 #define __darray_resize(_d, _element_size, _new_size, _gfp) \ (unlikely((_new_size) > (_d)->size) \ - ? __bch2_darray_resize((_d), (_element_size), (_new_size), (_gfp))\ + ? __darray_resize_slowpath((_d), (_element_size), (_new_size), (_gfp))\ : 0) =20 #define darray_resize_gfp(_d, _new_size, _gfp) \ @@ -74,6 +55,28 @@ int __bch2_darray_resize_noprof(darray_char *, size_t, s= ize_t, gfp_t); #define darray_first(_d) ((_d).data[0]) #define darray_last(_d) ((_d).data[(_d).nr - 1]) =20 +/* Insert/remove items into the middle of a darray: */ + +#define array_insert_item(_array, _nr, _pos, _new_item) \ +do { \ + memmove(&(_array)[(_pos) + 1], \ + &(_array)[(_pos)], \ + sizeof((_array)[0]) * ((_nr) - (_pos))); \ + (_nr)++; \ + (_array)[(_pos)] =3D (_new_item); \ +} while (0) + +#define array_remove_items(_array, _nr, _pos, _nr_to_remove) \ +do { \ + (_nr) -=3D (_nr_to_remove); \ + memmove(&(_array)[(_pos)], \ + &(_array)[(_pos) + (_nr_to_remove)], \ + sizeof((_array)[0]) * ((_nr) - (_pos))); \ +} while (0) + +#define array_remove_item(_array, _nr, _pos) \ + array_remove_items(_array, _nr, _pos, 1) + #define darray_insert_item(_d, pos, _item) \ ({ \ size_t _pos =3D (pos); \ @@ -84,10 +87,15 @@ int __bch2_darray_resize_noprof(darray_char *, size_t, = size_t, gfp_t); _ret; \ }) =20 +#define darray_remove_items(_d, _pos, _nr_to_remove) \ + array_remove_items((_d)->data, (_d)->nr, (_pos) - (_d)->data, _nr_to_remo= ve) + #define darray_remove_item(_d, _pos) \ - array_remove_item((_d)->data, (_d)->nr, (_pos) - (_d)->data) + darray_remove_items(_d, _pos, 1) + +/* Iteration: */ =20 -#define __darray_for_each(_d, _i) \ +#define __darray_for_each(_d, _i) \ for ((_i) =3D (_d).data; _i < (_d).data + (_d).nr; _i++) =20 #define darray_for_each(_d, _i) \ @@ -111,4 +119,4 @@ do { \ darray_init(_d); \ } while (0) =20 -#endif /* _BCACHEFS_DARRAY_H */ +#endif /* _LINUX_DARRAY_H */ diff --git a/include/linux/darray_types.h b/include/linux/darray_types.h new file mode 100644 index 000000000000..c55484487905 --- /dev/null +++ b/include/linux/darray_types.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * (C) 2022-2024 Kent Overstreet + */ +#ifndef _LINUX_DARRAY_TYPES_H +#define _LINUX_DARRAY_TYPES_H + +#include + +#define DARRAY_PREALLOCATED(_type, _nr) \ +struct { \ + size_t nr, size; \ + _type *data; \ + _type preallocated[_nr]; \ +} + +#define DARRAY(_type) DARRAY_PREALLOCATED(_type, 0) + +typedef DARRAY(char) darray_char; +typedef DARRAY(char *) darray_str; +typedef DARRAY(const char *) darray_const_str; + +typedef DARRAY(u8) darray_u8; +typedef DARRAY(u16) darray_u16; +typedef DARRAY(u32) darray_u32; +typedef DARRAY(u64) darray_u64; + +typedef DARRAY(s8) darray_s8; +typedef DARRAY(s16) darray_s16; +typedef DARRAY(s32) darray_s32; +typedef DARRAY(s64) darray_s64; + +#endif /* _LINUX_DARRAY_TYPES_H */ diff --git a/lib/Makefile b/lib/Makefile index f07b24ce1b3f..4f053d577bf3 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -56,7 +56,7 @@ obj-y +=3D bcd.o sort.o parser.o debug_locks.o random32.o= \ bsearch.o find_bit.o llist.o lwq.o memweight.o kfifo.o \ percpu-refcount.o rhashtable.o base64.o \ once.o refcount.o rcuref.o usercopy.o errseq.o bucket_locks.o \ - generic-radix-tree.o bitmap-str.o + generic-radix-tree.o bitmap-str.o darray.o obj-y +=3D string_helpers.o obj-y +=3D hexdump.o obj-$(CONFIG_TEST_HEXDUMP) +=3D test_hexdump.o diff --git a/fs/bcachefs/darray.c b/lib/darray.c similarity index 75% rename from fs/bcachefs/darray.c rename to lib/darray.c index e86d36d23e9e..1d3820a43e14 100644 --- a/fs/bcachefs/darray.c +++ b/lib/darray.c @@ -1,11 +1,15 @@ // SPDX-License-Identifier: GPL-2.0 +/* + * (C) 2022-2024 Kent Overstreet + */ =20 +#include +#include #include #include #include -#include "darray.h" =20 -int __bch2_darray_resize_noprof(darray_char *d, size_t element_size, size_= t new_size, gfp_t gfp) +int __darray_resize_slowpath(darray_char *d, size_t element_size, size_t n= ew_size, gfp_t gfp) { if (new_size > d->size) { new_size =3D roundup_pow_of_two(new_size); @@ -36,3 +40,4 @@ int __bch2_darray_resize_noprof(darray_char *d, size_t el= ement_size, size_t new_ =20 return 0; } +EXPORT_SYMBOL_GPL(__darray_resize_slowpath); --=20 2.49.0 From nobody Fri Dec 19 14:21:13 2025 Received: from out-188.mta0.migadu.com (out-188.mta0.migadu.com [91.218.175.188]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5573825B1F9 for ; Tue, 20 May 2025 05:16:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.218.175.188 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747718176; cv=none; b=tp1xVUN6+RlJFpLRsCHO857SzGkGw4JslAk0nDVGoGdXQ1dYxsszKT/YsKrj2aG9UNbBgIaveoo8+Zr+htr6r5sQ7O+CKsUchOtIhYNU/MN+zdq2E8DtdAGB5WaZImrpPXelJHwUd6uPngNoxkIIIcwO6ADSDB3y1SKr7S9BKSs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747718176; c=relaxed/simple; bh=01omtfy4X5jE3LXfqVXbJmQ6mr3USqlmpq4zi+mvzFg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Cn1aP+pGYaXL/1XXZ/v1hQhHgFDXieui18OMQ/PfIFL9urXTmlTKNzJH4DiJRUVI3H+yXByp3C8wTwiviyx/Pf4FBbo+KVgbmq4XCl/WOrhqSkEzu4SQVRi48b9U6moi2ReWAR4L4ufGr/YDWIqL/F2bWLEbH9HuTQi3H8HS/5Y= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=fmRsB82G; arc=none smtp.client-ip=91.218.175.188 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="fmRsB82G" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1747718172; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=+htYYKrqbHWauEKNKRRY4W/ntjUCZcSdjtiG28wmDtA=; b=fmRsB82Ghtc8jo1wrGIJAilW8J61sUqjXEr6sxY8wCTQwC5s66I7kTyEDQFNlE3j/sKJGh ZszCInCgvKY6hUlYPAYTzhhGtnvtPV8FgCYCUi1+h8KeavkOVBZAvaW9PnsIeoRs31lB/W usxJmMSTtCdEDn4C/eK67q8K9DOJdDM= From: Kent Overstreet To: linux-fsdevel@vger.kernel.org, linux-bcachefs@vger.kernel.org, linux-kernel@vger.kernel.org, linux-unionfs@vger.kernel.org Cc: Kent Overstreet , Miklos Szeredi , Amir Goldstein , Alexander Viro , Christian Brauner , Jan Kara Subject: [PATCH 3/6] fs: SB_CASEFOLD Date: Tue, 20 May 2025 01:15:55 -0400 Message-ID: <20250520051600.1903319-4-kent.overstreet@linux.dev> In-Reply-To: <20250520051600.1903319-1-kent.overstreet@linux.dev> References: <20250520051600.1903319-1-kent.overstreet@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Migadu-Flow: FLOW_OUT Content-Type: text/plain; charset="utf-8" Add a new flag indicating that a filesystem supports casefolding. This is better than overlayfs's current method of checking for sb->s_encoding, which isn't reliable - XFS implements ASCII casefolding, so it won't be set there. It's needed for overlayfs and the new dcache exclusion code to check "should we allow union mounts on this filesystem even though the dcache hash/compare ops are set? and do we need the new exclusion?". Cc: Alexander Viro Cc: Christian Brauner Cc: Jan Kara Signed-off-by: Kent Overstreet --- fs/libfs.c | 1 + include/linux/fs.h | 1 + 2 files changed, 2 insertions(+) diff --git a/fs/libfs.c b/fs/libfs.c index 6393d7c49ee6..d9f6ed6ec4ea 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -1952,6 +1952,7 @@ void generic_set_sb_d_ops(struct super_block *sb) { #if IS_ENABLED(CONFIG_UNICODE) if (sb->s_encoding) { + sb->s_flags |=3D SB_CASEFOLD; sb->s_d_op =3D &generic_ci_dentry_ops; return; } diff --git a/include/linux/fs.h b/include/linux/fs.h index 016b0fe1536e..ba942cd2fea1 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1228,6 +1228,7 @@ extern int send_sigurg(struct file *file); #define SB_SYNCHRONOUS BIT(4) /* Writes are synced at once */ #define SB_MANDLOCK BIT(6) /* Allow mandatory locks on an FS */ #define SB_DIRSYNC BIT(7) /* Directory modifications are synchronous = */ +#define SB_CASEFOLD BIT(8) /* Superblock supports casefolding */ #define SB_NOATIME BIT(10) /* Do not update access times. */ #define SB_NODIRATIME BIT(11) /* Do not update directory access times */ #define SB_SILENT BIT(15) --=20 2.49.0 From nobody Fri Dec 19 14:21:13 2025 Received: from out-170.mta0.migadu.com (out-170.mta0.migadu.com [91.218.175.170]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C258825B69D; Tue, 20 May 2025 05:16:15 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.218.175.170 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747718178; cv=none; b=GBUA68RrChKzZt1fp/sZ4F5CYjZ/qoPASdD00ArMHvPhLytfegzS5XA/hEr/0VSvQ3QrpoYzGKWt9fKYREe59kRe0uAAKmsyIOXY5nroFb4UHCy5AInXOXel5+yVHaRxtFs5TkfaQI3vMsD5fk1rLkO1ks3v0B6ofkcFAStqRzg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747718178; c=relaxed/simple; bh=A9eJZ5jQiNjme2efRTyoSl9kLsZnk7LxjkDkCvQ0vkM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=DETJGPRjtkOZ+5+65ad+AUhLovw5qPS8MzJ0TO8Z67fQcdroc8ghaF3JMKi59vNKNSlHvQQ0twxHqE7bkYLSHeh/GmrIdypaWqBSm1jprBzRyy96qPPO9r4EqRYy8EiipilTvJHhZ/vki3ivYZ8ISvkvlOIdNOMsn+z5lWeiBhE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=WaqVG+oI; arc=none smtp.client-ip=91.218.175.170 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="WaqVG+oI" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1747718173; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=L94gNOs1zA4JaSEM3Ng1trC1ToVPa//1ULc22IZviDk=; b=WaqVG+oIDhqyk1rF1Pk7+8U4rkTyUhC6VXGhhIMyvy4bRww/xIEjFHEfrYYctNdseflZ/L jlcwht3+T2RRGTtfbfKYaj3jgla3zZNVXYfxpb1o5W6lFmgHY2vWtqVDNeKaUNBfXhL91y mqYllNgKqSeGBqRXZTLpTl4dfS4TnDE= From: Kent Overstreet To: linux-fsdevel@vger.kernel.org, linux-bcachefs@vger.kernel.org, linux-kernel@vger.kernel.org, linux-unionfs@vger.kernel.org Cc: Kent Overstreet , Miklos Szeredi , Amir Goldstein , Alexander Viro , Christian Brauner , Jan Kara Subject: [PATCH 4/6] fs: dcache locking for exlusion between overlayfs, casefolding Date: Tue, 20 May 2025 01:15:56 -0400 Message-ID: <20250520051600.1903319-5-kent.overstreet@linux.dev> In-Reply-To: <20250520051600.1903319-1-kent.overstreet@linux.dev> References: <20250520051600.1903319-1-kent.overstreet@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Migadu-Flow: FLOW_OUT Content-Type: text/plain; charset="utf-8" Allow casefolding to be used on the same filesystem as overlayfs. Overlayfs cannot handle a directory that has been casefolding, so this implements locking between enabling casefolding and overlayfs. The underlying filesystem mus also track, for each directory, whether casefolding has been enabled on any subdirectory, and provide this information to the VFS with the S_NO_CASEFOLD flag. Since we can't inflate struct dentry for this, add a separate rhashtable that functions as a per-dentry "casefolding disabled" ref. New helpers, for overlayfs: - d_casefold_disabled_put() - d_casefold_disabled_get() These acquire and release refs that check that S_NO_CASEFOLD is set on a directory, and prevent it from being cleared while the ref exists. For the underlying filesystem: - d_casefolding_enable() - d_casefolding_enable_commit() This is for the underlying filesystem that implements casefolding, in settar and rename. d_casefolding_enable() checks for conflicting refs held by overlayfs and acquires refs while the rename or setatr is being done, d_casefolding_enable_commit() releases those refs and clears the S_NO_CASEFOLD flag. Implementation - We can't inflate struct dentry for this, so references are tracked in a separate rhashtable. To guard against races with rename(), d_casefolding_enable() must take refs (which in turn call dget()) on all dentries with inodes that need S_NO_CASEFOLD flipped; these dget() refs are released by commit(). d_casefold_disabled_get/put take dget() refs for the duration of the union mount, but this is unchanged because unionfs already holds a path ref for the duration of the mount. Cc: Alexander Viro Cc: Christian Brauner Cc: Jan Kara Signed-off-by: Kent Overstreet --- fs/dcache.c | 177 +++++++++++++++++++++++++++++++++++++++++ include/linux/dcache.h | 10 +++ include/linux/fs.h | 3 + 3 files changed, 190 insertions(+) diff --git a/fs/dcache.c b/fs/dcache.c index bd5aa136153a..971dd90bce26 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -32,6 +32,9 @@ #include #include #include +#include +#include +#include #include "internal.h" #include "mount.h" =20 @@ -3141,6 +3144,176 @@ ino_t d_parent_ino(struct dentry *dentry) } EXPORT_SYMBOL(d_parent_ino); =20 +static DEFINE_MUTEX(no_casefold_dentries_lock); +struct rhashtable no_casefold_dentries; + +enum no_casefold_dentry_ref { + ref_casefold_disable, + ref_casefold_enable, +}; + +struct no_casefold_dentry { + struct rhash_head hash; + struct dentry *dentry; + unsigned long ref[2]; +}; + +static const struct rhashtable_params no_casefold_dentries_params =3D { + .head_offset =3D offsetof(struct no_casefold_dentry, hash), + .key_offset =3D offsetof(struct no_casefold_dentry, dentry), + .key_len =3D sizeof(struct dentry *), + .automatic_shrinking =3D true, +}; + +static int no_casefold_dentry_get(struct dentry *dentry, + enum no_casefold_dentry_ref ref) +{ + struct no_casefold_dentry *n =3D + rhashtable_lookup_fast(&no_casefold_dentries, + &dentry, + no_casefold_dentries_params); + if (n) { + if (n->ref[!ref]) + return -EINVAL; + + n->ref[ref]++; + return 0; + } + + n =3D kzalloc(sizeof(*n), GFP_KERNEL); + if (!n) + return -ENOMEM; + + n->dentry =3D dget(dentry); + n->ref[ref]++; + + int ret =3D rhashtable_lookup_insert_fast(&no_casefold_dentries, + &n->hash, no_casefold_dentries_params); + if (WARN_ON(ret)) { + kfree(n); + return ret; + } + + return 0; +} + +static void no_casefold_dentry_put(struct dentry *dentry, + enum no_casefold_dentry_ref ref) +{ + struct no_casefold_dentry *n =3D + rhashtable_lookup_fast(&no_casefold_dentries, + &dentry, + no_casefold_dentries_params); + if (WARN_ON(!n)) + return; + + if (--n->ref[ref]) + return; + + dput(n->dentry); + int ret =3D rhashtable_remove_fast(&no_casefold_dentries, + &n->hash, no_casefold_dentries_params); + WARN_ON(ret); +} + +/** + * d_casefold_disabled_put - drop a "casefold disabled" ref + * + * Only for overlayfs. + */ +void d_casefold_disabled_put(struct dentry *dentry) +{ + if (!(dentry->d_sb->s_flags & SB_CASEFOLD)) + return; + + guard(mutex)(&no_casefold_dentries_lock); + no_casefold_dentry_put(dentry, ref_casefold_disable); +} +EXPORT_SYMBOL_GPL(d_casefold_disabled_put); + +/** + * d_casefold_disabled_get - attempt to disable casefold on a tree + * + * Only for overlayfs. + * + * Returns -EINVAL if casefolding is in use on any subdirectory; this must= be + * tracked by the filesystem. + * + * On success, returns with a reference held that must be released by + * d_casefold_disabled_put(); this ref blocks casefold from being enabled + * by d_casefold_enable(). + */ +int d_casefold_disabled_get(struct dentry *dentry) +{ + if (!(dentry->d_sb->s_flags & SB_CASEFOLD)) + return 0; + + guard(mutex)(&no_casefold_dentries_lock); + + if (!(dentry->d_inode->i_flags & S_NO_CASEFOLD)) + return -EINVAL; + + return no_casefold_dentry_get(dentry, ref_casefold_disable); +} +EXPORT_SYMBOL_GPL(d_casefold_disabled_get); + +/** + * d_casefold_enable - check if casefolding may be enabled on a dentry + * + * Returns -EINVAL if casefolding has been disabled on any parent director= y (by + * overlayfs). + * + * On success the inode will hase S_NO_CASEFOLD cleared. + */ +int d_casefold_enable(struct dentry *dentry, struct d_casefold_enable *e) +{ + struct dentry *root =3D dentry->d_sb->s_root; + int ret =3D 0; + + guard(mutex)(&no_casefold_dentries_lock); + + for (struct dentry *i =3D dentry; + i && i->d_inode->i_flags & S_NO_CASEFOLD; + i =3D i !=3D root ? i->d_parent : NULL) { + ret =3D darray_push(&e->refs, i); + if (ret) + goto err; + + ret =3D no_casefold_dentry_get(i, ref_casefold_enable); + if (ret) { + darray_pop(&e->refs); + goto err; + } + } + + return 0; +err: + darray_for_each(e->refs, i) + no_casefold_dentry_put(*i, ref_casefold_enable); + darray_exit(&e->refs); + return ret; +} +EXPORT_SYMBOL_GPL(d_casefold_enable); + +void d_casefold_enable_commit(struct d_casefold_enable *e, int ret) +{ + guard(mutex)(&no_casefold_dentries_lock); + + darray_for_each(e->refs, i) { + if (!ret) { + struct inode *inode =3D (*i)->d_inode; + + spin_lock(&inode->i_lock); + inode->i_flags &=3D ~S_NO_CASEFOLD; + spin_unlock(&inode->i_lock); + } + + no_casefold_dentry_put(*i, ref_casefold_enable); + } + darray_exit(&e->refs); +} +EXPORT_SYMBOL_GPL(d_casefold_enable_commit); + static __initdata unsigned long dhash_entries; static int __init set_dhash_entries(char *str) { @@ -3186,6 +3359,10 @@ static void __init dcache_init(void) SLAB_RECLAIM_ACCOUNT|SLAB_PANIC|SLAB_ACCOUNT, d_shortname.string); =20 + int ret =3D rhashtable_init(&no_casefold_dentries, &no_casefold_dentries_= params); + if (ret) + panic("error initializing no_casefold_dentries: %s\n", errname(ret)); + /* Hash may have been set up in dcache_init_early */ if (!hashdist) return; diff --git a/include/linux/dcache.h b/include/linux/dcache.h index e9f07e37dd6f..a386f636b15d 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -3,6 +3,7 @@ #define __LINUX_DCACHE_H =20 #include +#include #include #include #include @@ -604,4 +605,13 @@ static inline struct dentry *d_next_sibling(const stru= ct dentry *dentry) return hlist_entry_safe(dentry->d_sib.next, struct dentry, d_sib); } =20 +void d_casefold_disabled_put(struct dentry *dentry); +int d_casefold_disabled_get(struct dentry *dentry); + +struct d_casefold_enable { + DARRAY(struct dentry *) refs; +}; +int d_casefold_enable(struct dentry *dentry, struct d_casefold_enable *e); +void d_casefold_enable_commit(struct d_casefold_enable *e, int ret); + #endif /* __LINUX_DCACHE_H */ diff --git a/include/linux/fs.h b/include/linux/fs.h index ba942cd2fea1..962a60c9b50d 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -48,6 +48,7 @@ #include #include #include +#include =20 #include #include @@ -1440,6 +1441,7 @@ struct super_block { =20 struct mutex s_sync_lock; /* sync serialisation lock */ =20 + /* * Indicates how deep in a filesystem stack this SB is */ @@ -2345,6 +2347,7 @@ struct super_operations { #define S_CASEFOLD (1 << 15) /* Casefolded file */ #define S_VERITY (1 << 16) /* Verity file (using fs/verity/) */ #define S_KERNEL_FILE (1 << 17) /* File is in use by the kernel (eg. fs/ca= chefiles) */ +#define S_NO_CASEFOLD (1 << 18) /* Directory, and all descendents, are not= casefolded */ =20 /* * Note that nosuid etc flags are inode-specific: setting some file-system --=20 2.49.0 From nobody Fri Dec 19 14:21:13 2025 Received: from out-186.mta0.migadu.com (out-186.mta0.migadu.com [91.218.175.186]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3BF5525C810 for ; Tue, 20 May 2025 05:16:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.218.175.186 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747718179; cv=none; b=Zu6KGbTGWd4S2NaYTsrzKRg7MXZPYPSgL4jIZaBwfYhHOgwJb5lpyiyQ3IppjQvwb61Kw4/5UEdubTNWELeUwNZDkfi29f5XMQYDJ/AUFaYIQSHo2xfVxAK085ZB8/GMU6926anfIcQe7w75ALTcRRNPIJS1qkI02yFiA7Z4Pvs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747718179; c=relaxed/simple; bh=ckfgp5cSo5K5k1rAontem8yZ2aUqnKaCPUb6Zy3JpNo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=A5pNYNuXqjuyIZb+RyJDvn+ajwK538xy8iJ+QiFKkCnW3vBfenJ09Q2y3qQY4Y5LEgESSxyyCDBlOYbRr/xQmXIQTqcD5kIcNCSbGpoFSWp613Fl74s/59H97cmo3iO6Dd5i76DCyk3P7gL7xGS9osbRNRVgtJ2zZBxc7/bnDc4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=xTX4ys/h; arc=none smtp.client-ip=91.218.175.186 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="xTX4ys/h" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1747718175; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=2tyGTr5uf1FpOqkJ6li2TLa5npwmkkdAbemi7PJM5Xs=; b=xTX4ys/hOr8D50A+DkeqPIH+ve4DprVZXSh3TJdQm91HkvlXJS568/3duBtbZ7R7XSZYR+ 6c+CjezmJwp4td1Lcs2MrcjEqEnMUn5WXWZNJafdfVI4CQdUHsEXLEvbboGja9DFFtSyfS cBvluvPA+HM3Tsiwku7Nzq0WwVUEikg= From: Kent Overstreet To: linux-fsdevel@vger.kernel.org, linux-bcachefs@vger.kernel.org, linux-kernel@vger.kernel.org, linux-unionfs@vger.kernel.org Cc: Kent Overstreet , Miklos Szeredi , Amir Goldstein , Alexander Viro , Christian Brauner , Jan Kara Subject: [PATCH 5/6] bcachefs: Hook up d_casefold_enable() Date: Tue, 20 May 2025 01:15:57 -0400 Message-ID: <20250520051600.1903319-6-kent.overstreet@linux.dev> In-Reply-To: <20250520051600.1903319-1-kent.overstreet@linux.dev> References: <20250520051600.1903319-1-kent.overstreet@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Migadu-Flow: FLOW_OUT Content-Type: text/plain; charset="utf-8" Hook up BCH_INODE_has_case_insensitive -> S_NO_CASEFOLD, and call the new dcache methods for exclusion with overlayfs when the flag is changing. Signed-off-by: Kent Overstreet --- fs/bcachefs/fs.c | 41 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/fs/bcachefs/fs.c b/fs/bcachefs/fs.c index 0b5d52895e05..a02f787a6d05 100644 --- a/fs/bcachefs/fs.c +++ b/fs/bcachefs/fs.c @@ -68,6 +68,11 @@ static inline void bch2_inode_flags_to_vfs(struct bch_fs= *c, struct bch_inode_in inode->v.i_flags |=3D S_CASEFOLD; else inode->v.i_flags &=3D ~S_CASEFOLD; + + if (inode->ei_inode.bi_flags & BCH_INODE_has_case_insensitive) + inode->v.i_flags &=3D ~S_NO_CASEFOLD; + else + inode->v.i_flags |=3D S_NO_CASEFOLD; } =20 void bch2_inode_update_after_write(struct btree_trans *trans, @@ -916,6 +921,8 @@ static int bch2_rename2(struct mnt_idmap *idmap, struct bch_inode_info *dst_inode =3D to_bch_ei(dst_dentry->d_inode); struct bch_inode_unpacked dst_dir_u, src_dir_u; struct bch_inode_unpacked src_inode_u, dst_inode_u, *whiteout_inode_u; + struct d_casefold_enable casefold_enable_src =3D {}; + struct d_casefold_enable casefold_enable_dst =3D {}; struct btree_trans *trans; enum bch_rename_mode mode =3D flags & RENAME_EXCHANGE ? BCH_RENAME_EXCHANGE @@ -940,6 +947,21 @@ static int bch2_rename2(struct mnt_idmap *idmap, src_inode, dst_inode); =20 + if (src_dir !=3D dst_dir) { + if (bch2_inode_casefold(c, &src_inode->ei_inode)) { + ret =3D d_casefold_enable(dst_dentry, &casefold_enable_dst); + if (ret) + goto err; + } + + if (mode =3D=3D BCH_RENAME_EXCHANGE && + bch2_inode_casefold(c, &dst_inode->ei_inode)) { + ret =3D d_casefold_enable(src_dentry, &casefold_enable_src); + if (ret) + goto err; + } + } + trans =3D bch2_trans_get(c); =20 ret =3D bch2_subvol_is_ro_trans(trans, src_dir->ei_inum.subvol) ?: @@ -1044,6 +1066,9 @@ static int bch2_rename2(struct mnt_idmap *idmap, src_inode, dst_inode); =20 + d_casefold_enable_commit(&casefold_enable_dst, ret); + d_casefold_enable_commit(&casefold_enable_src, ret); + return bch2_err_class(ret); } =20 @@ -1710,6 +1735,7 @@ static int bch2_fileattr_set(struct mnt_idmap *idmap, struct bch_inode_info *inode =3D to_bch_ei(d_inode(dentry)); struct bch_fs *c =3D inode->v.i_sb->s_fs_info; struct flags_set s =3D {}; + struct d_casefold_enable casefold_enable =3D {}; int ret; =20 if (fa->fsx_valid) { @@ -1742,9 +1768,17 @@ static int bch2_fileattr_set(struct mnt_idmap *idmap, s.casefold =3D (fa->flags & FS_CASEFOLD_FL) !=3D 0; fa->flags &=3D ~FS_CASEFOLD_FL; =20 + if (s.casefold) { + ret =3D d_casefold_enable(dentry, &casefold_enable); + if (ret) + goto err; + } + s.flags |=3D map_flags_rev(bch_flags_to_uflags, fa->flags); - if (fa->flags) - return -EOPNOTSUPP; + if (fa->flags) { + ret =3D -EOPNOTSUPP; + goto err; + } } =20 mutex_lock(&inode->ei_update_lock); @@ -1755,6 +1789,9 @@ static int bch2_fileattr_set(struct mnt_idmap *idmap, bch2_write_inode(c, inode, fssetxattr_inode_update_fn, &s, ATTR_CTIME); mutex_unlock(&inode->ei_update_lock); +err: + d_casefold_enable_commit(&casefold_enable, ret); + return ret; } =20 --=20 2.49.0 From nobody Fri Dec 19 14:21:13 2025 Received: from out-180.mta0.migadu.com (out-180.mta0.migadu.com [91.218.175.180]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 09EBC25CC51 for ; Tue, 20 May 2025 05:16:18 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.218.175.180 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747718181; cv=none; b=ruTTYQphQN3s71hF2TUUc4E7pN7+aDYJNRSNVytcWLgw+mlhdf46j9f6PTyiM88ybc912vTgKRtVAMTy/m6uoLLmqm7AI4X1FyavzRFcWU53HKymDPZjpllSewW04xDqG9K0HZhLgOUyeMZq4BpHBNhq+Ak9wcIUhXW4x2XSgXI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747718181; c=relaxed/simple; bh=XrNNzUY1TXKJiOxaGFQwiLM9iQNnTBPUEAMRSKaZEtQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=AtU/42K7pxjX8dacgqfg2C5dcqH4XTpzV2S0ldsKlgFgSI45ZkZ/ypcV/GNrxTtojg5AVz6FivTba6U/BfgN6ieD/tFIvdr5k72O6VJa/PrANUDQN06eJrJO+BL4S0nX1WU1d/WV5PqgzJ74C59Pkz7atXgegfQFFQNPQzf3gjc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=Sol6R6Lb; arc=none smtp.client-ip=91.218.175.180 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="Sol6R6Lb" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1747718176; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=EGQ1zxXw5QQ6/+CjgC6ci/ZRW8eopkMw2zSjQhSjJdk=; b=Sol6R6LbqJ+Hh10AABgv0JL5++/JH7BlUN9ny23vNyrqcFPCwTyoypoLpjXmh7HXc++t0a N/AJUpjtNrykPoG1OSC4g1YnUJ1v4kVlFogto/jcMfOuwVGvOPMYHCrl1cfIfOUpOYgU6n gS1cL+M/5jEyKqoh21DhSNgim+tZie0= From: Kent Overstreet To: linux-fsdevel@vger.kernel.org, linux-bcachefs@vger.kernel.org, linux-kernel@vger.kernel.org, linux-unionfs@vger.kernel.org Cc: Kent Overstreet , Miklos Szeredi , Amir Goldstein , Alexander Viro , Christian Brauner , Jan Kara Subject: [PATCH 6/6] overlayfs: Support casefolded filesystems Date: Tue, 20 May 2025 01:15:58 -0400 Message-ID: <20250520051600.1903319-7-kent.overstreet@linux.dev> In-Reply-To: <20250520051600.1903319-1-kent.overstreet@linux.dev> References: <20250520051600.1903319-1-kent.overstreet@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Migadu-Flow: FLOW_OUT Content-Type: text/plain; charset="utf-8" Overlayfs can now work on filesystems that support casefolding, provided the specific subtree overlayfs is using as layers don't have casefolding enabled. d_casefold_disabled_get() and put() are used, which check that casefolding is enabled nowhere on a given subtree, and get and release a reference that prevents the filesystem from enabling casefolding on that tree while overlayfs is in use. We also now check the new SB_CASEFOLD superblock flag; if it's set we allow for dcache hash and compare ops to be set, relying instead on the new dcache methods. Cc: Miklos Szeredi Cc: Amir Goldstein Cc: linux-unionfs@vger.kernel.org Signed-off-by: Kent Overstreet --- fs/overlayfs/params.c | 20 +++++++++++++++++--- fs/overlayfs/util.c | 19 +++++++++++++++---- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/fs/overlayfs/params.c b/fs/overlayfs/params.c index 6759f7d040c8..ae7424e075a7 100644 --- a/fs/overlayfs/params.c +++ b/fs/overlayfs/params.c @@ -287,7 +287,8 @@ static int ovl_mount_dir_check(struct fs_context *fc, c= onst struct path *path, * with overlayfs. Check explicitly to prevent post-mount * failures. */ - if (sb_has_encoding(path->mnt->mnt_sb)) + if ((path->mnt->mnt_sb->s_flags & SB_CASEFOLD) && + !(path->dentry->d_inode->i_flags & S_NO_CASEFOLD)) return invalfc(fc, "case-insensitive capable filesystem on %s not suppor= ted", name); =20 if (ovl_dentry_weird(path->dentry)) @@ -411,20 +412,32 @@ static int ovl_do_parse_layer(struct fs_context *fc, = const char *layer_name, if (!name) return -ENOMEM; =20 + if (layer !=3D Opt_workdir && + layer !=3D Opt_upperdir) { + err =3D d_casefold_disabled_get(layer_path->dentry); + if (err) + return err; + } + upper =3D is_upper_layer(layer); err =3D ovl_mount_dir_check(fc, layer_path, layer, name, upper); if (err) - return err; + goto err_put; =20 if (!upper) { err =3D ovl_ctx_realloc_lower(fc); if (err) - return err; + goto err_put; } =20 /* Store the user provided path string in ctx to show in mountinfo */ ovl_add_layer(fc, layer, layer_path, &name); return err; +err_put: + if (layer !=3D Opt_workdir && + layer !=3D Opt_upperdir) + d_casefold_disabled_put(layer_path->dentry); + return err; } =20 static int ovl_parse_layer(struct fs_context *fc, struct fs_parameter *par= am, @@ -475,6 +488,7 @@ static void ovl_reset_lowerdirs(struct ovl_fs_context *= ctx) ctx->lowerdir_all =3D NULL; =20 for (size_t nr =3D 0; nr < ctx->nr; nr++, l++) { + d_casefold_disabled_put(l->path.dentry); path_put(&l->path); kfree(l->name); l->name =3D NULL; diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c index 0819c739cc2f..c515f260032c 100644 --- a/fs/overlayfs/util.c +++ b/fs/overlayfs/util.c @@ -205,10 +205,21 @@ bool ovl_dentry_weird(struct dentry *dentry) if (!d_can_lookup(dentry) && !d_is_file(dentry) && !d_is_symlink(dentry)) return true; =20 - return dentry->d_flags & (DCACHE_NEED_AUTOMOUNT | - DCACHE_MANAGE_TRANSIT | - DCACHE_OP_HASH | - DCACHE_OP_COMPARE); + if (dentry->d_flags & (DCACHE_NEED_AUTOMOUNT | + DCACHE_MANAGE_TRANSIT)) + return true; + + /* + * The filesystem might support casefolding, but we've already checked + * that casefolding isn't present on this tree: we only need to check + * for non-casefolding hash/compare ops + */ + if (!(dentry->d_sb->s_flags & SB_CASEFOLD) && + (dentry->d_flags & (DCACHE_OP_HASH | + DCACHE_OP_COMPARE))) + return true; + + return false; } =20 enum ovl_path_type ovl_path_type(struct dentry *dentry) --=20 2.49.0