From 8d00b9bc2c30b05ca9464554fb96fbacac36234a Mon Sep 17 00:00:00 2001 From: chlorodose Date: Fri, 20 Feb 2026 14:57:29 +0800 Subject: [PATCH] bcachefs: Add SELinux support Integrate Linux Security Module (LSM) hooks to enable SELinux security label initialization during inode creation. This ensures that security xattrs are atomically set when files are created, preventing any window where files exist without proper security labels. Implementation: - Add bch2_xattr_set_init() internal function for setting xattrs during inode initialization without redundant inode operations - Add bch2_initxattrs() callback for security_inode_init_security() - Add bch2_init_security() wrapper to integrate LSM hooks into bcachefs transaction flow - Call bch2_init_security() in bch2_create_trans() after ACL setup, within the same transaction for atomicity All security xattr operations are performed within the existing btree transaction, ensuring atomicity with ACLs, directory entry creation, and inode persistence. Tested edge cases: LSM disabled (-EOPNOTSUPP), tmpfile creation (skipped), snapshot creation (skipped), and transaction restart scenarios. Co-Authored-By: Claude Sonnet 4.5 --- fs/bcachefs/fs/namei.c | 3 ++ fs/bcachefs/fs/xattr.c | 114 +++++++++++++++++++++++++++++++++++++++++ fs/bcachefs/fs/xattr.h | 3 ++ 3 files changed, 120 insertions(+) diff --git a/fs/bcachefs/fs/namei.c b/fs/bcachefs/fs/namei.c index b5d6c594a1abd..ec4f6b79ad351 100644 --- a/fs/bcachefs/fs/namei.c +++ b/fs/bcachefs/fs/namei.c @@ -128,6 +128,9 @@ int bch2_create_trans(struct btree_trans *trans, if (acl) try(bch2_set_acl_trans(trans, new_inum, new_inode, acl, ACL_TYPE_ACCESS)); + + if (name) + try(bch2_init_security(trans, new_inum, new_inode, dir, name)); } if (!(flags & BCH_CREATE_TMPFILE)) { diff --git a/fs/bcachefs/fs/xattr.c b/fs/bcachefs/fs/xattr.c index 5015f93e5201d..3b4d3a103e0b0 100644 --- a/fs/bcachefs/fs/xattr.c +++ b/fs/bcachefs/fs/xattr.c @@ -18,6 +18,7 @@ #include #include #include +#include static const struct xattr_handler *bch2_xattr_type_to_handler(unsigned); @@ -227,6 +228,119 @@ int bch2_xattr_set(struct btree_trans *trans, subvol_inum inum, return ret; } +/* + * SELinux support functions + */ + +struct bch2_security_init_ctx { + struct btree_trans *trans; + subvol_inum inum; + struct bch_inode_unpacked *inode_u; +}; + +static int bch2_xattr_set_init(struct btree_trans *trans, + subvol_inum inum, + struct bch_inode_unpacked *inode_u, + const char *name, + const void *value, + size_t size, + int type) +{ + struct bch_fs *c = trans->c; + struct bch_hash_info hash_info; + int ret; + + ret = bch2_hash_info_init(c, inode_u, &hash_info); + if (ret) + return ret; + + unsigned namelen = strlen(name); + unsigned u64s = BKEY_U64s + xattr_val_u64s(namelen, size); + + if (u64s > U8_MAX) + return -ERANGE; + + struct bkey_i_xattr *xattr = bch2_trans_kmalloc(trans, u64s * sizeof(u64)); + if (IS_ERR(xattr)) + return PTR_ERR(xattr); + + bkey_xattr_init(&xattr->k_i); + xattr->k.u64s = u64s; + xattr->v.x_type = type; + xattr->v.x_name_len = namelen; + xattr->v.x_val_len = cpu_to_le16(size); + memcpy(xattr->v.x_name_and_value, name, namelen); + memcpy(xattr_val(&xattr->v), value, size); + + return bch2_hash_set(trans, bch2_xattr_hash_desc, &hash_info, + inum, &xattr->k_i, STR_HASH_must_create); +} + +static int bch2_initxattrs(struct inode *inode, + const struct xattr *xattr_array, + void *fs_private) +{ + struct bch2_security_init_ctx *ctx = fs_private; + const struct xattr *xattr; + int ret = 0; + + for (xattr = xattr_array; xattr->name != NULL; xattr++) { + ret = bch2_xattr_set_init(ctx->trans, ctx->inum, ctx->inode_u, + xattr->name, xattr->value, + xattr->value_len, + KEY_TYPE_XATTR_INDEX_SECURITY); + if (ret) + break; + } + + return ret; +} + +int bch2_init_security(struct btree_trans *trans, + subvol_inum inum, + struct bch_inode_unpacked *inode_u, + subvol_inum dir_inum, + const struct qstr *qstr) +{ + struct bch_fs *c = trans->c; + struct inode *dir = NULL; + struct inode *inode = NULL; + int ret; + + dir = bch2_vfs_inode_get(c, dir_inum, __func__); + if (IS_ERR(dir)) + return PTR_ERR(dir); + + inode = new_inode(c->vfs_sb); + if (!inode) { + ret = -ENOMEM; + goto out; + } + + inode->i_ino = inode_u->bi_inum; + inode->i_mode = inode_u->bi_mode; + inode->i_uid = make_kuid(&init_user_ns, inode_u->bi_uid); + inode->i_gid = make_kgid(&init_user_ns, inode_u->bi_gid); + + struct bch2_security_init_ctx ctx = { + .trans = trans, + .inum = inum, + .inode_u = inode_u, + }; + + ret = security_inode_init_security(inode, dir, qstr, + &bch2_initxattrs, &ctx); +out: + if (inode) + iput(inode); + iput(dir); + + if (ret == -EOPNOTSUPP) + ret = 0; + + return ret; +} + struct xattr_buf { char *buf; size_t len; diff --git a/fs/bcachefs/fs/xattr.h b/fs/bcachefs/fs/xattr.h index 919341ca37e70..0846d376c165e 100644 --- a/fs/bcachefs/fs/xattr.h +++ b/fs/bcachefs/fs/xattr.h @@ -42,6 +42,9 @@ struct bch_inode_info; int bch2_xattr_set(struct btree_trans *, subvol_inum, struct bch_inode_unpacked *, const char *, const void *, size_t, int, int); +int bch2_init_security(struct btree_trans *, subvol_inum, + struct bch_inode_unpacked *, + subvol_inum, const struct qstr *); ssize_t bch2_xattr_list(struct dentry *, char *, size_t);