]> Zhao Yanbai Git Server - minix.git/commitdiff
test67: test opening files and sockets with O_CLOEXEC 65/365/4
authorThomas Veerman <thomas@minix3.org>
Tue, 26 Feb 2013 11:30:44 +0000 (11:30 +0000)
committerThomas Veerman <thomas@minix3.org>
Thu, 28 Feb 2013 10:08:54 +0000 (10:08 +0000)
Change-Id: If71e61f830c6d3b5154368e189baa4f70bd73850

test/Makefile
test/run
test/t67a.c [new file with mode: 0644]
test/t67b.c [new file with mode: 0644]
test/test67.c [new file with mode: 0644]

index 8f53897c400173d48bc16f52136f3e411bdaff5a..bdb06e0888c37a050db1a432f443658a90665d14 100644 (file)
@@ -23,11 +23,11 @@ OBJS.test57=test57loop.o
  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 \
 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 \
 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 \
-61 62    64 65 66
+61 62    64 65 66 67
 PROG+= test$(t)
 .endfor
   
-PROG+= t10a t11a t11b t40a t40b t40c t40d t40e t40f t60a t60b 
+PROG+= t10a t11a t11b t40a t40b t40c t40d t40e t40f t60a t60b t67a t67b
 
 .include <bsd.own.mk>
 
@@ -53,5 +53,5 @@ clean: .PHONY .MAKE
        $(MAKE) -C select clean
        rm -rf *.o *.s *.bak test? test?? t10a t11a t11b \
                t40a t40b t40c t40d t40e t40f \
-               t60a t60b \
+               t60a t60b t67a t67b \
                DIR*
index 7b5c621b53a3d05d09f491ec543212bfb199549b..df2ca9912ddeba62524942af5e2d307e057224a6 100755 (executable)
--- a/test/run
+++ b/test/run
@@ -14,7 +14,7 @@ badones=                      # list of tests that failed
 tests="   1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 \
          21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 \
          41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 \
-         61 62 63 64 65 66 \
+         61 62 63 64 65 66 67\
         sh1.sh sh2.sh interp.sh"
 tests_no=`expr 0`
 
