2012-06-20 20:00:30 +04:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <errno.h>
|
2012-09-26 06:22:52 +04:00
|
|
|
#include <string.h>
|
2012-06-20 20:00:30 +04:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
|
|
|
#include "crtools.h"
|
2012-09-26 06:22:52 +04:00
|
|
|
#include "file-ids.h"
|
2012-08-01 07:00:48 +04:00
|
|
|
#include "mount.h"
|
2012-06-20 20:00:30 +04:00
|
|
|
#include "files.h"
|
|
|
|
#include "image.h"
|
|
|
|
#include "list.h"
|
|
|
|
#include "util.h"
|
2013-01-09 17:02:47 +04:00
|
|
|
#include "asm/atomic.h"
|
2012-06-20 20:00:30 +04:00
|
|
|
|
2012-07-17 07:17:02 +04:00
|
|
|
#include "protobuf.h"
|
|
|
|
#include "protobuf/regfile.pb-c.h"
|
2012-07-12 13:07:00 +04:00
|
|
|
#include "protobuf/remap-file-path.pb-c.h"
|
2012-07-17 07:17:02 +04:00
|
|
|
|
2012-06-20 20:00:30 +04:00
|
|
|
#include "files-reg.h"
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Ghost files are those not visible from the FS. Dumping them is
|
|
|
|
* nasty and the only way we have -- just carry its contents with
|
|
|
|
* us. Any brave soul to implement link unlinked file back?
|
|
|
|
*/
|
2012-07-01 05:48:15 +04:00
|
|
|
struct ghost_file {
|
2012-06-20 20:54:00 +04:00
|
|
|
struct list_head list;
|
|
|
|
u32 id;
|
2012-12-06 11:11:10 +04:00
|
|
|
|
|
|
|
u32 dev;
|
|
|
|
u32 ino;
|
|
|
|
|
|
|
|
struct file_remap remap;
|
2012-06-20 20:00:30 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
static u32 ghost_file_ids = 1;
|
|
|
|
static LIST_HEAD(ghost_files);
|
|
|
|
|
2012-09-17 20:12:58 +04:00
|
|
|
static mutex_t *ghost_file_mutex;
|
|
|
|
|
2012-06-20 20:00:30 +04:00
|
|
|
/*
|
|
|
|
* This constant is selected without any calculations. Just do not
|
|
|
|
* want to pick up too big files with us in the image.
|
|
|
|
*/
|
|
|
|
#define MAX_GHOST_FILE_SIZE (1 * 1024 * 1024)
|
|
|
|
|
|
|
|
static int open_remap_ghost(struct reg_file_info *rfi,
|
2012-07-12 13:07:00 +04:00
|
|
|
RemapFilePathEntry *rfe)
|
2012-06-20 20:00:30 +04:00
|
|
|
{
|
|
|
|
struct ghost_file *gf;
|
2012-07-17 07:17:51 +04:00
|
|
|
GhostFileEntry *gfe = NULL;
|
2012-07-01 17:14:32 +04:00
|
|
|
int gfd, ifd, ghost_flags;
|
2012-06-20 20:00:30 +04:00
|
|
|
|
2012-09-26 06:16:51 +04:00
|
|
|
rfe->remap_id &= ~REMAP_GHOST;
|
2012-06-20 20:00:30 +04:00
|
|
|
list_for_each_entry(gf, &ghost_files, list)
|
|
|
|
if (gf->id == rfe->remap_id)
|
|
|
|
goto gf_found;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Ghost not found. We will create one in the same dir
|
|
|
|
* as the very first client of it thus resolving any
|
|
|
|
* issues with cross-device links.
|
|
|
|
*/
|
|
|
|
|
|
|
|
pr_info("Opening ghost file %#x for %s\n", rfe->remap_id, rfi->path);
|
|
|
|
|
2012-09-11 17:59:59 +04:00
|
|
|
gf = shmalloc(sizeof(*gf));
|
2012-06-20 20:00:30 +04:00
|
|
|
if (!gf)
|
|
|
|
return -1;
|
2012-09-26 06:20:06 +04:00
|
|
|
gf->remap.path = xmalloc(PATH_MAX);
|
|
|
|
if (!gf->remap.path)
|
2012-06-20 20:54:00 +04:00
|
|
|
goto err;
|
2012-06-20 20:00:30 +04:00
|
|
|
|
|
|
|
ifd = open_image_ro(CR_FD_GHOST_FILE, rfe->remap_id);
|
|
|
|
if (ifd < 0)
|
2012-06-20 20:54:00 +04:00
|
|
|
goto err;
|
2012-06-20 20:00:30 +04:00
|
|
|
|
2012-08-07 02:42:58 +04:00
|
|
|
if (pb_read_one(ifd, &gfe, PB_GHOST_FILE) < 0)
|
2012-10-24 16:51:50 +04:00
|
|
|
goto close_ifd;
|
2012-06-20 20:00:30 +04:00
|
|
|
|
2012-12-06 11:11:10 +04:00
|
|
|
/*
|
|
|
|
* For old formats where optional has_[dev|ino] is
|
|
|
|
* not present we will have zeros here which is quite
|
|
|
|
* a sign for "absent" fields.
|
|
|
|
*/
|
|
|
|
gf->dev = gfe->dev;
|
|
|
|
gf->ino = gfe->ino;
|
|
|
|
|
2012-09-26 06:20:06 +04:00
|
|
|
snprintf(gf->remap.path, PATH_MAX, "%s.cr.%x.ghost", rfi->path, rfe->remap_id);
|
2012-07-01 17:14:32 +04:00
|
|
|
|
2012-07-17 07:17:51 +04:00
|
|
|
if (S_ISFIFO(gfe->mode)) {
|
2012-09-26 06:20:06 +04:00
|
|
|
if (mknod(gf->remap.path, gfe->mode, 0)) {
|
2012-07-01 17:14:32 +04:00
|
|
|
pr_perror("Can't create node for ghost file\n");
|
2012-10-24 16:51:50 +04:00
|
|
|
goto close_ifd;
|
2012-07-01 17:14:32 +04:00
|
|
|
}
|
|
|
|
ghost_flags = O_RDWR; /* To not block */
|
|
|
|
} else
|
|
|
|
ghost_flags = O_WRONLY | O_CREAT | O_EXCL;
|
|
|
|
|
2012-09-26 06:20:06 +04:00
|
|
|
gfd = open(gf->remap.path, ghost_flags, gfe->mode);
|
2012-06-20 20:00:30 +04:00
|
|
|
if (gfd < 0) {
|
|
|
|
pr_perror("Can't open ghost file");
|
2012-10-24 16:51:50 +04:00
|
|
|
goto close_ifd;
|
2012-06-20 20:00:30 +04:00
|
|
|
}
|
|
|
|
|
2012-07-17 07:17:51 +04:00
|
|
|
if (fchown(gfd, gfe->uid, gfe->gid) < 0) {
|
2012-06-20 20:00:30 +04:00
|
|
|
pr_perror("Can't reset user/group on ghost %#x\n", rfe->remap_id);
|
2012-10-24 16:51:50 +04:00
|
|
|
goto close_all;
|
2012-06-20 20:00:30 +04:00
|
|
|
}
|
|
|
|
|
2012-07-17 07:17:51 +04:00
|
|
|
if (S_ISREG(gfe->mode)) {
|
2012-07-01 17:14:32 +04:00
|
|
|
if (copy_file(ifd, gfd, 0) < 0)
|
2012-10-24 16:51:50 +04:00
|
|
|
goto close_all;
|
2012-07-01 17:14:32 +04:00
|
|
|
}
|
2012-06-20 20:00:30 +04:00
|
|
|
|
2012-07-17 07:17:51 +04:00
|
|
|
ghost_file_entry__free_unpacked(gfe, NULL);
|
2012-06-20 20:00:30 +04:00
|
|
|
close(ifd);
|
|
|
|
close(gfd);
|
|
|
|
|
|
|
|
gf->id = rfe->remap_id;
|
2012-09-26 06:20:06 +04:00
|
|
|
gf->remap.users = 0;
|
2012-06-20 20:00:30 +04:00
|
|
|
list_add_tail(&gf->list, &ghost_files);
|
|
|
|
gf_found:
|
2012-09-26 06:20:06 +04:00
|
|
|
gf->remap.users++;
|
|
|
|
rfi->remap = &gf->remap;
|
2012-06-20 20:00:30 +04:00
|
|
|
return 0;
|
2012-06-20 20:54:00 +04:00
|
|
|
|
2012-10-24 16:51:50 +04:00
|
|
|
close_all:
|
|
|
|
close_safe(&gfd);
|
|
|
|
close_ifd:
|
|
|
|
close_safe(&ifd);
|
2012-06-20 20:54:00 +04:00
|
|
|
err:
|
2012-07-17 07:17:51 +04:00
|
|
|
if (gfe)
|
|
|
|
ghost_file_entry__free_unpacked(gfe, NULL);
|
2012-09-26 06:20:06 +04:00
|
|
|
xfree(gf->remap.path);
|
2012-09-11 17:59:59 +04:00
|
|
|
shfree_last(gf);
|
2012-06-20 20:54:00 +04:00
|
|
|
return -1;
|
2012-06-20 20:00:30 +04:00
|
|
|
}
|
|
|
|
|
2012-09-26 06:22:52 +04:00
|
|
|
static int open_remap_linked(struct reg_file_info *rfi,
|
|
|
|
RemapFilePathEntry *rfe)
|
|
|
|
{
|
|
|
|
struct file_remap *rm;
|
|
|
|
struct file_desc *rdesc;
|
|
|
|
struct reg_file_info *rrfi;
|
|
|
|
|
|
|
|
rdesc = find_file_desc_raw(FD_TYPES__REG, rfe->remap_id);
|
|
|
|
if (!rdesc) {
|
|
|
|
pr_err("Can't find target file %x\n", rfe->remap_id);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
rm = xmalloc(sizeof(*rm));
|
|
|
|
if (!rm)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
rrfi = container_of(rdesc, struct reg_file_info, d);
|
|
|
|
pr_info("Remapped %s -> %s\n", rfi->path, rrfi->path);
|
|
|
|
|
|
|
|
rm->path = rrfi->path;
|
|
|
|
rm->users = 1;
|
|
|
|
rfi->remap = rm;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-06-20 20:00:30 +04:00
|
|
|
static int collect_remaps(void)
|
|
|
|
{
|
|
|
|
int fd, ret = 0;
|
|
|
|
|
|
|
|
fd = open_image_ro(CR_FD_REMAP_FPATH);
|
|
|
|
if (fd < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
while (1) {
|
2012-07-12 13:07:00 +04:00
|
|
|
RemapFilePathEntry *rfe = NULL;
|
2012-06-20 20:00:30 +04:00
|
|
|
struct file_desc *fdesc;
|
|
|
|
struct reg_file_info *rfi;
|
|
|
|
|
2012-08-07 02:42:58 +04:00
|
|
|
ret = pb_read_one_eof(fd, &rfe, PB_REMAP_FPATH);
|
2012-06-20 20:00:30 +04:00
|
|
|
if (ret <= 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
ret = -1;
|
2012-07-19 10:18:37 +04:00
|
|
|
fdesc = find_file_desc_raw(FD_TYPES__REG, rfe->orig_id);
|
2012-06-20 20:00:30 +04:00
|
|
|
if (fdesc == NULL) {
|
|
|
|
pr_err("Remap for non existing file %#x\n",
|
2012-07-12 13:07:00 +04:00
|
|
|
rfe->orig_id);
|
|
|
|
goto tail;
|
2012-06-20 20:00:30 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
rfi = container_of(fdesc, struct reg_file_info, d);
|
2012-07-12 13:07:00 +04:00
|
|
|
pr_info("Configuring remap %#x -> %#x\n", rfi->rfe->id, rfe->remap_id);
|
2012-09-26 06:22:52 +04:00
|
|
|
|
|
|
|
if (rfe->remap_id & REMAP_GHOST)
|
|
|
|
ret = open_remap_ghost(rfi, rfe);
|
|
|
|
else
|
|
|
|
ret = open_remap_linked(rfi, rfe);
|
2012-07-12 13:07:00 +04:00
|
|
|
tail:
|
|
|
|
remap_file_path_entry__free_unpacked(rfe, NULL);
|
|
|
|
if (ret)
|
2012-06-20 20:00:30 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
close(fd);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dump_ghost_file(int _fd, u32 id, const struct stat *st)
|
|
|
|
{
|
2012-12-05 22:59:35 +03:00
|
|
|
int img;
|
2012-07-17 07:17:51 +04:00
|
|
|
GhostFileEntry gfe = GHOST_FILE_ENTRY__INIT;
|
2012-06-20 20:00:30 +04:00
|
|
|
char lpath[32];
|
|
|
|
|
|
|
|
pr_info("Dumping ghost file contents (id %#x)\n", id);
|
|
|
|
|
|
|
|
img = open_image(CR_FD_GHOST_FILE, O_DUMP, id);
|
|
|
|
if (img < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
gfe.uid = st->st_uid;
|
|
|
|
gfe.gid = st->st_gid;
|
|
|
|
gfe.mode = st->st_mode;
|
|
|
|
|
2012-12-06 11:11:10 +04:00
|
|
|
gfe.has_dev = gfe.has_ino = true;
|
|
|
|
gfe.dev = MKKDEV(MAJOR(st->st_dev), MINOR(st->st_dev));
|
|
|
|
gfe.ino = st->st_ino;
|
|
|
|
|
2012-08-07 02:26:50 +04:00
|
|
|
if (pb_write_one(img, &gfe, PB_GHOST_FILE))
|
2012-06-20 20:00:30 +04:00
|
|
|
return -1;
|
|
|
|
|
2012-07-01 17:14:32 +04:00
|
|
|
if (S_ISREG(st->st_mode)) {
|
2012-12-05 22:59:35 +03:00
|
|
|
int fd;
|
|
|
|
|
2012-07-01 17:14:32 +04:00
|
|
|
/*
|
|
|
|
* Reopen file locally since it may have no read
|
|
|
|
* permissions when drained
|
|
|
|
*/
|
|
|
|
snprintf(lpath, sizeof(lpath), "/proc/self/fd/%d", _fd);
|
|
|
|
fd = open(lpath, O_RDONLY);
|
|
|
|
if (fd < 0) {
|
|
|
|
pr_perror("Can't open ghost original file");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (copy_file(fd, img, st->st_size))
|
|
|
|
return -1;
|
2012-12-05 22:59:35 +03:00
|
|
|
|
|
|
|
close(fd);
|
2012-07-01 17:14:32 +04:00
|
|
|
}
|
2012-06-20 20:00:30 +04:00
|
|
|
|
|
|
|
close(img);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-12-06 11:10:14 +04:00
|
|
|
void remap_put(struct file_remap *remap)
|
|
|
|
{
|
|
|
|
mutex_lock(ghost_file_mutex);
|
|
|
|
if (--remap->users == 0) {
|
|
|
|
pr_info("Unlink the ghost %s\n", remap->path);
|
|
|
|
unlink(remap->path);
|
|
|
|
}
|
|
|
|
mutex_unlock(ghost_file_mutex);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct file_remap *lookup_ghost_remap(u32 dev, u32 ino)
|
|
|
|
{
|
|
|
|
struct ghost_file *gf;
|
|
|
|
|
|
|
|
mutex_lock(ghost_file_mutex);
|
|
|
|
list_for_each_entry(gf, &ghost_files, list) {
|
|
|
|
if (gf->dev == dev && gf->ino == ino) {
|
|
|
|
gf->remap.users++;
|
|
|
|
mutex_unlock(ghost_file_mutex);
|
|
|
|
return &gf->remap;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
mutex_unlock(ghost_file_mutex);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-06-20 20:00:30 +04:00
|
|
|
static int dump_ghost_remap(char *path, const struct stat *st, int lfd, u32 id)
|
|
|
|
{
|
2012-07-01 05:48:15 +04:00
|
|
|
struct ghost_file *gf;
|
2012-07-12 13:07:00 +04:00
|
|
|
RemapFilePathEntry rpe = REMAP_FILE_PATH_ENTRY__INIT;
|
2012-06-20 20:00:30 +04:00
|
|
|
|
|
|
|
pr_info("Dumping ghost file for fd %d id %#x\n", lfd, id);
|
|
|
|
|
|
|
|
if (st->st_size > MAX_GHOST_FILE_SIZE) {
|
|
|
|
pr_err("Can't dump ghost file %s of %lu size\n",
|
|
|
|
path, st->st_size);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
list_for_each_entry(gf, &ghost_files, list)
|
|
|
|
if ((gf->dev == st->st_dev) && (gf->ino == st->st_ino))
|
|
|
|
goto dump_entry;
|
|
|
|
|
|
|
|
gf = xmalloc(sizeof(*gf));
|
|
|
|
if (gf == NULL)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
gf->dev = st->st_dev;
|
|
|
|
gf->ino = st->st_ino;
|
|
|
|
gf->id = ghost_file_ids++;
|
|
|
|
list_add_tail(&gf->list, &ghost_files);
|
|
|
|
|
|
|
|
if (dump_ghost_file(lfd, gf->id, st))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
dump_entry:
|
|
|
|
rpe.orig_id = id;
|
|
|
|
rpe.remap_id = gf->id | REMAP_GHOST;
|
|
|
|
|
2012-08-07 02:26:50 +04:00
|
|
|
return pb_write_one(fdset_fd(glob_fdset, CR_FD_REMAP_FPATH),
|
|
|
|
&rpe, PB_REMAP_FPATH);
|
2012-06-20 20:00:30 +04:00
|
|
|
}
|
|
|
|
|
2012-09-26 06:22:52 +04:00
|
|
|
static int create_link_remap(char *path, int len, int lfd, u32 *idp)
|
|
|
|
{
|
|
|
|
char link_name[PATH_MAX], *tmp;
|
|
|
|
RegFileEntry rfe = REG_FILE_ENTRY__INIT;
|
|
|
|
FownEntry fwn = FOWN_ENTRY__INIT;
|
|
|
|
|
2012-09-26 06:39:23 +04:00
|
|
|
if (!opts.link_remap_ok) {
|
|
|
|
pr_err("Can't create link remap for %s. "
|
|
|
|
"Use " LREMAP_PARAM " option.\n", path);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-09-26 06:22:52 +04:00
|
|
|
/*
|
|
|
|
* Linked remapping -- we create a hard link on a removed file
|
|
|
|
* in the directory original file used to sit.
|
|
|
|
*
|
|
|
|
* Bad news is than we can't easily open lfd's parent dir. Thus
|
|
|
|
* we have to just generate an absolute path and use it. The linkat
|
|
|
|
* will fail if we chose the bad one.
|
|
|
|
*/
|
|
|
|
|
2012-09-28 19:15:52 +04:00
|
|
|
link_name[0] = '.';
|
|
|
|
memcpy(link_name + 1, path, len);
|
|
|
|
tmp = link_name + len + 1;
|
2012-09-26 06:22:52 +04:00
|
|
|
while (*tmp != '/') {
|
|
|
|
BUG_ON(tmp == link_name);
|
|
|
|
tmp--;
|
|
|
|
}
|
|
|
|
|
|
|
|
rfe.id = *idp = fd_id_generate_special();
|
|
|
|
rfe.flags = 0;
|
|
|
|
rfe.pos = 0;
|
|
|
|
rfe.fown = &fwn;
|
2012-09-28 19:15:52 +04:00
|
|
|
rfe.name = link_name + 1;
|
2012-09-26 06:22:52 +04:00
|
|
|
|
|
|
|
/* Any 'unique' name works here actually. Remap works by reg-file ids. */
|
|
|
|
sprintf(tmp + 1, "link_remap.%d", rfe.id);
|
|
|
|
|
2012-09-28 19:15:52 +04:00
|
|
|
if (linkat(lfd, "", mntns_root, link_name, AT_EMPTY_PATH) < 0) {
|
2012-09-26 06:22:52 +04:00
|
|
|
pr_perror("Can't link remap to %s", path);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return pb_write_one(fdset_fd(glob_fdset, CR_FD_REG_FILES), &rfe, PB_REG_FILES);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dump_linked_remap(char *path, int len, const struct stat *ost, int lfd, u32 id)
|
|
|
|
{
|
|
|
|
u32 lid;
|
|
|
|
RemapFilePathEntry rpe = REMAP_FILE_PATH_ENTRY__INIT;
|
|
|
|
|
|
|
|
if (create_link_remap(path, len, lfd, &lid))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
rpe.orig_id = id;
|
|
|
|
rpe.remap_id = lid;
|
|
|
|
|
|
|
|
return pb_write_one(fdset_fd(glob_fdset, CR_FD_REMAP_FPATH),
|
|
|
|
&rpe, PB_REMAP_FPATH);
|
|
|
|
}
|
|
|
|
|
2012-09-26 06:15:43 +04:00
|
|
|
static int check_path_remap(char *rpath, int plen, const struct stat *ost, int lfd, u32 id)
|
2012-06-20 20:00:30 +04:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct stat pst;
|
|
|
|
|
|
|
|
if (ost->st_nlink == 0)
|
|
|
|
/*
|
|
|
|
* Unpleasant, but easy case. File is completely invisible
|
|
|
|
* from the FS. Just dump its contents and that's it. But
|
|
|
|
* be careful whether anybody still has any of its hardlinks
|
|
|
|
* also open.
|
|
|
|
*/
|
2012-08-01 07:00:48 +04:00
|
|
|
return dump_ghost_remap(rpath + 1, ost, lfd, id);
|
2012-06-20 20:00:30 +04:00
|
|
|
|
2012-08-01 07:00:48 +04:00
|
|
|
ret = fstatat(mntns_root, rpath, &pst, 0);
|
2012-06-20 20:00:30 +04:00
|
|
|
if (ret < 0) {
|
|
|
|
/*
|
|
|
|
* FIXME linked file, but path is not accessible (unless any
|
|
|
|
* other error occurred). We can create a temporary link to it
|
|
|
|
* uning linkat with AT_EMPTY_PATH flag and remap it to this
|
|
|
|
* name.
|
|
|
|
*/
|
2012-09-26 06:22:52 +04:00
|
|
|
|
|
|
|
if (errno == ENOENT)
|
|
|
|
return dump_linked_remap(rpath + 1, plen - 1, ost, lfd, id);
|
|
|
|
|
2012-06-20 20:00:30 +04:00
|
|
|
pr_perror("Can't stat path");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((pst.st_ino != ost->st_ino) || (pst.st_dev != ost->st_dev)) {
|
2012-08-07 19:33:49 +04:00
|
|
|
if (opts.evasive_devices &&
|
|
|
|
(S_ISCHR(ost->st_mode) || S_ISBLK(ost->st_mode)) &&
|
|
|
|
pst.st_rdev == ost->st_rdev)
|
|
|
|
return 0;
|
2012-06-20 20:00:30 +04:00
|
|
|
/*
|
|
|
|
* FIXME linked file, but the name we see it by is reused
|
|
|
|
* by somebody else.
|
|
|
|
*/
|
|
|
|
pr_err("Unaccessible path opened %u:%u, need %u:%u\n",
|
|
|
|
(int)pst.st_dev, (int)pst.st_ino,
|
|
|
|
(int)ost->st_dev, (int)ost->st_ino);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* File is linked and visible by the name it is opened by
|
|
|
|
* this task. Go ahead and dump it.
|
|
|
|
*/
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int dump_one_reg_file(int lfd, u32 id, const struct fd_parms *p)
|
|
|
|
{
|
|
|
|
char fd_str[128];
|
2012-08-06 18:28:27 +04:00
|
|
|
char rpath[PATH_MAX + 1] = ".", *path = rpath + 1;
|
2012-06-20 20:00:30 +04:00
|
|
|
int len, rfd;
|
2012-07-17 07:17:02 +04:00
|
|
|
|
|
|
|
RegFileEntry rfe = REG_FILE_ENTRY__INIT;
|
2012-06-20 20:00:30 +04:00
|
|
|
|
|
|
|
snprintf(fd_str, sizeof(fd_str), "/proc/self/fd/%d", lfd);
|
2012-08-01 07:00:48 +04:00
|
|
|
len = readlink(fd_str, path, sizeof(rpath) - 2);
|
2012-06-20 20:00:30 +04:00
|
|
|
if (len < 0) {
|
|
|
|
pr_perror("Can't readlink %s", fd_str);
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
2012-06-20 20:54:00 +04:00
|
|
|
path[len] = '\0';
|
2012-06-20 20:00:30 +04:00
|
|
|
pr_info("Dumping path for %d fd via self %d [%s]\n",
|
2012-06-20 20:54:00 +04:00
|
|
|
p->fd, lfd, path);
|
2012-06-20 20:00:30 +04:00
|
|
|
|
2012-09-26 06:15:43 +04:00
|
|
|
if (check_path_remap(rpath, len, &p->stat, lfd, id))
|
2012-06-20 20:00:30 +04:00
|
|
|
return -1;
|
|
|
|
|
2012-07-17 07:17:02 +04:00
|
|
|
rfe.id = id;
|
|
|
|
rfe.flags = p->flags;
|
|
|
|
rfe.pos = p->pos;
|
2012-07-19 09:39:00 +04:00
|
|
|
rfe.fown = (FownEntry *)&p->fown;
|
2012-07-17 07:17:02 +04:00
|
|
|
rfe.name = path;
|
2012-06-20 20:00:30 +04:00
|
|
|
|
2012-07-17 07:17:02 +04:00
|
|
|
rfd = fdset_fd(glob_fdset, CR_FD_REG_FILES);
|
2012-06-20 20:00:30 +04:00
|
|
|
|
2012-08-07 02:26:50 +04:00
|
|
|
return pb_write_one(rfd, &rfe, PB_REG_FILES);
|
2012-06-20 20:00:30 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static const struct fdtype_ops regfile_ops = {
|
2012-07-19 10:18:37 +04:00
|
|
|
.type = FD_TYPES__REG,
|
2012-06-20 20:00:30 +04:00
|
|
|
.dump = dump_one_reg_file,
|
|
|
|
};
|
|
|
|
|
|
|
|
int dump_reg_file(struct fd_parms *p, int lfd,
|
|
|
|
const struct cr_fdset *cr_fdset)
|
|
|
|
{
|
|
|
|
return do_dump_gen_file(p, lfd, ®file_ops, cr_fdset);
|
|
|
|
}
|
|
|
|
|
2012-07-01 17:56:00 +04:00
|
|
|
static int open_path(struct file_desc *d,
|
|
|
|
int(*open_cb)(struct reg_file_info *, void *), void *arg)
|
2012-06-20 20:00:30 +04:00
|
|
|
{
|
|
|
|
struct reg_file_info *rfi;
|
|
|
|
int tmp;
|
|
|
|
|
|
|
|
rfi = container_of(d, struct reg_file_info, d);
|
|
|
|
|
2012-09-26 06:20:06 +04:00
|
|
|
if (rfi->remap) {
|
2012-09-17 20:12:58 +04:00
|
|
|
mutex_lock(ghost_file_mutex);
|
2012-09-26 06:20:06 +04:00
|
|
|
if (link(rfi->remap->path, rfi->path) < 0) {
|
2012-06-20 20:00:30 +04:00
|
|
|
pr_perror("Can't link %s -> %s\n",
|
2012-09-26 06:20:06 +04:00
|
|
|
rfi->remap->path, rfi->path);
|
2012-06-20 20:00:30 +04:00
|
|
|
return -1;
|
|
|
|
}
|
2012-09-17 20:12:58 +04:00
|
|
|
}
|
2012-06-20 20:00:30 +04:00
|
|
|
|
2012-07-01 17:56:00 +04:00
|
|
|
tmp = open_cb(rfi, arg);
|
2012-06-20 20:00:30 +04:00
|
|
|
if (tmp < 0) {
|
|
|
|
pr_perror("Can't open file %s", rfi->path);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-09-26 06:20:06 +04:00
|
|
|
if (rfi->remap) {
|
2012-06-20 20:00:30 +04:00
|
|
|
unlink(rfi->path);
|
2012-09-26 06:20:06 +04:00
|
|
|
BUG_ON(!rfi->remap->users);
|
|
|
|
if (--rfi->remap->users == 0) {
|
|
|
|
pr_info("Unlink the ghost %s\n", rfi->remap->path);
|
|
|
|
unlink(rfi->remap->path);
|
2012-09-11 17:59:59 +04:00
|
|
|
}
|
2012-09-17 20:12:58 +04:00
|
|
|
mutex_unlock(ghost_file_mutex);
|
2012-09-11 17:59:59 +04:00
|
|
|
}
|
2012-06-20 20:00:30 +04:00
|
|
|
|
2012-07-19 09:39:00 +04:00
|
|
|
if (restore_fown(tmp, rfi->rfe->fown))
|
2012-06-20 20:00:30 +04:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
return tmp;
|
|
|
|
}
|
2012-06-20 20:54:00 +04:00
|
|
|
|
2012-07-01 17:56:00 +04:00
|
|
|
int open_path_by_id(u32 id, int (*open_cb)(struct reg_file_info *, void *), void *arg)
|
2012-06-20 20:00:30 +04:00
|
|
|
{
|
|
|
|
struct file_desc *fd;
|
|
|
|
|
2012-07-19 10:18:37 +04:00
|
|
|
fd = find_file_desc_raw(FD_TYPES__REG, id);
|
2012-06-20 20:00:30 +04:00
|
|
|
if (fd == NULL) {
|
|
|
|
pr_perror("Can't find regfile for %#x\n", id);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-07-01 17:56:00 +04:00
|
|
|
return open_path(fd, open_cb, arg);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int do_open_reg(struct reg_file_info *rfi, void *arg)
|
|
|
|
{
|
2012-07-12 08:37:39 +04:00
|
|
|
int fd;
|
|
|
|
|
2012-07-17 07:17:02 +04:00
|
|
|
fd = open(rfi->path, rfi->rfe->flags);
|
2012-07-12 08:37:39 +04:00
|
|
|
if (fd < 0) {
|
|
|
|
pr_perror("Can't open file on restore");
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
2012-07-17 07:17:02 +04:00
|
|
|
if (lseek(fd, rfi->rfe->pos, SEEK_SET) < 0) {
|
2012-07-12 08:37:39 +04:00
|
|
|
pr_perror("Can't restore file pos");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return fd;
|
2012-07-01 17:56:00 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static int open_fe_fd(struct file_desc *fd)
|
|
|
|
{
|
|
|
|
return open_path(fd, do_open_reg, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
int open_reg_by_id(u32 id)
|
|
|
|
{
|
|
|
|
return open_path_by_id(id, do_open_reg, NULL);
|
2012-06-20 20:00:30 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct file_desc_ops reg_desc_ops = {
|
2012-07-19 10:18:37 +04:00
|
|
|
.type = FD_TYPES__REG,
|
2012-06-20 20:00:30 +04:00
|
|
|
.open = open_fe_fd,
|
|
|
|
};
|
|
|
|
|
2012-08-09 13:16:46 +04:00
|
|
|
static int collect_one_regfile(void *o, ProtobufCMessage *base)
|
2012-06-20 20:00:30 +04:00
|
|
|
{
|
2012-08-09 13:16:46 +04:00
|
|
|
struct reg_file_info *rfi = o;
|
2012-06-20 20:00:30 +04:00
|
|
|
|
2012-08-09 13:16:46 +04:00
|
|
|
rfi->rfe = pb_msg(base, RegFileEntry);
|
|
|
|
rfi->path = rfi->rfe->name;
|
2012-09-26 06:20:06 +04:00
|
|
|
rfi->remap = NULL;
|
2012-06-20 20:00:30 +04:00
|
|
|
|
2012-08-09 13:16:46 +04:00
|
|
|
pr_info("Collected [%s] ID %#x\n", rfi->path, rfi->rfe->id);
|
|
|
|
file_desc_add(&rfi->d, rfi->rfe->id, ®_desc_ops);
|
2012-06-20 20:00:30 +04:00
|
|
|
|
2012-08-09 13:16:46 +04:00
|
|
|
return 0;
|
|
|
|
}
|
2012-06-20 20:00:30 +04:00
|
|
|
|
2012-09-17 20:12:58 +04:00
|
|
|
int prepare_shared_reg_files(void)
|
|
|
|
{
|
|
|
|
ghost_file_mutex = shmalloc(sizeof(*ghost_file_mutex));
|
|
|
|
if (!ghost_file_mutex)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
mutex_init(ghost_file_mutex);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-08-09 13:16:46 +04:00
|
|
|
int collect_reg_files(void)
|
|
|
|
{
|
|
|
|
int ret;
|
2012-06-20 20:00:30 +04:00
|
|
|
|
2012-08-09 13:16:46 +04:00
|
|
|
ret = collect_image(CR_FD_REG_FILES, PB_REG_FILES,
|
|
|
|
sizeof(struct reg_file_info), collect_one_regfile);
|
|
|
|
if (!ret)
|
|
|
|
ret = collect_remaps();
|
2012-06-20 20:00:30 +04:00
|
|
|
|
2012-08-09 13:16:46 +04:00
|
|
|
return ret;
|
2012-06-20 20:00:30 +04:00
|
|
|
}
|