diff --git a/criu/Makefile.crtools b/criu/Makefile.crtools index 46d7fd424..22108cce0 100644 --- a/criu/Makefile.crtools +++ b/criu/Makefile.crtools @@ -41,6 +41,7 @@ obj-y += lsm.o obj-y += mem.o obj-y += memfd.o obj-y += mount.o +obj-y += mount-v2.o obj-y += filesystems.o obj-y += namespaces.o obj-y += netfilter.o diff --git a/criu/config.c b/criu/config.c index 4a8338423..8b6d105ae 100644 --- a/criu/config.c +++ b/criu/config.c @@ -20,6 +20,7 @@ #include "file-lock.h" #include "irmap.h" #include "mount.h" +#include "mount-v2.h" #include "namespaces.h" #include "net.h" #include "sk-inet.h" @@ -697,6 +698,7 @@ int parse_options(int argc, char **argv, bool *usage_error, bool *has_exec_cmd, { "file-validation", required_argument, 0, 1098 }, { "lsm-mount-context", required_argument, 0, 1099 }, { "network-lock", required_argument, 0, 1100 }, + BOOL_OPT("mntns-compat-mode", &opts.mntns_compat_mode), {}, }; @@ -1103,6 +1105,16 @@ int check_options(void) } #endif + if (opts.mntns_compat_mode && opts.mode != CR_RESTORE) { + pr_err("Option --mntns-compat-mode is only valid on restore\n"); + return 1; + } else if (!opts.mntns_compat_mode && opts.mode == CR_RESTORE) { + if (check_mount_v2()) { + pr_warn("Mount engine fallback to --mntns-compat-mode mode\n"); + opts.mntns_compat_mode = true; + } + } + if (check_namespace_opts()) { pr_err("Error: namespace flags conflict\n"); return 1; diff --git a/criu/cr-service.c b/criu/cr-service.c index 59f46b320..a6eb9ebd3 100644 --- a/criu/cr-service.c +++ b/criu/cr-service.c @@ -719,6 +719,9 @@ static int setup_opts_from_req(int sk, CriuOpts *req) goto err; } + if (req->mntns_compat_mode) + opts.mntns_compat_mode = true; + log_set_loglevel(opts.log_level); if (check_options()) goto err; diff --git a/criu/crtools.c b/criu/crtools.c index 86aeb4d2b..cc8d9179f 100644 --- a/criu/crtools.c +++ b/criu/crtools.c @@ -408,6 +408,9 @@ usage: " in lazy-pages mode: 'criu lazy-pages -D DIR'\n" " --lazy-pages and lazy-pages mode require userfaultfd\n" " --stream dump/restore images using criu-image-streamer\n" + " --mntns-compat-mode Use mount engine in compatibility mode. By default criu\n" + " tries to use mount-v2 mode with more reliable algorithm\n" + " based on MOVE_MOUNT_SET_GROUP kernel feature\n" " --network-lock METHOD\n" " network locking/unlocking method; argument\n" " can be 'nftables' or 'iptables' (default).\n" diff --git a/criu/include/cr_options.h b/criu/include/cr_options.h index 85648bf1c..bf1a762cc 100644 --- a/criu/include/cr_options.h +++ b/criu/include/cr_options.h @@ -207,6 +207,8 @@ struct cr_options { /* Shows the mode criu is running at the moment: dump/pre-dump/restore/... */ enum criu_mode mode; + + int mntns_compat_mode; }; extern struct cr_options opts; diff --git a/criu/include/mount-v2.h b/criu/include/mount-v2.h index a2e2fa1c8..61f62966d 100644 --- a/criu/include/mount-v2.h +++ b/criu/include/mount-v2.h @@ -55,4 +55,6 @@ static inline long sys_openat2(int dirfd, const char *pathname, struct open_how return syscall(__NR_openat2, dirfd, pathname, how, size); } +extern int check_mount_v2(void); + #endif /* __CR_MOUNT_V2_H__ */ diff --git a/criu/mount-v2.c b/criu/mount-v2.c new file mode 100644 index 000000000..801ade748 --- /dev/null +++ b/criu/mount-v2.c @@ -0,0 +1,20 @@ +#include "kerndat.h" +#include "log.h" + +#undef LOG_PREFIX +#define LOG_PREFIX "mnt-v2: " + +int check_mount_v2(void) +{ + if (!kdat.has_move_mount_set_group) { + pr_warn("Mounts-v2 requires MOVE_MOUNT_SET_GROUP support\n"); + return -1; + } + + if (!kdat.has_openat2) { + pr_warn("Mounts-v2 requires openat2 support\n"); + return -1; + } + + return 0; +} diff --git a/criu/unittest/mock.c b/criu/unittest/mock.c index 127cc7498..dc5b627f7 100644 --- a/criu/unittest/mock.c +++ b/criu/unittest/mock.c @@ -136,3 +136,8 @@ struct kerndat_s kdat = {}; int service_fd_rlim_cur; unsigned __page_size; + +int check_mount_v2(void) +{ + return 0; +} diff --git a/images/rpc.proto b/images/rpc.proto index 1d3befd23..a6cc5da48 100644 --- a/images/rpc.proto +++ b/images/rpc.proto @@ -137,6 +137,7 @@ message criu_opts { optional int32 pidfd_store_sk = 62; optional string lsm_mount_context = 63; optional criu_network_lock_method network_lock = 64 [default = IPTABLES]; + optional bool mntns_compat_mode = 65; /* optional bool check_mounts = 128; */ } diff --git a/lib/c/criu.c b/lib/c/criu.c index dea5896f7..7807d7bc5 100644 --- a/lib/c/criu.c +++ b/lib/c/criu.c @@ -1170,6 +1170,17 @@ int criu_set_page_server_address_port(const char *address, int port) return criu_local_set_page_server_address_port(global_opts, address, port); } +void criu_local_set_mntns_compat_mode(criu_opts *opts, bool val) +{ + opts->rpc->has_mntns_compat_mode = true; + opts->rpc->mntns_compat_mode = val; +} + +void criu_set_mntns_compat_mode(bool val) +{ + criu_local_set_mntns_compat_mode(global_opts, val); +} + static CriuResp *recv_resp(int socket_fd) { struct msghdr msg_hdr = { 0 }; diff --git a/lib/c/criu.h b/lib/c/criu.h index aed2c3481..40e3106fc 100644 --- a/lib/c/criu.h +++ b/lib/c/criu.h @@ -112,6 +112,7 @@ int criu_set_pre_dump_mode(enum criu_pre_dump_mode mode); void criu_set_pidfd_store_sk(int sk); int criu_set_network_lock(enum criu_network_lock_method method); int criu_join_ns_add(const char *ns, const char *ns_file, const char *extra_opt); +void criu_set_mntns_compat_mode(bool val); /* * The criu_notify_arg_t na argument is an opaque @@ -275,6 +276,7 @@ int criu_local_set_pre_dump_mode(criu_opts *opts, enum criu_pre_dump_mode mode); void criu_local_set_pidfd_store_sk(criu_opts *opts, int sk); int criu_local_set_network_lock(criu_opts *opts, enum criu_network_lock_method method); int criu_local_join_ns_add(criu_opts *opts, const char *ns, const char *ns_file, const char *extra_opt); +void criu_local_set_mntns_compat_mode(criu_opts *opts, bool val); void criu_local_set_notify_cb(criu_opts *opts, int (*cb)(char *action, criu_notify_arg_t na));