mirror of
https://github.com/checkpoint-restore/criu.git
synced 2026-08-03 07:12:48 +00:00
Merge pull request #1003 from avagin/v3.14-part2
Prepare v3.14 (part 2)
This commit is contained in:
commit
fcb23dbfcf
102 changed files with 2214 additions and 291 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -42,3 +42,4 @@ lib/.crit-setup.files
|
|||
compel/include/asm
|
||||
include/common/asm
|
||||
include/common/config.h
|
||||
build/
|
||||
|
|
|
|||
8
.mailmap
8
.mailmap
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"*
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
#include <string.h>
|
||||
|
||||
#include "uapi/compel.h"
|
||||
#include <errno.h>
|
||||
|
||||
#include "handle-elf.h"
|
||||
#include "piegen.h"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
#include <string.h>
|
||||
|
||||
#include "uapi/compel.h"
|
||||
#include <errno.h>
|
||||
|
||||
#include "handle-elf.h"
|
||||
#include "piegen.h"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#include <asm/cputable.h>
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "compel-cpu.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
#include <string.h>
|
||||
|
||||
#include "uapi/compel.h"
|
||||
#include <errno.h>
|
||||
|
||||
#include "handle-elf.h"
|
||||
#include "piegen.h"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
#include <string.h>
|
||||
|
||||
#include "uapi/compel.h"
|
||||
#include <errno.h>
|
||||
|
||||
#include "handle-elf.h"
|
||||
#include "piegen.h"
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
#include <string.h>
|
||||
|
||||
#include "uapi/compel.h"
|
||||
#include <errno.h>
|
||||
|
||||
#include "handle-elf.h"
|
||||
#include "piegen.h"
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#include <sys/auxv.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/user.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <compel/asm/fpu.h>
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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__ */
|
||||
|
|
@ -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];
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
#include <compel/compel.h>
|
||||
|
||||
static struct syscall_exec_desc sc_exec_table_64[] = {
|
||||
#include "sys-exec-tbl-64.c"
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
122
criu/files-reg.c
122
criu/files-reg.c
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
32
criu/files.c
32
criu/files.c
|
|
@ -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, ®file_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 = ®file_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;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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"),
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
33
criu/include/memfd.h
Normal 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__ */
|
||||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -61,6 +61,8 @@ enum {
|
|||
PB_AUTOFS,
|
||||
PB_GHOST_CHUNK,
|
||||
PB_FILE,
|
||||
PB_MEMFD_FILE,
|
||||
PB_MEMFD_INODE, /* 60 */
|
||||
|
||||
/* PB_AUTOGEN_STOP */
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
452
criu/memfd.c
Normal 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;
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
#ifndef __PIE_RELOCS_H__
|
||||
#define __PIE_RELOCS_H__
|
||||
|
||||
#include <compel/compel.h>
|
||||
|
||||
#include "common/config.h"
|
||||
#include "common/compiler.h"
|
||||
|
||||
|
|
|
|||
28
criu/pipes.c
28
criu/pipes.c
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
251
criu/seize.c
251
criu/seize.c
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
66
criu/shmem.c
66
criu/shmem.c
|
|
@ -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;
|
||||
|
|
|
|||
11
criu/uffd.c
11
criu/uffd.c
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@
|
|||
|
||||
#include "linux/mount.h"
|
||||
|
||||
#include "linux/mount.h"
|
||||
|
||||
#include "kerndat.h"
|
||||
#include "page.h"
|
||||
#include "util.h"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)/
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
22
images/memfd.proto
Normal 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;
|
||||
};
|
||||
|
|
@ -28,6 +28,8 @@ enum fstype {
|
|||
// RPC_PIPEFS = 20;
|
||||
// NFS = 21;
|
||||
// NFS4 = 22;
|
||||
|
||||
CGROUP2 = 23;
|
||||
};
|
||||
|
||||
message mnt_entry {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
|
|
|
|||
45
scripts/build/Dockerfile.aarch64-cross
Normal file
45
scripts/build/Dockerfile.aarch64-cross
Normal 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
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
45
scripts/build/Dockerfile.ppc64-cross
Normal file
45
scripts/build/Dockerfile.ppc64-cross
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
||||
#
|
||||
|
|
|
|||
|
|
@ -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
28
test/inhfd/memfd.py
Executable 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
7
test/inhfd/memfd.py.checkskip
Executable 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
1
test/inhfd/memfd.py.desc
Normal file
|
|
@ -0,0 +1 @@
|
|||
{ 'flavor': 'h' }
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
12
test/zdtm.py
12
test/zdtm.py
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
19
test/zdtm/lib/unix.c
Normal 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;
|
||||
}
|
||||
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
103
test/zdtm/static/memfd00.c
Normal 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
114
test/zdtm/static/memfd01.c
Normal 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;
|
||||
}
|
||||
87
test/zdtm/static/memfd02.c
Normal file
87
test/zdtm/static/memfd02.c
Normal 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;
|
||||
}
|
||||
97
test/zdtm/static/memfd03.c
Normal file
97
test/zdtm/static/memfd03.c
Normal 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;
|
||||
}
|
||||
95
test/zdtm/static/opath_file.c
Normal file
95
test/zdtm/static/opath_file.c
Normal 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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
84
test/zdtm/static/shmemfd-priv.c
Normal file
84
test/zdtm/static/shmemfd-priv.c
Normal 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;
|
||||
}
|
||||
1
test/zdtm/static/shmemfd-priv.desc
Normal file
1
test/zdtm/static/shmemfd-priv.desc
Normal file
|
|
@ -0,0 +1 @@
|
|||
{'flavor': 'h ns', 'flags': 'suid'}
|
||||
107
test/zdtm/static/shmemfd.c
Normal file
107
test/zdtm/static/shmemfd.c
Normal 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;
|
||||
}
|
||||
1
test/zdtm/static/shmemfd.desc
Normal file
1
test/zdtm/static/shmemfd.desc
Normal file
|
|
@ -0,0 +1 @@
|
|||
{'flavor': 'h ns', 'flags': 'suid'}
|
||||
|
|
@ -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
102
test/zdtm/static/symlink.c
Normal 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
Loading…
Add table
Add a link
Reference in a new issue