test67: test opening files and sockets with O_CLOEXEC
Change-Id: If71e61f830c6d3b5154368e189baa4f70bd73850
This commit is contained in:
parent
39fad09a94
commit
9c99b3b3b9
5 changed files with 485 additions and 4 deletions
|
@ -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*
|
||||
|
|
2
test/run
2
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`
|
||||
|
||||
|
|
33
test/t67a.c
Normal file
33
test/t67a.c
Normal file
|
@ -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;
|
||||
}
|
||||
|
39
test/t67b.c
Normal file
39
test/t67b.c
Normal file
|
@ -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;
|
||||
}
|
||||
|
409
test/test67.c
Normal file
409
test/test67.c
Normal file
|
@ -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 */
|
||||
}
|
||||
|
Loading…
Reference in a new issue