2012-05-04 13:38:00 +04:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <sys/mman.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <dirent.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <string.h>
|
2012-06-27 20:57:31 +04:00
|
|
|
#include <stdlib.h>
|
2012-06-27 20:57:38 +04:00
|
|
|
#include <sys/mount.h>
|
2012-08-09 19:51:22 +04:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/wait.h>
|
2012-05-04 13:38:00 +04:00
|
|
|
|
2012-06-27 20:57:31 +04:00
|
|
|
#include "crtools.h"
|
2013-01-09 17:02:47 +04:00
|
|
|
#include "asm/types.h"
|
2012-05-04 13:38:00 +04:00
|
|
|
#include "util.h"
|
2013-07-29 13:12:00 +04:00
|
|
|
#include "util-pie.h"
|
2012-05-04 13:38:00 +04:00
|
|
|
#include "log.h"
|
2012-05-04 13:38:00 +04:00
|
|
|
#include "mount.h"
|
|
|
|
#include "proc_parse.h"
|
2012-06-27 20:57:31 +04:00
|
|
|
#include "image.h"
|
2013-01-15 23:24:01 +04:00
|
|
|
#include "namespaces.h"
|
2012-07-17 14:23:37 +04:00
|
|
|
#include "protobuf.h"
|
|
|
|
#include "protobuf/mnt.pb-c.h"
|
|
|
|
|
2012-06-27 20:57:30 +04:00
|
|
|
static struct mount_info *mntinfo;
|
2012-08-01 07:00:48 +04:00
|
|
|
int mntns_root = -1;
|
2012-05-04 13:38:00 +04:00
|
|
|
|
2013-07-30 20:25:30 +04:00
|
|
|
static inline int is_root(char *p)
|
|
|
|
{
|
|
|
|
return p[0] == '/' && p[1] == '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int is_root_mount(struct mount_info *mi)
|
|
|
|
{
|
|
|
|
return is_root(mi->mountpoint);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int fsroot_mounted(struct mount_info *mi)
|
|
|
|
{
|
|
|
|
return is_root(mi->root);
|
|
|
|
}
|
|
|
|
|
2012-05-04 13:38:00 +04:00
|
|
|
int open_mount(unsigned int s_dev)
|
2012-05-04 13:38:00 +04:00
|
|
|
{
|
2012-06-27 20:57:30 +04:00
|
|
|
struct mount_info *i;
|
2012-05-04 13:38:00 +04:00
|
|
|
|
2012-05-13 00:07:11 +04:00
|
|
|
for (i = mntinfo; i != NULL; i = i->next)
|
|
|
|
if (s_dev == i->s_dev)
|
|
|
|
return open(i->mountpoint, O_RDONLY);
|
2012-05-04 13:38:00 +04:00
|
|
|
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
2012-05-04 13:38:00 +04:00
|
|
|
|
2013-04-06 02:15:41 +04:00
|
|
|
int collect_mount_info(pid_t pid)
|
2012-05-04 13:38:00 +04:00
|
|
|
{
|
2012-07-15 08:43:37 +04:00
|
|
|
pr_info("Collecting mountinfo\n");
|
|
|
|
|
2013-04-06 02:15:41 +04:00
|
|
|
mntinfo = parse_mountinfo(pid);
|
2012-05-13 00:07:11 +04:00
|
|
|
if (!mntinfo) {
|
2012-05-04 13:38:00 +04:00
|
|
|
pr_err("Parsing mountinfo %d failed\n", getpid());
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2012-05-12 03:30:10 +04:00
|
|
|
|
2013-01-14 20:47:53 +04:00
|
|
|
static struct mount_info *__lookup_mnt_id(struct mount_info *list, int id)
|
2012-06-27 20:57:34 +04:00
|
|
|
{
|
|
|
|
struct mount_info *m;
|
|
|
|
|
|
|
|
for (m = list; m != NULL; m = m->next)
|
|
|
|
if (m->mnt_id == id)
|
|
|
|
return m;
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-01-14 20:47:53 +04:00
|
|
|
struct mount_info *lookup_mnt_id(unsigned int id)
|
|
|
|
{
|
|
|
|
return __lookup_mnt_id(mntinfo, id);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct mount_info *lookup_mnt_sdev(unsigned int s_dev)
|
|
|
|
{
|
|
|
|
struct mount_info *m;
|
|
|
|
|
|
|
|
for (m = mntinfo; m != NULL; m = m->next)
|
|
|
|
if (m->s_dev == s_dev)
|
|
|
|
return m;
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-06-27 20:57:34 +04:00
|
|
|
static struct mount_info *mnt_build_ids_tree(struct mount_info *list)
|
|
|
|
{
|
|
|
|
struct mount_info *m, *root = NULL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Just resolve the mnt_id:parent_mnt_id relations
|
|
|
|
*/
|
|
|
|
|
|
|
|
pr_debug("\tBuilding plain mount tree\n");
|
|
|
|
for (m = list; m != NULL; m = m->next) {
|
|
|
|
struct mount_info *p;
|
|
|
|
|
|
|
|
pr_debug("\t\tWorking on %d->%d\n", m->mnt_id, m->parent_mnt_id);
|
2013-01-14 20:47:53 +04:00
|
|
|
p = __lookup_mnt_id(list, m->parent_mnt_id);
|
2012-06-27 20:57:34 +04:00
|
|
|
if (!p) {
|
|
|
|
/* This should be / */
|
|
|
|
if (root == NULL && !strcmp(m->mountpoint, "/")) {
|
|
|
|
root = m;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
pr_err("Mountpoint %d w/o parent %d found @%s (root %s)\n",
|
|
|
|
m->mnt_id, m->parent_mnt_id, m->mountpoint,
|
|
|
|
root ? "found" : "not found");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
m->parent = p;
|
|
|
|
list_add_tail(&m->siblings, &p->children);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!root) {
|
|
|
|
pr_err("No root found for tree\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return root;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mnt_depth(struct mount_info *m)
|
|
|
|
{
|
|
|
|
int depth = 0;
|
|
|
|
char *c;
|
|
|
|
|
|
|
|
for (c = m->mountpoint; *c != '\0'; c++)
|
|
|
|
if (*c == '/')
|
|
|
|
depth++;
|
|
|
|
|
|
|
|
return depth;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void mnt_resort_siblings(struct mount_info *tree)
|
|
|
|
{
|
|
|
|
struct mount_info *m, *p;
|
|
|
|
LIST_HEAD(list);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Put siblings of each node in an order they can be (u)mounted
|
|
|
|
* I.e. if we have mounts on foo/bar/, foo/bar/foobar/ and foo/
|
|
|
|
* we should put them in the foo/bar/foobar/, foo/bar/, foo/ order.
|
|
|
|
* Otherwise we will not be able to (u)mount them in a sequence.
|
|
|
|
*
|
|
|
|
* Funny, but all we need for this is to sort them in the descending
|
|
|
|
* order of the amount of /-s in a path =)
|
|
|
|
*
|
|
|
|
* Use stupid insertion sort here, we're not expecting mount trees
|
|
|
|
* to contain hundreds (or more) elements.
|
|
|
|
*/
|
|
|
|
|
|
|
|
pr_info("\tResorting siblings on %d\n", tree->mnt_id);
|
|
|
|
while (!list_empty(&tree->children)) {
|
|
|
|
int depth;
|
|
|
|
|
|
|
|
m = list_first_entry(&tree->children, struct mount_info, siblings);
|
|
|
|
list_del(&m->siblings);
|
|
|
|
|
|
|
|
depth = mnt_depth(m);
|
|
|
|
list_for_each_entry(p, &list, siblings)
|
|
|
|
if (mnt_depth(p) <= depth)
|
|
|
|
break;
|
|
|
|
|
|
|
|
list_add(&m->siblings, &p->siblings);
|
|
|
|
mnt_resort_siblings(m);
|
|
|
|
}
|
|
|
|
|
|
|
|
list_splice(&list, &tree->children);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void mnt_tree_show(struct mount_info *tree, int off)
|
|
|
|
{
|
|
|
|
struct mount_info *m;
|
|
|
|
|
|
|
|
pr_info("%*s[%s](%d->%d)\n", off, "",
|
|
|
|
tree->mountpoint, tree->mnt_id, tree->parent_mnt_id);
|
|
|
|
|
|
|
|
list_for_each_entry(m, &tree->children, siblings)
|
|
|
|
mnt_tree_show(m, off + 1);
|
|
|
|
|
|
|
|
pr_info("%*s<--\n", off, "");
|
|
|
|
}
|
|
|
|
|
2013-08-29 18:55:38 +04:00
|
|
|
static int collect_shared(struct mount_info *info)
|
|
|
|
{
|
|
|
|
struct mount_info *m, *t;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we have a shared mounts, both master
|
|
|
|
* slave targets are to be present in mount
|
|
|
|
* list, otherwise we can't be sure if we can
|
|
|
|
* recreate the scheme later on restore.
|
|
|
|
*/
|
|
|
|
for (m = info; m; m = m->next) {
|
|
|
|
bool need_share, need_master;
|
|
|
|
|
|
|
|
need_share = m->shared_id && list_empty(&m->mnt_share);
|
|
|
|
need_master = m->master_id;
|
|
|
|
|
|
|
|
for (t = info; t && (need_share || need_master); t = t->next) {
|
|
|
|
if (t == m)
|
|
|
|
continue;
|
|
|
|
if (need_master && t->shared_id == m->master_id) {
|
|
|
|
pr_debug("The mount %d is slave for %d\n", m->mnt_id, t->mnt_id);
|
|
|
|
list_add(&m->mnt_slave, &t->mnt_slave_list);
|
|
|
|
m->mnt_master = t;
|
|
|
|
need_master = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Collect all mounts from this group */
|
|
|
|
if (need_share && t->shared_id == m->shared_id) {
|
|
|
|
pr_debug("Mount %d is shared with %d group %d\n",
|
|
|
|
m->mnt_id, t->mnt_id, m->shared_id);
|
|
|
|
list_add(&t->mnt_share, &m->mnt_share);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (need_master) {
|
|
|
|
pr_err("Mount %d (master_id: %d shared_id: %d) "
|
|
|
|
"has unreachable sharing\n", m->mnt_id,
|
|
|
|
m->master_id, m->shared_id);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-06-27 20:57:34 +04:00
|
|
|
static struct mount_info *mnt_build_tree(struct mount_info *list)
|
|
|
|
{
|
|
|
|
struct mount_info *tree;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Organize them in a sequence in which they can be mounted/umounted.
|
|
|
|
*/
|
|
|
|
|
|
|
|
pr_info("Building mountpoints tree\n");
|
|
|
|
tree = mnt_build_ids_tree(list);
|
|
|
|
if (!tree)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
mnt_resort_siblings(tree);
|
2013-08-29 18:55:38 +04:00
|
|
|
if (collect_shared(list))
|
|
|
|
return NULL;
|
2012-06-27 20:57:34 +04:00
|
|
|
pr_info("Done:\n");
|
|
|
|
mnt_tree_show(tree, 0);
|
|
|
|
return tree;
|
|
|
|
}
|
|
|
|
|
2013-08-01 17:17:03 +04:00
|
|
|
/*
|
|
|
|
* mnt_fd is a file descriptor on the mountpoint, which is closed in an error case.
|
|
|
|
* If mnt_fd is -1, the mountpoint will be opened by this function.
|
|
|
|
*/
|
|
|
|
static DIR *__open_mountpoint(struct mount_info *pm, int mnt_fd)
|
2012-08-09 19:45:18 +04:00
|
|
|
{
|
|
|
|
char path[PATH_MAX + 1];
|
|
|
|
struct stat st;
|
|
|
|
DIR *fdir;
|
2013-08-01 17:17:03 +04:00
|
|
|
int ret;
|
2012-08-09 19:45:18 +04:00
|
|
|
|
2013-08-01 17:17:03 +04:00
|
|
|
if (mnt_fd == -1) {
|
|
|
|
snprintf(path, sizeof(path), ".%s", pm->mountpoint);
|
|
|
|
mnt_fd = openat(mntns_root, path, O_RDONLY);
|
|
|
|
if (mnt_fd < 0) {
|
|
|
|
pr_perror("Can't open %s", pm->mountpoint);
|
|
|
|
return NULL;
|
|
|
|
}
|
2012-08-09 19:45:18 +04:00
|
|
|
}
|
|
|
|
|
2013-08-01 17:17:03 +04:00
|
|
|
ret = fstat(mnt_fd, &st);
|
2012-08-09 19:45:18 +04:00
|
|
|
if (ret < 0) {
|
|
|
|
pr_perror("fstat(%s) failed", path);
|
2013-08-01 17:17:03 +04:00
|
|
|
goto err;
|
2012-08-09 19:45:18 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (st.st_dev != pm->s_dev) {
|
2012-08-11 21:55:48 +04:00
|
|
|
pr_err("The file system %#x %s %s is inaccessible\n",
|
2012-08-09 19:45:18 +04:00
|
|
|
pm->s_dev, pm->fstype->name, pm->mountpoint);
|
2013-08-01 17:17:03 +04:00
|
|
|
goto err;
|
2012-08-09 19:45:18 +04:00
|
|
|
}
|
|
|
|
|
2013-08-01 17:17:03 +04:00
|
|
|
fdir = fdopendir(mnt_fd);
|
2012-08-09 19:45:18 +04:00
|
|
|
if (fdir == NULL) {
|
|
|
|
pr_perror("Can't open %s", pm->mountpoint);
|
2013-08-01 17:17:03 +04:00
|
|
|
goto err;
|
2012-08-09 19:45:18 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return fdir;
|
2013-08-01 17:17:03 +04:00
|
|
|
err:
|
|
|
|
close(mnt_fd);
|
|
|
|
return NULL;
|
2012-08-09 19:45:18 +04:00
|
|
|
}
|
|
|
|
|
2012-08-09 19:47:20 +04:00
|
|
|
static int close_mountpoint(DIR *dfd)
|
2012-08-09 19:45:18 +04:00
|
|
|
{
|
|
|
|
if (closedir(dfd)) {
|
|
|
|
pr_perror("Unable to close directory");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-08-01 17:17:03 +04:00
|
|
|
static DIR *open_mountpoint(struct mount_info *pm)
|
|
|
|
{
|
|
|
|
int fd = -1, ns_old = -1;
|
|
|
|
char buf[PATH_MAX];
|
|
|
|
char mnt_path[] = "/tmp/cr-tmpfs.XXXXXX";
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If a mount doesn't have children, we can open a mount point,
|
|
|
|
* otherwise we need to create a "private" copy.
|
|
|
|
*/
|
|
|
|
if (list_empty(&pm->children))
|
|
|
|
return __open_mountpoint(pm, -1);
|
|
|
|
|
|
|
|
pr_info("Something is mounted on top of %s\n", pm->mountpoint);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* To create a "private" copy, the target mount is bind-mounted
|
|
|
|
* in a temporary place w/o MS_REC (non-recursively).
|
|
|
|
* A mount point can't be bind-mounted in criu's namespace, it will be
|
|
|
|
* mounted in a target namespace. The sequence of actions is
|
|
|
|
* mkdtemp, setns(tgt), mount, open, detach, setns(old).
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (switch_ns(root_item->pid.real, &mnt_ns_desc, &ns_old) < 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (mkdtemp(mnt_path) == NULL) {
|
|
|
|
pr_perror("Can't create a temporary directory");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
snprintf(buf, sizeof(buf), "/proc/self/root/%s", pm->mountpoint);
|
|
|
|
if (mount(buf, mnt_path, NULL, MS_BIND, NULL)) {
|
|
|
|
pr_perror("Can't bind-mount %d:%s to %s",
|
|
|
|
pm->mnt_id, pm->mountpoint, mnt_path);
|
|
|
|
rmdir(mnt_path);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2013-07-29 13:12:00 +04:00
|
|
|
fd = open_detach_mount(mnt_path);
|
2013-08-01 17:17:03 +04:00
|
|
|
if (fd < 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (restore_ns(ns_old, &mnt_ns_desc))
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
return __open_mountpoint(pm, fd);;
|
|
|
|
out:
|
|
|
|
if (ns_old >= 0)
|
|
|
|
restore_ns(ns_old, &mnt_ns_desc);
|
|
|
|
close_safe(&fd);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-08-09 19:51:22 +04:00
|
|
|
static int tmpfs_dump(struct mount_info *pm)
|
|
|
|
{
|
2012-09-28 14:09:58 +04:00
|
|
|
int ret = -1;
|
|
|
|
char tmpfs_path[PATH_MAX];
|
|
|
|
int fd, fd_img = -1;
|
|
|
|
DIR *fdir = NULL;
|
2012-08-09 19:51:22 +04:00
|
|
|
|
2012-09-28 14:09:58 +04:00
|
|
|
fdir = open_mountpoint(pm);
|
|
|
|
if (fdir == NULL)
|
2012-08-09 19:51:22 +04:00
|
|
|
return -1;
|
|
|
|
|
2012-09-28 14:09:58 +04:00
|
|
|
fd = dirfd(fdir);
|
|
|
|
if (fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) & ~FD_CLOEXEC) == -1) {
|
|
|
|
pr_perror("Can not drop FD_CLOEXEC");
|
|
|
|
goto out;
|
|
|
|
}
|
2012-08-09 19:51:22 +04:00
|
|
|
|
2012-09-28 14:09:58 +04:00
|
|
|
fd_img = open_image(CR_FD_TMPFS, O_DUMP, pm->mnt_id);
|
|
|
|
if (fd_img < 0)
|
|
|
|
goto out;
|
2012-08-11 21:48:17 +04:00
|
|
|
|
2012-09-28 14:09:58 +04:00
|
|
|
snprintf(tmpfs_path, sizeof(tmpfs_path),
|
|
|
|
"/proc/self/fd/%d", fd);
|
2012-08-09 19:51:22 +04:00
|
|
|
|
2012-09-28 14:09:58 +04:00
|
|
|
ret = cr_system(-1, fd_img, -1, "tar", (char *[])
|
|
|
|
{ "tar", "--create",
|
2012-09-12 18:34:19 +04:00
|
|
|
"--gzip",
|
2013-07-09 15:05:49 +04:00
|
|
|
"--one-file-system",
|
2012-09-12 18:34:19 +04:00
|
|
|
"--check-links",
|
|
|
|
"--preserve-permissions",
|
2012-08-09 19:51:22 +04:00
|
|
|
"--sparse",
|
|
|
|
"--numeric-owner",
|
2012-09-28 14:09:58 +04:00
|
|
|
"--directory", tmpfs_path, ".", NULL });
|
2012-08-09 19:51:22 +04:00
|
|
|
|
2012-09-28 14:09:58 +04:00
|
|
|
if (ret)
|
2012-08-09 19:51:22 +04:00
|
|
|
pr_err("Can't dump tmpfs content\n");
|
|
|
|
|
2012-09-28 14:09:58 +04:00
|
|
|
out:
|
|
|
|
close_safe(&fd_img);
|
|
|
|
close_mountpoint(fdir);
|
|
|
|
return ret;
|
2012-08-09 19:51:22 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static int tmpfs_restore(struct mount_info *pm)
|
|
|
|
{
|
2012-09-28 14:09:58 +04:00
|
|
|
int ret;
|
|
|
|
int fd_img;
|
2012-08-09 19:51:22 +04:00
|
|
|
|
2013-04-09 11:13:51 +04:00
|
|
|
fd_img = open_image(CR_FD_TMPFS, O_RSTR, pm->mnt_id);
|
2012-09-28 14:09:58 +04:00
|
|
|
if (fd_img < 0)
|
2012-09-25 15:59:13 +04:00
|
|
|
return -1;
|
2012-08-09 19:51:22 +04:00
|
|
|
|
2012-09-28 14:09:58 +04:00
|
|
|
ret = cr_system(fd_img, -1, -1, "tar",
|
|
|
|
(char *[]) {"tar", "--extract", "--gzip",
|
|
|
|
"--directory", pm->mountpoint, NULL});
|
|
|
|
close(fd_img);
|
2012-08-09 19:51:22 +04:00
|
|
|
|
2012-09-28 14:09:58 +04:00
|
|
|
if (ret) {
|
2012-08-09 19:51:22 +04:00
|
|
|
pr_err("Can't restore tmpfs content\n");
|
2012-09-25 15:59:13 +04:00
|
|
|
return -1;
|
2012-08-09 19:51:22 +04:00
|
|
|
}
|
|
|
|
|
2012-09-28 14:09:58 +04:00
|
|
|
return 0;
|
2012-08-09 19:51:22 +04:00
|
|
|
}
|
|
|
|
|
2012-08-09 19:47:20 +04:00
|
|
|
static int binfmt_misc_dump(struct mount_info *pm)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
struct dirent *de;
|
|
|
|
DIR *fdir = NULL;
|
|
|
|
|
|
|
|
fdir = open_mountpoint(pm);
|
|
|
|
if (fdir == NULL)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
while ((de = readdir(fdir))) {
|
2012-11-29 21:12:51 +03:00
|
|
|
if (dir_dots(de))
|
2012-08-09 19:47:20 +04:00
|
|
|
continue;
|
|
|
|
if (!strcmp(de->d_name, "register"))
|
|
|
|
continue;
|
|
|
|
if (!strcmp(de->d_name, "status"))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
pr_err("binfmt_misc isn't empty: %s\n", de->d_name);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
out:
|
|
|
|
close_mountpoint(fdir);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-08-09 16:27:30 +04:00
|
|
|
static struct fstype fstypes[] = {
|
2013-03-18 22:32:57 +04:00
|
|
|
[FSTYPE__UNSUPPORTED] = { "unsupported" },
|
|
|
|
[FSTYPE__PROC] = { "proc" },
|
|
|
|
[FSTYPE__SYSFS] = { "sysfs" },
|
|
|
|
[FSTYPE__DEVTMPFS] = { "devtmpfs" },
|
|
|
|
[FSTYPE__BINFMT_MISC] = { "binfmt_misc", binfmt_misc_dump },
|
|
|
|
[FSTYPE__TMPFS] = { "tmpfs", tmpfs_dump, tmpfs_restore },
|
|
|
|
[FSTYPE__DEVPTS] = { "devpts" },
|
2013-04-03 10:31:35 +04:00
|
|
|
[FSTYPE__SIMFS] = { "simfs" },
|
2012-06-27 20:57:32 +04:00
|
|
|
};
|
|
|
|
|
2012-08-09 16:27:30 +04:00
|
|
|
struct fstype *find_fstype_by_name(char *fst)
|
2012-06-27 20:57:32 +04:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This fn is required for two things.
|
|
|
|
* 1st -- to check supported filesystems (as just mounting
|
|
|
|
* anything is wrong, almost every fs has its own features)
|
|
|
|
* 2nd -- save some space in the image (since we scan all
|
|
|
|
* names anyway)
|
|
|
|
*/
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(fstypes); i++)
|
2012-08-09 16:27:30 +04:00
|
|
|
if (!strcmp(fstypes[i].name, fst))
|
|
|
|
return fstypes + i;
|
2012-06-27 20:57:32 +04:00
|
|
|
|
2012-08-09 16:27:30 +04:00
|
|
|
return &fstypes[0];
|
2012-06-27 20:57:32 +04:00
|
|
|
}
|
|
|
|
|
2012-08-09 16:27:30 +04:00
|
|
|
static u32 encode_fstype(struct fstype *fst)
|
2012-06-27 20:57:32 +04:00
|
|
|
{
|
2012-08-09 16:27:30 +04:00
|
|
|
return fst - fstypes;
|
|
|
|
}
|
2012-06-27 20:57:32 +04:00
|
|
|
|
2012-08-09 16:27:30 +04:00
|
|
|
static struct fstype *decode_fstype(u32 fst)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (fst >= ARRAY_SIZE(fstypes))
|
|
|
|
return &fstypes[0];
|
2012-06-27 20:57:32 +04:00
|
|
|
|
2012-08-09 16:27:30 +04:00
|
|
|
return &fstypes[fst];
|
2012-06-27 20:57:32 +04:00
|
|
|
}
|
|
|
|
|
2012-06-27 20:57:31 +04:00
|
|
|
static int dump_one_mountpoint(struct mount_info *pm, int fd)
|
|
|
|
{
|
2012-07-17 14:23:37 +04:00
|
|
|
MntEntry me = MNT_ENTRY__INIT;
|
2012-06-27 20:57:31 +04:00
|
|
|
|
|
|
|
pr_info("\t%d: %x:%s @ %s\n", pm->mnt_id, pm->s_dev,
|
|
|
|
pm->root, pm->mountpoint);
|
|
|
|
|
2012-07-17 14:23:37 +04:00
|
|
|
me.fstype = encode_fstype(pm->fstype);
|
2012-08-09 16:27:30 +04:00
|
|
|
if (fstypes[me.fstype].dump && fstypes[me.fstype].dump(pm))
|
|
|
|
return -1;
|
|
|
|
|
2012-07-17 14:23:37 +04:00
|
|
|
me.mnt_id = pm->mnt_id;
|
|
|
|
me.root_dev = pm->s_dev;
|
|
|
|
me.parent_mnt_id = pm->parent_mnt_id;
|
|
|
|
me.flags = pm->flags;
|
|
|
|
me.root = pm->root;
|
|
|
|
me.mountpoint = pm->mountpoint;
|
|
|
|
me.source = pm->source;
|
|
|
|
me.options = pm->options;
|
|
|
|
|
2012-06-27 20:57:32 +04:00
|
|
|
if (!me.fstype && !is_root_mount(pm)) {
|
2012-08-11 21:55:48 +04:00
|
|
|
pr_err("FS mnt %s dev %#x root %s unsupported\n",
|
2012-08-09 16:27:30 +04:00
|
|
|
pm->mountpoint, pm->s_dev, pm->root);
|
2012-06-27 20:57:32 +04:00
|
|
|
return -1;
|
|
|
|
}
|
2012-06-27 20:57:31 +04:00
|
|
|
|
2013-08-23 21:47:31 +04:00
|
|
|
if (pb_write_one(fd, &me, PB_MNT))
|
2012-06-27 20:57:31 +04:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-05-12 03:30:10 +04:00
|
|
|
int dump_mnt_ns(int ns_pid, struct cr_fdset *fdset)
|
|
|
|
{
|
2012-06-27 20:57:31 +04:00
|
|
|
struct mount_info *pm;
|
|
|
|
int img_fd;
|
|
|
|
|
|
|
|
pm = parse_mountinfo(ns_pid);
|
|
|
|
if (!pm) {
|
|
|
|
pr_err("Can't parse %d's mountinfo\n", ns_pid);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2013-08-01 16:44:38 +04:00
|
|
|
if (mnt_build_tree(pm) == NULL)
|
|
|
|
return -1;
|
|
|
|
|
2012-06-27 20:57:31 +04:00
|
|
|
pr_info("Dumping mountpoints\n");
|
|
|
|
|
2013-08-24 04:00:32 +04:00
|
|
|
img_fd = fdset_fd(fdset, CR_FD_MNTS);
|
2012-06-27 20:57:31 +04:00
|
|
|
do {
|
|
|
|
struct mount_info *n = pm->next;
|
|
|
|
|
|
|
|
if (dump_one_mountpoint(pm, img_fd))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
xfree(pm);
|
|
|
|
pm = n;
|
|
|
|
} while (pm);
|
|
|
|
|
|
|
|
return 0;
|
2012-05-12 03:30:10 +04:00
|
|
|
}
|
|
|
|
|
2013-07-30 20:25:28 +04:00
|
|
|
#define MNT_TREE_WALK(_r, _el, _fn_f, _fn_r) do { \
|
|
|
|
struct mount_info *_mi = _r; \
|
|
|
|
\
|
2012-06-27 20:57:37 +04:00
|
|
|
while (1) { \
|
|
|
|
if (_fn_f(_mi)) \
|
|
|
|
return -1; \
|
|
|
|
if (!list_empty(&_mi->children)) { \
|
|
|
|
_mi = list_entry(_mi->children._el, \
|
|
|
|
struct mount_info, siblings); \
|
|
|
|
continue; \
|
|
|
|
} \
|
|
|
|
up: \
|
|
|
|
if (_fn_r(_mi)) \
|
|
|
|
return -1; \
|
2013-07-30 20:25:28 +04:00
|
|
|
if (_mi == _r) \
|
|
|
|
break; \
|
2012-06-27 20:57:37 +04:00
|
|
|
if (_mi->siblings._el == &_mi->parent->children) { \
|
|
|
|
_mi = _mi->parent; \
|
|
|
|
goto up; \
|
|
|
|
} \
|
|
|
|
_mi = list_entry(_mi->siblings._el, \
|
|
|
|
struct mount_info, siblings); \
|
|
|
|
} \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
#define MNT_WALK_NONE 0 &&
|
|
|
|
|
|
|
|
|
2012-06-27 20:57:36 +04:00
|
|
|
static int mnt_tree_for_each(struct mount_info *m,
|
|
|
|
int (*fn)(struct mount_info *))
|
|
|
|
{
|
2012-06-27 20:57:37 +04:00
|
|
|
MNT_TREE_WALK(m, next, fn, MNT_WALK_NONE);
|
2013-07-30 20:25:28 +04:00
|
|
|
|
|
|
|
return 0;
|
2012-06-27 20:57:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static int mnt_tree_for_each_reverse(struct mount_info *m,
|
|
|
|
int (*fn)(struct mount_info *))
|
|
|
|
{
|
2012-06-27 20:57:37 +04:00
|
|
|
MNT_TREE_WALK(m, prev, MNT_WALK_NONE, fn);
|
2013-07-30 20:25:28 +04:00
|
|
|
|
|
|
|
return 0;
|
2012-06-27 20:57:36 +04:00
|
|
|
}
|
|
|
|
|
2012-06-27 20:57:39 +04:00
|
|
|
static char *resolve_source(struct mount_info *mi)
|
|
|
|
{
|
|
|
|
if (kdev_major(mi->s_dev) == 0)
|
|
|
|
/*
|
|
|
|
* Anonymous block device. Kernel creates them for
|
|
|
|
* diskless mounts.
|
|
|
|
*/
|
|
|
|
return mi->source;
|
|
|
|
|
|
|
|
pr_err("No device for %s mount\n", mi->mountpoint);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int do_new_mount(struct mount_info *mi)
|
|
|
|
{
|
|
|
|
char *src;
|
2012-08-09 16:27:30 +04:00
|
|
|
struct fstype *tp = mi->fstype;
|
2012-06-27 20:57:39 +04:00
|
|
|
|
|
|
|
src = resolve_source(mi);
|
|
|
|
if (!src)
|
|
|
|
return -1;
|
|
|
|
|
2012-08-09 16:27:30 +04:00
|
|
|
if (mount(src, mi->mountpoint, tp->name,
|
2012-06-27 20:57:39 +04:00
|
|
|
mi->flags, mi->options) < 0) {
|
|
|
|
pr_perror("Can't mount at %s", mi->mountpoint);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-08-09 16:27:30 +04:00
|
|
|
if (tp->restore && tp->restore(mi))
|
|
|
|
return -1;
|
|
|
|
|
2012-06-27 20:57:39 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int do_bind_mount(struct mount_info *mi)
|
|
|
|
{
|
|
|
|
pr_err("No bind mounts at %s\n", mi->mountpoint);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-06-27 20:57:36 +04:00
|
|
|
static int do_mount_one(struct mount_info *mi)
|
|
|
|
{
|
|
|
|
if (!mi->parent)
|
|
|
|
return 0;
|
|
|
|
|
2012-08-09 16:27:30 +04:00
|
|
|
pr_debug("\tMounting %s @%s\n", mi->fstype->name, mi->mountpoint);
|
2012-06-27 20:57:39 +04:00
|
|
|
|
|
|
|
if (fsroot_mounted(mi))
|
|
|
|
return do_new_mount(mi);
|
|
|
|
else
|
|
|
|
return do_bind_mount(mi);
|
2012-06-27 20:57:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static int do_umount_one(struct mount_info *mi)
|
|
|
|
{
|
|
|
|
if (!mi->parent)
|
|
|
|
return 0;
|
|
|
|
|
2013-07-05 12:19:54 +04:00
|
|
|
if (mount("none", mi->parent->mountpoint, "none", MS_REC|MS_PRIVATE, NULL)) {
|
|
|
|
pr_perror("Can't mark %s as private", mi->parent->mountpoint);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-06-27 20:57:38 +04:00
|
|
|
if (umount(mi->mountpoint)) {
|
|
|
|
pr_perror("Can't umount at %s", mi->mountpoint);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
pr_info("Umounted at %s\n", mi->mountpoint);
|
2012-06-27 20:57:36 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int clean_mnt_ns(void)
|
|
|
|
{
|
2012-08-02 09:54:22 +04:00
|
|
|
int ret;
|
2012-06-27 20:57:36 +04:00
|
|
|
struct mount_info *pm;
|
|
|
|
|
|
|
|
pr_info("Cleaning mount namespace\n");
|
|
|
|
|
2012-07-15 08:43:37 +04:00
|
|
|
/*
|
|
|
|
* Mountinfos were collected at prepare stage
|
|
|
|
*/
|
2012-06-27 20:57:36 +04:00
|
|
|
|
2013-03-26 14:16:51 +04:00
|
|
|
if (mount("none", "/", "none", MS_REC|MS_PRIVATE, NULL)) {
|
|
|
|
pr_perror("Can't remount root with MS_PRIVATE");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-07-15 08:43:37 +04:00
|
|
|
pm = mnt_build_tree(mntinfo);
|
2012-06-27 20:57:36 +04:00
|
|
|
if (!pm)
|
|
|
|
return -1;
|
|
|
|
|
2012-08-02 09:54:22 +04:00
|
|
|
ret = mnt_tree_for_each_reverse(pm, do_umount_one);
|
|
|
|
|
|
|
|
while (mntinfo) {
|
|
|
|
pm = mntinfo->next;
|
|
|
|
xfree(mntinfo);
|
|
|
|
mntinfo = pm;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
2012-06-27 20:57:36 +04:00
|
|
|
}
|
|
|
|
|
2012-09-28 14:13:18 +04:00
|
|
|
static int cr_pivot_root()
|
2012-06-27 20:57:36 +04:00
|
|
|
{
|
2012-12-06 16:20:19 +03:00
|
|
|
char put_root[] = "crtools-put-root.XXXXXX";
|
2012-08-02 16:07:43 +04:00
|
|
|
|
2013-03-26 19:23:17 +04:00
|
|
|
pr_info("Move the root to %s\n", opts.root);
|
2012-09-28 14:13:18 +04:00
|
|
|
|
2012-09-28 14:13:20 +04:00
|
|
|
if (chdir(opts.root)) {
|
|
|
|
pr_perror("chdir(%s) failed", opts.root);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (mkdtemp(put_root) == NULL) {
|
2013-04-12 13:00:05 -07:00
|
|
|
pr_perror("Can't create a temporary directory");
|
2012-09-28 14:13:20 +04:00
|
|
|
return -1;
|
|
|
|
}
|
2013-03-26 14:16:50 +04:00
|
|
|
|
|
|
|
if (mount("none", "/", "none", MS_REC|MS_PRIVATE, NULL)) {
|
|
|
|
pr_perror("Can't remount root with MS_PRIVATE");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-09-28 14:13:20 +04:00
|
|
|
if (pivot_root(".", put_root)) {
|
|
|
|
pr_perror("pivot_root(., %s) failed", put_root);
|
|
|
|
if (rmdir(put_root))
|
2012-08-02 16:07:43 +04:00
|
|
|
pr_perror("Can't remove the directory %s", put_root);
|
2012-09-28 14:13:20 +04:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (umount2(put_root, MNT_DETACH)) {
|
|
|
|
pr_perror("Can't umount %s", put_root);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (rmdir(put_root)) {
|
|
|
|
pr_perror("Can't remove the directory %s", put_root);
|
|
|
|
return -1;
|
|
|
|
}
|
2012-09-28 14:13:18 +04:00
|
|
|
|
2012-09-28 14:13:20 +04:00
|
|
|
return 0;
|
2012-09-28 14:13:18 +04:00
|
|
|
}
|
|
|
|
|
2013-04-10 01:26:59 +04:00
|
|
|
struct mount_info *mnt_entry_alloc()
|
|
|
|
{
|
|
|
|
struct mount_info *new;
|
|
|
|
|
2013-06-04 10:44:25 +04:00
|
|
|
new = xzalloc(sizeof(struct mount_info));
|
|
|
|
if (new) {
|
|
|
|
INIT_LIST_HEAD(&new->children);
|
|
|
|
INIT_LIST_HEAD(&new->siblings);
|
2013-08-29 18:55:38 +04:00
|
|
|
INIT_LIST_HEAD(&new->mnt_slave_list);
|
|
|
|
INIT_LIST_HEAD(&new->mnt_share);
|
|
|
|
new->mnt_master = NULL;
|
2013-06-04 10:44:25 +04:00
|
|
|
}
|
2013-04-10 01:26:59 +04:00
|
|
|
return new;
|
|
|
|
}
|
|
|
|
|
|
|
|
void mnt_entry_free(struct mount_info *mi)
|
|
|
|
{
|
|
|
|
if (mi == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
xfree(mi->root);
|
|
|
|
xfree(mi->mountpoint);
|
|
|
|
xfree(mi->source);
|
|
|
|
xfree(mi->options);
|
|
|
|
xfree(mi);
|
|
|
|
}
|
|
|
|
|
2012-09-28 14:13:18 +04:00
|
|
|
static int populate_mnt_ns(int ns_pid)
|
|
|
|
{
|
|
|
|
MntEntry *me = NULL;
|
|
|
|
int img, ret;
|
|
|
|
struct mount_info *pms = NULL;
|
|
|
|
|
|
|
|
pr_info("Populating mount namespace\n");
|
2012-08-02 16:07:43 +04:00
|
|
|
|
2013-08-24 04:00:32 +04:00
|
|
|
img = open_image(CR_FD_MNTS, O_RSTR, ns_pid);
|
2012-06-27 20:57:36 +04:00
|
|
|
if (img < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
pr_debug("Reading mountpoint images\n");
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
struct mount_info *pm;
|
|
|
|
|
2013-08-23 21:47:31 +04:00
|
|
|
ret = pb_read_one_eof(img, &me, PB_MNT);
|
2012-06-27 20:57:36 +04:00
|
|
|
if (ret <= 0)
|
|
|
|
break;
|
|
|
|
|
2013-04-10 01:26:59 +04:00
|
|
|
pm = mnt_entry_alloc();
|
2012-06-27 20:57:36 +04:00
|
|
|
if (!pm)
|
2013-04-03 21:31:04 +04:00
|
|
|
goto err;
|
2012-06-27 20:57:36 +04:00
|
|
|
|
2013-04-10 01:26:59 +04:00
|
|
|
pm->next = pms;
|
|
|
|
pms = pm;
|
2012-06-27 20:57:36 +04:00
|
|
|
|
2012-07-17 14:23:37 +04:00
|
|
|
pm->mnt_id = me->mnt_id;
|
|
|
|
pm->parent_mnt_id = me->parent_mnt_id;
|
|
|
|
pm->s_dev = me->root_dev;
|
|
|
|
pm->flags = me->flags;
|
|
|
|
|
|
|
|
/* FIXME: abort unsupported early */
|
|
|
|
pm->fstype = decode_fstype(me->fstype);
|
2012-06-27 20:57:36 +04:00
|
|
|
|
|
|
|
pr_debug("\t\tGetting root for %d\n", pm->mnt_id);
|
2012-07-17 14:23:37 +04:00
|
|
|
pm->root = xstrdup(me->root);
|
|
|
|
if (!pm->root)
|
2012-10-24 16:51:50 +04:00
|
|
|
goto err;
|
2012-06-27 20:57:36 +04:00
|
|
|
|
|
|
|
pr_debug("\t\tGetting mpt for %d\n", pm->mnt_id);
|
2012-07-17 14:23:37 +04:00
|
|
|
pm->mountpoint = xstrdup(me->mountpoint);
|
|
|
|
if (!pm->mountpoint)
|
2012-10-24 16:51:50 +04:00
|
|
|
goto err;
|
2012-06-27 20:57:36 +04:00
|
|
|
|
|
|
|
pr_debug("\t\tGetting source for %d\n", pm->mnt_id);
|
2012-07-17 14:23:37 +04:00
|
|
|
pm->source = xstrdup(me->source);
|
|
|
|
if (!pm->source)
|
2012-10-24 16:51:50 +04:00
|
|
|
goto err;
|
2012-06-27 20:57:36 +04:00
|
|
|
|
|
|
|
pr_debug("\t\tGetting opts for %d\n", pm->mnt_id);
|
2012-07-17 14:23:37 +04:00
|
|
|
pm->options = xstrdup(me->options);
|
|
|
|
if (!pm->options)
|
2012-10-24 16:51:50 +04:00
|
|
|
goto err;
|
2012-06-27 20:57:36 +04:00
|
|
|
|
|
|
|
pr_debug("\tRead %d mp @ %s\n", pm->mnt_id, pm->mountpoint);
|
|
|
|
}
|
|
|
|
|
2012-07-17 14:23:37 +04:00
|
|
|
if (me)
|
|
|
|
mnt_entry__free_unpacked(me, NULL);
|
|
|
|
|
2012-06-27 20:57:36 +04:00
|
|
|
close(img);
|
2012-08-02 09:54:22 +04:00
|
|
|
mntinfo = pms;
|
2012-06-27 20:57:36 +04:00
|
|
|
|
|
|
|
pms = mnt_build_tree(pms);
|
|
|
|
if (!pms)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return mnt_tree_for_each(pms, do_mount_one);
|
2012-10-24 16:51:50 +04:00
|
|
|
err:
|
2013-04-03 21:31:04 +04:00
|
|
|
while (pms) {
|
|
|
|
struct mount_info *pm = pms;
|
|
|
|
pms = pm->next;
|
2013-04-10 01:26:59 +04:00
|
|
|
mnt_entry_free(pm);
|
2013-04-03 21:31:04 +04:00
|
|
|
}
|
2012-10-24 16:51:50 +04:00
|
|
|
close_safe(&img);
|
|
|
|
return -1;
|
2012-06-27 20:57:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
int prepare_mnt_ns(int ns_pid)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
pr_info("Restoring mount namespace\n");
|
|
|
|
|
2013-01-11 18:16:18 +04:00
|
|
|
close_proc();
|
|
|
|
|
2012-06-27 20:57:36 +04:00
|
|
|
/*
|
|
|
|
* The new mount namespace is filled with the mountpoint
|
|
|
|
* clones from the original one. We have to umount them
|
|
|
|
* prior to recreating new ones.
|
|
|
|
*/
|
|
|
|
|
2012-09-28 14:13:18 +04:00
|
|
|
if (opts.root)
|
|
|
|
ret = cr_pivot_root();
|
|
|
|
else
|
|
|
|
ret = clean_mnt_ns();
|
|
|
|
|
2012-06-27 20:57:36 +04:00
|
|
|
if (!ret)
|
|
|
|
ret = populate_mnt_ns(ns_pid);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-08-01 07:00:48 +04:00
|
|
|
int mntns_collect_root(pid_t pid)
|
|
|
|
{
|
|
|
|
int fd, pfd;
|
|
|
|
int ret;
|
|
|
|
char path[PATH_MAX + 1];
|
|
|
|
|
2012-08-11 21:48:17 +04:00
|
|
|
/*
|
|
|
|
* If /proc/pid/root links on '/', it signs that a root of the task
|
|
|
|
* and a root of mntns is the same.
|
|
|
|
*/
|
2012-08-01 07:00:48 +04:00
|
|
|
|
|
|
|
pfd = open_pid_proc(pid);
|
2012-08-02 11:24:00 +04:00
|
|
|
ret = readlinkat(pfd, "root", path, sizeof(path) - 1);
|
2013-05-16 21:00:43 +08:00
|
|
|
if (ret < 0){
|
|
|
|
close_pid_proc();
|
2012-08-01 07:00:48 +04:00
|
|
|
return ret;
|
2013-05-16 21:00:43 +08:00
|
|
|
}
|
2012-08-01 07:00:48 +04:00
|
|
|
|
2012-08-02 11:24:00 +04:00
|
|
|
path[ret] = '\0';
|
2012-08-01 07:00:48 +04:00
|
|
|
|
|
|
|
if (ret != 1 || path[0] != '/') {
|
|
|
|
pr_err("The root task has another root than mntns: %s\n", path);
|
|
|
|
close_pid_proc();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
fd = openat(pfd, "root", O_RDONLY | O_DIRECTORY, 0);
|
|
|
|
close_pid_proc();
|
|
|
|
if (fd < 0) {
|
|
|
|
pr_perror("Can't open the task root");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
mntns_root = fd;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2013-01-15 23:24:01 +04:00
|
|
|
|
2013-05-20 13:30:17 +04:00
|
|
|
struct ns_desc mnt_ns_desc = NS_DESC_ENTRY(CLONE_NEWNS, "mnt");
|