Commit ec50a077 authored by Pavel Emelyanov's avatar Pavel Emelyanov

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: 's avatarPavel Emelyanov <xemul@parallels.com>
parent a49325b5
...@@ -124,6 +124,9 @@ static int root_prepare_shared(void) ...@@ -124,6 +124,9 @@ static int root_prepare_shared(void)
if (collect_reg_files()) if (collect_reg_files())
return -1; return -1;
if (collect_ns_files())
return -1;
if (collect_pipes()) if (collect_pipes())
return -1; return -1;
......
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
#include "eventpoll.h" #include "eventpoll.h"
#include "fsnotify.h" #include "fsnotify.h"
#include "signalfd.h" #include "signalfd.h"
#include "namespaces.h"
#include "parasite.h" #include "parasite.h"
#include "parasite-syscall.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 ...@@ -267,7 +268,13 @@ static int dump_one_file(struct parasite_ctl *ctl, int fd, int lfd, struct fd_op
return -1; return -1;
p.link = &link; 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)) { if (S_ISFIFO(p.stat.st_mode)) {
......
...@@ -18,8 +18,19 @@ struct rst_info; ...@@ -18,8 +18,19 @@ struct rst_info;
struct parasite_ctl; struct parasite_ctl;
struct fd_link { struct fd_link {
char name[PATH_MAX + 1]; union {
size_t len; /* 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 { struct fd_parms {
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
#include "crtools.h" #include "crtools.h"
#include "pstree.h" #include "pstree.h"
#include "files.h"
struct cr_options; struct cr_options;
...@@ -19,10 +20,15 @@ struct ns_desc { ...@@ -19,10 +20,15 @@ struct ns_desc {
.len = sizeof(_str) - 1, \ .len = sizeof(_str) - 1, \
} }
extern bool check_ns_proc(struct fd_link *link);
extern struct ns_desc pid_ns_desc; extern struct ns_desc pid_ns_desc;
extern struct ns_desc user_ns_desc; extern struct ns_desc user_ns_desc;
extern unsigned long current_ns_mask; 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 dump_namespaces(struct pid *pid, unsigned int ns_flags);
int prepare_namespace(int pid, unsigned long clone_flags); int prepare_namespace(int pid, unsigned long clone_flags);
int try_show_namespaces(int pid, struct cr_options *o); int try_show_namespaces(int pid, struct cr_options *o);
......
...@@ -10,6 +10,53 @@ ...@@ -10,6 +10,53 @@
#include "namespaces.h" #include "namespaces.h"
#include "net.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) int switch_ns(int pid, struct ns_desc *nd, int *rst)
{ {
char buf[32]; char buf[32];
...@@ -74,7 +121,7 @@ static struct ns_id *ns_ids; ...@@ -74,7 +121,7 @@ static struct ns_id *ns_ids;
static unsigned int ns_next_id = 1; static unsigned int ns_next_id = 1;
unsigned long current_ns_mask = 0; 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; struct ns_id *nsid;
...@@ -82,6 +129,18 @@ static unsigned int generate_ns_id(int pid, unsigned int kid, struct ns_desc *nd ...@@ -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) if (nsid->kid == kid && nsid->nd == nd)
return nsid->id; 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 != getpid()) {
if (pid == root_item->pid.real) { if (pid == root_item->pid.real) {
BUG_ON(current_ns_mask & nd->cflag); BUG_ON(current_ns_mask & nd->cflag);
...@@ -113,7 +172,7 @@ static unsigned int get_ns_id(int pid, struct ns_desc *nd) ...@@ -113,7 +172,7 @@ static unsigned int get_ns_id(int pid, struct ns_desc *nd)
{ {
int proc_dir, ret; int proc_dir, ret;
unsigned int kid; unsigned int kid;
char ns_path[10], ns_id[32], *end; char ns_path[10], ns_id[32];
proc_dir = open_pid_proc(pid); proc_dir = open_pid_proc(pid);
if (proc_dir < 0) if (proc_dir < 0)
...@@ -126,11 +185,137 @@ static unsigned int get_ns_id(int pid, struct ns_desc *nd) ...@@ -126,11 +185,137 @@ static unsigned int get_ns_id(int pid, struct ns_desc *nd)
return 0; return 0;
} }
/* XXX: Does it make sense to validate kernel links to <name>:[<id>]? */ kid = parse_ns_link(ns_id, ret, nd);
kid = strtoul(ns_id + strlen(nd->str) + 2, &end, 10); BUG_ON(!kid);
return generate_ns_id(pid, kid, nd); 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 dump_task_ns_ids(struct pstree_item *item)
{ {
int pid = item->pid.real; int pid = item->pid.real;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment