[4/9] unix: Collect bindmounted unix sockets

Submitted by Cyrill Gorcunov on June 9, 2018, 1:26 p.m.

Details

Message ID 20180609132611.31726-5-gorcunov@gmail.com
State New
Series "unix: Add support for bindmounted dgram sockets"
Headers show

Commit Message

Cyrill Gorcunov June 9, 2018, 1:26 p.m.
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@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(-)

Patch hide | download patch | download mbox

diff --git a/criu/cr-dump.c b/criu/cr-dump.c
index 50b622a65c0c..2103de480ba9 100644
--- a/criu/cr-dump.c
+++ b/criu/cr-dump.c
@@ -1930,6 +1930,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 f2085ace70b2..d278f52b628c 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 463ae71ceb43..1714154b4213 100644
--- a/criu/sk-unix.c
+++ b/criu/sk-unix.c
@@ -73,6 +73,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;
@@ -380,6 +383,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.
@@ -567,11 +573,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)
@@ -692,6 +703,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]);
@@ -887,6 +899,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;
+			}
+			return -1;
+		}
+
+		if (stat(mi->mountpoint, &st)) {
+			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 &&
+				    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);
+					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)) {
+			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 46b0ba61464a..025a5503d22c 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 {