[3/4] sk-unix: Handle bindmounted dgram sockets on restore

Submitted by Cyrill Gorcunov on Aug. 8, 2017, 11:25 a.m.

Details

Message ID 1502191510-15586-4-git-send-email-gorcunov@openvz.org
State New
Series "sk-unix: Handle bindmounted dgram sockets"
Headers show

Commit Message

Cyrill Gorcunov Aug. 8, 2017, 11:25 a.m.
If a socket is bindmounted one we open it early (thus mount engine
will be eable to proceed), put it into fdstore and then simply fetch
it from there.

Note it's important that only basic scenario with dgram/closed sockets
is supported by now: established, listening sockets requires a way
more code change and we will do it only when really needed.

Signed-off-by: Cyrill Gorcunov <gorcunov@openvz.org>
---
 criu/include/sockets.h |  2 ++
 criu/mount.c           |  7 +++++++
 criu/sk-unix.c         | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 62 insertions(+)

Patch hide | download patch | download mbox

diff --git a/criu/include/sockets.h b/criu/include/sockets.h
index 64673c5e5dc8..3d4669859182 100644
--- a/criu/include/sockets.h
+++ b/criu/include/sockets.h
@@ -8,6 +8,7 @@ 
 
 struct fdinfo_list_entry;
 struct sk_opts_entry;
+struct mount_info;
 struct file_desc;
 struct fd_parms;
 struct cr_imgset;
@@ -38,6 +39,7 @@  extern struct collect_image_info inet_sk_cinfo;
 extern struct collect_image_info unix_sk_cinfo;
 extern int fix_external_unix_sockets(void);
 extern int collect_unix_bindmounts(void);
+extern int unix_prepare_bindmount(struct mount_info *mi);
 
 extern struct collect_image_info netlink_sk_cinfo;
 
diff --git a/criu/mount.c b/criu/mount.c
index 59fd17870db8..174cc4639209 100644
--- a/criu/mount.c
+++ b/criu/mount.c
@@ -27,6 +27,7 @@ 
 #include "files-reg.h"
 #include "external.h"
 #include "fdstore.h"
+#include "sockets.h"
 
 #include "images/mnt.pb-c.h"
 
@@ -2042,6 +2043,12 @@  static int do_bind_mount(struct mount_info *mi)
 		}
 	}
 
+	if (unix_prepare_bindmount(mi)) {
+		pr_err("Failed to prepare bindmount on unix at %s\n",
+		       mi->mountpoint);
+		goto err;
+	}
+
 	if (mount(root, mi->mountpoint, NULL, MS_BIND | (mi->flags & MS_REC), NULL) < 0) {
 		pr_perror("Can't mount at %s", mi->mountpoint);
 		goto err;
diff --git a/criu/sk-unix.c b/criu/sk-unix.c
index bf013c5ca4de..adc6d58aa173 100644
--- a/criu/sk-unix.c
+++ b/criu/sk-unix.c
@@ -867,6 +867,7 @@  struct unix_sk_info {
 	struct file_desc d;
 	struct list_head connected; /* List of sockets, connected to me */
 	struct list_head node; /* To link in peer's connected list  */
+	int fdstore_id;
 
 	/*
 	 * For DGRAM sockets with queues, we should only restore the queue
@@ -1241,6 +1242,12 @@  static int open_unixsk_standalone(struct unix_sk_info *ui, int *new_fd)
 	pr_info("Opening standalone socket (id %#x ino %#x peer %#x)\n",
 			ui->ue->id, ui->ue->ino, ui->ue->peer);
 
+	if (ui->fdstore_id >= 0) {
+		pr_debug("\tObtaining from fdstore id %#x\n", ui->fdstore_id);
+		*new_fd = fdstore_get(ui->fdstore_id);
+		return 0;
+	}
+
 	if (set_netns(ui->ue->ns_id))
 		return -1;
 
@@ -1453,6 +1460,9 @@  static void unlink_stale(struct unix_sk_info *ui)
 {
 	int ret, cwd_fd = -1, root_fd = -1;
 
+	if (ui->fdstore_id >= 0)
+		return;
+
 	if (ui->name[0] == '\0' || (ui->ue->uflags & USK_EXTERN))
 		return;
 
@@ -1509,6 +1519,7 @@  static int collect_one_unixsk(void *o, ProtobufCMessage *base, struct cr_img *i)
 	INIT_LIST_HEAD(&ui->connected);
 	INIT_LIST_HEAD(&ui->node);
 	ui->flags = 0;
+	ui->fdstore_id = -1;
 	fixup_sock_net_ns_id(&ui->ue->ns_id, &ui->ue->has_ns_id);
 
 	uname = ui->name;
@@ -1553,6 +1564,48 @@  struct collect_image_info unix_sk_cinfo = {
 	.flags = COLLECT_SHARED,
 };
 
+int unix_prepare_bindmount(struct mount_info *mi)
+{
+	char name_dir[PATH_MAX], ns_root[PATH_MAX];
+	struct unix_sk_info *ui, *t = NULL;
+	char *old_name_dir = NULL;
+	int ret, sk;
+
+	list_for_each_entry(ui, &unix_sockets, list) {
+		if (!ui->ue->has_mnt_id || ui->ue->mnt_id != mi->mnt_id)
+			continue;
+		t = ui;
+		break;
+	}
+
+	if (!t)
+		return 0;
+
+	print_ns_root(mi->nsid, 0, ns_root, sizeof(ns_root));
+	if (ui->name_dir) {
+		old_name_dir = ui->name_dir;
+		snprintf(name_dir, sizeof(name_dir), "%s/%s", ns_root, ui->name_dir);
+		ui->name_dir = name_dir;
+	}
+
+	ret = open_unixsk_standalone(ui, &sk);
+	if (ui->name_dir)
+		ui->name_dir = old_name_dir;
+	BUG_ON(ret > 0);
+
+	if (ret)
+		return -1;
+
+	ui->fdstore_id = fdstore_add(sk);
+	if (ui->fdstore_id < 0)
+		return -1;
+	close(sk);
+
+	pr_debug("Standalone socket moved into fdstore (id %#x ino %#x peer %#x)\n",
+		 ui->ue->id, ui->ue->ino, ui->ue->peer);
+	return 0;
+}
+
 static void set_peer(struct unix_sk_info *ui, struct unix_sk_info *peer)
 {
 	ui->peer = peer;

Comments

Andrei Vagin Aug. 9, 2017, 11:42 p.m.
On Tue, Aug 08, 2017 at 02:25:09PM +0300, Cyrill Gorcunov wrote:
> If a socket is bindmounted one we open it early (thus mount engine
> will be eable to proceed), put it into fdstore and then simply fetch
> it from there.
> 
> Note it's important that only basic scenario with dgram/closed sockets
> is supported by now: established, listening sockets requires a way
> more code change and we will do it only when really needed.
> 
> Signed-off-by: Cyrill Gorcunov <gorcunov@openvz.org>
> ---
>  criu/include/sockets.h |  2 ++
>  criu/mount.c           |  7 +++++++
>  criu/sk-unix.c         | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 62 insertions(+)
> 
> diff --git a/criu/include/sockets.h b/criu/include/sockets.h
> index 64673c5e5dc8..3d4669859182 100644
> --- a/criu/include/sockets.h
> +++ b/criu/include/sockets.h
> @@ -8,6 +8,7 @@
>  
>  struct fdinfo_list_entry;
>  struct sk_opts_entry;
> +struct mount_info;
>  struct file_desc;
>  struct fd_parms;
>  struct cr_imgset;
> @@ -38,6 +39,7 @@ extern struct collect_image_info inet_sk_cinfo;
>  extern struct collect_image_info unix_sk_cinfo;
>  extern int fix_external_unix_sockets(void);
>  extern int collect_unix_bindmounts(void);
> +extern int unix_prepare_bindmount(struct mount_info *mi);
>  
>  extern struct collect_image_info netlink_sk_cinfo;
>  
> diff --git a/criu/mount.c b/criu/mount.c
> index 59fd17870db8..174cc4639209 100644
> --- a/criu/mount.c
> +++ b/criu/mount.c
> @@ -27,6 +27,7 @@
>  #include "files-reg.h"
>  #include "external.h"
>  #include "fdstore.h"
> +#include "sockets.h"
>  
>  #include "images/mnt.pb-c.h"
>  
> @@ -2042,6 +2043,12 @@ static int do_bind_mount(struct mount_info *mi)
>  		}
>  	}
>  
> +	if (unix_prepare_bindmount(mi)) {

Why do we call this function for all mounts?

> +		pr_err("Failed to prepare bindmount on unix at %s\n",
> +		       mi->mountpoint);
> +		goto err;
> +	}
> +
>  	if (mount(root, mi->mountpoint, NULL, MS_BIND | (mi->flags & MS_REC), NULL) < 0) {
>  		pr_perror("Can't mount at %s", mi->mountpoint);
>  		goto err;
> diff --git a/criu/sk-unix.c b/criu/sk-unix.c
> index bf013c5ca4de..adc6d58aa173 100644
> --- a/criu/sk-unix.c
> +++ b/criu/sk-unix.c
> @@ -867,6 +867,7 @@ struct unix_sk_info {
>  	struct file_desc d;
>  	struct list_head connected; /* List of sockets, connected to me */
>  	struct list_head node; /* To link in peer's connected list  */
> +	int fdstore_id;
>  
>  	/*
>  	 * For DGRAM sockets with queues, we should only restore the queue
> @@ -1241,6 +1242,12 @@ static int open_unixsk_standalone(struct unix_sk_info *ui, int *new_fd)
>  	pr_info("Opening standalone socket (id %#x ino %#x peer %#x)\n",
>  			ui->ue->id, ui->ue->ino, ui->ue->peer);
>  
> +	if (ui->fdstore_id >= 0) {
> +		pr_debug("\tObtaining from fdstore id %#x\n", ui->fdstore_id);
> +		*new_fd = fdstore_get(ui->fdstore_id);
> +		return 0;
> +	}
> +
>  	if (set_netns(ui->ue->ns_id))
>  		return -1;
>  
> @@ -1453,6 +1460,9 @@ static void unlink_stale(struct unix_sk_info *ui)
>  {
>  	int ret, cwd_fd = -1, root_fd = -1;
>  
> +	if (ui->fdstore_id >= 0)
> +		return;
> +
>  	if (ui->name[0] == '\0' || (ui->ue->uflags & USK_EXTERN))
>  		return;
>  
> @@ -1509,6 +1519,7 @@ static int collect_one_unixsk(void *o, ProtobufCMessage *base, struct cr_img *i)
>  	INIT_LIST_HEAD(&ui->connected);
>  	INIT_LIST_HEAD(&ui->node);
>  	ui->flags = 0;
> +	ui->fdstore_id = -1;
>  	fixup_sock_net_ns_id(&ui->ue->ns_id, &ui->ue->has_ns_id);
>  
>  	uname = ui->name;
> @@ -1553,6 +1564,48 @@ struct collect_image_info unix_sk_cinfo = {
>  	.flags = COLLECT_SHARED,
>  };
>  
> +int unix_prepare_bindmount(struct mount_info *mi)
> +{
> +	char name_dir[PATH_MAX], ns_root[PATH_MAX];
> +	struct unix_sk_info *ui, *t = NULL;
> +	char *old_name_dir = NULL;
> +	int ret, sk;
> +
> +	list_for_each_entry(ui, &unix_sockets, list) {
> +		if (!ui->ue->has_mnt_id || ui->ue->mnt_id != mi->mnt_id)

I don't like an idea to run this loop for each mount
> +			continue;
> +		t = ui;
> +		break;
> +	}
> +
> +	if (!t)
> +		return 0;
> +
> +	print_ns_root(mi->nsid, 0, ns_root, sizeof(ns_root));
> +	if (ui->name_dir) {
> +		old_name_dir = ui->name_dir;
> +		snprintf(name_dir, sizeof(name_dir), "%s/%s", ns_root, ui->name_dir);
> +		ui->name_dir = name_dir;
> +	}
> +
> +	ret = open_unixsk_standalone(ui, &sk);
> +	if (ui->name_dir)
> +		ui->name_dir = old_name_dir;
> +	BUG_ON(ret > 0);
> +
> +	if (ret)
> +		return -1;
> +
> +	ui->fdstore_id = fdstore_add(sk);
> +	if (ui->fdstore_id < 0)
> +		return -1;
> +	close(sk);
> +
> +	pr_debug("Standalone socket moved into fdstore (id %#x ino %#x peer %#x)\n",
> +		 ui->ue->id, ui->ue->ino, ui->ue->peer);
> +	return 0;
> +}
> +
>  static void set_peer(struct unix_sk_info *ui, struct unix_sk_info *peer)
>  {
>  	ui->peer = peer;
> -- 
> 2.7.5
> 
> _______________________________________________
> CRIU mailing list
> CRIU@openvz.org
> https://lists.openvz.org/mailman/listinfo/criu
Cyrill Gorcunov Aug. 10, 2017, 7:03 a.m.
On Wed, Aug 09, 2017 at 04:42:15PM -0700, Andrei Vagin wrote:
> >  
> > +	if (unix_prepare_bindmount(mi)) {
> 
> Why do we call this function for all mounts?

For simplicity case. Look, I *don't* want to mangle mount code, neither
I want to modify mount images. This problem must only touch unix sockets
code. unix_prepare_bindmount simply exit early if socket is not found
(I'll make it as a hash so it will be fast).

	Cyrill