[CRIU] [PATCH 2/3] mount: Forced mount unmounted binfmt_misc to do not lost its content
Andrew Vagin
avagin at virtuozzo.com
Wed Jun 22 21:59:12 PDT 2016
On Wed, Jun 22, 2016 at 05:19:15PM +0300, Kirill Tkhai wrote:
> Umount does not remove binfmt_misc content. If it's mounted once again,
> the same entries remain registered.
>
> Criu does not dump content of umounted binfmt_misc. So, after C/R we
> lose it at all.
>
> This patch forces mounting of unmounted binfmt_misc before we collect
> mountpoints. If it's unmounted, we mount it back and add this mount
> to the list of forced mounted mountpoints. Next patch need this
> list to mark the mount in special way in dump image.
>
> Signed-off-by: Kirill Tkhai <ktkhai at virtuozzo.com>
> ---
> criu/cr-dump.c | 13 +++++
> criu/include/mount.h | 1
> criu/mount.c | 129 ++++++++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 143 insertions(+)
>
> diff --git a/criu/cr-dump.c b/criu/cr-dump.c
> index 00d28e9..dbda4b7 100644
> --- a/criu/cr-dump.c
> +++ b/criu/cr-dump.c
> @@ -747,6 +747,16 @@ static int dump_task_core_all(struct parasite_ctl *ctl,
> return ret;
> }
>
> +static int prepare_dump__tasks_freezed(void)
> +{
> + int ret;
> +
> + /* Tasks freezed, so we do not race with systemd's autofs unmounter */
> + ret = try_mount_binfmt_misc(root_item->pid.real);
Can we do this only when binfmt_misc is virtuallized?
> +
> + return ret;
> +}
> +
> static int collect_pstree_ids_predump(void)
> {
> struct pstree_item *item;
> @@ -1681,6 +1691,9 @@ int cr_dump_tasks(pid_t pid)
> if (collect_pstree())
> goto err;
>
> + if (prepare_dump__tasks_freezed())
> + goto err;
> +
> if (collect_pstree_ids())
> goto err;
>
> diff --git a/criu/include/mount.h b/criu/include/mount.h
> index c7992ac..191f6a3 100644
> --- a/criu/include/mount.h
> +++ b/criu/include/mount.h
> @@ -128,4 +128,5 @@ extern int mntns_maybe_create_roots(void);
> extern int read_mnt_ns_img(void);
> extern void cleanup_mnt_ns(void);
>
> +extern int try_mount_binfmt_misc(pid_t pid);
> #endif /* __CR_MOUNT_H__ */
> diff --git a/criu/mount.c b/criu/mount.c
> index e891c92..f91654c 100644
> --- a/criu/mount.c
> +++ b/criu/mount.c
> @@ -41,6 +41,13 @@
> #define LOG_PREFIX "mnt: "
>
> static struct fstype fstypes[];
> +static LIST_HEAD(forced_mounts_list);
> +
> +struct forced_mount {
> + struct list_head list;
> + unsigned int ns_id;
> + unsigned int mnt_id;
> +};
>
> int ext_mount_add(char *key, char *val)
> {
> @@ -3685,4 +3692,126 @@ int dump_mnt_namespaces(void)
> return 0;
> }
>
> +static int add_forced_mount(pid_t pid, const char *path)
> +{
> + unsigned int ns_id, mnt_id = 0;
> + struct forced_mount *fm;
> + int i, len, ret = 0;
> + char *str, *p;
> + struct bfd f;
> +
> + if (read_ns_id(pid, &mnt_ns_desc, &ns_id) < 0 || !ns_id) {
> + pr_err("Can't read mnt_ns id\n");
> + return -1;
> + }
> + f.fd = open_proc(pid, "mountinfo");
> + if (f.fd < 0) {
> + pr_perror("Can't open mountinfo to parse");
> + return -1;
> + }
> + if (bfdopenr(&f))
> + return -1;
> + len = strlen(path);
> +
> + while (1) {
> + p = str = breadline(&f);
> + if (!p)
> + break;
> + i = 0;
> + while (i < 4) {
> + p = strchr(p, ' ');
> + if (!str)
> + break;
> + i++;
> + p++;
> + }
> +
> + if (i != 4) {
> + pr_err("Can't parse mountinfo\n");
> + ret = -1;
> + break;
> + }
> +
> + if (strncmp(p, path, len))
> + continue;
> + if (sscanf(str, "%u", &mnt_id) != 1) {
> + pr_err("Can't parse mountinfo\n");
> + ret = -1;
> + }
> + /* Do not break as we're interested in the last entry */
> + }
If you want to get mnt_id, you can open path and then parse
/proc/pid/fdinfo/X. I think we already have a helper to get mnt_id for a
file descriptor.
> +
> + bclose(&f);
> +
> + if (ret || !mnt_id) {
> + if (!ret)
> + pr_err("Can't find %s mounted\n", path);
> + return -1;
> + }
> +
> + fm = xmalloc(sizeof(*fm));
> + if (!fm)
> + return -1;
> + fm->ns_id = ns_id;
> + fm->mnt_id = mnt_id;
> + list_add(&fm->list, &forced_mounts_list);
> +
> + return 0;
> +}
> +
> +#define BINFMT_MISC_HOME "/proc/sys/fs/binfmt_misc"
> +
> +int try_mount_binfmt_misc(pid_t pid)
> +{
> + int num, mnt_fd, ret, exit_code = -1;
> + struct dirent *de;
> + DIR *dir;
> +
> + ret = switch_ns(pid, &mnt_ns_desc, &mnt_fd);
> + if (ret < 0) {
> + pr_err("Can't switch mnt_ns\n");
> + return -1;
> + }
> +
> + ret = mount("binfmt_misc", BINFMT_MISC_HOME, "binfmt_misc", 0, NULL);
Are you sure that this will work if the BINFMT_MISC_HOME path doesn't exist?
> + if (ret < 0) {
> + if (errno == EPERM) {
> + pr_info("Can't mount binfmt_misc: EPERM. Running in user_ns?\n");
> + exit_code = 0;
> + goto restore_ns;
> + }
> + if (errno != EBUSY && errno != ENODEV && errno != ENOENT) {
> + pr_perror("Can't mount binfmt_misc");
> + goto restore_ns;
> + }
> + pr_info("Prepare binfmt_misc: skipping(%d)\n", errno);
> + } else {
> + dir = opendir(BINFMT_MISC_HOME);
> + if (!dir) {
> + pr_perror("Can't read binfmt_misc dir");
> + goto restore_ns;
> + }
> +
> + num = 0;
> + /* ".", "..", "register", "status" */
> + while (num <= 4 && (de = readdir(dir)) != NULL)
> + num++;
> + if (num <= 4) {
> + /* No entries */
> + umount(BINFMT_MISC_HOME);
Pls call pr_perror here in a case of any error
> + } else {
> + ret = add_forced_mount(pid, BINFMT_MISC_HOME);
> + if (ret)
> + goto restore_ns;
> + }
> + closedir(dir);
> + }
> +
> + exit_code = 0;
> +restore_ns:
> + ret = restore_ns(mnt_fd, &mnt_ns_desc);
> +
> + return ret ? -1 : exit_code;
> +}
> +
> struct ns_desc mnt_ns_desc = NS_DESC_ENTRY(CLONE_NEWNS, "mnt");
>
> _______________________________________________
> CRIU mailing list
> CRIU at openvz.org
> https://lists.openvz.org/mailman/listinfo/criu
More information about the CRIU
mailing list