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

View File

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

View File

@@ -42,7 +42,6 @@
#include <sys/prctl.h> #include <sys/prctl.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/vfs.h>
#include <sys/wait.h> #include <sys/wait.h>
#include "log.h" #include "log.h"
@@ -183,22 +182,24 @@ static int _recursive_rmdir(char *dirname, dev_t pdev,
return failed ? -1 : 0; 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 #define OVERLAYFS_SUPER_MAGIC 0x794c764f
#endif
#ifndef OVERLAY_SUPER_MAGIC
#define OVERLAY_SUPER_MAGIC 0x794c7630 #define OVERLAY_SUPER_MAGIC 0x794c7630
/* #endif
* In overlayfs, st_dev is unreliable. so on overlayfs we don't do
* the lxc_rmdir_onedev() /* 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) static bool is_native_overlayfs(const char *path)
{ {
struct statfs sb; if (has_fs_type(path, OVERLAY_SUPER_MAGIC) ||
has_fs_type(path, OVERLAYFS_SUPER_MAGIC))
if (statfs(path, &sb) < 0)
return false;
if (sb.f_type == OVERLAYFS_SUPER_MAGIC ||
sb.f_type == OVERLAY_SUPER_MAGIC)
return true; return true;
return false; return false;
} }
@@ -728,47 +729,46 @@ char **lxc_normalize_path(const char *path)
return components; return components;
} }
bool lxc_deslashify(char **path) char *lxc_deslashify(const char *path)
{ {
bool ret = false; char *dup, *p;
char *p;
char **parts = NULL; char **parts = NULL;
size_t n, len; size_t n, len;
parts = lxc_normalize_path(*path); dup = strdup(path);
if (!parts) if (!dup)
return false; return NULL;
parts = lxc_normalize_path(dup);
if (!parts) {
free(dup);
return NULL;
}
/* We'll end up here if path == "///" or path == "". */ /* We'll end up here if path == "///" or path == "". */
if (!*parts) { if (!*parts) {
len = strlen(*path); len = strlen(dup);
if (!len) { if (!len) {
ret = true; lxc_free_array((void **)parts, free);
goto out; return dup;
} }
n = strcspn(*path, "/"); n = strcspn(dup, "/");
if (n == len) { if (n == len) {
free(dup);
lxc_free_array((void **)parts, free);
p = strdup("/"); p = strdup("/");
if (!p) if (!p)
goto out; return NULL;
free(*path);
*path = p; return p;
ret = true;
goto out;
} }
} }
p = lxc_string_join("/", (const char **)parts, **path == '/'); p = lxc_string_join("/", (const char **)parts, *dup == '/');
if (!p) free(dup);
goto out;
free(*path);
*path = p;
ret = true;
out:
lxc_free_array((void **)parts, free); lxc_free_array((void **)parts, free);
return ret; return p;
} }
char *lxc_append_paths(const char *first, const char *second) char *lxc_append_paths(const char *first, const char *second)
@@ -2384,3 +2384,25 @@ void *must_realloc(void *orig, size_t sz)
return ret; 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 <stdbool.h>
#include <unistd.h> #include <unistd.h>
#include <linux/loop.h> #include <linux/loop.h>
#include <linux/magic.h>
#include <sys/syscall.h> #include <sys/syscall.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/vfs.h>
#include "initutils.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); extern char **lxc_normalize_path(const char *path);
/* remove multiple slashes from the path, e.g. ///foo//bar -> /foo/bar */ /* 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); extern char *lxc_append_paths(const char *first, const char *second);
/* Note: the following two functions use strtok(), so they will never /* Note: the following two functions use strtok(), so they will never
* consider an empty element, even if two delimiters are next to * 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 */ /* Re-alllocate a pointer, do not fail */
void *must_realloc(void *orig, size_t sz); 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 */ #endif /* __LXC_UTILS_H */

View File

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