nspawn: Move cgroup mount stuff from nspawn-mount.c to nspawn-cgroup.c
This commit is contained in:
@@ -5,12 +5,16 @@
|
||||
#include "alloc-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "fs-util.h"
|
||||
#include "mkdir.h"
|
||||
#include "mount-util.h"
|
||||
#include "nspawn-cgroup.h"
|
||||
#include "nspawn-mount.h"
|
||||
#include "path-util.h"
|
||||
#include "rm-rf.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "user-util.h"
|
||||
#include "util.h"
|
||||
|
||||
static int chown_cgroup_path(const char *path, uid_t uid_shift) {
|
||||
@@ -188,3 +192,416 @@ int create_subcgroup(pid_t pid, bool keep_unit, CGroupUnified unified_requested)
|
||||
(void) cg_enable_everywhere(supported, supported, cgroup);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Retrieve existing subsystems. This function is called in a new cgroup
|
||||
* namespace.
|
||||
*/
|
||||
static int get_process_controllers(Set **ret) {
|
||||
_cleanup_set_free_free_ Set *controllers = NULL;
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
|
||||
controllers = set_new(&string_hash_ops);
|
||||
if (!controllers)
|
||||
return -ENOMEM;
|
||||
|
||||
f = fopen("/proc/self/cgroup", "re");
|
||||
if (!f)
|
||||
return errno == ENOENT ? -ESRCH : -errno;
|
||||
|
||||
for (;;) {
|
||||
_cleanup_free_ char *line = NULL;
|
||||
char *e, *l;
|
||||
|
||||
r = read_line(f, LONG_LINE_MAX, &line);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
l = strchr(line, ':');
|
||||
if (!l)
|
||||
continue;
|
||||
|
||||
l++;
|
||||
e = strchr(l, ':');
|
||||
if (!e)
|
||||
continue;
|
||||
|
||||
*e = 0;
|
||||
|
||||
if (STR_IN_SET(l, "", "name=systemd", "name=unified"))
|
||||
continue;
|
||||
|
||||
r = set_put_strdup(controllers, l);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
*ret = TAKE_PTR(controllers);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mount_legacy_cgroup_hierarchy(
|
||||
const char *dest,
|
||||
const char *controller,
|
||||
const char *hierarchy,
|
||||
bool read_only) {
|
||||
|
||||
const char *to, *fstype, *opts;
|
||||
int r;
|
||||
|
||||
to = strjoina(strempty(dest), "/sys/fs/cgroup/", hierarchy);
|
||||
|
||||
r = path_is_mount_point(to, dest, 0);
|
||||
if (r < 0 && r != -ENOENT)
|
||||
return log_error_errno(r, "Failed to determine if %s is mounted already: %m", to);
|
||||
if (r > 0)
|
||||
return 0;
|
||||
|
||||
mkdir_p(to, 0755);
|
||||
|
||||
/* The superblock mount options of the mount point need to be
|
||||
* identical to the hosts', and hence writable... */
|
||||
if (streq(controller, SYSTEMD_CGROUP_CONTROLLER_HYBRID)) {
|
||||
fstype = "cgroup2";
|
||||
opts = NULL;
|
||||
} else if (streq(controller, SYSTEMD_CGROUP_CONTROLLER_LEGACY)) {
|
||||
fstype = "cgroup";
|
||||
opts = "none,name=systemd,xattr";
|
||||
} else {
|
||||
fstype = "cgroup";
|
||||
opts = controller;
|
||||
}
|
||||
|
||||
r = mount_verbose(LOG_ERR, "cgroup", to, fstype, MS_NOSUID|MS_NOEXEC|MS_NODEV, opts);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* ... hence let's only make the bind mount read-only, not the superblock. */
|
||||
if (read_only) {
|
||||
r = mount_verbose(LOG_ERR, NULL, to, NULL,
|
||||
MS_BIND|MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_RDONLY, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Mount a legacy cgroup hierarchy when cgroup namespaces are supported. */
|
||||
static int mount_legacy_cgns_supported(
|
||||
const char *dest,
|
||||
CGroupUnified unified_requested,
|
||||
bool userns,
|
||||
uid_t uid_shift,
|
||||
uid_t uid_range,
|
||||
const char *selinux_apifs_context) {
|
||||
|
||||
_cleanup_set_free_free_ Set *controllers = NULL;
|
||||
const char *cgroup_root = "/sys/fs/cgroup", *c;
|
||||
int r;
|
||||
|
||||
(void) mkdir_p(cgroup_root, 0755);
|
||||
|
||||
/* Mount a tmpfs to /sys/fs/cgroup if it's not mounted there yet. */
|
||||
r = path_is_mount_point(cgroup_root, dest, AT_SYMLINK_FOLLOW);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to determine if /sys/fs/cgroup is already mounted: %m");
|
||||
if (r == 0) {
|
||||
_cleanup_free_ char *options = NULL;
|
||||
|
||||
/* When cgroup namespaces are enabled and user namespaces are
|
||||
* used then the mount of the cgroupfs is done *inside* the new
|
||||
* user namespace. We're root in the new user namespace and the
|
||||
* kernel will happily translate our uid/gid to the correct
|
||||
* uid/gid as seen from e.g. /proc/1/mountinfo. So we simply
|
||||
* pass uid 0 and not uid_shift to tmpfs_patch_options().
|
||||
*/
|
||||
r = tmpfs_patch_options("mode=755", 0, selinux_apifs_context, &options);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
r = mount_verbose(LOG_ERR, "tmpfs", cgroup_root, "tmpfs",
|
||||
MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME, options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = cg_all_unified();
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0)
|
||||
goto skip_controllers;
|
||||
|
||||
r = get_process_controllers(&controllers);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to determine cgroup controllers: %m");
|
||||
|
||||
for (;;) {
|
||||
_cleanup_free_ const char *controller = NULL;
|
||||
|
||||
controller = set_steal_first(controllers);
|
||||
if (!controller)
|
||||
break;
|
||||
|
||||
r = mount_legacy_cgroup_hierarchy("", controller, controller, !userns);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* When multiple hierarchies are co-mounted, make their
|
||||
* constituting individual hierarchies a symlink to the
|
||||
* co-mount.
|
||||
*/
|
||||
c = controller;
|
||||
for (;;) {
|
||||
_cleanup_free_ char *target = NULL, *tok = NULL;
|
||||
|
||||
r = extract_first_word(&c, &tok, ",", 0);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to extract co-mounted cgroup controller: %m");
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
if (streq(controller, tok))
|
||||
break;
|
||||
|
||||
target = prefix_root("/sys/fs/cgroup/", tok);
|
||||
if (!target)
|
||||
return log_oom();
|
||||
|
||||
r = symlink_idempotent(controller, target);
|
||||
if (r == -EINVAL)
|
||||
return log_error_errno(r, "Invalid existing symlink for combined hierarchy: %m");
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to create symlink for combined hierarchy: %m");
|
||||
}
|
||||
}
|
||||
|
||||
skip_controllers:
|
||||
if (unified_requested >= CGROUP_UNIFIED_SYSTEMD) {
|
||||
r = mount_legacy_cgroup_hierarchy("", SYSTEMD_CGROUP_CONTROLLER_HYBRID, "unified", false);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = mount_legacy_cgroup_hierarchy("", SYSTEMD_CGROUP_CONTROLLER_LEGACY, "systemd", false);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!userns)
|
||||
return mount_verbose(LOG_ERR, NULL, cgroup_root, NULL,
|
||||
MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY, "mode=755");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Mount legacy cgroup hierarchy when cgroup namespaces are unsupported. */
|
||||
static int mount_legacy_cgns_unsupported(
|
||||
const char *dest,
|
||||
CGroupUnified unified_requested,
|
||||
bool userns,
|
||||
uid_t uid_shift,
|
||||
uid_t uid_range,
|
||||
const char *selinux_apifs_context) {
|
||||
|
||||
_cleanup_set_free_free_ Set *controllers = NULL;
|
||||
const char *cgroup_root;
|
||||
int r;
|
||||
|
||||
cgroup_root = prefix_roota(dest, "/sys/fs/cgroup");
|
||||
|
||||
(void) mkdir_p(cgroup_root, 0755);
|
||||
|
||||
/* Mount a tmpfs to /sys/fs/cgroup if it's not mounted there yet. */
|
||||
r = path_is_mount_point(cgroup_root, dest, AT_SYMLINK_FOLLOW);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to determine if /sys/fs/cgroup is already mounted: %m");
|
||||
if (r == 0) {
|
||||
_cleanup_free_ char *options = NULL;
|
||||
|
||||
r = tmpfs_patch_options("mode=755", uid_shift == 0 ? UID_INVALID : uid_shift, selinux_apifs_context, &options);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
r = mount_verbose(LOG_ERR, "tmpfs", cgroup_root, "tmpfs",
|
||||
MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME, options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = cg_all_unified();
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0)
|
||||
goto skip_controllers;
|
||||
|
||||
r = cg_kernel_controllers(&controllers);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to determine cgroup controllers: %m");
|
||||
|
||||
for (;;) {
|
||||
_cleanup_free_ char *controller = NULL, *origin = NULL, *combined = NULL;
|
||||
|
||||
controller = set_steal_first(controllers);
|
||||
if (!controller)
|
||||
break;
|
||||
|
||||
origin = prefix_root("/sys/fs/cgroup/", controller);
|
||||
if (!origin)
|
||||
return log_oom();
|
||||
|
||||
r = readlink_malloc(origin, &combined);
|
||||
if (r == -EINVAL) {
|
||||
/* Not a symbolic link, but directly a single cgroup hierarchy */
|
||||
|
||||
r = mount_legacy_cgroup_hierarchy(dest, controller, controller, true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
} else if (r < 0)
|
||||
return log_error_errno(r, "Failed to read link %s: %m", origin);
|
||||
else {
|
||||
_cleanup_free_ char *target = NULL;
|
||||
|
||||
target = prefix_root(dest, origin);
|
||||
if (!target)
|
||||
return log_oom();
|
||||
|
||||
/* A symbolic link, a combination of controllers in one hierarchy */
|
||||
|
||||
if (!filename_is_valid(combined)) {
|
||||
log_warning("Ignoring invalid combined hierarchy %s.", combined);
|
||||
continue;
|
||||
}
|
||||
|
||||
r = mount_legacy_cgroup_hierarchy(dest, combined, combined, true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = symlink_idempotent(combined, target);
|
||||
if (r == -EINVAL)
|
||||
return log_error_errno(r, "Invalid existing symlink for combined hierarchy: %m");
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to create symlink for combined hierarchy: %m");
|
||||
}
|
||||
}
|
||||
|
||||
skip_controllers:
|
||||
if (unified_requested >= CGROUP_UNIFIED_SYSTEMD) {
|
||||
r = mount_legacy_cgroup_hierarchy(dest, SYSTEMD_CGROUP_CONTROLLER_HYBRID, "unified", false);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = mount_legacy_cgroup_hierarchy(dest, SYSTEMD_CGROUP_CONTROLLER_LEGACY, "systemd", false);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return mount_verbose(LOG_ERR, NULL, cgroup_root, NULL,
|
||||
MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY, "mode=755");
|
||||
}
|
||||
|
||||
static int mount_unified_cgroups(const char *dest) {
|
||||
const char *p;
|
||||
int r;
|
||||
|
||||
assert(dest);
|
||||
|
||||
p = prefix_roota(dest, "/sys/fs/cgroup");
|
||||
|
||||
(void) mkdir_p(p, 0755);
|
||||
|
||||
r = path_is_mount_point(p, dest, AT_SYMLINK_FOLLOW);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to determine if %s is mounted already: %m", p);
|
||||
if (r > 0) {
|
||||
p = prefix_roota(dest, "/sys/fs/cgroup/cgroup.procs");
|
||||
if (access(p, F_OK) >= 0)
|
||||
return 0;
|
||||
if (errno != ENOENT)
|
||||
return log_error_errno(errno, "Failed to determine if mount point %s contains the unified cgroup hierarchy: %m", p);
|
||||
|
||||
log_error("%s is already mounted but not a unified cgroup hierarchy. Refusing.", p);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return mount_verbose(LOG_ERR, "cgroup", p, "cgroup2", MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL);
|
||||
}
|
||||
|
||||
int mount_cgroups(
|
||||
const char *dest,
|
||||
CGroupUnified unified_requested,
|
||||
bool userns,
|
||||
uid_t uid_shift,
|
||||
uid_t uid_range,
|
||||
const char *selinux_apifs_context,
|
||||
bool use_cgns) {
|
||||
|
||||
if (unified_requested >= CGROUP_UNIFIED_ALL)
|
||||
return mount_unified_cgroups(dest);
|
||||
if (use_cgns)
|
||||
return mount_legacy_cgns_supported(dest, unified_requested, userns, uid_shift, uid_range, selinux_apifs_context);
|
||||
|
||||
return mount_legacy_cgns_unsupported(dest, unified_requested, userns, uid_shift, uid_range, selinux_apifs_context);
|
||||
}
|
||||
|
||||
static int mount_systemd_cgroup_writable_one(const char *root, const char *own) {
|
||||
int r;
|
||||
|
||||
assert(root);
|
||||
assert(own);
|
||||
|
||||
/* Make our own cgroup a (writable) bind mount */
|
||||
r = mount_verbose(LOG_ERR, own, own, NULL, MS_BIND, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* And then remount the systemd cgroup root read-only */
|
||||
return mount_verbose(LOG_ERR, NULL, root, NULL,
|
||||
MS_BIND|MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_RDONLY, NULL);
|
||||
}
|
||||
|
||||
int mount_systemd_cgroup_writable(
|
||||
const char *dest,
|
||||
CGroupUnified unified_requested) {
|
||||
|
||||
_cleanup_free_ char *own_cgroup_path = NULL;
|
||||
const char *root, *own;
|
||||
int r;
|
||||
|
||||
assert(dest);
|
||||
|
||||
r = cg_pid_get_path(NULL, 0, &own_cgroup_path);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to determine our own cgroup path: %m");
|
||||
|
||||
/* If we are living in the top-level, then there's nothing to do... */
|
||||
if (path_equal(own_cgroup_path, "/"))
|
||||
return 0;
|
||||
|
||||
if (unified_requested >= CGROUP_UNIFIED_ALL) {
|
||||
|
||||
root = prefix_roota(dest, "/sys/fs/cgroup");
|
||||
own = strjoina(root, own_cgroup_path);
|
||||
|
||||
} else {
|
||||
|
||||
if (unified_requested >= CGROUP_UNIFIED_SYSTEMD) {
|
||||
root = prefix_roota(dest, "/sys/fs/cgroup/unified");
|
||||
own = strjoina(root, own_cgroup_path);
|
||||
|
||||
r = mount_systemd_cgroup_writable_one(root, own);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
root = prefix_roota(dest, "/sys/fs/cgroup/systemd");
|
||||
own = strjoina(root, own_cgroup_path);
|
||||
}
|
||||
|
||||
return mount_systemd_cgroup_writable_one(root, own);
|
||||
}
|
||||
|
||||
@@ -9,3 +9,6 @@
|
||||
int chown_cgroup(pid_t pid, CGroupUnified unified_requested, uid_t uid_shift);
|
||||
int sync_cgroup(pid_t pid, CGroupUnified unified_requested, uid_t uid_shift);
|
||||
int create_subcgroup(pid_t pid, bool keep_unit, CGroupUnified unified_requested);
|
||||
|
||||
int mount_cgroups(const char *dest, CGroupUnified unified_requested, bool userns, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context, bool use_cgns);
|
||||
int mount_systemd_cgroup_writable(const char *dest, CGroupUnified unified_requested);
|
||||
|
||||
+1
-414
@@ -327,7 +327,7 @@ int overlay_mount_parse(CustomMount **l, size_t *n, const char *s, bool read_onl
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tmpfs_patch_options(
|
||||
int tmpfs_patch_options(
|
||||
const char *options,
|
||||
uid_t uid_shift,
|
||||
const char *selinux_apifs_context,
|
||||
@@ -850,419 +850,6 @@ int mount_custom(
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Retrieve existing subsystems. This function is called in a new cgroup
|
||||
* namespace.
|
||||
*/
|
||||
static int get_process_controllers(Set **ret) {
|
||||
_cleanup_set_free_free_ Set *controllers = NULL;
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
|
||||
controllers = set_new(&string_hash_ops);
|
||||
if (!controllers)
|
||||
return -ENOMEM;
|
||||
|
||||
f = fopen("/proc/self/cgroup", "re");
|
||||
if (!f)
|
||||
return errno == ENOENT ? -ESRCH : -errno;
|
||||
|
||||
for (;;) {
|
||||
_cleanup_free_ char *line = NULL;
|
||||
char *e, *l;
|
||||
|
||||
r = read_line(f, LONG_LINE_MAX, &line);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
l = strchr(line, ':');
|
||||
if (!l)
|
||||
continue;
|
||||
|
||||
l++;
|
||||
e = strchr(l, ':');
|
||||
if (!e)
|
||||
continue;
|
||||
|
||||
*e = 0;
|
||||
|
||||
if (STR_IN_SET(l, "", "name=systemd", "name=unified"))
|
||||
continue;
|
||||
|
||||
r = set_put_strdup(controllers, l);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
*ret = TAKE_PTR(controllers);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mount_legacy_cgroup_hierarchy(
|
||||
const char *dest,
|
||||
const char *controller,
|
||||
const char *hierarchy,
|
||||
bool read_only) {
|
||||
|
||||
const char *to, *fstype, *opts;
|
||||
int r;
|
||||
|
||||
to = strjoina(strempty(dest), "/sys/fs/cgroup/", hierarchy);
|
||||
|
||||
r = path_is_mount_point(to, dest, 0);
|
||||
if (r < 0 && r != -ENOENT)
|
||||
return log_error_errno(r, "Failed to determine if %s is mounted already: %m", to);
|
||||
if (r > 0)
|
||||
return 0;
|
||||
|
||||
mkdir_p(to, 0755);
|
||||
|
||||
/* The superblock mount options of the mount point need to be
|
||||
* identical to the hosts', and hence writable... */
|
||||
if (streq(controller, SYSTEMD_CGROUP_CONTROLLER_HYBRID)) {
|
||||
fstype = "cgroup2";
|
||||
opts = NULL;
|
||||
} else if (streq(controller, SYSTEMD_CGROUP_CONTROLLER_LEGACY)) {
|
||||
fstype = "cgroup";
|
||||
opts = "none,name=systemd,xattr";
|
||||
} else {
|
||||
fstype = "cgroup";
|
||||
opts = controller;
|
||||
}
|
||||
|
||||
r = mount_verbose(LOG_ERR, "cgroup", to, fstype, MS_NOSUID|MS_NOEXEC|MS_NODEV, opts);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* ... hence let's only make the bind mount read-only, not the superblock. */
|
||||
if (read_only) {
|
||||
r = mount_verbose(LOG_ERR, NULL, to, NULL,
|
||||
MS_BIND|MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_RDONLY, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Mount a legacy cgroup hierarchy when cgroup namespaces are supported. */
|
||||
static int mount_legacy_cgns_supported(
|
||||
const char *dest,
|
||||
CGroupUnified unified_requested,
|
||||
bool userns,
|
||||
uid_t uid_shift,
|
||||
uid_t uid_range,
|
||||
const char *selinux_apifs_context) {
|
||||
|
||||
_cleanup_set_free_free_ Set *controllers = NULL;
|
||||
const char *cgroup_root = "/sys/fs/cgroup", *c;
|
||||
int r;
|
||||
|
||||
(void) mkdir_p(cgroup_root, 0755);
|
||||
|
||||
/* Mount a tmpfs to /sys/fs/cgroup if it's not mounted there yet. */
|
||||
r = path_is_mount_point(cgroup_root, dest, AT_SYMLINK_FOLLOW);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to determine if /sys/fs/cgroup is already mounted: %m");
|
||||
if (r == 0) {
|
||||
_cleanup_free_ char *options = NULL;
|
||||
|
||||
/* When cgroup namespaces are enabled and user namespaces are
|
||||
* used then the mount of the cgroupfs is done *inside* the new
|
||||
* user namespace. We're root in the new user namespace and the
|
||||
* kernel will happily translate our uid/gid to the correct
|
||||
* uid/gid as seen from e.g. /proc/1/mountinfo. So we simply
|
||||
* pass uid 0 and not uid_shift to tmpfs_patch_options().
|
||||
*/
|
||||
r = tmpfs_patch_options("mode=755", 0, selinux_apifs_context, &options);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
r = mount_verbose(LOG_ERR, "tmpfs", cgroup_root, "tmpfs",
|
||||
MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME, options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = cg_all_unified();
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0)
|
||||
goto skip_controllers;
|
||||
|
||||
r = get_process_controllers(&controllers);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to determine cgroup controllers: %m");
|
||||
|
||||
for (;;) {
|
||||
_cleanup_free_ const char *controller = NULL;
|
||||
|
||||
controller = set_steal_first(controllers);
|
||||
if (!controller)
|
||||
break;
|
||||
|
||||
r = mount_legacy_cgroup_hierarchy("", controller, controller, !userns);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* When multiple hierarchies are co-mounted, make their
|
||||
* constituting individual hierarchies a symlink to the
|
||||
* co-mount.
|
||||
*/
|
||||
c = controller;
|
||||
for (;;) {
|
||||
_cleanup_free_ char *target = NULL, *tok = NULL;
|
||||
|
||||
r = extract_first_word(&c, &tok, ",", 0);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to extract co-mounted cgroup controller: %m");
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
if (streq(controller, tok))
|
||||
break;
|
||||
|
||||
target = prefix_root("/sys/fs/cgroup/", tok);
|
||||
if (!target)
|
||||
return log_oom();
|
||||
|
||||
r = symlink_idempotent(controller, target);
|
||||
if (r == -EINVAL)
|
||||
return log_error_errno(r, "Invalid existing symlink for combined hierarchy: %m");
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to create symlink for combined hierarchy: %m");
|
||||
}
|
||||
}
|
||||
|
||||
skip_controllers:
|
||||
if (unified_requested >= CGROUP_UNIFIED_SYSTEMD) {
|
||||
r = mount_legacy_cgroup_hierarchy("", SYSTEMD_CGROUP_CONTROLLER_HYBRID, "unified", false);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = mount_legacy_cgroup_hierarchy("", SYSTEMD_CGROUP_CONTROLLER_LEGACY, "systemd", false);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!userns)
|
||||
return mount_verbose(LOG_ERR, NULL, cgroup_root, NULL,
|
||||
MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY, "mode=755");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Mount legacy cgroup hierarchy when cgroup namespaces are unsupported. */
|
||||
static int mount_legacy_cgns_unsupported(
|
||||
const char *dest,
|
||||
CGroupUnified unified_requested,
|
||||
bool userns,
|
||||
uid_t uid_shift,
|
||||
uid_t uid_range,
|
||||
const char *selinux_apifs_context) {
|
||||
|
||||
_cleanup_set_free_free_ Set *controllers = NULL;
|
||||
const char *cgroup_root;
|
||||
int r;
|
||||
|
||||
cgroup_root = prefix_roota(dest, "/sys/fs/cgroup");
|
||||
|
||||
(void) mkdir_p(cgroup_root, 0755);
|
||||
|
||||
/* Mount a tmpfs to /sys/fs/cgroup if it's not mounted there yet. */
|
||||
r = path_is_mount_point(cgroup_root, dest, AT_SYMLINK_FOLLOW);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to determine if /sys/fs/cgroup is already mounted: %m");
|
||||
if (r == 0) {
|
||||
_cleanup_free_ char *options = NULL;
|
||||
|
||||
r = tmpfs_patch_options("mode=755", uid_shift == 0 ? UID_INVALID : uid_shift, selinux_apifs_context, &options);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
r = mount_verbose(LOG_ERR, "tmpfs", cgroup_root, "tmpfs",
|
||||
MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME, options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = cg_all_unified();
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0)
|
||||
goto skip_controllers;
|
||||
|
||||
r = cg_kernel_controllers(&controllers);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to determine cgroup controllers: %m");
|
||||
|
||||
for (;;) {
|
||||
_cleanup_free_ char *controller = NULL, *origin = NULL, *combined = NULL;
|
||||
|
||||
controller = set_steal_first(controllers);
|
||||
if (!controller)
|
||||
break;
|
||||
|
||||
origin = prefix_root("/sys/fs/cgroup/", controller);
|
||||
if (!origin)
|
||||
return log_oom();
|
||||
|
||||
r = readlink_malloc(origin, &combined);
|
||||
if (r == -EINVAL) {
|
||||
/* Not a symbolic link, but directly a single cgroup hierarchy */
|
||||
|
||||
r = mount_legacy_cgroup_hierarchy(dest, controller, controller, true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
} else if (r < 0)
|
||||
return log_error_errno(r, "Failed to read link %s: %m", origin);
|
||||
else {
|
||||
_cleanup_free_ char *target = NULL;
|
||||
|
||||
target = prefix_root(dest, origin);
|
||||
if (!target)
|
||||
return log_oom();
|
||||
|
||||
/* A symbolic link, a combination of controllers in one hierarchy */
|
||||
|
||||
if (!filename_is_valid(combined)) {
|
||||
log_warning("Ignoring invalid combined hierarchy %s.", combined);
|
||||
continue;
|
||||
}
|
||||
|
||||
r = mount_legacy_cgroup_hierarchy(dest, combined, combined, true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = symlink_idempotent(combined, target);
|
||||
if (r == -EINVAL)
|
||||
return log_error_errno(r, "Invalid existing symlink for combined hierarchy: %m");
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to create symlink for combined hierarchy: %m");
|
||||
}
|
||||
}
|
||||
|
||||
skip_controllers:
|
||||
if (unified_requested >= CGROUP_UNIFIED_SYSTEMD) {
|
||||
r = mount_legacy_cgroup_hierarchy(dest, SYSTEMD_CGROUP_CONTROLLER_HYBRID, "unified", false);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = mount_legacy_cgroup_hierarchy(dest, SYSTEMD_CGROUP_CONTROLLER_LEGACY, "systemd", false);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return mount_verbose(LOG_ERR, NULL, cgroup_root, NULL,
|
||||
MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY, "mode=755");
|
||||
}
|
||||
|
||||
static int mount_unified_cgroups(const char *dest) {
|
||||
const char *p;
|
||||
int r;
|
||||
|
||||
assert(dest);
|
||||
|
||||
p = prefix_roota(dest, "/sys/fs/cgroup");
|
||||
|
||||
(void) mkdir_p(p, 0755);
|
||||
|
||||
r = path_is_mount_point(p, dest, AT_SYMLINK_FOLLOW);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to determine if %s is mounted already: %m", p);
|
||||
if (r > 0) {
|
||||
p = prefix_roota(dest, "/sys/fs/cgroup/cgroup.procs");
|
||||
if (access(p, F_OK) >= 0)
|
||||
return 0;
|
||||
if (errno != ENOENT)
|
||||
return log_error_errno(errno, "Failed to determine if mount point %s contains the unified cgroup hierarchy: %m", p);
|
||||
|
||||
log_error("%s is already mounted but not a unified cgroup hierarchy. Refusing.", p);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return mount_verbose(LOG_ERR, "cgroup", p, "cgroup2", MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL);
|
||||
}
|
||||
|
||||
int mount_cgroups(
|
||||
const char *dest,
|
||||
CGroupUnified unified_requested,
|
||||
bool userns,
|
||||
uid_t uid_shift,
|
||||
uid_t uid_range,
|
||||
const char *selinux_apifs_context,
|
||||
bool use_cgns) {
|
||||
|
||||
if (unified_requested >= CGROUP_UNIFIED_ALL)
|
||||
return mount_unified_cgroups(dest);
|
||||
if (use_cgns)
|
||||
return mount_legacy_cgns_supported(dest, unified_requested, userns, uid_shift, uid_range, selinux_apifs_context);
|
||||
|
||||
return mount_legacy_cgns_unsupported(dest, unified_requested, userns, uid_shift, uid_range, selinux_apifs_context);
|
||||
}
|
||||
|
||||
static int mount_systemd_cgroup_writable_one(const char *root, const char *own) {
|
||||
int r;
|
||||
|
||||
assert(root);
|
||||
assert(own);
|
||||
|
||||
/* Make our own cgroup a (writable) bind mount */
|
||||
r = mount_verbose(LOG_ERR, own, own, NULL, MS_BIND, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* And then remount the systemd cgroup root read-only */
|
||||
return mount_verbose(LOG_ERR, NULL, root, NULL,
|
||||
MS_BIND|MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_RDONLY, NULL);
|
||||
}
|
||||
|
||||
int mount_systemd_cgroup_writable(
|
||||
const char *dest,
|
||||
CGroupUnified unified_requested) {
|
||||
|
||||
_cleanup_free_ char *own_cgroup_path = NULL;
|
||||
const char *root, *own;
|
||||
int r;
|
||||
|
||||
assert(dest);
|
||||
|
||||
r = cg_pid_get_path(NULL, 0, &own_cgroup_path);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to determine our own cgroup path: %m");
|
||||
|
||||
/* If we are living in the top-level, then there's nothing to do... */
|
||||
if (path_equal(own_cgroup_path, "/"))
|
||||
return 0;
|
||||
|
||||
if (unified_requested >= CGROUP_UNIFIED_ALL) {
|
||||
|
||||
root = prefix_roota(dest, "/sys/fs/cgroup");
|
||||
own = strjoina(root, own_cgroup_path);
|
||||
|
||||
} else {
|
||||
|
||||
if (unified_requested >= CGROUP_UNIFIED_SYSTEMD) {
|
||||
root = prefix_roota(dest, "/sys/fs/cgroup/unified");
|
||||
own = strjoina(root, own_cgroup_path);
|
||||
|
||||
r = mount_systemd_cgroup_writable_one(root, own);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
root = prefix_roota(dest, "/sys/fs/cgroup/systemd");
|
||||
own = strjoina(root, own_cgroup_path);
|
||||
}
|
||||
|
||||
return mount_systemd_cgroup_writable_one(root, own);
|
||||
}
|
||||
|
||||
int setup_volatile_state(
|
||||
const char *directory,
|
||||
VolatileMode mode,
|
||||
|
||||
@@ -46,9 +46,6 @@ int overlay_mount_parse(CustomMount **l, size_t *n, const char *s, bool read_onl
|
||||
int mount_all(const char *dest, MountSettingsMask mount_settings, uid_t uid_shift, const char *selinux_apifs_context);
|
||||
int mount_sysfs(const char *dest, MountSettingsMask mount_settings);
|
||||
|
||||
int mount_cgroups(const char *dest, CGroupUnified unified_requested, bool userns, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context, bool use_cgns);
|
||||
int mount_systemd_cgroup_writable(const char *dest, CGroupUnified unified_requested);
|
||||
|
||||
int mount_custom(const char *dest, CustomMount *mounts, size_t n, bool userns, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context);
|
||||
|
||||
int setup_volatile(const char *directory, VolatileMode mode, bool userns, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context);
|
||||
@@ -56,3 +53,5 @@ int setup_volatile_state(const char *directory, VolatileMode mode, bool userns,
|
||||
|
||||
int pivot_root_parse(char **pivot_root_new, char **pivot_root_old, const char *s);
|
||||
int setup_pivot_root(const char *directory, const char *pivot_root_new, const char *pivot_root_old);
|
||||
|
||||
int tmpfs_patch_options(const char *options,uid_t uid_shift, const char *selinux_apifs_context, char **ret);
|
||||
|
||||
Reference in New Issue
Block a user