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:
@@ -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
|
|
||||||
|
475
src/lxc/conf.c
475
src/lxc/conf.c
@@ -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;
|
||||||
|
@@ -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) {
|
||||||
|
@@ -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;
|
||||||
|
}
|
||||||
|
@@ -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 */
|
||||||
|
@@ -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) */
|
||||||
|
Reference in New Issue
Block a user