[CRIU] [PATCH 2/2] mnt: add support for external shared mounts

Tycho Andersen tycho.andersen at canonical.com
Mon Apr 6 07:54:49 PDT 2015


Add support for external shared mounts via the --enable-external-masters flag.
This flag assumes that the mount is visible to criu via /proc/self/mountinfo
and that it will be present at the same location on restore. No attempt to dump
or restore the content is made; the external master is bind mounted into the
mount ns and whatever is there is what the container sees.

v2: * only collect external mountns on dump
    * use the regular mountns parsing code instead of having extre symbols
    * minor review rearrangements

Signed-off-by: Tycho Andersen <tycho.andersen at canonical.com>
---
 crtools.c            |   6 +++
 include/cr_options.h |   1 +
 include/proc_parse.h |   1 +
 mount.c              | 131 ++++++++++++++++++++++++++++++++++++++++++++-------
 protobuf/mnt.proto   |   2 +
 5 files changed, 123 insertions(+), 18 deletions(-)

diff --git a/crtools.c b/crtools.c
index 0b3c497..d5b000c 100644
--- a/crtools.c
+++ b/crtools.c
@@ -203,6 +203,7 @@ int main(int argc, char *argv[], char *envp[])
 		{ "cgroup-root", required_argument, 0, 1061},
 		{ "inherit-fd", required_argument, 0, 1062},
 		{ "feature", required_argument, 0, 1063},
+		{ "enable-external-masters", no_argument, 0, 1064},
 		{ },
 	};
 
@@ -416,6 +417,9 @@ int main(int argc, char *argv[], char *envp[])
 			if (check_add_feature(optarg) < 0)
 				return 1;
 			break;
