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
|
|
|
|
2013-11-06 17:21:11 +04:00
|
|
|
#include "cr_options.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"
|
2013-12-25 16:54:36 +04:00
|
|
|
#include "plugin.h"
|
2012-05-04 13:38:00 +04:00
|
|
|
#include "mount.h"
|
2013-11-05 12:32:59 +04:00
|
|
|
#include "pstree.h"
|
2012-05-04 13:38:00 +04:00
|
|
|
#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"
|
2014-04-02 11:12:00 +04:00
|
|
|
#include "kerndat.h"
|
2014-04-17 15:04:00 +04:00
|
|
|
#include "fs-magic.h"
|
2014-08-19 22:31:07 -07:00
|
|
|
#include "sysfs_parse.h"
|
2014-04-17 15:04:00 +04:00
|
|
|
|
2012-07-17 14:23:37 +04:00
|
|
|
#include "protobuf/mnt.pb-c.h"
|
|
|
|
|
2014-06-09 17:26:17 +04:00
|
|
|
/*
|
|
|
|
* Structure to keep external mount points resolving info.
|
|
|
|
*
|
|
|
|
* On dump the key is the mountpoint as seen from the mount
|
|
|
|
* namespace, the val is some name that will be put into image
|
|
|
|
* instead of the mount point's root path.
|
|
|
|
*
|
|
|
|
* On restore the key is the name from the image (the one
|
|
|
|
* mentioned above) and the val is the path in criu's mount
|
|
|
|
* namespace that will become the mount point's root, i.e. --
|
|
|
|
* be bind mounted to the respective mountpoint.
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct ext_mount {
|
|
|
|
char *key;
|
|
|
|
char *val;
|
|
|
|
struct list_head l;
|
|
|
|
};
|
|
|
|
|
|
|
|
int ext_mount_add(char *key, char *val)
|
|
|
|
{
|
|
|
|
struct ext_mount *em;
|
|
|
|
|
|
|
|
em = xmalloc(sizeof(*em));
|
|
|
|
if (!em)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
em->key = key;
|
|
|
|
em->val = val;
|
|
|
|
list_add_tail(&em->l, &opts.ext_mounts);
|
|
|
|
pr_info("Added %s:%s ext mount mapping\n", key, val);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-06-09 17:26:59 +04:00
|
|
|
/* Lookup ext_mount by key field */
|
|
|
|
static struct ext_mount *ext_mount_lookup(char *key)
|
|
|
|
{
|
|
|
|
struct ext_mount *em;
|
|
|
|
|
|
|
|
list_for_each_entry(em, &opts.ext_mounts, l)
|
|
|
|
if (!strcmp(em->key, key))
|
|
|
|
return em;
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-12-11 17:34:41 +04:00
|
|
|
/*
|
|
|
|
* Single linked list of mount points get from proc/images
|
|
|
|
*/
|
2014-04-21 18:23:41 +04:00
|
|
|
struct mount_info *mntinfo;
|
2012-05-04 13:38:00 +04:00
|
|
|
|
2014-04-22 20:35:07 +04:00
|
|
|
static void mntinfo_add_list(struct mount_info *new)
|
|
|
|
{
|
|
|
|
if (!mntinfo)
|
|
|
|
mntinfo = new;
|
|
|
|
else {
|
|
|
|
struct mount_info *pm;
|
|
|
|
|
|
|
|
/* Add to the tail. (FIXME -- make O(1) ) */
|
|
|
|
for (pm = mntinfo; pm->next != NULL; pm = pm->next)
|
|
|
|
;
|
|
|
|
pm->next = new;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-16 09:04:25 +04:00
|
|
|
static int open_mountpoint(struct mount_info *pm);
|
2013-08-13 17:02:51 +04:00
|
|
|
|
2013-12-07 00:41:46 +04:00
|
|
|
static struct mount_info *mnt_build_tree(struct mount_info *list);
|
2014-06-09 17:24:13 +04:00
|
|
|
static int validate_mounts(struct mount_info *info, bool for_dump);
|
2013-12-07 00:41:46 +04:00
|
|
|
|
2014-02-28 18:39:50 +04:00
|
|
|
/* Asolute paths are used on dump and relative paths are used on restore */
|
2013-07-30 20:25:30 +04:00
|
|
|
static inline int is_root(char *p)
|
|
|
|
{
|
2014-04-16 14:48:00 +04:00
|
|
|
return (!strcmp(p, "/"));
|
2013-07-30 20:25:30 +04:00
|
|
|
}
|
|
|
|
|
2013-12-26 11:22:01 +04:00
|
|
|
/* True for the root mount (the topmost one) */
|
2013-07-30 20:25:30 +04:00
|
|
|
static inline int is_root_mount(struct mount_info *mi)
|
|
|
|
{
|
2014-04-16 14:48:00 +04:00
|
|
|
return is_root(mi->mountpoint + 1);
|
2013-07-30 20:25:30 +04:00
|
|
|
}
|
|
|
|
|
2013-12-26 11:22:01 +04:00
|
|
|
/*
|
|
|
|
* True if the mountpoint target is root on its FS.
|
|
|
|
*
|
|
|
|
* This is used to determine whether we need to postpone
|
|
|
|
* mounting. E.g. one can bind mount some subdir from a
|
|
|
|
* disk, and in this case we'll have to get the root disk
|
|
|
|
* mount first, then bind-mount it. See do_mount_one().
|
|
|
|
*/
|
2013-07-30 20:25:30 +04:00
|
|
|
static inline int fsroot_mounted(struct mount_info *mi)
|
|
|
|
{
|
|
|
|
return is_root(mi->root);
|
|
|
|
}
|
|
|
|
|
2014-04-16 09:04:27 +04:00
|
|
|
static int __open_mountpoint(struct mount_info *pm, int mnt_fd);
|
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)
|
2014-04-16 09:04:27 +04:00
|
|
|
if (s_dev == i->s_dev)
|
|
|
|
return __open_mountpoint(i, -1);
|
2012-05-04 13:38:00 +04:00
|
|
|
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
2012-05-04 13:38:00 +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;
|
|
|
|
}
|
|
|
|
|
2014-04-21 18:23:45 +04:00
|
|
|
static struct mount_info *mount_resolve_path(struct mount_info *mntinfo_tree, const char *path)
|
2013-12-11 09:04:14 +04:00
|
|
|
{
|
|
|
|
size_t pathlen = strlen(path);
|
|
|
|
struct mount_info *m = mntinfo_tree, *c;
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
list_for_each_entry(c, &m->children, siblings) {
|
|
|
|
size_t n;
|
|
|
|
|
2014-04-16 14:48:00 +04:00
|
|
|
n = strlen(c->mountpoint + 1);
|
2013-12-11 09:04:14 +04:00
|
|
|
if (n > pathlen)
|
|
|
|
continue;
|
|
|
|
|
2014-04-16 14:48:00 +04:00
|
|
|
if (strncmp(c->mountpoint + 1, path, min(n, pathlen)))
|
2013-12-11 09:04:14 +04:00
|
|
|
continue;
|
2014-03-18 15:31:00 +04:00
|
|
|
if (n < pathlen && path[n] != '/')
|
|
|
|
continue;
|
2013-12-11 09:04:14 +04:00
|
|
|
|
|
|
|
m = c;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (&c->siblings == &m->children)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
pr_debug("Path `%s' resolved to `%s' mountpoint\n", path, m->mountpoint);
|
|
|
|
return m;
|
|
|
|
}
|
|
|
|
|
2014-08-06 10:57:27 +04:00
|
|
|
dev_t phys_stat_resolve_dev(struct ns_id *ns, dev_t st_dev, const char *path)
|
2013-12-11 09:04:14 +04:00
|
|
|
{
|
2013-12-11 17:19:40 +04:00
|
|
|
struct mount_info *m;
|
2013-12-19 13:24:28 -08:00
|
|
|
|
2014-08-06 10:57:27 +04:00
|
|
|
m = mount_resolve_path(ns->mnt.mntinfo_tree, path);
|
2013-12-11 09:04:14 +04:00
|
|
|
/*
|
|
|
|
* BTRFS returns subvolume dev-id instead of
|
|
|
|
* superblock dev-id, in such case return device
|
|
|
|
* obtained from mountinfo (ie subvolume0).
|
|
|
|
*/
|
2013-12-12 08:18:21 +04:00
|
|
|
return strcmp(m->fstype->name, "btrfs") ?
|
2014-05-05 14:26:00 +04:00
|
|
|
MKKDEV(major(st_dev), minor(st_dev)) : m->s_dev;
|
2013-12-11 09:04:14 +04:00
|
|
|
}
|
|
|
|
|
2014-08-06 10:57:25 +04:00
|
|
|
bool phys_stat_dev_match(dev_t st_dev, dev_t phys_dev,
|
|
|
|
struct ns_id *ns, const char *path)
|
2013-12-04 19:15:12 +04:00
|
|
|
{
|
2013-12-12 08:44:15 +04:00
|
|
|
if (st_dev == kdev_to_odev(phys_dev))
|
2013-12-04 19:15:12 +04:00
|
|
|
return true;
|
2013-12-11 17:19:40 +04:00
|
|
|
|
2014-08-06 10:57:27 +04:00
|
|
|
return phys_dev == phys_stat_resolve_dev(ns, st_dev, path);
|
2013-12-04 19:15:12 +04:00
|
|
|
}
|
|
|
|
|
mounts: find mounts, which are propagated from a current one (v2)
A few sentences, which are required for understanging this patch
2a) A shared mount can be replicated to as many mountpoints and all the
replicas continue to be exactly same.
2b) A slave mount is like a shared mount except that mount and umount
events only propagate towards it.
2c) A private mount does not forward or receive propagation.
All rules is there Documentation/filesystems/sharedsubtree.txt
If it's a first mount in a group, all group members should be
bind-mounted from this one.
Each mount propagates to all members of parent's group. The group can
contains a few slaves.
Mounts, which have propagated to slaves, are unmounted, because we can't
be sure, that they propagated in real life. For example:
mount --bind --make-slave /share /slave1
mount --bind --make-slave /share /slave2
mount /share/test
umount /slave2/test
mount --make-share /slave1/test
mount --bind --make-share /slave1/test /slave2/test
41 40 0:33 / /share rw,relatime shared:28 - tmpfs xxx rw
42 40 0:33 / /slave1 rw,relatime master:28 - tmpfs xxx rw
43 40 0:33 / /slave2 rw,relatime master:28 - tmpfs xxx rw
44 41 0:34 / /share/test rw,relatime shared:29 - tmpfs xxx rw
46 42 0:34 / /slave1/test rw,relatime shared:30 master:29 - tmpfs xxx rw
45 43 0:34 / /slave2/test rw,relatime shared:30 master:29 - tmpfs xxx rw
/slave1/test and /slave2/test depend on each other and minimum one of them
doesn't propagate from /share/test
v2: use false and true for bool
Signed-off-by: Andrey Vagin <avagin@openvz.org>
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
2013-08-29 18:54:00 +04:00
|
|
|
/*
|
|
|
|
* Comparer two mounts. Return true if only mount points are differ.
|
|
|
|
* Don't care about root and mountpoints, if bind is true.
|
|
|
|
*/
|
|
|
|
static bool mounts_equal(struct mount_info* mi, struct mount_info *c, bool bind)
|
|
|
|
{
|
|
|
|
if (mi->s_dev != c->s_dev ||
|
|
|
|
c->fstype != mi->fstype ||
|
|
|
|
strcmp(c->source, mi->source) ||
|
|
|
|
strcmp(c->options, mi->options))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (bind)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (strcmp(c->root, mi->root))
|
|
|
|
return false;
|
|
|
|
if (strcmp(basename(c->mountpoint), basename(mi->mountpoint)))
|
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
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);
|
2014-09-12 15:41:00 +04:00
|
|
|
|
|
|
|
if (m->mnt_id != m->parent_mnt_id)
|
|
|
|
p = __lookup_mnt_id(list, m->parent_mnt_id);
|
|
|
|
else /* a circular mount reference. It's rootfs or smth like it. */
|
|
|
|
p = NULL;
|
|
|
|
|
2012-06-27 20:57:34 +04:00
|
|
|
if (!p) {
|
|
|
|
/* This should be / */
|
2013-11-28 15:46:00 +04:00
|
|
|
if (root == NULL && is_root_mount(m)) {
|
2012-06-27 20:57:34 +04:00
|
|
|
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");
|
2014-04-21 18:23:16 +04:00
|
|
|
if (root && m->is_ns_root) {
|
2014-04-21 18:23:17 +04:00
|
|
|
if (!mounts_equal(root, m, true) ||
|
|
|
|
strcmp(root->root, m->root)) {
|
|
|
|
pr_err("Nested mount namespaces with different roots are not supported yet");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2014-04-21 18:23:16 +04:00
|
|
|
/*
|
|
|
|
* A root of a sub mount namespace is
|
|
|
|
* mounted in a temporary directory in the
|
|
|
|
* root mount namespace, so its parent is
|
|
|
|
* the main root.
|
|
|
|
*/
|
|
|
|
p = root;
|
|
|
|
} else
|
|
|
|
return NULL;
|
2012-06-27 20:57:34 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
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, "");
|
|
|
|
}
|
|
|
|
|
2014-06-09 17:26:59 +04:00
|
|
|
static int try_resolve_ext_mount(struct mount_info *info)
|
|
|
|
{
|
|
|
|
struct ext_mount *em;
|
|
|
|
|
|
|
|
em = ext_mount_lookup(info->mountpoint + 1 /* trim the . */);
|
|
|
|
if (em == NULL)
|
|
|
|
return -ENOTSUP;
|
|
|
|
|
|
|
|
pr_info("Found %s mapping for %s mountpoint\n",
|
|
|
|
em->val, info->mountpoint);
|
|
|
|
info->external = em;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-06-09 17:24:13 +04:00
|
|
|
static int validate_mounts(struct mount_info *info, bool for_dump)
|
2013-08-13 17:02:51 +04:00
|
|
|
{
|
|
|
|
struct mount_info *m, *t;
|
|
|
|
|
|
|
|
for (m = info; m; m = m->next) {
|
2014-05-30 17:57:55 +04:00
|
|
|
if (m->parent == NULL || m->is_ns_root)
|
2014-05-30 17:57:39 +04:00
|
|
|
/* root mount can be any */
|
|
|
|
continue;
|
|
|
|
|
2014-09-10 16:46:41 +04:00
|
|
|
if (m->parent->shared_id && !list_empty(&m->parent->mnt_share)) {
|
2013-08-13 17:02:51 +04:00
|
|
|
struct mount_info *ct;
|
2014-09-10 16:46:41 +04:00
|
|
|
|
2013-08-13 17:02:51 +04:00
|
|
|
t = list_first_entry(&m->parent->mnt_share, struct mount_info, mnt_share);
|
|
|
|
|
|
|
|
list_for_each_entry(ct, &t->children, siblings) {
|
|
|
|
if (mounts_equal(m, ct, false))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (&ct->siblings == &t->children) {
|
|
|
|
pr_err("Two shared mounts %d, %d have different sets of children\n",
|
|
|
|
m->parent->mnt_id, t->mnt_id);
|
|
|
|
pr_err("%d:%s doesn't have a proper point for %d:%s\n",
|
|
|
|
t->mnt_id, t->mountpoint,
|
|
|
|
m->mnt_id, m->mountpoint);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-30 17:57:55 +04:00
|
|
|
/*
|
|
|
|
* Mountpoint can point to / of an FS. In that case this FS
|
|
|
|
* should be of some known type so that we can just mount one.
|
|
|
|
*
|
|
|
|
* Otherwise it's a bindmount mountpoint and we try to find
|
|
|
|
* what fsroot mountpoint it's bound to. If this point is the
|
|
|
|
* root mount, the path to bindmount root should be accessible
|
|
|
|
* form the rootmount path (the strstartswith check in the
|
|
|
|
* else branch below).
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (fsroot_mounted(m)) {
|
|
|
|
if (m->fstype->code == FSTYPE__UNSUPPORTED) {
|
|
|
|
pr_err("FS mnt %s dev %#x root %s unsupported id %x\n",
|
|
|
|
m->mountpoint, m->s_dev, m->root, m->mnt_id);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
} else {
|
2013-08-13 17:02:51 +04:00
|
|
|
list_for_each_entry(t, &m->mnt_bind, mnt_bind) {
|
2014-05-30 17:57:55 +04:00
|
|
|
if (fsroot_mounted(t) ||
|
|
|
|
(t->parent == NULL &&
|
|
|
|
strstartswith(m->root, t->root)))
|
2013-08-13 17:02:51 +04:00
|
|
|
break;
|
|
|
|
}
|
2014-05-30 17:57:55 +04:00
|
|
|
|
2013-08-13 17:02:51 +04:00
|
|
|
if (&t->mnt_bind == &m->mnt_bind) {
|
2013-12-25 16:54:53 +04:00
|
|
|
int ret;
|
2013-12-25 16:54:36 +04:00
|
|
|
|
2014-06-09 17:24:13 +04:00
|
|
|
if (for_dump) {
|
2014-02-27 20:58:23 +04:00
|
|
|
ret = run_plugins(DUMP_EXT_MOUNT, m->mountpoint, m->mnt_id);
|
2013-12-25 16:54:36 +04:00
|
|
|
if (ret == 0)
|
|
|
|
m->need_plugin = true;
|
2014-06-09 17:26:59 +04:00
|
|
|
else if (ret == -ENOTSUP)
|
|
|
|
ret = try_resolve_ext_mount(m);
|
2014-06-09 17:24:13 +04:00
|
|
|
} else {
|
2014-06-09 17:26:59 +04:00
|
|
|
if (m->need_plugin || m->external)
|
2014-06-09 17:24:13 +04:00
|
|
|
/*
|
|
|
|
* plugin should take care of this one
|
2014-06-09 17:26:59 +04:00
|
|
|
* in restore_ext_mount, or do_bind_mount
|
|
|
|
* will mount it as external
|
2014-06-09 17:24:13 +04:00
|
|
|
*/
|
|
|
|
ret = 0;
|
|
|
|
else
|
|
|
|
ret = -ENOTSUP;
|
|
|
|
}
|
|
|
|
|
2013-12-25 16:54:36 +04:00
|
|
|
if (ret < 0) {
|
|
|
|
if (ret == -ENOTSUP)
|
|
|
|
pr_err("%d:%s doesn't have a proper root mount\n",
|
|
|
|
m->mnt_id, m->mountpoint);
|
|
|
|
return -1;
|
|
|
|
}
|
2013-08-13 17:02:51 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
list_for_each_entry(t, &m->parent->children, siblings) {
|
|
|
|
int tlen, mlen;
|
|
|
|
|
|
|
|
if (m == t)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
tlen = strlen(t->mountpoint);
|
|
|
|
mlen = strlen(m->mountpoint);
|
|
|
|
if (mlen < tlen)
|
|
|
|
continue;
|
|
|
|
if (strncmp(t->mountpoint, m->mountpoint, tlen))
|
|
|
|
continue;
|
|
|
|
if (mlen > tlen && m->mountpoint[tlen] != '/')
|
|
|
|
continue;
|
2013-10-09 17:17:58 +04:00
|
|
|
pr_err("%d:%s is overmounted\n", m->mnt_id, m->mountpoint);
|
2013-08-13 17:02:51 +04:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-20 17:30:41 +04:00
|
|
|
if (need_master && m->parent) {
|
2013-08-29 18:55:38 +04:00
|
|
|
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;
|
|
|
|
}
|
2013-08-13 17:02:50 +04:00
|
|
|
|
|
|
|
/* Search bind-mounts */
|
|
|
|
if (list_empty(&m->mnt_bind)) {
|
|
|
|
/*
|
|
|
|
* A first mounted point will be set up as a source point
|
|
|
|
* for others. Look at propagate_mount()
|
|
|
|
*/
|
|
|
|
for (t = m->next; t; t = t->next) {
|
|
|
|
if (mounts_equal(m, t, true))
|
|
|
|
list_add(&t->mnt_bind, &m->mnt_bind);
|
|
|
|
}
|
|
|
|
}
|
2013-08-29 18:55:38 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
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.
|
|
|
|
*/
|
2014-04-16 09:04:25 +04:00
|
|
|
static int __open_mountpoint(struct mount_info *pm, int mnt_fd)
|
2012-08-09 19:45:18 +04:00
|
|
|
{
|
2014-04-17 15:04:01 +04:00
|
|
|
dev_t dev;
|
2012-08-09 19:45:18 +04:00
|
|
|
struct stat st;
|
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) {
|
2014-04-16 09:04:24 +04:00
|
|
|
int mntns_root;
|
|
|
|
|
2014-04-23 02:31:16 +04:00
|
|
|
mntns_root = mntns_get_root_fd(pm->nsid);
|
2014-04-21 18:23:37 +04:00
|
|
|
if (mntns_root < 0)
|
|
|
|
return -1;
|
2014-04-16 09:04:24 +04:00
|
|
|
|
2014-04-16 14:48:00 +04:00
|
|
|
mnt_fd = openat(mntns_root, pm->mountpoint, O_RDONLY);
|
2013-08-01 17:17:03 +04:00
|
|
|
if (mnt_fd < 0) {
|
|
|
|
pr_perror("Can't open %s", pm->mountpoint);
|
2014-04-16 09:04:25 +04:00
|
|
|
return -1;
|
2013-08-01 17:17:03 +04:00
|
|
|
}
|
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) {
|
2014-04-16 14:48:00 +04:00
|
|
|
pr_perror("fstat(%s) failed", pm->mountpoint);
|
2013-08-01 17:17:03 +04:00
|
|
|
goto err;
|
2012-08-09 19:45:18 +04:00
|
|
|
}
|
|
|
|
|
2014-08-06 10:57:27 +04:00
|
|
|
dev = phys_stat_resolve_dev(pm->nsid, st.st_dev, pm->mountpoint + 1);
|
2014-04-17 15:04:01 +04:00
|
|
|
if (dev != pm->s_dev) {
|
2013-12-03 22:50:40 +04:00
|
|
|
pr_err("The file system %#x (%#x) %s %s is inaccessible\n",
|
2014-04-17 15:04:01 +04:00
|
|
|
pm->s_dev, (int)dev, pm->fstype->name, pm->mountpoint);
|
2013-08-01 17:17:03 +04:00
|
|
|
goto err;
|
2012-08-09 19:45:18 +04:00
|
|
|
}
|
|
|
|
|
2014-04-16 09:04:25 +04:00
|
|
|
return mnt_fd;
|
2013-08-01 17:17:03 +04:00
|
|
|
err:
|
|
|
|
close(mnt_fd);
|
2014-04-16 09:04:25 +04:00
|
|
|
return -1;
|
2012-08-09 19:45:18 +04:00
|
|
|
}
|
|
|
|
|
2014-04-16 09:04:25 +04:00
|
|
|
static int open_mountpoint(struct mount_info *pm)
|
2013-08-01 17:17:03 +04:00
|
|
|
{
|
|
|
|
int fd = -1, ns_old = -1;
|
|
|
|
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)
|
2014-04-16 09:04:25 +04:00
|
|
|
return -1;
|
2013-08-01 17:17:03 +04:00
|
|
|
|
|
|
|
if (mkdtemp(mnt_path) == NULL) {
|
|
|
|
pr_perror("Can't create a temporary directory");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2014-02-21 17:51:00 +04:00
|
|
|
if (mount(pm->mountpoint, mnt_path, NULL, MS_BIND, NULL)) {
|
2013-08-01 17:17:03 +04:00
|
|
|
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;
|
|
|
|
|
2013-09-03 13:59:01 +04:00
|
|
|
if (restore_ns(ns_old, &mnt_ns_desc)) {
|
|
|
|
ns_old = -1;
|
2013-08-01 17:17:03 +04:00
|
|
|
goto out;
|
2013-09-03 13:59:01 +04:00
|
|
|
}
|
2013-08-01 17:17:03 +04:00
|
|
|
|
|
|
|
return __open_mountpoint(pm, fd);;
|
|
|
|
out:
|
|
|
|
if (ns_old >= 0)
|
|
|
|
restore_ns(ns_old, &mnt_ns_desc);
|
|
|
|
close_safe(&fd);
|
2014-04-16 09:04:25 +04:00
|
|
|
return -1;
|
2013-08-01 17:17:03 +04:00
|
|
|
}
|
|
|
|
|
2014-05-30 18:47:00 +04:00
|
|
|
static int attach_option(struct mount_info *pm, char *opt)
|
|
|
|
{
|
|
|
|
char *buf;
|
|
|
|
int len, olen;
|
|
|
|
|
|
|
|
len = strlen(pm->options);
|
|
|
|
olen = strlen(opt);
|
|
|
|
buf = xrealloc(pm->options, len + olen + 2);
|
|
|
|
if (buf == NULL)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (len && buf[len - 1] != ',') {
|
|
|
|
buf[len] = ',';
|
|
|
|
len++;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(buf + len, opt, olen + 1);
|
|
|
|
pm->options = buf;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-04-02 11:12:00 +04:00
|
|
|
/* Is it mounted w or w/o the newinstance option */
|
2014-06-25 12:36:43 +04:00
|
|
|
static int devpts_parse(struct mount_info *pm)
|
2014-04-02 11:12:00 +04:00
|
|
|
{
|
|
|
|
struct stat *host_st;
|
|
|
|
|
|
|
|
host_st = kerndat_get_devpts_stat();
|
|
|
|
if (host_st == NULL)
|
|
|
|
return -1;
|
|
|
|
|
2014-06-25 12:36:42 +04:00
|
|
|
if (host_st->st_dev == kdev_to_odev(pm->s_dev))
|
2014-04-02 11:12:00 +04:00
|
|
|
return 0;
|
|
|
|
|
2014-05-30 18:47:00 +04:00
|
|
|
return attach_option(pm, "newinstance");
|
2014-04-02 11:12:00 +04:00
|
|
|
}
|
|
|
|
|
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;
|
2013-10-15 13:59:59 +04:00
|
|
|
char tmpfs_path[PSFDS];
|
2014-09-29 12:48:53 +04:00
|
|
|
int fd = -1;
|
|
|
|
struct cr_img *img;
|
2012-08-09 19:51:22 +04:00
|
|
|
|
2014-04-16 09:04:25 +04:00
|
|
|
fd = open_mountpoint(pm);
|
|
|
|
if (fd < 0)
|
2012-08-09 19:51:22 +04:00
|
|
|
return -1;
|
|
|
|
|
2012-09-28 14:09:58 +04:00
|
|
|
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
|
|
|
|
2014-09-29 12:48:53 +04:00
|
|
|
img = open_image(CR_FD_TMPFS_DEV, O_DUMP, pm->s_dev);
|
|
|
|
if (!img)
|
2012-09-28 14:09:58 +04:00
|
|
|
goto out;
|
2012-08-11 21:48:17 +04:00
|
|
|
|
2013-10-15 13:59:59 +04:00
|
|
|
sprintf(tmpfs_path, "/proc/self/fd/%d", fd);
|
2012-08-09 19:51:22 +04:00
|
|
|
|
2014-09-29 12:48:53 +04:00
|
|
|
ret = cr_system(-1, img_raw_fd(img), -1, "tar", (char *[])
|
2012-09-28 14:09:58 +04:00
|
|
|
{ "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");
|
|
|
|
|
2014-09-29 12:48:53 +04:00
|
|
|
close_image(img);
|
2012-09-28 14:09:58 +04:00
|
|
|
out:
|
2014-04-16 09:04:25 +04:00
|
|
|
close_safe(&fd);
|
2012-09-28 14:09:58 +04:00
|
|
|
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;
|
2014-09-29 12:48:53 +04:00
|
|
|
struct cr_img *img;
|
2012-08-09 19:51:22 +04:00
|
|
|
|
2014-09-29 12:48:53 +04:00
|
|
|
img = open_image(CR_FD_TMPFS_DEV, O_RSTR, pm->s_dev);
|
|
|
|
if (!img && errno == ENOENT)
|
|
|
|
img = open_image(CR_FD_TMPFS_IMG, O_RSTR, pm->mnt_id);
|
|
|
|
if (!img)
|
2012-09-25 15:59:13 +04:00
|
|
|
return -1;
|
2012-08-09 19:51:22 +04:00
|
|
|
|
2014-09-29 12:48:53 +04:00
|
|
|
ret = cr_system(img_raw_fd(img), -1, -1, "tar",
|
2012-09-28 14:09:58 +04:00
|
|
|
(char *[]) {"tar", "--extract", "--gzip",
|
|
|
|
"--directory", pm->mountpoint, NULL});
|
2014-09-29 12:48:53 +04:00
|
|
|
close_image(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)
|
|
|
|
{
|
2014-04-16 09:04:25 +04:00
|
|
|
int fd, ret = -1;
|
2012-08-09 19:47:20 +04:00
|
|
|
struct dirent *de;
|
|
|
|
DIR *fdir = NULL;
|
|
|
|
|
2014-04-16 09:04:25 +04:00
|
|
|
fd = open_mountpoint(pm);
|
|
|
|
if (fd < 0)
|
2012-08-09 19:47:20 +04:00
|
|
|
return -1;
|
|
|
|
|
2014-04-16 09:04:25 +04:00
|
|
|
fdir = fdopendir(fd);
|
|
|
|
if (fdir == NULL) {
|
|
|
|
close(fd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-08-09 19:47:20 +04:00
|
|
|
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:
|
2014-04-16 09:04:25 +04:00
|
|
|
closedir(fdir);
|
2012-08-09 19:47:20 +04:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-06-10 18:13:00 +04:00
|
|
|
|
|
|
|
static int dump_empty_fs(struct mount_info *pm)
|
|
|
|
{
|
|
|
|
int fd, ret = -1;
|
|
|
|
struct dirent *de;
|
|
|
|
DIR *fdir = NULL;
|
|
|
|
fd = open_mountpoint(pm);
|
|
|
|
|
|
|
|
if (fd < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
fdir = fdopendir(fd);
|
|
|
|
if (fdir == NULL) {
|
|
|
|
close(fd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
while ((de = readdir(fdir))) {
|
|
|
|
if (dir_dots(de))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
pr_err("%s isn't empty: %s\n", pm->fstype->name, de->d_name);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
out:
|
|
|
|
closedir(fdir);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-08-09 16:27:30 +04:00
|
|
|
static struct fstype fstypes[] = {
|
2013-12-11 14:03:11 -08:00
|
|
|
{
|
2013-12-04 19:51:10 +04:00
|
|
|
.name = "unsupported",
|
|
|
|
.code = FSTYPE__UNSUPPORTED,
|
|
|
|
}, {
|
|
|
|
.name = "proc",
|
|
|
|
.code = FSTYPE__PROC,
|
|
|
|
}, {
|
|
|
|
.name = "sysfs",
|
|
|
|
.code = FSTYPE__SYSFS,
|
|
|
|
}, {
|
|
|
|
.name = "devtmpfs",
|
|
|
|
.code = FSTYPE__DEVTMPFS,
|
|
|
|
}, {
|
|
|
|
.name = "binfmt_misc",
|
|
|
|
.code = FSTYPE__BINFMT_MISC,
|
|
|
|
.dump = binfmt_misc_dump,
|
|
|
|
}, {
|
|
|
|
.name = "tmpfs",
|
|
|
|
.code = FSTYPE__TMPFS,
|
|
|
|
.dump = tmpfs_dump,
|
|
|
|
.restore = tmpfs_restore,
|
|
|
|
}, {
|
|
|
|
.name = "devpts",
|
2014-06-25 12:36:43 +04:00
|
|
|
.parse = devpts_parse,
|
2013-12-04 19:51:10 +04:00
|
|
|
.code = FSTYPE__DEVPTS,
|
|
|
|
}, {
|
|
|
|
.name = "simfs",
|
|
|
|
.code = FSTYPE__SIMFS,
|
2013-12-12 01:07:12 +04:00
|
|
|
}, {
|
|
|
|
.name = "btrfs",
|
|
|
|
.code = FSTYPE__UNSUPPORTED,
|
2014-06-10 18:13:00 +04:00
|
|
|
}, {
|
|
|
|
.name = "pstore",
|
|
|
|
.dump = dump_empty_fs,
|
|
|
|
.code = FSTYPE__PSTORE,
|
|
|
|
}, {
|
|
|
|
.name = "securityfs",
|
|
|
|
.code = FSTYPE__SECURITYFS,
|
|
|
|
}, {
|
|
|
|
.name = "fusectl",
|
|
|
|
.dump = dump_empty_fs,
|
|
|
|
.code = FSTYPE__FUSECTL,
|
|
|
|
}, {
|
|
|
|
.name = "debugfs",
|
|
|
|
.code = FSTYPE__DEBUGFS,
|
2014-07-10 17:00:28 +04:00
|
|
|
}, {
|
|
|
|
.name = "cgroup",
|
|
|
|
.code = FSTYPE__CGROUP,
|
2014-08-19 22:31:07 -07:00
|
|
|
}, {
|
|
|
|
.name = "aufs",
|
|
|
|
.code = FSTYPE__AUFS,
|
|
|
|
.parse = aufs_parse,
|
|
|
|
},
|
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 struct fstype *decode_fstype(u32 fst)
|
|
|
|
{
|
2013-12-04 19:51:10 +04:00
|
|
|
int i;
|
2012-08-09 16:27:30 +04:00
|
|
|
|
2013-12-04 19:51:10 +04:00
|
|
|
if (fst == FSTYPE__UNSUPPORTED)
|
|
|
|
goto uns;
|
2012-06-27 20:57:32 +04:00
|
|
|
|
2013-12-04 19:51:10 +04:00
|
|
|
for (i = 0; i < ARRAY_SIZE(fstypes); i++)
|
|
|
|
if (fstypes[i].code == fst)
|
|
|
|
return fstypes + i;
|
|
|
|
uns:
|
|
|
|
return &fstypes[0];
|
2012-06-27 20:57:32 +04:00
|
|
|
}
|
|
|
|
|
2014-09-29 12:48:53 +04:00
|
|
|
static int dump_one_mountpoint(struct mount_info *pm, struct cr_img *img)
|
2012-06-27 20:57:31 +04:00
|
|
|
{
|
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);
|
|
|
|
|
2013-12-04 19:51:10 +04:00
|
|
|
me.fstype = pm->fstype->code;
|
2014-06-16 18:40:04 +04:00
|
|
|
|
|
|
|
if (pm->parent && !pm->dumped && !pm->need_plugin &&
|
|
|
|
pm->fstype->dump && fsroot_mounted(pm)) {
|
|
|
|
struct mount_info *t;
|
|
|
|
|
|
|
|
if (pm->fstype->dump(pm))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
list_for_each_entry(t, &pm->mnt_bind, mnt_bind)
|
|
|
|
t->dumped = true;
|
|
|
|
}
|
2012-08-09 16:27:30 +04:00
|
|
|
|
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;
|
2014-04-16 14:48:00 +04:00
|
|
|
me.mountpoint = pm->mountpoint + 1;
|
2012-07-17 14:23:37 +04:00
|
|
|
me.source = pm->source;
|
2014-09-10 00:17:46 +04:00
|
|
|
me.options = pm->options;
|
2013-08-13 17:02:45 +04:00
|
|
|
me.shared_id = pm->shared_id;
|
|
|
|
me.has_shared_id = true;
|
|
|
|
me.master_id = pm->master_id;
|
|
|
|
me.has_master_id = true;
|
2013-12-25 16:54:36 +04:00
|
|
|
if (pm->need_plugin) {
|
|
|
|
me.has_with_plugin = true;
|
|
|
|
me.with_plugin = true;
|
|
|
|
}
|
2012-07-17 14:23:37 +04:00
|
|
|
|
2014-06-09 17:26:59 +04:00
|
|
|
if (pm->external) {
|
|
|
|
/*
|
|
|
|
* For external mount points dump the mapping's
|
|
|
|
* value instead of root. See collect_mnt_from_image
|
|
|
|
* for reverse mapping details.
|
|
|
|
*/
|
|
|
|
me.root = pm->external->val;
|
|
|
|
me.has_ext_mount = true;
|
|
|
|
me.ext_mount = true;
|
|
|
|
} else
|
|
|
|
me.root = pm->root;
|
|
|
|
|
2014-09-29 12:48:53 +04:00
|
|
|
if (pb_write_one(img, &me, PB_MNT))
|
2012-06-27 20:57:31 +04:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-04-22 20:38:34 +04:00
|
|
|
static void free_mntinfo(struct mount_info *pms)
|
|
|
|
{
|
|
|
|
while (pms) {
|
|
|
|
struct mount_info *pm;
|
|
|
|
|
|
|
|
pm = pms->next;
|
|
|
|
mnt_entry_free(pms);
|
|
|
|
pms = pm;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-21 18:23:34 +04:00
|
|
|
struct mount_info *collect_mntinfo(struct ns_id *ns)
|
2012-05-12 03:30:10 +04:00
|
|
|
{
|
2014-04-22 20:38:34 +04:00
|
|
|
struct mount_info *pm;
|
2012-06-27 20:57:31 +04:00
|
|
|
|
2014-09-22 16:26:00 +04:00
|
|
|
ns->mnt.mntinfo_list = pm = parse_mountinfo(ns->pid, ns);
|
2012-06-27 20:57:31 +04:00
|
|
|
if (!pm) {
|
2014-04-21 18:23:34 +04:00
|
|
|
pr_err("Can't parse %d's mountinfo\n", ns->pid);
|
|
|
|
return NULL;
|
2012-06-27 20:57:31 +04:00
|
|
|
}
|
|
|
|
|
2014-04-21 18:23:32 +04:00
|
|
|
ns->mnt.mntinfo_tree = mnt_build_tree(pm);
|
|
|
|
if (ns->mnt.mntinfo_tree == NULL)
|
2013-09-30 17:16:48 +04:00
|
|
|
goto err;
|
2013-08-01 16:44:38 +04:00
|
|
|
|
2014-04-21 18:23:34 +04:00
|
|
|
return pm;
|
|
|
|
err:
|
2014-04-22 20:38:34 +04:00
|
|
|
free_mntinfo(pm);
|
2014-04-21 18:23:34 +04:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2014-08-03 23:31:04 +04:00
|
|
|
static int dump_mnt_ns(struct ns_id *ns, struct mount_info *pms)
|
2014-04-21 18:23:34 +04:00
|
|
|
{
|
2014-04-22 20:36:57 +04:00
|
|
|
struct mount_info *pm;
|
2014-09-29 12:48:53 +04:00
|
|
|
int ret = -1;
|
|
|
|
struct cr_img *img;
|
2014-04-21 18:23:34 +04:00
|
|
|
int ns_id = ns->id;
|
|
|
|
|
2012-06-27 20:57:31 +04:00
|
|
|
pr_info("Dumping mountpoints\n");
|
2014-09-29 12:48:53 +04:00
|
|
|
img = open_image(CR_FD_MNTS, O_DUMP, ns_id);
|
|
|
|
if (!img)
|
2014-04-21 18:23:34 +04:00
|
|
|
goto err;
|
2012-06-27 20:57:31 +04:00
|
|
|
|
2014-08-03 23:31:04 +04:00
|
|
|
for (pm = pms; pm && pm->nsid == ns; pm = pm->next)
|
2014-09-29 12:48:53 +04:00
|
|
|
if (dump_one_mountpoint(pm, img))
|
2014-04-22 20:37:13 +04:00
|
|
|
goto err_i;
|
2012-06-27 20:57:31 +04:00
|
|
|
|
2013-09-30 17:16:48 +04:00
|
|
|
ret = 0;
|
2014-04-22 20:37:13 +04:00
|
|
|
err_i:
|
2014-09-29 12:48:53 +04:00
|
|
|
close_image(img);
|
2014-04-22 20:37:13 +04:00
|
|
|
err:
|
2013-09-30 17:16:48 +04:00
|
|
|
return ret;
|
2012-05-12 03:30:10 +04:00
|
|
|
}
|
|
|
|
|
2013-08-29 18:52:43 +04:00
|
|
|
/*
|
|
|
|
* _fn_f - pre-order traversal function
|
|
|
|
* _fn_f - post-order traversal function
|
|
|
|
* _plist - a postpone list. _el is added to this list, if _fn_f returns
|
|
|
|
* a positive value, and all lower elements are not enumirated.
|
|
|
|
*/
|
|
|
|
#define MNT_TREE_WALK(_r, _el, _fn_f, _fn_r, _plist, _prgs) do { \
|
2013-07-30 20:25:28 +04:00
|
|
|
struct mount_info *_mi = _r; \
|
|
|
|
\
|
2012-06-27 20:57:37 +04:00
|
|
|
while (1) { \
|
2013-08-29 18:52:43 +04:00
|
|
|
int ret; \
|
|
|
|
\
|
|
|
|
list_del_init(&_mi->postpone); \
|
|
|
|
\
|
|
|
|
ret = _fn_f(_mi); \
|
|
|
|
if (ret < 0) \
|
2012-06-27 20:57:37 +04:00
|
|
|
return -1; \
|
2013-08-29 18:52:43 +04:00
|
|
|
else if (ret > 0) { \
|
|
|
|
list_add_tail(&_mi->postpone, _plist); \
|
|
|
|
goto up; \
|
|
|
|
} \
|
|
|
|
\
|
|
|
|
_prgs++; \
|
|
|
|
\
|
2012-06-27 20:57:37 +04:00
|
|
|
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 &&
|
|
|
|
|
|
|
|
|
2013-08-29 18:52:43 +04:00
|
|
|
static int mnt_tree_for_each(struct mount_info *start,
|
2012-06-27 20:57:36 +04:00
|
|
|
int (*fn)(struct mount_info *))
|
|
|
|
{
|
2013-08-29 18:52:43 +04:00
|
|
|
struct mount_info *tmp;
|
|
|
|
LIST_HEAD(postpone);
|
|
|
|
LIST_HEAD(postpone2);
|
|
|
|
int progress;
|
|
|
|
|
|
|
|
pr_debug("Start with %d:%s\n", start->mnt_id, start->mountpoint);
|
|
|
|
list_add(&start->postpone, &postpone);
|
|
|
|
|
|
|
|
again:
|
|
|
|
progress = 0;
|
|
|
|
|
|
|
|
list_for_each_entry_safe(start, tmp, &postpone, postpone)
|
|
|
|
MNT_TREE_WALK(start, next, fn, MNT_WALK_NONE, &postpone2, progress);
|
|
|
|
|
|
|
|
if (!progress) {
|
|
|
|
struct mount_info *m;
|
|
|
|
|
|
|
|
pr_err("A few mount points can't be mounted");
|
|
|
|
list_for_each_entry(m, &postpone2, postpone) {
|
|
|
|
pr_err("%d:%d %s %s %s\n", m->mnt_id,
|
|
|
|
m->parent_mnt_id, m->root,
|
|
|
|
m->mountpoint, m->source);
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
list_splice_init(&postpone2, &postpone);
|
|
|
|
|
|
|
|
if (!list_empty(&postpone))
|
|
|
|
goto again;
|
2013-07-30 20:25:28 +04:00
|
|
|
|
|
|
|
return 0;
|
2013-08-29 18:52:43 +04:00
|
|
|
|
2012-06-27 20:57:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static int mnt_tree_for_each_reverse(struct mount_info *m,
|
|
|
|
int (*fn)(struct mount_info *))
|
|
|
|
{
|
2013-12-20 01:18:00 +04:00
|
|
|
int progress = 0;
|
2013-08-29 18:52:43 +04:00
|
|
|
|
|
|
|
MNT_TREE_WALK(m, prev, MNT_WALK_NONE, fn, (struct list_head *) NULL, progress);
|
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;
|
|
|
|
}
|
|
|
|
|
2013-08-13 17:02:48 +04:00
|
|
|
static int restore_shared_options(struct mount_info *mi, bool private, bool shared, bool slave)
|
|
|
|
{
|
|
|
|
pr_debug("%d:%s private %d shared %d slave %d\n",
|
|
|
|
mi->mnt_id, mi->mountpoint, private, shared, slave);
|
|
|
|
|
|
|
|
if (private && mount(NULL, mi->mountpoint, NULL, MS_PRIVATE, NULL)) {
|
|
|
|
pr_perror("Unable to make %s private", mi->mountpoint);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (slave && mount(NULL, mi->mountpoint, NULL, MS_SLAVE, NULL)) {
|
|
|
|
pr_perror("Unable to make %s slave", mi->mountpoint);
|
|
|
|
return -1;
|
|
|
|
}
|
2013-12-30 12:30:18 +04:00
|
|
|
if (shared && mount(NULL, mi->mountpoint, NULL, MS_SHARED, NULL)) {
|
|
|
|
pr_perror("Unable to make %s shared", mi->mountpoint);
|
|
|
|
return -1;
|
|
|
|
}
|
2013-08-13 17:02:48 +04:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
mounts: find mounts, which are propagated from a current one (v2)
A few sentences, which are required for understanging this patch
2a) A shared mount can be replicated to as many mountpoints and all the
replicas continue to be exactly same.
2b) A slave mount is like a shared mount except that mount and umount
events only propagate towards it.
2c) A private mount does not forward or receive propagation.
All rules is there Documentation/filesystems/sharedsubtree.txt
If it's a first mount in a group, all group members should be
bind-mounted from this one.
Each mount propagates to all members of parent's group. The group can
contains a few slaves.
Mounts, which have propagated to slaves, are unmounted, because we can't
be sure, that they propagated in real life. For example:
mount --bind --make-slave /share /slave1
mount --bind --make-slave /share /slave2
mount /share/test
umount /slave2/test
mount --make-share /slave1/test
mount --bind --make-share /slave1/test /slave2/test
41 40 0:33 / /share rw,relatime shared:28 - tmpfs xxx rw
42 40 0:33 / /slave1 rw,relatime master:28 - tmpfs xxx rw
43 40 0:33 / /slave2 rw,relatime master:28 - tmpfs xxx rw
44 41 0:34 / /share/test rw,relatime shared:29 - tmpfs xxx rw
46 42 0:34 / /slave1/test rw,relatime shared:30 master:29 - tmpfs xxx rw
45 43 0:34 / /slave2/test rw,relatime shared:30 master:29 - tmpfs xxx rw
/slave1/test and /slave2/test depend on each other and minimum one of them
doesn't propagate from /share/test
v2: use false and true for bool
Signed-off-by: Andrey Vagin <avagin@openvz.org>
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
2013-08-29 18:54:00 +04:00
|
|
|
/*
|
|
|
|
* Umount points, which are propagated in slave parents, because
|
|
|
|
* we can't be sure, that they were inherited in a real life.
|
|
|
|
*/
|
|
|
|
static int umount_from_slaves(struct mount_info *mi)
|
|
|
|
{
|
|
|
|
struct mount_info *t;
|
|
|
|
char mpath[PATH_MAX];
|
|
|
|
|
|
|
|
list_for_each_entry(t, &mi->parent->mnt_slave_list, mnt_slave) {
|
2013-08-13 17:02:49 +04:00
|
|
|
if (!t->mounted)
|
|
|
|
continue;
|
|
|
|
|
mounts: find mounts, which are propagated from a current one (v2)
A few sentences, which are required for understanging this patch
2a) A shared mount can be replicated to as many mountpoints and all the
replicas continue to be exactly same.
2b) A slave mount is like a shared mount except that mount and umount
events only propagate towards it.
2c) A private mount does not forward or receive propagation.
All rules is there Documentation/filesystems/sharedsubtree.txt
If it's a first mount in a group, all group members should be
bind-mounted from this one.
Each mount propagates to all members of parent's group. The group can
contains a few slaves.
Mounts, which have propagated to slaves, are unmounted, because we can't
be sure, that they propagated in real life. For example:
mount --bind --make-slave /share /slave1
mount --bind --make-slave /share /slave2
mount /share/test
umount /slave2/test
mount --make-share /slave1/test
mount --bind --make-share /slave1/test /slave2/test
41 40 0:33 / /share rw,relatime shared:28 - tmpfs xxx rw
42 40 0:33 / /slave1 rw,relatime master:28 - tmpfs xxx rw
43 40 0:33 / /slave2 rw,relatime master:28 - tmpfs xxx rw
44 41 0:34 / /share/test rw,relatime shared:29 - tmpfs xxx rw
46 42 0:34 / /slave1/test rw,relatime shared:30 master:29 - tmpfs xxx rw
45 43 0:34 / /slave2/test rw,relatime shared:30 master:29 - tmpfs xxx rw
/slave1/test and /slave2/test depend on each other and minimum one of them
doesn't propagate from /share/test
v2: use false and true for bool
Signed-off-by: Andrey Vagin <avagin@openvz.org>
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
2013-08-29 18:54:00 +04:00
|
|
|
snprintf(mpath, sizeof(mpath), "%s/%s",
|
|
|
|
t->mountpoint, basename(mi->mountpoint));
|
|
|
|
pr_debug("\t\tUmount %s\n", mpath);
|
|
|
|
if (umount(mpath) == -1) {
|
|
|
|
pr_perror("Can't umount %s", mpath);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If something is mounted in one shared point, it will be spread in
|
|
|
|
* all other points from this shared group.
|
|
|
|
*
|
|
|
|
* Look at Documentation/filesystems/sharedsubtree.txt for more details
|
|
|
|
*/
|
|
|
|
static int propagate_siblings(struct mount_info *mi)
|
|
|
|
{
|
|
|
|
struct mount_info *t;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find all mounts, which must be bind-mounted from this one
|
|
|
|
* to inherite shared group or master id
|
|
|
|
*/
|
|
|
|
list_for_each_entry(t, &mi->mnt_share, mnt_share) {
|
2014-08-05 17:20:24 +04:00
|
|
|
if (t->mounted)
|
|
|
|
continue;
|
mounts: find mounts, which are propagated from a current one (v2)
A few sentences, which are required for understanging this patch
2a) A shared mount can be replicated to as many mountpoints and all the
replicas continue to be exactly same.
2b) A slave mount is like a shared mount except that mount and umount
events only propagate towards it.
2c) A private mount does not forward or receive propagation.
All rules is there Documentation/filesystems/sharedsubtree.txt
If it's a first mount in a group, all group members should be
bind-mounted from this one.
Each mount propagates to all members of parent's group. The group can
contains a few slaves.
Mounts, which have propagated to slaves, are unmounted, because we can't
be sure, that they propagated in real life. For example:
mount --bind --make-slave /share /slave1
mount --bind --make-slave /share /slave2
mount /share/test
umount /slave2/test
mount --make-share /slave1/test
mount --bind --make-share /slave1/test /slave2/test
41 40 0:33 / /share rw,relatime shared:28 - tmpfs xxx rw
42 40 0:33 / /slave1 rw,relatime master:28 - tmpfs xxx rw
43 40 0:33 / /slave2 rw,relatime master:28 - tmpfs xxx rw
44 41 0:34 / /share/test rw,relatime shared:29 - tmpfs xxx rw
46 42 0:34 / /slave1/test rw,relatime shared:30 master:29 - tmpfs xxx rw
45 43 0:34 / /slave2/test rw,relatime shared:30 master:29 - tmpfs xxx rw
/slave1/test and /slave2/test depend on each other and minimum one of them
doesn't propagate from /share/test
v2: use false and true for bool
Signed-off-by: Andrey Vagin <avagin@openvz.org>
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
2013-08-29 18:54:00 +04:00
|
|
|
pr_debug("\t\tBind %s\n", t->mountpoint);
|
|
|
|
t->bind = mi;
|
|
|
|
}
|
|
|
|
|
|
|
|
list_for_each_entry(t, &mi->mnt_slave_list, mnt_slave) {
|
2014-08-05 17:20:24 +04:00
|
|
|
if (t->mounted)
|
|
|
|
continue;
|
mounts: find mounts, which are propagated from a current one (v2)
A few sentences, which are required for understanging this patch
2a) A shared mount can be replicated to as many mountpoints and all the
replicas continue to be exactly same.
2b) A slave mount is like a shared mount except that mount and umount
events only propagate towards it.
2c) A private mount does not forward or receive propagation.
All rules is there Documentation/filesystems/sharedsubtree.txt
If it's a first mount in a group, all group members should be
bind-mounted from this one.
Each mount propagates to all members of parent's group. The group can
contains a few slaves.
Mounts, which have propagated to slaves, are unmounted, because we can't
be sure, that they propagated in real life. For example:
mount --bind --make-slave /share /slave1
mount --bind --make-slave /share /slave2
mount /share/test
umount /slave2/test
mount --make-share /slave1/test
mount --bind --make-share /slave1/test /slave2/test
41 40 0:33 / /share rw,relatime shared:28 - tmpfs xxx rw
42 40 0:33 / /slave1 rw,relatime master:28 - tmpfs xxx rw
43 40 0:33 / /slave2 rw,relatime master:28 - tmpfs xxx rw
44 41 0:34 / /share/test rw,relatime shared:29 - tmpfs xxx rw
46 42 0:34 / /slave1/test rw,relatime shared:30 master:29 - tmpfs xxx rw
45 43 0:34 / /slave2/test rw,relatime shared:30 master:29 - tmpfs xxx rw
/slave1/test and /slave2/test depend on each other and minimum one of them
doesn't propagate from /share/test
v2: use false and true for bool
Signed-off-by: Andrey Vagin <avagin@openvz.org>
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
2013-08-29 18:54:00 +04:00
|
|
|
pr_debug("\t\tBind %s\n", t->mountpoint);
|
|
|
|
t->bind = mi;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int propagate_mount(struct mount_info *mi)
|
|
|
|
{
|
|
|
|
struct mount_info *t;
|
|
|
|
|
|
|
|
propagate_siblings(mi);
|
2014-03-20 17:30:41 +04:00
|
|
|
|
|
|
|
if (!mi->parent)
|
|
|
|
goto skip_parent;
|
|
|
|
|
mounts: find mounts, which are propagated from a current one (v2)
A few sentences, which are required for understanging this patch
2a) A shared mount can be replicated to as many mountpoints and all the
replicas continue to be exactly same.
2b) A slave mount is like a shared mount except that mount and umount
events only propagate towards it.
2c) A private mount does not forward or receive propagation.
All rules is there Documentation/filesystems/sharedsubtree.txt
If it's a first mount in a group, all group members should be
bind-mounted from this one.
Each mount propagates to all members of parent's group. The group can
contains a few slaves.
Mounts, which have propagated to slaves, are unmounted, because we can't
be sure, that they propagated in real life. For example:
mount --bind --make-slave /share /slave1
mount --bind --make-slave /share /slave2
mount /share/test
umount /slave2/test
mount --make-share /slave1/test
mount --bind --make-share /slave1/test /slave2/test
41 40 0:33 / /share rw,relatime shared:28 - tmpfs xxx rw
42 40 0:33 / /slave1 rw,relatime master:28 - tmpfs xxx rw
43 40 0:33 / /slave2 rw,relatime master:28 - tmpfs xxx rw
44 41 0:34 / /share/test rw,relatime shared:29 - tmpfs xxx rw
46 42 0:34 / /slave1/test rw,relatime shared:30 master:29 - tmpfs xxx rw
45 43 0:34 / /slave2/test rw,relatime shared:30 master:29 - tmpfs xxx rw
/slave1/test and /slave2/test depend on each other and minimum one of them
doesn't propagate from /share/test
v2: use false and true for bool
Signed-off-by: Andrey Vagin <avagin@openvz.org>
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
2013-08-29 18:54:00 +04:00
|
|
|
umount_from_slaves(mi);
|
|
|
|
|
|
|
|
/* Propagate this mount to everyone from a parent group */
|
|
|
|
|
|
|
|
list_for_each_entry(t, &mi->parent->mnt_share, mnt_share) {
|
|
|
|
struct mount_info *c;
|
|
|
|
|
|
|
|
list_for_each_entry(c, &t->children, siblings) {
|
|
|
|
if (mounts_equal(mi, c, false)) {
|
|
|
|
pr_debug("\t\tPropogate %s\n", c->mountpoint);
|
|
|
|
c->mounted = true;
|
|
|
|
propagate_siblings(c);
|
|
|
|
umount_from_slaves(c);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-20 17:30:41 +04:00
|
|
|
skip_parent:
|
2013-08-13 17:02:50 +04:00
|
|
|
/*
|
|
|
|
* FIXME Currently non-root mounts can be restored
|
|
|
|
* only if a proper root mount exists
|
|
|
|
*/
|
2014-04-10 14:52:33 +04:00
|
|
|
if (fsroot_mounted(mi) || mi->parent == NULL)
|
2013-08-13 17:02:50 +04:00
|
|
|
list_for_each_entry(t, &mi->mnt_bind, mnt_bind) {
|
2014-08-05 17:20:24 +04:00
|
|
|
if (t->mounted)
|
|
|
|
continue;
|
2013-08-13 17:02:50 +04:00
|
|
|
if (t->bind)
|
|
|
|
continue;
|
|
|
|
if (t->master_id)
|
|
|
|
continue;
|
|
|
|
t->bind = mi;
|
|
|
|
}
|
|
|
|
|
mounts: find mounts, which are propagated from a current one (v2)
A few sentences, which are required for understanging this patch
2a) A shared mount can be replicated to as many mountpoints and all the
replicas continue to be exactly same.
2b) A slave mount is like a shared mount except that mount and umount
events only propagate towards it.
2c) A private mount does not forward or receive propagation.
All rules is there Documentation/filesystems/sharedsubtree.txt
If it's a first mount in a group, all group members should be
bind-mounted from this one.
Each mount propagates to all members of parent's group. The group can
contains a few slaves.
Mounts, which have propagated to slaves, are unmounted, because we can't
be sure, that they propagated in real life. For example:
mount --bind --make-slave /share /slave1
mount --bind --make-slave /share /slave2
mount /share/test
umount /slave2/test
mount --make-share /slave1/test
mount --bind --make-share /slave1/test /slave2/test
41 40 0:33 / /share rw,relatime shared:28 - tmpfs xxx rw
42 40 0:33 / /slave1 rw,relatime master:28 - tmpfs xxx rw
43 40 0:33 / /slave2 rw,relatime master:28 - tmpfs xxx rw
44 41 0:34 / /share/test rw,relatime shared:29 - tmpfs xxx rw
46 42 0:34 / /slave1/test rw,relatime shared:30 master:29 - tmpfs xxx rw
45 43 0:34 / /slave2/test rw,relatime shared:30 master:29 - tmpfs xxx rw
/slave1/test and /slave2/test depend on each other and minimum one of them
doesn't propagate from /share/test
v2: use false and true for bool
Signed-off-by: Andrey Vagin <avagin@openvz.org>
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
2013-08-29 18:54:00 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-06-27 20:57:39 +04:00
|
|
|
static int do_new_mount(struct mount_info *mi)
|
|
|
|
{
|
|
|
|
char *src;
|
2012-08-09 16:27:30 +04:00
|
|
|
struct fstype *tp = mi->fstype;
|
2013-08-13 17:02:49 +04:00
|
|
|
struct mount_info *t;
|
2012-06-27 20:57:39 +04:00
|
|
|
|
|
|
|
src = resolve_source(mi);
|
|
|
|
if (!src)
|
|
|
|
return -1;
|
|
|
|
|
2013-08-13 17:02:49 +04:00
|
|
|
/*
|
|
|
|
* Wait while all parent are not mounted
|
|
|
|
*
|
|
|
|
* FIXME a child is shared only between parents,
|
|
|
|
* who was present in a moment of birth
|
|
|
|
*/
|
|
|
|
if (mi->parent->flags & MS_SHARED) {
|
|
|
|
list_for_each_entry(t, &mi->parent->mnt_share, mnt_share) {
|
|
|
|
if (!t->mounted) {
|
|
|
|
pr_debug("\t\tPostpone %s due to %s\n",
|
|
|
|
mi->mountpoint, t->mountpoint);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-09 16:27:30 +04:00
|
|
|
if (mount(src, mi->mountpoint, tp->name,
|
2013-08-13 17:02:49 +04:00
|
|
|
mi->flags & (~MS_SHARED), mi->options) < 0) {
|
2012-06-27 20:57:39 +04:00
|
|
|
pr_perror("Can't mount at %s", mi->mountpoint);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2013-08-13 17:02:48 +04:00
|
|
|
if (restore_shared_options(mi, 0, mi->shared_id, 0))
|
|
|
|
return -1;
|
|
|
|
|
2013-08-13 17:02:49 +04:00
|
|
|
mi->mounted = true;
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2013-12-25 16:54:53 +04:00
|
|
|
static int restore_ext_mount(struct mount_info *mi)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
pr_debug("Restoring external bind mount %s\n", mi->mountpoint);
|
2014-02-27 20:58:23 +04:00
|
|
|
ret = run_plugins(RESTORE_EXT_MOUNT, mi->mnt_id, mi->mountpoint, "/", NULL);
|
2013-12-25 16:54:53 +04:00
|
|
|
if (ret)
|
2014-08-20 14:52:00 +04:00
|
|
|
pr_err("Can't restore ext mount (%d)\n", ret);
|
2013-12-25 16:54:53 +04:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-06-27 20:57:39 +04:00
|
|
|
static int do_bind_mount(struct mount_info *mi)
|
|
|
|
{
|
2014-08-06 17:47:00 +04:00
|
|
|
bool shared = 0;
|
2013-08-13 17:02:49 +04:00
|
|
|
|
2013-12-25 16:54:53 +04:00
|
|
|
if (!mi->need_plugin) {
|
2014-06-09 17:26:59 +04:00
|
|
|
char *root, rpath[PATH_MAX];
|
2014-04-10 14:52:32 +04:00
|
|
|
int tok = 0;
|
|
|
|
|
2014-06-09 17:26:59 +04:00
|
|
|
if (mi->external) {
|
|
|
|
/*
|
|
|
|
* We have / pointing to criu's ns root still,
|
|
|
|
* so just use the mapping's path. The mountpoint
|
|
|
|
* is tuned in collect_mnt_from_image to refer
|
|
|
|
* to proper location in the namespace we restore.
|
|
|
|
*/
|
|
|
|
root = mi->root;
|
|
|
|
goto do_bind;
|
|
|
|
}
|
|
|
|
|
2014-08-06 17:47:00 +04:00
|
|
|
shared = mi->shared_id && mi->shared_id == mi->bind->shared_id;
|
|
|
|
|
2014-04-10 14:52:32 +04:00
|
|
|
/*
|
|
|
|
* Cut common part of root.
|
|
|
|
* For non-root binds the source is always "/" (checked)
|
|
|
|
* so this will result in this slash removal only.
|
|
|
|
*/
|
|
|
|
while (mi->root[tok] == mi->bind->root[tok]) {
|
|
|
|
tok++;
|
|
|
|
if (mi->bind->root[tok] == '\0')
|
|
|
|
break;
|
|
|
|
BUG_ON(mi->root[tok] == '\0');
|
|
|
|
}
|
|
|
|
|
|
|
|
snprintf(rpath, sizeof(rpath), "%s/%s",
|
|
|
|
mi->bind->mountpoint, mi->root + tok);
|
2014-06-09 17:26:59 +04:00
|
|
|
root = rpath;
|
|
|
|
do_bind:
|
|
|
|
pr_info("\tBind %s to %s\n", root, mi->mountpoint);
|
|
|
|
if (mount(root, mi->mountpoint, NULL,
|
2013-12-25 16:54:53 +04:00
|
|
|
MS_BIND, NULL) < 0) {
|
|
|
|
pr_perror("Can't mount at %s", mi->mountpoint);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (restore_ext_mount(mi))
|
|
|
|
return -1;
|
2013-08-13 17:02:49 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* shared - the mount is in the same shared group with mi->bind
|
|
|
|
* mi->shared_id && !shared - create a new shared group
|
|
|
|
*/
|
2013-12-30 12:30:18 +04:00
|
|
|
if (restore_shared_options(mi, !shared && !mi->master_id,
|
2013-08-13 17:02:49 +04:00
|
|
|
mi->shared_id && !shared,
|
|
|
|
mi->master_id))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
mi->mounted = true;
|
|
|
|
|
|
|
|
return 0;
|
2012-06-27 20:57:39 +04:00
|
|
|
}
|
|
|
|
|
2013-12-26 11:40:31 +04:00
|
|
|
static bool can_mount_now(struct mount_info *mi)
|
|
|
|
{
|
2014-03-20 17:30:41 +04:00
|
|
|
/* The root mount */
|
|
|
|
if (!mi->parent)
|
|
|
|
return true;
|
|
|
|
|
2013-12-26 11:40:31 +04:00
|
|
|
/*
|
|
|
|
* Private root mounts can be mounted at any time
|
|
|
|
*/
|
|
|
|
if (!mi->master_id && fsroot_mounted(mi))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Other mounts can be mounted only if they have
|
|
|
|
* the master mount (see propagate_mount) or if we
|
2014-08-08 15:51:00 +04:00
|
|
|
* expect a plugin/ext-mount-map to help us.
|
2013-12-26 11:40:31 +04:00
|
|
|
*/
|
2014-08-08 15:51:00 +04:00
|
|
|
if (mi->bind || mi->need_plugin || mi->external)
|
2013-12-26 11:40:31 +04:00
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2014-03-20 17:30:41 +04:00
|
|
|
static int do_mount_root(struct mount_info *mi)
|
|
|
|
{
|
|
|
|
if (restore_shared_options(mi, !mi->shared_id && !mi->master_id,
|
|
|
|
mi->shared_id, mi->master_id))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
mi->mounted = true;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-06-27 20:57:36 +04:00
|
|
|
static int do_mount_one(struct mount_info *mi)
|
|
|
|
{
|
mounts: find mounts, which are propagated from a current one (v2)
A few sentences, which are required for understanging this patch
2a) A shared mount can be replicated to as many mountpoints and all the
replicas continue to be exactly same.
2b) A slave mount is like a shared mount except that mount and umount
events only propagate towards it.
2c) A private mount does not forward or receive propagation.
All rules is there Documentation/filesystems/sharedsubtree.txt
If it's a first mount in a group, all group members should be
bind-mounted from this one.
Each mount propagates to all members of parent's group. The group can
contains a few slaves.
Mounts, which have propagated to slaves, are unmounted, because we can't
be sure, that they propagated in real life. For example:
mount --bind --make-slave /share /slave1
mount --bind --make-slave /share /slave2
mount /share/test
umount /slave2/test
mount --make-share /slave1/test
mount --bind --make-share /slave1/test /slave2/test
41 40 0:33 / /share rw,relatime shared:28 - tmpfs xxx rw
42 40 0:33 / /slave1 rw,relatime master:28 - tmpfs xxx rw
43 40 0:33 / /slave2 rw,relatime master:28 - tmpfs xxx rw
44 41 0:34 / /share/test rw,relatime shared:29 - tmpfs xxx rw
46 42 0:34 / /slave1/test rw,relatime shared:30 master:29 - tmpfs xxx rw
45 43 0:34 / /slave2/test rw,relatime shared:30 master:29 - tmpfs xxx rw
/slave1/test and /slave2/test depend on each other and minimum one of them
doesn't propagate from /share/test
v2: use false and true for bool
Signed-off-by: Andrey Vagin <avagin@openvz.org>
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
2013-08-29 18:54:00 +04:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (mi->mounted)
|
|
|
|
return 0;
|
|
|
|
|
2013-12-26 11:40:31 +04:00
|
|
|
if (!can_mount_now(mi)) {
|
2013-08-13 17:02:49 +04:00
|
|
|
pr_debug("Postpone slave %s\n", mi->mountpoint);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2013-12-25 16:54:53 +04:00
|
|
|
pr_debug("\tMounting %s @%s (%d)\n", mi->fstype->name, mi->mountpoint, mi->need_plugin);
|
2012-06-27 20:57:39 +04:00
|
|
|
|
2014-03-20 17:30:41 +04:00
|
|
|
if (!mi->parent)
|
|
|
|
ret = do_mount_root(mi);
|
2014-08-08 15:51:00 +04:00
|
|
|
else if (!mi->bind && !mi->need_plugin && !mi->external)
|
mounts: find mounts, which are propagated from a current one (v2)
A few sentences, which are required for understanging this patch
2a) A shared mount can be replicated to as many mountpoints and all the
replicas continue to be exactly same.
2b) A slave mount is like a shared mount except that mount and umount
events only propagate towards it.
2c) A private mount does not forward or receive propagation.
All rules is there Documentation/filesystems/sharedsubtree.txt
If it's a first mount in a group, all group members should be
bind-mounted from this one.
Each mount propagates to all members of parent's group. The group can
contains a few slaves.
Mounts, which have propagated to slaves, are unmounted, because we can't
be sure, that they propagated in real life. For example:
mount --bind --make-slave /share /slave1
mount --bind --make-slave /share /slave2
mount /share/test
umount /slave2/test
mount --make-share /slave1/test
mount --bind --make-share /slave1/test /slave2/test
41 40 0:33 / /share rw,relatime shared:28 - tmpfs xxx rw
42 40 0:33 / /slave1 rw,relatime master:28 - tmpfs xxx rw
43 40 0:33 / /slave2 rw,relatime master:28 - tmpfs xxx rw
44 41 0:34 / /share/test rw,relatime shared:29 - tmpfs xxx rw
46 42 0:34 / /slave1/test rw,relatime shared:30 master:29 - tmpfs xxx rw
45 43 0:34 / /slave2/test rw,relatime shared:30 master:29 - tmpfs xxx rw
/slave1/test and /slave2/test depend on each other and minimum one of them
doesn't propagate from /share/test
v2: use false and true for bool
Signed-off-by: Andrey Vagin <avagin@openvz.org>
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
2013-08-29 18:54:00 +04:00
|
|
|
ret = do_new_mount(mi);
|
2012-06-27 20:57:39 +04:00
|
|
|
else
|
mounts: find mounts, which are propagated from a current one (v2)
A few sentences, which are required for understanging this patch
2a) A shared mount can be replicated to as many mountpoints and all the
replicas continue to be exactly same.
2b) A slave mount is like a shared mount except that mount and umount
events only propagate towards it.
2c) A private mount does not forward or receive propagation.
All rules is there Documentation/filesystems/sharedsubtree.txt
If it's a first mount in a group, all group members should be
bind-mounted from this one.
Each mount propagates to all members of parent's group. The group can
contains a few slaves.
Mounts, which have propagated to slaves, are unmounted, because we can't
be sure, that they propagated in real life. For example:
mount --bind --make-slave /share /slave1
mount --bind --make-slave /share /slave2
mount /share/test
umount /slave2/test
mount --make-share /slave1/test
mount --bind --make-share /slave1/test /slave2/test
41 40 0:33 / /share rw,relatime shared:28 - tmpfs xxx rw
42 40 0:33 / /slave1 rw,relatime master:28 - tmpfs xxx rw
43 40 0:33 / /slave2 rw,relatime master:28 - tmpfs xxx rw
44 41 0:34 / /share/test rw,relatime shared:29 - tmpfs xxx rw
46 42 0:34 / /slave1/test rw,relatime shared:30 master:29 - tmpfs xxx rw
45 43 0:34 / /slave2/test rw,relatime shared:30 master:29 - tmpfs xxx rw
/slave1/test and /slave2/test depend on each other and minimum one of them
doesn't propagate from /share/test
v2: use false and true for bool
Signed-off-by: Andrey Vagin <avagin@openvz.org>
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
2013-08-29 18:54:00 +04:00
|
|
|
ret = do_bind_mount(mi);
|
|
|
|
|
|
|
|
if (ret == 0 && propagate_mount(mi))
|
|
|
|
return -1;
|
|
|
|
|
2014-04-17 15:04:00 +04:00
|
|
|
if (mi->fstype->code == FSTYPE__UNSUPPORTED) {
|
|
|
|
struct statfs st;
|
|
|
|
|
|
|
|
if (statfs(mi->mountpoint, &st)) {
|
|
|
|
pr_perror("Unable to statfs %s", mi->mountpoint);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (st.f_type == BTRFS_SUPER_MAGIC)
|
|
|
|
mi->fstype = find_fstype_by_name("btrfs");
|
|
|
|
}
|
|
|
|
|
mounts: find mounts, which are propagated from a current one (v2)
A few sentences, which are required for understanging this patch
2a) A shared mount can be replicated to as many mountpoints and all the
replicas continue to be exactly same.
2b) A slave mount is like a shared mount except that mount and umount
events only propagate towards it.
2c) A private mount does not forward or receive propagation.
All rules is there Documentation/filesystems/sharedsubtree.txt
If it's a first mount in a group, all group members should be
bind-mounted from this one.
Each mount propagates to all members of parent's group. The group can
contains a few slaves.
Mounts, which have propagated to slaves, are unmounted, because we can't
be sure, that they propagated in real life. For example:
mount --bind --make-slave /share /slave1
mount --bind --make-slave /share /slave2
mount /share/test
umount /slave2/test
mount --make-share /slave1/test
mount --bind --make-share /slave1/test /slave2/test
41 40 0:33 / /share rw,relatime shared:28 - tmpfs xxx rw
42 40 0:33 / /slave1 rw,relatime master:28 - tmpfs xxx rw
43 40 0:33 / /slave2 rw,relatime master:28 - tmpfs xxx rw
44 41 0:34 / /share/test rw,relatime shared:29 - tmpfs xxx rw
46 42 0:34 / /slave1/test rw,relatime shared:30 master:29 - tmpfs xxx rw
45 43 0:34 / /slave2/test rw,relatime shared:30 master:29 - tmpfs xxx rw
/slave1/test and /slave2/test depend on each other and minimum one of them
doesn't propagate from /share/test
v2: use false and true for bool
Signed-off-by: Andrey Vagin <avagin@openvz.org>
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
2013-08-29 18:54:00 +04:00
|
|
|
return ret;
|
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;
|
|
|
|
}
|
|
|
|
|
2014-04-21 18:23:40 +04:00
|
|
|
static int clean_mnt_ns(struct mount_info *mntinfo_tree)
|
2012-06-27 20:57:36 +04:00
|
|
|
{
|
|
|
|
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-12-12 16:19:48 +04:00
|
|
|
return mnt_tree_for_each_reverse(mntinfo_tree, do_umount_one);
|
2012-06-27 20:57:36 +04:00
|
|
|
}
|
|
|
|
|
2014-04-21 18:23:13 +04:00
|
|
|
static int cr_pivot_root(char *root)
|
2012-06-27 20:57:36 +04:00
|
|
|
{
|
2014-10-05 02:41:00 +04:00
|
|
|
int old_root;
|
2012-08-02 16:07:43 +04:00
|
|
|
|
2014-04-21 18:23:13 +04:00
|
|
|
pr_info("Move the root to %s\n", root ? : ".");
|
|
|
|
|
|
|
|
if (root) {
|
|
|
|
if (chdir(root)) {
|
|
|
|
pr_perror("chdir(%s) failed", root);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
2012-09-28 14:13:18 +04:00
|
|
|
|
2014-10-05 02:41:00 +04:00
|
|
|
old_root = open("/", O_DIRECTORY | O_RDONLY);
|
|
|
|
if (old_root < 0) {
|
|
|
|
pr_perror("Unable to open /");
|
2012-09-28 14:13:20 +04:00
|
|
|
return -1;
|
|
|
|
}
|
2013-03-26 14:16:50 +04:00
|
|
|
|
2014-10-05 02:41:00 +04:00
|
|
|
if (pivot_root(".", ".")) {
|
|
|
|
close(old_root);
|
|
|
|
pr_perror("pivot_root(., .) failed");
|
2012-09-28 14:13:20 +04:00
|
|
|
return -1;
|
|
|
|
}
|
2013-12-25 16:54:53 +04:00
|
|
|
|
2014-10-05 02:41:00 +04:00
|
|
|
if (fchdir(old_root)) {
|
|
|
|
perror("Unable to change working directory");
|
2014-03-20 16:45:46 +04:00
|
|
|
return -1;
|
|
|
|
}
|
2014-10-05 02:41:00 +04:00
|
|
|
close(old_root);
|
2014-03-20 16:45:46 +04:00
|
|
|
|
2014-10-05 02:41:00 +04:00
|
|
|
if (mount("none", ".", "none", MS_REC|MS_PRIVATE, NULL)) {
|
2014-04-21 18:23:13 +04:00
|
|
|
pr_perror("Can't remount root with MS_PRIVATE");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-10-05 02:41:00 +04:00
|
|
|
if (umount2(".", MNT_DETACH)) {
|
|
|
|
pr_perror("Can't umount the old root");
|
2012-09-28 14:13:20 +04:00
|
|
|
return -1;
|
|
|
|
}
|
2014-10-05 02:41:00 +04:00
|
|
|
|
|
|
|
if (chdir("/")) {
|
|
|
|
perror("Unable to change working directory");
|
2012-09-28 14:13:20 +04:00
|
|
|
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);
|
mounts: find mounts, which are propagated from a current one (v2)
A few sentences, which are required for understanging this patch
2a) A shared mount can be replicated to as many mountpoints and all the
replicas continue to be exactly same.
2b) A slave mount is like a shared mount except that mount and umount
events only propagate towards it.
2c) A private mount does not forward or receive propagation.
All rules is there Documentation/filesystems/sharedsubtree.txt
If it's a first mount in a group, all group members should be
bind-mounted from this one.
Each mount propagates to all members of parent's group. The group can
contains a few slaves.
Mounts, which have propagated to slaves, are unmounted, because we can't
be sure, that they propagated in real life. For example:
mount --bind --make-slave /share /slave1
mount --bind --make-slave /share /slave2
mount /share/test
umount /slave2/test
mount --make-share /slave1/test
mount --bind --make-share /slave1/test /slave2/test
41 40 0:33 / /share rw,relatime shared:28 - tmpfs xxx rw
42 40 0:33 / /slave1 rw,relatime master:28 - tmpfs xxx rw
43 40 0:33 / /slave2 rw,relatime master:28 - tmpfs xxx rw
44 41 0:34 / /share/test rw,relatime shared:29 - tmpfs xxx rw
46 42 0:34 / /slave1/test rw,relatime shared:30 master:29 - tmpfs xxx rw
45 43 0:34 / /slave2/test rw,relatime shared:30 master:29 - tmpfs xxx rw
/slave1/test and /slave2/test depend on each other and minimum one of them
doesn't propagate from /share/test
v2: use false and true for bool
Signed-off-by: Andrey Vagin <avagin@openvz.org>
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
2013-08-29 18:54:00 +04:00
|
|
|
INIT_LIST_HEAD(&new->mnt_bind);
|
2013-08-29 18:52:43 +04:00
|
|
|
INIT_LIST_HEAD(&new->postpone);
|
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);
|
|
|
|
}
|
|
|
|
|
2014-04-21 18:23:19 +04:00
|
|
|
/*
|
|
|
|
* mnt_roots is a temporary directory for restoring sub-trees of
|
|
|
|
* non-root namespaces.
|
|
|
|
*/
|
|
|
|
static char *mnt_roots;
|
|
|
|
|
2014-04-22 20:38:00 +04:00
|
|
|
/*
|
|
|
|
* Helper for getting a path to where the namespace's root
|
|
|
|
* is re-constructed.
|
|
|
|
*/
|
|
|
|
static inline int print_ns_root(struct ns_id *ns, char *buf, int bs)
|
|
|
|
{
|
|
|
|
return snprintf(buf, bs, "%s/%d/", mnt_roots, ns->id);
|
|
|
|
}
|
|
|
|
|
2014-04-22 20:37:45 +04:00
|
|
|
static int create_mnt_roots(void)
|
2014-04-21 18:23:19 +04:00
|
|
|
{
|
|
|
|
if (mnt_roots)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (chdir(opts.root ? : "/")) {
|
|
|
|
pr_perror("Unable to change working directory on %s", opts.root);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
mnt_roots = strdup(".criu.mntns.XXXXXX");
|
|
|
|
if (mnt_roots == NULL) {
|
|
|
|
pr_perror("Can't allocate memory");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mkdtemp(mnt_roots) == NULL) {
|
|
|
|
pr_perror("Unable to create a temporary directory");
|
|
|
|
mnt_roots = NULL;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-04-23 01:49:34 +04:00
|
|
|
static int rst_collect_local_mntns(void)
|
2014-04-21 18:23:35 +04:00
|
|
|
{
|
|
|
|
struct ns_id *nsid;
|
|
|
|
|
2014-04-23 13:22:12 +04:00
|
|
|
nsid = rst_new_ns_id(0, getpid(), &mnt_ns_desc);
|
|
|
|
if (!nsid)
|
2014-04-21 18:23:35 +04:00
|
|
|
return -1;
|
|
|
|
|
2014-04-23 01:49:34 +04:00
|
|
|
mntinfo = collect_mntinfo(nsid);
|
2014-04-23 13:22:12 +04:00
|
|
|
if (!mntinfo)
|
2014-04-23 01:49:34 +04:00
|
|
|
return -1;
|
|
|
|
|
2014-04-23 13:22:12 +04:00
|
|
|
futex_set(&nsid->created, 1);
|
2014-04-21 18:23:35 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-04-21 18:23:18 +04:00
|
|
|
static int collect_mnt_from_image(struct mount_info **pms, struct ns_id *nsid)
|
2012-09-28 14:13:18 +04:00
|
|
|
{
|
|
|
|
MntEntry *me = NULL;
|
2014-09-29 12:48:53 +04:00
|
|
|
int ret, root_len = 1;
|
|
|
|
struct cr_img *img;
|
2014-04-23 17:40:39 +04:00
|
|
|
char root[PATH_MAX] = ".";
|
2012-08-02 16:07:43 +04:00
|
|
|
|
2014-04-21 18:23:18 +04:00
|
|
|
img = open_image(CR_FD_MNTS, O_RSTR, nsid->id);
|
2012-06-27 20:57:36 +04:00
|
|
|
if (img < 0)
|
2014-04-21 18:23:18 +04:00
|
|
|
return -1;
|
2012-06-27 20:57:36 +04:00
|
|
|
|
2014-04-23 17:40:39 +04:00
|
|
|
if (nsid->id != root_item->ids->mnt_ns_id)
|
|
|
|
root_len = print_ns_root(nsid, root, sizeof(root));
|
|
|
|
|
2012-06-27 20:57:36 +04:00
|
|
|
pr_debug("Reading mountpoint images\n");
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
struct mount_info *pm;
|
2014-04-23 17:40:39 +04:00
|
|
|
int len;
|
2012-06-27 20:57:36 +04:00
|
|
|
|
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
|
|
|
|
2014-04-21 18:23:36 +04:00
|
|
|
pm->nsid = nsid;
|
2014-04-21 18:23:18 +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;
|
2013-08-13 17:02:45 +04:00
|
|
|
pm->shared_id = me->shared_id;
|
|
|
|
pm->master_id = me->master_id;
|
2013-12-25 16:54:53 +04:00
|
|
|
pm->need_plugin = me->with_plugin;
|
2014-04-21 18:23:15 +04:00
|
|
|
pm->is_ns_root = is_root(me->mountpoint);
|
2012-07-17 14:23:37 +04:00
|
|
|
|
|
|
|
/* FIXME: abort unsupported early */
|
|
|
|
pm->fstype = decode_fstype(me->fstype);
|
2012-06-27 20:57:36 +04:00
|
|
|
|
2014-06-09 17:26:59 +04:00
|
|
|
if (me->ext_mount) {
|
|
|
|
struct ext_mount *em;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* External mount point -- get the reverse mapping
|
|
|
|
* from the command line and put into root's place
|
|
|
|
*/
|
|
|
|
|
|
|
|
em = ext_mount_lookup(me->root);
|
|
|
|
if (!em) {
|
|
|
|
pr_err("No mapping for %s mountpoint\n", me->mountpoint);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
pm->external = em;
|
|
|
|
pm->root = em->val;
|
|
|
|
pr_debug("Mountpoint %s will have root from %s\n",
|
|
|
|
me->mountpoint, pm->root);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
pr_debug("\t\tGetting root for %d\n", pm->mnt_id);
|
|
|
|
pm->root = xstrdup(me->root);
|
|
|
|
if (!pm->root)
|
|
|
|
goto err;
|
|
|
|
}
|
2012-06-27 20:57:36 +04:00
|
|
|
|
2014-04-21 18:23:19 +04:00
|
|
|
len = strlen(me->mountpoint) + root_len + 1;
|
2014-02-28 18:39:50 +04:00
|
|
|
pm->mountpoint = xmalloc(len);
|
2012-07-17 14:23:37 +04:00
|
|
|
if (!pm->mountpoint)
|
2012-10-24 16:51:50 +04:00
|
|
|
goto err;
|
2014-08-05 17:20:25 +04:00
|
|
|
pm->ns_mountpoint = pm->mountpoint + root_len;
|
2014-02-28 18:39:50 +04:00
|
|
|
/*
|
|
|
|
* For bind-mounts we would also fix the root here
|
|
|
|
* too, but bind-mounts restore merges mountpoint
|
|
|
|
* and root paths together, so there's no need in
|
|
|
|
* that.
|
|
|
|
*/
|
|
|
|
|
2014-04-21 18:23:19 +04:00
|
|
|
strcpy(pm->mountpoint, root);
|
|
|
|
strcpy(pm->mountpoint + root_len, me->mountpoint);
|
|
|
|
|
|
|
|
pr_debug("\t\tGetting mpt for %d %s\n", pm->mnt_id, pm->mountpoint);
|
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);
|
|
|
|
|
2014-09-29 12:48:53 +04:00
|
|
|
close_image(img);
|
2013-08-13 17:02:51 +04:00
|
|
|
|
2014-04-21 18:23:18 +04:00
|
|
|
return 0;
|
2012-10-24 16:51:50 +04:00
|
|
|
err:
|
2014-09-29 12:48:53 +04:00
|
|
|
close_image(img);
|
2014-04-21 18:23:18 +04:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-04-22 20:35:26 +04:00
|
|
|
static struct mount_info *read_mnt_ns_img(void)
|
2014-04-21 18:23:18 +04:00
|
|
|
{
|
|
|
|
struct mount_info *pms = NULL;
|
|
|
|
struct ns_id *nsid;
|
|
|
|
|
2014-04-22 20:36:03 +04:00
|
|
|
for (nsid = ns_ids; nsid != NULL; nsid = nsid->next) {
|
|
|
|
if (nsid->nd != &mnt_ns_desc)
|
2014-04-21 18:23:18 +04:00
|
|
|
continue;
|
|
|
|
|
2014-04-21 18:23:19 +04:00
|
|
|
if (nsid->id != root_item->ids->mnt_ns_id)
|
2014-04-23 02:47:42 +04:00
|
|
|
/*
|
|
|
|
* If we have more than one (root) namespace,
|
|
|
|
* then we'll need the roots yard.
|
|
|
|
*/
|
2014-04-22 20:37:45 +04:00
|
|
|
if (create_mnt_roots())
|
2014-04-21 18:23:19 +04:00
|
|
|
return NULL;
|
|
|
|
|
2014-04-21 18:23:18 +04:00
|
|
|
if (collect_mnt_from_image(&pms, nsid))
|
2014-04-22 20:36:03 +04:00
|
|
|
return NULL;
|
2014-04-21 18:23:18 +04:00
|
|
|
}
|
2014-04-21 18:23:45 +04:00
|
|
|
|
2014-04-22 20:36:03 +04:00
|
|
|
/* Here it doesn't matter where the mount list is saved */
|
2014-04-21 18:23:45 +04:00
|
|
|
mntinfo = pms;
|
2014-04-21 18:23:18 +04:00
|
|
|
return pms;
|
2012-06-27 20:57:36 +04:00
|
|
|
}
|
|
|
|
|
2014-04-21 18:23:39 +04:00
|
|
|
char *rst_get_mnt_root(int mnt_id)
|
|
|
|
{
|
|
|
|
struct mount_info *m;
|
|
|
|
static char path[PATH_MAX] = "/";
|
|
|
|
|
|
|
|
if (!(root_ns_mask & CLONE_NEWNS))
|
|
|
|
return path;
|
|
|
|
|
2014-04-21 18:23:42 +04:00
|
|
|
if (mnt_id == -1)
|
|
|
|
return path;
|
|
|
|
|
2014-04-21 18:23:39 +04:00
|
|
|
m = lookup_mnt_id(mnt_id);
|
|
|
|
if (m == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (m->nsid->pid == getpid())
|
|
|
|
return path;
|
|
|
|
|
2014-04-22 20:38:00 +04:00
|
|
|
print_ns_root(m->nsid, path, sizeof(path));
|
2014-04-21 18:23:39 +04:00
|
|
|
return path;
|
|
|
|
}
|
|
|
|
|
2014-04-22 20:40:29 +04:00
|
|
|
static int do_restore_task_mnt_ns(struct ns_id *nsid)
|
2014-04-21 18:23:20 +04:00
|
|
|
{
|
|
|
|
char path[PATH_MAX];
|
|
|
|
|
|
|
|
if (nsid->pid != getpid()) {
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
futex_wait_while_eq(&nsid->created, 0);
|
|
|
|
fd = open_proc(nsid->pid, "ns/mnt");
|
|
|
|
if (fd < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (setns(fd, CLONE_NEWNS)) {
|
|
|
|
pr_perror("Unable to change mount namespace");
|
|
|
|
return -1;
|
|
|
|
}
|
2014-04-22 20:39:43 +04:00
|
|
|
|
|
|
|
close(fd);
|
2014-04-21 18:23:20 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (unshare(CLONE_NEWNS)) {
|
|
|
|
pr_perror("Unable to unshare mount namespace");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-04-22 20:38:00 +04:00
|
|
|
print_ns_root(nsid, path, sizeof(path));
|
2014-04-21 18:23:20 +04:00
|
|
|
if (cr_pivot_root(path))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
futex_set_and_wake(&nsid->created, 1);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-04-22 20:40:06 +04:00
|
|
|
int restore_task_mnt_ns(struct pstree_item *current)
|
|
|
|
{
|
|
|
|
if (current->ids && current->ids->has_mnt_ns_id) {
|
2014-04-22 20:40:29 +04:00
|
|
|
unsigned int id = current->ids->mnt_ns_id;
|
2014-04-22 20:40:06 +04:00
|
|
|
struct ns_id *nsid;
|
|
|
|
|
2014-04-22 20:40:29 +04:00
|
|
|
if (root_item->ids->mnt_ns_id == id)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
nsid = lookup_ns_by_id(id, &mnt_ns_desc);
|
2014-04-22 20:40:06 +04:00
|
|
|
if (nsid == NULL) {
|
2014-04-22 20:40:29 +04:00
|
|
|
pr_err("Can't find mount namespace %d\n", id);
|
2014-04-22 20:40:06 +04:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-04-22 20:40:29 +04:00
|
|
|
if (do_restore_task_mnt_ns(nsid))
|
2014-04-22 20:40:06 +04:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-04-21 18:23:19 +04:00
|
|
|
/*
|
|
|
|
* All nested mount namespaces are restore as sub-trees of the root namespace.
|
|
|
|
*/
|
|
|
|
static int prepare_roots_yard(void)
|
|
|
|
{
|
|
|
|
char path[PATH_MAX];
|
|
|
|
struct ns_id *nsid;
|
|
|
|
|
|
|
|
if (mnt_roots == NULL)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (mount("none", mnt_roots, "tmpfs", 0, NULL)) {
|
|
|
|
pr_perror("Unable to mount tmpfs in %s", mnt_roots);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (mount("none", mnt_roots, NULL, MS_PRIVATE, NULL))
|
|
|
|
return -1;
|
|
|
|
|
2014-04-23 02:35:04 +04:00
|
|
|
for (nsid = ns_ids; nsid != NULL; nsid = nsid->next) {
|
|
|
|
if (nsid->nd != &mnt_ns_desc)
|
2014-04-21 18:23:19 +04:00
|
|
|
continue;
|
|
|
|
|
2014-04-22 20:38:00 +04:00
|
|
|
print_ns_root(nsid, path, sizeof(path));
|
2014-04-21 18:23:19 +04:00
|
|
|
if (mkdir(path, 0600)) {
|
|
|
|
pr_perror("Unable to create %s", path);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-04-22 20:35:43 +04:00
|
|
|
static int populate_mnt_ns(struct mount_info *mis)
|
2013-12-25 16:53:27 +04:00
|
|
|
{
|
|
|
|
struct mount_info *pms;
|
2014-04-21 18:23:44 +04:00
|
|
|
struct ns_id *nsid;
|
2013-12-25 16:53:27 +04:00
|
|
|
|
2014-04-21 18:23:19 +04:00
|
|
|
if (prepare_roots_yard())
|
|
|
|
return -1;
|
|
|
|
|
2014-04-21 18:23:45 +04:00
|
|
|
pms = mnt_build_tree(mis);
|
2013-12-25 16:53:27 +04:00
|
|
|
if (!pms)
|
|
|
|
return -1;
|
|
|
|
|
2014-08-03 22:31:16 +04:00
|
|
|
if (collect_shared(mis))
|
|
|
|
return -1;
|
|
|
|
|
2014-04-21 18:23:44 +04:00
|
|
|
for (nsid = ns_ids; nsid; nsid = nsid->next) {
|
|
|
|
if (nsid->nd != &mnt_ns_desc)
|
|
|
|
continue;
|
|
|
|
|
2014-04-23 02:47:42 +04:00
|
|
|
/*
|
|
|
|
* Make trees of all namespaces look the
|
|
|
|
* same, so that manual paths resolution
|
|
|
|
* works on them.
|
|
|
|
*/
|
2014-04-21 18:23:44 +04:00
|
|
|
nsid->mnt.mntinfo_tree = pms;
|
|
|
|
}
|
|
|
|
|
2014-04-21 18:23:43 +04:00
|
|
|
if (validate_mounts(mis, false))
|
2013-12-25 16:53:27 +04:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
return mnt_tree_for_each(pms, do_mount_one);
|
|
|
|
}
|
|
|
|
|
2014-04-22 20:38:16 +04:00
|
|
|
int fini_mnt_ns(void)
|
2014-04-21 18:23:19 +04:00
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
if (mnt_roots == NULL)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (mount("none", mnt_roots, "none", MS_REC|MS_PRIVATE, NULL)) {
|
|
|
|
pr_perror("Can't remount root with MS_PRIVATE");
|
|
|
|
ret = 1;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Don't exit after a first error, becuase this function
|
|
|
|
* can be used to rollback in a error case.
|
|
|
|
* Don't worry about MNT_DETACH, because files are restored after this
|
|
|
|
* and nobody will not be restored from a wrong mount namespace.
|
|
|
|
*/
|
|
|
|
if (umount2(mnt_roots, MNT_DETACH)) {
|
|
|
|
pr_perror("Can't unmount %s", mnt_roots);
|
|
|
|
ret = 1;
|
|
|
|
}
|
|
|
|
if (rmdir(mnt_roots)) {
|
|
|
|
pr_perror("Can't remove the directory %s", mnt_roots);
|
|
|
|
ret = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-04-22 20:35:43 +04:00
|
|
|
int prepare_mnt_ns(void)
|
2012-06-27 20:57:36 +04:00
|
|
|
{
|
2013-12-25 16:54:03 +04:00
|
|
|
int ret = -1;
|
2014-04-21 18:23:40 +04:00
|
|
|
struct mount_info *mis, *old;
|
2014-08-12 14:35:25 +04:00
|
|
|
struct ns_id ns = { .pid = PROC_SELF, .nd = &mnt_ns_desc };
|
2012-06-27 20:57:36 +04:00
|
|
|
|
2014-04-22 20:39:28 +04:00
|
|
|
if (!(root_ns_mask & CLONE_NEWNS))
|
2014-04-23 01:49:34 +04:00
|
|
|
return rst_collect_local_mntns();
|
2014-04-22 20:39:28 +04:00
|
|
|
|
|
|
|
pr_info("Restoring mount namespace\n");
|
|
|
|
|
2014-04-23 01:49:34 +04:00
|
|
|
old = collect_mntinfo(&ns);
|
|
|
|
if (old == NULL)
|
|
|
|
return -1;
|
|
|
|
|
2013-01-11 18:16:18 +04:00
|
|
|
close_proc();
|
|
|
|
|
2014-04-22 20:35:26 +04:00
|
|
|
mis = read_mnt_ns_img();
|
2013-12-25 16:54:03 +04:00
|
|
|
if (!mis)
|
|
|
|
goto out;
|
|
|
|
|
2014-02-28 18:39:51 +04:00
|
|
|
if (chdir(opts.root ? : "/")) {
|
|
|
|
pr_perror("chdir(%s) failed", opts.root ? : "/");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
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.
|
|
|
|
*/
|
2014-03-20 16:45:46 +04:00
|
|
|
if (!opts.root) {
|
2014-04-21 18:23:40 +04:00
|
|
|
if (clean_mnt_ns(ns.mnt.mntinfo_tree))
|
2014-03-20 16:45:46 +04:00
|
|
|
return -1;
|
|
|
|
} else {
|
|
|
|
struct mount_info *mi;
|
|
|
|
|
|
|
|
/* moving a mount residing under a shared mount is invalid. */
|
2014-04-21 18:23:45 +04:00
|
|
|
mi = mount_resolve_path(ns.mnt.mntinfo_tree, opts.root);
|
2014-03-20 16:45:46 +04:00
|
|
|
if (mi == NULL) {
|
|
|
|
pr_err("Unable to find mount point for %s\n", opts.root);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (mi->parent == NULL) {
|
|
|
|
pr_err("New root and old root are the same\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Our root is mounted over the parent (in the same directory) */
|
|
|
|
if (!strcmp(mi->parent->mountpoint, mi->mountpoint)) {
|
|
|
|
pr_err("The parent of the new root is unreachable\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-04-16 14:48:00 +04:00
|
|
|
if (mount("none", mi->parent->mountpoint + 1, "none", MS_SLAVE, NULL)) {
|
2014-03-20 16:45:46 +04:00
|
|
|
pr_perror("Can't remount the parent of the new root with MS_SLAVE");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
2012-09-28 14:13:18 +04:00
|
|
|
|
2014-04-21 18:23:40 +04:00
|
|
|
free_mntinfo(old);
|
2013-12-12 16:11:18 +04:00
|
|
|
|
2014-04-22 20:35:43 +04:00
|
|
|
ret = populate_mnt_ns(mis);
|
2014-02-28 18:39:51 +04:00
|
|
|
if (ret)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (opts.root)
|
2014-04-21 18:23:13 +04:00
|
|
|
ret = cr_pivot_root(NULL);
|
2013-12-25 16:54:03 +04:00
|
|
|
out:
|
2012-06-27 20:57:36 +04:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-04-23 02:31:16 +04:00
|
|
|
int __mntns_get_root_fd(pid_t pid)
|
2012-08-01 07:00:48 +04:00
|
|
|
{
|
2014-04-21 18:23:46 +04:00
|
|
|
static int mntns_root_pid = -1;
|
|
|
|
|
2012-08-01 07:00:48 +04:00
|
|
|
int fd, pfd;
|
|
|
|
int ret;
|
|
|
|
char path[PATH_MAX + 1];
|
|
|
|
|
2014-04-21 18:23:46 +04:00
|
|
|
if (mntns_root_pid == pid) /* The required root is already opened */
|
|
|
|
return get_service_fd(ROOT_FD_OFF);
|
|
|
|
|
2014-04-21 18:23:11 +04:00
|
|
|
close_service_fd(ROOT_FD_OFF);
|
|
|
|
|
2014-04-21 18:23:22 +04:00
|
|
|
if (!(root_ns_mask & CLONE_NEWNS)) {
|
2013-09-27 21:20:41 +04:00
|
|
|
/*
|
|
|
|
* If criu and tasks we dump live in the same mount
|
|
|
|
* namespace, we can just open the root directory.
|
|
|
|
* All paths resolution would occur relative to criu's
|
|
|
|
* root. Even if it is not namespace's root, provided
|
|
|
|
* file paths are resolved, we'd get consistent dump.
|
|
|
|
*/
|
|
|
|
fd = open("/", O_RDONLY | O_DIRECTORY);
|
|
|
|
if (fd < 0) {
|
2014-08-20 14:52:00 +04:00
|
|
|
pr_perror("Can't open root");
|
2013-09-27 21:20:41 +04:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
goto set_root;
|
|
|
|
}
|
|
|
|
|
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);
|
2014-01-14 09:33:19 +04:00
|
|
|
if (ret < 0) {
|
2013-05-16 21:00:43 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2013-09-27 21:20:41 +04:00
|
|
|
set_root:
|
2014-04-16 09:04:24 +04:00
|
|
|
ret = install_service_fd(ROOT_FD_OFF, fd);
|
2014-04-21 18:23:46 +04:00
|
|
|
if (ret >= 0)
|
|
|
|
mntns_root_pid = pid;
|
2014-04-16 09:04:24 +04:00
|
|
|
close(fd);
|
2014-04-21 18:23:31 +04:00
|
|
|
return ret;
|
2012-08-01 07:00:48 +04:00
|
|
|
}
|
2013-01-15 23:24:01 +04:00
|
|
|
|
2014-04-23 02:31:16 +04:00
|
|
|
int mntns_get_root_fd(struct ns_id *mntns)
|
|
|
|
{
|
|
|
|
return __mntns_get_root_fd(mntns->pid);
|
|
|
|
}
|
|
|
|
|
2014-04-21 18:23:42 +04:00
|
|
|
struct ns_id *lookup_nsid_by_mnt_id(int mnt_id)
|
|
|
|
{
|
|
|
|
struct mount_info *mi;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Kernel before 3.15 doesn't show mnt_id for file descriptors.
|
|
|
|
* mnt_id isn't saved for files, if mntns isn't dumped.
|
|
|
|
* In both these cases we have only one root, so here
|
|
|
|
* is not matter which mount will be restured.
|
|
|
|
*/
|
|
|
|
if (mnt_id == -1)
|
|
|
|
mi = mntinfo;
|
|
|
|
else
|
|
|
|
mi = lookup_mnt_id(mnt_id);
|
|
|
|
|
|
|
|
if (mi == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return mi->nsid;
|
|
|
|
}
|
|
|
|
|
2014-08-03 22:31:20 +04:00
|
|
|
int mntns_get_root_by_mnt_id(int mnt_id)
|
|
|
|
{
|
|
|
|
struct ns_id *mntns;
|
|
|
|
|
|
|
|
mntns = lookup_nsid_by_mnt_id(mnt_id);
|
|
|
|
BUG_ON(mntns == NULL);
|
|
|
|
|
|
|
|
return mntns_get_root_fd(mntns);
|
|
|
|
}
|
|
|
|
|
2014-10-09 17:43:19 +04:00
|
|
|
int collect_mnt_namespaces(void)
|
2014-04-21 18:23:34 +04:00
|
|
|
{
|
2014-04-22 20:35:07 +04:00
|
|
|
struct mount_info *pms;
|
2014-04-21 18:23:34 +04:00
|
|
|
struct ns_id *ns;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
for (ns = ns_ids; ns; ns = ns->next) {
|
2014-04-22 20:34:45 +04:00
|
|
|
if (!(ns->nd->cflag & CLONE_NEWNS))
|
|
|
|
continue;
|
|
|
|
|
2014-04-21 18:23:34 +04:00
|
|
|
if (ns->pid == getpid()) {
|
2014-04-23 13:10:21 +04:00
|
|
|
/*
|
|
|
|
* Collect criu's mounts only if the target
|
|
|
|
* task does NOT live in mount namespaces to
|
|
|
|
* make smart paths resolution work.
|
|
|
|
*
|
|
|
|
* Otherwise, the necessary list of mounts
|
|
|
|
* will be collected below.
|
|
|
|
*/
|
2014-09-18 16:48:00 +04:00
|
|
|
if ((root_ns_mask & CLONE_NEWNS))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
mntinfo = collect_mntinfo(ns);
|
|
|
|
if (mntinfo == NULL)
|
|
|
|
goto err;
|
2014-09-12 16:52:00 +04:00
|
|
|
/*
|
|
|
|
* Mount namespaces are dumped only if the root task lives in
|
|
|
|
* its own mntns, so we can stop enumeration of namespaces.
|
|
|
|
* We are not going to dump this tree, so we skip validation.
|
|
|
|
*/
|
|
|
|
goto out;
|
2014-04-21 18:23:34 +04:00
|
|
|
}
|
|
|
|
|
2014-04-23 13:10:21 +04:00
|
|
|
pr_info("Dump MNT namespace (mountpoints) %d via %d\n", ns->id, ns->pid);
|
2014-04-21 18:23:45 +04:00
|
|
|
pms = collect_mntinfo(ns);
|
|
|
|
if (pms == NULL)
|
2014-04-21 18:23:34 +04:00
|
|
|
goto err;
|
2014-04-21 18:23:45 +04:00
|
|
|
|
2014-04-22 20:35:07 +04:00
|
|
|
mntinfo_add_list(pms);
|
2014-04-21 18:23:34 +04:00
|
|
|
}
|
2014-10-09 17:43:19 +04:00
|
|
|
|
2014-08-03 22:31:16 +04:00
|
|
|
if (collect_shared(mntinfo))
|
|
|
|
goto err;
|
2014-09-12 16:52:00 +04:00
|
|
|
if (validate_mounts(mntinfo, true))
|
|
|
|
goto err;
|
|
|
|
out:
|
2014-04-21 18:23:34 +04:00
|
|
|
ret = 0;
|
|
|
|
err:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-04-22 20:37:13 +04:00
|
|
|
int dump_mnt_namespaces(void)
|
|
|
|
{
|
2014-09-22 16:26:00 +04:00
|
|
|
struct ns_id *nsid;
|
2014-04-22 20:37:13 +04:00
|
|
|
int n = 0;
|
2014-08-03 23:31:04 +04:00
|
|
|
|
|
|
|
if (!(root_ns_mask & CLONE_NEWNS))
|
|
|
|
return 0;
|
|
|
|
|
2014-09-22 16:26:00 +04:00
|
|
|
for (nsid = ns_ids; nsid != NULL; nsid = nsid->next) {
|
|
|
|
if (nsid->nd != &mnt_ns_desc)
|
2014-08-03 23:31:04 +04:00
|
|
|
continue;
|
|
|
|
|
2014-09-24 16:49:00 +04:00
|
|
|
if (nsid->pid == getpid())
|
|
|
|
continue;
|
|
|
|
|
2014-08-03 23:31:04 +04:00
|
|
|
if (++n == 2 && check_mnt_id()) {
|
|
|
|
pr_err("Nested mount namespaces are not supported "
|
|
|
|
"without mnt_id in fdinfo\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-09-22 16:26:00 +04:00
|
|
|
if (dump_mnt_ns(nsid, nsid->mnt.mntinfo_list))
|
2014-08-03 23:31:04 +04:00
|
|
|
return -1;
|
|
|
|
}
|
2014-09-22 16:26:00 +04:00
|
|
|
|
2014-08-03 23:31:04 +04:00
|
|
|
return 0;
|
2014-04-21 18:23:33 +04:00
|
|
|
}
|
|
|
|
|
2013-05-20 13:30:17 +04:00
|
|
|
struct ns_desc mnt_ns_desc = NS_DESC_ENTRY(CLONE_NEWNS, "mnt");
|