[CRIU] [PATCH 1/5] mnt: add --ext-mount-map auto option

Tycho Andersen tycho.andersen at canonical.com
Thu Apr 9 09:32:32 PDT 2015


When this option is specified, if an external (private) bind mount is not
specified by --ext-mount-map KEY:VAL then it is attempted to be resolved
automatically.

v2: introduce find_best_external_match, which looks for the best match based on
    sharing/slave ids; don't try to resolve fsroot_mounted() mountpoints

Signed-off-by: Tycho Andersen <tycho.andersen at canonical.com>
---
 crtools.c            |   7 ++
 include/cr_options.h |   1 +
 mount.c              | 212 ++++++++++++++++++++++++++++++++++++++++++++++-----
 3 files changed, 199 insertions(+), 21 deletions(-)

diff --git a/crtools.c b/crtools.c
index 9a0902d..a6ee2d7 100644
--- a/crtools.c
+++ b/crtools.c
@@ -425,6 +425,11 @@ int main(int argc, char *argv[], char *envp[])
 			{
 				char *aux;
 
+				if (strcmp(optarg, "auto") == 0) {
+					opts.autodetect_ext_mounts = true;
+					break;
+				}
+
 				aux = strchr(optarg, ':');
 				if (aux == NULL)
 					goto bad_arg;
@@ -642,6 +647,8 @@ usage:
 "  --force-irmap         force resolving names for inotify/fsnotify watches\n"
 "  -M|--ext-mount-map KEY:VALUE\n"
 "                        add external mount mapping\n"
+"  -M|--ext-mount-map auto\n"
+"                        attempt to autodetect external mount mapings\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 27102b7..f1cfc84 100644
--- a/include/cr_options.h
+++ b/include/cr_options.h
@@ -62,6 +62,7 @@ struct cr_options {
 	bool			manage_cgroups;
 	char			*new_global_cg_root;
 	struct list_head	new_cgroup_roots;
+	bool			autodetect_ext_mounts;
 	bool			aufs;		/* auto-deteced, not via cli */
 };
 
diff --git a/mount.c b/mount.c
index c4c2c86..154c389 100644
--- a/mount.c
+++ b/mount.c
@@ -30,6 +30,8 @@
 
 #include "protobuf/mnt.pb-c.h"
 
+#define AUTODETECTED_MOUNT "CRIU:AUTOGENERATED"
+
 /*
  * Structure to keep external mount points resolving info.
  *
@@ -601,11 +603,15 @@ static int validate_mounts(struct mount_info *info, bool for_dump)
 				int ret;
 
 				if (for_dump) {
-					ret = run_plugins(DUMP_EXT_MOUNT, m->mountpoint, m->mnt_id);
-					if (ret == 0)
-						m->need_plugin = true;
-					else if (ret == -ENOTSUP)
-						ret = try_resolve_ext_mount(m);
+					// We've already resolved the mount
+					// and it is external.
+					if (m->external) {
+						ret = 0;
+					} else {
+						ret = run_plugins(DUMP_EXT_MOUNT, m->mountpoint, m->mnt_id);
+						if (ret == 0)
+							m->need_plugin = true;
+					}
 				} else {
 					if (m->need_plugin || m->external)
 						/*
@@ -641,7 +647,125 @@ static int validate_mounts(struct mount_info *info, bool for_dump)
 	return 0;
 }
 
-static int collect_shared(struct mount_info *info)
+static struct mount_info *find_best_external_match(struct mount_info *list, struct mount_info *info)
+{
+	struct mount_info *it, *candidate = NULL;
+
+	for (it = list; it; it = it->next) {
+		if (!mounts_equal(info, it, true))
+			continue;
+
+		candidate = it;
+
+		/*
+		 * Consider the case of:
+		 *
+		 * mount /xxx
+		 * mount --bind /xxx /yyy
+		 * mount --make-shared /yyy
+		 * mount --bind /xxx /zzz
+		 * mount --make-shared /zzz
+		 * bind mount a shared mount into the namespace
+		 *
+		 * Here, we want to return the /right/ mount, not just a mount
+		 * that's equal. However, in the case:
+		 *
+		 * bind mount a shared mount into the namespace
+		 * inside the namespace, remount MS_PRIVATE
+		 * inside the namespace, remount MS_SHARED
+		 *
+		 * there will be no external mount with matching sharing
+		 * because the sharing is only internal; we still want to bind
+		 * mount from this mountinfo so we should return it, but we
+		 * should make the sharing namespace private after that bind
+		 * mount.
+		 *
+		 * Below are the cases where we found an exact match.
+		 */
+		if (info->flags & MS_SHARED && info->shared_id == it->shared_id)
+			return candidate;
+
+		if (info->flags & MS_SLAVE && info->master_id == it->shared_id)
+			return candidate;
+	}
+
+	return candidate;
+}
+
+static int resolve_external_mounts(struct mount_info *info)
+{
+	struct mount_info *m;
+	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 (m = info; m; m = m->next) {
+		int ret, size;
+		char *p;
+		struct ext_mount *em;
+		struct mount_info *match;
+
+		if (m->parent == NULL || m->is_ns_root)
+			continue;
+
+		if (fsroot_mounted(m))
+			continue;
+
+		ret = try_resolve_ext_mount(m);
+		if (ret < 0 && ret != -ENOTSUP) {
+			return -1;
+		} else if (ret == -ENOTSUP && !opts.autodetect_ext_mounts) {
+			continue;
+		} else if (ret == 0) {
+			continue;
+		}
+
+		match = find_best_external_match(ns->mnt.mntinfo_list, m);
+		if (!match)
+			continue;
+
+		size = strlen(match->mountpoint + 1) + strlen(m->root) + 1;
+		p = xmalloc(sizeof(char) * size);
+		if (!p)
+			return -1;
+
+		ret = snprintf(p, size+1, "%s%s", match->mountpoint + 1, m->root);
+		if (ret < 0 || ret >= size) {
+			free(p);
+			return -1;
+		}
+
+		em = xmalloc(sizeof(struct ext_mount));
+		if (!em) {
+			free(p);
+			return -1;
+		}
+
+		em->val = AUTODETECTED_MOUNT;
+		em->key = p;
+
+		m->external = em;
+
+		xfree(m->source);
+		m->source = p;
+
+		pr_info("autodetected external mount %s for %s\n", p, m->mountpoint);
+	}
+
+	return 0;
+}
+
+static int collect_shared(struct mount_info *info, bool for_dump)
 {
 	struct mount_info *m, *t;
 
@@ -675,10 +799,12 @@ 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);
+		// If we haven't already determined this mount is external,
+		// then we don't know where it came from.
+		if (need_master && m->parent && !m->external) {
+			pr_err("Mount %d %s (master_id: %d shared_id: %d) "
+			       "has unreachable sharing. Try --enable-external-masters.\n", m->mnt_id,
+				m->mountpoint, m->master_id, m->shared_id);
 			return -1;
 		}
 
