/* SPDX-License-Identifier: LGPL-2.1+ */
-#ifndef _GNU_SOURCE
-#define _GNU_SOURCE 1
-#endif
+#include "config.h"
+
#include <arpa/inet.h>
#include <dirent.h>
#include <errno.h>
#include <time.h>
#include <unistd.h>
+#include "conf.h"
#include "af_unix.h"
#include "caps.h"
#include "cgroups/cgroup.h"
#include "compiler.h"
-#include "conf.h"
-#include "config.h"
#include "confile.h"
#include "confile_utils.h"
#include "error.h"
#include "mount_utils.h"
#include "namespace.h"
#include "network.h"
+#include "open_utils.h"
#include "parse.h"
#include "process_utils.h"
#include "ringbuf.h"
#if HAVE_OPENPTY
#include <pty.h>
#else
-#include <../include/openpty.h>
+#include "openpty.h"
#endif
#if HAVE_LIBCAP
#include <sys/capability.h>
#endif
-#ifndef HAVE_STRLCAT
-#include "include/strlcat.h"
+#if !HAVE_STRLCAT
+#include "strlcat.h"
#endif
#if IS_BIONIC
-#include <../include/lxcmntent.h>
+#include "lxcmntent.h"
#else
#include <mntent.h>
#endif
-#if !defined(HAVE_PRLIMIT) && defined(HAVE_PRLIMIT64)
-#include <../include/prlimit.h>
+#if !HAVE_PRLIMIT && HAVE_PRLIMIT64
+#include "prlimit.h"
#endif
-#ifndef HAVE_STRLCPY
-#include "include/strlcpy.h"
+#if !HAVE_STRLCPY
+#include "strlcpy.h"
#endif
-#ifndef HAVE_STRCHRNUL
-#include "include/strchrnul.h"
+#if !HAVE_STRCHRNUL
+#include "strchrnul.h"
#endif
lxc_log_define(conf, lxc);
struct caps_opt {
char *name;
- int value;
+ __u32 value;
};
struct limit_opt {
{ "rshared", 0, true, MS_SHARED, MS_SHARED | MS_REC },
{ "rslave", 0, true, MS_SLAVE, MS_SLAVE | MS_REC },
{ "runbindable", 0, true, MS_UNBINDABLE, MS_UNBINDABLE | MS_REC },
- { NULL, 0, 0 },
+ { NULL, 0, false, 0, 0 },
};
static struct caps_opt caps_opt[] = {
if (!rootfs->storage)
return log_error(-1, "Failed to mount rootfs \"%s\" onto \"%s\" with options \"%s\"",
rootfs->path, rootfs->mount,
- rootfs->options ? rootfs->options : "(null)");
+ rootfs->mnt_opts.raw_options ? rootfs->mnt_opts.raw_options : "(null)");
return 0;
}
PROTECT_LOOKUP_BENEATH,
S_IWUSR | S_IRUSR);
if (fd_pin < 0) {
- if (errno == EROFS) {
+ if (errno == EROFS)
return log_trace_errno(0, EROFS, "Not pinning on read-only filesystem");
- }
return syserror("Failed to pin rootfs");
}
int ret;
const char *path_source;
- if (lxc_list_empty(&handler->conf->id_map))
+ if (list_empty(&handler->conf->id_map))
return 0;
if (is_empty_string(rootfs->mnt_opts.userns_path))
{ LXC_AUTO_PROC_MASK, LXC_AUTO_PROC_RW, "proc", "%r/proc", "proc", MS_NODEV|MS_NOEXEC|MS_NOSUID, NULL, false },
{ LXC_AUTO_SYS_MASK, LXC_AUTO_SYS_RW, "sysfs", "%r/sys", "sysfs", 0, NULL, false },
{ LXC_AUTO_SYS_MASK, LXC_AUTO_SYS_RO, "sysfs", "%r/sys", "sysfs", MS_RDONLY, NULL, false },
+ /* /proc/sys is used as a temporary staging directory for the read-write sysfs mount and unmounted after binding net */
+ { LXC_AUTO_SYS_MASK, LXC_AUTO_SYS_MIXED, "sysfs", "%r/proc/sys", "sysfs", MS_NOSUID|MS_NODEV|MS_NOEXEC, NULL, false },
{ LXC_AUTO_SYS_MASK, LXC_AUTO_SYS_MIXED, "sysfs", "%r/sys", "sysfs", MS_RDONLY|MS_NOSUID|MS_NODEV|MS_NOEXEC, NULL, false },
- { LXC_AUTO_SYS_MASK, LXC_AUTO_SYS_MIXED, "%r/sys/devices/virtual/net", "%r/sys/devices/virtual/net", NULL, MS_BIND, NULL, false },
- { LXC_AUTO_SYS_MASK, LXC_AUTO_SYS_MIXED, NULL, "%r/sys/devices/virtual/net", NULL, MS_REMOUNT|MS_NOSUID|MS_NODEV|MS_NOEXEC, NULL, false },
+ { LXC_AUTO_SYS_MASK, LXC_AUTO_SYS_MIXED, "%r/proc/sys/devices/virtual/net", "%r/sys/devices/virtual/net", NULL, MS_BIND, NULL, false },
+ { LXC_AUTO_SYS_MASK, LXC_AUTO_SYS_MIXED, "%r/proc/sys", NULL, NULL, 0, NULL, false },
{ 0, 0, NULL, NULL, NULL, 0, NULL, false }
};
struct lxc_conf *conf = handler->conf;
return syserror_set(-ENOMEM, "Failed to create source path");
}
- if (!default_mounts[i].destination)
- return syserror_set(-EINVAL, "BUG: auto mounts destination %d was NULL", i);
-
if (!has_cap_net_admin && default_mounts[i].requires_cap_net_admin) {
TRACE("Container does not have CAP_NET_ADMIN. Skipping \"%s\" mount", default_mounts[i].source ?: "(null)");
continue;
}
+ if (!default_mounts[i].destination) {
+ ret = umount2(source, MNT_DETACH);
+ if (ret < 0)
+ return log_error_errno(-1, errno,
+ "Failed to unmount \"%s\"",
+ source);
+ TRACE("Unmounted automount \"%s\"", source);
+ continue;
+ }
+
/* will act like strdup if %r is not present */
destination = lxc_string_replace("%r", rootfs->path ? rootfs->mount : "", default_mounts[i].destination);
if (!destination)
* container can't remount it read-write.
*/
if ((cg_flags == LXC_AUTO_CGROUP_NOSPEC) || (cg_flags == LXC_AUTO_CGROUP_FULL_NOSPEC)) {
- int has_sys_admin = 0;
-
- if (!lxc_list_empty(&conf->keepcaps))
- has_sys_admin = in_caplist(CAP_SYS_ADMIN, &conf->keepcaps);
- else
- has_sys_admin = !in_caplist(CAP_SYS_ADMIN, &conf->caps);
-
if (cg_flags == LXC_AUTO_CGROUP_NOSPEC)
- cg_flags = has_sys_admin ? LXC_AUTO_CGROUP_RW : LXC_AUTO_CGROUP_MIXED;
+ cg_flags = has_cap(CAP_SYS_ADMIN, conf)
+ ? LXC_AUTO_CGROUP_RW
+ : LXC_AUTO_CGROUP_MIXED;
else
- cg_flags = has_sys_admin ? LXC_AUTO_CGROUP_FULL_RW : LXC_AUTO_CGROUP_FULL_MIXED;
+ cg_flags = has_cap(CAP_SYS_ADMIN, conf)
+ ? LXC_AUTO_CGROUP_FULL_RW
+ : LXC_AUTO_CGROUP_FULL_MIXED;
}
if (flags & LXC_AUTO_CGROUP_FORCE)
static int lxc_setup_dev_symlinks(const struct lxc_rootfs *rootfs)
{
- for (int i = 0; i < sizeof(dev_symlinks) / sizeof(dev_symlinks[0]); i++) {
+ for (size_t i = 0; i < sizeof(dev_symlinks) / sizeof(dev_symlinks[0]); i++) {
int ret;
struct stat s;
const struct dev_symlinks *d = &dev_symlinks[i];
}
/* Build a space-separate list of ptys to pass to systemd. */
-static bool append_ttyname(char **pp, char *name)
+static bool append_ttyname(struct lxc_tty_info *ttys, char *tty_name)
{
- char *p;
+ char *tty_names, *buf;
size_t size;
- if (!*pp) {
- *pp = zalloc(strlen(name) + strlen("container_ttys=") + 1);
- if (!*pp)
- return false;
+ if (!tty_name)
+ return false;
- sprintf(*pp, "container_ttys=%s", name);
- return true;
- }
+ size = strlen(tty_name) + 1;
+ if (ttys->tty_names)
+ size += strlen(ttys->tty_names) + 1;
- size = strlen(*pp) + strlen(name) + 2;
- p = realloc(*pp, size);
- if (!p)
+ buf = realloc(ttys->tty_names, size);
+ if (!buf)
return false;
+ tty_names = buf;
- *pp = p;
- (void)strlcat(p, " ", size);
- (void)strlcat(p, name, size);
-
+ if (ttys->tty_names)
+ (void)strlcat(buf, " ", size);
+ else
+ buf[0] = '\0';
+ (void)strlcat(buf, tty_name, size);
+ ttys->tty_names = tty_names;
return true;
}
PROTECT_LOOKUP_BENEATH,
0);
if (fd < 0) {
- if (!IN_SET(errno, ENXIO, EEXIST))
+ if (errno != ENXIO && errno != EEXIST)
return syserror("Failed to create \"%d/\%s\"", dfd, path);
SYSINFO("Failed to create \"%d/\%s\"", dfd, path);
if (!conf->rootfs.path)
return 0;
- for (int i = 0; i < ttys->max; i++) {
+ for (size_t i = 0; i < ttys->max; i++) {
__do_close int fd_to = -EBADF;
struct lxc_terminal_info *tty = &ttys->tty[i];
char *tty_name, *tty_path;
ret = strnprintf(rootfs->buf, sizeof(rootfs->buf),
- "/dev/%s/tty%d", ttydir, i + 1);
+ "/dev/%s/tty%zu", ttydir, i + 1);
if (ret < 0)
return ret_errno(-EIO);
rootfs->dfd_dev, tty_name,
rootfs->dfd_dev, tty_path);
} else {
- ret = strnprintf(rootfs->buf, sizeof(rootfs->buf), "tty%d", i + 1);
+ ret = strnprintf(rootfs->buf, sizeof(rootfs->buf), "tty%zu", i + 1);
if (ret < 0)
return ret_errno(-EIO);
DEBUG("Bind mounted \"%s\" onto \"%s\"", tty->name, rootfs->buf);
}
- if (!append_ttyname(&conf->ttys.tty_names, tty->name))
+ if (!append_ttyname(&conf->ttys, tty->name))
return log_error(-1, "Error setting up container_ttys string");
}
return -ENOMEM;
for (size_t i = 0; i < conf->ttys.max; i++) {
- int pty_nr = -1;
struct lxc_terminal_info *tty = &ttys->tty[i];
ret = lxc_devpts_terminal(conf->devpts_fd, &tty->ptx,
- &tty->pty, &pty_nr, false);
+ &tty->pty, &tty->pty_nr, false);
if (ret < 0) {
conf->ttys.max = i;
return syserror_set(-ENOTTY, "Failed to create tty %zu", i);
}
+ ret = strnprintf(tty->name, sizeof(tty->name), "pts/%d", tty->pty_nr);
+ if (ret < 0)
+ return syserror("Failed to create tty %zu", i);
+
DEBUG("Created tty with ptx fd %d and pty fd %d and index %d",
- tty->ptx, tty->pty, pty_nr);
+ tty->ptx, tty->pty, tty->pty_nr);
tty->busy = -1;
}
if (!ttys || !ttys->tty)
return;
- for (int i = 0; i < ttys->max; i++) {
+ for (size_t i = 0; i < ttys->max; i++) {
struct lxc_terminal_info *tty = &ttys->tty[i];
close_prot_errno_disarm(tty->ptx);
close_prot_errno_disarm(tty->pty);
static int __lxc_send_ttys_to_parent(struct lxc_handler *handler)
{
- int i;
int ret = -1;
struct lxc_conf *conf = handler->conf;
struct lxc_tty_info *ttys = &conf->ttys;
if (ttys->max == 0)
return 0;
- for (i = 0; i < ttys->max; i++) {
+ for (size_t i = 0; i < ttys->max; i++) {
int ttyfds[2];
struct lxc_terminal_info *tty = &ttys->tty[i];
SYSERROR("Failed to set \"container_ttys=%s\"", conf->ttys.tty_names);
goto on_error;
}
+ TRACE("Set \"container_ttys=%s\"", conf->ttys.tty_names);
}
return 0;
static int lxc_fill_autodev(struct lxc_rootfs *rootfs)
{
- int i, ret;
+ int ret;
mode_t cmask;
int use_mknod = LXC_DEVNODE_MKNOD;
INFO("Populating \"/dev\"");
cmask = umask(S_IXUSR | S_IXGRP | S_IXOTH);
- for (i = 0; i < sizeof(lxc_devices) / sizeof(lxc_devices[0]); i++) {
+ for (size_t i = 0; i < sizeof(lxc_devices) / sizeof(lxc_devices[0]); i++) {
const struct lxc_device_node *device = &lxc_devices[i];
if (use_mknod >= LXC_DEVNODE_MKNOD) {
if (ret < 0)
return log_error(-1, "Failed to mount rootfs \"%s\" onto \"%s\" with options \"%s\"",
rootfs->path, rootfs->mount,
- rootfs->options ? rootfs->options : "(null)");
+ rootfs->mnt_opts.raw_options ? rootfs->mnt_opts.raw_options : "(null)");
DEBUG("Mounted rootfs \"%s\" onto \"%s\" with options \"%s\"",
rootfs->path, rootfs->mount,
- rootfs->options ? rootfs->options : "(null)");
+ rootfs->mnt_opts.raw_options ? rootfs->mnt_opts.raw_options : "(null)");
rootfs->dfd_mnt = open_at(-EBADF, rootfs->mount, PROTECT_OPATH_DIRECTORY, PROTECT_LOOKUP_ABSOLUTE_XDEV, 0);
if (rootfs->dfd_mnt < 0)
return log_trace(0, "Container uses separate rootfs. Opened container's rootfs");
}
+static bool lxc_rootfs_overmounted(struct lxc_rootfs *rootfs)
+{
+ __do_close int fd_rootfs = -EBADF;
+
+ if (!rootfs->path)
+ fd_rootfs = open_at(-EBADF, "/", PROTECT_OPATH_DIRECTORY, PROTECT_LOOKUP_ABSOLUTE, 0);
+ else
+ fd_rootfs = open_at(-EBADF, rootfs->mount, PROTECT_OPATH_DIRECTORY, PROTECT_LOOKUP_ABSOLUTE_XDEV, 0);
+ if (fd_rootfs < 0)
+ return true;
+
+ if (!same_file_lax(rootfs->dfd_mnt, fd_rootfs))
+ return syswarn_ret(true, "Rootfs seems to have changed after setting up mounts");
+
+ return false;
+}
+
static int lxc_chroot(const struct lxc_rootfs *rootfs)
{
__do_free char *nroot = NULL;
return log_error_errno(-errno, errno, "Failed to enter old root directory");
/*
- * Make fd_oldroot a depedent mount to make sure our umounts don't
- * propagate to the host.
+ * Unprivileged containers will have had all their mounts turned into
+ * dependent mounts when the container was created. But for privileged
+ * containers we need to turn the old root mount tree into a dependent
+ * mount tree to prevent propagating mounts and umounts into the host
+ * mount namespace.
*/
ret = mount("", ".", "", MS_SLAVE | MS_REC, NULL);
if (ret < 0)
if (ret < 0)
return log_error_errno(-errno, errno, "Failed to re-enter new root directory \"%s\"", rootfs->mount);
+ /*
+ * Finally, we turn the rootfs into a shared mount. Note, that this
+ * doesn't reestablish mount propagation with the hosts mount
+ * namespace. Instead we'll create a new peer group.
+ *
+ * We're doing this because most workloads do rely on the rootfs being
+ * a shared mount. For example, systemd daemon like sytemd-udevd run in
+ * their own mount namespace. Their mount namespace has been made a
+ * dependent mount (MS_SLAVE) with the host rootfs as it's dominating
+ * mount. This means new mounts on the host propagate into the
+ * respective services.
+ *
+ * This is broken if we leave the container's rootfs a dependent mount.
+ * In which case both the container's rootfs and the service's rootfs
+ * will be dependent mounts with the host's rootfs as their dominating
+ * mount. So if you were to mount over the rootfs from the host it
+ * would not just propagate into the container's mount namespace it
+ * would also propagate into the service. That's nonsense semantics for
+ * nearly all relevant use-cases. Instead, establish the container's
+ * rootfs as a separate peer group mirroring the behavior on the host.
+ */
+ ret = mount("", ".", "", MS_SHARED | MS_REC, NULL);
+ if (ret < 0)
+ return log_error_errno(-errno, errno, "Failed to turn new root mount tree into shared mount tree");
+
TRACE("Changed into new rootfs \"%s\"", rootfs->mount);
return 0;
}
unsigned id,
enum idtype idtype)
{
- struct lxc_list *it;
struct id_map *map;
struct id_map *retmap = NULL;
return conf->root_nsgid_map;
}
- lxc_list_for_each(it, &conf->id_map) {
- map = it->elem;
+ list_for_each_entry(map, &conf->id_map, head) {
if (map->idtype != idtype)
continue;
return syserror("Failed to create path");
close_prot_errno_disarm(conf->devpts_fd);
- return umount2(rootfs->buf, MNT_DETACH);
+ (void)umount2(rootfs->buf, MNT_DETACH);
+ return 0;
}
static int lxc_send_devpts_to_parent(struct lxc_handler *handler)
static int parse_mntopt(char *opt, unsigned long *flags, char **data, size_t size)
{
- ssize_t ret;
+ size_t ret;
/* If '=' is contained in opt, the option must go into data. */
if (!strchr(opt, '=')) {
if (strlen(*data)) {
ret = strlcat(*data, ",", size);
- if (ret < 0)
+ if (ret >= size)
return log_error_errno(ret, errno, "Failed to append \",\" to %s", *data);
}
ret = strlcat(*data, opt, size);
- if (ret < 0)
+ if (ret >= size)
return log_error_errno(ret, errno, "Failed to append \"%s\" to %s", opt, *data);
return 0;
/* This is a recursive bind-mount. */
if (strequal(mo->name, "rbind")) {
- opts->recursive = 1;
+ opts->bind_recursively = 1;
opts->bind = 1;
opts->mnt_flags |= mo->legacy_flag; /* MS_BIND | MS_REC */
return 0;
return 0;
}
- if (mo->flag == ~0)
+ if (mo->flag == (__u64)~0)
return log_info(0, "Ignoring %s mount option", mo->name);
if (mo->clear) {
if (!strnequal(opt, mo->name, strlen(mo->name)))
continue;
- /* TODO: Handle recursive propagation requests. */
+ if (strequal(mo->name, "rslave") ||
+ strequal(mo->name, "rshared") ||
+ strequal(mo->name, "runbindable") ||
+ strequal(mo->name, "rprivate"))
+ opts->propagate_recursively = 1;
+
opts->attr.propagation = mo->flag;
- opts->mnt_flags |= mo->legacy_flag;
+ opts->prop_flags |= mo->legacy_flag;
return 0;
}
return 0;
}
-static void parse_propagationopt(char *opt, unsigned long *flags)
-{
- struct mount_opt *mo;
-
- /* If opt is found in propagation_opt, set or clear flags. */
- for (mo = &propagation_opt[0]; mo->name != NULL; mo++) {
- if (!strnequal(opt, mo->name, strlen(mo->name)))
- continue;
-
- if (mo->clear)
- *flags &= ~mo->legacy_flag;
- else
- *flags |= mo->legacy_flag;
-
- return;
- }
-}
-
-int parse_propagationopts(const char *mntopts, unsigned long *pflags)
-{
- __do_free char *s = NULL;
- char *p;
-
- if (!mntopts)
- return 0;
-
- s = strdup(mntopts);
- if (!s)
- return log_error_errno(-ENOMEM, errno, "Failed to allocate memory");
-
- *pflags = 0L;
- lxc_iterate_parts(p, s, ",")
- parse_propagationopt(p, pflags);
-
- return 0;
-}
-
static void null_endofword(char *word)
{
while (*word && *word != ' ' && *word != '\t')
const char *lxc_path)
{
__do_free char *mntdata = NULL;
- unsigned long mntflags = 0, pflags = 0;
char *rootfs_path = NULL;
int ret;
bool dev, optional, relative;
if (!is_empty_string(opts.userns_path))
return systrace_ret(0, "Skipping idmapped mount entry");
- ret = parse_propagationopts(mntent->mnt_opts, &pflags);
+ ret = parse_mount_attrs(&opts, mntent->mnt_opts);
if (ret < 0)
return -1;
- ret = parse_mntopts_legacy(mntent->mnt_opts, &mntflags, &mntdata);
- if (ret < 0)
- return ret;
-
- ret = mount_entry(mntent->mnt_fsname, path, mntent->mnt_type, mntflags,
- pflags, mntdata, optional, dev, relative, rootfs_path);
+ ret = mount_entry(mntent->mnt_fsname,
+ path,
+ mntent->mnt_type,
+ opts.mnt_flags,
+ opts.prop_flags,
+ opts.data,
+ optional,
+ dev,
+ relative,
+ rootfs_path);
return ret;
}
"proc dev/.lxc/proc proc create=dir,optional 0 0\n"
"sys dev/.lxc/sys sysfs create=dir,optional 0 0\n";
-FILE *make_anonymous_mount_file(struct lxc_list *mount,
+FILE *make_anonymous_mount_file(const struct list_head *mount_entries,
bool include_nesting_helpers)
{
__do_close int fd = -EBADF;
FILE *f;
int ret;
- char *mount_entry;
- struct lxc_list *iterator;
+ struct string_entry *entry;
fd = memfd_create(".lxc_mount_file", MFD_CLOEXEC);
if (fd < 0) {
TRACE("Created temporary mount file");
}
- lxc_list_for_each (iterator, mount) {
+ list_for_each_entry(entry, mount_entries, head) {
size_t len;
- mount_entry = iterator->elem;
- len = strlen(mount_entry);
+ len = strlen(entry->val);
- ret = lxc_write_nointr(fd, mount_entry, len);
- if (ret != len)
+ ret = lxc_write_nointr(fd, entry->val, len);
+ if (ret < 0 || (size_t)ret != len)
return NULL;
ret = lxc_write_nointr(fd, "\n", 1);
}
static int setup_mount_entries(const struct lxc_conf *conf,
- struct lxc_rootfs *rootfs, struct lxc_list *mount,
+ struct lxc_rootfs *rootfs,
const char *lxc_name, const char *lxc_path)
{
__do_fclose FILE *f = NULL;
- f = make_anonymous_mount_file(mount, conf->lsm_aa_allow_nesting);
+ f = make_anonymous_mount_file(&conf->mount_entries, conf->lsm_aa_allow_nesting);
if (!f)
return -1;
struct lxc_mount_options opts = {};
int dfd_from;
const char *source_relative, *target_relative;
+ struct mount_attr attr = {};
ret = parse_lxc_mount_attrs(&opts, mntent.mnt_opts);
if (ret < 0)
dfd_from = rootfs->dfd_host;
fd_from = open_tree(dfd_from, source_relative,
OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC |
- (opts.recursive ? AT_RECURSIVE : 0));
+ (opts.bind_recursively ? AT_RECURSIVE : 0));
if (fd_from < 0)
return syserror("Failed to create detached %smount of %d/%s",
- opts.recursive ? "recursive " : "",
+ opts.bind_recursively ? "recursive " : "",
dfd_from, source_relative);
if (strequal(opts.userns_path, "container"))
}
return syserror("Failed to open user namespace \"%s\" for detached %smount of %d/%s",
- opts.userns_path, opts.recursive ? "recursive " : "",
+ opts.userns_path, opts.bind_recursively ? "recursive " : "",
dfd_from, source_relative);
}
}
return syserror("Failed to send file descriptor %d for detached %smount of %d/%s and file descriptor %d of user namespace \"%s\" to parent",
- fd_from, opts.recursive ? "recursive " : "",
+ fd_from, opts.bind_recursively ? "recursive " : "",
dfd_from, source_relative, fd_userns,
opts.userns_path);
}
}
return syserror("Failed to receive notification that parent idmapped detached %smount %d/%s to user namespace %d",
- opts.recursive ? "recursive " : "",
+ opts.bind_recursively ? "recursive " : "",
dfd_from, source_relative, fd_userns);
}
mnt_seq, cur_mnt_seq);
mnt_seq++;
- /* Set remaining mount options. */
- ret = mount_setattr(fd_from, "", AT_EMPTY_PATH |
- (opts.recursive ? AT_RECURSIVE : 0),
- &opts.attr, sizeof(opts.attr));
+ /* Set regular mount options. */
+ attr = opts.attr;
+ attr.propagation = 0;
+ ret = mount_setattr(fd_from,
+ "",
+ AT_EMPTY_PATH |
+ (opts.bind_recursively ? AT_RECURSIVE : 0),
+ &attr,
+ sizeof(attr));
if (ret < 0) {
if (opts.optional) {
TRACE("Skipping optional idmapped mount");
continue;
}
- return syserror("Failed to receive notification that parent idmapped detached %smount %d/%s to user namespace %d",
- opts.recursive ? "recursive " : "",
- dfd_from, source_relative, fd_userns);
+ return syserror("Failed to set %smount options on detached %d/%s",
+ opts.bind_recursively ? "recursive " : "",
+ dfd_from, source_relative);
+ }
+
+ /* Set propagation mount options. */
+ if (opts.attr.propagation) {
+ attr = (struct mount_attr) {
+ .propagation = opts.attr.propagation,
+ };
+
+ ret = mount_setattr(fd_from,
+ "",
+ AT_EMPTY_PATH |
+ (opts.propagate_recursively ? AT_RECURSIVE : 0),
+ &attr,
+ sizeof(attr));
+ if (ret < 0) {
+ if (opts.optional) {
+ TRACE("Skipping optional idmapped mount");
+ continue;
+ }
+
+ return syserror("Failed to set %spropagation mount options on detached %d/%s",
+ opts.bind_recursively ? "recursive " : "",
+ dfd_from, source_relative);
+ }
}
+
/*
* In contrast to the legacy mount codepath we will simplify
* our lifes and just always treat the target mountpoint to be
dfd_from = rootfs->dfd_mnt;
else
dfd_from = rootfs->dfd_host;
- fd_to = open_at(dfd_from, target_relative, PROTECT_OPATH_FILE, PROTECT_LOOKUP_BENEATH_WITH_SYMLINKS, 0);
+ fd_to = open_at(dfd_from, target_relative, PROTECT_OPATH_FILE, PROTECT_LOOKUP_BENEATH_XDEV, 0);
if (fd_to < 0) {
if (opts.optional) {
TRACE("Skipping optional idmapped mount");
return syserror("Failed to open target mountpoint %d/%s for detached idmapped %smount %d:%d/%s",
dfd_from, target_relative,
- opts.recursive ? "recursive " : "",
+ opts.bind_recursively ? "recursive " : "",
fd_userns, dfd_from, source_relative);
}
}
return syserror("Failed to attach detached idmapped %smount %d:%d/%s to target mountpoint %d/%s",
- opts.recursive ? "recursive " : "",
+ opts.bind_recursively ? "recursive " : "",
fd_userns, dfd_from, source_relative, dfd_from, target_relative);
}
TRACE("Attached detached idmapped %smount %d:%d/%s to target mountpoint %d/%s",
- opts.recursive ? "recursive " : "", fd_userns, dfd_from,
+ opts.bind_recursively ? "recursive " : "", fd_userns, dfd_from,
source_relative, dfd_from, target_relative);
}
int fret = -1;
struct lxc_conf *conf = handler->conf;
const char *fstab = conf->fstab;
- struct lxc_list *mount = &conf->mount_list;
int ret;
- f_entries = make_anonymous_mount_file(mount, conf->lsm_aa_allow_nesting);
+ f_entries = make_anonymous_mount_file(&conf->mount_entries,
+ conf->lsm_aa_allow_nesting);
if (!f_entries) {
SYSERROR("Failed to create anonymous mount file");
goto out;
return fret;
}
-static int parse_cap(const char *cap)
+int parse_cap(const char *cap_name, __u32 *cap)
{
- size_t i;
- int capid = -1;
size_t end = sizeof(caps_opt) / sizeof(caps_opt[0]);
- char *ptr = NULL;
+ int ret;
+ unsigned int res;
+ __u32 last_cap;
- if (strequal(cap, "none"))
+ if (strequal(cap_name, "none"))
return -2;
- for (i = 0; i < end; i++) {
- if (!strequal(cap, caps_opt[i].name))
+ for (size_t i = 0; i < end; i++) {
+ if (!strequal(cap_name, caps_opt[i].name))
continue;
- capid = caps_opt[i].value;
- break;
+ *cap = caps_opt[i].value;
+ return 0;
}
- if (capid < 0) {
- /* Try to see if it's numeric, so the user may specify
- * capabilities that the running kernel knows about but we
- * don't
- */
- errno = 0;
- capid = strtol(cap, &ptr, 10);
- if (!ptr || *ptr != '\0' || errno != 0)
- /* not a valid number */
- capid = -1;
- else if (capid > lxc_caps_last_cap())
- /* we have a number but it's not a valid
- * capability */
- capid = -1;
- }
-
- return capid;
+ /*
+ * Try to see if it's numeric, so the user may specify
+ * capabilities that the running kernel knows about but we
+ * don't.
+ */
+ ret = lxc_safe_uint(cap_name, &res);
+ if (ret < 0)
+ return -1;
+
+ ret = lxc_caps_last_cap(&last_cap);
+ if (ret)
+ return -1;
+
+ if ((__u32)res > last_cap)
+ return -1;
+
+ *cap = (__u32)res;
+ return 0;
}
-int in_caplist(int cap, struct lxc_list *caps)
+bool has_cap(__u32 cap, struct lxc_conf *conf)
{
- int capid;
- struct lxc_list *iterator;
+ bool cap_in_list = false;
+ struct cap_entry *cap_entry;
+
+ list_for_each_entry(cap_entry, &conf->caps.list, head) {
+ if (cap_entry->cap != cap)
+ continue;
- lxc_list_for_each (iterator, caps) {
- capid = parse_cap(iterator->elem);
- if (capid == cap)
- return 1;
+ cap_in_list = true;
}
- return 0;
+ /* The capability is kept. */
+ if (conf->caps.keep)
+ return cap_in_list;
+
+ /* The capability is not dropped. */
+ return !cap_in_list;
}
-static int setup_caps(struct lxc_list *caps)
+static int capabilities_deny(struct lxc_conf *conf)
{
- int capid;
- char *drop_entry;
- struct lxc_list *iterator;
+ struct cap_entry *cap;
- lxc_list_for_each (iterator, caps) {
+ list_for_each_entry(cap, &conf->caps.list, head) {
int ret;
- drop_entry = iterator->elem;
-
- capid = parse_cap(drop_entry);
- if (capid < 0)
- return log_error(-1, "unknown capability %s", drop_entry);
-
- ret = prctl(PR_CAPBSET_DROP, prctl_arg(capid), prctl_arg(0),
+ ret = prctl(PR_CAPBSET_DROP, prctl_arg(cap->cap), prctl_arg(0),
prctl_arg(0), prctl_arg(0));
if (ret < 0)
- return log_error_errno(-1, errno, "Failed to remove %s capability", drop_entry);
- DEBUG("Dropped %s (%d) capability", drop_entry, capid);
+ return syserror("Failed to remove %s capability", cap->cap_name);
+
+ DEBUG("Dropped %s (%d) capability", cap->cap_name, cap->cap);
}
DEBUG("Capabilities have been setup");
return 0;
}
-static int dropcaps_except(struct lxc_list *caps)
+static int capabilities_allow(struct lxc_conf *conf)
{
- __do_free int *caplist = NULL;
- int i, capid, numcaps;
- char *keep_entry;
- struct lxc_list *iterator;
+ __do_free __u32 *keep_bits = NULL;
+ int ret;
+ struct cap_entry *cap;
+ __u32 last_cap, nr_u32;
- numcaps = lxc_caps_last_cap() + 1;
- if (numcaps <= 0 || numcaps > 200)
- return -1;
- TRACE("Found %d capabilities", numcaps);
+ ret = lxc_caps_last_cap(&last_cap);
+ if (ret || last_cap > 200)
+ return ret_errno(EINVAL);
- /* caplist[i] is 1 if we keep capability i */
- caplist = must_realloc(NULL, numcaps * sizeof(int));
- memset(caplist, 0, numcaps * sizeof(int));
+ TRACE("Found %d capabilities", last_cap);
- lxc_list_for_each (iterator, caps) {
- keep_entry = iterator->elem;
+ nr_u32 = BITS_TO_LONGS(last_cap);
+ keep_bits = zalloc(nr_u32 * sizeof(__u32));
+ if (!keep_bits)
+ return ret_errno(ENOMEM);
- capid = parse_cap(keep_entry);
- if (capid == -2)
+ list_for_each_entry(cap, &conf->caps.list, head) {
+ if (cap->cap > last_cap)
continue;
- if (capid < 0)
- return log_error(-1, "Unknown capability %s", keep_entry);
-
- DEBUG("Keep capability %s (%d)", keep_entry, capid);
- caplist[capid] = 1;
+ set_bit(cap->cap, keep_bits);
+ DEBUG("Keeping %s (%d) capability", cap->cap_name, cap->cap);
}
- for (i = 0; i < numcaps; i++) {
- int ret;
-
- if (caplist[i])
+ for (__u32 cap_bit = 0; cap_bit <= last_cap; cap_bit++) {
+ if (is_set(cap_bit, keep_bits))
continue;
- ret = prctl(PR_CAPBSET_DROP, prctl_arg(i), prctl_arg(0),
+ ret = prctl(PR_CAPBSET_DROP, prctl_arg(cap_bit), prctl_arg(0),
prctl_arg(0), prctl_arg(0));
if (ret < 0)
- return log_error_errno(-1, errno, "Failed to remove capability %d", i);
+ return syserror("Failed to remove capability %d", cap_bit);
+
+ TRACE("Dropped capability %d", cap_bit);
}
DEBUG("Capabilities have been setup");
return resid;
}
-int setup_resource_limits(struct lxc_list *limits, pid_t pid)
+int setup_resource_limits(struct lxc_conf *conf, pid_t pid)
{
int resid;
- struct lxc_list *it;
struct lxc_limit *lim;
- lxc_list_for_each (it, limits) {
- lim = it->elem;
+ if (list_empty(&conf->limits))
+ return 0;
+ list_for_each_entry(lim, &conf->limits, head) {
resid = parse_resource(lim->resource);
if (resid < 0)
return log_error(-1, "Unknown resource %s", lim->resource);
#endif
}
+ TRACE("Setup resource limits");
return 0;
}
-int setup_sysctl_parameters(struct lxc_list *sysctls)
+int setup_sysctl_parameters(struct lxc_conf *conf)
{
__do_free char *tmp = NULL;
- struct lxc_list *it;
- struct lxc_sysctl *elem;
int ret = 0;
char filename[PATH_MAX] = {0};
+ struct lxc_sysctl *sysctl, *nsysctl;
+
+ if (list_empty(&conf->sysctls))
+ return 0;
- lxc_list_for_each (it, sysctls) {
- elem = it->elem;
- tmp = lxc_string_replace(".", "/", elem->key);
+ list_for_each_entry_safe(sysctl, nsysctl, &conf->sysctls, head) {
+ tmp = lxc_string_replace(".", "/", sysctl->key);
if (!tmp)
- return log_error(-1, "Failed to replace key %s", elem->key);
+ return log_error(-1, "Failed to replace key %s", sysctl->key);
ret = strnprintf(filename, sizeof(filename), "/proc/sys/%s", tmp);
if (ret < 0)
return log_error(-1, "Error setting up sysctl parameters path");
- ret = lxc_write_to_file(filename, elem->value,
- strlen(elem->value), false, 0666);
+ ret = lxc_write_to_file(filename, sysctl->value,
+ strlen(sysctl->value), false, 0666);
if (ret < 0)
return log_error_errno(-1, errno, "Failed to setup sysctl parameters %s to %s",
- elem->key, elem->value);
+ sysctl->key, sysctl->value);
+
+ TRACE("Setting %s to %s", filename, sysctl->value);
}
+ TRACE("Setup /proc/sys settings");
return 0;
}
-int setup_proc_filesystem(struct lxc_list *procs, pid_t pid)
+int setup_proc_filesystem(struct lxc_conf *conf, pid_t pid)
{
__do_free char *tmp = NULL;
- struct lxc_list *it;
- struct lxc_proc *elem;
int ret = 0;
char filename[PATH_MAX] = {0};
+ struct lxc_proc *proc;
- lxc_list_for_each (it, procs) {
- elem = it->elem;
- tmp = lxc_string_replace(".", "/", elem->filename);
+ if (list_empty(&conf->procs))
+ return 0;
+
+ list_for_each_entry(proc, &conf->procs, head) {
+ tmp = lxc_string_replace(".", "/", proc->filename);
if (!tmp)
- return log_error(-1, "Failed to replace key %s", elem->filename);
+ return log_error(-1, "Failed to replace key %s", proc->filename);
ret = strnprintf(filename, sizeof(filename), "/proc/%d/%s", pid, tmp);
if (ret < 0)
return log_error(-1, "Error setting up proc filesystem path");
- ret = lxc_write_to_file(filename, elem->value,
- strlen(elem->value), false, 0666);
+ ret = lxc_write_to_file(filename, proc->value,
+ strlen(proc->value), false, 0666);
if (ret < 0)
- return log_error_errno(-1, errno, "Failed to setup proc filesystem %s to %s", elem->filename, elem->value);
+ return log_error_errno(-1, errno, "Failed to setup proc filesystem %s to %s",
+ proc->filename, proc->value);
+
+ TRACE("Setting %s to %s", filename, proc->value);
}
+ TRACE("Setup /proc/%d settings", pid);
return 0;
}
new->rootfs.fd_path_pin = -EBADF;
new->rootfs.dfd_idmapped = -EBADF;
new->logfd = -1;
- lxc_list_init(&new->cgroup);
- lxc_list_init(&new->cgroup2);
+ INIT_LIST_HEAD(&new->cgroup);
+ INIT_LIST_HEAD(&new->cgroup2);
/* Block ("allowlist") all devices by default. */
new->bpf_devices.list_type = LXC_BPF_DEVICE_CGROUP_ALLOWLIST;
- lxc_list_init(&(new->bpf_devices).device_item);
- lxc_list_init(&new->network);
- lxc_list_init(&new->mount_list);
- lxc_list_init(&new->caps);
- lxc_list_init(&new->keepcaps);
- lxc_list_init(&new->id_map);
+ INIT_LIST_HEAD(&(new->bpf_devices).devices);
+ INIT_LIST_HEAD(&new->mount_entries);
+ INIT_LIST_HEAD(&new->caps.list);
+ INIT_LIST_HEAD(&new->id_map);
new->root_nsuid_map = NULL;
new->root_nsgid_map = NULL;
- lxc_list_init(&new->includes);
- lxc_list_init(&new->aliens);
- lxc_list_init(&new->environment);
- lxc_list_init(&new->limits);
- lxc_list_init(&new->sysctls);
- lxc_list_init(&new->procs);
+ INIT_LIST_HEAD(&new->environment);
+ INIT_LIST_HEAD(&new->limits);
+ INIT_LIST_HEAD(&new->sysctls);
+ INIT_LIST_HEAD(&new->procs);
new->hooks_version = 0;
for (i = 0; i < NUM_LXC_HOOKS; i++)
- lxc_list_init(&new->hooks[i]);
- lxc_list_init(&new->groups);
- lxc_list_init(&new->state_clients);
+ INIT_LIST_HEAD(&new->hooks[i]);
+ INIT_LIST_HEAD(&new->groups);
+ INIT_LIST_HEAD(&new->state_clients);
new->lsm_aa_profile = NULL;
- lxc_list_init(&new->lsm_aa_raw);
+ INIT_LIST_HEAD(&new->lsm_aa_raw);
new->lsm_se_context = NULL;
new->lsm_se_keyring_context = NULL;
new->keyring_disable_session = false;
new->transient_procfs_mnt = false;
new->shmount.path_host = NULL;
new->shmount.path_cont = NULL;
+ new->sched_core = false;
+ new->sched_core_cookie = INVALID_SCHED_CORE_COOKIE;
/* if running in a new user namespace, init and COMMAND
* default to running as UID/GID 0 when using lxc-execute */
memset(&new->timens, 0, sizeof(struct timens_offsets));
seccomp_conf_init(new);
+ INIT_LIST_HEAD(&new->netdevs);
+
return new;
}
return log_error_errno(-1, errno, "Failed to open \"%s\"", path);
ret = lxc_write_nointr(fd, buf, buf_size);
- if (ret != buf_size)
+ if (ret < 0 || (size_t)ret != buf_size)
return log_error_errno(-1, errno, "Failed to write %cid mapping to \"%s\"",
idtype == ID_TYPE_UID ? 'u' : 'g', path);
return -1;
}
-static struct id_map *find_mapped_hostid_entry(const struct lxc_list *idmap,
+static struct id_map *find_mapped_hostid_entry(const struct list_head *idmap,
unsigned id, enum idtype idtype);
-int lxc_map_ids(struct lxc_list *idmap, pid_t pid)
+int lxc_map_ids(struct list_head *idmap, pid_t pid)
{
int fill, left;
+ uid_t hostuid;
+ gid_t hostgid;
char u_or_g;
char *pos;
char cmd_output[PATH_MAX];
struct id_map *map;
- struct lxc_list *iterator;
enum idtype type;
int ret = 0, gidmap = 0, uidmap = 0;
char mapbuf[STRLITERALLEN("new@idmap") + STRLITERALLEN(" ") +
INTTYPE_TO_STRLEN(pid_t) + STRLITERALLEN(" ") +
LXC_IDMAPLEN] = {0};
bool had_entry = false, maps_host_root = false, use_shadow = false;
- int hostuid, hostgid;
hostuid = geteuid();
hostgid = getegid();
/* Check if we really need to use newuidmap and newgidmap.
* If the user is only remapping their own {g,u}id, we don't need it.
*/
- if (use_shadow && lxc_list_len(idmap) == 2) {
+ if (use_shadow && list_len(map, idmap, head) == 2) {
use_shadow = false;
- lxc_list_for_each(iterator, idmap) {
- map = iterator->elem;
+ list_for_each_entry(map, idmap, head) {
if (map->idtype == ID_TYPE_UID && map->range == 1 &&
map->nsid == hostuid && map->hostid == hostuid)
continue;
if (use_shadow)
pos += sprintf(mapbuf, "new%cidmap %d", u_or_g, pid);
- lxc_list_for_each(iterator, idmap) {
- map = iterator->elem;
+ list_for_each_entry(map, idmap, head) {
if (map->idtype != type)
continue;
{
unsigned nsid;
struct id_map *map;
- struct lxc_list *it;
if (idtype == ID_TYPE_UID)
nsid = (conf->root_nsuid_map != NULL) ? 0 : conf->init_uid;
else
nsid = (conf->root_nsgid_map != NULL) ? 0 : conf->init_gid;
- lxc_list_for_each (it, &conf->id_map) {
- map = it->elem;
+ list_for_each_entry (map, &conf->id_map, head) {
if (map->idtype != idtype)
continue;
if (map->nsid != nsid)
int mapped_hostid(unsigned id, const struct lxc_conf *conf, enum idtype idtype)
{
struct id_map *map;
- struct lxc_list *it;
- lxc_list_for_each (it, &conf->id_map) {
- map = it->elem;
+ list_for_each_entry(map, &conf->id_map, head) {
if (map->idtype != idtype)
continue;
int find_unmapped_nsid(const struct lxc_conf *conf, enum idtype idtype)
{
struct id_map *map;
- struct lxc_list *it;
unsigned int freeid = 0;
again:
- lxc_list_for_each (it, &conf->id_map) {
- map = it->elem;
+ list_for_each_entry(map, &conf->id_map, head) {
if (map->idtype != idtype)
continue;
return log_error_errno(-errno, errno, "Failed to create %d(proc)", rootfs->dfd_mnt);
goto domount;
- } else if (link_len >= sizeof(link)) {
+ } else if ((size_t)link_len >= sizeof(link)) {
return log_error_errno(-EIO, EIO, "Truncated link target");
}
link[link_len] = '\0';
static bool verify_start_hooks(struct lxc_conf *conf)
{
char path[PATH_MAX];
- struct lxc_list *it;
+ struct string_entry *hook;
- lxc_list_for_each (it, &conf->hooks[LXCHOOK_START]) {
+ list_for_each_entry(hook, &conf->hooks[LXCHOOK_START], head) {
int ret;
- char *hookname = it->elem;
+ char *hookname = hook->val;
ret = strnprintf(path, sizeof(path), "%s%s",
conf->rootfs.path ? conf->rootfs.mount : "",
int dfd_idmapped = -EBADF;
int ret;
- if (lxc_list_empty(&handler->conf->id_map))
+ if (list_empty(&handler->conf->id_map))
return 0;
if (is_empty_string(rootfs->mnt_opts.userns_path))
for (;;) {
__do_close int fd_from = -EBADF, fd_userns = -EBADF;
- struct lxc_mount_attr attr = {};
+ struct mount_attr attr = {};
struct lxc_mount_options opts = {};
ssize_t ret;
return syserror("Failed to receive idmapped mount file descriptors from child");
if (fd_from < 0 || fd_userns < 0)
- return log_trace(0, "Finished receiving idmapped mount file descriptors from child");
+ return log_trace(0, "Finished receiving idmapped mount file descriptors (%d | %d) from child", fd_from, fd_userns);
attr.attr_set = MOUNT_ATTR_IDMAP;
attr.userns_fd = fd_userns;
ret = mount_setattr(fd_from, "",
AT_EMPTY_PATH |
- (opts.recursive ? AT_RECURSIVE : 0),
+ (opts.bind_recursively ? AT_RECURSIVE : 0),
&attr, sizeof(attr));
if (ret)
return syserror("Failed to idmap detached %smount %d to %d",
- opts.recursive ? "recursive " : "",
+ opts.bind_recursively ? "recursive " : "",
fd_from, fd_userns);
ret = lxc_abstract_unix_send_credential(handler->data_sock[1],
sizeof(mnt_seq));
if (ret < 0)
return syserror("Parent failed to notify child that detached %smount %d was idmapped to user namespace %d",
- opts.recursive ? "recursive " : "",
+ opts.bind_recursively ? "recursive " : "",
fd_from, fd_userns);
TRACE("Parent idmapped detached %smount %d to user namespace %d",
- opts.recursive ? "recursive " : "", fd_from, fd_userns);
+ opts.bind_recursively ? "recursive " : "", fd_from, fd_userns);
mnt_seq++;
}
}
if (!info_new->tty)
return ret_errno(ENOMEM);
- for (int i = 0; i < ttys_max; i++) {
+ for (size_t i = 0; i < ttys_max; i++) {
terminal_info = &info_new->tty[i];
terminal_info->busy = -1;
+ terminal_info->pty_nr = -1;
terminal_info->ptx = -EBADF;
terminal_info->pty = -EBADF;
}
- for (int i = 0; i < ttys_max; i++) {
+ for (size_t i = 0; i < ttys_max; i++) {
int ptx = -EBADF, pty = -EBADF;
ret = lxc_abstract_unix_recv_two_fds(sock, &ptx, &pty);
return 0;
}
+static int setup_capabilities(struct lxc_conf *conf)
+{
+ int ret;
+
+ if (conf->caps.keep)
+ ret = capabilities_allow(conf);
+ else
+ ret = capabilities_deny(conf);
+ if (ret < 0)
+ return syserror_ret(ret, "Failed to %s capabilities", conf->caps.keep ? "allow" : "deny");
+
+ return 0;
+}
+
+static int make_shmount_dependent_mount(const struct lxc_conf *conf)
+{
+ if (!(conf->auto_mounts & LXC_AUTO_SHMOUNTS_MASK))
+ return 0;
+
+ return mount(NULL, conf->shmount.path_cont, NULL, MS_REC | MS_SLAVE, 0);
+}
+
int lxc_setup(struct lxc_handler *handler)
{
int ret;
if (ret < 0)
return log_error(-1, "Failed to receive veth names from parent");
- ret = lxc_setup_network_in_child_namespaces(lxc_conf,
- &lxc_conf->network);
+ ret = lxc_setup_network_in_child_namespaces(lxc_conf);
if (ret < 0)
return log_error(-1, "Failed to setup network");
}
if (ret < 0)
return log_error(-1, "Failed to setup mounts");
- if (!lxc_list_empty(&lxc_conf->mount_list)) {
- ret = setup_mount_entries(lxc_conf, &lxc_conf->rootfs,
- &lxc_conf->mount_list, name, lxcpath);
+ if (!list_empty(&lxc_conf->mount_entries)) {
+ ret = setup_mount_entries(lxc_conf, &lxc_conf->rootfs, name, lxcpath);
if (ret < 0)
return log_error(-1, "Failed to setup mount entries");
}
if (ret < 0)
return log_error(-1, "Failed to run mount hooks");
+ if (lxc_rootfs_overmounted(&lxc_conf->rootfs))
+ return log_error(-1, "Rootfs overmounted");
+
if (lxc_conf->autodev > 0) {
ret = run_lxc_hooks(name, "autodev", lxc_conf, NULL);
if (ret < 0)
if (ret < 0)
return log_error(-1, "Failed to pivot root into rootfs");
+ ret = make_shmount_dependent_mount(lxc_conf);
+ if (ret < 0)
+ return log_error(-1, "Failed to turn mount tunnel \"%s\" into dependent mount",
+ lxc_conf->shmount.path_cont);
+
/* Setting the boot-id is best-effort for now. */
if (lxc_conf->autodev > 0)
(void)lxc_setup_boot_id();
* key. For e.g. net.ipv4.ip_forward translated to
* /proc/sys/net/ipv4/ip_forward.
*/
- if (!lxc_list_empty(&lxc_conf->sysctls)) {
- ret = setup_sysctl_parameters(&lxc_conf->sysctls);
- if (ret < 0)
- return log_error(-1, "Failed to setup sysctl parameters");
- }
-
- if (!lxc_list_empty(&lxc_conf->keepcaps)) {
- if (!lxc_list_empty(&lxc_conf->caps))
- return log_error(-1, "Container requests lxc.cap.drop and lxc.cap.keep: either use lxc.cap.drop or lxc.cap.keep, not both");
+ ret = setup_sysctl_parameters(lxc_conf);
+ if (ret < 0)
+ return log_error(-1, "Failed to setup sysctl parameters");
- if (dropcaps_except(&lxc_conf->keepcaps))
- return log_error(-1, "Failed to keep capabilities");
- } else if (setup_caps(&lxc_conf->caps)) {
- return log_error(-1, "Failed to drop capabilities");
- }
+ ret = setup_capabilities(lxc_conf);
+ if (ret < 0)
+ return log_error(-1, "Failed to setup capabilities");
put_lxc_rootfs(&handler->conf->rootfs, true);
NOTICE("The container \"%s\" is set up", name);
int run_lxc_hooks(const char *name, char *hookname, struct lxc_conf *conf,
char *argv[])
{
- struct lxc_list *it;
int which;
+ struct string_entry *entry;
for (which = 0; which < NUM_LXC_HOOKS; which ++) {
if (strequal(hookname, lxchook_names[which]))
if (which >= NUM_LXC_HOOKS)
return -1;
- lxc_list_for_each (it, &conf->hooks[which]) {
+ list_for_each_entry(entry, &conf->hooks[which], head) {
int ret;
- char *hook = it->elem;
+ char *hook = entry->val;
ret = run_script_argv(name, conf->hooks_version, "lxc", hook,
hookname, argv);
int lxc_clear_config_caps(struct lxc_conf *c)
{
- struct lxc_list *it, *next;
+ struct cap_entry *cap, *ncap;
- lxc_list_for_each_safe (it, &c->caps, next) {
- lxc_list_del(it);
- free(it->elem);
- free(it);
+ list_for_each_entry_safe(cap, ncap, &c->caps.list, head) {
+ list_del(&cap->head);
+ free(cap->cap_name);
+ free(cap);
}
- lxc_list_init(&c->caps);
+ c->caps.keep = false;
+ INIT_LIST_HEAD(&c->caps.list);
return 0;
}
-static int lxc_free_idmap(struct lxc_list *id_map)
+static int lxc_free_idmap(struct list_head *id_map)
{
- struct lxc_list *it, *next;
+ struct id_map *map, *nmap;
- lxc_list_for_each_safe(it, id_map, next) {
- lxc_list_del(it);
- free(it->elem);
- free(it);
+ list_for_each_entry_safe(map, nmap, id_map, head) {
+ list_del(&map->head);
+ free(map);
}
- lxc_list_init(id_map);
+ INIT_LIST_HEAD(id_map);
return 0;
}
-static int __lxc_free_idmap(struct lxc_list *id_map)
+static int __lxc_free_idmap(struct list_head *id_map)
{
lxc_free_idmap(id_map);
- free(id_map);
return 0;
}
-define_cleanup_function(struct lxc_list *, __lxc_free_idmap);
+define_cleanup_function(struct list_head *, __lxc_free_idmap);
int lxc_clear_idmaps(struct lxc_conf *c)
{
return lxc_free_idmap(&c->id_map);
}
-int lxc_clear_config_keepcaps(struct lxc_conf *c)
-{
- struct lxc_list *it, *next;
-
- lxc_list_for_each_safe (it, &c->keepcaps, next) {
- lxc_list_del(it);
- free(it->elem);
- free(it);
- }
-
- lxc_list_init(&c->keepcaps);
- return 0;
-}
-
int lxc_clear_namespace(struct lxc_conf *c)
{
for (int i = 0; i < LXC_NS_MAX; i++)
int lxc_clear_cgroups(struct lxc_conf *c, const char *key, int version)
{
- char *global_token, *namespaced_token;
- size_t namespaced_token_len;
- struct lxc_list *it, *next, *list;
const char *k = key;
bool all = false;
+ char *global_token, *namespaced_token;
+ size_t namespaced_token_len;
+ struct list_head *list;
+ struct lxc_cgroup *cgroup, *ncgroup;
if (version == CGROUP2_SUPER_MAGIC) {
global_token = "lxc.cgroup2";
else
return ret_errno(EINVAL);
- lxc_list_for_each_safe(it, list, next) {
- struct lxc_cgroup *cg = it->elem;
-
- if (!all && !strequal(cg->subsystem, k))
+ list_for_each_entry_safe(cgroup, ncgroup, list, head) {
+ if (!all && !strequal(cgroup->subsystem, k))
continue;
- lxc_list_del(it);
- free(cg->subsystem);
- free(cg->value);
- free(cg);
- free(it);
+ list_del(&cgroup->head);
+ free(cgroup->subsystem);
+ free(cgroup->value);
+ free(cgroup);
}
if (all)
- lxc_list_init(list);
+ INIT_LIST_HEAD(list);
return 0;
}
int lxc_clear_limits(struct lxc_conf *c, const char *key)
{
- struct lxc_list *it, *next;
const char *k = NULL;
bool all = false;
+ struct lxc_limit *lim, *nlim;
if (strequal(key, "lxc.limit") || strequal(key, "lxc.prlimit"))
all = true;
else
return ret_errno(EINVAL);
- lxc_list_for_each_safe (it, &c->limits, next) {
- struct lxc_limit *lim = it->elem;
-
+ list_for_each_entry_safe(lim, nlim, &c->limits, head) {
if (!all && !strequal(lim->resource, k))
continue;
- lxc_list_del(it);
-
+ list_del(&lim->head);
free_disarm(lim->resource);
free(lim);
- free(it);
}
if (all)
- lxc_list_init(&c->limits);
+ INIT_LIST_HEAD(&c->limits);
return 0;
}
int lxc_clear_sysctls(struct lxc_conf *c, const char *key)
{
- struct lxc_list *it, *next;
const char *k = NULL;
bool all = false;
+ struct lxc_sysctl *sysctl, *nsysctl;
if (strequal(key, "lxc.sysctl"))
all = true;
else
return -1;
- lxc_list_for_each_safe(it, &c->sysctls, next) {
- struct lxc_sysctl *elem = it->elem;
-
- if (!all && !strequal(elem->key, k))
+ list_for_each_entry_safe(sysctl, nsysctl, &c->sysctls, head) {
+ if (!all && !strequal(sysctl->key, k))
continue;
- lxc_list_del(it);
- free(elem->key);
- free(elem->value);
- free(elem);
- free(it);
+ list_del(&sysctl->head);
+ free(sysctl->key);
+ free(sysctl->value);
+ free(sysctl);
}
if (all)
- lxc_list_init(&c->sysctls);
+ INIT_LIST_HEAD(&c->sysctls);
return 0;
}
int lxc_clear_procs(struct lxc_conf *c, const char *key)
{
- struct lxc_list *it, *next;
const char *k = NULL;
bool all = false;
+ struct lxc_proc *proc, *nproc;
if (strequal(key, "lxc.proc"))
all = true;
else
return -1;
- lxc_list_for_each_safe(it, &c->procs, next) {
- struct lxc_proc *proc = it->elem;
-
+ list_for_each_entry_safe(proc, nproc, &c->procs, head) {
if (!all && !strequal(proc->filename, k))
continue;
- lxc_list_del(it);
+ list_del(&proc->head);
free(proc->filename);
free(proc->value);
free(proc);
- free(it);
}
if (all)
- lxc_list_init(&c->procs);
+ INIT_LIST_HEAD(&c->procs);
return 0;
}
int lxc_clear_groups(struct lxc_conf *c)
{
- struct lxc_list *it, *next;
+ struct string_entry *entry, *nentry;
- lxc_list_for_each_safe (it, &c->groups, next) {
- lxc_list_del(it);
- free(it->elem);
- free(it);
+ list_for_each_entry_safe(entry, nentry, &c->groups, head) {
+ list_del(&entry->head);
+ free(entry->val);
+ free(entry);
}
- lxc_list_init(&c->groups);
+ INIT_LIST_HEAD(&c->groups);
return 0;
}
int lxc_clear_environment(struct lxc_conf *c)
{
- struct lxc_list *it, *next;
+ struct environment_entry *env, *nenv;
- lxc_list_for_each_safe (it, &c->environment, next) {
- lxc_list_del(it);
- free(it->elem);
- free(it);
+ list_for_each_entry_safe(env, nenv, &c->environment, head) {
+ list_del(&env->head);
+ free(env->key);
+ free(env->val);
+ free(env);
}
- lxc_list_init(&c->environment);
+ INIT_LIST_HEAD(&c->environment);
return 0;
}
int lxc_clear_mount_entries(struct lxc_conf *c)
{
- struct lxc_list *it, *next;
+ struct string_entry *entry, *nentry;
- lxc_list_for_each_safe (it, &c->mount_list, next) {
- lxc_list_del(it);
- free(it->elem);
- free(it);
+ list_for_each_entry_safe(entry, nentry, &c->mount_entries, head) {
+ list_del(&entry->head);
+ free(entry->val);
+ free(entry);
}
- lxc_list_init(&c->mount_list);
+ INIT_LIST_HEAD(&c->mount_entries);
return 0;
}
int lxc_clear_hooks(struct lxc_conf *c, const char *key)
{
- struct lxc_list *it, *next;
const char *k = NULL;
bool all = false, done = false;
+ struct string_entry *entry, *nentry;
if (strequal(key, "lxc.hook"))
all = true;
for (int i = 0; i < NUM_LXC_HOOKS; i++) {
if (all || strequal(k, lxchook_names[i])) {
- lxc_list_for_each_safe (it, &c->hooks[i], next) {
- lxc_list_del(it);
- free(it->elem);
- free(it);
+ list_for_each_entry_safe(entry, nentry, &c->hooks[i], head) {
+ list_del(&entry->head);
+ free(entry->val);
+ free(entry);
}
- lxc_list_init(&c->hooks[i]);
-
+ INIT_LIST_HEAD(&c->hooks[i]);
done = true;
}
}
return 0;
}
-static inline void lxc_clear_aliens(struct lxc_conf *conf)
-{
- struct lxc_list *it, *next;
-
- lxc_list_for_each_safe (it, &conf->aliens, next) {
- lxc_list_del(it);
- free(it->elem);
- free(it);
- }
-
- lxc_list_init(&conf->aliens);
-}
-
-void lxc_clear_includes(struct lxc_conf *conf)
-{
- struct lxc_list *it, *next;
-
- lxc_list_for_each_safe(it, &conf->includes, next) {
- lxc_list_del(it);
- free(it->elem);
- free(it);
- }
-
- lxc_list_init(&conf->includes);
-}
-
int lxc_clear_apparmor_raw(struct lxc_conf *c)
{
- struct lxc_list *it, *next;
+ struct string_entry *entry, *nentry;
- lxc_list_for_each_safe (it, &c->lsm_aa_raw, next) {
- lxc_list_del(it);
- free(it->elem);
- free(it);
+ list_for_each_entry_safe(entry, nentry, &c->lsm_aa_raw, head) {
+ list_del(&entry->head);
+ free(entry->val);
+ free(entry);
}
- lxc_list_init(&c->lsm_aa_raw);
+ INIT_LIST_HEAD(&c->lsm_aa_raw);
return 0;
}
lxc_terminal_conf_free(&conf->console);
free(conf->rootfs.mount);
free(conf->rootfs.bdev_type);
- free(conf->rootfs.options);
free(conf->rootfs.path);
put_lxc_rootfs(&conf->rootfs, true);
free(conf->logfile);
free(conf->init_cwd);
free(conf->unexpanded_config);
free(conf->syslog);
- lxc_free_networks(&conf->network);
+ lxc_free_networks(conf);
free(conf->lsm_aa_profile);
free(conf->lsm_aa_profile_computed);
free(conf->lsm_se_context);
free(conf->lsm_se_keyring_context);
lxc_seccomp_free(&conf->seccomp);
lxc_clear_config_caps(conf);
- lxc_clear_config_keepcaps(conf);
lxc_clear_cgroups(conf, "lxc.cgroup", CGROUP_SUPER_MAGIC);
lxc_clear_cgroups(conf, "lxc.cgroup2", CGROUP2_SUPER_MAGIC);
lxc_clear_cgroups_devices(conf);
lxc_clear_mount_entries(conf);
lxc_clear_idmaps(conf);
lxc_clear_groups(conf);
- lxc_clear_includes(conf);
- lxc_clear_aliens(conf);
lxc_clear_environment(conf);
lxc_clear_limits(conf, "lxc.prlimit");
lxc_clear_sysctls(conf, "lxc.sysctl");
free(conf->cgroup_meta.container_dir);
free(conf->cgroup_meta.namespace_dir);
free(conf->cgroup_meta.controllers);
+ free(conf->cgroup_meta.systemd_scope);
free(conf->shmount.path_host);
free(conf->shmount.path_cont);
free(conf);
return retmap;
}
-static struct id_map *find_mapped_hostid_entry(const struct lxc_list *idmap,
+static struct id_map *find_mapped_hostid_entry(const struct list_head *idmap,
unsigned id, enum idtype idtype)
{
- struct id_map *map;
- struct lxc_list *it;
struct id_map *retmap = NULL;
+ struct id_map *map;
- lxc_list_for_each (it, idmap) {
- map = it->elem;
+ list_for_each_entry(map, idmap, head) {
if (map->idtype != idtype)
continue;
return move_ptr(entry);
}
-static struct lxc_list *get_minimal_idmap(const struct lxc_conf *conf,
- uid_t *resuid, gid_t *resgid)
+static int get_minimal_idmap(const struct lxc_conf *conf, uid_t *resuid,
+ gid_t *resgid, struct list_head *head_ret)
{
__do_free struct id_map *container_root_uid = NULL,
*container_root_gid = NULL,
*host_uid_map = NULL, *host_gid_map = NULL;
- __do_free struct lxc_list *idmap = NULL;
uid_t euid, egid;
uid_t nsuid = (conf->root_nsuid_map != NULL) ? 0 : conf->init_uid;
gid_t nsgid = (conf->root_nsgid_map != NULL) ? 0 : conf->init_gid;
- struct lxc_list *tmplist = NULL;
/* Find container root mappings. */
container_root_uid = mapped_nsid_add(conf, nsuid, ID_TYPE_UID);
if (!container_root_uid)
- return log_debug(NULL, "Failed to find mapping for namespace uid %d", 0);
+ return sysdebug("Failed to find mapping for namespace uid %d", 0);
euid = geteuid();
if (euid >= container_root_uid->hostid &&
euid < (container_root_uid->hostid + container_root_uid->range))
container_root_gid = mapped_nsid_add(conf, nsgid, ID_TYPE_GID);
if (!container_root_gid)
- return log_debug(NULL, "Failed to find mapping for namespace gid %d", 0);
+ return sysdebug("Failed to find mapping for namespace gid %d", 0);
egid = getegid();
if (egid >= container_root_gid->hostid &&
egid < (container_root_gid->hostid + container_root_gid->range))
if (!host_uid_map)
host_uid_map = mapped_hostid_add(conf, euid, ID_TYPE_UID);
if (!host_uid_map)
- return log_debug(NULL, "Failed to find mapping for uid %d", euid);
+ return sysdebug("Failed to find mapping for uid %d", euid);
if (!host_gid_map)
host_gid_map = mapped_hostid_add(conf, egid, ID_TYPE_GID);
if (!host_gid_map)
- return log_debug(NULL, "Failed to find mapping for gid %d", egid);
+ return sysdebug("Failed to find mapping for gid %d", egid);
- /* Allocate new {g,u}id map list. */
- idmap = lxc_list_new();
- if (!idmap)
- return NULL;
-
- /* Add container root to the map. */
- tmplist = lxc_list_new();
- if (!tmplist)
- return NULL;
/* idmap will now keep track of that memory. */
- lxc_list_add_elem(tmplist, move_ptr(host_uid_map));
- lxc_list_add_tail(idmap, tmplist);
+ list_add_tail(&host_uid_map->head, head_ret);
+ move_ptr(host_uid_map);
if (container_root_uid) {
- /* Add container root to the map. */
- tmplist = lxc_list_new();
- if (!tmplist)
- return NULL;
/* idmap will now keep track of that memory. */
- lxc_list_add_elem(tmplist, move_ptr(container_root_uid));
- lxc_list_add_tail(idmap, tmplist);
+ list_add_tail(&container_root_uid->head, head_ret);
+ move_ptr(container_root_uid);
}
- tmplist = lxc_list_new();
- if (!tmplist)
- return NULL;
/* idmap will now keep track of that memory. */
- lxc_list_add_elem(tmplist, move_ptr(host_gid_map));
- lxc_list_add_tail(idmap, tmplist);
+ list_add_tail(&host_gid_map->head, head_ret);
+ move_ptr(host_gid_map);
if (container_root_gid) {
- tmplist = lxc_list_new();
- if (!tmplist)
- return NULL;
/* idmap will now keep track of that memory. */
- lxc_list_add_elem(tmplist, move_ptr(container_root_gid));
- lxc_list_add_tail(idmap, tmplist);
+ list_add_tail(&container_root_gid->head, head_ret);
+ move_ptr(container_root_gid);
}
TRACE("Allocated minimal idmapping for ns uid %d and ns gid %d", nsuid, nsgid);
*resuid = nsuid;
if (resgid)
*resgid = nsgid;
- return move_ptr(idmap);
+
+ return 0;
}
/*
int userns_exec_1(const struct lxc_conf *conf, int (*fn)(void *), void *data,
const char *fn_name)
{
- call_cleaner(__lxc_free_idmap) struct lxc_list *idmap = NULL;
+ LIST_HEAD(minimal_idmap);
+ call_cleaner(__lxc_free_idmap) struct list_head *idmap = &minimal_idmap;
int ret = -1, status = -1;
char c = '1';
struct userns_fn_data d = {
if (!conf)
return -EINVAL;
- idmap = get_minimal_idmap(conf, NULL, NULL);
- if (!idmap)
+ ret = get_minimal_idmap(conf, NULL, NULL, idmap);
+ if (ret)
return ret_errno(ENOENT);
ret = pipe2(pipe_fds, O_CLOEXEC);
if (lxc_log_trace()) {
struct id_map *map;
- struct lxc_list *it;
- lxc_list_for_each(it, idmap) {
- map = it->elem;
+ list_for_each_entry(map, idmap, head)
TRACE("Establishing %cid mapping for \"%d\" in new user namespace: nsuid %lu - hostid %lu - range %lu",
(map->idtype == ID_TYPE_UID) ? 'u' : 'g', pid, map->nsid, map->hostid, map->range);
- }
}
/* Set up {g,u}id mapping for user namespace of child process. */
int (*fn_parent)(void *), void *fn_parent_data,
int (*fn_child)(void *), void *fn_child_data)
{
- call_cleaner(__lxc_free_idmap) struct lxc_list *idmap = NULL;
+ LIST_HEAD(minimal_idmap);
+ call_cleaner(__lxc_free_idmap) struct list_head *idmap = &minimal_idmap;
uid_t resuid = LXC_INVALID_UID;
gid_t resgid = LXC_INVALID_GID;
char c = '1';
if (!conf || !fn_child)
return ret_errno(EINVAL);
- idmap = get_minimal_idmap(conf, &resuid, &resgid);
- if (!idmap)
+ ret = get_minimal_idmap(conf, &resuid, &resgid, idmap);
+ if (ret)
return ret_errno(ENOENT);
ret = socketpair(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, sock_fds);
if (lxc_log_trace()) {
struct id_map *map;
- struct lxc_list *it;
- lxc_list_for_each(it, idmap) {
- map = it->elem;
+ list_for_each_entry(map, idmap, head)
TRACE("Establishing %cid mapping for \"%d\" in new user namespace: nsuid %lu - hostid %lu - range %lu",
(map->idtype == ID_TYPE_UID) ? 'u' : 'g', pid, map->nsid, map->hostid, map->range);
- }
}
ret = lxc_read_nointr(sock_fds[1], &c, 1);
int userns_exec_full(struct lxc_conf *conf, int (*fn)(void *), void *data,
const char *fn_name)
{
+ LIST_HEAD(full_idmap);
+ int ret = -1;
+ char c = '1';
+ struct id_map *container_root_uid = NULL, *container_root_gid = NULL,
+ *host_uid_map = NULL, *host_gid_map = NULL;
pid_t pid;
uid_t euid, egid;
int p[2];
struct id_map *map;
- struct lxc_list *cur;
struct userns_fn_data d;
- int ret = -1;
- char c = '1';
- struct lxc_list *idmap = NULL, *tmplist = NULL;
- struct id_map *container_root_uid = NULL, *container_root_gid = NULL,
- *host_uid_map = NULL, *host_gid_map = NULL;
if (!conf)
return -EINVAL;
ret = pipe2(p, O_CLOEXEC);
- if (ret < 0) {
- SYSERROR("opening pipe");
- return -1;
- }
+ if (ret < 0)
+ return -errno;
+
d.fn = fn;
d.fn_name = fn_name;
d.arg = data;
euid = geteuid();
egid = getegid();
- /* Allocate new {g,u}id map list. */
- idmap = lxc_list_new();
- if (!idmap)
- goto on_error;
-
/* Find container root. */
- lxc_list_for_each (cur, &conf->id_map) {
- struct id_map *tmpmap;
-
- tmplist = lxc_list_new();
- if (!tmplist)
- goto on_error;
+ list_for_each_entry(map, &conf->id_map, head) {
+ __do_free struct id_map *dup_map = NULL;
- tmpmap = zalloc(sizeof(*tmpmap));
- if (!tmpmap) {
- free(tmplist);
+ dup_map = memdup(map, sizeof(struct id_map));
+ if (!dup_map)
goto on_error;
- }
- memset(tmpmap, 0, sizeof(*tmpmap));
- memcpy(tmpmap, cur->elem, sizeof(*tmpmap));
- tmplist->elem = tmpmap;
-
- lxc_list_add_tail(idmap, tmplist);
-
- map = cur->elem;
+ list_add_tail(&dup_map->head, &full_idmap);
+ move_ptr(dup_map);
if (map->idtype == ID_TYPE_UID)
if (euid >= map->hostid && euid < map->hostid + map->range)
}
if (host_uid_map && (host_uid_map != container_root_uid)) {
- /* Add container root to the map. */
- tmplist = lxc_list_new();
- if (!tmplist)
- goto on_error;
- lxc_list_add_elem(tmplist, host_uid_map);
- lxc_list_add_tail(idmap, tmplist);
+ /* idmap will now keep track of that memory. */
+ list_add_tail(&host_uid_map->head, &full_idmap);
+ move_ptr(host_uid_map);
}
- /* idmap will now keep track of that memory. */
- host_uid_map = NULL;
if (host_gid_map && (host_gid_map != container_root_gid)) {
- tmplist = lxc_list_new();
- if (!tmplist)
- goto on_error;
- lxc_list_add_elem(tmplist, host_gid_map);
- lxc_list_add_tail(idmap, tmplist);
+ /* idmap will now keep track of that memory. */
+ list_add_tail(&host_gid_map->head, &full_idmap);
+ move_ptr(host_gid_map);
}
- /* idmap will now keep track of that memory. */
- host_gid_map = NULL;
if (lxc_log_trace()) {
- lxc_list_for_each (cur, idmap) {
- map = cur->elem;
- TRACE("establishing %cid mapping for \"%d\" in new "
- "user namespace: nsuid %lu - hostid %lu - range "
- "%lu",
+ list_for_each_entry(map, &full_idmap, head) {
+ TRACE("establishing %cid mapping for \"%d\" in new user namespace: nsuid %lu - hostid %lu - range %lu",
(map->idtype == ID_TYPE_UID) ? 'u' : 'g', pid,
map->nsid, map->hostid, map->range);
}
}
/* Set up {g,u}id mapping for user namespace of child process. */
- ret = lxc_map_ids(idmap, pid);
+ ret = lxc_map_ids(&full_idmap, pid);
if (ret < 0) {
ERROR("error setting up {g,u}id mappings for child process \"%d\"", pid);
goto on_error;
if (pid > 0)
ret = wait_for_pid(pid);
- if (idmap)
- __lxc_free_idmap(idmap);
+ __lxc_free_idmap(&full_idmap);
if (host_uid_map && (host_uid_map != container_root_uid))
free(host_uid_map);
return ret;
}
-static int add_idmap_entry(struct lxc_list *idmap, enum idtype idtype,
+static int add_idmap_entry(struct list_head *idmap_list, enum idtype idtype,
unsigned long nsid, unsigned long hostid,
unsigned long range)
{
__do_free struct id_map *new_idmap = NULL;
- __do_free struct lxc_list *new_list = NULL;
new_idmap = zalloc(sizeof(*new_idmap));
if (!new_idmap)
new_idmap->nsid = nsid;
new_idmap->range = range;
- new_list = zalloc(sizeof(*new_list));
- if (!new_list)
- return ret_errno(ENOMEM);
-
- new_list->elem = move_ptr(new_idmap);
- lxc_list_add_tail(idmap, move_ptr(new_list));
+ list_add_tail(&new_idmap->head, idmap_list);
+ move_ptr(new_idmap);
INFO("Adding id map: type %c nsid %lu hostid %lu range %lu",
idtype == ID_TYPE_UID ? 'u' : 'g', nsid, hostid, range);
int userns_exec_mapped_root(const char *path, int path_fd,
const struct lxc_conf *conf)
{
- call_cleaner(__lxc_free_idmap) struct lxc_list *idmap = NULL;
+ LIST_HEAD(idmap_list);
+ call_cleaner(__lxc_free_idmap) struct list_head *idmap = &idmap_list;
__do_close int fd = -EBADF;
int target_fd = -EBADF;
char c = '1';
TRACE("Chowned %d(%s) to -1:%d", target_fd, path, hostgid);
}
- idmap = lxc_list_new();
- if (!idmap)
- return -ENOMEM;
-
/* "u:0:rootuid:1" */
ret = add_idmap_entry(idmap, ID_TYPE_UID, 0, container_host_uid, 1);
if (ret < 0)
close_prot_errno_disarm(sock_fds[0]);
- if (!lxc_switch_uid_gid(0, 0))
+ if (!lxc_drop_groups() && errno != EPERM)
+ _exit(EXIT_FAILURE);
+
+ ret = setresgid(0, 0, 0);
+ if (ret < 0) {
+ SYSERROR("Failed to setresgid(0, 0, 0)");
_exit(EXIT_FAILURE);
+ }
- if (!lxc_drop_groups())
+ ret = setresuid(0, 0, 0);
+ if (ret < 0) {
+ SYSERROR("Failed to setresuid(0, 0, 0)");
_exit(EXIT_FAILURE);
+ }
ret = fchown(target_fd, 0, st.st_gid);
if (ret) {
if (lxc_log_trace()) {
struct id_map *map;
- struct lxc_list *it;
- lxc_list_for_each(it, idmap) {
- map = it->elem;
+ list_for_each_entry(map, idmap, head)
TRACE("Establishing %cid mapping for \"%d\" in new user namespace: nsuid %lu - hostid %lu - range %lu",
(map->idtype == ID_TYPE_UID) ? 'u' : 'g', pid, map->nsid, map->hostid, map->range);
- }
}
ret = lxc_read_nointr(sock_fds[1], &c, 1);
/* Wait for child to finish. */
if (pid < 0)
+ return log_error(-1, "Failed to create child process");
+
+ if (!wait_exited(pid))
return -1;
- return wait_for_pid(pid);
+ return 0;
}
/* not thread-safe, do not use from api without first forking */
__do_free char *buf = NULL;
struct passwd pwent;
struct passwd *pwentp = NULL;
- size_t bufsize;
+ ssize_t bufsize;
int ret;
bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
- if (bufsize == -1)
+ if (bufsize < 0)
bufsize = 1024;
buf = zalloc(bufsize);
__do_free char *buf = NULL;
struct group grent;
struct group *grentp = NULL;
- size_t bufsize;
+ ssize_t bufsize;
int ret;
bufsize = sysconf(_SC_GETGR_R_SIZE_MAX);
- if (bufsize == -1)
+ if (bufsize < 0)
bufsize = 1024;
buf = zalloc(bufsize);
ERROR("lxc.idmap = g 0 %u %u", gid, grange);
}
-static void free_cgroup_settings(struct lxc_list *result)
-{
- struct lxc_list *iterator, *next;
-
- lxc_list_for_each_safe (iterator, result, next) {
- lxc_list_del(iterator);
- free_disarm(iterator);
- }
- free_disarm(result);
-}
-
-/* Return the list of cgroup_settings sorted according to the following rules
- * 1. Put memory.limit_in_bytes before memory.memsw.limit_in_bytes
- */
-struct lxc_list *sort_cgroup_settings(struct lxc_list *cgroup_settings)
+int lxc_set_environment(const struct lxc_conf *conf)
{
- struct lxc_list *result;
- struct lxc_cgroup *cg = NULL;
- struct lxc_list *it = NULL, *item = NULL, *memsw_limit = NULL;
+ struct environment_entry *env;
- result = lxc_list_new();
- if (!result)
- return NULL;
- lxc_list_init(result);
-
- /* Iterate over the cgroup settings and copy them to the output list. */
- lxc_list_for_each (it, cgroup_settings) {
- item = zalloc(sizeof(*item));
- if (!item) {
- free_cgroup_settings(result);
- return NULL;
- }
+ list_for_each_entry(env, &conf->environment, head) {
+ int ret;
- item->elem = it->elem;
- cg = it->elem;
- if (strequal(cg->subsystem, "memory.memsw.limit_in_bytes")) {
- /* Store the memsw_limit location */
- memsw_limit = item;
- } else if (strequal(cg->subsystem, "memory.limit_in_bytes") &&
- memsw_limit != NULL) {
- /* lxc.cgroup.memory.memsw.limit_in_bytes is found
- * before lxc.cgroup.memory.limit_in_bytes, swap these
- * two items */
- item->elem = memsw_limit->elem;
- memsw_limit->elem = it->elem;
- }
- lxc_list_add_tail(result, item);
+ ret = setenv(env->key, env->val, 1);
+ if (ret < 0)
+ return syserror("Failed to set environment variable: %s=%s",
+ env->key, env->val);
+ TRACE("Set environment variable: %s=%s", env->key, env->val);
}
- return result;
+ return 0;
}