+		case 1064:
+			opts.enable_external_masters = true;
+			break;
 		case 'M':
 			{
 				char *aux;
@@ -637,6 +641,8 @@ usage:
 "  --force-irmap         force resolving names for inotify/fsnotify watches\n"
 "  -M|--ext-mount-map KEY:VALUE\n"
 "                        add external mount mapping\n"
+"  --enable-external-masters\n"
+"                        allow mounts with external masters\n"
 "  --manage-cgroups      dump or restore cgroups the process is in\n"
 "  --cgroup-root [controller:]/newroot\n"
 "                        change the root cgroup the controller will be\n"
diff --git a/include/cr_options.h b/include/cr_options.h
index d6ce2ae..846f03c 100644
--- a/include/cr_options.h
+++ b/include/cr_options.h
@@ -59,6 +59,7 @@ struct cr_options {
 	bool			manage_cgroups;
 	char			*new_global_cg_root;
 	struct list_head	new_cgroup_roots;
+	bool			enable_external_masters;
 	bool			aufs;		/* auto-deteced, not via cli */
 };
 
diff --git a/include/proc_parse.h b/include/proc_parse.h
index 792cf06..46ae393 100644
--- a/include/proc_parse.h
+++ b/include/proc_parse.h
@@ -130,6 +130,7 @@ struct mount_info {
 	struct ns_id	*nsid;
 
 	struct ext_mount *external;
+	bool		external_master;
 
 	/* tree linkage */
 	struct mount_info *parent;
diff --git a/mount.c b/mount.c
index 276262c..2e69484 100644
--- a/mount.c
+++ b/mount.c
@@ -606,6 +606,8 @@ static int validate_mounts(struct mount_info *info, bool for_dump)
 						m->need_plugin = true;
 					else if (ret == -ENOTSUP)
 						ret = try_resolve_ext_mount(m);
+					else if (m->external_master)
+						ret = 0;
 				} else {
 					if (m->need_plugin || m->external)
 						/*
@@ -641,15 +643,16 @@ static int validate_mounts(struct mount_info *info, bool for_dump)
 	return 0;
 }
 
-static int collect_shared(struct mount_info *info)
+static int collect_shared(struct mount_info *info, bool for_dump)
 {
 	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.
+	 * 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. If --enable-external-masters
+	 * is supplied, we assume that the scheme will be present on restore
+	 * and allow the mounts to be dumped.
 	 */
 	for (m = info; m; m = m->next) {
 		bool need_share, need_master;
@@ -675,11 +678,63 @@ static int collect_shared(struct mount_info *info)
 			}
 		}
 
-		if (need_master && m->parent) {
-			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;
+		if (need_master && m->parent && for_dump) {
+			if (opts.enable_external_masters) {
+				struct mount_info *pm;
+				bool found = false;
+				struct ns_id *ns = NULL, *iter;
+
+				for (iter = ns_ids; iter->next; iter = iter->next) {
+					if (iter->pid == getpid() && iter->nd == &mnt_ns_desc) {
+						ns = iter;
+						break;
+					}
+				}
+
+				if (!ns) {
+					pr_err("Failed to find criu pid's mount ns!");
+					return -1;
+				}
+
+				for (pm = ns->mnt.mntinfo_list; pm->next != NULL; pm = pm->next) {
+					int size, ret;
+					char *p;
+
+					if (pm->shared_id != m->master_id)
+						continue;
+
+					size = strlen(pm->mountpoint + 1) + strlen(m->root) + 1;
+					p = xmalloc(sizeof(char) * size);
+					if (!p)
+						return -1;
+
+					ret = snprintf(p, size+1, "%s%s", pm->mountpoint + 1, m->root);
+					if (ret < 0 || ret >= size) {
+						free(p);
+						return -1;
+					}
+
+					pr_info("setting external master %s for %s\n", p, m->mountpoint);
+
+					xfree(m->source);
+					m->source = p;
+					m->external_master = true;
+					m->flags |= MS_BIND;
+					found = true;
+					break;
+				}
+
+				if (!found) {
+					pr_err("couldn't find sharing for %d "
+						"(master_id: %d shared_id: %d)",
+						m->mnt_id, m->master_id, m->shared_id);
+				}
+			} else {
+				pr_err("Mount %d (master_id: %d shared_id: %d) "
+				       "has unreachable sharing. Try --enable-external-masters.\n", m->mnt_id,
+					m->master_id, m->shared_id);
+				return -1;
+			}
 		}
 
 		/* Search bind-mounts */
@@ -1159,6 +1214,12 @@ static int dump_one_mountpoint(struct mount_info *pm, struct cr_img *img)
 	me.has_shared_id	= true;
 	me.master_id		= pm->master_id;
 	me.has_master_id	= true;
+
+	if (pm->external_master) {
+		me.external_master	= true;
+		me.has_external_master  = true;
+	}
+
 	if (pm->need_plugin) {
 		me.has_with_plugin = true;
 		me.with_plugin = true;
@@ -1475,17 +1536,21 @@ static int do_new_mount(struct mount_info *mi)
 		return -1;
 
 	if (mount(src, mi->mountpoint, tp->name,
-			mi->flags & (~MS_SHARED), mi->options) < 0) {
+			mi->flags & ~(MS_SLAVE | MS_SHARED), mi->options) < 0) {
 		pr_perror("Can't mount at %s", mi->mountpoint);
 		return -1;
 	}
 
-	if (restore_shared_options(mi, 0, mi->shared_id, 0))
+	if (restore_shared_options(mi, 0, mi->shared_id, mi->flags & MS_SLAVE || mi->external_master))
 		return -1;
 
 	mi->mounted = true;
 
-	if (tp->restore && tp->restore(mi))
+	/*
+	 * If there is an external master, the content of the mount comes from
+	 * there; otherwise, we restore it as usual.
+	 */
+	if (!mi->external_master && tp->restore && tp->restore(mi))
 		return -1;
 
 	return 0;
@@ -1617,7 +1682,7 @@ static int do_mount_one(struct mount_info *mi)
 	if (mi->mounted)
 		return 0;
 
-	if (!can_mount_now(mi)) {
+	if (!mi->external_master && !can_mount_now(mi)) {
 		pr_debug("Postpone slave %s\n", mi->mountpoint);
 		return 1;
 	}
@@ -1858,6 +1923,11 @@ static int collect_mnt_from_image(struct mount_info **pms, struct ns_id *nsid)
 		pm->need_plugin		= me->with_plugin;
 		pm->is_ns_root		= is_root(me->mountpoint);
 
+		if (me->has_external_master)
+			pm->external_master = me->external_master;
+		else
+			pm->external_master = false;
+
 		/* FIXME: abort unsupported early */
 		pm->fstype		= decode_fstype(me->fstype);
 
@@ -2077,7 +2147,7 @@ static int populate_mnt_ns(struct mount_info *mis)
 	if (!pms)
 		return -1;
 
-	if (collect_shared(mis))
+	if (collect_shared(mis, false))
 		return -1;
 
 	for (nsid = ns_ids; nsid; nsid = nsid->next) {
@@ -2312,6 +2382,8 @@ int mntns_get_root_by_mnt_id(int mnt_id)
 	return mntns_get_root_fd(mntns);
 }
 
+static bool really_collect_self_mounts = false;
+
 static int collect_mntns(struct ns_id *ns, void *oarg)
 {
 	struct mount_info *pms;
@@ -2323,22 +2395,45 @@ static int collect_mntns(struct ns_id *ns, void *oarg)
 	if (ns->pid != getpid())
 		*(int *)oarg = 1;
 
-	mntinfo_add_list(pms);
+	if (!opts.enable_external_masters || ns->pid != getpid() || really_collect_self_mounts)
+		mntinfo_add_list(pms);
 	return 0;
 }
 
 int collect_mnt_namespaces(bool for_dump)
 {
-	int need_to_validate = 0, ret;
+	int need_to_validate = 0, ret, orig_root_ns_mask = root_ns_mask;
+
+	/*
+	 * XXX: this is a little bit of spaghetti. If we are allowing external
+	 * masters, we want to collect the criu pid's mountinfo (and thus the
+	 * whole mountns) so we can corrolate the external masters. So we clear
+	 * the mountns bit for this call to walk_namespaces.
+	 *
+	 * However, we don't want to add criu's mountns to the dump target list
+	 * if it isn't really going to be dumped, so we set the
+	 * really_collect_self_mounts flag if we really do want to collect them
+	 * above.
+	 */
+	if (opts.enable_external_masters) {
+		if (!(root_ns_mask & CLONE_NEWNS))
+			really_collect_self_mounts = true;
+		else
+			root_ns_mask &= ~CLONE_NEWNS;
+	}
 
 	ret = walk_namespaces(&mnt_ns_desc, collect_mntns, &need_to_validate);
+	if (opts.enable_external_masters) {
+		root_ns_mask = orig_root_ns_mask;
+	}
+
 	if (ret)
 		goto err;
 
 	if (for_dump && need_to_validate) {
 		ret = -1;
 
-		if (collect_shared(mntinfo))
+		if (collect_shared(mntinfo, true))
 			goto err;
 		if (validate_mounts(mntinfo, true))
 			goto err;
diff --git a/protobuf/mnt.proto b/protobuf/mnt.proto
index 343bd6d..6f52010 100644
--- a/protobuf/mnt.proto
+++ b/protobuf/mnt.proto
@@ -35,4 +35,6 @@ message mnt_entry {
 
 	optional bool		with_plugin		= 12;
 	optional bool		ext_mount		= 13;
+
+	optional bool		external_master = 14;
 }
-- 
2.1.4



More information about the CRIU mailing list