Merge pull request #1003 from avagin/v3.14-part2

Prepare v3.14 (part 2)
This commit is contained in:
Pavel Emelyanov 2020-03-30 13:50:56 +03:00 committed by GitHub
commit fcb23dbfcf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
102 changed files with 2214 additions and 291 deletions

1
.gitignore vendored
View file

@ -42,3 +42,4 @@ lib/.crit-setup.files
compel/include/asm
include/common/asm
include/common/config.h
build/

View file

@ -1,6 +1,8 @@
Stanislav Kinsbursky <skinsbursky@parallels.com> <skinsbursky@openvz.org>
Pavel Emelyanov <xemul@parallels.com> <xemul@openvz.org>
Andrey Vagin <avagin@parallels.com> <avagin@openvz.org>
Andrey Vagin <avagin@parallels.com> <avagin@gmail.com>
Andrey Vagin <avagin@parallels.com> Andrew Vagin <avagin@parallels.com>
Andrei Vagin <avagin@gmail.com> <avagin@openvz.org>
Andrei Vagin <avagin@gmail.com> <avagin@parallels.com>
Andrei Vagin <avagin@gmail.com> <avagin@virtuozzo.com>
Andrei Vagin <avagin@gmail.com> <avagin@odin.com>
Andrei Vagin <avagin@gmail.com> <avagin@google.com>
Cyrill Gorcunov <gorcunov@openvz.org> <gorcunov@gmail.com>

View file

@ -86,6 +86,14 @@ jobs:
arch: amd64
env: TR_ARCH=armv7-cross
dist: bionic
- os: linux
arch: amd64
env: TR_ARCH=aarch64-cross
dist: bionic
- os: linux
arch: amd64
env: TR_ARCH=ppc64-cross
dist: bionic
allow_failures:
- env: TR_ARCH=docker-test
- env: TR_ARCH=docker-test DIST=xenial

View file

@ -86,7 +86,7 @@ Infecting code
~~~~~~~~~~~~~~
The parasitic code is compiled and converted to a header using *compel*, and included here.
*#include <compel/compel.h>*
*#include <compel/infect.h>*
*#include "parasite.h"*

View file

@ -90,6 +90,19 @@ The following levels are available:
*-L*, *--libdir* 'path'::
Path to plugins directory.
*--enable-fs* ['fs'[,'fs'...]]::
Specify a comma-separated list of filesystem names that should
be auto-detected. The value 'all' enables auto-detection for
all filesystems.
+
Note: This option is not safe, use at your own risk.
Auto-detecting a filesystem mount assumes that the mountpoint can
be restored with *mount(src, mountpoint, flags, options)*. When used,
*dump* is expected to always succeed if a mountpoint is to be
auto-detected, however *restore* may fail (or do something wrong)
if the assumption for restore logic is incorrect. This option is
not compatable with *--external* *dev*.
*--action-script* 'script'::
Add an external action script to be executed at certain stages.
The environment variable *CRTOOLS_SCRIPT_ACTION* is available
@ -464,7 +477,7 @@ The 'mode' may be one of the following:
*soft*::: Restore cgroup properties if only cgroup has been created
by *criu*, otherwise do not restore properties. This is the
default if mode is unspecified.
default if mode is unspecified.
*full*::: Always restore all cgroups and their properties.
@ -562,17 +575,17 @@ check* always checks Category 1 features unless *--feature* is specified
which only checks a specified feature.
*Category 1*::: Absolutely required. These are features like support for
*/proc/PID/map_files*, *NETLINK_SOCK_DIAG* socket
monitoring, */proc/sys/kernel/ns_last_pid* etc.
*/proc/PID/map_files*, *NETLINK_SOCK_DIAG* socket
monitoring, */proc/sys/kernel/ns_last_pid* etc.
*Category 2*::: Required only for specific cases. These are features
like AIO remap, */dev/net/tun* and others that are only
required if a process being dumped or restored
is using those.
like AIO remap, */dev/net/tun* and others that are only
required if a process being dumped or restored
is using those.
*Category 3*::: Experimental. These are features like *task-diag* that
are used for experimental purposes (mostly
during development).
are used for experimental purposes (mostly
during development).
If there are no errors or warnings, *criu* prints "Looks good." and its
exit code is 0.

View file

@ -64,7 +64,7 @@ export DEFINES += $(FEATURE_DEFINES)
export CFLAGS += $(FEATURE_DEFINES)
FEATURES_LIST := TCP_REPAIR STRLCPY STRLCAT PTRACE_PEEKSIGINFO \
SETPROCTITLE_INIT MEMFD TCP_REPAIR_WINDOW FSCONFIG
SETPROCTITLE_INIT MEMFD TCP_REPAIR_WINDOW FSCONFIG MEMFD_CREATE
# $1 - config name
define gen-feature-test

View file

@ -1,6 +1,5 @@
#include <string.h>
#include "uapi/compel.h"
#include <errno.h>
#include "handle-elf.h"
#include "piegen.h"

View file

@ -1,6 +1,5 @@
#include <string.h>
#include "uapi/compel.h"
#include <errno.h>
#include "handle-elf.h"
#include "piegen.h"

View file

@ -1,6 +1,7 @@
#include <stdlib.h>
#include <sys/ptrace.h>
#include <sys/types.h>
#include <string.h>
#include <compel/plugins/std/syscall-codes.h>
#include <compel/asm/processor-flags.h>
#include "common/page.h"

View file

@ -2,6 +2,7 @@
#include <asm/cputable.h>
#include <errno.h>
#include <stdbool.h>
#include <string.h>
#include "compel-cpu.h"

View file

@ -1,6 +1,5 @@
#include <string.h>
#include "uapi/compel.h"
#include <errno.h>
#include "handle-elf.h"
#include "piegen.h"

View file

@ -1,6 +1,5 @@
#include <string.h>
#include "uapi/compel.h"
#include <errno.h>
#include "handle-elf.h"
#include "piegen.h"

View file

@ -5,6 +5,7 @@
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <elf.h>
#include <compel/plugins/std/syscall-codes.h>

View file

@ -1,6 +1,5 @@
#include <string.h>
#include "uapi/compel.h"
#include <errno.h>
#include "handle-elf.h"
#include "piegen.h"

View file

@ -3,6 +3,7 @@
#include <sys/auxv.h>
#include <sys/mman.h>
#include <sys/user.h>
#include <errno.h>
#include <compel/asm/fpu.h>

View file

@ -1,8 +1,7 @@
#ifndef COMPEL_LOG_H__
#define COMPEL_LOG_H__
#include "uapi/compel/compel.h"
#include "uapi/compel/loglevels.h"
#include "uapi/compel/log.h"
#ifndef LOG_PREFIX
# define LOG_PREFIX

View file

@ -1,14 +0,0 @@
#ifndef UAPI_COMPEL_H__
#define UAPI_COMPEL_H__
#include <errno.h>
#include <stdarg.h>
#include <compel/asm/infect-types.h>
#include <compel/handle-elf.h>
#include <compel/log.h>
#include <compel/infect-util.h>
#include <compel/infect-rpc.h>
#include <compel/infect.h>
#endif /* UAPI_COMPEL_H__ */

View file

