2013-04-11 17:50:26 +04:00
|
|
|
#include <fcntl.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
2013-11-06 14:34:30 +04:00
|
|
|
#include "image.h"
|
2013-11-05 20:17:47 +04:00
|
|
|
#include "servicefd.h"
|
2013-04-11 17:50:26 +04:00
|
|
|
#include "page-read.h"
|
|
|
|
|
|
|
|
#include "protobuf.h"
|
|
|
|
#include "protobuf/pagemap.pb-c.h"
|
|
|
|
|
|
|
|
static int get_page_vaddr(struct page_read *pr, struct iovec *iov)
|
|
|
|
{
|
|
|
|
int ret;
|
2013-11-06 14:34:30 +04:00
|
|
|
u64 img_va;
|
2013-04-11 17:50:26 +04:00
|
|
|
|
|
|
|
ret = read_img_eof(pr->fd_pg, &img_va);
|
|
|
|
if (ret <= 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
iov->iov_base = (void *)decode_pointer(img_va);
|
|
|
|
iov->iov_len = PAGE_SIZE;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int read_page(struct page_read *pr, unsigned long vaddr, void *buf)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = read(pr->fd_pg, buf, PAGE_SIZE);
|
|
|
|
if (ret != PAGE_SIZE) {
|
2013-04-12 13:00:05 -07:00
|
|
|
pr_err("Can't read mapping page %d\n", ret);
|
2013-04-11 17:50:26 +04:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2013-12-17 19:27:04 +04:00
|
|
|
void pagemap2iovec(PagemapEntry *pe, struct iovec *iov)
|
2013-04-16 14:36:05 +04:00
|
|
|
{
|
|
|
|
iov->iov_base = decode_pointer(pe->vaddr);
|
|
|
|
iov->iov_len = pe->nr_pages * PAGE_SIZE;
|
|
|
|
}
|
|
|
|
|
2013-04-11 17:50:26 +04:00
|
|
|
static int get_pagemap(struct page_read *pr, struct iovec *iov)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
PagemapEntry *pe;
|
|
|
|
|
|
|
|
ret = pb_read_one_eof(pr->fd, &pe, PB_PAGEMAP);
|
|
|
|
if (ret <= 0)
|
|
|
|
return ret;
|
|
|
|
|
2013-04-16 14:36:05 +04:00
|
|
|
pagemap2iovec(pe, iov);
|
2013-04-11 17:51:11 +04:00
|
|
|
|
|
|
|
pr->pe = pe;
|
|
|
|
pr->cvaddr = (unsigned long)iov->iov_base;
|
|
|
|
|
|
|
|
if (pe->in_parent && !pr->parent) {
|
|
|
|
pr_err("No parent for snapshot pagemap\n");
|
|
|
|
return -1;
|
|
|
|
}
|
2013-04-11 17:50:26 +04:00
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void put_pagemap(struct page_read *pr)
|
|
|
|
{
|
2013-04-11 17:51:11 +04:00
|
|
|
pagemap_entry__free_unpacked(pr->pe, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int read_pagemap_page(struct page_read *pr, unsigned long vaddr, void *buf);
|
|
|
|
|
|
|
|
static void skip_pagemap_pages(struct page_read *pr, unsigned long len)
|
|
|
|
{
|
|
|
|
if (!len)
|
|
|
|
return;
|
|
|
|
|
|
|
|
pr_debug("\tpr%u Skip %lx bytes from page-dump\n", pr->id, len);
|
|
|
|
if (!pr->pe->in_parent)
|
|
|
|
lseek(pr->fd_pg, len, SEEK_CUR);
|
|
|
|
pr->cvaddr += len;
|
|
|
|
}
|
|
|
|
|
2013-12-17 19:27:05 +04:00
|
|
|
int seek_pagemap_page(struct page_read *pr, unsigned long vaddr, bool warn)
|
2013-04-11 17:51:11 +04:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct iovec iov;
|
|
|
|
|
2013-04-16 14:36:05 +04:00
|
|
|
if (pr->pe)
|
|
|
|
pagemap2iovec(pr->pe, &iov);
|
|
|
|
else
|
2013-04-11 17:51:11 +04:00
|
|
|
goto new_pagemap;
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
unsigned long iov_end;
|
|
|
|
|
2013-12-17 19:27:01 +04:00
|
|
|
if (vaddr < pr->cvaddr) {
|
|
|
|
if (warn)
|
|
|
|
pr_err("Missing %lu in parent pagemap, current iov: base=%lu,len=%lu\n",
|
|
|
|
vaddr, (unsigned long)iov.iov_base, iov.iov_len);
|
|
|
|
return -1;
|
|
|
|
}
|
2013-04-11 17:51:11 +04:00
|
|
|
iov_end = (unsigned long)iov.iov_base + iov.iov_len;
|
|
|
|
|
|
|
|
if (iov_end <= vaddr) {
|
|
|
|
skip_pagemap_pages(pr, iov_end - pr->cvaddr);
|
|
|
|
put_pagemap(pr);
|
|
|
|
new_pagemap:
|
|
|
|
ret = get_pagemap(pr, &iov);
|
|
|
|
if (ret <= 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
skip_pagemap_pages(pr, vaddr - pr->cvaddr);
|
2013-12-17 19:27:03 +04:00
|
|
|
return 0;
|
2013-04-11 17:51:11 +04:00
|
|
|
}
|
2013-04-11 17:50:26 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static int read_pagemap_page(struct page_read *pr, unsigned long vaddr, void *buf)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
2013-04-11 17:51:11 +04:00
|
|
|
if (pr->pe->in_parent) {
|
|
|
|
pr_debug("\tpr%u Read page %lx from parent\n", pr->id, vaddr);
|
2013-12-17 19:27:03 +04:00
|
|
|
ret = seek_pagemap_page(pr->parent, vaddr, true);
|
|
|
|
if (ret == -1)
|
|
|
|
return ret;
|
|
|
|
ret = read_pagemap_page(pr->parent, vaddr, buf);
|
2013-12-02 15:28:59 +04:00
|
|
|
if (ret == -1)
|
|
|
|
return ret;
|
2013-04-11 17:51:11 +04:00
|
|
|
} else {
|
2013-12-17 19:27:00 +04:00
|
|
|
off_t current_vaddr = lseek(pr->fd_pg, 0, SEEK_CUR);
|
2013-04-30 06:05:27 +00:00
|
|
|
pr_debug("\tpr%u Read page %lx from self %lx/%"PRIx64"\n", pr->id,
|
2013-12-17 19:27:00 +04:00
|
|
|
vaddr, pr->cvaddr, current_vaddr);
|
|
|
|
if (current_vaddr != lseek(pr->fd_pg, current_vaddr, SEEK_DATA)) {
|
|
|
|
pr_perror("Can't read page because of hole /%"PRIx64, current_vaddr);
|
|
|
|
return -1;
|
|
|
|
}
|
2013-04-11 17:51:11 +04:00
|
|
|
ret = read(pr->fd_pg, buf, PAGE_SIZE);
|
|
|
|
if (ret != PAGE_SIZE) {
|
2013-08-28 17:00:17 +04:00
|
|
|
pr_perror("Can't read mapping page %d", ret);
|
2013-04-11 17:51:11 +04:00
|
|
|
return -1;
|
|
|
|
}
|
2013-04-11 17:50:26 +04:00
|
|
|
}
|
|
|
|
|
2013-04-11 17:51:11 +04:00
|
|
|
pr->cvaddr += PAGE_SIZE;
|
|
|
|
|
2013-04-11 17:50:26 +04:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void close_page_read(struct page_read *pr)
|
|
|
|
{
|
2013-04-11 17:51:11 +04:00
|
|
|
if (pr->parent) {
|
|
|
|
close_page_read(pr->parent);
|
|
|
|
xfree(pr->parent);
|
|
|
|
}
|
|
|
|
|
2013-04-11 17:50:26 +04:00
|
|
|
close(pr->fd_pg);
|
|
|
|
close(pr->fd);
|
|
|
|
}
|
|
|
|
|
2013-04-11 17:51:11 +04:00
|
|
|
static int open_page_read_at(int dfd, int pid, struct page_read *pr);
|
|
|
|
|
|
|
|
static int try_open_parent(int dfd, int pid, struct page_read *pr)
|
|
|
|
{
|
|
|
|
int pfd;
|
|
|
|
struct page_read *parent = NULL;
|
|
|
|
|
|
|
|
pfd = openat(dfd, CR_PARENT_LINK, O_RDONLY);
|
|
|
|
if (pfd < 0 && errno == ENOENT)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
parent = xmalloc(sizeof(*parent));
|
|
|
|
if (!parent)
|
|
|
|
goto err_cl;
|
|
|
|
|
2013-06-26 01:27:09 +04:00
|
|
|
if (open_page_read_at(pfd, pid, parent)) {
|
|
|
|
if (errno != ENOENT)
|
|
|
|
goto err_free;
|
|
|
|
xfree(parent);
|
|
|
|
parent = NULL;
|
|
|
|
}
|
2013-04-11 17:51:11 +04:00
|
|
|
|
|
|
|
close(pfd);
|
|
|
|
out:
|
|
|
|
pr->parent = parent;
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
err_free:
|
|
|
|
xfree(parent);
|
|
|
|
err_cl:
|
|
|
|
close(pfd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2013-04-11 17:50:26 +04:00
|
|
|
static int open_page_read_at(int dfd, int pid, struct page_read *pr)
|
|
|
|
{
|
2013-06-22 13:15:27 +04:00
|
|
|
pr->pe = NULL;
|
|
|
|
|
2013-04-11 17:50:26 +04:00
|
|
|
pr->fd = open_image_at(dfd, CR_FD_PAGEMAP, O_RSTR, (long)pid);
|
|
|
|
if (pr->fd < 0) {
|
|
|
|
pr->fd_pg = open_image_at(dfd, CR_FD_PAGES_OLD, O_RSTR, pid);
|
|
|
|
if (pr->fd_pg < 0)
|
|
|
|
return -1;
|
|
|
|
|
2013-04-11 17:51:11 +04:00
|
|
|
pr->parent = NULL;
|
2013-04-11 17:50:26 +04:00
|
|
|
pr->get_pagemap = get_page_vaddr;
|
|
|
|
pr->put_pagemap = NULL;
|
|
|
|
pr->read_page = read_page;
|
|
|
|
} else {
|
2013-04-11 17:51:11 +04:00
|
|
|
static unsigned ids = 1;
|
|
|
|
|
|
|
|
if (try_open_parent(dfd, pid, pr)) {
|
|
|
|
close(pr->fd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2013-04-11 17:50:26 +04:00
|
|
|
pr->fd_pg = open_pages_image_at(dfd, O_RSTR, pr->fd);
|
|
|
|
if (pr->fd_pg < 0) {
|
|
|
|
close_page_read(pr);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
pr->get_pagemap = get_pagemap;
|
|
|
|
pr->put_pagemap = put_pagemap;
|
|
|
|
pr->read_page = read_pagemap_page;
|
2013-04-11 17:51:11 +04:00
|
|
|
pr->id = ids++;
|
|
|
|
|
|
|
|
pr_debug("Opened page read %u (parent %u)\n",
|
|
|
|
pr->id, pr->parent ? pr->parent->id : 0);
|
2013-04-11 17:50:26 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
pr->close = close_page_read;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int open_page_read(int pid, struct page_read *pr)
|
|
|
|
{
|
|
|
|
return open_page_read_at(get_service_fd(IMG_FD_OFF), pid, pr);
|
|
|
|
}
|