diff --git a/test/t67a.c b/test/t67a.c
new file mode 100644 (file)
index 0000000..d398358
--- /dev/null
@@ -0,0 +1,33 @@
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+int
+main(int argc, char *argv[])
+{
+       int fd, fd_parent;
+       char buf[1];
+
+       if (argc != 2) {
+               return 1;
+       }
+
+       fd_parent = atoi(argv[1]);
+
+       /* If we open a new file, the fd we obtain should be fd_parent + 1 */
+       fd = open("open_plusplus_fd", O_CREAT|O_RDWR, 0660);
+       if (fd != fd_parent + 1) {
+               return 2;
+       }
+
+       /* Also, writing to fd_parent should succeed */
+       if (write(fd_parent, buf, sizeof(buf)) <= 0) {
+               return 3;
+       }
+
+       return 0;
+}
+
diff --git a/test/t67b.c b/test/t67b.c
new file mode 100644 (file)
index 0000000..ccdebd5
--- /dev/null
@@ -0,0 +1,39 @@
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+int
+main(int argc, char *argv[])
+{
+       int fd, fd_parent;
+       char buf[1];
+
+       if (argc != 2) {
+               return 1;
+       }
+
+       fd_parent = atoi(argv[1]);
+
+       /* Writing to fd_parent should fail as it has to be closed at this
+        * point */
+       if (write(fd_parent, buf, sizeof(buf)) != -1) {
+               return 2;
+       }
+       if (errno != EBADF) {
+               return 3;
+       }
+
+       /* If we open a new file, the fd we obtain should be identical to
+        * fd_parent */
+       fd = open("open_identical_fd", O_CREAT|O_RDWR, 0660);
+       if (fd != fd_parent) {
+               return 4;
+       }
+
+       return 0;
+}
+
diff --git a/test/test67.c b/test/test67.c
new file mode 100644 (file)
index 0000000..7f79b39
--- /dev/null
@@ -0,0 +1,409 @@
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#define MAX_ERROR 5
+#define CLOEXEC_PORT 3490
+#define FORK_PORT 3491
+#include "common.c"
+
+static int fd = 0;
+
+void copy_subtests(void);
+void test_open_file_cloexec(void);
+void test_open_file_fork(void);
+void test_open_socket_cloexec(void);
+void test_open_socket_fork(void);
+void start_socket_server(int port);
+int start_socket_client(int port, int flag);
+
+void
+copy_subtests()
+{
+       char *subtests[] = { "t67a", "t67b" };
+       char copy_cmd[8 + PATH_MAX + 1];
+       int i, no_tests;
+
+       no_tests = sizeof(subtests) / sizeof(char *);
+
+       for (i = 0; i < no_tests; i++) {
+               snprintf(copy_cmd, 8 + PATH_MAX, "cp ../%s .", subtests[i]);
+               system(copy_cmd);
+       }
+}
+
+void
+test_open_file_cloexec()
+{
+       int flags;
+       pid_t pid;
+
+       /* Let's create a file with O_CLOEXEC turned on */
+       fd = open("file", O_RDWR|O_CREAT|O_CLOEXEC, 0660);
+       if (fd < 0) e(1);
+
+       /* Now verify through fcntl the flag is indeed set */
+       flags = fcntl(fd, F_GETFD);
+       if (flags < 0) e(2);
+       if (!(flags & FD_CLOEXEC)) e(3);
+
+       /* Fork a child and let child exec a test program that verifies
+        * fd is not a valid file */
+       pid = fork();
+       if (pid == -1) e(4);    
+       else if (pid == 0) {
+               /* We're the child */
+               char fd_buf[2];
+
+               /* Verify again O_CLOEXEC is on */
+               flags = fcntl(fd, F_GETFD);
+               if (flags < 0) e(5);
+               if (!(flags & FD_CLOEXEC)) e(6);
+
+               snprintf(fd_buf, sizeof(fd_buf), "%d", fd);
+               execl("./t67b", "t67b", fd_buf, NULL);
+
+               /* Should not reach this */
+               exit(1);
+       } else {
+               /* We're the parent */
+               int result;
+
+               if (waitpid(pid, &result, 0) == -1) e(7);
+               if (WEXITSTATUS(result) != 0) e(8);
+       }
+       close(fd);
+}
+
+void
+test_open_file_fork()
+{
+       int flags;
+       pid_t pid;
+
+       /* Let's create a file with O_CLOEXEC NOT turned on */
+       fd = open("file", O_RDWR|O_CREAT, 0660);
+       if (fd < 0) e(1);
+
+       /* Now verify through fcntl the flag is indeed not set */
+       flags = fcntl(fd, F_GETFD);
+       if (flags < 0) e(2);
+       if (flags & FD_CLOEXEC) e(3);
+
+       /* Fork a child and let child exec a test program that verifies
+        * fd is a valid file */
+       pid = fork();
+       if (pid == -1) e(4);    
+       else if (pid == 0) {
+               /* We're the child */
+               char fd_buf[2];
+
+               /* Verify again O_CLOEXEC is off */
+               flags = fcntl(fd, F_GETFD);
+               if (flags < 0) e(5);
+               if (flags & FD_CLOEXEC) e(6);
+
+               snprintf(fd_buf, sizeof(fd_buf), "%d", fd);
+               execl("./t67a", "t67a", fd_buf, NULL);
+
+               /* Should not reach this */
+               exit(1);
+       } else {
+               /* We're the parent */
+               int result = 0;
+
+               if (waitpid(pid, &result, 0) == -1) e(7);
+               if (WEXITSTATUS(result) != 0) e(8);
+       }
+       close(fd);
+}
+
+int
+start_socket_client(int port, int flag)
+{
+       int fd_sock;
+       struct hostent *he;
+       struct sockaddr_in server;
+
+       if ((fd_sock = socket(PF_INET, SOCK_STREAM|flag, 0)) < 0) {
+               perror("Error obtaining socket\n");
+               e(1);
+       }
+
+       if ((he = gethostbyname("127.0.0.1")) == NULL) {
+               perror("Error retrieving home\n");
+               e(2);
+       }
+
+       /* Copy server host result */
+       memcpy(&server.sin_addr, he->h_addr_list[0], he->h_length);
+       server.sin_family = AF_INET;
+       server.sin_port = htons(port);
+
+       /* Normally, we'd zerofill sin_zero, but there is no such thing on
+        * Minix at the moment */
+#if !defined(__minix)
+       memset(&server.sin_zero, '\0', sizeof(server.sin_zero));
+#endif
+       
+       if (connect(fd_sock, (struct sockaddr *) &server, sizeof(server)) < 0){
+               perror("Error connecting to server\n");
+               e(3);
+       }
+
+       return fd_sock;
+}
+
+
+void
+start_socket_server(int port)
+{
+#if !defined(__minix)
+       int yes = 1;
+#endif
+       int fd_sock, fd_new, r;
+       struct sockaddr_in my_addr;
+       struct sockaddr_in other_addr;
+       socklen_t other_size;
+       char buf[1];
+
+       if ((fd_sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
+               perror("Error getting socket\n");
+               e(1);
+       }
+
+       my_addr.sin_family = AF_INET;
+       my_addr.sin_port = htons(port);
+       my_addr.sin_addr.s_addr = INADDR_ANY;
+       /* Normally we'd zerofill sin_zero, but current Minix socket interface
+        * does not support that field */
+#if !defined(__minix)
+       memset(&my_addr.sin_zero, '\0', sizeof(sin.sin_zero));
+#endif
+       
+       /* Reuse port number when invoking test often */
+#if !defined(__minix)
+       if (setsockopt(fd_sock, SOL_SOCKET, SO_REUSEADDR, &yes,
+           sizeof(int)) < 0) {
+               perror("Error setting port reuse option\n");
+               e(2);
+       }
+#endif
+
+       /* Bind to port */
+       if (bind(fd_sock, (struct sockaddr *) &my_addr, sizeof(my_addr)) < 0) {
+               perror("Error binding to port\n");
+               e(3);
+       }
+
+       /* Set socket in listening mode */
+       if (listen(fd_sock, 20) < 0) {
+               perror("Error listening for incoming connections");
+               e(4);
+       }
+
+       /* Accept incoming connections */
+       fd_new = accept(fd_sock, (struct sockaddr *) &other_addr, &other_size);
+
+       if (fd_new < 0) {
+               perror("Error accepting new connections\n");
+               e(5);
+       }
+
+       r = read(fd_new, buf, sizeof(buf));
+       exit(0);
+}
+
+void
+test_open_socket_cloexec()
+{
+/* This subtest will start a server and client using TCP. The client will
+ * open the socket with SOCK_CLOEXEC turned on, so that after a fork+exec, the
+ * socket should become invalid.
+ *                o
+ *              / | 
+ *        server  |
+ *      (accept)  |
+ *             |  | \
+ *             |  |  client
+ *             |  |  (connect)
+ *             |  |  | \
+ *             |  |  |  client_fork
+ *             |  |  |  (exec t67b)
+ *        (read)  |  |  (write)
+ *             |  |  | /
+ *             |  |  (waitpid client_fork) 
+ *              \ |  |
+ * (waitpid server)  |
+ *                | /
+ * (waitpid client)
+ *                |
+ *                o
+ */
+       pid_t pid_server, pid_client;
+       int result;
+
+       pid_server = fork();
+       if (pid_server < 0) e(1);
+       if (pid_server == 0) {
+               start_socket_server(CLOEXEC_PORT);
+               return; /* Never reached */
+       }
+
+       pid_client = fork();
+       if (pid_client < 0) e(2);
+       if (pid_client == 0) {
+               pid_t pid_client_fork;
+               int sockfd;
+
+               sockfd = start_socket_client(CLOEXEC_PORT, SOCK_CLOEXEC);
+               if (sockfd < 0) e(4);
+
+               pid_client_fork = fork();
+               if (pid_client_fork < 0) {
+                       e(5);
+                       exit(5);
+               }
+               if (pid_client_fork == 0) {
+                       /* We're a fork of the client. After we exec, the
+                        * socket should become invalid due to the SOCK_CLOEXEC
+                        * flag.
+                        */
+                       char sockfd_buf[2];
+                       int flags;
+
+                       /* Verify O_CLOEXEC is on */
+                       flags = fcntl(sockfd, F_GETFD);
+                       if (flags < 0) e(5);
+                       if (!(flags & FD_CLOEXEC)) e(6);
+
+                       /* t67b will verify that it can't write to sockfd and
+                        * that opening a new file will yield a file descriptor
+                        * with the same number.
+                        */
+                       snprintf(sockfd_buf, sizeof(sockfd_buf), "%d", sockfd);
+                       execl("./t67b", "t67b", sockfd_buf, NULL);
+
+                       /* Should not reach this */
+                       exit(1);
+               } else {
+                       if (waitpid(pid_client_fork, &result, 0) < 0) e(8);
+                       exit(WEXITSTATUS(result)); /* Pass on error to main */
+               }
+               exit(0);        /* Never reached */
+       }
+
+       if (waitpid(pid_server, &result, 0) < 0) e(3);
+       if (waitpid(pid_client, &result, 0) < 0) e(4);
+
+       /* Let's inspect client result */
+       if (WEXITSTATUS(result) != 0) e(5);
+}
+
+void
+test_open_socket_fork(void)
+{
+/* This subtest will start a server and client using TCP. The client will
+ * open the socket with SOCK_CLOEXEC turned off, so that after a fork+exec, the
+ * socket should stay valid.
+ *                o
+ *              / | 
+ *        server  |
+ *      (accept)  |
+ *             |  | \
+ *             |  |  client
+ *             |  |  (connect)
+ *             |  |  | \
+ *             |  |  |  client_fork
+ *             |  |  |  (exec t67a)
+ *        (read)  |  |  (write)
+ *             |  |  | /
+ *             |  |  (waitpid client_fork) 
+ *              \ |  |
+ * (waitpid server)  |
+ *                | /
+ * (waitpid client)
+ *                |
+ *                o
+ */
+       pid_t pid_server, pid_client;
+       int result;
+
+       pid_server = fork();
+       if (pid_server < 0) e(1);
+       if (pid_server == 0) {
+               start_socket_server(FORK_PORT);
+               return; /* Never reached */
+       }
+
+       pid_client = fork();
+       if (pid_client < 0) e(2);
+       if (pid_client == 0) {
+               pid_t pid_client_fork;
+               int sockfd;
+
+               sockfd = start_socket_client(FORK_PORT, 0);
+               if (sockfd < 0) e(4);
+
+               pid_client_fork = fork();
+               if (pid_client_fork < 0) {
+                       e(5);
+                       exit(5);
+               }
+               if (pid_client_fork == 0) {
+                       /* We're a fork of the client. After we exec, the
+                        * socket should stay valid due to lack of SOCK_CLOEXEC
+                        * flag.
+                        */
+                       char sockfd_buf[2];
+                       int flags;
+
+                       /* Verify O_CLOEXEC is off */
+                       flags = fcntl(sockfd, F_GETFD);
+                       if (flags < 0) e(5);
+                       if (flags & FD_CLOEXEC) e(6);
+
+                       /* t67a will verify that it can't write to sockfd and
+                        * that opening a new file will yield a file descriptor
+                        * with a higher number.
+                        */
+                       snprintf(sockfd_buf, sizeof(sockfd_buf), "%d", sockfd);
+                       execl("./t67a", "t67a", sockfd_buf, NULL);
+
+                       /* Should not reach this */
+                       exit(1);
+               } else {
+                       if (waitpid(pid_client_fork, &result, 0) < 0) e(8);
+                       exit(WEXITSTATUS(result)); /* Pass on error to main */
+               }
+               exit(0);        /* Never reached */
+       }
+
+       if (waitpid(pid_server, &result, 0) < 0) e(3);
+       if (waitpid(pid_client, &result, 0) < 0) e(4);
+
+       /* Let's inspect client result */
+       if (WEXITSTATUS(result) != 0) e(5);
+}
+
+int
+main(int argc, char *argv[])
+{
+       start(67);
+       copy_subtests();
+       test_open_file_fork();
+       test_open_file_cloexec();
+       test_open_socket_fork();
+       test_open_socket_cloexec();
+       quit();
+       return(-1);     /* Unreachable */
+}
+