[PATCH RFC v3 17/90] cgroup: add cgroup_cell_chown()

From: Josef Bacik

Date: Thu Nov 05 2026 - 10:06:01 EST


A cgroup is delegated by giving somebody ownership of its directory and of
cgroup.procs, cgroup.threads and cgroup.subtree_control. The builder of a
cell cannot do that for the root user of the cell. It cannot name the
kuid, and cgroupfs could not store that kuid through a chown anyway.

Add cgroup_cell_chown(), which changes the owner directly on the kernfs
nodes. The files that hold the limits keep their owner.

The function can fail half way if it runs out of memory. The caller undoes
a partial change by calling it again with the previous owner. That second
call cannot fail: kernfs_setattr() only allocates memory the first time
the attributes of a node are set, and the nodes in question have already
been changed once.

Signed-off-by: Josef Bacik <josef@xxxxxxxxxxxxxx>
---
include/linux/cgroup.h | 1 +
kernel/cgroup/cgroup.c | 37 +++++++++++++++++++++++++++++++++++++
2 files changed, 38 insertions(+)

diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h
index a5ef01688e7f..0e45a5a1afea 100644
--- a/include/linux/cgroup.h
+++ b/include/linux/cgroup.h
@@ -154,6 +154,7 @@ struct cgroup *cgroup_get_from_path(const char *path);
struct cgroup *cgroup_get_from_fd(int fd);

struct cgroup *cgroup_cell_budget_get(struct file *file, kuid_t *uid, kgid_t *gid);
+int cgroup_cell_chown(struct cgroup *cgrp, kuid_t uid, kgid_t gid);
struct cgroup *cgroup_v1v2_get_from_fd(int fd);

/* Was this controller blocked from v1 hierarchies by cgroup_no_v1= ? */
diff --git a/kernel/cgroup/cgroup.c b/kernel/cgroup/cgroup.c
index c4bbb70f490f..fb2775006087 100644
--- a/kernel/cgroup/cgroup.c
+++ b/kernel/cgroup/cgroup.c
@@ -7124,6 +7124,43 @@ struct cgroup *cgroup_cell_budget_get(struct file *file, kuid_t *uid, kgid_t *gi
}
return cgrp;
}
+
+/**
+ * cgroup_cell_chown - hand a cgroup to somebody
+ * @cgrp: the cgroup
+ * @uid: the new owner
+ * @gid: the new group
+ *
+ * Changes what delegating a cgroup is about: the directory, cgroup.procs,
+ * cgroup.threads and cgroup.subtree_control. The limits stay with whoever is
+ * above.
+ *
+ * Return: 0 or a negative error, in which case some of it may have changed. To
+ * undo that call this again with what it was. All that kernfs_setattr() can fail
+ * for is the memory for the attributes of a node that never had any set, so it
+ * does not fail for a node that has been changed.
+ */
+int cgroup_cell_chown(struct cgroup *cgrp, kuid_t uid, kgid_t gid)
+{
+ static const char * const files[] = {
+ "cgroup.procs", "cgroup.threads", "cgroup.subtree_control",
+ };
+ struct iattr iattr = {
+ .ia_valid = ATTR_UID | ATTR_GID, .ia_uid = uid, .ia_gid = gid,
+ };
+ int i, ret;
+
+ ret = kernfs_setattr(cgrp->kn, &iattr);
+ for (i = 0; !ret && i < ARRAY_SIZE(files); i++) {
+ struct kernfs_node *kn = kernfs_find_and_get(cgrp->kn, files[i]);
+
+ if (!kn)
+ continue;
+ ret = kernfs_setattr(kn, &iattr);
+ kernfs_put(kn);
+ }
+ return ret;
+}
#endif /* CONFIG_CELLS */

/**

--
2.55.0