[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