[CRIU] [PATCH 2/3] sk-unix: Restore deleted sockets together with removed dirs
Cyrill Gorcunov
gorcunov at openvz.org
Wed Nov 23 10:28:31 PST 2016
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 at openvz.org>
---
criu/cr-restore.c | 3 ++
criu/include/sockets.h | 1 +
criu/sk-unix.c | 98 +++++++++++++++++++++++++++++++++++++++++++++++---
3 files changed, 97 insertions(+), 5 deletions(-)
diff --git a/criu/cr-restore.c b/criu/cr-restore.c
index 51c423d2f9ee..1dbb0370fd4f 100644
--- a/criu/cr-restore.c
+++ b/criu/cr-restore.c
@@ -226,6 +226,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 0de527c63a30..950a673a7336 100644
--- a/criu/include/sockets.h
+++ b/criu/include/sockets.h
@@ -36,6 +36,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 202228da9789..898b3026c023 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 "libnetlink.h"
#include "cr_options.h"
@@ -28,6 +29,7 @@
#include "pstree.h"
#include "external.h"
#include "crtools.h"
+#include "rst-malloc.h"
#include "protobuf.h"
#include "images/sk-unix.pb-c.h"
@@ -97,8 +99,19 @@ struct unix_sk_listen_icon {
#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;
@@ -923,11 +936,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)) {
/*
@@ -944,6 +1020,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;
@@ -990,8 +1067,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;
+ }
}
}
@@ -1018,9 +1100,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", 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;
+ }
}
}
--
2.7.4
More information about the CRIU
mailing list