From ec50a07727cfd13c7f08635be10f95ae962dcc9c Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Sat, 18 May 2013 04:00:05 +0400 Subject: [PATCH] ns: Add c/r for /proc/$pid/ns/$ids references Based on work done by Cyrill Corcunov (many thanks for that). In this commit we implement c/r for files which have opened /proc/$pid/ns/$ids entries. The idea is rather simple one Checkpoint ========== - Check if the file name is the one of known to be ns ref - If match then write protobuf entry Restore ======= - Read all ns entries from the image - When criu tries to open one we lookup over process tree to figure out which PID should be used in path and then just open it Signed-off-by: Pavel Emelyanov --- cr-restore.c | 3 + files.c | 9 +- include/files.h | 15 +++- include/namespaces.h | 6 ++ namespaces.c | 193 ++++++++++++++++++++++++++++++++++++++++++- 5 files changed, 219 insertions(+), 7 deletions(-) diff --git a/cr-restore.c b/cr-restore.c index c76eed8f0..08dc97029 100644 --- a/cr-restore.c +++ b/cr-restore.c @@ -124,6 +124,9 @@ static int root_prepare_shared(void) if (collect_reg_files()) return -1; + if (collect_ns_files()) + return -1; + if (collect_pipes()) return -1; diff --git a/files.c b/files.c index cbee107f9..3abf39060 100644 --- a/files.c +++ b/files.c @@ -32,6 +32,7 @@ #include "eventpoll.h" #include "fsnotify.h" #include "signalfd.h" +#include "namespaces.h" #include "parasite.h" #include "parasite-syscall.h" @@ -267,7 +268,13 @@ static int dump_one_file(struct parasite_ctl *ctl, int fd, int lfd, struct fd_op return -1; p.link = &link; - return dump_reg_file(&p, lfd, fdinfo); + if (link.name[1] == '/') + return dump_reg_file(&p, lfd, fdinfo); + + if (check_ns_proc(&link)) + return dump_ns_file(&p, lfd, fdinfo); + + return dump_unsupp_fd(&p); } if (S_ISFIFO(p.stat.st_mode)) { diff --git a/include/files.h b/include/files.h index 3305818f1..2879f9216 100644 --- a/include/files.h +++ b/include/files.h @@ -18,8 +18,19 @@ struct rst_info; struct parasite_ctl; struct fd_link { - char name[PATH_MAX + 1]; - size_t len; + union { + /* Link info for generic file (path) */ + struct { + char name[PATH_MAX + 1]; + size_t len; + }; + + /* Link info for proc-ns file */ + struct { + struct ns_desc *ns_d; + unsigned int ns_kid; + }; + }; }; struct fd_parms { diff --git a/include/namespaces.h b/include/namespaces.h index 7e0f97fa3..3079453d5 100644 --- a/include/namespaces.h +++ b/include/namespaces.h @@ -3,6 +3,7 @@ #include "crtools.h" #include "pstree.h" +#include "files.h" struct cr_options; @@ -19,10 +20,15 @@ struct ns_desc { .len = sizeof(_str) - 1, \ } +extern bool check_ns_proc(struct fd_link *link); + extern struct ns_desc pid_ns_desc; extern struct ns_desc user_ns_desc; extern unsigned long current_ns_mask; +extern int dump_ns_file(struct fd_parms *p, int lfd, const int fdinfo); +extern int collect_ns_files(void); + int dump_namespaces(struct pid *pid, unsigned int ns_flags); int prepare_namespace(int pid, unsigned long clone_flags); int try_show_namespaces(int pid, struct cr_options *o); diff --git a/namespaces.c b/namespaces.c index 5b1b57536..5928bd569 100644 --- a/namespaces.c +++ b/namespaces.c @@ -10,6 +10,53 @@ #include "namespaces.h" #include "net.h" +#include "protobuf.h" +#include "protobuf/ns.pb-c.h" + +struct ns_desc *ns_desc_array[] = { + &net_ns_desc, + &uts_ns_desc, + &ipc_ns_desc, + &pid_ns_desc, + &user_ns_desc, + &mnt_ns_desc, +}; + +static unsigned int parse_ns_link(char *link, size_t len, struct ns_desc *d) +{ + unsigned int kid = 0; + char *end; + + if (len >= d->len + 2) { + if (link[d->len] == ':' && !memcmp(link, d->str, d->len)) { + kid = strtoul(&link[d->len + 2], &end, 10); + if (end && *end == ']') + BUG_ON(kid > UINT_MAX); + else + kid = 0; + } + } + + return kid; +} + +bool check_ns_proc(struct fd_link *link) +{ + unsigned int i, kid; + + for (i = 0; i < ARRAY_SIZE(ns_desc_array); i++) { + kid = parse_ns_link(link->name + 1, link->len - 1, ns_desc_array[i]); + if (!kid) + continue; + + link->ns_d = ns_desc_array[i]; + link->ns_kid = kid; + return true; + } + + return false; +} + int switch_ns(int pid, struct ns_desc *nd, int *rst) { char buf[32]; @@ -74,7 +121,7 @@ static struct ns_id *ns_ids; static unsigned int ns_next_id = 1; unsigned long current_ns_mask = 0; -static unsigned int generate_ns_id(int pid, unsigned int kid, struct ns_desc *nd) +static unsigned int lookup_ns_id(unsigned int kid, struct ns_desc *nd) { struct ns_id *nsid; @@ -82,6 +129,18 @@ static unsigned int generate_ns_id(int pid, unsigned int kid, struct ns_desc *nd if (nsid->kid == kid && nsid->nd == nd) return nsid->id; + return 0; +} + +static unsigned int generate_ns_id(int pid, unsigned int kid, struct ns_desc *nd) +{ + unsigned int id; + struct ns_id *nsid; + + id = lookup_ns_id(kid, nd); + if (id) + return id; + if (pid != getpid()) { if (pid == root_item->pid.real) { BUG_ON(current_ns_mask & nd->cflag); @@ -113,7 +172,7 @@ static unsigned int get_ns_id(int pid, struct ns_desc *nd) { int proc_dir, ret; unsigned int kid; - char ns_path[10], ns_id[32], *end; + char ns_path[10], ns_id[32]; proc_dir = open_pid_proc(pid); if (proc_dir < 0) @@ -126,11 +185,137 @@ static unsigned int get_ns_id(int pid, struct ns_desc *nd) return 0; } - /* XXX: Does it make sense to validate kernel links to :[]? */ - kid = strtoul(ns_id + strlen(nd->str) + 2, &end, 10); + kid = parse_ns_link(ns_id, ret, nd); + BUG_ON(!kid); + return generate_ns_id(pid, kid, nd); } +int dump_one_ns_file(int lfd, u32 id, const struct fd_parms *p) +{ + int fd = fdset_fd(glob_fdset, CR_FD_NS_FILES); + NsFileEntry nfe = NS_FILE_ENTRY__INIT; + struct fd_link *link = p->link; + unsigned int nsid; + + nsid = lookup_ns_id(link->ns_kid, link->ns_d); + if (!nsid) { + pr_err("No NS ID with kid %u\n", link->ns_kid); + return -1; + } + + nfe.id = id; + nfe.ns_id = nsid; + nfe.ns_cflag = link->ns_d->cflag; + nfe.flags = p->flags; + + return pb_write_one(fd, &nfe, PB_NS_FILES); +} + +static const struct fdtype_ops nsfile_ops = { + .type = FD_TYPES__NS, + .dump = dump_one_ns_file, +}; + +int dump_ns_file(struct fd_parms *p, int lfd, const int fdinfo) +{ + return do_dump_gen_file(p, lfd, &nsfile_ops, fdinfo); +} + +struct ns_file_info { + struct file_desc d; + NsFileEntry *nfe; +}; + +static int open_ns_fd(struct file_desc *d) +{ + struct ns_file_info *nfi = container_of(d, struct ns_file_info, d); + struct pstree_item *item, *t; + struct ns_desc *nd = NULL; + char path[64]; + int fd; + + /* + * Find out who can open us. + * + * FIXME I need a hash or RBtree here. + */ + for_each_pstree_item(t) { + TaskKobjIdsEntry *ids = t->ids; + + if (ids->pid_ns_id == nfi->nfe->ns_id) { + item = t; + nd = &pid_ns_desc; + break; + } else if (ids->net_ns_id == nfi->nfe->ns_id) { + item = t; + nd = &net_ns_desc; + break; + } else if (ids->ipc_ns_id == nfi->nfe->ns_id) { + item = t; + nd = &ipc_ns_desc; + break; + } else if (ids->uts_ns_id == nfi->nfe->ns_id) { + item = t; + nd = &uts_ns_desc; + break; + } else if (ids->mnt_ns_id == nfi->nfe->ns_id) { + item = t; + nd = &mnt_ns_desc; + break; + } + } + + if (!nd || !item) { + pr_err("Can't find suitable NS ID for %#x\n", nfi->nfe->ns_id); + return -1; + } + + if (nd->cflag != nfi->nfe->ns_cflag) { + pr_err("Clone flag mismatch for %#x\n", nfi->nfe->ns_id); + return -1; + } + + snprintf(path, sizeof(path) - 1, "/proc/%d/ns/%s", item->pid.virt, nd->str); + path[sizeof(path) - 1] = '\0'; + + fd = open(path, nfi->nfe->flags); + if (fd < 0) { + pr_perror("Can't open file %s on restore", path); + return fd; + } + + return fd; +} + +static struct file_desc_ops ns_desc_ops = { + .type = FD_TYPES__NS, + .open = open_ns_fd, +}; + +static int collect_one_nsfile(void *o, ProtobufCMessage *base) +{ + struct ns_file_info *nfi = o; + + nfi->nfe = pb_msg(base, NsFileEntry); + + pr_info("Collected ns file ID %#x NS-ID %#x\n", nfi->nfe->id, nfi->nfe->ns_id); + file_desc_add(&nfi->d, nfi->nfe->id, &ns_desc_ops); + + return 0; +} + +int collect_ns_files(void) +{ + int ret; + + ret = collect_image(CR_FD_NS_FILES, PB_NS_FILES, + sizeof(struct ns_file_info), collect_one_nsfile); + if (ret < 0 && errno == ENOENT) + ret = 0; + return ret; +} + int dump_task_ns_ids(struct pstree_item *item) { int pid = item->pid.real;