@ -4,7 +4,7 @@
#include <unistd.h>
#include <stdint.h>
#include <string.h>
#include <stdint.h>
#include <errno.h>
#include <fcntl.h>
@ -12,8 +12,6 @@
#include <sys/stat.h>
#include <sys/mman.h>
#include "uapi/compel.h"
#include "handle-elf.h"
#include "piegen.h"
#include "log.h"
@ -228,7 +226,7 @@ int __handle_elf(void *mem, size_t size)
}
pr_out("/* Autogenerated from %s */\n", opts.input_filename);
pr_out("#include <compel/compel.h>\n");
pr_out("#include <compel/infect.h>\n");
for (i = 0; i < symtab_hdr->sh_size / symtab_hdr->sh_entsize; i++) {
Elf_Sym *sym = &symbols[i];

View file

@ -4,11 +4,8 @@
#include <stdarg.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <compel/compel.h>
#include "log.h"
static unsigned int current_loglevel = COMPEL_DEFAULT_LOGLEVEL;

View file

@ -13,8 +13,6 @@
#include <sys/stat.h>
#include <sys/mman.h>
#include "uapi/compel/compel.h"
#include "version.h"
#include "piegen.h"
#include "log.h"

View file

@ -5,7 +5,6 @@
#include <stdlib.h>
#include <sys/wait.h>
#include <compel/compel.h>
#include "parasite.h"
#define PARASITE_CMD_GETFD PARASITE_USER_CMDS

View file

@ -3,7 +3,6 @@
#include <stdlib.h>
#include <sys/wait.h>
#include <compel/compel.h>
#include "parasite.h"
#define PARASITE_CMD_INC PARASITE_USER_CMDS

View file

@ -4,8 +4,6 @@
#include <sys/wait.h>
#include <sys/syscall.h>
#include <compel/compel.h>
static void print_vmsg(unsigned int lvl, const char *fmt, va_list parms)
{
printf("\tLC%u: ", lvl);

View file

@ -37,6 +37,7 @@ obj-y += libnetlink.o
obj-y += log.o
obj-y += lsm.o
obj-y += mem.o
obj-y += memfd.o
obj-y += mount.o
obj-y += filesystems.o
obj-y += namespaces.o

View file

@ -11,7 +11,7 @@
#include "parasite.h"
#include "parasite-syscall.h"
#include "images/mm.pb-c.h"
#include <compel/compel.h>
#include "compel/infect.h"
#define NR_IOEVENTS_IN_NPAGES(npages) ((PAGE_SIZE * (npages) - sizeof(struct aio_ring)) / sizeof(struct io_event))

View file

@ -19,7 +19,7 @@
#include "util.h"
#include "cpu.h"
#include "restorer.h"
#include <compel/compel.h>
#include "compel/infect.h"
#define assign_reg(dst, src, e) dst->e = (__typeof__(dst->e))(src)->e

View file

@ -18,8 +18,7 @@
#include "elf.h"
#include "parasite-syscall.h"
#include "restorer.h"
#include <compel/compel.h>
#include "compel/infect.h"
#define assign_reg(dst, src, e) dst->e = (__typeof__(dst->e))((src)->ARM_##e)

View file

@ -17,7 +17,7 @@
#include "log.h"
#include "util.h"
#include "cpu.h"
#include <compel/compel.h>
#include "compel/infect.h"
#include "protobuf.h"
#include "images/core.pb-c.h"

View file

@ -17,7 +17,7 @@
#include "log.h"
#include "util.h"
#include "cpu.h"
#include <compel/compel.h>
#include "compel/infect.h"
#include "protobuf.h"
#include "images/core.pb-c.h"

View file

@ -1,5 +1,5 @@
#include "compel/asm/fpu.h"
#include "compel/compel.h"
#include "compel/infect.h"
#include "compel/plugins/std/syscall-codes.h"
#include "cpu.h"
#include "cr_options.h"

View file

@ -1,4 +1,3 @@
#include <compel/compel.h>
static struct syscall_exec_desc sc_exec_table_64[] = {
#include "sys-exec-tbl-64.c"

View file

@ -80,6 +80,7 @@
#include "fault-injection.h"
#include "dump.h"
#include "eventpoll.h"
#include "memfd.h"
/*
* Architectures can overwrite this function to restore register sets that
@ -414,7 +415,10 @@ static int dump_filemap(struct vma_area *vma_area, int fd)
/* Flags will be set during restore in open_filmap() */
ret = dump_one_reg_file_cond(fd, &id, &p);
if (vma->status & VMA_AREA_MEMFD)
ret = dump_one_memfd_cond(fd, &id, &p);
else
ret = dump_one_reg_file_cond(fd, &id, &p);
vma->shmid = id;
return ret;

View file

@ -67,7 +67,6 @@
#include "timerfd.h"
#include "action-scripts.h"
#include "shmem.h"
#include <compel/compel.h>
#include "aio.h"
#include "lsm.h"
#include "seccomp.h"
@ -76,6 +75,8 @@
#include "sigframe.h"
#include "fdstore.h"
#include "string.h"
#include "memfd.h"
#include "string.h"
#include "parasite-syscall.h"
#include "files-reg.h"
@ -232,6 +233,9 @@ static int restore_finish_ns_stage(int from, int to)
static int crtools_prepare_shared(void)
{
if (prepare_memfd_inodes())
return -1;
if (prepare_files())
return -1;
@ -289,6 +293,7 @@ static struct collect_image_info *cinfos_files[] = {
&fanotify_cinfo,
&fanotify_mark_cinfo,
&ext_file_cinfo,
&memfd_cinfo,
};
/* These images are required to restore namespaces */
@ -2230,6 +2235,10 @@ skip_ns_bouncing:
if (ret < 0)
goto out_kill;
ret = apply_memfd_seals();
if (ret < 0)
goto out_kill;
/*
* Zombies die after CR_STATE_RESTORE which is switched
* by root task, not by us. See comment before CR_STATE_FORKING

View file

@ -392,6 +392,7 @@ usage:
" pipe[inode]\n"
" socket[inode]\n"
" file[mnt_id:inode]\n"
" /memfd:name\n"
" path/to/file\n"
" --empty-ns net Create a namespace, but don't restore its properties\n"
" (assuming it will be restored by action scripts)\n"

View file

@ -33,8 +33,10 @@
#include "namespaces.h"
#include "proc_parse.h"
#include "pstree.h"
#include "string.h"
#include "fault-injection.h"
#include "external.h"
#include "memfd.h"
#include "protobuf.h"
#include "util.h"
@ -280,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) {
@ -351,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";
@ -456,7 +495,7 @@ static int open_remap_ghost(struct reg_file_info *rfi,
gf->remap.rmnt_id = rfi->rfe->mnt_id;
if (S_ISDIR(gfe->mode))
strncpy(gf->remap.rpath, rfi->path, PATH_MAX);
strlcpy(gf->remap.rpath, rfi->path, PATH_MAX);
else
ghost_path(gf->remap.rpath, PATH_MAX, rfi, rpe);
@ -738,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);
@ -771,19 +811,47 @@ 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;
if (S_ISREG(st->st_mode)) {
int fd, ret;
char lpath[PSFDS];
/*
* Reopen file locally since it may have no read
* permissions when drained
*/
sprintf(lpath, "/proc/self/fd/%d", _fd);
fd = open(lpath, O_RDONLY);
fd = open_proc(PROC_SELF, "fd/%d", _fd);
if (fd < 0) {
pr_perror("Can't open ghost original 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
@ -1776,11 +1848,17 @@ static int do_open_reg(int ns_root_fd, struct reg_file_info *rfi, void *arg)
if (fd < 0)
return fd;
if ((rfi->rfe->pos != -1ULL) &&
lseek(fd, rfi->rfe->pos, SEEK_SET) < 0) {
pr_perror("Can't restore file pos");
close(fd);
return -1;
/*
* O_PATH opened files carry empty fops in kernel,
* just ignore positioning at all.
*/
if (!(rfi->rfe->flags & O_PATH)) {
if (rfi->rfe->pos != -1ULL &&
lseek(fd, rfi->rfe->pos, SEEK_SET) < 0) {
pr_perror("Can't restore file pos");
close(fd);
return -1;
}
}
return fd;
@ -1879,7 +1957,10 @@ static int open_filemap(int pid, struct vma_area *vma)
flags = vma->e->fdflags;
if (ctx.flags != flags || ctx.desc != vma->vmfd) {
ret = open_path(vma->vmfd, do_open_reg_noseek_flags, &flags);
if (vma->e->status & VMA_AREA_MEMFD)
ret = memfd_open(vma->vmfd, &flags);
else
ret = open_path(vma->vmfd, do_open_reg_noseek_flags, &flags);
if (ret < 0)
return ret;
@ -1909,7 +1990,10 @@ int collect_filemap(struct vma_area *vma)
vma->e->fdflags = O_RDONLY;
}
fd = collect_special_file(vma->e->shmid);
if (vma->e->status & VMA_AREA_MEMFD)
fd = collect_memfd(vma->e->shmid);
else
fd = collect_special_file(vma->e->shmid);
if (!fd)
return -1;

View file

@ -34,6 +34,7 @@
#include "sk-packet.h"
#include "mount.h"
#include "signalfd.h"
#include "memfd.h"
#include "namespaces.h"
#include "tun.h"
#include "timerfd.h"
@ -44,6 +45,7 @@
#include "autofs.h"
#include "parasite.h"
#include "parasite-syscall.h"
#include "string.h"
#include "kerndat.h"
#include "fdstore.h"
@ -290,8 +292,7 @@ static int fixup_overlayfs(struct fd_parms *p, struct fd_link *link)
char buf[PATH_MAX];
int n;
strncpy(buf, link->name, PATH_MAX);
buf[PATH_MAX - 1] = 0;
strlcpy(buf, link->name, PATH_MAX);
n = snprintf(link->name, PATH_MAX, "%s/%s", m->mountpoint, buf + 2);
if (n >= PATH_MAX) {
pr_err("Not enough space to replace %s\n", buf);
@ -398,7 +399,10 @@ static int fill_fd_params(struct pid *owner_pid, int fd, int lfd,
pr_info("%d fdinfo %d: pos: %#16"PRIx64" flags: %16o/%#x\n",
owner_pid->real, fd, p->pos, p->flags, (int)p->fd_flags);
ret = fcntl(lfd, F_GETSIG, 0);
if (p->flags & O_PATH)
ret = 0;
else
ret = fcntl(lfd, F_GETSIG, 0);
if (ret < 0) {
pr_perror("Can't get owner signum on %d", lfd);
return -1;
@ -541,18 +545,23 @@ 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;
p.link = &link;
if (link.name[1] == '/')
return do_dump_gen_file(&p, lfd, &regfile_dump_ops, e);
if (check_ns_proc(&link))
return do_dump_gen_file(&p, lfd, &nsfile_dump_ops, e);
if (is_memfd(p.stat.st_dev))
ops = &memfd_dump_ops;
else if (link.name[1] == '/')
ops = &regfile_dump_ops;
else if (check_ns_proc(&link))
ops = &nsfile_dump_ops;
else
return dump_unsupp_fd(&p, lfd, "reg", link.name + 1, e);
return dump_unsupp_fd(&p, lfd, "reg", link.name + 1, e);
return do_dump_gen_file(&p, lfd, ops, e);
}
if (S_ISFIFO(p.stat.st_mode)) {
@ -1603,7 +1612,7 @@ int inherit_fd_lookup_id(char *id)
bool inherited_fd(struct file_desc *d, int *fd_p)
{
char buf[32], *id_str;
char buf[PATH_MAX], *id_str;
int i_fd;
if (!d->ops->name)
@ -1721,6 +1730,9 @@ static int collect_one_file(void *o, ProtobufCMessage *base, struct cr_img *i)
case FD_TYPES__TTY:
ret = collect_one_file_entry(fe, fe->tty->id, &fe->tty->base, &tty_cinfo);
break;
case FD_TYPES__MEMFD:
ret = collect_one_file_entry(fe, fe->memfd->id, &fe->memfd->base, &memfd_cinfo);
break;
}
return ret;

View file

@ -747,6 +747,11 @@ static struct fstype fstypes[] = {
.code = FSTYPE__CGROUP,
.parse = cgroup_parse,
.sb_equal = cgroup_sb_equal,
}, {
.name = "cgroup2",
.code = FSTYPE__CGROUP2,
.parse = cgroup_parse,
.sb_equal = cgroup_sb_equal,
}, {
.name = "aufs",
.code = FSTYPE__AUFS,

View file

@ -66,6 +66,7 @@ struct cr_fd_desc_tmpl imgset_template[CR_FD_MAX] = {
FD_ENTRY(FS, "fs-%u"),
FD_ENTRY(REMAP_FPATH, "remap-fpath"),
FD_ENTRY_F(GHOST_FILE, "ghost-file-%x", O_NOBUF),
FD_ENTRY_F(MEMFD_INODE, "memfd", O_NOBUF),
FD_ENTRY(TCP_STREAM, "tcp-stream-%x"),
FD_ENTRY(MNTS, "mountpoints-%u"),
FD_ENTRY(NETDEV, "netdev-%u"),

View file

@ -34,6 +34,14 @@ struct f_owner_ex {
# define F_GETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 8)
#endif
#ifndef F_ADD_SEALS
# define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9)
#endif
#ifndef F_GET_SEALS
# define F_GET_SEALS (F_LINUX_SPECIFIC_BASE + 10)
#endif
#ifndef O_PATH
# define O_PATH 010000000
#endif

View file

@ -65,6 +65,7 @@ enum {
CR_FD_CGROUP,
CR_FD_FILE_LOCKS,
CR_FD_SECCOMP,
CR_FD_MEMFD_INODE,
_CR_FD_GLOB_TO,
CR_FD_TMPFS_IMG,
@ -106,6 +107,7 @@ enum {
CR_FD_FIFO,
CR_FD_PIPES,
CR_FD_TTY_FILES,
CR_FD_MEMFD_FILE,
CR_FD_AUTOFS,

View file

@ -83,6 +83,7 @@
#define VMA_AREA_SOCKET (1 << 11)
#define VMA_AREA_VVAR (1 << 12)
#define VMA_AREA_AIORING (1 << 13)
#define VMA_AREA_MEMFD (1 << 14)
#define VMA_CLOSE (1 << 28)
#define VMA_NO_PROT_WRITE (1 << 29)

View file

@ -94,6 +94,7 @@
#define BINFMT_MISC_MAGIC 0x67343323 /* Apatity */
#define AUTOFS_MAGIC 0x49353943 /* Sochi */
#define FILES_MAGIC 0x56303138 /* Toropets */
#define MEMFD_INODE_MAGIC 0x48453499 /* Dnipro */
#define IFADDR_MAGIC RAW_IMAGE_MAGIC
#define ROUTE_MAGIC RAW_IMAGE_MAGIC

33
criu/include/memfd.h Normal file
View file

@ -0,0 +1,33 @@
#ifndef __CR_MEMFD_H__
#define __CR_MEMFD_H__
#include <sys/stat.h>
#include "int.h"
#include "common/config.h"
struct fd_parms;
struct file_desc;
extern int is_memfd(dev_t dev);
extern int dump_one_memfd_cond(int lfd, u32 *id, struct fd_parms *parms);
extern const struct fdtype_ops memfd_dump_ops;
extern int memfd_open(struct file_desc *d, u32 *fdflags);
extern struct collect_image_info memfd_cinfo;
extern struct file_desc *collect_memfd(u32 id);
extern int apply_memfd_seals(void);
extern int prepare_memfd_inodes(void);
#ifdef CONFIG_HAS_MEMFD_CREATE
# include <sys/mman.h>
#else
# include <sys/syscall.h>
# include <linux/memfd.h>
static inline int memfd_create(const char *name, unsigned int flags)
{
return syscall(SYS_memfd_create, name, flags);
}
#endif /* CONFIG_HAS_MEMFD_CREATE */
#endif /* __CR_MEMFD_H__ */

View file

@ -3,7 +3,7 @@
#include <sys/types.h>
#include <compel/compel.h>
#include "compel/infect.h"
#define PROC_TASK_COMM_LEN 32
#define PROC_TASK_COMM_LEN_FMT "(%31s"

View file

@ -61,6 +61,8 @@ enum {
PB_AUTOFS,
PB_GHOST_CHUNK,
PB_FILE,
PB_MEMFD_FILE,
PB_MEMFD_INODE, /* 60 */
/* PB_AUTOGEN_STOP */

View file

@ -13,8 +13,11 @@ extern int collect_sysv_shmem(unsigned long shmid, unsigned long size);
extern int cr_dump_shmem(void);
extern int add_shmem_area(pid_t pid, VmaEntry *vma, u64 *map);
extern int fixup_sysv_shmems(void);
extern int dump_one_memfd_shmem(int fd, unsigned long shmid, unsigned long size);
extern int dump_one_sysv_shmem(void *addr, unsigned long size, unsigned long shmid);
extern int restore_sysv_shmem_content(void *addr, unsigned long size, unsigned long shmid);
extern int restore_memfd_shmem_content(int fd, unsigned long shmid, unsigned long size);
#define SYSV_SHMEM_SKIP_FD (0x7fffffff)

View file

@ -33,7 +33,6 @@
#include "net.h"
#include "tun.h"
#include <compel/plugins/std/syscall-codes.h>
#include <compel/compel.h>
#include "netfilter.h"
#include "fsnotify.h"
#include "linux/userfaultfd.h"
@ -42,6 +41,7 @@
#include "vdso.h"
#include "kcmp.h"
#include "sched.h"
#include "memfd.h"
struct kerndat_s kdat = {
};
@ -409,7 +409,7 @@ static bool kerndat_has_memfd_create(void)
{
int ret;
ret = syscall(SYS_memfd_create, NULL, 0);
ret = memfd_create(NULL, 0);
if (ret == -1 && errno == ENOSYS)
kdat.has_memfd = false;

View file

@ -29,7 +29,7 @@
#include "pagemap-cache.h"
#include "fault-injection.h"
#include "prctl.h"
#include <compel/compel.h>
#include "compel/infect-util.h"
#include "protobuf.h"
#include "images/pagemap.pb-c.h"

452
criu/memfd.c Normal file
View file

@ -0,0 +1,452 @@
#include <unistd.h>
#include <linux/memfd.h>
#include "common/compiler.h"
#include "common/lock.h"
#include "memfd.h"
#include "fdinfo.h"
#include "imgset.h"
#include "image.h"
#include "util.h"
#include "log.h"
#include "files.h"
#include "fs-magic.h"
#include "kerndat.h"
#include "files-reg.h"
#include "rst-malloc.h"
#include "fdstore.h"
#include "file-ids.h"
#include "namespaces.h"
#include "shmem.h"
#include "protobuf.h"
#include "images/memfd.pb-c.h"
#define MEMFD_PREFIX "/memfd:"
#define MEMFD_PREFIX_LEN (sizeof(MEMFD_PREFIX)-1)
#define F_SEAL_SEAL 0x0001 /* prevent further seals from being set */
#define F_SEAL_SHRINK 0x0002 /* prevent file from shrinking */
#define F_SEAL_GROW 0x0004 /* prevent file from growing */
#define F_SEAL_WRITE 0x0008 /* prevent writes */
/* Linux 5.1+ */
#define F_SEAL_FUTURE_WRITE 0x0010 /* prevent future writes while mapped */
struct memfd_dump_inode {
struct list_head list;
u32 id;
u32 dev;
u32 ino;
};
struct memfd_restore_inode {
struct list_head list;
mutex_t lock;
int fdstore_id;
unsigned int pending_seals;
MemfdInodeEntry *mie;
};
static LIST_HEAD(memfd_inodes);
/*
* Dump only
*/
static u32 memfd_inode_ids = 1;
int is_memfd(dev_t dev)
{
/*
* TODO When MAP_HUGETLB is used, the file device is not shmem_dev,
* Note that other parts of CRIU have similar issues, see
* is_anon_shmem_map().
*/
return dev == kdat.shmem_dev;
}
static int dump_memfd_inode(int fd, struct memfd_dump_inode *inode,
const char *name, const struct stat *st)
{
MemfdInodeEntry mie = MEMFD_INODE_ENTRY__INIT;
int ret = -1;
u32 shmid;
/*
* shmids are chosen as the inode number of the corresponding mmaped
* file. See handle_vma() in proc_parse.c.
* It works for memfd too, because we share the same device as the
* shmem device.
*/
shmid = inode->ino;
pr_info("Dumping memfd:%s contents (id %#x, shmid: %#x, size: %"PRIu64")\n",
name, inode->id, shmid, st->st_size);
if (dump_one_memfd_shmem(fd, shmid, st->st_size) < 0)
goto out;
mie.inode_id = inode->id;
mie.uid = userns_uid(st->st_uid);
mie.gid = userns_gid(st->st_gid);
mie.name = (char *)name;
mie.size = st->st_size;
mie.shmid = shmid;
mie.seals = fcntl(fd, F_GET_SEALS);
if (mie.seals == -1)
goto out;
if (pb_write_one(img_from_set(glob_imgset, CR_FD_MEMFD_INODE), &mie, PB_MEMFD_INODE))
goto out;
ret = 0;
out:
return ret;
}
static struct memfd_dump_inode *
dump_unique_memfd_inode(int lfd, const char *name, const struct stat *st)
{
struct memfd_dump_inode *inode;
int fd;
list_for_each_entry(inode, &memfd_inodes, list)
if ((inode->dev == st->st_dev) && (inode->ino == st->st_ino))
return inode;
inode = xmalloc(sizeof(*inode));
if (inode == NULL)
return NULL;
inode->dev = st->st_dev;
inode->ino = st->st_ino;
inode->id = memfd_inode_ids++;
fd = open_proc(PROC_SELF, "fd/%d", lfd);
if (fd < 0) {
xfree(inode);
return NULL;
}
if (dump_memfd_inode(fd, inode, name, st)) {
close(fd);
xfree(inode);
return NULL;
}
close(fd);
list_add_tail(&inode->list, &memfd_inodes);
return inode;
}
static int dump_one_memfd(int lfd, u32 id, const struct fd_parms *p)
{
MemfdFileEntry mfe = MEMFD_FILE_ENTRY__INIT;
FileEntry fe = FILE_ENTRY__INIT;
struct memfd_dump_inode *inode;
struct fd_link _link, *link;
const char *name;
if (!p->link) {
if (fill_fdlink(lfd, p, &_link))
return -1;
link = &_link;
} else
link = p->link;
strip_deleted(link);
/* link->name is always started with "." which has to be skipped. */
if (strncmp(link->name + 1, MEMFD_PREFIX, MEMFD_PREFIX_LEN) == 0)
name = &link->name[1 + MEMFD_PREFIX_LEN];
else
name = link->name + 1;
inode = dump_unique_memfd_inode(lfd, name, &p->stat);
if (!inode)
return -1;
mfe.id = id;
mfe.flags = p->flags;
mfe.pos = p->pos;
mfe.fown = (FownEntry *)&p->fown;
mfe.inode_id = inode->id;
fe.type = FD_TYPES__MEMFD;
fe.id = mfe.id;
fe.memfd = &mfe;
return pb_write_one(img_from_set(glob_imgset, CR_FD_FILES), &fe, PB_FILE);
}
int dump_one_memfd_cond(int lfd, u32 *id, struct fd_parms *parms)
{
if (fd_id_generate_special(parms, id))
return dump_one_memfd(lfd, *id, parms);
return 0;
}
const struct fdtype_ops memfd_dump_ops = {
.type = FD_TYPES__MEMFD,
.dump = dump_one_memfd,
};
/*
* Restore only
*/
struct memfd_info {
MemfdFileEntry *mfe;
struct file_desc d;
struct memfd_restore_inode *inode;
};
static struct memfd_restore_inode *memfd_alloc_inode(int id)
{
struct memfd_restore_inode *inode;
list_for_each_entry(inode, &memfd_inodes, list)
if (inode->mie->inode_id == id)
return inode;
pr_err("Unable to find the %d memfd inode\n", id);
return NULL;
}
static int collect_one_memfd_inode(void *o, ProtobufCMessage *base, struct cr_img *i)
{
MemfdInodeEntry *mie = pb_msg(base, MemfdInodeEntry);
struct memfd_restore_inode *inode = o;
inode->mie = mie;
mutex_init(&inode->lock);
inode->fdstore_id = -1;
inode->pending_seals = 0;
list_add_tail(&inode->list, &memfd_inodes);
return 0;
}
static struct collect_image_info memfd_inode_cinfo = {
.fd_type = CR_FD_MEMFD_INODE,
.pb_type = PB_MEMFD_INODE,
.priv_size = sizeof(struct memfd_restore_inode),
.collect = collect_one_memfd_inode,
.flags = COLLECT_SHARED | COLLECT_NOFREE,
};
int prepare_memfd_inodes(void)
{
return collect_image(&memfd_inode_cinfo);
}
static int memfd_open_inode_nocache(struct memfd_restore_inode *inode)
{
MemfdInodeEntry *mie = NULL;
int fd = -1;
int ret = -1;
int flags;
mie = inode->mie;
if (mie->seals == F_SEAL_SEAL) {
inode->pending_seals = 0;
flags = 0;
} else {
/* Seals are applied later due to F_SEAL_FUTURE_WRITE */
inode->pending_seals = mie->seals;
flags = MFD_ALLOW_SEALING;
}
fd = memfd_create(mie->name, flags);
if (fd < 0) {
pr_perror("Can't create memfd:%s", mie->name);
goto out;
}
if (restore_memfd_shmem_content(fd, mie->shmid, mie->size))
goto out;
if (fchown(fd, mie->uid, mie->gid)) {
pr_perror("Can't change uid %d gid %d of memfd:%s",
(int)mie->uid, (int)mie->gid, mie->name);
goto out;
}
inode->fdstore_id = fdstore_add(fd);
if (inode->fdstore_id < 0)
goto out;
ret = fd;
fd = -1;
out:
if (fd != -1)
close(fd);
return ret;
}
static int memfd_open_inode(struct memfd_restore_inode *inode)
{
int fd;
if (inode->fdstore_id != -1)
return fdstore_get(inode->fdstore_id);
mutex_lock(&inode->lock);
if (inode->fdstore_id != -1)
fd = fdstore_get(inode->fdstore_id);
else
fd = memfd_open_inode_nocache(inode);
mutex_unlock(&inode->lock);
return fd;
}
int memfd_open(struct file_desc *d, u32 *fdflags)
{
struct memfd_info *mfi;
MemfdFileEntry *mfe;
int fd, _fd;
u32 flags;
mfi = container_of(d, struct memfd_info, d);
mfe = mfi->mfe;
if (inherited_fd(d, &fd))
return fd;
pr_info("Restoring memfd id=%d\n", mfe->id);
fd = memfd_open_inode(mfi->inode);
if (fd < 0)
goto err;
/* Reopen the fd with original permissions */
flags = fdflags ? *fdflags : mfe->flags;
/*
* Ideally we should call compat version open() to not force the
* O_LARGEFILE file flag with regular open(). It doesn't seem that
* important though.
*/
_fd = __open_proc(getpid(), 0, flags, "fd/%d", fd);
if (_fd < 0) {
pr_perror("Can't reopen memfd id=%d", mfe->id);
goto err;
}
close(fd);
fd = _fd;
if (restore_fown(fd, mfe->fown) < 0)
goto err;
if (lseek(fd, mfe->pos, SEEK_SET) < 0) {
pr_perror("Can't restore file position of memfd id=%d", mfe->id);
goto err;
}
return fd;
err:
if (fd >= 0)
close(fd);
return -1;
}
static int memfd_open_fe_fd(struct file_desc *fd, int *new_fd)
{
int tmp;
tmp = memfd_open(fd, NULL);
if (tmp < 0)
return -1;
*new_fd = tmp;
return 0;
}
static char *memfd_d_name(struct file_desc *d, char *buf, size_t s)
{
MemfdInodeEntry *mie = NULL;
struct memfd_info *mfi;
mfi = container_of(d, struct memfd_info, d);
mie = mfi->inode->mie;
if (snprintf(buf, s, "%s%s", MEMFD_PREFIX, mie->name) >= s) {
pr_err("Buffer too small for memfd name %s\n", mie->name);
return NULL;
}
return buf;
}
static struct file_desc_ops memfd_desc_ops = {
.type = FD_TYPES__MEMFD,
.open = memfd_open_fe_fd,
.name = memfd_d_name,
};
static int collect_one_memfd(void *o, ProtobufCMessage *msg, struct cr_img *i)
{
struct memfd_info *info = o;
info->mfe = pb_msg(msg, MemfdFileEntry);
info->inode = memfd_alloc_inode(info->mfe->inode_id);
if (!info->inode)
return -1;
return file_desc_add(&info->d, info->mfe->id, &memfd_desc_ops);
}
struct collect_image_info memfd_cinfo = {
.fd_type = CR_FD_MEMFD_FILE,
.pb_type = PB_MEMFD_FILE,
.priv_size = sizeof(struct memfd_info),
.collect = collect_one_memfd,
};
struct file_desc *collect_memfd(u32 id)
{
struct file_desc *fdesc;
fdesc = find_file_desc_raw(FD_TYPES__MEMFD, id);
if (fdesc == NULL)
pr_err("No entry for memfd %#x\n", id);
return fdesc;
}
int apply_memfd_seals(void)
{
/*
* We apply the seals after all the mappings are done because the seal
* F_SEAL_FUTURE_WRITE prevents future write access (added in
* Linux 5.1). Thus we must make sure all writable mappings are opened
* before applying this seal.
*/
int ret, fd;
struct memfd_restore_inode *inode;
list_for_each_entry(inode, &memfd_inodes, list) {
if (!inode->pending_seals)
continue;
fd = memfd_open_inode(inode);
if (fd < 0)
return -1;
ret = fcntl(fd, F_ADD_SEALS, inode->pending_seals);
close(fd);
if (ret < 0) {
pr_perror("Cannot apply seals on memfd");
return -1;
}
}
return 0;
}

View file

@ -938,9 +938,9 @@ static int check_user_ns(int pid)
int dump_user_ns(pid_t pid, int ns_id)
{
int ret, exit_code = -1;
UsernsEntry *e = &userns_entry;
struct cr_img *img;
int ret;
ret = parse_id_map(pid, "uid_map", &e->uid_map);
if (ret < 0)
@ -953,7 +953,7 @@ int dump_user_ns(pid_t pid, int ns_id)
e->n_gid_map = ret;
if (check_user_ns(pid))
return -1;
goto err;
img = open_image(CR_FD_USERNS, O_DUMP, ns_id);
if (!img)
@ -973,7 +973,7 @@ err:
xfree(e->gid_map[0]);
xfree(e->gid_map);
}
return exit_code;
return -1;
}
void free_userns_maps(void)

View file

@ -45,8 +45,6 @@
#include "infect-rpc.h"
#include "pie/parasite-blob.h"
#include <compel/compel.h>
unsigned long get_exec_start(struct vm_area_list *vmas)
{
struct vma_area *vma_area;

View file

@ -317,15 +317,60 @@ grps_err:
return -1;
}
static int fill_fds_fown(int fd, struct fd_opts *p)
{
int flags, ret;
struct f_owner_ex owner_ex;
uint32_t v[2];
/*
* For O_PATH opened files there is no owner at all.
*/
flags = sys_fcntl(fd, F_GETFL, 0);
if (flags < 0) {
pr_err("fcntl(%d, F_GETFL) -> %d\n", fd, flags);
return -1;
}
if (flags & O_PATH) {
p->fown.pid = 0;
return 0;
}
ret = sys_fcntl(fd, F_GETOWN_EX, (long)&owner_ex);
if (ret) {
pr_err("fcntl(%d, F_GETOWN_EX) -> %d\n", fd, ret);
return -1;
}
/*
* Simple case -- nothing is changed.
*/
if (owner_ex.pid == 0) {
p->fown.pid = 0;
return 0;
}
ret = sys_fcntl(fd, F_GETOWNER_UIDS, (long)&v);
if (ret) {
pr_err("fcntl(%d, F_GETOWNER_UIDS) -> %d\n", fd, ret);
return -1;
}
p->fown.uid = v[0];
p->fown.euid = v[1];
p->fown.pid_type = owner_ex.type;
p->fown.pid = owner_ex.pid;
return 0;
}
static int fill_fds_opts(struct parasite_drain_fd *fds, struct fd_opts *opts)
{
int i;
for (i = 0; i < fds->nr_fds; i++) {
int flags, fd = fds->fds[i], ret;
int flags, fd = fds->fds[i];
struct fd_opts *p = opts + i;
struct f_owner_ex owner_ex;
uint32_t v[2];
flags = sys_fcntl(fd, F_GETFD, 0);
if (flags < 0) {
@ -335,30 +380,8 @@ static int fill_fds_opts(struct parasite_drain_fd *fds, struct fd_opts *opts)
p->flags = (char)flags;
ret = sys_fcntl(fd, F_GETOWN_EX, (long)&owner_ex);
if (ret) {
pr_err("fcntl(%d, F_GETOWN_EX) -> %d\n", fd, ret);
if (fill_fds_fown(fd, p))
return -1;
}
/*
* Simple case -- nothing is changed.
*/
if (owner_ex.pid == 0) {
p->fown.pid = 0;
continue;
}
ret = sys_fcntl(fd, F_GETOWNER_UIDS, (long)&v);
if (ret) {
pr_err("fcntl(%d, F_GETOWNER_UIDS) -> %d\n", fd, ret);
return -1;
}
p->fown.uid = v[0];
p->fown.euid = v[1];
p->fown.pid_type = owner_ex.type;
p->fown.pid = owner_ex.pid;
}
return 0;

View file

@ -1,8 +1,6 @@
#ifndef __PIE_RELOCS_H__
#define __PIE_RELOCS_H__
#include <compel/compel.h>
#include "common/config.h"
#include "common/compiler.h"

View file

@ -160,24 +160,24 @@ int restore_pipe_data(int img_type, int pfd, u32 id, struct pipe_data_rst **hash
return 0;
}
if (!pd->pde->bytes)
goto out;
if (!pd->data) {
pr_err("Double data restore occurred on %#x\n", id);
return -1;
}
if (pd->pde->has_size) {
pr_info("Restoring size %#x for %#x\n",
pd->pde->size, pd->pde->pipe_id);
ret = fcntl(pfd, F_SETPIPE_SZ, pd->pde->size);
if (ret < 0) {
pr_perror("Can't restore pipe size");
goto err;
return -1;
}
}
if (!pd->pde->bytes)
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;
@ -185,14 +185,13 @@ int restore_pipe_data(int img_type, int pfd, u32 id, struct pipe_data_rst **hash
ret = vmsplice(pfd, &iov, 1, SPLICE_F_GIFT | SPLICE_F_NONBLOCK);
if (ret < 0) {
pr_perror("%#x: Error splicing data", id);
goto err;
return -1;
}
if (ret == 0 || ret > iov.iov_len /* sanity */) {
pr_err("%#x: Wanted to restore %zu bytes, but got %d\n", id,
iov.iov_len, ret);
ret = -1;
goto err;
return -1;
}
iov.iov_base += ret;
@ -211,10 +210,7 @@ int restore_pipe_data(int img_type, int pfd, u32 id, struct pipe_data_rst **hash
munmap(pd->data, pd->pde->bytes);
pd->data = NULL;
out:
ret = 0;
err:
return ret;
return 0;
}
static int userns_reopen(void *_arg, int fd, pid_t pid)

View file

@ -41,6 +41,7 @@
#include "timerfd.h"
#include "path.h"
#include "fault-injection.h"
#include "memfd.h"
#include "protobuf.h"
#include "images/fdinfo.pb-c.h"
@ -303,6 +304,26 @@ static int vma_get_mapfile_user(const char *fname, struct vma_area *vma,
}
vfi_dev = makedev(vfi->dev_maj, vfi->dev_min);
if (is_memfd(vfi_dev)) {
struct fd_link link;
link.len = strlen(fname);
strlcpy(link.name, fname, sizeof(link.name));
strip_deleted(&link);
/*
* The error EPERM will be shown in the following pr_perror().
* It comes from the previous open() call.
*/
pr_perror("Can't open mapped [%s]", link.name);
/*
* TODO Perhaps we could do better than failing and dump the
* memory like what is being done in shmem.c
*/
return -1;
}
if (is_anon_shmem_map(vfi_dev)) {
if (!(vma->e->flags & MAP_SHARED))
return -1;
@ -563,6 +584,14 @@ static int handle_vma(pid_t pid, struct vma_area *vma_area,
vma_area->e->shmid = prev->e->shmid;
vma_area->vmst = prev->vmst;
vma_area->mnt_id = prev->mnt_id;
if (!(vma_area->e->status & VMA_AREA_SYSVIPC)) {
vma_area->e->status &= ~(VMA_FILE_PRIVATE | VMA_FILE_SHARED);
if (vma_area->e->flags & MAP_PRIVATE)
vma_area->e->status |= VMA_FILE_PRIVATE;
else
vma_area->e->status |= VMA_FILE_SHARED;
}
} else if (*vm_file_fd >= 0) {
struct stat *st_buf = vma_area->vmst;
@ -575,25 +604,21 @@ static int handle_vma(pid_t pid, struct vma_area *vma_area,
goto err;
}
/*
* /dev/zero stands for anon-shared mapping
* otherwise it's some file mapping.
*/
if (is_anon_shmem_map(st_buf->st_dev)) {
if (!(vma_area->e->flags & MAP_SHARED))
goto err_bogus_mapping;
if (is_anon_shmem_map(st_buf->st_dev) && !strncmp(file_path, "/SYSV", 5)) {
vma_area->e->flags |= MAP_ANONYMOUS;
vma_area->e->status |= VMA_ANON_SHARED;
vma_area->e->shmid = st_buf->st_ino;
if (!strncmp(file_path, "/SYSV", 5)) {
pr_info("path: %s\n", file_path);
vma_area->e->status |= VMA_AREA_SYSVIPC;
} else {
if (!(vma_area->e->flags & MAP_SHARED))
goto err_bogus_mapping;
pr_info("path: %s\n", file_path);
vma_area->e->status |= VMA_AREA_SYSVIPC;
} else {
if (is_anon_shmem_map(st_buf->st_dev)) {
vma_area->e->status |= VMA_AREA_MEMFD;
if (fault_injected(FI_HUGE_ANON_SHMEM_ID))
vma_area->e->shmid += FI_HUGE_ANON_SHMEM_ID_BASE;
}
} else {
if (vma_area->e->flags & MAP_PRIVATE)
vma_area->e->status |= VMA_FILE_PRIVATE;
else

View file

@ -20,9 +20,9 @@
#include "seccomp.h"
#include "seize.h"
#include "stats.h"
#include "string.h"
#include "xmalloc.h"
#include "util.h"
#include <compel/compel.h>
#define NR_ATTEMPTS 5
@ -30,7 +30,17 @@ static const char frozen[] = "FROZEN";
static const char freezing[] = "FREEZING";
static const char thawed[] = "THAWED";
static const char *get_freezer_state(int fd)
enum freezer_state {
FREEZER_ERROR = -1,
THAWED,
FROZEN,
FREEZING
};
/* Track if we are running on cgroup v2 system. */
static bool cgroup_v2 = false;
static enum freezer_state get_freezer_v1_state(int fd)
{
char state[32];
int ret;
@ -52,15 +62,79 @@ static const char *get_freezer_state(int fd)
pr_debug("freezer.state=%s\n", state);
if (strcmp(state, frozen) == 0)
return frozen;
return FROZEN;
else if (strcmp(state, freezing) == 0)
return freezing;
return FREEZING;
else if (strcmp(state, thawed) == 0)
return thawed;
return THAWED;
pr_err("Unknown freezer state: %s\n", state);
err:
return NULL;
return FREEZER_ERROR;
}
static enum freezer_state get_freezer_v2_state(int fd)
{
int exit_code = FREEZER_ERROR;
char path[PATH_MAX];
FILE *event;
char state;
int ret;
/*
* cgroupv2 freezer uses cgroup.freeze to control the state. The file
* can return 0 or 1. 1 means the cgroup is frozen; 0 means it is not
* frozen. Writing 1 to an unfrozen cgroup can freeze it. Freezing can
* take some time and if the cgroup has finished freezing can be
* seen in cgroup.events: frozen 0|1.
*/
ret = lseek(fd, 0, SEEK_SET);
if (ret < 0) {
pr_perror("Unable to seek freezer FD");
goto out;
}
ret = read(fd, &state, 1);
if (ret <= 0) {
pr_perror("Unable to read from freezer FD");
goto out;
}
pr_debug("cgroup.freeze=%c\n", state);
if (state == '0') {
exit_code = THAWED;
goto out;
}
snprintf(path, sizeof(path), "%s/cgroup.events", opts.freeze_cgroup);
event = fopen(path, "r");
if (event == NULL) {
pr_perror("Unable to open %s", path);
goto out;
}
while (fgets(path, sizeof(path), event)) {
if (strncmp(path, "frozen", 6) != 0) {
continue;
} else if (strncmp(path, "frozen 0", 8) == 0) {
exit_code = FREEZING;
goto close;
} else if (strncmp(path, "frozen 1", 8) == 0) {
exit_code = FROZEN;
goto close;
}
}
pr_err("Unknown freezer state: %c\n", state);
close:
fclose(event);
out:
return exit_code;
}
static enum freezer_state get_freezer_state(int fd)
{
if (cgroup_v2)
return get_freezer_v2_state(fd);
return get_freezer_v1_state(fd);
}
static bool freezer_thawed;
@ -70,35 +144,99 @@ const char *get_real_freezer_state(void)
return freezer_thawed ? thawed : frozen;
}
static int freezer_restore_state(void)
static int freezer_write_state(int fd, enum freezer_state new_state)
{
int fd;
char state[32] = {0};
int ret;
if (new_state == THAWED) {
if (cgroup_v2)
state[0] = '0';
else
if (strlcpy(state, thawed, sizeof(state)) >=
sizeof(state))
return -1;
} else if (new_state == FROZEN) {
if (cgroup_v2)
state[0] = '1';
else
if (strlcpy(state, frozen, sizeof(state)) >=
sizeof(state))
return -1;
} else {
return -1;
}
ret = lseek(fd, 0, SEEK_SET);
if (ret < 0) {
pr_perror("Unable to seek freezer FD");
return -1;
}
if (write(fd, state, sizeof(state)) != sizeof(state)) {
pr_perror("Unable to %s tasks",
(new_state == THAWED) ? "thaw" : "freeze");
return -1;
}
return 0;
}
static int freezer_open(void)
{
const char freezer_v1[] = "freezer.state";
const char freezer_v2[] = "cgroup.freeze";
char path[PATH_MAX];
int fd;
if (!opts.freeze_cgroup || freezer_thawed)
return 0;
snprintf(path, sizeof(path), "%s/freezer.state", opts.freeze_cgroup);
snprintf(path, sizeof(path), "%s/%s", opts.freeze_cgroup,
cgroup_v2 ? freezer_v2 : freezer_v1);
fd = open(path, O_RDWR);
if (fd < 0) {
pr_perror("Unable to open %s", path);
return -1;
}
if (write(fd, frozen, sizeof(frozen)) != sizeof(frozen)) {
pr_perror("Unable to freeze tasks");
close(fd);
return fd;
}
static int freezer_restore_state(void)
{
int fd;
int ret;
if (!opts.freeze_cgroup || freezer_thawed)
return 0;
fd = freezer_open();
if (fd < 0)
return -1;
}
ret = freezer_write_state(fd, FROZEN);
close(fd);
return 0;
return ret;
}
static FILE *freezer_open_thread_list(char *root_path)
{
char path[PATH_MAX];
FILE *f;
snprintf(path, sizeof(path), "%s/%s", root_path,
cgroup_v2 ? "cgroup.threads" : "tasks");
f = fopen(path, "r");
if (f == NULL) {
pr_perror("Unable to open %s", path);
return NULL;
}
return f;
}
/* A number of tasks in a freezer cgroup which are not going to be dumped */
static int processes_to_wait;
static pid_t *processes_to_wait_pids;
static int seize_cgroup_tree(char *root_path, const char *state)
static int seize_cgroup_tree(char *root_path, enum freezer_state state)
{
DIR *dir;
struct dirent *de;
@ -109,12 +247,10 @@ static int seize_cgroup_tree(char *root_path, const char *state)
* New tasks can appear while a freezer state isn't
* frozen, so we need to catch all new tasks.
*/
snprintf(path, sizeof(path), "%s/tasks", root_path);
f = fopen(path, "r");
if (f == NULL) {
pr_perror("Unable to open %s", path);
f = freezer_open_thread_list(root_path);
if (f == NULL)
return -1;
}
while (fgets(path, sizeof(path), f)) {
pid_t pid;
int ret;
@ -134,7 +270,7 @@ static int seize_cgroup_tree(char *root_path, const char *state)
if (!compel_interrupt_task(pid)) {
pr_debug("SEIZE %d: success\n", pid);
processes_to_wait++;
} else if (state == frozen) {
} else if (state == FROZEN) {
char buf[] = "/proc/XXXXXXXXXX/exe";
struct stat st;
@ -261,12 +397,10 @@ static int log_unfrozen_stacks(char *root)
char path[PATH_MAX];
FILE *f;
snprintf(path, sizeof(path), "%s/tasks", root);
f = fopen(path, "r");
if (f == NULL) {
pr_perror("Unable to open %s", path);
f = freezer_open_thread_list(root);
if (f == NULL)
return -1;
}
while (fgets(path, sizeof(path), f)) {
pid_t pid;
int ret, stack;
@ -331,8 +465,7 @@ static int log_unfrozen_stacks(char *root)
static int freeze_processes(void)
{
int fd, exit_code = -1;
char path[PATH_MAX];
const char *state = thawed;
enum freezer_state state = THAWED;
static const unsigned long step_ms = 100;
unsigned long nr_attempts = (opts.timeout * 1000000) / step_ms;
@ -354,23 +487,19 @@ static int freeze_processes(void)
pr_debug("freezing processes: %lu attempts with %lu ms steps\n",
nr_attempts, step_ms);
snprintf(path, sizeof(path), "%s/freezer.state", opts.freeze_cgroup);
fd = open(path, O_RDWR);
if (fd < 0) {
pr_perror("Unable to open %s", path);
fd = freezer_open();
if (fd < 0)
return -1;
}
state = get_freezer_state(fd);
if (!state) {
if (state == FREEZER_ERROR) {
close(fd);
return -1;
}
if (state == thawed) {
if (state == THAWED) {
freezer_thawed = true;
lseek(fd, 0, SEEK_SET);
if (write(fd, frozen, sizeof(frozen)) != sizeof(frozen)) {
pr_perror("Unable to freeze tasks");
if (freezer_write_state(fd, FROZEN)) {
close(fd);
return -1;
}
@ -384,12 +513,12 @@ static int freeze_processes(void)
*/
for (; i <= nr_attempts; i++) {
state = get_freezer_state(fd);
if (!state) {
if (state == FREEZER_ERROR) {
close(fd);
return -1;
}
if (state == frozen)
if (state == FROZEN)
break;
if (alarm_timeouted())
goto err;
@ -420,13 +549,9 @@ static int freeze_processes(void)
}
err:
if (exit_code == 0 || freezer_thawed) {
lseek(fd, 0, SEEK_SET);
if (write(fd, thawed, sizeof(thawed)) != sizeof(thawed)) {
pr_perror("Unable to thaw tasks");
exit_code = -1;
}
}
if (exit_code == 0 || freezer_thawed)
exit_code = freezer_write_state(fd, THAWED);
if (close(fd)) {
pr_perror("Unable to thaw tasks");
return -1;
@ -784,6 +909,27 @@ err_close:
return -1;
}
static int cgroup_version(void)
{
char path[PATH_MAX];
snprintf(path, sizeof(path), "%s/freezer.state", opts.freeze_cgroup);
if (access(path, F_OK) == 0) {
cgroup_v2 = false;
return 0;
}
snprintf(path, sizeof(path), "%s/cgroup.freeze", opts.freeze_cgroup);
if (access(path, F_OK) == 0) {
cgroup_v2 = true;
return 0;
}
pr_err("Neither a cgroupv1 (freezer.state) or cgroupv2 (cgroup.freeze) control file found.\n");
return -1;
}
int collect_pstree(void)
{
pid_t pid = root_item->pid->real;
@ -799,6 +945,11 @@ int collect_pstree(void)
*/
alarm(opts.timeout);
if (opts.freeze_cgroup && cgroup_version())
goto err;
pr_debug("Detected cgroup V%d freezer\n", cgroup_v2 ? 2 : 1);
if (opts.freeze_cgroup && freeze_processes())
goto err;

View file

@ -23,6 +23,7 @@
#include "types.h"
#include "page.h"
#include "util.h"
#include "memfd.h"
#include "protobuf.h"
#include "images/pagemap.pb-c.h"
@ -490,7 +491,7 @@ static int do_restore_shmem_content(void *addr, unsigned long size, unsigned lon
return ret;
}
static int restore_shmem_content(void *addr, struct shmem_info *si)
int restore_shmem_content(void *addr, struct shmem_info *si)
{
return do_restore_shmem_content(addr, si->size, si->shmid);
}
@ -500,6 +501,41 @@ int restore_sysv_shmem_content(void *addr, unsigned long size, unsigned long shm
return do_restore_shmem_content(addr, round_up(size, PAGE_SIZE), shmid);
}
int restore_memfd_shmem_content(int fd, unsigned long shmid, unsigned long size)
{
void *addr = NULL;
int ret = 1;
if (size == 0)
return 0;
if (ftruncate(fd, size) < 0) {
pr_perror("Can't resize shmem 0x%lx size=%ld", shmid, size);
goto out;
}
addr = mmap(NULL, size, PROT_WRITE | PROT_READ, MAP_SHARED, fd, 0);
if (addr == MAP_FAILED) {
pr_perror("Can't mmap shmem 0x%lx size=%ld", shmid, size);
goto out;
}
/*
* do_restore_shmem_content needs size to be page aligned.
*/
if (do_restore_shmem_content(addr, round_up(size, PAGE_SIZE), shmid) < 0) {
pr_err("Can't restore shmem content\n");
goto out;
}
ret = 0;
out:
if (addr)
munmap(addr, size);
return ret;
}
static int open_shmem(int pid, struct vma_area *vma)
{
VmaEntry *vi = vma->e;
@ -532,7 +568,7 @@ static int open_shmem(int pid, struct vma_area *vma)
flags = MAP_SHARED;
if (kdat.has_memfd) {
f = syscall(SYS_memfd_create, "", 0);
f = memfd_create("", 0);
if (f < 0) {
pr_perror("Unable to create memfd");
goto err;
@ -779,6 +815,32 @@ err:
return ret;
}
int dump_one_memfd_shmem(int fd, unsigned long shmid, unsigned long size)
{
int ret = -1;
void *addr;
struct shmem_info si;
if (size == 0)
return 0;
memset(&si, 0, sizeof(si));
si.shmid = shmid;
si.size = size;
addr = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
if (addr == MAP_FAILED) {
pr_perror("Can't mmap shmem 0x%lx", shmid);
goto err;
}
ret = do_dump_one_shmem(fd, addr, &si);
munmap(addr, size);
err:
return ret;
}
int dump_one_sysv_shmem(void *addr, unsigned long size, unsigned long shmid)
{
int fd, ret;

View file

@ -40,6 +40,7 @@
#include "tls.h"
#include "fdstore.h"
#include "util.h"
#include "namespaces.h"
#undef LOG_PREFIX
#define LOG_PREFIX "uffd: "
@ -254,6 +255,13 @@ bool uffd_noncooperative(void)
return (kdat.uffd_features & features) == features;
}
static int uffd_api_ioctl(void *arg, int fd, pid_t pid)
{
struct uffdio_api *uffdio_api = arg;
return ioctl(fd, UFFDIO_API, uffdio_api);
}
int uffd_open(int flags, unsigned long *features)
{
struct uffdio_api uffdio_api = { 0 };
@ -269,7 +277,8 @@ int uffd_open(int flags, unsigned long *features)
if (features)
uffdio_api.features = *features;
if (ioctl(uffd, UFFDIO_API, &uffdio_api)) {
if (userns_call(uffd_api_ioctl, 0, &uffdio_api, sizeof(uffdio_api),
uffd)) {
pr_perror("Failed to get uffd API");
goto err;
}

View file

@ -30,6 +30,8 @@
#include "linux/mount.h"
#include "linux/mount.h"
#include "kerndat.h"
#include "page.h"
#include "util.h"

View file

@ -20,7 +20,6 @@
#include "criu-log.h"
#include "mem.h"
#include "vma.h"
#include <compel/compel.h>
#include <compel/plugins/std/syscall.h>
#ifdef LOG_PREFIX

View file

@ -63,6 +63,7 @@ proto-obj-y += sysctl.o
proto-obj-y += autofs.o
proto-obj-y += macvlan.o
proto-obj-y += sit.o
proto-obj-y += memfd.o
CFLAGS += -iquote $(obj)/

View file

@ -16,6 +16,7 @@ import "sk-unix.proto";
import "fifo.proto";
import "pipe.proto";
import "tty.proto";
import "memfd.proto";
enum fd_types {
UND = 0;
@ -36,6 +37,7 @@ enum fd_types {
TUNF = 15;
EXT = 16;
TIMERFD = 17;
MEMFD = 18;
/* Any number above the real used. Not stored to image */
CTL_TTY = 65534;
@ -70,4 +72,5 @@ message file_entry {
optional fifo_entry fifo = 17;
optional pipe_entry pipe = 18;
optional tty_file_entry tty = 19;
optional memfd_file_entry memfd = 20;
}

View file

@ -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 {

22
images/memfd.proto Normal file
View file

@ -0,0 +1,22 @@
syntax = "proto2";
import "opts.proto";
import "fown.proto";
message memfd_file_entry {
required uint32 id = 1;
required uint32 flags = 2 [(criu).flags = "rfile.flags"];
required uint64 pos = 3;
required fown_entry fown = 4;
required uint32 inode_id = 5;
};
message memfd_inode_entry {
required string name = 1;
required uint32 uid = 2;
required uint32 gid = 3;
required uint64 size = 4;
required uint32 shmid = 5;
required uint32 seals = 6 [(criu).flags = "seals.flags"];
required uint64 inode_id = 7;
};

View file

@ -28,6 +28,8 @@ enum fstype {
// RPC_PIPEFS = 20;
// NFS = 21;
// NFS4 = 22;
CGROUP2 = 23;
};
message mnt_entry {

View file

@ -3,7 +3,9 @@
#include <stdint.h>
#include <stdbool.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/uio.h>
/*
* Because of kernel doing kmalloc for user data passed

View file

@ -522,6 +522,8 @@ handlers = {
'AUTOFS': entry_handler(pb.autofs_entry),
'FILES': entry_handler(pb.file_entry),
'CPUINFO': entry_handler(pb.cpuinfo_entry),
'MEMFD_FILE': entry_handler(pb.memfd_file_entry),
'MEMFD_INODE': entry_handler(pb.memfd_inode_entry),
}

View file

@ -124,6 +124,14 @@ rfile_flags_map = [
('O_CLOEXEC', 0o02000000),
]
seals_flags_map = [
('F_SEAL_SEAL', 0x0001),
('F_SEAL_SHRINK', 0x0002),
('F_SEAL_GROW', 0x0004),
('F_SEAL_WRITE', 0x0008),
('F_SEAL_FUTURE_WRITE', 0x0010),
]
pmap_flags_map = [
('PE_PARENT', 1 << 0),
('PE_LAZY', 1 << 1),
@ -136,6 +144,7 @@ flags_maps = {
'mmap.status': mmap_status_map,
'rfile.flags': rfile_flags_map,
'pmap.flags': pmap_flags_map,
'seals.flags': seals_flags_map,
}
gen_maps = {

View file

@ -0,0 +1,45 @@
FROM dockcross/base:latest
# Add the cross compiler sources
RUN echo "deb http://ftp.us.debian.org/debian/ buster main" >> /etc/apt/sources.list && \
dpkg --add-architecture arm64 && \
apt-get install emdebian-archive-keyring
RUN apt-get update && apt-get install -y \
crossbuild-essential-arm64 \
libc6-dev-arm64-cross \
libc6-arm64-cross \
libbz2-dev:arm64 \
libexpat1-dev:arm64 \
ncurses-dev:arm64 \
libssl-dev:arm64 \
protobuf-c-compiler \
protobuf-compiler \
python-protobuf \
libnl-3-dev:arm64 \
libprotobuf-dev:arm64 \
libnet-dev:arm64 \
libprotobuf-c-dev:arm64 \
libcap-dev:arm64 \
libaio-dev:arm64 \
libnl-route-3-dev:arm64
ENV CROSS_TRIPLE=aarch64-linux-gnu
ENV CROSS_COMPILE=${CROSS_TRIPLE}- \
CROSS_ROOT=/usr/${CROSS_TRIPLE} \
AS=/usr/bin/${CROSS_TRIPLE}-as \
AR=/usr/bin/${CROSS_TRIPLE}-ar \
CC=/usr/bin/${CROSS_TRIPLE}-gcc \
CPP=/usr/bin/${CROSS_TRIPLE}-cpp \
CXX=/usr/bin/${CROSS_TRIPLE}-g++ \
LD=/usr/bin/${CROSS_TRIPLE}-ld \
FC=/usr/bin/${CROSS_TRIPLE}-gfortran
ENV PATH="${PATH}:${CROSS_ROOT}/bin" \
PKG_CONFIG_PATH=/usr/lib/${CROSS_TRIPLE}/pkgconfig \
ARCH=aarch64
COPY . /criu
WORKDIR /criu
RUN make mrproper && date && make -j $(nproc) zdtm && date

View file

@ -1,7 +1,7 @@
FROM dockcross/base:latest
# Add the cross compiler sources
RUN echo "deb http://ftp.us.debian.org/debian/ jessie main" >> /etc/apt/sources.list && \
RUN echo "deb http://ftp.us.debian.org/debian/ buster main" >> /etc/apt/sources.list && \
dpkg --add-architecture armhf && \
apt-get install emdebian-archive-keyring

View file

@ -0,0 +1,45 @@
FROM dockcross/base:latest
# Add the cross compiler sources
RUN echo "deb http://ftp.us.debian.org/debian/ buster main" >> /etc/apt/sources.list && \
dpkg --add-architecture ppc64el && \
apt-get install emdebian-archive-keyring
RUN apt-get update && apt-get install -y \
crossbuild-essential-ppc64el \
libc6-dev-ppc64el-cross \
libc6-ppc64el-cross \
libbz2-dev:ppc64el \
libexpat1-dev:ppc64el \
ncurses-dev:ppc64el \
libssl-dev:ppc64el \
protobuf-c-compiler \
protobuf-compiler \
python-protobuf \
libnl-3-dev:ppc64el \
libprotobuf-dev:ppc64el \
libnet-dev:ppc64el \
libprotobuf-c-dev:ppc64el \
libcap-dev:ppc64el \
libaio-dev:ppc64el \
libnl-route-3-dev:ppc64el
ENV CROSS_TRIPLE=powerpc64le-linux-gnu
ENV CROSS_COMPILE=${CROSS_TRIPLE}- \
CROSS_ROOT=/usr/${CROSS_TRIPLE} \
AS=/usr/bin/${CROSS_TRIPLE}-as \
AR=/usr/bin/${CROSS_TRIPLE}-ar \
CC=/usr/bin/${CROSS_TRIPLE}-gcc \
CPP=/usr/bin/${CROSS_TRIPLE}-cpp \
CXX=/usr/bin/${CROSS_TRIPLE}-g++ \
LD=/usr/bin/${CROSS_TRIPLE}-ld \
FC=/usr/bin/${CROSS_TRIPLE}-gfortran
ENV PATH="${PATH}:${CROSS_ROOT}/bin" \
PKG_CONFIG_PATH=/usr/lib/${CROSS_TRIPLE}/pkgconfig \
ARCH=ppc64
COPY . /criu
WORKDIR /criu
RUN make mrproper && date && make -j $(nproc) zdtm && date

View file

@ -2,7 +2,7 @@ ARCHES := x86_64 fedora-asan fedora-rawhide centos armv7hf
TARGETS := $(ARCHES) alpine
TARGETS_CLANG := $(addsuffix $(TARGETS),-clang)
CONTAINER_RUNTIME := docker
TARGETS += armv7-cross
TARGETS += armv7-cross aarch64-cross ppc64-cross
all: $(TARGETS) $(TARGETS_CLANG)
.PHONY: all

View file

@ -152,6 +152,8 @@ endef
define FEATURE_TEST_NFTABLES_LIB_API_0
#include <string.h>
#include <nftables/libnftables.h>
int main(int argc, char **argv)
@ -171,3 +173,14 @@ int main(int argc, char **argv)
}
endef
define FEATURE_TEST_MEMFD_CREATE
#include <sys/mman.h>
#include <stddef.h>
int main(void)
{
return memfd_create(NULL, 0);
}
endef

View file

@ -3,7 +3,7 @@ ifndef ____nmk_defined__utils
#
# Usage: option := $(call try-compile,language,source-to-build,cc-options,cc-defines)
try-compile = $(shell sh -c 'echo "$(2)" | \
$(CC) $(4) -x $(1) - $(3) -o /dev/null > /dev/null 2>&1 && \
$(CC) $(CFLAGS) $(LDFLAGS) $(4) -x $(1) - $(3) -o /dev/null > /dev/null 2>&1 && \
echo true || echo false')
#

View file

@ -47,6 +47,8 @@ for imgf in find.stdout.readlines():
continue
if imgf_b.startswith(b'ip6tables-'):
continue
if imgf_b.startswith(b'nftables-'):
continue
if imgf_b.startswith(b'route-'):
continue
if imgf_b.startswith(b'route6-'):

28
test/inhfd/memfd.py Executable file
View file

@ -0,0 +1,28 @@
import ctypes
import os
libc = ctypes.CDLL(None)
def memfd_create(name, flags):
return libc.memfd_create(name.encode('utf8'), flags)
def create_fds():
def create_memfd_pair(name):
fd = memfd_create(name, 0)
fw = open('/proc/self/fd/{}'.format(fd), 'wb')
fr = open('/proc/self/fd/{}'.format(fd), 'rb')
os.close(fd)
return (fw, fr)
return [create_memfd_pair("name{}".format(i)) for i in range(10)]
def filename(f):
name = os.readlink('/proc/self/fd/{}'.format(f.fileno()))
name = name.replace(' (deleted)', '')
return name
def dump_opts(sockf):
return []

7
test/inhfd/memfd.py.checkskip Executable file
View file

@ -0,0 +1,7 @@
#!/usr/bin/env python
import ctypes
libc = ctypes.CDLL(None)
# libc may not have memfd_create (e.g., centos on travis)
libc.memfd_create("test".encode('utf8'), 0)

1
test/inhfd/memfd.py.desc Normal file
View file

@ -0,0 +1 @@
{ 'flavor': 'h' }

View file

@ -27,4 +27,3 @@ fi
./test/zdtm.py run -t zdtm/static/maps04 --fault 131 --keep-going --report report --pre 2:1 || fail
./test/zdtm.py run -t zdtm/transition/maps008 --fault 131 --keep-going --report report --pre 2:1 || fail
./test/zdtm.py run -t zdtm/static/maps01 --fault 132 -f h || fail

View file

@ -691,9 +691,17 @@ class inhfd_test:
i = 0
for _, peer_file in self.__files:
msg = self.__get_message(i)
my_file.close()
try:
data = peer_file.read(16)
# File pairs naturally block on read() until the write()
# happen (or the writer is closed). This is not the case for
# regular files, so we loop.
data = b''
while not data:
# In python 2.7, peer_file.read() doesn't call the read
# system call if it's read file to the end once. The
# next seek allows to workaround this problem.
data = os.read(peer_file.fileno(), 16)
time.sleep(0.1)
except Exception as e:
print("Unable to read a peer file: %s" % e)
sys.exit(1)

View file

@ -4,7 +4,7 @@ CFLAGS += $(USERCFLAGS)
LIB := libzdtmtst.a
LIBSRC := datagen.c msg.c parseargs.c test.c streamutil.c lock.c ns.c tcp.c fs.c sysctl.c
LIBSRC := datagen.c msg.c parseargs.c test.c streamutil.c lock.c ns.c tcp.c unix.c fs.c sysctl.c
LIBOBJ := $(LIBSRC:%.c=%.o)
BIN := groups

View file

@ -94,3 +94,27 @@ err:
mnt_info_free(&m);
goto out;
}
int get_cwd_check_perm(char **result)
{
char *cwd;
*result = 0;
cwd = get_current_dir_name();
if (!cwd) {
pr_perror("failed to get current directory");
return -1;
}
if (access(cwd, X_OK)) {
pr_err("access check for bit X for current dir path '%s' "
"failed for uid:%d,gid:%d, error: %d(%s). "
"Bit 'x' should be set in all path components of "
"this directory\n",
cwd, getuid(), getgid(), errno, strerror(errno)
);
return -1;
}
*result = cwd;
return 0;
}

View file

@ -50,4 +50,28 @@ extern mnt_info_t *mnt_info_alloc(void);
extern void mnt_info_free(mnt_info_t **m);
extern mnt_info_t *get_cwd_mnt_info(void);
/*
* get_cwd_check_perm is called to check that cwd is actually usable for a calling
* process.
*
* Example output of a stat command on a '/root' path shows file access bits:
* > stat /root
* File: /root
* ...
* Access: (0550/dr-xr-x---) Uid: ( 0/root) Gid: ( 0/root)
* ^- no 'x' bit for other
*
* Here we can see that '/root' dir (that often can be part of cwd path) does not
* allow non-root user and non-root group to list contents of this directory.
* Calling process matching 'other' access category may succeed getting cwd path, but will
* fail performing further filesystem operations based on this path with confusing errors.
*
* This function calls get_current_dir_name and explicitly checks that bit 'x' is enabled for
* a calling process and logs the error.
*
* If check passes, stores get_current_dir's result in *result and returns 0
* If check fails, stores 0 in *result and returns -1
*/
extern int get_cwd_check_perm(char **result);
#endif /* ZDTM_FS_H_ */

19
test/zdtm/lib/unix.c Normal file
View file

@ -0,0 +1,19 @@
#include <sys/socket.h>
#include <sys/un.h>
#include "zdtmtst.h"
#include "fs.h"
int unix_fill_sock_name(struct sockaddr_un *name, char *relFilename)
{
char *cwd;
if (get_cwd_check_perm(&cwd)) {
pr_err("failed to get current working directory with valid permissions.\n");
return -1;
}
name->sun_family = AF_LOCAL;
ssprintf(name->sun_path, "%s/%s", cwd, relFilename);
return 0;
}

View file

@ -149,6 +149,9 @@ extern int tcp_init_server(int family, int *port);
extern int tcp_accept_server(int sock);
extern int tcp_init_client(int family, char *servIP, unsigned short servPort);
struct sockaddr_un;
extern int unix_fill_sock_name(struct sockaddr_un *name, char *relFilename);
struct zdtm_tcp_opts {
bool reuseaddr;
bool reuseport;

View file

@ -220,6 +220,12 @@ TST_NOFILE := \
child_subreaper \
child_subreaper_existing_child \
child_subreaper_and_reparent \
memfd00 \
memfd01 \
memfd02 \
memfd03 \
shmemfd \
shmemfd-priv \
# jobctl00 \
ifneq ($(ARCH),arm)
@ -359,6 +365,9 @@ TST_DIR = \
private_bind_propagation \
ghost_on_rofs \
overmounted_file \
opath_file \
symlink \
symlink01 \
TST_DIR_FILE = \
chroot \
@ -534,6 +543,7 @@ clone_fs: LDLIBS += -pthread
# we have to explicitly specify both .o and .d for this case:
netns_sub_veth.o netns_sub_veth.d: CPPFLAGS += $(call pkg-cflags, libnl-3.0)
netns_sub_veth: LDLIBS += $(call pkg-libs, libnl-route-3.0 libnl-3.0)
symlink01: CFLAGS += -DZDTM_UNLINK_SYMLINK
socket-tcp-fin-wait1: CFLAGS += -D ZDTM_TCP_FIN_WAIT1
socket-tcp-fin-wait2: CFLAGS += -D ZDTM_TCP_FIN_WAIT2

View file

@ -16,19 +16,6 @@ const char *test_author = "Tycho Andersen <tycho.andersen@canonical.com>";
char *dirname;
TEST_OPTION(dirname, string, "directory name", 1);
static int fill_sock_name(struct sockaddr_un *name, const char *filename)
{
char *cwd;
cwd = get_current_dir_name();
if (strlen(filename) + strlen(cwd) + 1 >= sizeof(name->sun_path))
return -1;
name->sun_family = AF_LOCAL;
ssprintf(name->sun_path, "%s/%s", cwd, filename);
return 0;
}
static int bind_and_listen(struct sockaddr_un *addr)
{
int sk;
@ -71,10 +58,8 @@ int main(int argc, char **argv)
goto out;
}
if (fill_sock_name(&addr, filename) < 0) {
pr_err("filename \"%s\" is too long\n", filename);
if (unix_fill_sock_name(&addr, filename))
goto out;
}
sk1 = bind_and_listen(&addr);
if (sk1 < 0)

View file

@ -17,28 +17,13 @@ const char *test_author = "Roman Kagan <rkagan@parallels.com>";
char *filename;
TEST_OPTION(filename, string, "file name", 1);
static int fill_sock_name(struct sockaddr_un *name, const char *filename)
{
char *cwd;
cwd = get_current_dir_name();
if (strlen(filename) + strlen(cwd) + 1 >= sizeof(name->sun_path))
return -1;
name->sun_family = AF_LOCAL;
sprintf(name->sun_path, "%s/%s", cwd, filename);
return 0;
}
static int setup_srv_sock(void)
{
struct sockaddr_un name;
int sock;
if (fill_sock_name(&name, filename) < 0) {
pr_perror("filename \"%s\" is too long", filename);
if (unix_fill_sock_name(&name, filename))
return -1;
}
sock = socket(PF_LOCAL, SOCK_STREAM, 0);
if (sock < 0) {
@ -67,7 +52,7 @@ static int setup_clnt_sock(void)
struct sockaddr_un name;
int sock;
if (fill_sock_name(&name, filename) < 0)
if (unix_fill_sock_name(&name, filename))
return -1;
sock = socket(PF_LOCAL, SOCK_STREAM, 0);

103
test/zdtm/static/memfd00.c Normal file
View file

@ -0,0 +1,103 @@
#include <fcntl.h>
#include <linux/memfd.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/vfs.h>
#include <unistd.h>
#include "zdtmtst.h"
const char *test_doc = "memfd file descriptor";
const char *test_author = "Nicolas Viennot <Nicolas.Viennot@twosigma.com>";
#define err(exitcode, msg, ...) ({ pr_perror(msg, ##__VA_ARGS__); exit(exitcode); })
static int _memfd_create(const char *name, unsigned int flags)
{
return syscall(SYS_memfd_create, name, flags);
}
int main(int argc, char *argv[])
{
int fd, fl_flags1, fl_flags2, fd_flags1, fd_flags2;
struct statfs statfs1, statfs2;
off_t pos1, pos2;
char buf[5];
test_init(argc, argv);
fd = _memfd_create("somename", MFD_CLOEXEC);
if (fd < 0)
err(1, "Can't call memfd_create");
if (fcntl(fd, F_SETFL, O_APPEND) < 0)
err(1, "Can't get fl flags");
if ((fl_flags1 = fcntl(fd, F_GETFL)) == -1)
err(1, "Can't get fl flags");
if ((fd_flags1 = fcntl(fd, F_GETFD)) == -1)
err(1, "Can't get fd flags");
if (fstatfs(fd, &statfs1) < 0)
err(1, "statfs issue");
if (write(fd, "hello", 5) != 5)
err(1, "write error");
pos1 = 3;
if (lseek(fd, pos1, SEEK_SET) < 0)
err(1, "seek error");
test_daemon();
test_waitsig();
if ((fl_flags2 = fcntl(fd, F_GETFL)) == -1)
err(1, "Can't get fl flags");
if (fl_flags1 != fl_flags2) {
fail("fl flags differs");
return 1;
}
if ((fd_flags2 = fcntl(fd, F_GETFD)) == -1)
err(1, "Can't get fd flags");
if (fd_flags1 != fd_flags2) {
fail("fd flags differs");
return 1;
}
if (fstatfs(fd, &statfs2) < 0)
err(1, "statfs issue");
if (statfs1.f_type != statfs2.f_type) {
fail("statfs.f_type differs");
return 1;
}
pos2 = lseek(fd, 0, SEEK_CUR);
if (pos1 != pos2) {
fail("position differs");
return 1;
}
if (pread(fd, buf, sizeof(buf), 0) != sizeof(buf)) {
fail("read problem");
return 1;
}
if (memcmp(buf, "hello", sizeof(buf))) {
fail("content mismatch");
return 1;
}
pass();
return 0;
}

114
test/zdtm/static/memfd01.c Normal file
View file

@ -0,0 +1,114 @@
#include <fcntl.h>
#include <linux/memfd.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/vfs.h>
#include <unistd.h>
#include "zdtmtst.h"
const char *test_doc = "memfd with different file pointer";
const char *test_author = "Nicolas Viennot <Nicolas.Viennot@twosigma.com>";
#define err(exitcode, msg, ...) ({ pr_perror(msg, ##__VA_ARGS__); exit(exitcode); })
static int _memfd_create(const char *name, unsigned int flags)
{
return syscall(SYS_memfd_create, name, flags);
}
int main(int argc, char *argv[])
{
pid_t pid, pid_child;
int fd, ret, status;
task_waiter_t t;
test_init(argc, argv);
task_waiter_init(&t);
fd = _memfd_create("somename", MFD_CLOEXEC);
if (fd < 0)
err(1, "Can't call memfd_create");
pid = getpid();
pid_child = fork();
if (pid_child < 0)
err(1, "Can't fork");
if (!pid_child) {
char fdpath[100];
char buf[1];
int fl_flags1, fl_flags2, fd_flags1, fd_flags2;
snprintf(fdpath, sizeof(fdpath), "/proc/%d/fd/%d", pid, fd);
/*
* We pass O_LARGEFILE because in compat mode, our file
* descriptor does not get O_LARGEFILE automatically, but the
* restorer using non-compat open() is forced O_LARGEFILE.
* This creates a flag difference, which we don't want to deal
* with this at the moment.
*/
fd = open(fdpath, O_RDONLY | O_LARGEFILE);
if (fd < 0)
err(1, "Can't open memfd via proc");
if ((fl_flags1 = fcntl(fd, F_GETFL)) == -1)
err(1, "Can't get fl flags");
if ((fd_flags1 = fcntl(fd, F_GETFD)) == -1)
err(1, "Can't get fd flags");
task_waiter_complete(&t, 1);
// checkpoint-restore happens here
task_waiter_wait4(&t, 2);
if (read(fd, buf, 1) != 1)
err(1, "Can't read");
if ((fl_flags2 = fcntl(fd, F_GETFL)) == -1)
err(1, "Can't get fl flags");
if (fl_flags1 != fl_flags2)
err(1, "fl flags differs");
if ((fd_flags2 = fcntl(fd, F_GETFD)) == -1)
err(1, "Can't get fd flags");
if (fd_flags1 != fd_flags2)
err(1, "fd flags differs");
if (buf[0] != 'x')
err(1, "Read incorrect");
return 0;
}
task_waiter_wait4(&t, 1);
test_daemon();
test_waitsig();
if (write(fd, "x", 1) != 1)
err(1, "Can't write");
task_waiter_complete(&t, 2);
ret = wait(&status);
if (ret == -1 || !WIFEXITED(status) || WEXITSTATUS(status)) {
kill(pid, SIGKILL);
fail("child had issue");
return 1;
}
pass();
return 0;
}

View file

@ -0,0 +1,87 @@
#include <fcntl.h>
#include <linux/memfd.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/vfs.h>
#include <unistd.h>
#include <sys/mman.h>
#include "zdtmtst.h"
const char *test_doc = "memfd mmap";
const char *test_author = "Nicolas Viennot <Nicolas.Viennot@twosigma.com>";
#define err(exitcode, msg, ...) ({ pr_perror(msg, ##__VA_ARGS__); exit(exitcode); })
static int _memfd_create(const char *name, unsigned int flags)
{
return syscall(SYS_memfd_create, name, flags);
}
int main(int argc, char *argv[])
{
#define LEN 6
int fd;
void *addr_shared, *addr_private;
char buf[LEN];
test_init(argc, argv);
fd = _memfd_create("somename", MFD_CLOEXEC);
if (fd < 0)
err(1, "Can't call memfd_create");
if (ftruncate(fd, LEN) < 0)
err(1, "Can't truncate");
addr_shared = mmap(NULL, LEN, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (addr_shared == MAP_FAILED)
err(1, "Can't mmap");
write(fd, "write1", LEN);
addr_private = mmap(NULL, LEN, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
if (addr_private == MAP_FAILED)
err(1, "Can't mmap");
test_daemon();
test_waitsig();
if (memcmp(addr_shared, "write1", LEN)) {
fail("content mismatch (shared)");
return 1;
}
strcpy(addr_shared, "write2");
if (pread(fd, buf, LEN, 0) != LEN) {
fail("read problem");
return 1;
}
if (memcmp(buf, "write2", LEN)) {
fail("content mismatch (shared)");
return 1;
}
if (memcmp(addr_private, "write2", LEN)) {
fail("content mismatch (private)");
return 1;
}
strcpy(addr_private, "write3");
if (memcmp(addr_shared, "write2", LEN)) {
fail("content mismatch (shared)");
return 1;
}
pass();
return 0;
}

View file

@ -0,0 +1,97 @@
#include <fcntl.h>
#include <linux/memfd.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/vfs.h>
#include <unistd.h>
#include <sys/mman.h>
#include "zdtmtst.h"
const char *test_doc = "memfd seals";
const char *test_author = "Nicolas Viennot <Nicolas.Viennot@twosigma.com>";
#define err(exitcode, msg, ...) ({ pr_perror(msg, ##__VA_ARGS__); exit(exitcode); })
static int _memfd_create(const char *name, unsigned int flags)
{
return syscall(SYS_memfd_create, name, flags);
}
#ifndef F_LINUX_SPECIFIC_BASE
# define F_LINUX_SPECIFIC_BASE 1024
#endif
#ifndef F_ADD_SEALS
#define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9)
#endif
#ifndef F_GET_SEALS
#define F_GET_SEALS (F_LINUX_SPECIFIC_BASE + 10)
#endif
#ifndef F_SEAL_SEAL
#define F_SEAL_SEAL 0x0001 /* prevent further seals from being set */
#define F_SEAL_SHRINK 0x0002 /* prevent file from shrinking */
#define F_SEAL_GROW 0x0004 /* prevent file from growing */
#define F_SEAL_WRITE 0x0008 /* prevent writes */
#endif
int main(int argc, char *argv[])
{
#define LEN 5
int fd, fd2;
void *addr_write, *addr_read;
char fdpath[100];
test_init(argc, argv);
fd = _memfd_create("somename", MFD_ALLOW_SEALING | MFD_CLOEXEC);
if (fd < 0)
err(1, "Can't call memfd_create");
if (write(fd, "hello", LEN) != LEN)
err(1, "Can't write");
if (fcntl(fd, F_ADD_SEALS, F_SEAL_WRITE) < 0)
err(1, "Can't add seals");
test_daemon();
test_waitsig();
snprintf(fdpath, sizeof(fdpath), "/proc/self/fd/%d", fd);
fd2 = open(fdpath, O_RDWR);
if (fd2 < 0)
err(1, "Can't open memfd via proc");
if (fcntl(fd, F_GET_SEALS) != F_SEAL_WRITE) {
fail("Seals are different");
return 1;
}
addr_write = mmap(NULL, LEN, PROT_WRITE, MAP_SHARED, fd2, 0);
if (addr_write != MAP_FAILED) {
fail("Should not be able to get write access");
return 1;
}
addr_read = mmap(NULL, 1, PROT_READ, MAP_PRIVATE, fd2, 0);
if (addr_read == MAP_FAILED)
err(1, "Can't mmap");
if (memcmp(addr_read, "hello", LEN)) {
fail("Mapping has bad data");
return 1;
}
pass();
return 0;
}

View file

@ -0,0 +1,95 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <limits.h>
#include "zdtmtst.h"
#define TEST_FILE "test_file"
#define BUF_SIZE 4096
#define fdinfo_field(str, field) !strncmp(str, field":", sizeof(field))
#define pr_debug(format, arg...) test_msg("DBG: %s:%d: " format, __FILE__, __LINE__, ## arg)
const char *test_doc = "Check open file with O_PATH preserved";
const char *test_author = "Pavel Tikhomirov <ptikhomirov@virtuozzo.com>";
char *dirname;
TEST_OPTION(dirname, string, "directory name", 1);
struct fdinfo {
int flags;
};
static int parse_self_fdinfo(int fd, struct fdinfo *fi)
{
char path[PATH_MAX], line[BUF_SIZE];
FILE *file;
int ret = -1;
unsigned long long val;
snprintf(path, sizeof(path), "/proc/self/fdinfo/%d", fd);
file = fopen(path, "r");
if (!file) {
pr_perror("fopen");
return -1;
}
while (fgets(line, sizeof(line), file)) {
if (fdinfo_field(line, "flags")) {
if (sscanf(line, "%*s %llo", &val) != 1) {
pr_err("failed to read flags: %s", line);
goto fail;
}
pr_debug("Open flags = %llu\n", val);
fi->flags = val;
ret = 0;
break;
}
}
fail:
fclose(file);
return ret;
}
int main(int argc, char **argv)
{
char test_file[PATH_MAX];
struct fdinfo fi;
int fd;
test_init(argc, argv);
if (mkdir(dirname, 0700)) {
pr_perror("can't make directory %s", dirname);
exit(1);
}
snprintf(test_file, sizeof(test_file), "%s/%s", dirname, TEST_FILE);
fd = creat(test_file, 0644);
if (fd == -1) {
pr_perror("cat't create %s", test_file);
return 1;
}
close(fd);
fd = open(test_file, O_PATH);
if (fd == -1) {
pr_perror("cat't open file %s with O_PATH", test_file);
return 1;
}
test_daemon();
test_waitsig();
if (parse_self_fdinfo(fd, &fi))
return 1;
if (!(fi.flags & O_PATH)) {
fail("File lost O_PATH open flag");
return 1;
}
close(fd);
pass();
return 0;
}

View file

@ -13,27 +13,28 @@ const char *test_author = "Andrei Vagin <avagin@gmail.com>";
int main(int argc, char **argv)
{
int p[2], i;
int p[2][2], i;
uint8_t buf[BUF_SIZE];
uint32_t crc;
test_init(argc, argv);
if (pipe2(p, O_NONBLOCK)) {
pr_perror("pipe");
return 1;
}
if (fcntl(p[1], F_SETPIPE_SZ, DATA_SIZE) == -1) {
pr_perror("Unable to change a pipe size");
return 1;
for (i = 0; i < 2; i++) {
if (pipe2(p[i], O_NONBLOCK)) {
pr_perror("pipe");
return 1;
}
if (fcntl(p[i][1], F_SETPIPE_SZ, DATA_SIZE) == -1) {
pr_perror("Unable to change a pipe size");
return 1;
}
}
crc = ~0;
datagen(buf, BUF_SIZE, &crc);
for (i = 0; i < DATA_SIZE / BUF_SIZE; i++) {
if (write(p[1], buf, BUF_SIZE) != BUF_SIZE) {
if (write(p[0][1], buf, BUF_SIZE) != BUF_SIZE) {
pr_perror("write");
return 1;
}
@ -43,12 +44,26 @@ int main(int argc, char **argv)
test_waitsig();
for (i = 0; i < DATA_SIZE / BUF_SIZE; i++) {
if (read(p[0], buf, BUF_SIZE) != BUF_SIZE) {
if (read(p[0][0], buf, BUF_SIZE) != BUF_SIZE) {
pr_perror("read");
return 1;
}
}
for (i = 0; i < 2; i++) {
int size;
size = fcntl(p[i][1], F_GETPIPE_SZ);
if (size < 0) {
pr_perror("Unable to get a pipe size");
return 1;
}
if (size != DATA_SIZE) {
fail("%d: size %d expected %d", i, size, DATA_SIZE);
return 1;
}
}
pass();
return 0;
}

View file

@ -0,0 +1,84 @@
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/vfs.h>
#include <fcntl.h>
#include "zdtmtst.h"
const char *test_doc = "Test C/R of shared memory file descriptors";
const char *test_author = "Andrei Vagin <avagin@gmail.com>";
#define err(exitcode, msg, ...) ({ pr_perror(msg, ##__VA_ARGS__); exit(exitcode); })
int main(int argc, char *argv[])
{
void *addr, *priv_addr, *addr2;
char path[4096];
int fd;
test_init(argc, argv);
addr = mmap(NULL, 5 * PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0);
if (addr == MAP_FAILED) {
pr_perror("mmap");
return 1;
}
*(int *) addr = 1;
*(int *) (addr + PAGE_SIZE) = 11;
*(int *) (addr + 2 * PAGE_SIZE) = 111;
snprintf(path, sizeof(path), "/proc/self/map_files/%lx-%lx",
(long)addr, (long)addr + 5 * PAGE_SIZE);
fd = open(path, O_RDWR | O_LARGEFILE);
if (fd < 0)
err(1, "Can't open %s", path);
priv_addr = mmap(NULL, 5 * PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_FILE | MAP_PRIVATE, fd, PAGE_SIZE);
if (priv_addr == MAP_FAILED) {
pr_perror("mmap");
return 1;
}
addr2 = mmap(NULL, 5 * PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, fd, 2 * PAGE_SIZE);
if (addr2 == MAP_FAILED) {
pr_perror("mmap");
return 1;
}
*(int *) (priv_addr + PAGE_SIZE) = 22;
test_daemon();
test_waitsig();
if (*(int *) (priv_addr + PAGE_SIZE) != 22) {
fail("the second page of the private mapping is corrupted");
return 1;
}
if (*(int *) (priv_addr) != 11) {
fail("the first page of the private mapping is corrupted");
return 1;
}
if (*(int *) (addr2) != 111) {
fail("the first page of the second shared mapping is corrupted");
return 1;
}
*(int *) (addr2) = 333;
if (*(int *) (addr + 2 * PAGE_SIZE) != 333) {
fail("the first page of the second shared mapping isn't shared");
return 1;
}
*(int *) (addr + 3 * PAGE_SIZE) = 444;
if (*(int *) (priv_addr + 2 * PAGE_SIZE) != 444) {
fail("the third page of the private mapping is corrupted");
return 1;
}
pass();
return 0;
}

View file

@ -0,0 +1 @@
{'flavor': 'h ns', 'flags': 'suid'}

107
test/zdtm/static/shmemfd.c Normal file
View file

@ -0,0 +1,107 @@
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/vfs.h>
#include <fcntl.h>
#include "zdtmtst.h"
const char *test_doc = "Test C/R of shared memory file descriptors";
const char *test_author = "Andrei Vagin <avagin@gmail.com>";
#define err(exitcode, msg, ...) ({ pr_perror(msg, ##__VA_ARGS__); exit(exitcode); })
int main(int argc, char *argv[])
{
int fd, fl_flags1, fl_flags2, fd_flags1, fd_flags2;
struct statfs statfs1, statfs2;
off_t pos1, pos2;
char path[4096];
char buf[5];
void *addr;
test_init(argc, argv);
addr = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0);
if (addr == MAP_FAILED) {
pr_perror("mmap");
return 1;
}
snprintf(path, sizeof(path), "/proc/self/map_files/%lx-%lx",
(long)addr, (long)addr + PAGE_SIZE);
fd = open(path, O_RDWR | O_LARGEFILE);
if (fd < 0)
err(1, "Can't open %s", path);
ftruncate(fd, 0);
munmap(addr, PAGE_SIZE);
if (fcntl(fd, F_SETFL, O_APPEND) < 0)
err(1, "Can't get fl flags");
if ((fl_flags1 = fcntl(fd, F_GETFL)) == -1)
err(1, "Can't get fl flags");
if ((fd_flags1 = fcntl(fd, F_GETFD)) == -1)
err(1, "Can't get fd flags");
if (fstatfs(fd, &statfs1) < 0)
err(1, "statfs issue");
if (write(fd, "hello", 5) != 5)
err(1, "write error");
pos1 = 3;
if (lseek(fd, pos1, SEEK_SET) < 0)
err(1, "seek error");
test_daemon();
test_waitsig();
if ((fl_flags2 = fcntl(fd, F_GETFL)) == -1)
err(1, "Can't get fl flags");
if (fl_flags1 != fl_flags2) {
fail("fl flags differs %x %x", fl_flags1, fl_flags2);
return 1;
}
if ((fd_flags2 = fcntl(fd, F_GETFD)) == -1)
err(1, "Can't get fd flags");
if (fd_flags1 != fd_flags2) {
fail("fd flags differs");
return 1;
}
if (fstatfs(fd, &statfs2) < 0)
err(1, "statfs issue");
if (statfs1.f_type != statfs2.f_type) {
fail("statfs.f_type differs");
return 1;
}
pos2 = lseek(fd, 0, SEEK_CUR);
if (pos1 != pos2) {
fail("position differs");
return 1;
}
if (pread(fd, buf, sizeof(buf), 0) != sizeof(buf)) {
fail("read problem");
return 1;
}
if (memcmp(buf, "hello", sizeof(buf))) {
fail("content mismatch");
return 1;
}
pass();
return 0;
}

View file

@ -0,0 +1 @@
{'flavor': 'h ns', 'flags': 'suid'}

View file

@ -24,22 +24,6 @@ const char *test_author = "Cyrill Gorcunov <gorcunov@openvz.org>";
char *dirname;
TEST_OPTION(dirname, string, "directory name", 1);
static int fill_sock_name(struct sockaddr_un *name, const char *filename)
{
char *cwd;
cwd = get_current_dir_name();
if (strlen(filename) + strlen(cwd) + 1 >= sizeof(name->sun_path)) {
pr_err("Name %s/%s is too long for socket\n",
cwd, filename);
return -1;
}
name->sun_family = AF_LOCAL;
ssprintf(name->sun_path, "%s/%s", cwd, filename);
return 0;
}
static int sk_alloc_bind(int type, struct sockaddr_un *addr)
{
int sk;
@ -155,10 +139,9 @@ int main(int argc, char **argv)
*/
ssprintf(filename, "%s/%s", subdir_dg, "sk-dt");
if (fill_sock_name(&addr, filename) < 0) {
pr_err("%s is too long for socket\n", filename);
if (unix_fill_sock_name(&addr, filename))
return 1;
}
unlink(addr.sun_path);
sk_dgram[0] = sk_alloc_bind(SOCK_DGRAM, &addr);
@ -184,10 +167,9 @@ int main(int argc, char **argv)
test_msg("sk-dt: alloc/connect/unlink %d %s\n", sk_dgram[3], addr.sun_path);
ssprintf(filename, "%s/%s", dirname, "sole");
if (fill_sock_name(&addr, filename) < 0) {
pr_err("%s is too long for socket\n", filename);
if (unix_fill_sock_name(&addr, filename))
return 1;
}
unlink(addr.sun_path);
sk_dgram[4] = sk_alloc_bind(SOCK_DGRAM, &addr);
@ -237,7 +219,7 @@ int main(int argc, char **argv)
sk_dgram_pair[0], sk_dgram_pair[1]);
ssprintf(filename, "%s/%s", subdir_dg, "sk-dtp");
if (fill_sock_name(&addr, filename) < 0) {
if (unix_fill_sock_name(&addr, filename)) {
pr_err("%s is too long for socket\n", filename);
return 1;
}
@ -270,10 +252,9 @@ int main(int argc, char **argv)
* - delete socket on fs
*/
ssprintf(filename, "%s/%s", subdir_st, "sk-st");
if (fill_sock_name(&addr, filename) < 0) {
pr_err("%s is too long for socket\n", filename);
if (unix_fill_sock_name(&addr, filename))
return 1;
}
unlink(addr.sun_path);
sk_st[0] = sk_alloc_bind(SOCK_STREAM, &addr);

102
test/zdtm/static/symlink.c Normal file
View file

@ -0,0 +1,102 @@
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <limits.h>
#include "zdtmtst.h"
#define TEST_FILE "test_file"
#define TEST_SYMLINK "test_symlink"
const char *test_doc = "Check open symlink preserved";
const char *test_author = "Pavel Tikhomirov <ptikhomirov@virtuozzo.com>";
char *dirname;
TEST_OPTION(dirname, string, "directory name", 1);
int main(int argc, char **argv)
{
char test_symlink[PATH_MAX];
char test_file[PATH_MAX];
char pathbuf[PATH_MAX];
struct stat stb, sta;
int ret, fd;
test_init(argc, argv);
if (mkdir(dirname, 0700)) {
pr_perror("can't make directory %s", dirname);
exit(1);
}
snprintf(test_file, sizeof(test_file), "%s/%s", dirname, TEST_FILE);
ret = creat(test_file, 0644);
if (ret == -1) {
pr_perror("cat't create %s", test_file);
return 1;
}
close(ret);
snprintf(test_symlink, sizeof(test_symlink), "%s/%s", dirname, TEST_SYMLINK);
ret = symlink(test_file, test_symlink);
if (ret == -1) {
pr_perror("cat't symlink to %s", test_symlink);
return 1;
}
fd = open(test_symlink, O_PATH | O_NOFOLLOW);
if (fd == -1) {
pr_perror("cat't open symlink %s", test_symlink);
return 1;
}
ret = fstat(fd, &sta);
if (ret == -1) {
pr_perror("cat't fstat %s", test_symlink);
return 1;
}
if (!S_ISLNK(sta.st_mode)) {
pr_perror("file is not symlink %s", test_symlink);
return 1;
}
#ifdef ZDTM_UNLINK_SYMLINK
if (unlink(test_symlink)) {
pr_perror("can't unlink symlink %s", test_symlink);
return 1;
}
#endif
test_daemon();
test_waitsig();
ret = fstat(fd, &stb);
if (ret == -1) {
fail("cat't fstat %s", test_symlink);
return 1;
}
if (!S_ISLNK(stb.st_mode)) {
fail("file is not symlink %s", test_symlink);
return 1;
}
ret = readlinkat(fd, "", pathbuf, sizeof(pathbuf) - 1);
if (ret < 0) {
fail("Can't readlinkat");
return 1;
}
pathbuf[ret] = 0;
if (strcmp(test_file, pathbuf)) {
fail("symlink points to %s but %s expected", pathbuf, test_file);
return 1;
}
close(fd);
pass();
return 0;
}

Some files were not shown because too many files have changed in this diff Show more