2
0
mirror of git://github.com/lxc/lxc synced 2025-08-31 09:39:34 +00:00

Merge pull request #1725 from brauner/2017-08-01/handle_pre_mounted_dev

conf: NOTICE() on mounts on container's /dev
This commit is contained in:
Serge Hallyn
2017-08-02 11:18:45 -05:00
committed by GitHub
6 changed files with 386 additions and 258 deletions

View File

@@ -4,11 +4,3 @@ lxc.cgroup.devices.allow =
# We can't move bind-mounts, so don't use /dev/lxc/
lxc.tty.dir =
# Extra bind-mounts for userns
lxc.mount.entry = /dev/full dev/full none bind,create=file 0 0
lxc.mount.entry = /dev/null dev/null none bind,create=file 0 0
lxc.mount.entry = /dev/random dev/random none bind,create=file 0 0
lxc.mount.entry = /dev/tty dev/tty none bind,create=file 0 0
lxc.mount.entry = /dev/urandom dev/urandom none bind,create=file 0 0
lxc.mount.entry = /dev/zero dev/zero none bind,create=file 0 0

View File

@@ -234,8 +234,9 @@ static int memfd_create(const char *name, unsigned int flags) {
extern int memfd_create(const char *name, unsigned int flags);
#endif
char *lxchook_names[NUM_LXC_HOOKS] = {
"pre-start", "pre-mount", "mount", "autodev", "start", "stop", "post-stop", "clone", "destroy" };
char *lxchook_names[NUM_LXC_HOOKS] = {"pre-start", "pre-mount", "mount",
"autodev", "start", "stop",
"post-stop", "clone", "destroy"};
typedef int (*instantiate_cb)(struct lxc_handler *, struct lxc_netdev *);
@@ -1034,58 +1035,56 @@ fail:
return -1;
}
/*
* Just create a path for /dev under $lxcpath/$name and in rootfs
* If we hit an error, log it but don't fail yet.
/* Just create a path for /dev under $lxcpath/$name and in rootfs If we hit an
* error, log it but don't fail yet.
*/
static int mount_autodev(const char *name, const struct lxc_rootfs *rootfs, const char *lxcpath)
static int mount_autodev(const char *name, const struct lxc_rootfs *rootfs,
const char *lxcpath)
{
int ret;
size_t clen;
char *path;
INFO("Mounting container /dev");
INFO("Preparing \"/dev\"");
/* $(rootfs->mount) + "/dev/pts" + '\0' */
clen = (rootfs->path ? strlen(rootfs->mount) : 0) + 9;
path = alloca(clen);
ret = snprintf(path, clen, "%s/dev", rootfs->path ? rootfs->mount : "");
if (ret < 0 || ret >= clen)
if (ret < 0 || (size_t)ret >= clen)
return -1;
if (!dir_exists(path)) {
WARN("No /dev in container.");
WARN("Proceeding without autodev setup");
WARN("\"/dev\" directory does not exist. Proceeding without "
"autodev being set up");
return 0;
}
ret = safe_mount("none", path, "tmpfs", 0, "size=500000,mode=755",
rootfs->path ? rootfs->mount : NULL);
if (ret != 0) {
SYSERROR("Failed mounting tmpfs onto %s\n", path);
rootfs->path ? rootfs->mount : NULL);
if (ret < 0) {
SYSERROR("Failed to mount tmpfs on \"%s\"", path);
return -1;
}
INFO("Mounted tmpfs onto %s", path);
INFO("Mounted tmpfs on \"%s\"", path);
ret = snprintf(path, clen, "%s/dev/pts", rootfs->path ? rootfs->mount : "");
if (ret < 0 || ret >= clen)
if (ret < 0 || (size_t)ret >= clen)
return -1;
/*
* If we are running on a devtmpfs mapping, dev/pts may already exist.
/* If we are running on a devtmpfs mapping, dev/pts may already exist.
* If not, then create it and exit if that fails...
*/
if (!dir_exists(path)) {
ret = mkdir(path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
if (ret) {
SYSERROR("Failed to create /dev/pts in container");
if (ret < 0) {
SYSERROR("Failed to create directory \"%s\"", path);
return -1;
}
}
INFO("Mounted container /dev");
INFO("Prepared \"/dev\"");
return 0;
}
@@ -1097,12 +1096,12 @@ struct lxc_devs {
};
static const struct lxc_devs lxc_devs[] = {
{ "null", S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO, 1, 3 },
{ "zero", S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO, 1, 5 },
{ "full", S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO, 1, 7 },
{ "urandom", S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO, 1, 9 },
{ "random", S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO, 1, 8 },
{ "tty", S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO, 5, 0 },
{ "null", S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO, 1, 3 },
{ "zero", S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO, 1, 5 },
{ "full", S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO, 1, 7 },
{ "urandom", S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO, 1, 9 },
{ "random", S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO, 1, 8 },
{ "tty", S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO, 5, 0 },
};
static int lxc_fill_autodev(const struct lxc_rootfs *rootfs)
@@ -1112,29 +1111,30 @@ static int lxc_fill_autodev(const struct lxc_rootfs *rootfs)
int i;
mode_t cmask;
ret = snprintf(path, MAXPATHLEN, "%s/dev", rootfs->path ? rootfs->mount : "");
if (ret < 0 || ret >= MAXPATHLEN) {
ERROR("Error calculating container /dev location");
ret = snprintf(path, MAXPATHLEN, "%s/dev",
rootfs->path ? rootfs->mount : "");
if (ret < 0 || ret >= MAXPATHLEN)
return -1;
}
/* ignore, just don't try to fill in */
if (!dir_exists(path))
return 0;
INFO("populating container /dev");
INFO("Populating \"/dev\"");
cmask = umask(S_IXUSR | S_IXGRP | S_IXOTH);
for (i = 0; i < sizeof(lxc_devs) / sizeof(lxc_devs[0]); i++) {
const struct lxc_devs *d = &lxc_devs[i];
ret = snprintf(path, MAXPATHLEN, "%s/dev/%s", rootfs->path ? rootfs->mount : "", d->name);
ret = snprintf(path, MAXPATHLEN, "%s/dev/%s",
rootfs->path ? rootfs->mount : "", d->name);
if (ret < 0 || ret >= MAXPATHLEN)
return -1;
ret = mknod(path, d->mode, makedev(d->maj, d->min));
if (ret < 0) {
char hostpath[MAXPATHLEN];
FILE *pathfile;
char hostpath[MAXPATHLEN];
if (errno == EEXIST) {
DEBUG("\"%s\" device already existed", path);
@@ -1147,24 +1147,31 @@ static int lxc_fill_autodev(const struct lxc_rootfs *rootfs)
ret = snprintf(hostpath, MAXPATHLEN, "/dev/%s", d->name);
if (ret < 0 || ret >= MAXPATHLEN)
return -1;
pathfile = fopen(path, "wb");
if (!pathfile) {
SYSERROR("Failed to create device mount target '%s'", path);
SYSERROR("Failed to create file \"%s\"", path);
return -1;
}
fclose(pathfile);
if (safe_mount(hostpath, path, 0, MS_BIND, NULL, rootfs->path ? rootfs->mount : NULL) != 0) {
SYSERROR("Failed bind mounting device %s from host into container", d->name);
ret = safe_mount(hostpath, path, 0, MS_BIND, NULL,
rootfs->path ? rootfs->mount : NULL);
if (ret < 0) {
SYSERROR("Failed to bind mount \"%s\" from "
"host into container",
d->name);
return -1;
}
DEBUG("bind mounted \"%s\" onto \"%s\"", hostpath, path);
DEBUG("Bind mounted \"%s\" onto \"%s\"", hostpath,
path);
} else {
DEBUG("created device node \"%s\"", path);
DEBUG("Created device node \"%s\"", path);
}
}
umask(cmask);
INFO("populated container /dev");
INFO("Populated \"/dev\"");
return 0;
}
@@ -1726,174 +1733,201 @@ static char *get_field(char *src, int nfields)
static int mount_entry(const char *fsname, const char *target,
const char *fstype, unsigned long mountflags,
const char *data, int optional, int dev, const char *rootfs)
const char *data, int optional, int dev,
const char *rootfs)
{
int ret;
#ifdef HAVE_STATVFS
struct statvfs sb;
#endif
if (safe_mount(fsname, target, fstype, mountflags & ~MS_REMOUNT, data, rootfs)) {
ret = safe_mount(fsname, target, fstype, mountflags & ~MS_REMOUNT, data,
rootfs);
if (ret < 0) {
if (optional) {
INFO("failed to mount '%s' on '%s' (optional): %s", fsname,
target, strerror(errno));
INFO("Failed to mount \"%s\" on \"%s\" (optional): %s",
fsname, target, strerror(errno));
return 0;
}
else {
SYSERROR("failed to mount '%s' on '%s'", fsname, target);
return -1;
}
SYSERROR("Failed to mount \"%s\" on \"%s\"", fsname, target);
return -1;
}
if ((mountflags & MS_REMOUNT) || (mountflags & MS_BIND)) {
DEBUG("remounting %s on %s to respect bind or remount options",
fsname ? fsname : "(none)", target ? target : "(none)");
unsigned long rqd_flags = 0;
DEBUG("Remounting \"%s\" on \"%s\" to respect bind or remount "
"options",
fsname ? fsname : "(none)", target ? target : "(none)");
if (mountflags & MS_RDONLY)
rqd_flags |= MS_RDONLY;
#ifdef HAVE_STATVFS
if (statvfs(fsname, &sb) == 0) {
unsigned long required_flags = rqd_flags;
if (sb.f_flag & MS_NOSUID)
required_flags |= MS_NOSUID;
if (sb.f_flag & MS_NODEV && !dev)
required_flags |= MS_NODEV;
if (sb.f_flag & MS_RDONLY)
required_flags |= MS_RDONLY;
if (sb.f_flag & MS_NOEXEC)
required_flags |= MS_NOEXEC;
DEBUG("(at remount) flags for %s was %lu, required extra flags are %lu", fsname, sb.f_flag, required_flags);
/*
* If this was a bind mount request, and required_flags
DEBUG("Flags for \"%s\" were %lu, required extra flags "
"are %lu", fsname, sb.f_flag, required_flags);
/* If this was a bind mount request, and required_flags
* does not have any flags which are not already in
* mountflags, then skip the remount
* mountflags, then skip the remount.
*/
if (!(mountflags & MS_REMOUNT)) {
if (!(required_flags & ~mountflags) && rqd_flags == 0) {
DEBUG("mountflags already was %lu, skipping remount",
mountflags);
if (!(required_flags & ~mountflags) &&
rqd_flags == 0) {
DEBUG("Mountflags already were %lu, "
"skipping remount", mountflags);
goto skipremount;
}
}
mountflags |= required_flags;
}
#endif
if (mount(fsname, target, fstype,
mountflags | MS_REMOUNT, data) < 0) {
ret = mount(fsname, target, fstype, mountflags | MS_REMOUNT, data);
if (ret < 0) {
if (optional) {
INFO("failed to mount '%s' on '%s' (optional): %s",
fsname, target, strerror(errno));
INFO("Failed to mount \"%s\" on \"%s\" "
"(optional): %s", fsname, target,
strerror(errno));
return 0;
}
else {
SYSERROR("failed to mount '%s' on '%s'",
fsname, target);
return -1;
}
SYSERROR("Failed to mount \"%s\" on \"%s\"", fsname, target);
return -1;
}
}
#ifdef HAVE_STATVFS
skipremount:
#endif
DEBUG("mounted '%s' on '%s', type '%s'", fsname, target, fstype);
DEBUG("Mounted \"%s\" on \"%s\" with filesystem type \"%s\"", fsname,
target, fstype);
return 0;
}
/*
* Remove 'optional', 'create=dir', and 'create=file' from mntopt
*/
/* Remove "optional", "create=dir", and "create=file" from mntopt */
static void cull_mntent_opt(struct mntent *mntent)
{
int i;
char *p, *p2;
char *list[] = {"create=dir",
"create=file",
"optional",
NULL };
char *list[] = {"create=dir", "create=file", "optional", NULL};
for (i=0; list[i]; i++) {
if (!(p = strstr(mntent->mnt_opts, list[i])))
for (i = 0; list[i]; i++) {
char *p, *p2;
p = strstr(mntent->mnt_opts, list[i]);
if (!p)
continue;
p2 = strchr(p, ',');
if (!p2) {
/* no more mntopts, so just chop it here */
*p = '\0';
continue;
}
memmove(p, p2+1, strlen(p2+1)+1);
memmove(p, p2 + 1, strlen(p2 + 1) + 1);
}
}
static int mount_entry_create_dir_file(const struct mntent *mntent,
const char* path, const struct lxc_rootfs *rootfs,
const char *lxc_name, const char *lxc_path)
const char *path,
const struct lxc_rootfs *rootfs,
const char *lxc_name,
const char *lxc_path)
{
char *pathdirname = NULL;
int ret = 0;
FILE *pathfile = NULL;
if (strncmp(mntent->mnt_type, "overlay", 7) == 0) {
if (ovl_mkdir(mntent, rootfs, lxc_name, lxc_path) < 0)
return -1;
} else if (strncmp(mntent->mnt_type, "aufs", 4) == 0) {
if (aufs_mkdir(mntent, rootfs, lxc_name, lxc_path) < 0)
return -1;
}
if (!strncmp(mntent->mnt_type, "overlay", 7))
ret = ovl_mkdir(mntent, rootfs, lxc_name, lxc_path);
else if (!strncmp(mntent->mnt_type, "aufs", 4))
ret = aufs_mkdir(mntent, rootfs, lxc_name, lxc_path);
if (ret < 0)
return -1;
if (hasmntopt(mntent, "create=dir")) {
if (mkdir_p(path, 0755) < 0) {
WARN("Failed to create mount target '%s'", path);
ret = -1;
ret = mkdir_p(path, 0755);
if (ret < 0 && errno != EEXIST) {
SYSERROR("Failed to create directory \"%s\"", path);
return -1;
}
}
if (hasmntopt(mntent, "create=file") && access(path, F_OK)) {
pathdirname = strdup(path);
pathdirname = dirname(pathdirname);
if (mkdir_p(pathdirname, 0755) < 0) {
WARN("Failed to create target directory");
}
pathfile = fopen(path, "wb");
if (!pathfile) {
WARN("Failed to create mount target '%s'", path);
ret = -1;
} else {
fclose(pathfile);
int fd;
char *p1, *p2;
p1 = strdup(path);
if (!p1)
return -1;
p2 = dirname(p1);
ret = mkdir_p(p2, 0755);
free(p1);
if (ret < 0 && errno != EEXIST) {
SYSERROR("Failed to create directory \"%s\"", path);
return -1;
}
fd = open(path, O_CREAT, 0644);
if (fd < 0)
return -1;
close(fd);
}
free(pathdirname);
return ret;
return 0;
}
/* rootfs, lxc_name, and lxc_path can be NULL when the container is created
* without a rootfs. */
static inline int mount_entry_on_generic(struct mntent *mntent,
const char* path, const struct lxc_rootfs *rootfs,
const char *lxc_name, const char *lxc_path)
const char *path,
const struct lxc_rootfs *rootfs,
const char *lxc_name,
const char *lxc_path)
{
int ret;
unsigned long mntflags;
char *mntdata;
int ret;
bool optional = hasmntopt(mntent, "optional") != NULL;
bool dev = hasmntopt(mntent, "dev") != NULL;
bool dev, optional;
char *rootfs_path = NULL;
optional = hasmntopt(mntent, "optional") != NULL;
dev = hasmntopt(mntent, "dev") != NULL;
if (rootfs && rootfs->path)
rootfs_path = rootfs->mount;
ret = mount_entry_create_dir_file(mntent, path, rootfs, lxc_name, lxc_path);
ret = mount_entry_create_dir_file(mntent, path, rootfs, lxc_name,
lxc_path);
if (ret < 0) {
if (optional)
return 0;
if (ret < 0)
return optional ? 0 : -1;
cull_mntent_opt(mntent);
if (parse_mntopts(mntent->mnt_opts, &mntflags, &mntdata) < 0) {
free(mntdata);
return -1;
}
cull_mntent_opt(mntent);
ret = parse_mntopts(mntent->mnt_opts, &mntflags, &mntdata);
if (ret < 0)
return -1;
ret = mount_entry(mntent->mnt_fsname, path, mntent->mnt_type, mntflags,
mntdata, optional, dev, rootfs_path);
@@ -1904,20 +1938,18 @@ static inline int mount_entry_on_generic(struct mntent *mntent,
static inline int mount_entry_on_systemfs(struct mntent *mntent)
{
char path[MAXPATHLEN];
int ret;
char path[MAXPATHLEN];
/* For containers created without a rootfs all mounts are treated as
* absolute paths starting at / on the host. */
* absolute paths starting at / on the host.
*/
if (mntent->mnt_dir[0] != '/')
ret = snprintf(path, sizeof(path), "/%s", mntent->mnt_dir);
else
ret = snprintf(path, sizeof(path), "%s", mntent->mnt_dir);
if (ret < 0 || ret >= sizeof(path)) {
ERROR("path name too long");
if (ret < 0 || ret >= sizeof(path))
return -1;
}
return mount_entry_on_generic(mntent, path, NULL, NULL, NULL);
}
@@ -1927,21 +1959,21 @@ static int mount_entry_on_absolute_rootfs(struct mntent *mntent,
const char *lxc_name,
const char *lxc_path)
{
int offset;
char *aux;
char path[MAXPATHLEN];
int r, ret = 0, offset;
const char *lxcpath;
char path[MAXPATHLEN];
int ret = 0;
lxcpath = lxc_global_config_value("lxc.lxcpath");
if (!lxcpath) {
ERROR("Out of memory");
if (!lxcpath)
return -1;
}
/* if rootfs->path is a blockdev path, allow container fstab to
* use $lxcpath/CN/rootfs as the target prefix */
r = snprintf(path, MAXPATHLEN, "%s/%s/rootfs", lxcpath, lxc_name);
if (r < 0 || r >= MAXPATHLEN)
/* If rootfs->path is a blockdev path, allow container fstab to use
* <lxcpath>/<name>/rootfs" as the target prefix.
*/
ret = snprintf(path, MAXPATHLEN, "%s/%s/rootfs", lxcpath, lxc_name);
if (ret < 0 || ret >= MAXPATHLEN)
goto skipvarlib;
aux = strstr(mntent->mnt_dir, path);
@@ -1953,19 +1985,15 @@ static int mount_entry_on_absolute_rootfs(struct mntent *mntent,
skipvarlib:
aux = strstr(mntent->mnt_dir, rootfs->path);
if (!aux) {
WARN("ignoring mount point '%s'", mntent->mnt_dir);
WARN("Ignoring mount point \"%s\"", mntent->mnt_dir);
return ret;
}
offset = strlen(rootfs->path);
skipabs:
r = snprintf(path, MAXPATHLEN, "%s/%s", rootfs->mount,
aux + offset);
if (r < 0 || r >= MAXPATHLEN) {
WARN("pathnme too long for '%s'", mntent->mnt_dir);
ret = snprintf(path, MAXPATHLEN, "%s/%s", rootfs->mount, aux + offset);
if (ret < 0 || ret >= MAXPATHLEN)
return -1;
}
return mount_entry_on_generic(mntent, path, rootfs, lxc_name, lxc_path);
}
@@ -1988,57 +2016,123 @@ static int mount_entry_on_relative_rootfs(struct mntent *mntent,
return mount_entry_on_generic(mntent, path, rootfs, lxc_name, lxc_path);
}
static int mount_file_entries(const struct lxc_rootfs *rootfs, FILE *file,
const char *lxc_name, const char *lxc_path)
/* This logs a NOTICE() when a user specifies mounts that would conflict with
* devices liblxc sets up automatically.
*/
static void log_notice_on_conflict(const struct lxc_conf *conf, const char *src,
const char *dest)
{
char *clean_mnt_fsname, *clean_mnt_dir, *tmp;
bool needs_warning = false;
clean_mnt_fsname = lxc_deslashify(src);
if (!clean_mnt_fsname)
return;
clean_mnt_dir = lxc_deslashify(dest);
if (!clean_mnt_dir) {
free(clean_mnt_fsname);
return;
}
tmp = clean_mnt_dir;
if (*tmp == '/')
tmp++;
if (strncmp(src, "/dev", 4) || strncmp(tmp, "dev", 3)) {
free(clean_mnt_dir);
free(clean_mnt_fsname);
return;
}
if (!conf->autodev && !conf->pts && !conf->tty &&
(!conf->console.path || !strcmp(conf->console.path, "none"))) {
free(clean_mnt_dir);
free(clean_mnt_fsname);
return;
}
if (!strcmp(tmp, "dev") && conf->autodev > 0)
needs_warning = true;
else if (!strcmp(tmp, "dev/pts") && (conf->autodev > 0 || conf->pts > 0))
needs_warning = true;
else if (!strcmp(tmp, "dev/ptmx") && (conf->autodev > 0 || conf->pts > 0))
needs_warning = true;
else if (!strcmp(tmp, "dev/pts/ptmx") && (conf->autodev > 0 || conf->pts > 0))
needs_warning = true;
else if (!strcmp(tmp, "dev/null") && conf->autodev > 0)
needs_warning = true;
else if (!strcmp(tmp, "dev/zero") && conf->autodev > 0)
needs_warning = true;
else if (!strcmp(tmp, "dev/full") && conf->autodev > 0)
needs_warning = true;
else if (!strcmp(tmp, "dev/urandom") && conf->autodev > 0)
needs_warning = true;
else if (!strcmp(tmp, "dev/random") && conf->autodev > 0)
needs_warning = true;
else if (!strcmp(tmp, "dev/tty") && conf->autodev > 0)
needs_warning = true;
else if (!strncmp(tmp, "dev/tty", 7) && (conf->autodev > 0 || conf->tty > 0))
needs_warning = true;
if (needs_warning)
NOTICE("Requesting to mount \"%s\" on \"%s\" while requesting "
"automatic device setup under \"/dev\"",
clean_mnt_fsname, clean_mnt_dir);
free(clean_mnt_dir);
free(clean_mnt_fsname);
}
static int mount_file_entries(const struct lxc_conf *conf,
const struct lxc_rootfs *rootfs, FILE *file,
const char *lxc_name, const char *lxc_path)
{
struct mntent mntent;
char buf[4096];
int ret = -1;
while (getmntent_r(file, &mntent, buf, sizeof(buf))) {
log_notice_on_conflict(conf, mntent.mnt_fsname, mntent.mnt_dir);
if (!rootfs->path) {
if (mount_entry_on_systemfs(&mntent))
goto out;
continue;
}
/* We have a separate root, mounts are relative to it */
if (mntent.mnt_dir[0] != '/') {
if (mount_entry_on_relative_rootfs(&mntent, rootfs, lxc_name, lxc_path))
goto out;
continue;
}
if (mount_entry_on_absolute_rootfs(&mntent, rootfs, lxc_name, lxc_path))
goto out;
if (!rootfs->path)
ret = mount_entry_on_systemfs(&mntent);
else if (mntent.mnt_dir[0] != '/')
ret = mount_entry_on_relative_rootfs(&mntent, rootfs,
lxc_name, lxc_path);
else
ret = mount_entry_on_absolute_rootfs(&mntent, rootfs,
lxc_name, lxc_path);
if (ret < 0)
return -1;
}
ret = 0;
INFO("mount points have been setup");
out:
INFO("Set up mount entries");
return ret;
}
static int setup_mount(const struct lxc_rootfs *rootfs, const char *fstab,
const char *lxc_name, const char *lxc_path)
static int setup_mount(const struct lxc_conf *conf,
const struct lxc_rootfs *rootfs, const char *fstab,
const char *lxc_name, const char *lxc_path)
{
FILE *file;
FILE *f;
int ret;
if (!fstab)
return 0;
file = setmntent(fstab, "r");
if (!file) {
SYSERROR("failed to use '%s'", fstab);
f = setmntent(fstab, "r");
if (!f) {
SYSERROR("Failed to open \"%s\"", fstab);
return -1;
}
ret = mount_file_entries(rootfs, file, lxc_name, lxc_path);
ret = mount_file_entries(conf, rootfs, f, lxc_name, lxc_path);
if (ret < 0)
ERROR("Failed to set up mount entries");
endmntent(file);
endmntent(f);
return ret;
}
@@ -2047,55 +2141,59 @@ FILE *make_anonymous_mount_file(struct lxc_list *mount)
int ret;
char *mount_entry;
struct lxc_list *iterator;
FILE *file;
FILE *f;
int fd = -1;
fd = memfd_create("lxc_mount_file", MFD_CLOEXEC);
if (fd < 0) {
if (errno != ENOSYS)
return NULL;
file = tmpfile();
f = tmpfile();
TRACE("Created temporary mount file");
} else {
file = fdopen(fd, "r+");
f = fdopen(fd, "r+");
TRACE("Created anonymous mount file");
}
if (!file) {
int saved_errno = errno;
if (!f) {
SYSERROR("Could not create mount file");
if (fd != -1)
close(fd);
ERROR("Could not create mount entry file: %s.", strerror(saved_errno));
return NULL;
}
lxc_list_for_each(iterator, mount) {
mount_entry = iterator->elem;
ret = fprintf(file, "%s\n", mount_entry);
ret = fprintf(f, "%s\n", mount_entry);
if (ret < strlen(mount_entry))
WARN("Could not write mount entry to anonymous mount file.");
WARN("Could not write mount entry to mount file");
}
if (fseek(file, 0, SEEK_SET) < 0) {
fclose(file);
ret = fseek(f, 0, SEEK_SET);
if (ret < 0) {
SYSERROR("Failed to seek mount file");
fclose(f);
return NULL;
}
return file;
return f;
}
static int setup_mount_entries(const struct lxc_rootfs *rootfs,
static int setup_mount_entries(const struct lxc_conf *conf,
const struct lxc_rootfs *rootfs,
struct lxc_list *mount, const char *lxc_name,
const char *lxc_path)
{
FILE *file;
FILE *f;
int ret;
file = make_anonymous_mount_file(mount);
if (!file)
f = make_anonymous_mount_file(mount);
if (!f)
return -1;
ret = mount_file_entries(rootfs, file, lxc_name, lxc_path);
ret = mount_file_entries(conf, rootfs, f, lxc_name, lxc_path);
fclose(file);
fclose(f);
return ret;
}
@@ -4137,12 +4235,12 @@ int lxc_setup(struct lxc_handler *handler)
return -1;
}
if (setup_mount(&lxc_conf->rootfs, lxc_conf->fstab, name, lxcpath)) {
if (setup_mount(lxc_conf, &lxc_conf->rootfs, lxc_conf->fstab, name, lxcpath)) {
ERROR("failed to setup the mounts for '%s'", name);
return -1;
}
if (!lxc_list_empty(&lxc_conf->mount_list) && setup_mount_entries(&lxc_conf->rootfs, &lxc_conf->mount_list, name, lxcpath)) {
if (!lxc_list_empty(&lxc_conf->mount_list) && setup_mount_entries(lxc_conf, &lxc_conf->rootfs, &lxc_conf->mount_list, name, lxcpath)) {
ERROR("failed to setup the mount entries for '%s'", name);
return -1;
}
@@ -4173,6 +4271,7 @@ int lxc_setup(struct lxc_handler *handler)
ERROR("failed to run autodev hooks for container '%s'.", name);
return -1;
}
if (lxc_fill_autodev(&lxc_conf->rootfs)) {
ERROR("failed to populate /dev in the container");
return -1;

View File

@@ -263,7 +263,7 @@ static void exec_criu(struct criu_opts *opts)
for (i = 0; i < cgroup_num_hierarchies(); i++) {
char **controllers = NULL, *fullname;
char *path;
char *path, *tmp;
if (!cgroup_get_hierarchies(i, &controllers)) {
ERROR("failed to get hierarchy %d", i);
@@ -296,11 +296,15 @@ static void exec_criu(struct criu_opts *opts)
}
}
if (!lxc_deslashify(&path)) {
ERROR("failed to deslashify %s", path);
tmp = lxc_deslashify(path);
if (!tmp) {
ERROR("Failed to remove extraneous slashes from \"%s\"",
path);
free(path);
goto err;
}
free(path);
path = tmp;
fullname = lxc_string_join(",", (const char **) controllers, false);
if (!fullname) {

View File

@@ -42,7 +42,6 @@
#include <sys/prctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/vfs.h>
#include <sys/wait.h>
#include "log.h"
@@ -183,22 +182,24 @@ static int _recursive_rmdir(char *dirname, dev_t pdev,
return failed ? -1 : 0;
}
/* we have two different magic values for overlayfs, yay */
/* We have two different magic values for overlayfs, yay. */
#ifndef OVERLAYFS_SUPER_MAGIC
#define OVERLAYFS_SUPER_MAGIC 0x794c764f
#endif
#ifndef OVERLAY_SUPER_MAGIC
#define OVERLAY_SUPER_MAGIC 0x794c7630
/*
* In overlayfs, st_dev is unreliable. so on overlayfs we don't do
* the lxc_rmdir_onedev()
#endif
/* In overlayfs, st_dev is unreliable. So on overlayfs we don't do the
* lxc_rmdir_onedev()
*/
static bool is_native_overlayfs(const char *path)
{
struct statfs sb;
if (statfs(path, &sb) < 0)
return false;
if (sb.f_type == OVERLAYFS_SUPER_MAGIC ||
sb.f_type == OVERLAY_SUPER_MAGIC)
if (has_fs_type(path, OVERLAY_SUPER_MAGIC) ||
has_fs_type(path, OVERLAYFS_SUPER_MAGIC))
return true;
return false;
}
@@ -728,47 +729,46 @@ char **lxc_normalize_path(const char *path)
return components;
}
bool lxc_deslashify(char **path)
char *lxc_deslashify(const char *path)
{
bool ret = false;
char *p;
char *dup, *p;
char **parts = NULL;
size_t n, len;
parts = lxc_normalize_path(*path);
if (!parts)
return false;
dup = strdup(path);
if (!dup)
return NULL;
parts = lxc_normalize_path(dup);
if (!parts) {
free(dup);
return NULL;
}
/* We'll end up here if path == "///" or path == "". */
if (!*parts) {
len = strlen(*path);
len = strlen(dup);
if (!len) {
ret = true;
goto out;
lxc_free_array((void **)parts, free);
return dup;
}
n = strcspn(*path, "/");
n = strcspn(dup, "/");
if (n == len) {
free(dup);
lxc_free_array((void **)parts, free);
p = strdup("/");
if (!p)
goto out;
free(*path);
*path = p;
ret = true;
goto out;
return NULL;
return p;
}
}
p = lxc_string_join("/", (const char **)parts, **path == '/');
if (!p)
goto out;
free(*path);
*path = p;
ret = true;
out:
p = lxc_string_join("/", (const char **)parts, *dup == '/');
free(dup);
lxc_free_array((void **)parts, free);
return ret;
return p;
}
char *lxc_append_paths(const char *first, const char *second)
@@ -2384,3 +2384,25 @@ void *must_realloc(void *orig, size_t sz)
return ret;
}
bool is_fs_type(const struct statfs *fs, fs_type_magic magic_val)
{
return (fs->f_type == (fs_type_magic)magic_val);
}
bool has_fs_type(const char *path, fs_type_magic magic_val)
{
bool has_type;
int ret;
struct statfs sb;
ret = statfs(path, &sb);
if (ret < 0)
return false;
has_type = is_fs_type(&sb, magic_val);
if (!has_type && magic_val == RAMFS_MAGIC)
WARN("When the ramfs it a tmpfs statfs() might report tmpfs");
return has_type;
}

View File

@@ -34,8 +34,10 @@
#include <stdbool.h>
#include <unistd.h>
#include <linux/loop.h>
#include <linux/magic.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/vfs.h>
#include "initutils.h"
@@ -273,7 +275,7 @@ extern char *lxc_string_join(const char *sep, const char **parts, bool use_as_pr
*/
extern char **lxc_normalize_path(const char *path);
/* remove multiple slashes from the path, e.g. ///foo//bar -> /foo/bar */
extern bool lxc_deslashify(char **path);
extern char *lxc_deslashify(const char *path);
extern char *lxc_append_paths(const char *first, const char *second);
/* Note: the following two functions use strtok(), so they will never
* consider an empty element, even if two delimiters are next to
@@ -386,4 +388,9 @@ char *must_copy_string(const char *entry);
/* Re-alllocate a pointer, do not fail */
void *must_realloc(void *orig, size_t sz);
/* __typeof__ should be safe to use with all compilers. */
typedef __typeof__(((struct statfs *)NULL)->f_type) fs_type_magic;
bool has_fs_type(const char *path, fs_type_magic magic_val);
bool is_fs_type(const struct statfs *fs, fs_type_magic magic_val);
#endif /* __LXC_UTILS_H */

View File

@@ -41,33 +41,37 @@
void test_lxc_deslashify(void)
{
char *s = strdup("/A///B//C/D/E/");
if (!s)
exit(EXIT_FAILURE);
lxc_test_assert_abort(lxc_deslashify(&s));
lxc_test_assert_abort(strcmp(s, "/A/B/C/D/E") == 0);
free(s);
char *s = "/A///B//C/D/E/";
char *t;
s = strdup("/A");
if (!s)
t = lxc_deslashify(s);
if (!t)
exit(EXIT_FAILURE);
lxc_test_assert_abort(lxc_deslashify(&s));
lxc_test_assert_abort(strcmp(s, "/A") == 0);
free(s);
lxc_test_assert_abort(strcmp(t, "/A/B/C/D/E") == 0);
free(t);
s = strdup("");
if (!s)
exit(EXIT_FAILURE);
lxc_test_assert_abort(lxc_deslashify(&s));
lxc_test_assert_abort(strcmp(s, "") == 0);
free(s);
s = "/A";
s = strdup("//");
if (!s)
t = lxc_deslashify(s);
if (!t)
exit(EXIT_FAILURE);
lxc_test_assert_abort(lxc_deslashify(&s));
lxc_test_assert_abort(strcmp(s, "/") == 0);
free(s);
lxc_test_assert_abort(strcmp(t, "/A") == 0);
free(t);
s = "";
t = lxc_deslashify(s);
if (!t)
exit(EXIT_FAILURE);
lxc_test_assert_abort(strcmp(t, "") == 0);
free(t);
s = "//";
t = lxc_deslashify(s);
if (!t)
exit(EXIT_FAILURE);
lxc_test_assert_abort(strcmp(t, "/") == 0);
free(t);
}
/* /proc/int_as_str/ns/mnt\0 = (5 + 21 + 7 + 1) */