@@ -1594,6 +1720,9 @@ static bool can_mount_now(struct mount_info *mi)
 	if (mi->is_ns_root)
 		return true;
 
+	if (mi->external)
+		return true;
+
 	if (mi->master_id && mi->bind == NULL)
 		return false;
 
@@ -1880,6 +2009,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);
 
+		pr_debug("\t\tGetting source for %d\n", pm->mnt_id);
+		pm->source = xstrdup(me->source);
+		if (!pm->source)
+			goto err;
+
 		/* FIXME: abort unsupported early */
 		pm->fstype		= decode_fstype(me->fstype);
 
@@ -1893,8 +2027,28 @@ static int collect_mnt_from_image(struct mount_info **pms, struct ns_id *nsid)
 
 			em = ext_mount_lookup(me->root);
 			if (!em) {
-				pr_err("No mapping for %s mountpoint\n", me->mountpoint);
-				goto err;
+				if (!opts.autodetect_ext_mounts) {
+					pr_err("No mapping for %s mountpoint\n", me->mountpoint);
+					goto err;
+				}
+
+				/*
+				 * Make up an external mount entry for this
+				 * mount point, since we couldn't find a user
+				 * supplied one.
+				 */
+				em = xmalloc(sizeof(struct ext_mount));
+				if (!em)
+					goto err;
+
+				em->val = pm->source;
+
+				/*
+				 * Put a : in here since those are invalid on
+				 * the cli, so we know it's autogenerated in
+				 * debugging.
+				 */
+				em->key = AUTODETECTED_MOUNT;
 			}
 
 			pm->external = em;
@@ -1926,11 +2080,6 @@ static int collect_mnt_from_image(struct mount_info **pms, struct ns_id *nsid)
 
 		pr_debug("\t\tGetting mpt for %d %s\n", pm->mnt_id, pm->mountpoint);
 
-		pr_debug("\t\tGetting source for %d\n", pm->mnt_id);
-		pm->source = xstrdup(me->source);
-		if (!pm->source)
-			goto err;
-
 		pr_debug("\t\tGetting opts for %d\n", pm->mnt_id);
 		pm->options = xstrdup(me->options);
 		if (!pm->options)
@@ -2108,7 +2257,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) {
@@ -2346,6 +2495,7 @@ int mntns_get_root_by_mnt_id(int mnt_id)
 struct collect_mntns_arg {
 	bool need_to_validate;
 	bool for_dump;
+	bool really_collect_self_mounts;
 };
 
 static int collect_mntns(struct ns_id *ns, void *__arg)
@@ -2360,7 +2510,8 @@ static int collect_mntns(struct ns_id *ns, void *__arg)
 	if (arg->for_dump && ns->pid != getpid())
 		arg->need_to_validate = true;
 
-	mntinfo_add_list(pms);
+	if (!opts.autodetect_ext_mounts || ns->pid != getpid() || arg->really_collect_self_mounts)
+		mntinfo_add_list(pms);
 	return 0;
 }
 
@@ -2371,15 +2522,34 @@ int collect_mnt_namespaces(bool for_dump)
 
 	arg.for_dump = for_dump;
 	arg.need_to_validate = false;
+	arg.really_collect_self_mounts = false;
+
+	/*
+	 * XXX: this is a little bit of spaghetti. If we are autodetecting
+	 * external mounts, 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.autodetect_ext_mounts && !(root_ns_mask & CLONE_NEWNS)) {
+		arg.really_collect_self_mounts = true;
+	}
 
-	ret = walk_namespaces(&mnt_ns_desc, false, collect_mntns, &need_to_validate);
+	ret = walk_namespaces(&mnt_ns_desc, opts.autodetect_ext_mounts, collect_mntns, &arg);
 	if (ret)
 		goto err;
 
+	if (resolve_external_mounts(mntinfo))
+		goto err;
+
 	if (arg.need_to_validate) {
 		ret = -1;
 
-		if (collect_shared(mntinfo))
+		if (collect_shared(mntinfo, true))
 			goto err;
 		if (validate_mounts(mntinfo, true))
 			goto err;
-- 
2.1.0



More information about the CRIU mailing list