[CRIU] [PATCH 2/3] mount: Forced mount unmounted binfmt_misc to do not lost its content

Kirill Tkhai ktkhai at virtuozzo.com
Wed Jun 22 07:19:15 PDT 2016


Umount does not remove binfmt_misc content. If it's mounted once again,
the same entries remain registered.

Criu does not dump content of umounted binfmt_misc. So, after C/R we
lose it at all.

This patch forces mounting of unmounted binfmt_misc before we collect
mountpoints. If it's unmounted, we mount it back and add this mount
to the list of forced mounted mountpoints. Next patch need this
list to mark the mount in special way in dump image.

Signed-off-by: Kirill Tkhai <ktkhai at virtuozzo.com>
---
 criu/cr-dump.c       |   13 +++++
 criu/include/mount.h |    1 
 criu/mount.c         |  129 ++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 143 insertions(+)

diff --git a/criu/cr-dump.c b/criu/cr-dump.c
index 00d28e9..dbda4b7 100644
--- a/criu/cr-dump.c
+++ b/criu/cr-dump.c
@@ -747,6 +747,16 @@ static int dump_task_core_all(struct parasite_ctl *ctl,
 	return ret;
 }
 
+static int prepare_dump__tasks_freezed(void)
+{
+	int ret;
+
+	/* Tasks freezed, so we do not race with systemd's autofs unmounter */
+	ret = try_mount_binfmt_misc(root_item->pid.real);
+
+	return ret;
+}
+
 static int collect_pstree_ids_predump(void)
 {
 	struct pstree_item *item;
@@ -1681,6 +1691,9 @@ int cr_dump_tasks(pid_t pid)
 	if (collect_pstree())
 		goto err;
 
+	if (prepare_dump__tasks_freezed())
+		goto err;
+
 	if (collect_pstree_ids())
 		goto err;
 
diff --git a/criu/include/mount.h b/criu/include/mount.h
index c7992ac..191f6a3 100644
--- a/criu/include/mount.h
+++ b/criu/include/mount.h
@@ -128,4 +128,5 @@ extern int mntns_maybe_create_roots(void);
 extern int read_mnt_ns_img(void);
 extern void cleanup_mnt_ns(void);
 
+extern int try_mount_binfmt_misc(pid_t pid);
 #endif /* __CR_MOUNT_H__ */
diff --git a/criu/mount.c b/criu/mount.c
index e891c92..f91654c 100644
--- a/criu/mount.c
+++ b/criu/mount.c
@@ -41,6 +41,13 @@
 #define LOG_PREFIX "mnt: "
 
 static struct fstype fstypes[];
+static LIST_HEAD(forced_mounts_list);
+
+struct forced_mount {
+	struct list_head list;
+	unsigned int ns_id;
+	unsigned int mnt_id;
+};
 
 int ext_mount_add(char *key, char *val)
 {
@@ -3685,4 +3692,126 @@ int dump_mnt_namespaces(void)
 	return 0;
 }
 
+static int add_forced_mount(pid_t pid, const char *path)
+{
+	unsigned int ns_id, mnt_id = 0;
+	struct forced_mount *fm;
+	int i, len, ret = 0;
+	char *str, *p;
+	struct bfd f;
+
+	if (read_ns_id(pid, &mnt_ns_desc, &ns_id) < 0 || !ns_id) {
+		pr_err("Can't read mnt_ns id\n");
+		return -1;
+	}
+	f.fd = open_proc(pid, "mountinfo");
+	if (f.fd < 0) {
+		pr_perror("Can't open mountinfo to parse");
+		return -1;
+	}
+	if (bfdopenr(&f))
+		return -1;
+	len = strlen(path);
+
+	while (1) {
+		p = str = breadline(&f);
+		if (!p)
+			break;
+		i = 0;
+		while (i < 4) {
+			p = strchr(p, ' ');
+			if (!str)
+				break;
+			i++;
+			p++;
+		}
+
+		if (i != 4) {
+			pr_err("Can't parse mountinfo\n");
+			ret = -1;
+			break;
+		}
+
+		if (strncmp(p, path, len))
+			continue;
+		if (sscanf(str, "%u", &mnt_id) != 1) {
+			pr_err("Can't parse mountinfo\n");
+			ret = -1;
+		}
+		/* Do not break as we're interested in the last entry */
+	}
+
+	bclose(&f);
+
+	if (ret || !mnt_id) {
+		if (!ret)
+			pr_err("Can't find %s mounted\n", path);
+		return -1;
+	}
+
+	fm = xmalloc(sizeof(*fm));
+	if (!fm)
+		return -1;
+	fm->ns_id = ns_id;
+	fm->mnt_id = mnt_id;
+	list_add(&fm->list, &forced_mounts_list);
+
+	return 0;
+}
+
+#define BINFMT_MISC_HOME "/proc/sys/fs/binfmt_misc"
+
+int try_mount_binfmt_misc(pid_t pid)
+{
+	int num, mnt_fd, ret, exit_code = -1;
+	struct dirent *de;
+	DIR *dir;
+
+	ret = switch_ns(pid, &mnt_ns_desc, &mnt_fd);
+	if (ret < 0) {
+		pr_err("Can't switch mnt_ns\n");
+		return -1;
+	}
+
+	ret = mount("binfmt_misc", BINFMT_MISC_HOME, "binfmt_misc", 0, NULL);
+	if (ret < 0) {
+		if (errno == EPERM) {
+			pr_info("Can't mount binfmt_misc: EPERM. Running in user_ns?\n");
+			exit_code = 0;
+			goto restore_ns;
+		}
+		if (errno != EBUSY && errno != ENODEV && errno != ENOENT) {
+			pr_perror("Can't mount binfmt_misc");
+			goto restore_ns;
+		}
+		pr_info("Prepare binfmt_misc: skipping(%d)\n", errno);
+	} else {
+		dir = opendir(BINFMT_MISC_HOME);
+		if (!dir) {
+			pr_perror("Can't read binfmt_misc dir");
+			goto restore_ns;
+		}
+
+		num = 0;
+		/* ".", "..", "register", "status" */
+		while (num <= 4 && (de = readdir(dir)) != NULL)
+			num++;
+		if (num <= 4) {
+			/* No entries */
+			umount(BINFMT_MISC_HOME);
+		} else {
+			ret = add_forced_mount(pid, BINFMT_MISC_HOME);
+			if (ret)
+				goto restore_ns;
+		}
+		closedir(dir);
+	}
+
+	exit_code = 0;
+restore_ns:
+	ret = restore_ns(mnt_fd, &mnt_ns_desc);
+
+	return ret ? -1 : exit_code;
+}
+
 struct ns_desc mnt_ns_desc = NS_DESC_ENTRY(CLONE_NEWNS, "mnt");



More information about the CRIU mailing list