mirror of
https://github.com/checkpoint-restore/criu
synced 2025-09-02 15:25:21 +00:00
mnt: add --ext-mount-map auto option
When this option is specified, if an external (private) bind mount is not specified by --ext-mount-map KEY:VAL then it is attempted to be resolved automatically. v2: introduce find_best_external_match, which looks for the best match based on sharing/slave ids; don't try to resolve fsroot_mounted() mountpoints v3: get rid of really_collect_self_mounts v4: get rid of fsroot_mounted() check when autodetecting external mounts Signed-off-by: Tycho Andersen <tycho.andersen@canonical.com> Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
This commit is contained in:
committed by
Pavel Emelyanov
parent
e2c38245c6
commit
aebfabb5ad
@@ -430,6 +430,11 @@ int main(int argc, char *argv[], char *envp[])
|
|||||||
{
|
{
|
||||||
char *aux;
|
char *aux;
|
||||||
|
|
||||||
|
if (strcmp(optarg, "auto") == 0) {
|
||||||
|
opts.autodetect_ext_mounts = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
aux = strchr(optarg, ':');
|
aux = strchr(optarg, ':');
|
||||||
if (aux == NULL)
|
if (aux == NULL)
|
||||||
goto bad_arg;
|
goto bad_arg;
|
||||||
@@ -647,6 +652,8 @@ usage:
|
|||||||
" --force-irmap force resolving names for inotify/fsnotify watches\n"
|
" --force-irmap force resolving names for inotify/fsnotify watches\n"
|
||||||
" -M|--ext-mount-map KEY:VALUE\n"
|
" -M|--ext-mount-map KEY:VALUE\n"
|
||||||
" add external mount mapping\n"
|
" add external mount mapping\n"
|
||||||
|
" -M|--ext-mount-map auto\n"
|
||||||
|
" attempt to autodetect external mount mapings\n"
|
||||||
" --manage-cgroups dump or restore cgroups the process is in\n"
|
" --manage-cgroups dump or restore cgroups the process is in\n"
|
||||||
" --cgroup-root [controller:]/newroot\n"
|
" --cgroup-root [controller:]/newroot\n"
|
||||||
" change the root cgroup the controller will be\n"
|
" change the root cgroup the controller will be\n"
|
||||||
|
@@ -62,6 +62,7 @@ struct cr_options {
|
|||||||
bool manage_cgroups;
|
bool manage_cgroups;
|
||||||
char *new_global_cg_root;
|
char *new_global_cg_root;
|
||||||
struct list_head new_cgroup_roots;
|
struct list_head new_cgroup_roots;
|
||||||
|
bool autodetect_ext_mounts;
|
||||||
bool aufs; /* auto-deteced, not via cli */
|
bool aufs; /* auto-deteced, not via cli */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
192
mount.c
192
mount.c
@@ -30,6 +30,8 @@
|
|||||||
|
|
||||||
#include "protobuf/mnt.pb-c.h"
|
#include "protobuf/mnt.pb-c.h"
|
||||||
|
|
||||||
|
#define AUTODETECTED_MOUNT "CRIU:AUTOGENERATED"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Structure to keep external mount points resolving info.
|
* Structure to keep external mount points resolving info.
|
||||||
*
|
*
|
||||||
@@ -601,11 +603,15 @@ static int validate_mounts(struct mount_info *info, bool for_dump)
|
|||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (for_dump) {
|
if (for_dump) {
|
||||||
ret = run_plugins(DUMP_EXT_MOUNT, m->mountpoint, m->mnt_id);
|
// We've already resolved the mount
|
||||||
if (ret == 0)
|
// and it is external.
|
||||||
m->need_plugin = true;
|
if (m->external) {
|
||||||
else if (ret == -ENOTSUP)
|
ret = 0;
|
||||||
ret = try_resolve_ext_mount(m);
|
} else {
|
||||||
|
ret = run_plugins(DUMP_EXT_MOUNT, m->mountpoint, m->mnt_id);
|
||||||
|
if (ret == 0)
|
||||||
|
m->need_plugin = true;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (m->need_plugin || m->external)
|
if (m->need_plugin || m->external)
|
||||||
/*
|
/*
|
||||||
@@ -641,7 +647,122 @@ static int validate_mounts(struct mount_info *info, bool for_dump)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int collect_shared(struct mount_info *info)
|
static struct mount_info *find_best_external_match(struct mount_info *list, struct mount_info *info)
|
||||||
|
{
|
||||||
|
struct mount_info *it, *candidate = NULL;
|
||||||
|
|
||||||
|
for (it = list; it; it = it->next) {
|
||||||
|
if (!mounts_equal(info, it, true))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
candidate = it;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Consider the case of:
|
||||||
|
*
|
||||||
|
* mount /xxx
|
||||||
|
* mount --bind /xxx /yyy
|
||||||
|
* mount --make-shared /yyy
|
||||||
|
* mount --bind /xxx /zzz
|
||||||
|
* mount --make-shared /zzz
|
||||||
|
* bind mount a shared mount into the namespace
|
||||||
|
*
|
||||||
|
* Here, we want to return the /right/ mount, not just a mount
|
||||||
|
* that's equal. However, in the case:
|
||||||
|
*
|
||||||
|
* bind mount a shared mount into the namespace
|
||||||
|
* inside the namespace, remount MS_PRIVATE
|
||||||
|
* inside the namespace, remount MS_SHARED
|
||||||
|
*
|
||||||
|
* there will be no external mount with matching sharing
|
||||||
|
* because the sharing is only internal; we still want to bind
|
||||||
|
* mount from this mountinfo so we should return it, but we
|
||||||
|
* should make the sharing namespace private after that bind
|
||||||
|
* mount.
|
||||||
|
*
|
||||||
|
* Below are the cases where we found an exact match.
|
||||||
|
*/
|
||||||
|
if (info->flags & MS_SHARED && info->shared_id == it->shared_id)
|
||||||
|
return candidate;
|
||||||
|
|
||||||
|
if (info->flags & MS_SLAVE && info->master_id == it->shared_id)
|
||||||
|
return candidate;
|
||||||
|
}
|
||||||
|
|
||||||
|
return candidate;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int resolve_external_mounts(struct mount_info *info)
|
||||||
|
{
|
||||||
|
struct mount_info *m;
|
||||||
|
struct ns_id *ns = NULL, *iter;
|
||||||
|
|
||||||
|
for (iter = ns_ids; iter->next; iter = iter->next) {
|
||||||
|
if (iter->pid == getpid() && iter->nd == &mnt_ns_desc) {
|
||||||
|
ns = iter;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ns) {
|
||||||
|
pr_err("Failed to find criu pid's mount ns!");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (m = info; m; m = m->next) {
|
||||||
|
int ret, size;
|
||||||
|
char *p;
|
||||||
|
struct ext_mount *em;
|
||||||
|
struct mount_info *match;
|
||||||
|
|
||||||
|
if (m->parent == NULL || m->is_ns_root)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ret = try_resolve_ext_mount(m);
|
||||||
|
if (ret < 0 && ret != -ENOTSUP) {
|
||||||
|
return -1;
|
||||||
|
} else if (ret == -ENOTSUP && !opts.autodetect_ext_mounts) {
|
||||||
|
continue;
|
||||||
|
} else if (ret == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
match = find_best_external_match(ns->mnt.mntinfo_list, m);
|
||||||
|
if (!match)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
size = strlen(match->mountpoint + 1) + strlen(m->root) + 1;
|
||||||
|
p = xmalloc(sizeof(char) * size);
|
||||||
|
if (!p)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
ret = snprintf(p, size+1, "%s%s", match->mountpoint + 1, m->root);
|
||||||
|
if (ret < 0 || ret >= size) {
|
||||||
|
free(p);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
em = xmalloc(sizeof(struct ext_mount));
|
||||||
|
if (!em) {
|
||||||
|
free(p);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
em->val = AUTODETECTED_MOUNT;
|
||||||
|
em->key = p;
|
||||||
|
|
||||||
|
m->external = em;
|
||||||
|
|
||||||
|
xfree(m->source);
|
||||||
|
m->source = p;
|
||||||
|
|
||||||
|
pr_info("autodetected external mount %s for %s\n", p, m->mountpoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int collect_shared(struct mount_info *info, bool for_dump)
|
||||||
{
|
{
|
||||||
struct mount_info *m, *t;
|
struct mount_info *m, *t;
|
||||||
|
|
||||||
@@ -675,10 +796,12 @@ static int collect_shared(struct mount_info *info)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (need_master && m->parent) {
|
// If we haven't already determined this mount is external,
|
||||||
pr_err("Mount %d (master_id: %d shared_id: %d) "
|
// then we don't know where it came from.
|
||||||
"has unreachable sharing\n", m->mnt_id,
|
if (need_master && m->parent && !m->external) {
|
||||||
m->master_id, m->shared_id);
|
pr_err("Mount %d %s (master_id: %d shared_id: %d) "
|
||||||
|
"has unreachable sharing. Try --enable-external-masters.\n", m->mnt_id,
|
||||||
|
m->mountpoint, m->master_id, m->shared_id);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1722,6 +1845,9 @@ static bool can_mount_now(struct mount_info *mi)
|
|||||||
if (mi->is_ns_root)
|
if (mi->is_ns_root)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
if (mi->external)
|
||||||
|
return true;
|
||||||
|
|
||||||
if (mi->master_id && mi->bind == NULL)
|
if (mi->master_id && mi->bind == NULL)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@@ -2008,6 +2134,11 @@ static int collect_mnt_from_image(struct mount_info **pms, struct ns_id *nsid)
|
|||||||
pm->need_plugin = me->with_plugin;
|
pm->need_plugin = me->with_plugin;
|
||||||
pm->is_ns_root = is_root(me->mountpoint);
|
pm->is_ns_root = is_root(me->mountpoint);
|
||||||
|
|
||||||
|
pr_debug("\t\tGetting source for %d\n", pm->mnt_id);
|
||||||
|
pm->source = xstrdup(me->source);
|
||||||
|
if (!pm->source)
|
||||||
|
goto err;
|
||||||
|
|
||||||
/* FIXME: abort unsupported early */
|
/* FIXME: abort unsupported early */
|
||||||
pm->fstype = decode_fstype(me->fstype, me->fsname);
|
pm->fstype = decode_fstype(me->fstype, me->fsname);
|
||||||
|
|
||||||
@@ -2021,8 +2152,28 @@ static int collect_mnt_from_image(struct mount_info **pms, struct ns_id *nsid)
|
|||||||
|
|
||||||
em = ext_mount_lookup(me->root);
|
em = ext_mount_lookup(me->root);
|
||||||
if (!em) {
|
if (!em) {
|
||||||
pr_err("No mapping for %s mountpoint\n", me->mountpoint);
|
if (!opts.autodetect_ext_mounts) {
|
||||||
goto err;
|
pr_err("No mapping for %s mountpoint\n", me->mountpoint);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make up an external mount entry for this
|
||||||
|
* mount point, since we couldn't find a user
|
||||||
|
* supplied one.
|
||||||
|
*/
|
||||||
|
em = xmalloc(sizeof(struct ext_mount));
|
||||||
|
if (!em)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
em->val = pm->source;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Put a : in here since those are invalid on
|
||||||
|
* the cli, so we know it's autogenerated in
|
||||||
|
* debugging.
|
||||||
|
*/
|
||||||
|
em->key = AUTODETECTED_MOUNT;
|
||||||
}
|
}
|
||||||
|
|
||||||
pm->external = em;
|
pm->external = em;
|
||||||
@@ -2054,11 +2205,6 @@ static int collect_mnt_from_image(struct mount_info **pms, struct ns_id *nsid)
|
|||||||
|
|
||||||
pr_debug("\t\tGetting mpt for %d %s\n", pm->mnt_id, pm->mountpoint);
|
pr_debug("\t\tGetting mpt for %d %s\n", pm->mnt_id, pm->mountpoint);
|
||||||
|
|
||||||
pr_debug("\t\tGetting source for %d\n", pm->mnt_id);
|
|
||||||
pm->source = xstrdup(me->source);
|
|
||||||
if (!pm->source)
|
|
||||||
goto err;
|
|
||||||
|
|
||||||
pr_debug("\t\tGetting opts for %d\n", pm->mnt_id);
|
pr_debug("\t\tGetting opts for %d\n", pm->mnt_id);
|
||||||
pm->options = xstrdup(me->options);
|
pm->options = xstrdup(me->options);
|
||||||
if (!pm->options)
|
if (!pm->options)
|
||||||
@@ -2236,7 +2382,7 @@ static int populate_mnt_ns(struct mount_info *mis)
|
|||||||
if (!pms)
|
if (!pms)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (collect_shared(mis))
|
if (collect_shared(mis, false))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
for (nsid = ns_ids; nsid; nsid = nsid->next) {
|
for (nsid = ns_ids; nsid; nsid = nsid->next) {
|
||||||
@@ -2488,7 +2634,8 @@ static int collect_mntns(struct ns_id *ns, void *__arg)
|
|||||||
if (arg->for_dump && ns->pid != getpid())
|
if (arg->for_dump && ns->pid != getpid())
|
||||||
arg->need_to_validate = true;
|
arg->need_to_validate = true;
|
||||||
|
|
||||||
mntinfo_add_list(pms);
|
if (ns->pid != getpid() || !(root_ns_mask & CLONE_NEWNS))
|
||||||
|
mntinfo_add_list(pms);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2500,14 +2647,17 @@ int collect_mnt_namespaces(bool for_dump)
|
|||||||
arg.for_dump = for_dump;
|
arg.for_dump = for_dump;
|
||||||
arg.need_to_validate = false;
|
arg.need_to_validate = false;
|
||||||
|
|
||||||
ret = walk_namespaces(&mnt_ns_desc, false, collect_mntns, &arg);
|
ret = walk_namespaces(&mnt_ns_desc, opts.autodetect_ext_mounts, collect_mntns, &arg);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
|
if (resolve_external_mounts(mntinfo))
|
||||||
|
goto err;
|
||||||
|
|
||||||
if (arg.need_to_validate) {
|
if (arg.need_to_validate) {
|
||||||
ret = -1;
|
ret = -1;
|
||||||
|
|
||||||
if (collect_shared(mntinfo))
|
if (collect_shared(mntinfo, true))
|
||||||
goto err;
|
goto err;
|
||||||
if (validate_mounts(mntinfo, true))
|
if (validate_mounts(mntinfo, true))
|
||||||
goto err;
|
goto err;
|
||||||
|
Reference in New Issue
Block a user