[CRIU] [PATCH] zdtm: unlinked socket with dentry alias
Stanislav Kinsburskiy
skinsbursky at odin.com
Mon Sep 28 07:38:42 PDT 2015
There can be an unlinked UNIX socket, which dentry has a linked alias,
pointing to the same inode.
To check, that such socket is migrated successfully, test perform the
following:
Before suspend:
1) Creates UNIX socket and binds it.
2) Created hard link to the binded path.
3) Unlinks original UNIX bind path.
3) Forks a child, listen to unlinked socket and accepting connection.
After resume:
1) Create UNIX socket and binds it to socket link.
2) Connects to socket.
3) Child have to accept the connection.
Signed-off-by: Stanislav Kinsburskiy <skinsbursky at parallels.com>
---
test/zdtm/live/static/Makefile | 1
test/zdtm/live/static/deleted_unix_sock_linked.c | 196 ++++++++++++++++++++++
2 files changed, 197 insertions(+)
create mode 100644 test/zdtm/live/static/deleted_unix_sock_linked.c
diff --git a/test/zdtm/live/static/Makefile b/test/zdtm/live/static/Makefile
index c3a9929..35a8b74 100644
--- a/test/zdtm/live/static/Makefile
+++ b/test/zdtm/live/static/Makefile
@@ -140,6 +140,7 @@ TST_FILE = \
link10 \
file_attr \
deleted_unix_sock \
+ deleted_unix_sock_linked \
sk-unix-rel \
deleted_dev \
unlink_fstat00 \
diff --git a/test/zdtm/live/static/deleted_unix_sock_linked.c b/test/zdtm/live/static/deleted_unix_sock_linked.c
new file mode 100644
index 0000000..3712fb2
--- /dev/null
+++ b/test/zdtm/live/static/deleted_unix_sock_linked.c
@@ -0,0 +1,196 @@
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include "zdtmtst.h"
+
+const char *test_doc = "Create a unix socket, bind to it, then create a hardlink and destroy socket before "
+ "migration; check that the child can accept connection to unlinked binded socket via linked one.";
+const char *test_author = "Stanislav Kinsbursky <skinsbursky at odin.com>";
+
+char *filename;
+TEST_OPTION(filename, string, "file name", 1);
+
+static int fill_sock_name(struct sockaddr_un *name, const char *filename)
+{
+ if (strlen(filename) >= sizeof(name->sun_path))
+ return -1;
+
+ name->sun_family = AF_LOCAL;
+ strcpy(name->sun_path, filename);
+ return 0;
+}
+
+static int setup_deleted_sock_linked(const char *linkname)
+{
+ struct sockaddr_un name;
+ int sock;
+
+ if (fill_sock_name(&name, filename) < 0) {
+ err("filename \"%s\" is too long", filename);
+ return -1;
+ }
+
+ sock = socket(PF_LOCAL, SOCK_STREAM, 0);
+ if (sock < 0) {
+ err("can't create socket: %m");
+ return -1;
+ }
+
+ if (bind(sock, (struct sockaddr *) &name, SUN_LEN(&name)) < 0) {
+ err("can't bind to socket \"%s\": %m", filename);
+ goto err;
+ }
+
+ if (link(filename, linkname)) {
+ err("can't create link: %m");
+ return -1;
+ }
+
+ if (unlink(filename)) {
+ err("can't unlink %s: %m", filename);
+ goto err;
+ }
+
+ return sock;
+err:
+ close(sock);
+ return -1;
+}
+
+static int connect_linked_sock(char *filename)
+{
+ struct sockaddr_un name;
+ int sock;
+
+ if (fill_sock_name(&name, filename) < 0) {
+ fail("failed to fill linked socket name: %m", filename);
+ return -1;
+ }
+
+ sock = socket(PF_LOCAL, SOCK_STREAM, 0);
+ if (sock < 0) {
+ fail("can't create socket: %m");
+ return -1;
+ }
+
+ if (connect(sock, (struct sockaddr *) &name, SUN_LEN(&name)) < 0) {
+ err("failed to connect to %s: %m", filename);
+ goto err;
+ }
+
+ return sock;
+err:
+ close(sock);
+ return -1;
+}
+
+int main(int argc, char ** argv)
+{
+ int deleted_sock, conn_sock, ret;
+ pid_t pid;
+ char linkname[108];
+
+ test_init(argc, argv);
+
+ snprintf(linkname, 108, "%s-link", filename);
+
+ deleted_sock = setup_deleted_sock_linked(linkname);
+ if (deleted_sock < 0) {
+ err("can't create socket: %m");
+ return -1;
+ }
+
+ pid = test_fork();
+ if (pid < 0) {
+ err("can't fork: %m\n");
+ exit(1);
+ }
+
+ if (pid == 0) { /* child waits for connection on the unlinked socket and returns */
+ int acc_sock;
+
+ if (listen(deleted_sock, 1)) {
+ err("failed to listen to deleted socket: %m");
+ _exit(1);
+ }
+
+ test_waitsig();
+
+ acc_sock = accept(deleted_sock, NULL, NULL);
+ if (acc_sock < 0) {
+ err("failed to accept connection on unlinked socket: %m");
+ _exit(1);
+ }
+
+ if (close(deleted_sock)) {
+ err("can't close unlinked socket: %m");
+ _exit(1);
+ }
+
+ if (close(acc_sock)) {
+ err("can't close accepted socket: %m");
+ _exit(1);
+ }
+
+ exit(0);
+ }
+
+ if (close(deleted_sock)) {
+ err("close failed: %m\n");
+ goto out_kill;
+ }
+
+ test_daemon();
+ test_waitsig();
+
+ conn_sock = connect_linked_sock(linkname);
+ if (conn_sock < 0)
+ goto out_kill;
+
+ if (close(conn_sock)) {
+ fail("connected socket close failed: %m\n");
+ goto out_kill;
+ }
+
+ if (kill(pid, SIGTERM)) {
+ fail("terminating the child failed: %m\n");
+ goto out_link;
+ }
+
+ if (wait(&ret) != pid) {
+ fail("wait() returned wrong pid %d: %m\n", pid);
+ goto out_link;
+ }
+
+ if (WIFEXITED(ret)) {
+ ret = WEXITSTATUS(ret);
+ if (ret) {
+ fail("child exited with nonzero code %d (%s)\n", ret, strerror(ret));
+ goto out_link;
+ }
+ }
+ if (WIFSIGNALED(ret)) {
+ fail("child exited on unexpected signal %d\n", WTERMSIG(ret));
+ goto out_link;
+ }
+
+ if (unlink(filename) != -1 || errno != ENOENT) {
+ fail("file %s should have been deleted before migration: unlink: %m\n");
+ goto out_link;
+ }
+
+ pass();
+
+out_kill:
+ kill(pid, SIGTERM);
+out_link:
+ if (unlink(linkname))
+ err("can't unlink %s: %m\n", filename);
+ return 0;
+}
More information about the CRIU
mailing list