[CRIU] [PATCH] remap: add a dead pid /proc remap

Tycho Andersen tycho.andersen at canonical.com
Wed Sep 3 14:13:35 PDT 2014


From: Tycho Andersen <tycho at tycho.ws>

If a file like /proc/20/mountinfo is open, but 20 is a zombie (or doesn't exist
any more), we can't read this file at all, so a link remap won't work. Instead,
we add a new remap, called the dead process remap, which forks a TASK_HELPER as
that dead pid so that the restore task can open the new /proc/20/mountinfo
instead.

Previously, TASK_HELPERs died before the restore stage took place. However, the
fds are set up during the restore stage, so we need TASK_HELPERs to live at
least until then. This patch also rearranges some of the synchronization to
keep TASK_HELPERs alive until the restore stage ends.

Signed-off-by: Tycho Andersen <tycho.andersen at canonical.com>
---
 cr-restore.c          | 37 +++++++++++++++++++++-------------
 files-reg.c           | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++
 include/pstree.h      |  3 +++
 protobuf/pstree.proto |  1 +
 pstree.c              | 41 ++++++++++++++++++++++++++++++++++++++
 5 files changed, 123 insertions(+), 14 deletions(-)

diff --git a/cr-restore.c b/cr-restore.c
index cefa78e..5f481b0 100644
--- a/cr-restore.c
+++ b/cr-restore.c
@@ -92,6 +92,7 @@ static int prepare_restorer_blob(void);
 static int prepare_rlimits(int pid, CoreEntry *core);
 static int prepare_posix_timers(int pid, CoreEntry *core);
 static int prepare_signals(int pid, CoreEntry *core);
+static int restore_switch_stage(int next_stage);
 
 static int root_as_sibling;
 
@@ -765,35 +766,42 @@ err:
 
 static int restore_one_alive_task(int pid, CoreEntry *core)
 {
+	int ret = -1;
+
 	pr_info("Restoring resources\n");
 
 	rst_mem_switch_to_private();
 
-	if (pstree_wait_helpers())
-		return -1;
-
 	if (prepare_fds(current))
-		return -1;
+		goto err;
 
 	if (prepare_file_locks(pid))
-		return -1;
+		goto err;
 
 	if (open_vmas(pid))
-		return -1;
+		goto err;
 
 	if (open_cores(pid, core))
-		return -1;
+		goto err;
 
 	if (prepare_signals(pid, core))
-		return -1;
+		goto err;
 
 	if (prepare_posix_timers(pid, core))
-		return -1;
+		goto err;
 
 	if (prepare_rlimits(pid, core) < 0)
-		return -1;
+		goto err;
+
+	if (sigreturn_restore(pid, core))
+		goto err;
 
-	return sigreturn_restore(pid, core);
+	ret = 0;
+err:
+	if (pstree_wait_helpers())
+		ret = -1;
+
+	return ret;
 }
 
 static void zombie_prepare_signals(void)
@@ -930,9 +938,9 @@ static int restore_one_task(int pid, CoreEntry *core)
 		ret = restore_one_alive_task(pid, core);
 	else if (current->state == TASK_DEAD)
 		ret = restore_one_zombie(pid, core);
-	else if (current->state == TASK_HELPER)
-		ret = 0;
-	else {
+	else if (current->state == TASK_HELPER) {
+		ret = restore_finish_stage(CR_STATE_RESTORE);
+	} else {
 		pr_err("Unknown state in code %d\n", (int)core->tc->task_state);
 		ret = -1;
 	}
@@ -1489,6 +1497,7 @@ static inline int stage_participants(int next_stage)
 	case CR_STATE_FORKING:
 		return task_entries->nr_tasks + task_entries->nr_helpers;
 	case CR_STATE_RESTORE:
+		return task_entries->nr_threads + task_entries->nr_helpers;
 	case CR_STATE_RESTORE_SIGCHLD:
 		return task_entries->nr_threads;
 	case CR_STATE_RESTORE_CREDS:
diff --git a/files-reg.c b/files-reg.c
index 0ffce0e..5913031 100644
--- a/files-reg.c
+++ b/files-reg.c
@@ -24,6 +24,7 @@
 #include "asm/atomic.h"
 #include "namespaces.h"
 #include "proc_parse.h"
+#include "pstree.h"
 
 #include "protobuf.h"
 #include "protobuf/regfile.pb-c.h"
@@ -498,6 +499,28 @@ static int dump_linked_remap(char *path, int len, const struct stat *ost,
 			&rpe, PB_REMAP_FPATH);
 }
 
