diff --git a/minix/lib/libc/sys/fcntl.c b/minix/lib/libc/sys/fcntl.c index e6f4e6786..8a243e5c1 100644 --- a/minix/lib/libc/sys/fcntl.c +++ b/minix/lib/libc/sys/fcntl.c @@ -21,6 +21,7 @@ int fcntl(int fd, int cmd, ...) /* Adjust for the stupid cases. */ switch(cmd) { case F_DUPFD: + case F_DUPFD_CLOEXEC: case F_SETFD: case F_SETFL: case F_SETNOSIGPIPE: diff --git a/minix/man/man2/fcntl.2 b/minix/man/man2/fcntl.2 index bc49672a9..7d7590181 100644 --- a/minix/man/man2/fcntl.2 +++ b/minix/man/man2/fcntl.2 @@ -37,6 +37,13 @@ or equal to .IR fd2 . .RE .SP +.BI "fcntl(" fd ", F_DUPFD_CLOEXEC, int " fd2 ")" +.RS +As +.BR F_DUPFD , +but the "close on exec" flag is set on the returned file descriptor. +.RE +.SP .BI "fcntl(" fd ", F_GETFD)" .RS Returns the file descriptor flags associated with file descriptor diff --git a/minix/servers/vfs/misc.c b/minix/servers/vfs/misc.c index 429244c0b..9e426ce0e 100644 --- a/minix/servers/vfs/misc.c +++ b/minix/servers/vfs/misc.c @@ -116,11 +116,15 @@ int do_fcntl(void) switch (fcntl_req) { case F_DUPFD: + case F_DUPFD_CLOEXEC: /* This replaces the old dup() system call. */ if (fcntl_argx < 0 || fcntl_argx >= OPEN_MAX) r = EINVAL; else if ((r = get_fd(fp, fcntl_argx, 0, &new_fd, NULL)) == OK) { f->filp_count++; fp->fp_filp[new_fd] = f; + assert(!FD_ISSET(new_fd, &fp->fp_cloexec_set)); + if (fcntl_req == F_DUPFD_CLOEXEC) + FD_SET(new_fd, &fp->fp_cloexec_set); r = new_fd; } break; diff --git a/minix/tests/test7.c b/minix/tests/test7.c index c8b78f117..2443d1480 100644 --- a/minix/tests/test7.c +++ b/minix/tests/test7.c @@ -213,8 +213,8 @@ void test7c() { /* Test fcntl(). */ - int fd, m, s, newfd, newfd2; - struct stat stat1, stat2, stat3; + int fd, m, s, newfd, newfd2, newfd3, newfd4; + struct stat stat1, stat2, stat3, stat4, stat5; subtest = 3; errno = -100; @@ -264,20 +264,42 @@ void test7c() if (fcntl(newfd2, F_GETFL) != (O_APPEND | m)) e(30); /* O_APPEND set */ if (fcntl(fd, F_SETFD, 0) != 0) e(31);/* turn FD_CLOEXEC off */ - /* Check if newfd and newfd2 are the same inode. */ + /* Also test F_DUPFD_CLOEXEC. */ + if ( (newfd3 = fcntl(fd, F_DUPFD_CLOEXEC, 0)) != 4) e(0); + if (fcntl(newfd3, F_GETFD) != FD_CLOEXEC) e(0); /* FD_CLOEXEC must be set */ + if (close(newfd3) != 0) e(0); + if ( (newfd3 = fcntl(fd, F_DUPFD_CLOEXEC, 12)) != 12) e(0); + if (fcntl(newfd3, F_GETFD) != FD_CLOEXEC) e(0); /* FD_CLOEXEC must be set */ + if ( (newfd4 = fcntl(newfd3, F_DUPFD, 0)) != 4) e(0); + if (fcntl(newfd4, F_GETFD) != 0) e(0); /* FD_CLOEXEC must be unset */ + if (close(newfd4) != 0) e(0); + if ( (newfd4 = fcntl(newfd3, F_DUPFD_CLOEXEC, 15)) != 15) e(0); + if (fcntl(newfd4, F_GETFD) != FD_CLOEXEC) e(0); /* FD_CLOEXEC must be set */ + if (fcntl(newfd4, F_SETFD, 0) != 0) e(0); /* turn FD_CLOEXEC off */ + if (fcntl(newfd4, F_GETFD) != 0) e(0); /* FD_CLOEXEC must be unset */ + + /* Check if all file descriptors are for the same inode. */ if (fstat(fd, &stat1) != 0) e(32); - if (fstat(fd, &stat2) != 0) e(33); - if (fstat(fd, &stat3) != 0) e(34); + if (fstat(newfd, &stat2) != 0) e(33); + if (fstat(newfd2, &stat3) != 0) e(34); + if (fstat(newfd3, &stat4) != 0) e(0); + if (fstat(newfd4, &stat5) != 0) e(0); if (stat1.st_dev != stat2.st_dev) e(35); if (stat1.st_dev != stat3.st_dev) e(36); + if (stat1.st_dev != stat4.st_dev) e(0); + if (stat1.st_dev != stat5.st_dev) e(0); if (stat1.st_ino != stat2.st_ino) e(37); if (stat1.st_ino != stat3.st_ino) e(38); + if (stat1.st_ino != stat4.st_ino) e(0); + if (stat1.st_ino != stat5.st_ino) e(0); /* Now check on the FD_CLOEXEC flag. Set it for fd (3) and newfd2 (10) */ - if (fd != 3 || newfd2 != 10 || newfd != 6) e(39); + if (fd != 3 || newfd2 != 10 || newfd3 != 12 || newfd4 != 15 || newfd != 6) + e(39); if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0) e(40); /* close 3 on exec */ if (fcntl(newfd2, F_SETFD, FD_CLOEXEC) != 0) e(41); /* close 10 on exec */ if (fcntl(newfd, F_SETFD, 0) != 0) e(42); /* don't close 6 */ + /* leave 12 and 15 as is */ if (fork()) { wait(&s); /* parent just waits */ if (WEXITSTATUS(s) != 0) e(43); @@ -630,12 +652,14 @@ void cloexec_test() * closed upon exec, we have to exec something. The test is carried * out by forking, and then having the child exec test7 itself, but * with argument 0. This is detected, and control comes here. - * File descriptors 3 and 10 should be closed here, and 10 open. + * File descriptors 3, 10, and 12 should be closed here, and 6 and 15 open. */ if (close(3) == 0) e(1001); /* close should fail; it was closed on exec */ if (close(6) != 0) e(1002); /* close should succeed */ if (close(10) == 0) e(1003); /* close should fail */ + if (close(12) == 0) e(1004); /* close should fail */ + if (close(15) != 0) e(1005); /* close should succeed */ fflush(stdout); exit(0); } diff --git a/minix/usr.bin/trace/service/vfs.c b/minix/usr.bin/trace/service/vfs.c index 71006c1cb..12e9965ab 100644 --- a/minix/usr.bin/trace/service/vfs.c +++ b/minix/usr.bin/trace/service/vfs.c @@ -603,6 +603,7 @@ vfs_fcntl_out(struct trace_proc * proc, const message * m_out) switch (m_out->m_lc_vfs_fcntl.cmd) { case F_DUPFD: + case F_DUPFD_CLOEXEC: put_fd(proc, "fd2", m_out->m_lc_vfs_fcntl.arg_int); break; case F_SETFD: