[CRIU] [PATCH 06/19] unix: Collect bindmounted unix sockets
Andrey Vagin
avagin at virtuozzo.com
Wed Sep 19 02:59:02 MSK 2018
On Fri, Sep 14, 2018 at 05:08:29PM +0300, Cyrill Gorcunov wrote:
> Mount points might be beindmount to some resources (say unix binded
> sockets) thus when times come to do real bind mount call we need
> to prepare appropriate resource first.
>
> On dump procedure we walk over all bind-mounts and check if
> the mountpoint is a unix socket saving the mnt_id into
> the image then. To distinguish such sockets from others
> we use UNIX_UFLAGS__BINDMOUNT flag.
>
> Note at moment we support only DGRAM closed sockets.
>
> Signed-off-by: Cyrill Gorcunov <gorcunov at gmail.com>
> ---
> criu/cr-dump.c | 3 ++
> criu/include/sockets.h | 1 +
> criu/sk-unix.c | 91 +++++++++++++++++++++++++++++++++++++++---
> images/sk-unix.proto | 1 +
> 4 files changed, 91 insertions(+), 5 deletions(-)
>
> diff --git a/criu/cr-dump.c b/criu/cr-dump.c
> index cbc72f01614d..79922e377279 100644
> --- a/criu/cr-dump.c
> +++ b/criu/cr-dump.c
> @@ -1839,6 +1839,9 @@ int cr_dump_tasks(pid_t pid)
> if (collect_namespaces(true) < 0)
> goto err;
>
> + if (collect_unix_bindmounts() < 0)
> + goto err;
> +
> glob_imgset = cr_glob_imgset_open(O_DUMP);
> if (!glob_imgset)
> goto err;
> diff --git a/criu/include/sockets.h b/criu/include/sockets.h
> index 371d1a5095b7..d56e25d07c12 100644
> --- a/criu/include/sockets.h
> +++ b/criu/include/sockets.h
> @@ -41,6 +41,7 @@ extern int add_fake_unix_queuers(void);
> extern int fix_external_unix_sockets(void);
> extern int prepare_scms(void);
> extern int unix_note_scm_rights(int id_for, uint32_t *file_ids, int *fds, int n_ids);
> +extern int collect_unix_bindmounts(void);
>
> extern struct collect_image_info netlink_sk_cinfo;
>
> diff --git a/criu/sk-unix.c b/criu/sk-unix.c
> index 78040a5c10fe..87f652689331 100644
> --- a/criu/sk-unix.c
> +++ b/criu/sk-unix.c
> @@ -74,6 +74,9 @@ struct unix_sk_desc {
> unsigned char shutdown;
> bool deleted;
>
> + bool bindmount;
> + unsigned int mnt_id;
> +
> mode_t mode;
> uid_t uid;
> gid_t gid;
> @@ -391,6 +394,9 @@ static int dump_one_unix_fd(int lfd, uint32_t id, const struct fd_parms *p)
> if (unix_resolve_name(lfd, id, sk, ue, p))
> goto err;
>
> + if (sk->bindmount)
> + ue->uflags |= UNIX_UFLAGS__BINDMOUNT;
> +
> /*
> * Check if this socket is connected to criu service.
> * Dump it like closed one and mark it for restore.
> @@ -579,11 +585,16 @@ static int unix_resolve_name(int lfd, uint32_t id, struct unix_sk_desc *d,
> if (d->namelen == 0 || name[0] == '\0')
> return 0;
>
> - if (kdat.sk_unix_file && (root_ns_mask & CLONE_NEWNS)) {
> - if (get_mnt_id(lfd, &mnt_id))
> - return -1;
> - ue->mnt_id = mnt_id;
> - ue->has_mnt_id = mnt_id;
> + if (!d->bindmount) {
> + if (kdat.sk_unix_file && (root_ns_mask & CLONE_NEWNS)) {
> + if (get_mnt_id(lfd, &mnt_id))
> + return -1;
> + ue->mnt_id = mnt_id;
> + ue->has_mnt_id = mnt_id;
> + }
> + } else {
> + ue->mnt_id = d->mnt_id;
> + ue->has_mnt_id = true;
> }
>
> if (ue->mnt_id >= 0)
> @@ -715,6 +726,7 @@ static int unix_collect_one(const struct unix_diag_msg *m,
> INIT_LIST_HEAD(&d->peer_list);
> INIT_LIST_HEAD(&d->peer_node);
> d->fd = -1;
> + d->mnt_id = -1;
>
> if (tb[UNIX_DIAG_SHUTDOWN])
> d->shutdown = nla_get_u8(tb[UNIX_DIAG_SHUTDOWN]);
> @@ -910,6 +922,75 @@ int fix_external_unix_sockets(void)
> return -1;
> }
>
> +int collect_unix_bindmounts(void)
> +{
> + struct mount_info *mi;
> + struct stat st = {};
> + int ns_old = -1;
> + int ret = 0;
> +
> + pr_debug("Collecting unix bindmounts\n");
> +
> + for (mi = mntinfo; mi; mi = mi->next) {
> + if (list_empty(&mi->mnt_bind))
> + continue;
> +
> + if (switch_ns(mi->nsid->ns_pid, &mnt_ns_desc, &ns_old) < 0) {
> + pr_err("Can't switch ns to mnt_id %d", mi->mnt_id);
> + if (restore_ns(ns_old, &mnt_ns_desc)) {
> + pr_err("Can't switch mount ns back from mnt_id %d\n", mi->mnt_id);
> + return -1;
> + }
I think we can move this restore_ns to the end under the err label.
> + return -1;
> + }
> +
> + if (stat(mi->mountpoint, &st)) {
can we use fstatat(mi->nsid->mnt.root, mi->mountpoint) here?
> + pr_warn("Can't stat on %s: %m\n", mi->mountpoint);
> + if (restore_ns(ns_old, &mnt_ns_desc)) {
> + pr_err("Can't switch mount ns back from mnt_id %d\n", mi->mnt_id);
> + return -1;
> + }
> + continue;
> + }
> +
> + if (S_ISSOCK(st.st_mode)) {
> + struct unix_sk_desc *sk;
> +
> + list_for_each_entry(sk, &unix_sockets, list) {
if (sk->vfs_ino != (int)st.st_ino)
continue;
...
> + if (sk->vfs_ino == (int)st.st_ino &&
> + sk->vfs_dev == (int)st.st_dev) {
> + pr_debug("Found sock s_dev %#x ino %d bindmounted mnt_id %d %s\n",
> + (int)st.st_dev, (int)st.st_ino, mi->mnt_id, mi->mountpoint);
too many indents
> + if (sk->bindmount) {
> + pr_err("Many bindings for sockets are not yet supported %d at %s\n",
> + (int)st.st_ino, mi->mountpoint);
> + ret = -1;
> + } else {
> + sk->mnt_id = mi->mnt_id;
> + sk->bindmount = true;
> + }
> + if (sk->type != SOCK_DGRAM && sk->state != TCP_CLOSE) {
> + pr_err("Unsupported bindmounted socket ino %d at %s\n",
> + (int)st.st_ino, mi->mountpoint);
> + ret = -1;
> + }
> + break;
> + }
> + }
> + }
> +
> + if (restore_ns(ns_old, &mnt_ns_desc)) {
restore_ns() should be called only once before exiting from this func
> + pr_err("Can't switch mount ns back from %d\n", mi->nsid->ns_pid);
> + return -1;
> + }
> +
> + if (ret)
> + break;
> + }
> +
> + return ret;
> +}
> +
> struct unix_sk_info {
> UnixSkEntry *ue;
> struct list_head list;
> diff --git a/images/sk-unix.proto b/images/sk-unix.proto
> index b7173eac9119..feb51328390f 100644
> --- a/images/sk-unix.proto
> +++ b/images/sk-unix.proto
> @@ -18,6 +18,7 @@ enum unix_uflags {
> SERVICE = 2;
> CALLBACK = 4;
> INHERIT = 8;
> + BINDMOUNT = 16;
> }
>
> message unix_sk_entry {
> --
> 2.17.1
>
More information about the CRIU
mailing list