mirror of
https://github.com/checkpoint-restore/criu.git
synced 2026-01-23 02:14:37 +00:00
files: allow dumping opened symlinks
To really open symlink file and not the regular file below it, one needs to do open with O_PATH|O_NOFOLLOW flags. Looks like systemd started to open /etc/localtime symlink this way sometimes, and before that nobody actually used this and thus we never supported this in CRIU. Error (criu/files-ext.c:96): Can't dump file 11 of that type [120777] (unknown /etc/localtime) Looks like it is quiet easy to support, as c/r of symlink file is almost the same as c/r of regular one. We need to only make fstatat not following links in check_path_remap. Also we need to take into account support of ghost symlinks. Signed-off-by: Alexander Mikhalitsyn (Virtuozzo) <alexander@mihalicyn.com> Co-developed-by: Pavel Tikhomirov <ptikhomirov@virtuozzo.com> Signed-off-by: Pavel Tikhomirov <ptikhomirov@virtuozzo.com>
This commit is contained in:
parent
8b9c1f4c5b
commit
1936608ce4
3 changed files with 84 additions and 9 deletions
|
|
@ -282,19 +282,53 @@ static int mkreg_ghost(char *path, GhostFileEntry *gfe, struct cr_img *img)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int mklnk_ghost(char *path, GhostFileEntry *gfe)
|
||||
{
|
||||
if (!gfe->symlnk_target) {
|
||||
pr_err("Ghost symlink target is NULL for %s. Image from old CRIU?\n", path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (symlink(gfe->symlnk_target, path) < 0) {
|
||||
/*
|
||||
* ENOENT case is OK
|
||||
* Take a look closer on create_ghost() function
|
||||
*/
|
||||
if (errno != ENOENT)
|
||||
pr_perror("symlink(%s, %s) failed", gfe->symlnk_target, path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ghost_apply_metadata(const char *path, GhostFileEntry *gfe)
|
||||
{
|
||||
struct timeval tv[2];
|
||||
int ret = -1;
|
||||
|
||||
if (chown(path, gfe->uid, gfe->gid) < 0) {
|
||||
pr_perror("Can't reset user/group on ghost %s", path);
|
||||
goto err;
|
||||
}
|
||||
if (S_ISLNK(gfe->mode)) {
|
||||
if (lchown(path, gfe->uid, gfe->gid) < 0) {
|
||||
pr_perror("Can't reset user/group on ghost %s", path);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (chmod(path, gfe->mode)) {
|
||||
pr_perror("Can't set perms %o on ghost %s", gfe->mode, path);
|
||||
goto err;
|
||||
/*
|
||||
* We have no lchmod() function, and fchmod() will fail on
|
||||
* O_PATH | O_NOFOLLOW fd. Yes, we have fchmodat()
|
||||
* function and flag AT_SYMLINK_NOFOLLOW described in
|
||||
* man 2 fchmodat, but it is not currently implemented. %)
|
||||
*/
|
||||
} else {
|
||||
if (chown(path, gfe->uid, gfe->gid) < 0) {
|
||||
pr_perror("Can't reset user/group on ghost %s", path);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (chmod(path, gfe->mode)) {
|
||||
pr_perror("Can't set perms %o on ghost %s", gfe->mode, path);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
if (gfe->atim) {
|
||||
|
|
@ -353,6 +387,9 @@ again:
|
|||
} else if (S_ISDIR(gfe->mode)) {
|
||||
if ((ret = mkdirpat(AT_FDCWD, path, gfe->mode)) < 0)
|
||||
msg = "Can't make ghost dir";
|
||||
} else if (S_ISLNK(gfe->mode)) {
|
||||
if ((ret = mklnk_ghost(path, gfe)) < 0)
|
||||
msg = "Can't create ghost symlink";
|
||||
} else {
|
||||
if ((ret = mkreg_ghost(path, gfe, img)) < 0)
|
||||
msg = "Can't create ghost regfile";
|
||||
|
|
@ -740,6 +777,7 @@ static int dump_ghost_file(int _fd, u32 id, const struct stat *st, dev_t phys_de
|
|||
int exit_code = -1;
|
||||
GhostFileEntry gfe = GHOST_FILE_ENTRY__INIT;
|
||||
Timeval atim = TIMEVAL__INIT, mtim = TIMEVAL__INIT;
|
||||
char pathbuf[PATH_MAX];
|
||||
|
||||
pr_info("Dumping ghost file contents (id %#x)\n", id);
|
||||
|
||||
|
|
@ -773,6 +811,36 @@ static int dump_ghost_file(int _fd, u32 id, const struct stat *st, dev_t phys_de
|
|||
gfe.size = st->st_size;
|
||||
}
|
||||
|
||||
/*
|
||||
* We set gfe.symlnk_target only if we need to dump
|
||||
* symlink content, otherwise we leave it NULL.
|
||||
* It will be taken into account on restore in mklnk_ghost function.
|
||||
*/
|
||||
if (S_ISLNK(st->st_mode)) {
|
||||
ssize_t ret;
|
||||
|
||||
/*
|
||||
* We assume that _fd opened with O_PATH | O_NOFOLLOW
|
||||
* flags because S_ISLNK(st->st_mode). With current kernel version,
|
||||
* it's looks like correct assumption in any case.
|
||||
*/
|
||||
ret = readlinkat(_fd, "", pathbuf, sizeof(pathbuf) - 1);
|
||||
if (ret < 0) {
|
||||
pr_perror("Can't readlinkat");
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
pathbuf[ret] = 0;
|
||||
|
||||
if (ret != st->st_size) {
|
||||
pr_err("Buffer for readlinkat is too small: ret %zd, st_size %"PRId64", buf %u %s\n",
|
||||
ret, st->st_size, PATH_MAX, pathbuf);
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
gfe.symlnk_target = pathbuf;
|
||||
}
|
||||
|
||||
if (pb_write_one(img, &gfe, PB_GHOST_FILE))
|
||||
goto err_out;
|
||||
|
||||
|
|
@ -1116,6 +1184,7 @@ static int check_path_remap(struct fd_link *link, const struct fd_parms *parms,
|
|||
int ret, mntns_root;
|
||||
struct stat pst;
|
||||
const struct stat *ost = &parms->stat;
|
||||
int flags = 0;
|
||||
|
||||
if (parms->fs_type == PROC_SUPER_MAGIC) {
|
||||
/* The file points to /proc/pid/<foo> where pid is a dead
|
||||
|
|
@ -1212,7 +1281,10 @@ static int check_path_remap(struct fd_link *link, const struct fd_parms *parms,
|
|||
if (mntns_root < 0)
|
||||
return -1;
|
||||
|
||||
ret = fstatat(mntns_root, rpath, &pst, 0);
|
||||
if (S_ISLNK(parms->stat.st_mode))
|
||||
flags = AT_SYMLINK_NOFOLLOW;
|
||||
|
||||
ret = fstatat(mntns_root, rpath, &pst, flags);
|
||||
if (ret < 0) {
|
||||
/*
|
||||
* Linked file, but path is not accessible (unless any
|
||||
|
|
|
|||
|
|
@ -545,7 +545,8 @@ static int dump_one_file(struct pid *pid, int fd, int lfd, struct fd_opts *opts,
|
|||
return do_dump_gen_file(&p, lfd, ops, e);
|
||||
}
|
||||
|
||||
if (S_ISREG(p.stat.st_mode) || S_ISDIR(p.stat.st_mode)) {
|
||||
if (S_ISREG(p.stat.st_mode) || S_ISDIR(p.stat.st_mode) ||
|
||||
S_ISLNK(p.stat.st_mode)) {
|
||||
if (fill_fdlink(lfd, &p, &link))
|
||||
return -1;
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ message ghost_file_entry {
|
|||
optional timeval mtim = 8;
|
||||
optional bool chunks = 9;
|
||||
optional uint64 size = 10;
|
||||
/* this field makes sense only when S_ISLNK(mode) */
|
||||
optional string symlnk_target = 11;
|
||||
}
|
||||
|
||||
message ghost_chunk_entry {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue