[CRIU] [PATCH 09/39] mounts: create a temporary directory for restoring non-root mntns (v2)

Andrey Vagin avagin at openvz.org
Mon Apr 21 07:23:19 PDT 2014


All non-root namespaces will be restored as sub-trees of the root tree.

This patch adds helpers to create a temporary directory and mount tmpfs
in it, then create directories for each non-root mount namespace.

tmpfs is quite useful here to simplify destroying this construction,
we don't need to unmount each namespace separately.

v2: add a comment why MNT_DETACH is not dangerous here
Signed-off-by: Andrey Vagin <avagin at openvz.org>
---
 cr-restore.c    |  22 +++++++----
 include/mount.h |   2 +
 mount.c         | 120 +++++++++++++++++++++++++++++++++++++++++++++++++++++---
 3 files changed, 132 insertions(+), 12 deletions(-)

diff --git a/cr-restore.c b/cr-restore.c
index 8e07c56..4c4e459 100644
--- a/cr-restore.c
+++ b/cr-restore.c
@@ -1249,7 +1249,7 @@ static int restore_task_with_children(void *_arg)
 		 * Thus -- mount proc at custom location for any new namespace
 		 */
 		if (mount_proc())
-			exit(1);
+			goto err;
 
 		if (close_old_fds(current))
 			exit(1);
@@ -1258,7 +1258,7 @@ static int restore_task_with_children(void *_arg)
 			exit(1);
 
 		if (root_prepare_shared())
-			exit(1);
+			goto err;
 	}
 
 	/*
@@ -1271,11 +1271,11 @@ static int restore_task_with_children(void *_arg)
 	ret = sigprocmask(SIG_BLOCK, &blockmask, NULL);
 	if (ret) {
 		pr_perror("%d: Can't block signals", current->pid.virt);
-		exit(1);
+		goto err;
 	}
 
 	if (prepare_mappings(pid))
-		exit(1);
+		goto err;
 
 	if (!(ca->clone_flags & CLONE_FILES)) {
 		ret = close_old_fds(current);
@@ -1284,20 +1284,28 @@ static int restore_task_with_children(void *_arg)
 	}
 
 	if (create_children_and_session())
-		exit(1);
+		goto err;
 
 	if (unmap_guard_pages())
-		exit(1);
+		goto err;
 
 	restore_pgid();
 
 	if (restore_finish_stage(CR_STATE_FORKING) < 0)
-		exit(1);
+		goto err;
+
+	if (current->parent == NULL && fini_mnt_ns())
+		exit (1);
 
 	if (current->state == TASK_HELPER)
 		return 0;
 
 	return restore_one_task(current->pid.virt, ca->core);
+err:
+	if (current->parent == NULL)
+		fini_mnt_ns();
+
+	exit(1);
 }
 
 static inline int stage_participants(int next_stage)
diff --git a/include/mount.h b/include/mount.h
index a2bc17d..4fd10e3 100644
--- a/include/mount.h
+++ b/include/mount.h
@@ -24,4 +24,6 @@ extern struct ns_desc mnt_ns_desc;
 extern dev_t phys_stat_resolve_dev(dev_t st_dev, const char *path);
 extern bool phys_stat_dev_match(dev_t st_dev, dev_t phys_dev, const char *path);
 
+extern int fini_mnt_ns(void);
+
 #endif /* __CR_MOUNT_H__ */
diff --git a/mount.c b/mount.c
index c0561bb..5262b08 100644
--- a/mount.c
+++ b/mount.c
@@ -1384,6 +1384,37 @@ static void free_mounts(void)
 	}
 }
 
+/*
+ * mnt_roots is a temporary directory for restoring sub-trees of
+ * non-root namespaces.
+ */
+static char *mnt_roots;
+
+static int create_mnt_roots()
+{
+	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;
+}
+
 static int collect_mnt_from_image(struct mount_info **pms, struct ns_id *nsid)
 {
 	MntEntry *me = NULL;
@@ -1397,7 +1428,8 @@ static int collect_mnt_from_image(struct mount_info **pms, struct ns_id *nsid)
 
 	while (1) {
 		struct mount_info *pm;
-		int len;
+		char root[PATH_MAX] = ".";
+		int len, root_len = 1;
 
 		ret = pb_read_one_eof(img, &me, PB_MNT);
 		if (ret <= 0)
@@ -1427,8 +1459,10 @@ static int collect_mnt_from_image(struct mount_info **pms, struct ns_id *nsid)
 		if (!pm->root)
 			goto err;
 
-		pr_debug("\t\tGetting mpt for %d:%s\n", pm->mnt_id, me->mountpoint);
-		len  = strlen(me->mountpoint) + 2;
+		if (nsid->id != root_item->ids->mnt_ns_id)
+			root_len = snprintf(root, sizeof(root), "%s/%d/",
+						mnt_roots, nsid->id);
+		len  = strlen(me->mountpoint) + root_len + 1;
 		pm->mountpoint = xmalloc(len);
 		if (!pm->mountpoint)
 			goto err;
@@ -1439,8 +1473,10 @@ static int collect_mnt_from_image(struct mount_info **pms, struct ns_id *nsid)
 		 * that.
 		 */
 
-		pm->mountpoint[0] = '.';
-		strcpy(pm->mountpoint + 1, me->mountpoint);
+		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);
 
 		pr_debug("\t\tGetting source for %d\n", pm->mnt_id);
 		pm->source = xstrdup(me->source);
@@ -1478,6 +1514,10 @@ static struct mount_info *read_mnt_ns_img()
 			continue;
 		}
 
+		if (nsid->id != root_item->ids->mnt_ns_id)
+			if (create_mnt_roots(true))
+				return NULL;
+
 		if (collect_mnt_from_image(&pms, nsid))
 			goto err;
 
@@ -1488,6 +1528,44 @@ err:
 	return NULL;
 }
 
+/*
+ * All nested mount namespaces are restore as sub-trees of the root namespace.
+ */
+static int prepare_temporary_roots()
+{
+	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;
+
+	nsid = ns_ids;
+	while (nsid) {
+		if (nsid->nd != &mnt_ns_desc) {
+			nsid = nsid->next;
+			continue;
+		}
+
+		snprintf(path, sizeof(path), "%s/%d",
+				mnt_roots, nsid->id);
+
+		if (mkdir(path, 0600)) {
+			pr_perror("Unable to create %s", path);
+			return -1;
+		}
+		nsid = nsid->next;
+	}
+
+	return 0;
+}
+
 static int populate_mnt_ns(int ns_pid, struct mount_info *mis)
 {
 	struct mount_info *pms;
@@ -1495,6 +1573,9 @@ static int populate_mnt_ns(int ns_pid, struct mount_info *mis)
 	mntinfo_tree = NULL;
 	mntinfo = mis;
 
+	if (prepare_temporary_roots())
+		return -1;
+
 	pms = mnt_build_tree(mntinfo);
 	if (!pms)
 		return -1;
@@ -1506,6 +1587,35 @@ static int populate_mnt_ns(int ns_pid, struct mount_info *mis)
 	return mnt_tree_for_each(pms, do_mount_one);
 }
 
+int fini_mnt_ns()
+{
+	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;
+}
+
 int prepare_mnt_ns(int ns_pid)
 {
 	int ret = -1;
-- 
1.8.5.3



More information about the CRIU mailing list