Unverified Commit 5ee510d6 by Stéphane Graber Committed by GitHub

Merge pull request #3651 from brauner/2021-02-04/fixes

cgroups: fix cgroup mounting
parents dfb71524 59114d80
......@@ -1636,9 +1636,14 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function,
* enough.
*/
ret = cgroup_attach(conf, name, lxcpath, pid);
if (ret == -ENOCGROUP2) {
if (ret) {
call_cleaner(cgroup_exit) struct cgroup_ops *cgroup_ops = NULL;
if (ret != -ENOCGROUP2) {
SYSERROR("Failed to attach cgroup");
goto on_error;
}
cgroup_ops = cgroup_init(conf);
if (!cgroup_ops)
goto on_error;
......@@ -1646,6 +1651,7 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function,
if (!cgroup_ops->attach(cgroup_ops, conf, name, lxcpath, pid))
goto on_error;
}
TRACE("Moved intermediate process %d into container's cgroups", pid);
}
......
......@@ -45,6 +45,7 @@
#include "mainloop.h"
#include "memory_utils.h"
#include "storage/storage.h"
#include "syscall_wrappers.h"
#include "utils.h"
#ifndef HAVE_STRLCPY
......@@ -1758,58 +1759,82 @@ static int cg_legacy_mount_controllers(int type, struct hierarchy *h,
* cgroups for the LXC_AUTO_CGROUP_FULL option.
*/
static int __cg_mount_direct(int type, struct hierarchy *h,
const char *controllerpath)
struct lxc_rootfs *rootfs,
int dfd_mnt_cgroupfs, const char *hierarchy_mnt)
{
__do_free char *controllers = NULL;
char *fstype = "cgroup2";
unsigned long flags = 0;
int ret;
__do_free char *controllers = NULL;
unsigned long flags = 0;
char *fstype;
int ret;
if (dfd_mnt_cgroupfs < 0)
return ret_errno(EINVAL);
flags |= MS_NOSUID;
flags |= MS_NOEXEC;
flags |= MS_NODEV;
flags |= MS_RELATIME;
flags |= MS_NOSUID;
flags |= MS_NOEXEC;
flags |= MS_NODEV;
flags |= MS_RELATIME;
if (type == LXC_AUTO_CGROUP_RO || type == LXC_AUTO_CGROUP_FULL_RO)
flags |= MS_RDONLY;
if (type == LXC_AUTO_CGROUP_RO || type == LXC_AUTO_CGROUP_FULL_RO)
flags |= MS_RDONLY;
if (is_unified_hierarchy(h)) {
fstype = "cgroup2";
} else {
fstype = "cgroup";
if (h->version != CGROUP2_SUPER_MAGIC) {
controllers = lxc_string_join(",", (const char **)h->controllers, false);
if (!controllers)
return -ENOMEM;
fstype = "cgroup";
controllers = lxc_string_join(",", (const char **)h->controllers, false);
if (!controllers)
return ret_errno(ENOMEM);
}
ret = mount("cgroup", controllerpath, fstype, flags, controllers);
ret = mount_at(dfd_mnt_cgroupfs, NULL, hierarchy_mnt,
PROTECT_OPATH_DIRECTORY, PROTECT_LOOKUP_BENEATH, fstype,
flags, controllers);
if (ret < 0 && errno == ENOSYS) {
__do_free char *target = NULL;
const char *rootfs_mnt;
rootfs_mnt = get_rootfs_mnt(rootfs);
target = must_make_path(rootfs_mnt, DEFAULT_CGROUP_MOUNTPOINT, hierarchy_mnt, NULL);
ret = safe_mount(NULL, target, fstype, flags, controllers, rootfs_mnt);
}
if (ret < 0)
return log_error_errno(-1, errno, "Failed to mount \"%s\" with cgroup filesystem type %s",
controllerpath, fstype);
return log_error_errno(ret, errno, "Failed to mount %s filesystem onto %d(%s)",
fstype, dfd_mnt_cgroupfs, maybe_empty(hierarchy_mnt));
DEBUG("Mounted \"%s\" with cgroup filesystem type %s", controllerpath, fstype);
DEBUG("Mounted cgroup filesystem %s onto %d(%s)",
fstype, dfd_mnt_cgroupfs, maybe_empty(hierarchy_mnt));
return 0;
}
static inline int cg_mount_in_cgroup_namespace(int type, struct hierarchy *h,
const char *controllerpath)
struct lxc_rootfs *rootfs,
int dfd_mnt_cgroupfs,
const char *hierarchy_mnt)
{
return __cg_mount_direct(type, h, controllerpath);
return __cg_mount_direct(type, h, rootfs, dfd_mnt_cgroupfs, hierarchy_mnt);
}
static inline int cg_mount_cgroup_full(int type, struct hierarchy *h,
const char *controllerpath)
struct lxc_rootfs *rootfs,
int dfd_mnt_cgroupfs,
const char *hierarchy_mnt)
{
if (type < LXC_AUTO_CGROUP_FULL_RO || type > LXC_AUTO_CGROUP_FULL_MIXED)
return 0;
return __cg_mount_direct(type, h, controllerpath);
return __cg_mount_direct(type, h, rootfs, dfd_mnt_cgroupfs, hierarchy_mnt);
}
__cgfsng_ops static bool cgfsng_mount(struct cgroup_ops *ops,
struct lxc_handler *handler,
const char *root, int type)
struct lxc_conf *conf, int type)
{
__do_close int dfd_mnt_cgroupfs = -EBADF;
__do_free char *cgroup_root = NULL;
bool has_cgns = false, wants_force_mount = false;
struct lxc_rootfs *rootfs = &conf->rootfs;
const char *rootfs_mnt = get_rootfs_mnt(rootfs);
int ret;
if (!ops)
......@@ -1818,7 +1843,7 @@ __cgfsng_ops static bool cgfsng_mount(struct cgroup_ops *ops,
if (!ops->hierarchies)
return true;
if (!handler || !handler->conf)
if (!conf)
return ret_set_errno(false, EINVAL);
if ((type & LXC_AUTO_CGROUP_MASK) == 0)
......@@ -1830,7 +1855,7 @@ __cgfsng_ops static bool cgfsng_mount(struct cgroup_ops *ops,
}
if (!wants_force_mount) {
wants_force_mount = !lxc_wants_cap(CAP_SYS_ADMIN, handler->conf);
wants_force_mount = !lxc_wants_cap(CAP_SYS_ADMIN, conf);
/*
* Most recent distro versions currently have init system that
......@@ -1855,18 +1880,26 @@ __cgfsng_ops static bool cgfsng_mount(struct cgroup_ops *ops,
else if (type == LXC_AUTO_CGROUP_FULL_NOSPEC)
type = LXC_AUTO_CGROUP_FULL_MIXED;
cgroup_root = must_make_path(root, DEFAULT_CGROUP_MOUNTPOINT, NULL);
if (ops->cgroup_layout == CGROUP_LAYOUT_UNIFIED) {
/* This is really the codepath that we want. */
if (pure_unified_layout(ops)) {
dfd_mnt_cgroupfs = open_at(rootfs->dfd_mnt,
DEFAULT_CGROUP_MOUNTPOINT_RELATIVE,
PROTECT_OPATH_DIRECTORY,
PROTECT_LOOKUP_BENEATH_XDEV, 0);
if (dfd_mnt_cgroupfs < 0)
return log_error_errno(-errno, errno, "Failed to open %d(%s)",
rootfs->dfd_mnt, DEFAULT_CGROUP_MOUNTPOINT_RELATIVE);
if (has_cgns && wants_force_mount) {
/*
* If cgroup namespaces are supported but the container
* will not have CAP_SYS_ADMIN after it has started we
* need to mount the cgroups manually.
*/
return cg_mount_in_cgroup_namespace(type, ops->unified, cgroup_root) == 0;
return cg_mount_in_cgroup_namespace(type, ops->unified, rootfs, dfd_mnt_cgroupfs, "") == 0;
}
return cg_mount_cgroup_full(type, ops->unified, cgroup_root) == 0;
return cg_mount_cgroup_full(type, ops->unified, rootfs, dfd_mnt_cgroupfs, "") == 0;
}
/*
......@@ -1874,23 +1907,28 @@ __cgfsng_ops static bool cgfsng_mount(struct cgroup_ops *ops,
* relying on RESOLVE_BENEATH so we need to skip the leading "/" in the
* DEFAULT_CGROUP_MOUNTPOINT define.
*/
ret = safe_mount_beneath(root, NULL,
DEFAULT_CGROUP_MOUNTPOINT_RELATIVE,
"tmpfs",
MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_RELATIME,
"size=10240k,mode=755");
if (ret < 0) {
if (errno != ENOSYS)
return log_error_errno(false, errno,
"Failed to mount tmpfs on %s",
DEFAULT_CGROUP_MOUNTPOINT);
ret = mount_at(rootfs->dfd_mnt, NULL, DEFAULT_CGROUP_MOUNTPOINT_RELATIVE,
PROTECT_OPATH_DIRECTORY, PROTECT_LOOKUP_BENEATH_XDEV,
"tmpfs", MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_RELATIME,
"size=10240k,mode=755");
if (ret < 0 && errno == ENOSYS) {
cgroup_root = must_make_path(rootfs_mnt, DEFAULT_CGROUP_MOUNTPOINT, NULL);
ret = safe_mount(NULL, cgroup_root, "tmpfs",
MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_RELATIME,
"size=10240k,mode=755", root);
"size=10240k,mode=755", rootfs_mnt);
}
if (ret < 0)
return false;
return log_error_errno(false, errno, "Failed to mount tmpfs on %s",
DEFAULT_CGROUP_MOUNTPOINT_RELATIVE);
dfd_mnt_cgroupfs = open_at(rootfs->dfd_mnt,
DEFAULT_CGROUP_MOUNTPOINT_RELATIVE,
PROTECT_OPATH_DIRECTORY,
PROTECT_LOOKUP_BENEATH_XDEV, 0);
if (dfd_mnt_cgroupfs < 0)
return log_error_errno(-errno, errno, "Failed to open %d(%s)",
rootfs->dfd_mnt, DEFAULT_CGROUP_MOUNTPOINT_RELATIVE);
for (int i = 0; ops->hierarchies[i]; i++) {
__do_free char *controllerpath = NULL, *path2 = NULL;
......@@ -1901,41 +1939,41 @@ __cgfsng_ops static bool cgfsng_mount(struct cgroup_ops *ops,
continue;
controller++;
controllerpath = must_make_path(cgroup_root, controller, NULL);
if (dir_exists(controllerpath))
continue;
ret = mkdir(controllerpath, 0755);
ret = mkdirat(dfd_mnt_cgroupfs, controller, 0000);
if (ret < 0)
return log_error_errno(false, errno, "Error creating cgroup path: %s", controllerpath);
return log_error_errno(false, errno, "Failed to create cgroup mountpoint %d(%s)", dfd_mnt_cgroupfs, controller);
if (has_cgns && wants_force_mount) {
/* If cgroup namespaces are supported but the container
/*
* If cgroup namespaces are supported but the container
* will not have CAP_SYS_ADMIN after it has started we
* need to mount the cgroups manually.
*/
ret = cg_mount_in_cgroup_namespace(type, h, controllerpath);
ret = cg_mount_in_cgroup_namespace(type, h, rootfs, dfd_mnt_cgroupfs, controller);
if (ret < 0)
return false;
continue;
}
ret = cg_mount_cgroup_full(type, h, controllerpath);
/* Here is where the ancient kernel section begins. */
ret = cg_mount_cgroup_full(type, h, rootfs, dfd_mnt_cgroupfs, controller);
if (ret < 0)
return false;
if (!cg_mount_needs_subdirs(type))
continue;
path2 = must_make_path(controllerpath, h->container_base_path,
ops->container_cgroup, NULL);
controllerpath = must_make_path(cgroup_root, controller, NULL);
if (dir_exists(controllerpath))
continue;
path2 = must_make_path(controllerpath, h->container_base_path, ops->container_cgroup, NULL);
ret = mkdir_p(path2, 0755);
if (ret < 0)
return false;
ret = cg_legacy_mount_controllers(type, h, controllerpath,
path2, ops->container_cgroup);
ret = cg_legacy_mount_controllers(type, h, controllerpath, path2, ops->container_cgroup);
if (ret < 0)
return false;
}
......@@ -2195,23 +2233,26 @@ static int cgroup_attach_leaf(const struct lxc_conf *conf, int unified_fd, pid_t
int idx = 1;
int ret;
char pidstr[INTTYPE_TO_STRLEN(int64_t) + 1];
size_t pidstr_len;
ssize_t pidstr_len;
/* Create leaf cgroup. */
ret = mkdirat(unified_fd, ".lxc", 0755);
if (ret < 0 && errno != EEXIST)
return log_error_errno(-1, errno, "Failed to create leaf cgroup \".lxc\"");
return log_error_errno(-errno, errno, "Failed to create leaf cgroup \".lxc\"");
pidstr_len = snprintf(pidstr, sizeof(pidstr), INT64_FMT, (int64_t)pid);
if (pidstr_len < 0 || (size_t)pidstr_len >= sizeof(pidstr))
return ret_errno(EIO);
pidstr_len = sprintf(pidstr, INT64_FMT, (int64_t)pid);
ret = lxc_writeat(unified_fd, ".lxc/cgroup.procs", pidstr, pidstr_len);
if (ret < 0)
ret = lxc_writeat(unified_fd, "cgroup.procs", pidstr, pidstr_len);
if (ret == 0)
return 0;
return log_trace(0, "Moved process %s into cgroup %d(.lxc)", pidstr, unified_fd);
/* this is a non-leaf node */
if (errno != EBUSY)
return log_error_errno(-1, errno, "Failed to attach to unified cgroup");
return log_error_errno(-errno, errno, "Failed to attach to unified cgroup");
do {
bool rm = false;
......@@ -2243,7 +2284,7 @@ static int cgroup_attach_leaf(const struct lxc_conf *conf, int unified_fd, pid_t
ret = lxc_writeat(unified_fd, attach_cgroup, pidstr, pidstr_len);
if (ret == 0)
return 0;
return log_trace(0, "Moved process %s into cgroup %d(%s)", pidstr, unified_fd, attach_cgroup);
if (rm && unlinkat(unified_fd, attach_cgroup, AT_REMOVEDIR))
SYSERROR("Failed to remove cgroup \"%d(%s)\"", unified_fd, attach_cgroup);
......@@ -2270,12 +2311,12 @@ static int cgroup_attach_create_leaf(const struct lxc_conf *conf,
if (ret < 0 && errno != EEXIST)
return log_error_errno(-1, errno, "Failed to create leaf cgroup \".lxc\"");
target_fd0 = openat(unified_fd, ".lxc/cgroup.procs", O_WRONLY | O_CLOEXEC | O_NOFOLLOW);
target_fd0 = open_at(unified_fd, ".lxc/cgroup.procs", PROTECT_OPEN_W, PROTECT_LOOKUP_BENEATH, 0);
if (target_fd0 < 0)
return log_error_errno(-errno, errno, "Failed to open \".lxc/cgroup.procs\"");
target_fds[0] = target_fd0;
target_fd1 = openat(unified_fd, "cgroup.procs", O_WRONLY | O_CLOEXEC | O_NOFOLLOW);
target_fd1 = open_at(unified_fd, "cgroup.procs", PROTECT_OPEN_W, PROTECT_LOOKUP_BENEATH, 0);
if (target_fd1 < 0)
return log_error_errno(-errno, errno, "Failed to open \".lxc/cgroup.procs\"");
target_fds[1] = target_fd1;
......@@ -2374,7 +2415,7 @@ static int __cg_unified_attach(const struct hierarchy *h,
ret = cgroup_attach(conf, name, lxcpath, pid);
if (ret == 0)
return log_trace(0, "Attached to unified cgroup via command handler");
if (ret != -EBADF)
if (ret != -ENOCGROUP2)
return log_error_errno(ret, errno, "Failed to attach to unified cgroup");
/* Fall back to retrieving the path for the unified cgroup. */
......@@ -3466,7 +3507,7 @@ int cgroup_attach(const struct lxc_conf *conf, const char *name,
__do_close int unified_fd = -EBADF;
int ret;
if (!conf || is_empty_string(name) || !is_empty_string(lxcpath) || pid <= 0)
if (!conf || is_empty_string(name) || is_empty_string(lxcpath) || pid <= 0)
return ret_errno(EINVAL);
unified_fd = lxc_cmd_get_cgroup2_fd(name, lxcpath);
......
......@@ -172,8 +172,7 @@ struct cgroup_ops {
bool (*chown)(struct cgroup_ops *ops, struct lxc_conf *conf);
bool (*attach)(struct cgroup_ops *ops, const struct lxc_conf *conf,
const char *name, const char *lxcpath, pid_t pid);
bool (*mount)(struct cgroup_ops *ops, struct lxc_handler *handler,
const char *root, int type);
bool (*mount)(struct cgroup_ops *ops, struct lxc_conf *conf, int type);
bool (*devices_activate)(struct cgroup_ops *ops,
struct lxc_handler *handler);
bool (*monitor_delegate_controllers)(struct cgroup_ops *ops);
......
......@@ -643,17 +643,17 @@ static int lxc_mount_auto_mounts(struct lxc_conf *conf, int flags, struct lxc_ha
bool has_cap_net_admin;
if (flags & LXC_AUTO_PROC_MASK) {
ret = mkdirat(rootfs->mntpt_fd, "proc" , S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
ret = mkdirat(rootfs->dfd_mnt, "proc" , S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
if (ret < 0 && errno != EEXIST)
return log_error_errno(-errno, errno,
"Failed to create proc mountpoint under %d", rootfs->mntpt_fd);
"Failed to create proc mountpoint under %d", rootfs->dfd_mnt);
}
if (flags & LXC_AUTO_SYS_MASK) {
ret = mkdirat(rootfs->mntpt_fd, "sys" , S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
ret = mkdirat(rootfs->dfd_mnt, "sys" , S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
if (ret < 0 && errno != EEXIST)
return log_error_errno(-errno, errno,
"Failed to create sysfs mountpoint under %d", rootfs->mntpt_fd);
"Failed to create sysfs mountpoint under %d", rootfs->dfd_mnt);
}
has_cap_net_admin = lxc_wants_cap(CAP_NET_ADMIN, conf);
......@@ -734,10 +734,7 @@ static int lxc_mount_auto_mounts(struct lxc_conf *conf, int flags, struct lxc_ha
if (flags & LXC_AUTO_CGROUP_FORCE)
cg_flags |= LXC_AUTO_CGROUP_FORCE;
if (!handler->cgroup_ops->mount(handler->cgroup_ops,
handler,
rootfs->path ? rootfs->mount : "",
cg_flags))
if (!handler->cgroup_ops->mount(handler->cgroup_ops, conf, cg_flags))
return log_error_errno(-1, errno, "Failed to mount \"/sys/fs/cgroup\"");
}
......@@ -790,11 +787,11 @@ static int lxc_setup_dev_symlinks(const struct lxc_rootfs *rootfs)
* Stat the path first. If we don't get an error accept it as
* is and don't try to create it
*/
ret = fstatat(rootfs->dev_mntpt_fd, d->name, &s, 0);
ret = fstatat(rootfs->dfd_dev, d->name, &s, 0);
if (ret == 0)
continue;
ret = symlinkat(d->oldpath, rootfs->dev_mntpt_fd, d->name);
ret = symlinkat(d->oldpath, rootfs->dfd_dev, d->name);
if (ret) {
switch (errno) {
case EROFS:
......@@ -1074,14 +1071,14 @@ static int mount_autodev(const char *name, const struct lxc_rootfs *rootfs,
DEBUG("Using mount options: %s", mount_options);
cur_mask = umask(S_IXUSR | S_IXGRP | S_IXOTH);
ret = mkdirat(rootfs->mntpt_fd, "dev" , S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
ret = mkdirat(rootfs->dfd_mnt, "dev" , S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
if (ret < 0 && errno != EEXIST) {
SYSERROR("Failed to create \"/dev\" directory");
ret = -errno;
goto reset_umask;
}
ret = safe_mount_beneath_at(rootfs->mntpt_fd, "none", "dev", "tmpfs", 0, mount_options);
ret = safe_mount_beneath_at(rootfs->dfd_mnt, "none", "dev", "tmpfs", 0, mount_options);
if (ret < 0) {
__do_free char *fallback_path = NULL;
......@@ -1106,7 +1103,7 @@ static int mount_autodev(const char *name, const struct lxc_rootfs *rootfs,
/* If we are running on a devtmpfs mapping, dev/pts may already exist.
* If not, then create it and exit if that fails...
*/
ret = mkdirat(rootfs->mntpt_fd, "dev/pts", S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
ret = mkdirat(rootfs->dfd_mnt, "dev/pts", S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
if (ret < 0 && errno != EEXIST) {
SYSERROR("Failed to create directory \"%s\"", path);
ret = -errno;
......@@ -1152,18 +1149,18 @@ static int lxc_fill_autodev(const struct lxc_rootfs *rootfs)
mode_t cmask;
int use_mknod = LXC_DEVNODE_MKNOD;
if (rootfs->dev_mntpt_fd < 0)
if (rootfs->dfd_dev < 0)
return log_info(0, "No /dev directory found, skipping setup");
INFO("Populating \"/dev\"");
cmask = umask(S_IXUSR | S_IXGRP | S_IXOTH);
for (i = 0; i < sizeof(lxc_devices) / sizeof(lxc_devices[0]); i++) {
char hostpath[PATH_MAX], path[PATH_MAX];
char device_path[PATH_MAX];
const struct lxc_device_node *device = &lxc_devices[i];
if (use_mknod >= LXC_DEVNODE_MKNOD) {
ret = mknodat(rootfs->dev_mntpt_fd, device->name, device->mode, makedev(device->maj, device->min));
ret = mknodat(rootfs->dfd_dev, device->name, device->mode, makedev(device->maj, device->min));
if (ret == 0 || (ret < 0 && errno == EEXIST)) {
DEBUG("Created device node \"%s\"", device->name);
} else if (ret < 0) {
......@@ -1183,7 +1180,7 @@ static int lxc_fill_autodev(const struct lxc_rootfs *rootfs)
* - https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=55956b59df336f6738da916dbb520b6e37df9fbd
* - https://lists.linuxfoundation.org/pipermail/containers/2018-June/039176.html
*/
fd = open_at(rootfs->dev_mntpt_fd, device->name, PROTECT_OPEN, PROTECT_LOOKUP_BENEATH, 0);
fd = open_at(rootfs->dfd_dev, device->name, PROTECT_OPEN, PROTECT_LOOKUP_BENEATH, 0);
if (fd >= 0) {
/* Device nodes are fully useable. */
use_mknod = LXC_DEVNODE_OPEN;
......@@ -1201,29 +1198,52 @@ static int lxc_fill_autodev(const struct lxc_rootfs *rootfs)
* nodes the prio mknod() call will have created the
* device node so we can use it as a bind-mount target.
*/
ret = mknodat(rootfs->dev_mntpt_fd, device->name, S_IFREG | 0000, 0);
ret = mknodat(rootfs->dfd_dev, device->name, S_IFREG | 0000, 0);
if (ret < 0 && errno != EEXIST)
return log_error_errno(-1, errno, "Failed to create file \"%s\"", device->name);
}
/* Fallback to bind-mounting the device from the host. */
ret = snprintf(hostpath, sizeof(hostpath), "/dev/%s", device->name);
if (ret < 0 || (size_t)ret >= sizeof(hostpath))
ret = snprintf(device_path, sizeof(device_path), "dev/%s", device->name);
if (ret < 0 || (size_t)ret >= sizeof(device_path))
return ret_errno(EIO);
ret = safe_mount_beneath_at(rootfs->dev_mntpt_fd, hostpath, device->name, NULL, MS_BIND, NULL);
ret = mount_from_at(rootfs->dfd_host, device_path,
PROTECT_OPATH_FILE,
PROTECT_LOOKUP_BENEATH_XDEV,
rootfs->dfd_dev, device->name,
PROTECT_OPATH_FILE,
PROTECT_LOOKUP_BENEATH,
NULL /* fstype */,
MS_BIND /* mount flags */,
NULL);
if (ret < 0) {
const char *mntpt = rootfs->path ? rootfs->mount : NULL;
if (errno == ENOSYS) {
ret = snprintf(path, sizeof(path), "%s/dev/%s", mntpt, device->name);
if (ret < 0 || ret >= sizeof(path))
return log_error(-1, "Failed to create device path for %s", device->name);
ret = safe_mount(hostpath, path, 0, MS_BIND, NULL, rootfs->path ? rootfs->mount : NULL);
}
char path[PATH_MAX];
if (errno != ENOSYS)
return log_error_errno(-errno, errno,
"Failed to mount %d(%s) to %d(%s)",
rootfs->dfd_host,
device_path,
rootfs->dfd_dev,
device->name);
ret = snprintf(device_path, sizeof(device_path), "/dev/%s", device->name);
if (ret < 0 || (size_t)ret >= sizeof(device_path))
return ret_errno(EIO);
ret = snprintf(path, sizeof(path), "%s/dev/%s", get_rootfs_mnt(rootfs), device->name);
if (ret < 0 || ret >= sizeof(path))
return log_error(-1, "Failed to create device path for %s", device->name);
ret = safe_mount(device_path, path, 0, MS_BIND, NULL, get_rootfs_mnt(rootfs));
if (ret < 0)
return log_error_errno(-1, errno, "Failed to bind mount host device node \"%s\" to \"%s\"", device_path, path);
DEBUG("Bind mounted host device node \"%s\" to \"%s\"", device_path, path);
continue;
}
if (ret < 0)
return log_error_errno(-1, errno, "Failed to bind mount host device node \"%s\" onto \"%s\"", hostpath, device->name);
DEBUG("Bind mounted host device node \"%s\" onto \"%s\"", hostpath, device->name);
DEBUG("Bind mounted host device %d(%s) to %d(%s)", rootfs->dfd_host, device_path, rootfs->dfd_dev, device->name);
}
(void)umask(cmask);
......@@ -1242,8 +1262,8 @@ static int lxc_mount_rootfs(struct lxc_conf *conf)
if (ret < 0)
return log_error_errno(-1, errno, "Failed to recursively turn root mount tree into dependent mount");
rootfs->mntpt_fd = open_at(-EBADF, "/", PROTECT_OPATH_DIRECTORY, PROTECT_LOOKUP_ABSOLUTE, 0);
if (rootfs->mntpt_fd < 0)
rootfs->dfd_mnt = open_at(-EBADF, "/", PROTECT_OPATH_DIRECTORY, PROTECT_LOOKUP_ABSOLUTE, 0);
if (rootfs->dfd_mnt < 0)
return -errno;
return 0;
......@@ -1271,8 +1291,8 @@ static int lxc_mount_rootfs(struct lxc_conf *conf)
rootfs->path, rootfs->mount,
rootfs->options ? rootfs->options : "(null)");
rootfs->mntpt_fd = open_at(-EBADF, rootfs->mount, PROTECT_OPATH_DIRECTORY, PROTECT_LOOKUP_ABSOLUTE_XDEV, 0);
if (rootfs->mntpt_fd < 0)
rootfs->dfd_mnt = open_at(-EBADF, rootfs->mount, PROTECT_OPATH_DIRECTORY, PROTECT_LOOKUP_ABSOLUTE_XDEV, 0);
if (rootfs->dfd_mnt < 0)
return -errno;
return 0;
......@@ -1404,7 +1424,7 @@ static int lxc_pivot_root(const struct lxc_rootfs *rootfs)
return log_error_errno(-1, errno, "Failed to open old root directory");
/* change into new root fs */
ret = fchdir(rootfs->mntpt_fd);
ret = fchdir(rootfs->dfd_mnt);
if (ret < 0)
return log_error_errno(-errno, errno, "Failed to change into new root directory \"%s\"", rootfs->mount);
......@@ -1433,7 +1453,7 @@ static int lxc_pivot_root(const struct lxc_rootfs *rootfs)
if (ret < 0)
return log_error_errno(-errno, errno, "Failed to detach old root directory");
ret = fchdir(rootfs->mntpt_fd);
ret = fchdir(rootfs->dfd_mnt);
if (ret < 0)
return log_error_errno(-errno, errno, "Failed to re-enter new root directory \"%s\"", rootfs->mount);
......@@ -1522,7 +1542,7 @@ static int lxc_setup_devpts_child(struct lxc_handler *handler)
(void)umount2("/dev/pts", MNT_DETACH);
/* Create mountpoint for devpts instance. */
ret = mkdirat(rootfs->dev_mntpt_fd, "pts", 0755);
ret = mkdirat(rootfs->dfd_dev, "pts", 0755);
if (ret < 0 && errno != EEXIST)
return log_error_errno(-1, errno, "Failed to create \"/dev/pts\" directory");
......@@ -1552,7 +1572,7 @@ static int lxc_setup_devpts_child(struct lxc_handler *handler)
return log_error_errno(-1, errno, "Failed to mount new devpts instance");
DEBUG("Mount new devpts instance with options \"%s\"", *opts);
devpts_fd = open_at(rootfs->dev_mntpt_fd, "pts", PROTECT_OPATH_DIRECTORY, PROTECT_LOOKUP_BENEATH_XDEV, 0);
devpts_fd = open_at(rootfs->dfd_dev, "pts", PROTECT_OPATH_DIRECTORY, PROTECT_LOOKUP_BENEATH_XDEV, 0);
if (devpts_fd < 0) {
devpts_fd = -EBADF;
TRACE("Failed to create detached devpts mount");
......@@ -1566,7 +1586,7 @@ static int lxc_setup_devpts_child(struct lxc_handler *handler)
TRACE("Sent devpts file descriptor %d to parent", devpts_fd);
/* Remove any pre-existing /dev/ptmx file. */
ret = unlinkat(rootfs->dev_mntpt_fd, "ptmx", 0);
ret = unlinkat(rootfs->dfd_dev, "ptmx", 0);
if (ret < 0) {
if (errno != ENOENT)
return log_error_errno(-1, errno, "Failed to remove existing \"/dev/ptmx\" file");
......@@ -1575,7 +1595,7 @@ static int lxc_setup_devpts_child(struct lxc_handler *handler)
}
/* Create dummy /dev/ptmx file as bind mountpoint for /dev/pts/ptmx. */
ret = mknodat(rootfs->dev_mntpt_fd, "ptmx", S_IFREG | 0000, 0);
ret = mknodat(rootfs->dfd_dev, "ptmx", S_IFREG | 0000, 0);
if (ret < 0 && errno != EEXIST)
return log_error_errno(-1, errno, "Failed to create dummy \"/dev/ptmx\" file as bind mount target");
DEBUG("Created dummy \"/dev/ptmx\" file as bind mount target");
......@@ -1589,12 +1609,12 @@ static int lxc_setup_devpts_child(struct lxc_handler *handler)
ERROR("Failed to bind mount \"/dev/pts/ptmx\" to \"/dev/ptmx\"");
/* Remove the dummy /dev/ptmx file we created above. */
ret = unlinkat(rootfs->dev_mntpt_fd, "ptmx", 0);
ret = unlinkat(rootfs->dfd_dev, "ptmx", 0);
if (ret < 0)
return log_error_errno(-1, errno, "Failed to remove existing \"/dev/ptmx\"");
/* Fallback option: Create symlink /dev/ptmx -> /dev/pts/ptmx. */
ret = symlinkat("/dev/pts/ptmx", rootfs->dev_mntpt_fd, "/dev/ptmx");
ret = symlinkat("/dev/pts/ptmx", rootfs->dfd_dev, "/dev/ptmx");
if (ret < 0)
return log_error_errno(-1, errno, "Failed to create symlink from \"/dev/ptmx\" to \"/dev/pts/ptmx\"");
......@@ -1640,7 +1660,7 @@ static int lxc_setup_dev_console(const struct lxc_rootfs *rootfs,
* When we are asked to setup a console we remove any previous
* /dev/console bind-mounts.
*/
if (exists_file_at(rootfs->dev_mntpt_fd, "console")) {
if (exists_file_at(rootfs->dfd_dev, "console")) {
ret = snprintf(path, sizeof(path), "%s/dev/console", rootfs_path);
if (ret < 0 || (size_t)ret >= sizeof(path))
return -1;
......@@ -1656,7 +1676,7 @@ static int lxc_setup_dev_console(const struct lxc_rootfs *rootfs,
* For unprivileged containers autodev or automounts will already have
* taken care of creating /dev/console.
*/
ret = mknodat(rootfs->dev_mntpt_fd, "console", S_IFREG | 0000, 0);
ret = mknodat(rootfs->dfd_dev, "console", S_IFREG | 0000, 0);
if (ret < 0 && errno != EEXIST)
return log_error_errno(-errno, errno, "Failed to create console");
......@@ -1665,19 +1685,19 @@ static int lxc_setup_dev_console(const struct lxc_rootfs *rootfs,
return log_error_errno(-errno, errno, "Failed to set mode \"0%o\" to \"%s\"", S_IXUSR | S_IXGRP, console->name);
if (pty_mnt_fd >= 0) {
ret = move_mount(pty_mnt_fd, "", rootfs->dev_mntpt_fd, "console", MOVE_MOUNT_F_EMPTY_PATH);
ret = move_mount(pty_mnt_fd, "", rootfs->dfd_dev, "console", MOVE_MOUNT_F_EMPTY_PATH);
if (!ret) {
DEBUG("Moved mount \"%s\" onto \"%s\"", console->name, path);
goto finish;
DEBUG("Moved mount \"%s\" onto %d(console)", console->name, rootfs->dfd_dev);
return 0;
}
if (ret && errno != ENOSYS)
return log_error_errno(-1, errno,
"Failed to mount %d(%s) on \"%s\"",
pty_mnt_fd, console->name, path);
"Failed to mount %d(%s) on %d(console)",
pty_mnt_fd, console->name, rootfs->dfd_dev);
}
ret = safe_mount_beneath_at(rootfs->dev_mntpt_fd, console->name, "console", NULL, MS_BIND, NULL);
ret = safe_mount_beneath_at(rootfs->dfd_dev, console->name, "console", NULL, MS_BIND, NULL);
if (ret < 0) {
if (errno == ENOSYS) {
ret = snprintf(path, sizeof(path), "%s/dev/console", rootfs_path);
......@@ -1690,7 +1710,6 @@ static int lxc_setup_dev_console(const struct lxc_rootfs *rootfs,
}
}
finish:
DEBUG("Mounted pty device %d(%s) onto \"%s\"", pty_mnt_fd, console->name, path);
return 0;
}
......@@ -2614,8 +2633,9 @@ struct lxc_conf *lxc_conf_init(void)
return NULL;
}
new->rootfs.managed = true;
new->rootfs.mntpt_fd = -EBADF;
new->rootfs.dev_mntpt_fd = -EBADF;
new->rootfs.dfd_mnt = -EBADF;
new->rootfs.dfd_dev = -EBADF;
new->rootfs.dfd_host = -EBADF;
new->logfd = -1;
lxc_list_init(&new->cgroup);
lxc_list_init(&new->cgroup2);
......@@ -2964,11 +2984,11 @@ static int lxc_transient_proc(struct lxc_rootfs *rootfs)
int link_to_pid, link_len, pid_self, ret;
char link[INTTYPE_TO_STRLEN(pid_t) + 1];
link_len = readlinkat(rootfs->mntpt_fd, "proc/self", link, sizeof(link));
link_len = readlinkat(rootfs->dfd_mnt, "proc/self", link, sizeof(link));
if (link_len < 0) {
ret = mkdirat(rootfs->mntpt_fd, "proc", 0000);
ret = mkdirat(rootfs->dfd_mnt, "proc", 0000);
if (ret < 0 && errno != EEXIST)
return log_error_errno(-errno, errno, "Failed to create %d(proc)", rootfs->mntpt_fd);
return log_error_errno(-errno, errno, "Failed to create %d(proc)", rootfs->dfd_mnt);
goto domount;
} else if (link_len >= sizeof(link)) {
......@@ -2987,7 +3007,7 @@ static int lxc_transient_proc(struct lxc_rootfs *rootfs)
if (link_to_pid == pid_self)
return log_trace(0, "Correct procfs instance mounted");
fd_proc = open_at(rootfs->mntpt_fd, "proc", PROTECT_OPATH_DIRECTORY,
fd_proc = open_at(rootfs->dfd_mnt, "proc", PROTECT_OPATH_DIRECTORY,
PROTECT_LOOKUP_BENEATH_XDEV, 0);
if (fd_proc < 0)
return log_error_errno(-errno, errno, "Failed to open transient procfs mountpoint");
......@@ -3005,7 +3025,7 @@ domount:
if (!rootfs->path) {
ret = mount("proc", rootfs->buf, "proc", 0, NULL);
} else {
ret = safe_mount_beneath_at(rootfs->mntpt_fd, "none", "proc", "proc", 0, NULL);
ret = safe_mount_beneath_at(rootfs->dfd_mnt, "none", "proc", "proc", 0, NULL);
if (ret < 0) {
ret = snprintf(rootfs->buf, sizeof(rootfs->buf), "%s/proc", rootfs->path ? rootfs->mount : "");
if (ret < 0 || (size_t)ret >= sizeof(rootfs->buf))
......@@ -3188,6 +3208,10 @@ int lxc_setup_rootfs_prepare_root(struct lxc_conf *conf, const char *name,
{
int ret;
conf->rootfs.dfd_host = open_at(-EBADF, "/", PROTECT_OPATH_DIRECTORY, PROTECT_LOOKUP_ABSOLUTE, 0);
if (conf->rootfs.dfd_host < 0)
return log_error_errno(-errno, errno, "Failed to open \"/\"");
if (conf->rootfs_setup) {
const char *path = conf->rootfs.mount;
......@@ -3198,8 +3222,8 @@ int lxc_setup_rootfs_prepare_root(struct lxc_conf *conf, const char *name,
if (ret < 0)
return log_error(-1, "Failed to bind mount container / onto itself");
conf->rootfs.mntpt_fd = openat(-EBADF, path, O_RDONLY | O_CLOEXEC | O_DIRECTORY | O_PATH | O_NOCTTY);
if (conf->rootfs.mntpt_fd < 0)
conf->rootfs.dfd_mnt = openat(-EBADF, path, O_RDONLY | O_CLOEXEC | O_DIRECTORY | O_PATH | O_NOCTTY);
if (conf->rootfs.dfd_mnt < 0)
return log_error_errno(-errno, errno, "Failed to open file descriptor for container rootfs");
return log_trace(0, "Bind mounted container / onto itself");
......@@ -3391,10 +3415,10 @@ int lxc_setup(struct lxc_handler *handler)
return log_error(-1, "Failed to mount \"/dev\"");
}
lxc_conf->rootfs.dev_mntpt_fd = open_at(lxc_conf->rootfs.mntpt_fd, "dev",
lxc_conf->rootfs.dfd_dev = open_at(lxc_conf->rootfs.dfd_mnt, "dev",
PROTECT_OPATH_DIRECTORY,
PROTECT_LOOKUP_BENEATH_XDEV, 0);
if (lxc_conf->rootfs.dev_mntpt_fd < 0 && errno != ENOENT)
if (lxc_conf->rootfs.dfd_dev < 0 && errno != ENOENT)
return log_error_errno(-errno, errno, "Failed to open \"/dev\"");
/* Do automatic mounts (mainly /proc and /sys), but exclude those that
......@@ -3516,8 +3540,9 @@ int lxc_setup(struct lxc_handler *handler)
return log_error(-1, "Failed to drop capabilities");
}
close_prot_errno_disarm(lxc_conf->rootfs.mntpt_fd)
close_prot_errno_disarm(lxc_conf->rootfs.dev_mntpt_fd)
close_prot_errno_disarm(lxc_conf->rootfs.dfd_mnt)
close_prot_errno_disarm(lxc_conf->rootfs.dfd_dev)
close_prot_errno_disarm(lxc_conf->rootfs.dfd_host)
NOTICE("The container \"%s\" is set up", name);
return 0;
......@@ -3881,8 +3906,9 @@ void lxc_conf_free(struct lxc_conf *conf)
free(conf->rootfs.options);
free(conf->rootfs.path);
free(conf->rootfs.data);
close_prot_errno_disarm(conf->rootfs.mntpt_fd);
close_prot_errno_disarm(conf->rootfs.dev_mntpt_fd);
close_prot_errno_disarm(conf->rootfs.dfd_mnt);
close_prot_errno_disarm(conf->rootfs.dfd_dev);
close_prot_errno_disarm(conf->rootfs.dfd_host);
free(conf->logfile);
if (conf->logfd != -1)
close(conf->logfd);
......
......@@ -23,6 +23,7 @@
#include "memory_utils.h"
#include "ringbuf.h"
#include "start.h"
#include "string_utils.h"
#include "terminal.h"
#if HAVE_SYS_RESOURCE_H
......@@ -189,12 +190,13 @@ struct lxc_tty_info {
* @mountflags : the portion of @options that are flags
* @data : the portion of @options that are not flags
* @managed : whether it is managed by LXC
* @mntpt_fd : fd for @mount
* @dev_mntpt_fd : fd for /dev of the container
* @dfd_mnt : fd for @mount
* @dfd_dev : fd for /dev of the container
*/
struct lxc_rootfs {
int mntpt_fd;
int dev_mntpt_fd;
int dfd_host;
int dfd_mnt;
int dfd_dev;
char *path;
char *mount;
char buf[PATH_MAX];
......@@ -547,4 +549,11 @@ static inline int chown_mapped_root(const char *path, const struct lxc_conf *con
__hidden int lxc_setup_devpts_parent(struct lxc_handler *handler);
static inline const char *get_rootfs_mnt(const struct lxc_rootfs *rootfs)
{
static const char *s = "/";
return !is_empty_string(rootfs->path) ? rootfs->mount : s;
}
#endif /* __LXC_CONF_H */
......@@ -117,6 +117,8 @@ static inline bool is_empty_string(const char *s)
return !s || strcmp(s, "") == 0;
}
#define maybe_empty(s) ((!is_empty_string(s)) ? (s) : ("(null)"))
static inline ssize_t safe_strlcat(char *src, const char *append, size_t len)
{
size_t new_len;
......
......@@ -271,7 +271,7 @@ struct lxc_open_how {
#define PROTECT_OPEN (PROTECT_OPEN_WITH_TRAILING_SYMLINKS | O_NOFOLLOW)
#define PROTECT_OPEN_W_WITH_TRAILING_SYMLINKS (O_CLOEXEC | O_NOCTTY | O_WRONLY)
#define PROTECT_OPEN_W (PROTECT_OPEN_WITH_TRAILING_SYMLINKS | O_NOFOLLOW)
#define PROTECT_OPEN_W (PROTECT_OPEN_W_WITH_TRAILING_SYMLINKS | O_NOFOLLOW)
#ifndef HAVE_OPENAT2
static inline int openat2(int dfd, const char *filename, struct lxc_open_how *how, size_t size)
......
......@@ -1208,6 +1208,108 @@ int safe_mount(const char *src, const char *dest, const char *fstype,
return 0;
}
int mount_at(int dfd,
const char *src_under_dfd,
const char *dst_under_dfd,
__u64 o_flags,
__u64 resolve_flags,
const char *fstype,
unsigned int mnt_flags,
const void *data)
{
__do_close int source_fd = -EBADF, target_fd = -EBADF;
struct lxc_open_how how = {
.flags = o_flags,
.resolve = resolve_flags,
};
int ret;
char src_buf[LXC_PROC_PID_FD_LEN], dst_buf[LXC_PROC_PID_FD_LEN];
if (dfd < 0)
return ret_errno(EINVAL);
if (!is_empty_string(src_buf) && *src_buf == '/')
return log_error_errno(-EINVAL, EINVAL, "Absolute path specified");
if (!is_empty_string(src_under_dfd)) {
source_fd = openat2(dfd, src_under_dfd, &how, sizeof(how));
if (source_fd < 0)
return -errno;
ret = snprintf(src_buf, sizeof(src_buf), "/proc/self/fd/%d", source_fd);
if (ret < 0 || ret >= sizeof(src_buf))
return -EIO;
}
if (!is_empty_string(dst_under_dfd)) {
target_fd = openat2(dfd, dst_under_dfd, &how, sizeof(how));
if (target_fd < 0)
return log_error_errno(-errno, errno, "Failed to open %d(%s)", dfd, dst_under_dfd);
TRACE("Mounting %d(%s) through /proc/self/fd/%d", target_fd, dst_under_dfd, target_fd);
ret = snprintf(dst_buf, sizeof(dst_buf), "/proc/self/fd/%d", target_fd);
} else {
TRACE("Mounting %d through /proc/self/fd/%d", dfd, dfd);
ret = snprintf(dst_buf, sizeof(dst_buf), "/proc/self/fd/%d", dfd);
}
if (ret < 0 || ret >= sizeof(dst_buf))
return -EIO;
if (!is_empty_string(src_buf))
ret = mount(src_buf, dst_buf, fstype, mnt_flags, data);
else
ret = mount(NULL, dst_buf, fstype, mnt_flags, data);
return ret;
}
int mount_from_at(int dfd_from, const char *path_from,
__u64 o_flags_from,
__u64 resolve_flags_from,
int dfd_to, const char *path_to,
__u64 o_flags_to,
__u64 resolve_flags_to,
const char *fstype, unsigned int mnt_flags, const void *data)
{
__do_close int fd_from = -EBADF, fd_to = -EBADF;
struct lxc_open_how how = {};
int ret;
char src_buf[LXC_PROC_PID_FD_LEN], dst_buf[LXC_PROC_PID_FD_LEN];
if (is_empty_string(path_from)) {
ret = snprintf(src_buf, sizeof(src_buf), "/proc/self/fd/%d", dfd_from);
} else {
how.flags = o_flags_from;
how.resolve = resolve_flags_from;
fd_from = openat2(dfd_from, path_from, &how, sizeof(how));
if (fd_from < 0)
return -errno;
ret = snprintf(src_buf, sizeof(src_buf), "/proc/self/fd/%d", fd_from);
}
if (ret < 0 || ret >= sizeof(src_buf))
return -EIO;
if (is_empty_string(path_to)) {
ret = snprintf(dst_buf, sizeof(dst_buf), "/proc/self/fd/%d", dfd_to);
} else {
how.flags = o_flags_to;
how.resolve = resolve_flags_to;
fd_to = openat2(dfd_to, path_to, &how, sizeof(how));
if (fd_to < 0)
return -errno;
ret = snprintf(dst_buf, sizeof(dst_buf), "/proc/self/fd/%d", fd_to);
}
if (is_empty_string(src_buf))
ret = mount(NULL, dst_buf, fstype, mnt_flags, data);
else
ret = mount(src_buf, dst_buf, fstype, mnt_flags, data);
return ret;
}
int open_devnull(void)
{
int fd = open("/dev/null", O_RDWR);
......
......@@ -243,5 +243,15 @@ __hidden extern int safe_mount_beneath(const char *beneath, const char *src, con
const char *fstype, unsigned int flags, const void *data);
__hidden extern int safe_mount_beneath_at(int beneat_fd, const char *src, const char *dst,
const char *fstype, unsigned int flags, const void *data);
__hidden extern int mount_at(int dfd, const char *src_under_dfd,
const char *dst_under_dfd, __u64 o_flags,
__u64 resolve_flags, const char *fstype,
unsigned int mnt_flags, const void *data);
__hidden extern int mount_from_at(int dfd_from, const char *path_from,
__u64 o_flags_from, __u64 resolve_flags_from,
int dfd_to, const char *path_to,
__u64 o_flags_to, __u64 resolve_flags_to,
const char *fstype, unsigned int mnt_flags,
const void *data);
#endif /* __LXC_UTILS_H */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment