[2/3] sk-unix: Restore deleted sockets together with removed dirs

Submitted by Cyrill Gorcunov on Nov. 22, 2016, 4:13 p.m.

Details

Message ID 1479831214-19760-3-git-send-email-gorcunov@virtuozzo.com
State Superseded
Series "sk-unix: Handle case when socket is deleted together with parent dir"
Headers show

Commit Message

Cyrill Gorcunov Nov. 22, 2016, 4:13 p.m.
In case if socket sits in a driectory which removed
altogether with socket itself, we fail on plain
bind call but have to restore complete dentry tree
first, bind the socket and remove them all then.

Signed-off-by: Cyrill Gorcunov <gorcunov@virtuozzo.com>
---
 criu/cr-restore.c      |  3 ++
 criu/include/sockets.h |  1 +
 criu/sk-unix.c         | 98 +++++++++++++++++++++++++++++++++++++++++++++++---
 3 files changed, 97 insertions(+), 5 deletions(-)

Patch hide | download patch | download mbox

diff --git a/criu/cr-restore.c b/criu/cr-restore.c
index 863fdfc..b9f7ba1 100644
--- a/criu/cr-restore.c
+++ b/criu/cr-restore.c
@@ -225,6 +225,9 @@  static int root_prepare_shared(void)
 
 	pr_info("Preparing info about shared resources\n");
 
+	if (prepare_shared_unix())
+		return -1;
+
 	if (prepare_shared_tty())
 		return -1;
 
diff --git a/criu/include/sockets.h b/criu/include/sockets.h
index 64a9865..fc6bbf9 100644
--- a/criu/include/sockets.h
+++ b/criu/include/sockets.h
@@ -39,6 +39,7 @@  extern int collect_sockets(struct ns_id *);
 extern int collect_inet_sockets(void);
 extern struct collect_image_info unix_sk_cinfo;
 extern int fix_external_unix_sockets(void);
+extern int prepare_shared_unix(void);
 
 extern struct collect_image_info netlink_sk_cinfo;
 
diff --git a/criu/sk-unix.c b/criu/sk-unix.c
index 323da04..1798add 100644
--- a/criu/sk-unix.c
+++ b/criu/sk-unix.c
@@ -9,6 +9,7 @@ 
 #include <sys/un.h>
 #include <stdlib.h>
 #include <dlfcn.h>
+#include <libgen.h>
 
 #include "asm/types.h"
 #include "libnetlink.h"
@@ -28,6 +29,7 @@ 
 #include "namespaces.h"
 #include "pstree.h"
 #include "crtools.h"
+#include "rst-malloc.h"
 
 #include "protobuf.h"
 #include "images/sk-unix.pb-c.h"
@@ -102,8 +104,19 @@  struct  unix_sk_exception {
 
 #define SK_HASH_SIZE		32
 
+static mutex_t *deleted_socket_mutex;
+
 static struct unix_sk_listen_icon *unix_listen_icons[SK_HASH_SIZE];
 
+int prepare_shared_unix(void)
+{
+	deleted_socket_mutex = shmalloc(sizeof(*deleted_socket_mutex));
+	if (!deleted_socket_mutex)
+		return -1;
+	mutex_init(deleted_socket_mutex);
+	return 0;
+}
+
 static struct unix_sk_listen_icon *lookup_unix_listen_icons(int peer_ino)
 {
 	struct unix_sk_listen_icon *ic;
@@ -951,11 +964,74 @@  static int post_open_unix_sk(struct file_desc *d, int fd)
 	return 0;
 }
 
+/*
+ * When path where socket lives is deleted, we need to reconstruct
+ * it back up but allow caller to remove it after.
+ */
+static int try_rebind_on_deleted(int sk, struct sockaddr_un *addr,
+				 struct unix_sk_info *ui,
+				 size_t *keep)
+{
+	char path[PATH_MAX], *pos;
+	int ret, _keep;
+
+	if (ui->ue->name.len >= sizeof(path)) {
+		pr_err("Too long name for socket\n");
+		return -ENOSPC;
+	}
+
+	memcpy(path, ui->name, ui->ue->name.len);
+	path[ui->ue->name.len] = '\0';
+
+	for (pos = strrchr(path, '/'); pos;
+	     pos = strrchr(path, '/')) {
+		*pos = '\0';
+
+		ret = access(path, R_OK | W_OK | X_OK);
+		if (ret == 0)
+			break;
+
+		if (errno != ENOENT) {
+			ret = -errno;
+			pr_perror("Can't access %s\n", path);
+			return ret;
+		}
+	}
+
+	_keep = pos ? pos - path : ui->ue->name.len;
+
+	memcpy(path, ui->name, ui->ue->name.len);
+	path[ui->ue->name.len] = '\0';
+
+	pos = dirname(path);
+	ret = mkdirpat(AT_FDCWD, pos, 0755);
+	if (ret) {
+		pr_err("Can't create %s\n", pos);
+		return ret;
+	}
+
+	ret = bind(sk, (struct sockaddr *)addr,
+		   sizeof(addr->sun_family) + ui->ue->name.len);
+	if (ret < 0) {
+		pr_perror("Can't bind on socket %s", (char *)ui->ue->name.data);
+		goto out;
+	}
+
+	*keep = _keep;
+	return 0;
+
+out:
+	if (rmdirp(pos, _keep))
+		pr_err("Can't cleanup %s\n", pos);
+	return ret;
+}
+
 static int bind_unix_sk(int sk, struct unix_sk_info *ui)
 {
 	struct sockaddr_un addr;
 	int cwd_fd = -1;
 	int ret = -1;
+	size_t keep;
 
 	if ((ui->ue->type == SOCK_STREAM) && (ui->ue->state == TCP_ESTABLISHED)) {
 		/*
@@ -972,6 +1048,7 @@  static int bind_unix_sk(int sk, struct unix_sk_info *ui)
 	memset(&addr, 0, sizeof(addr));
 	addr.sun_family = AF_UNIX;
 	memcpy(&addr.sun_path, ui->name, ui->ue->name.len);
+	keep = ui->ue->name.len;
 
 	if (prep_unix_sk_cwd(ui, &cwd_fd))
 		return -1;
@@ -1018,8 +1095,13 @@  static int bind_unix_sk(int sk, struct unix_sk_info *ui)
 				ui->ue->deleted = false;
 
 			} else {
-				pr_perror("Can't bind socket");
-				goto done;
+				mutex_lock(deleted_socket_mutex);
+				ret = try_rebind_on_deleted(sk, &addr, ui, &keep);
+				mutex_unlock(deleted_socket_mutex);
+				if (ret) {
+					pr_err("Can't bind deleted socket\n");
+					goto done;
+				}
 			}
 		}
 
@@ -1046,9 +1128,15 @@  static int bind_unix_sk(int sk, struct unix_sk_info *ui)
 			}
 		}
 
-		if (ui->ue->deleted && unlink((char *)ui->ue->name.data) < 0) {
-			pr_perror("failed to unlink %s\n", ui->ue->name.data);
-			goto done;
+		if (ui->ue->deleted) {
+			if (unlink((char *)ui->ue->name.data) < 0) {
+				pr_perror("failed to unlink %s\n", ui->ue->name.data);
+				goto done;
+			}
+			if (rmdirp((char *)ui->ue->name.data, keep)) {
+				pr_err("Can't clean up %s\n", ui->ue->name.data);
+				goto done;
+			}
 		}
 	}