+static int dump_dead_process_remap(pid_t pid, char *path, int len, const struct stat *ost,
+				int lfd, u32 id, struct ns_id *nsid)
+{
+	if (root_item->n_dead_pids == 0) {
+		root_item->dead_pids = malloc(sizeof(root_item->dead_pids[0]));
+		if (!root_item->dead_pids)
+			return -1;
+	} else {
+
+		void *m = realloc(root_item->dead_pids,
+				sizeof(root_item->dead_pids[0]) * (root_item->n_dead_pids + 1));
+		if (!m) {
+			root_item->n_dead_pids = 0;
+			return -1;
+		}
+		root_item->dead_pids = m;
+	}
+
+	root_item->dead_pids[root_item->n_dead_pids++] = pid;
+	return 0;
+}
+
 static bool is_sillyrename_name(char *name)
 {
 	int i;
@@ -539,6 +562,38 @@ static int check_path_remap(char *rpath, int plen, const struct fd_parms *parms,
 	struct stat pst;
 	const struct stat *ost = &parms->stat;
 
+	if (is_path_prefix(rpath, "./proc")) {
+		/* The file points to /proc/pid/<foo> where pid is a dead
+		 * process. We remap this file by adding this pid to be
+		 * fork()ed into a TASK_HELPER state so that we can point to it
+		 * on restore.
+		 */
+		pid_t pid;
+		char *start, *end;
+
+		/* skip "./proc/" */
+		start = strstr(rpath, "/") + 1;
+		if (!start)
+			return -1;
+		start = strstr(start, "/") + 1;
+		if (!start)
+			return -1;
+		pid = strtol(start, &end, 10);
+
+		/* if we didn't find another /, this path something
+		 * like ./proc/kmsg, which we shouldn't mess with. */
+		if (*end == '/') {
+			*end = 0;
+			ret = access(rpath, F_OK);
+			*end = '/';
+
+			if (ret) {
+				pr_info("Dumping dead process remap of %d\n", pid);
+				return dump_dead_process_remap(pid, rpath + 1, plen - 1, ost, lfd, id, nsid);
+			}
+		}
+	}
+
 	if (ost->st_nlink == 0)
 		/*
 		 * Unpleasant, but easy case. File is completely invisible
diff --git a/include/pstree.h b/include/pstree.h
index c361e18..8083f5f 100644
--- a/include/pstree.h
+++ b/include/pstree.h
@@ -30,6 +30,9 @@ struct pstree_item {
 	TaskKobjIdsEntry	*ids;
 
 	struct rst_info		rst[0];
+
+	uint32_t		n_dead_pids;
+	uint32_t		*dead_pids;
 };
 
 static inline int shared_fdtable(struct pstree_item *item) {
diff --git a/protobuf/pstree.proto b/protobuf/pstree.proto
index 6cbcfd3..6cc3953 100644
--- a/protobuf/pstree.proto
+++ b/protobuf/pstree.proto
@@ -4,4 +4,5 @@ message pstree_entry {
 	required uint32			pgid		= 3;
 	required uint32			sid		= 4;
 	repeated uint32			threads		= 5;
+	repeated uint32			dead_pids	= 6;
 }
diff --git a/pstree.c b/pstree.c
index d005b64..62eb294 100644
--- a/pstree.c
+++ b/pstree.c
@@ -240,6 +240,8 @@ int dump_pstree(struct pstree_item *root_item)
 		e.pgid		= item->pgid;
 		e.sid		= item->sid;
 		e.n_threads	= item->nr_threads;
+		e.n_dead_pids	= item->n_dead_pids;
+		e.dead_pids	= item->dead_pids;
 
 		e.threads = xmalloc(sizeof(e.threads[0]) * e.n_threads);
 		if (!e.threads)
@@ -250,6 +252,8 @@ int dump_pstree(struct pstree_item *root_item)
 
 		ret = pb_write_one(pstree_fd, &e, PB_PSTREE);
 		xfree(e.threads);
+		e.n_dead_pids = 0;
+		e.dead_pids = NULL;
 
 		if (ret)
 			goto err;
@@ -348,6 +352,12 @@ static int read_pstree_image(void)
 		pi->sid = e->sid;
 		max_pid = max((int)e->sid, max_pid);
 
+		pi->n_dead_pids = e->n_dead_pids;
+		pi->dead_pids = xmalloc(sizeof(pi->dead_pids[0]) * pi->n_dead_pids);
+		if (!pi->dead_pids)
+			goto err;
+		memcpy(pi->dead_pids, e->dead_pids, sizeof(pi->dead_pids[0]) * pi->n_dead_pids);
+
 		if (e->ppid == 0) {
 			if (root_item) {
 				pr_err("Parent missed on non-root task "
@@ -696,6 +706,31 @@ set_mask:
 	return 0;
 }
 
+int prepare_dead_proc_files(void)
+{
+	int i;
+
+	pr_info("Preparing %d dead processes\n", root_item->n_dead_pids);
+	for (i = 0; i < root_item->n_dead_pids; i++) {
+		struct pstree_item *helper;
+
+		helper = alloc_pstree_item_with_rst();
+		if (!helper)
+			return -1;
+		helper->sid = root_item->sid;
+		helper->pgid = root_item->sid;
+		helper->pid.virt = root_item->dead_pids[i];
+		helper->state = TASK_HELPER;
+		helper->parent = root_item;
+		list_add_tail(&helper->sibling, &root_item->children);
+		task_entries->nr_helpers++;
+
+		pr_info("Added a helper for restoring /proc/%d\n", helper->pid.virt);
+	}
+
+	return 0;
+}
+
 int prepare_pstree(void)
 {
 	int ret;
@@ -719,6 +754,12 @@ int prepare_pstree(void)
 		 * pstree with properly injected helper tasks.
 		 */
 		ret = prepare_pstree_ids();
+	if (!ret)
+		/*
+		 * Restore any dead pids as TASK_HELPERs, so that any task
+		 * which has one of their files open can open it.
+		 */
+		ret = prepare_dead_proc_files();
 
 	return ret;
 }
-- 
1.9.1



More information about the CRIU mailing list