[v2,06/19] unix: Collect bindmounted unix sockets

Submitted by Cyrill Gorcunov on Sept. 19, 2018, 9:25 a.m.

Details

Message ID 20180919092535.GE17524@uranus.lan
State New
Series "unix: Add support for bindmounted dgram sockets"
Headers show

Commit Message

Cyrill Gorcunov Sept. 19, 2018, 9:25 a.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.

v2:
 - Simplify mount ns handling
 - Shift code left

Signed-off-by: Cyrill Gorcunov <gorcunov@gmail.com>
---
 criu/cr-dump.c         |  3 ++
 criu/include/sockets.h |  1 +
 criu/sk-unix.c         | 86 +++++++++++++++++++++++++++++++++++++++---
 images/sk-unix.proto   |  1 +
 4 files changed, 86 insertions(+), 5 deletions(-)

Patch hide | download patch | download mbox

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..fa67ed745b13 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,70 @@  int fix_external_unix_sockets(void)
 	return -1;
 }
 
+int collect_unix_bindmounts(void)
+{
+	struct mount_info *mi;
+	int ns_old, ret = -1;
+	struct stat st = {};
+
+	pr_debug("Collecting unix bindmounts\n");
+
+	ns_old = open_proc(PROC_SELF, "ns/%s", mnt_ns_desc.str);
+	if (ns_old < 0) {
+		pr_err("Can't save former mount ns\n");
+		return -1;
+	}
+
+	for (mi = mntinfo; mi; mi = mi->next) {
+		struct unix_sk_desc *sk;
+
+		if (list_empty(&mi->mnt_bind))
+			continue;
+
+		if (switch_ns(mi->nsid->ns_pid, &mnt_ns_desc, NULL) < 0) {
+			pr_err("Can't switch ns to mnt_id %d", mi->mnt_id);
+			goto out;
+		}
+
+		if (stat(mi->mountpoint, &st)) {
+			pr_warn("Can't stat on %s: %m\n", mi->mountpoint);
+			continue;
+		}
+
+		if (!S_ISSOCK(st.st_mode))
+			continue;
+
+		list_for_each_entry(sk, &unix_sockets, list) {
+			if (sk->vfs_ino != (int)st.st_ino ||
+			    sk->vfs_dev != (int)st.st_dev)
+				continue;
+			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);
+				goto out;
+			} 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);
+				goto out;
+			}
+		}
+	}
+	ret = 0;
+out:
+	if (restore_ns(ns_old, &mnt_ns_desc)) {
+		pr_err("Can't switch mount ns to original\n");
+		ret = -1;
+	}
+
+	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 {