mirror of
https://github.com/checkpoint-restore/criu
synced 2025-08-29 13:28:27 +00:00
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 <xemul@parallels.com>
This commit is contained in:
parent
a49325b524
commit
ec50a07727
@ -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;
|
||||||
|
|
||||||
|
7
files.c
7
files.c
@ -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
|
|||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
p.link = &link;
|
p.link = &link;
|
||||||
|
if (link.name[1] == '/')
|
||||||
return dump_reg_file(&p, lfd, fdinfo);
|
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;
|
|||||||
struct parasite_ctl;
|
struct parasite_ctl;
|
||||||
|
|
||||||
struct fd_link {
|
struct fd_link {
|
||||||
|
union {
|
||||||
|
/* Link info for generic file (path) */
|
||||||
|
struct {
|
||||||
char name[PATH_MAX + 1];
|
char name[PATH_MAX + 1];
|
||||||
size_t len;
|
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 @@
|
|||||||
|
|
||||||
#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 {
|
|||||||
.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);
|
||||||
|
193
namespaces.c
193
namespaces.c
@ -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;
|
|||||||
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
|
|||||||
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)
|
|||||||
{
|
{
|
||||||
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)
|
|||||||
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;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user