2
0
mirror of https://github.com/checkpoint-restore/criu synced 2025-08-22 01:51:51 +00:00
criu/pipes.c
Pavel Emelyanov 9fc1d6bbeb fdinfo: Make fdinfo.type be an enum
This types specifies a strict set of what is hidden behind
the fd. Thus these numbers should be in the description of
the fdinfo message.

Plus protobuf makes shure nothing else will be there.

Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
2012-07-19 10:18:37 +04:00

458 lines
9.3 KiB
C

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/mman.h>
#include "crtools.h"
#include "image.h"
#include "files.h"
#include "pipes.h"
#include "util-net.h"
#include "protobuf.h"
#include "protobuf/pipe.pb-c.h"
#include "protobuf/pipe-data.pb-c.h"
/*
* The sequence of objects which should be restored:
* pipe -> files struct-s -> fd-s.
* pipe_entry describes pipe's file structs-s.
* A pipe doesn't have own properties, so it has no object.
*/
struct pipe_info {
PipeEntry *pe;
struct list_head pipe_list; /* All pipe_info with the same pipe_id
* This is pure circular list without head */
struct list_head list; /* list head for fdinfo_list_entry-s */
struct file_desc d;
int create;
};
static LIST_HEAD(pipes);
static void show_saved_pipe_fds(struct pipe_info *pi)
{
struct fdinfo_list_entry *fle;
pr_info(" `- ID %p %#xpn", pi, pi->pe->id);
list_for_each_entry(fle, &pi->d.fd_info_head, desc_list)
pr_info(" `- FD %d pid %d\n", fle->fe->fd, fle->pid);
}
static int pipe_data_read(int fd, struct pipe_data_rst *r)
{
/*
* We potentially allocate more memory than required for data,
* but this is OK. Look at restore_pipe_data -- it vmsplice-s
* this into the kernel with F_GIFT flag (since some time it
* works on non-aligned data), thus just giving this page to
* pipe buffer. And since kernel allocates pipe buffers in pages
* anyway we don't increase memory consumption :)
*/
r->data = mmap(NULL, r->pde->bytes, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANON, 0, 0);
if (r->data == MAP_FAILED) {
pr_perror("Can't map mem for pipe buffers");
return -1;
}
return read_img_buf(fd, r->data, r->pde->bytes);
}
int collect_pipe_data(int img_type, struct pipe_data_rst **hash)
{
int fd, ret;
struct pipe_data_rst *r = NULL;
fd = open_image_ro(img_type);
if (fd < 0)
return -1;
while (1) {
ret = -1;
r = xmalloc(sizeof(*r));
if (!r)
break;
ret = pb_read_eof(fd, &r->pde, pipe_data_entry);
if (ret <= 0)
break;
ret = pipe_data_read(fd, r);
if (ret < 0)
break;
ret = r->pde->pipe_id & PIPE_DATA_HASH_MASK;
r->next = hash[ret];
hash[ret] = r;
pr_info("Collected pipe data for %#x (chain %u)\n",
r->pde->pipe_id, ret);
}
if (r && r->pde)
pipe_data_entry__free_unpacked(r->pde, NULL);
xfree(r);
close(fd);
return ret;
}
/* Choose who will restore a pipe. */
void mark_pipe_master(void)
{
LIST_HEAD(head);
pr_info("Pipes:\n");
while (1) {
struct fdinfo_list_entry *fle;
struct pipe_info *pi, *pic, *p;
int fd, pid;
if (list_empty(&pipes))
break;
pi = list_first_entry(&pipes, struct pipe_info, list);
list_move(&pi->list, &head);
pr_info(" `- PIPE ID %#x\n", pi->pe->pipe_id);
show_saved_pipe_fds(pi);
fle = file_master(&pi->d);
p = pi;
fd = fle->fe->fd;
pid = fle->pid;
list_for_each_entry(pic, &pi->pipe_list, pipe_list) {
list_move(&pic->list, &head);
fle = file_master(&p->d);
if (fle->pid < pid ||
(pid == fle->pid && fle->fe->fd < fd)) {
p = pic;
fd = fle->fe->fd;
pid = fle->pid;
}
show_saved_pipe_fds(pic);
}
p->create = 1;
pr_info(" by %#x\n", p->pe->id);
}
list_splice(&head, &pipes);
}
static struct pipe_data_rst *pd_hash_pipes[PIPE_DATA_HASH_SIZE];
int restore_pipe_data(int img_type, int pfd, u32 id, struct pipe_data_rst **hash)
{
int ret;
struct pipe_data_rst *pd;
struct iovec iov;
for (pd = hash[id & PIPE_DATA_HASH_MASK]; pd != NULL; pd = pd->next)
if (pd->pde->pipe_id == id)
break;
if (!pd) { /* no data for this pipe */
pr_info("No data for pipe %#x\n", id);
return 0;
}
if (!pd->data) {
pr_err("Double data restore occurred on %#x\n", id);
return -1;
}
iov.iov_base = pd->data;
iov.iov_len = pd->pde->bytes;
while (iov.iov_len > 0) {
ret = vmsplice(pfd, &iov, 1, SPLICE_F_GIFT | SPLICE_F_NONBLOCK);
if (ret < 0) {
pr_perror("%#x: Error splicing data", id);
goto err;
}
if (ret == 0 || ret > iov.iov_len /* sanity */) {
pr_err("%#x: Wanted to restore %lu bytes, but got %d\n", id,
iov.iov_len, ret);
ret = -1;
goto err;
}
iov.iov_base += ret;
iov.iov_len -= ret;
}
/*
* 3 reasons for killing the buffer from our address space:
*
* 1. We gifted the pages to the kernel to optimize memory usage, thus
* accidental memory corruption can change the pipe buffer.
* 2. This will make the vmas restoration a bit faster due to less self
* mappings to be unmapped.
* 3. We can catch bugs with double pipe data restore.
*/
munmap(pd->data, pd->pde->bytes);
pd->data = NULL;
ret = 0;
err:
return ret;
}
static int recv_pipe_fd(struct pipe_info *pi)
{
struct fdinfo_list_entry *fle;
char path[32];
int tmp, fd;
fle = file_master(&pi->d);
fd = fle->fe->fd;
pr_info("\tWaiting fd for %d\n", fd);
tmp = recv_fd(fd);
if (tmp < 0) {
pr_err("Can't get fd %d\n", tmp);
return -1;
}
close(fd);
snprintf(path, sizeof(path), "/proc/self/fd/%d", tmp);
fd = open(path, pi->pe->flags);
close(tmp);
if (fd >= 0) {
if (restore_fown(fd, pi->pe->fown)) {
close(fd);
return -1;
}
}
return fd;
}
static int open_pipe(struct file_desc *d)
{
struct pipe_info *pi, *p;
int ret, tmp;
int pfd[2];
int sock;
pi = container_of(d, struct pipe_info, d);
pr_info("\t\tCreating pipe pipe_id=%#x id=%#x\n", pi->pe->pipe_id, pi->pe->id);
if (!pi->create)
return recv_pipe_fd(pi);
if (pipe(pfd) < 0) {
pr_perror("Can't create pipe");
return -1;
}
ret = restore_pipe_data(CR_FD_PIPES_DATA, pfd[1],
pi->pe->pipe_id, pd_hash_pipes);
if (ret)
return -1;
sock = socket(PF_UNIX, SOCK_DGRAM, 0);
if (sock < 0) {
pr_perror("Can't create socket");
return -1;
}
list_for_each_entry(p, &pi->pipe_list, pipe_list) {
struct fdinfo_list_entry *fle;
int fd;
fle = file_master(&p->d);
fd = pfd[p->pe->flags & O_WRONLY];
if (send_fd_to_peer(fd, fle, sock)) {
pr_perror("Can't send file descriptor");
return -1;
}
}
close(sock);
close(pfd[!(pi->pe->flags & O_WRONLY)]);
tmp = pfd[pi->pe->flags & O_WRONLY];
if (rst_file_params(tmp, pi->pe->fown, pi->pe->flags))
return -1;
return tmp;
}
static int want_transport(FdinfoEntry *fe, struct file_desc *d)
{
struct pipe_info *pi;
pi = container_of(d, struct pipe_info, d);
return !pi->create;
}
static struct file_desc_ops pipe_desc_ops = {
.type = FD_TYPES__PIPE,
.open = open_pipe,
.want_transport = want_transport,
};
int collect_pipes(void)
{
struct pipe_info *pi = NULL, *tmp;
int fd, ret = -1;
fd = open_image_ro(CR_FD_PIPES);
if (fd < 0)
return -1;
while (1) {
ret = -1;
pi = xmalloc(sizeof(*pi));
if (pi == NULL)
break;
pi->create = 0;
ret = pb_read_eof(fd, &pi->pe, pipe_entry);
if (ret <= 0)
break;
pr_info("Collected pipe entry ID %#x PIPE ID %#x\n",
pi->pe->id, pi->pe->pipe_id);
file_desc_add(&pi->d, pi->pe->id, &pipe_desc_ops);
list_for_each_entry(tmp, &pipes, list)
if (pi->pe->pipe_id == tmp->pe->pipe_id)
break;
if (&tmp->list == &pipes)
INIT_LIST_HEAD(&pi->pipe_list);
else
list_add(&pi->pipe_list, &tmp->pipe_list);
list_add_tail(&pi->list, &pipes);
}
xfree(pi);
close(fd);
return collect_pipe_data(CR_FD_PIPES_DATA, pd_hash_pipes);
}
int dump_one_pipe_data(struct pipe_data_dump *pd, int lfd, const struct fd_parms *p)
{
int img;
int pipe_size, i, bytes;
int steal_pipe[2];
int ret = -1;
if (p->flags & O_WRONLY)
return 0;
/* Maybe we've dumped it already */
for (i = 0; i < pd->nr; i++) {
if (pd->ids[i] == pipe_id(p))
return 0;
}
pr_info("Dumping data from pipe %#x fd %d\n", pipe_id(p), lfd);
if (pd->nr >= NR_PIPES_WITH_DATA) {
pr_err("OOM storing pipe\n");
return -1;
}
img = fdset_fd(glob_fdset, pd->img_type);
pd->ids[pd->nr++] = pipe_id(p);
pipe_size = fcntl(lfd, F_GETPIPE_SZ);
if (pipe_size < 0) {
pr_err("Can't obtain piped data size\n");
goto err;
}
if (pipe(steal_pipe) < 0) {
pr_perror("Can't create pipe for stealing data");
goto err;
}
bytes = tee(lfd, steal_pipe[1], pipe_size, SPLICE_F_NONBLOCK);
if (bytes > 0) {
PipeDataEntry pde = PIPE_DATA_ENTRY__INIT;
int wrote;
pde.pipe_id = pipe_id(p);
pde.bytes = bytes;
if (pb_write(img, &pde, pipe_data_entry))
goto err_close;
wrote = splice(steal_pipe[0], NULL, img, NULL, bytes, 0);
if (wrote < 0) {
pr_perror("Can't push pipe data");
goto err_close;
} else if (wrote != bytes) {
pr_err("%#x: Wanted to write %d bytes, but wrote %d\n",
pipe_id(p), bytes, wrote);
goto err_close;
}
} else if (bytes < 0) {
if (errno != EAGAIN) {
pr_perror("Can't pick pipe data");
goto err_close;
}
}
ret = 0;
err_close:
close(steal_pipe[0]);
close(steal_pipe[1]);
err:
return ret;
}
static struct pipe_data_dump pd_pipes = { .img_type = CR_FD_PIPES_DATA, };
static int dump_one_pipe(int lfd, u32 id, const struct fd_parms *p)
{
PipeEntry pe = PIPE_ENTRY__INIT;
pr_info("Dumping pipe %d with id %#x pipe_id %#x\n",
lfd, id, pipe_id(p));
pe.id = id;
pe.pipe_id = pipe_id(p);
pe.flags = p->flags;
pe.fown = (FownEntry *)&p->fown;
if (pb_write(fdset_fd(glob_fdset, CR_FD_PIPES), &pe, pipe_entry))
return -1;
return dump_one_pipe_data(&pd_pipes, lfd, p);
}
static const struct fdtype_ops pipe_ops = {
.type = FD_TYPES__PIPE,
.make_gen_id = make_gen_id,
.dump = dump_one_pipe,
};
int dump_pipe(struct fd_parms *p, int lfd, const struct cr_fdset *cr_fdset)
{
return do_dump_gen_file(p, lfd, &pipe_ops, cr_fdset);
}