aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKyle Evans <kevans@FreeBSD.org>2020-04-12 21:23:19 +0000
committerKyle Evans <kevans@FreeBSD.org>2020-04-12 21:23:19 +0000
commit472ced39efb537374068f06b348fe5dac389c45a (patch)
tree30f4cf81fcfd1a7f955265b34c52d3265a977d9a
parent1d3500e0658360e7475fbf0e3bdf9fe499c2c18a (diff)
Notes
-rw-r--r--include/unistd.h1
-rw-r--r--lib/libc/sys/Makefile.inc1
-rw-r--r--lib/libc/sys/Symbol.map1
-rw-r--r--lib/libc/sys/closefrom.241
-rw-r--r--sys/compat/freebsd32/syscalls.master2
-rw-r--r--sys/kern/capabilities.conf1
-rw-r--r--sys/kern/kern_descrip.c69
-rw-r--r--sys/kern/syscalls.master7
-rw-r--r--sys/sys/syscallsubr.h1
-rw-r--r--tests/sys/file/closefrom_test.c40
10 files changed, 146 insertions, 18 deletions
diff --git a/include/unistd.h b/include/unistd.h
index 5a4991ef9fe1d..02df2515eaaa6 100644
--- a/include/unistd.h
+++ b/include/unistd.h
@@ -494,6 +494,7 @@ struct crypt_data {
int acct(const char *);
int async_daemon(void);
int check_utility_compat(const char *);
+int close_range(unsigned int, unsigned int, int);
ssize_t copy_file_range(int, off_t *, int, off_t *, size_t, unsigned int);
const char *
crypt_get_format(void);
diff --git a/lib/libc/sys/Makefile.inc b/lib/libc/sys/Makefile.inc
index cc882e21a607a..913fe56eefbec 100644
--- a/lib/libc/sys/Makefile.inc
+++ b/lib/libc/sys/Makefile.inc
@@ -371,6 +371,7 @@ MLINKS+=chown.2 fchown.2 \
chown.2 lchown.2
MLINKS+=clock_gettime.2 clock_getres.2 \
clock_gettime.2 clock_settime.2
+MLINKS+=closefrom.2 close_range.2
MLINKS+=nanosleep.2 clock_nanosleep.2
MLINKS+=cpuset.2 cpuset_getid.2 \
cpuset.2 cpuset_setid.2
diff --git a/lib/libc/sys/Symbol.map b/lib/libc/sys/Symbol.map
index aa60da2a67899..4d3bd5115af3a 100644
--- a/lib/libc/sys/Symbol.map
+++ b/lib/libc/sys/Symbol.map
@@ -403,6 +403,7 @@ FBSD_1.5 {
FBSD_1.6 {
__sysctlbyname;
+ close_range;
copy_file_range;
fhlink;
fhlinkat;
diff --git a/lib/libc/sys/closefrom.2 b/lib/libc/sys/closefrom.2
index a0b5fc218666c..db41c617dc7f4 100644
--- a/lib/libc/sys/closefrom.2
+++ b/lib/libc/sys/closefrom.2
@@ -25,11 +25,12 @@
.\"
.\" $FreeBSD$
.\"
-.Dd June 12, 2009
+.Dd April 12, 2020
.Dt CLOSEFROM 2
.Os
.Sh NAME
-.Nm closefrom
+.Nm closefrom ,
+.Nm close_range
.Nd delete open file descriptors
.Sh LIBRARY
.Lb libc
@@ -37,6 +38,8 @@
.In unistd.h
.Ft void
.Fn closefrom "int lowfd"
+.Ft int
+.Fn close_range "u_int lowfd" "u_int highfd" "int flags"
.Sh DESCRIPTION
The
.Fn closefrom
@@ -44,6 +47,40 @@ system call deletes all open file descriptors greater than or equal to
.Fa lowfd
from the per-process object reference table.
Any errors encountered while closing file descriptors are ignored.
+.Pp
+The
+.Fn close_range
+system call deletes all open file descriptors between
+.Fa lowfd
+and
+.Fa highfd
+inclusive, clamped to the range of open file descriptors.
+Any errors encountered while closing file descriptors are ignored.
+There are currently no defined
+.Fa flags .
+.Sh RETURN VALUES
+Upon successful completion,
+.Fn close_range
+returns a value
+of 0.
+Otherwise, a value of -1 is returned and the global variable
+.Va errno
+is set to indicate the error.
+.Sh ERRORS
+The
+.Fn close_range
+system call
+will fail if:
+.Bl -tag -width Er
+.It Bq Er EINVAL
+The
+.Fa highfd
+argument is lower than the
+.Fa lowfd
+argument.
+.It Bq Er EINVAL
+An invalid flag was set.
+.El
.Sh SEE ALSO
.Xr close 2
.Sh HISTORY
diff --git a/sys/compat/freebsd32/syscalls.master b/sys/compat/freebsd32/syscalls.master
index 049e0b78500c2..0f735faca8e11 100644
--- a/sys/compat/freebsd32/syscalls.master
+++ b/sys/compat/freebsd32/syscalls.master
@@ -1162,5 +1162,7 @@
573 AUE_NULL NOPROTO { int sigfastblock(int cmd, uint32_t *ptr); }
574 AUE_REALPATHAT NOPROTO { int __realpathat(int fd, const char *path, \
char *buf, size_t size, int flags); }
+575 AUE_NULL NOPROTO { int close_range(u_int lowfd, u_int highfd, \
+ int flags); }
; vim: syntax=off
diff --git a/sys/kern/capabilities.conf b/sys/kern/capabilities.conf
index 555a0232bde38..c465eca0bc6d9 100644
--- a/sys/kern/capabilities.conf
+++ b/sys/kern/capabilities.conf
@@ -131,6 +131,7 @@ clock_gettime
## Always allow file descriptor close(2).
##
close
+close_range
closefrom
##
diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c
index 5357189936311..580956c5a0e31 100644
--- a/sys/kern/kern_descrip.c
+++ b/sys/kern/kern_descrip.c
@@ -1313,6 +1313,57 @@ kern_close(struct thread *td, int fd)
return (closefp(fdp, fd, fp, td, 1));
}
+int
+kern_close_range(struct thread *td, u_int lowfd, u_int highfd)
+{
+ struct filedesc *fdp;
+ int fd, ret;
+
+ ret = 0;
+ fdp = td->td_proc->p_fd;
+ FILEDESC_SLOCK(fdp);
+
+ /*
+ * Check this prior to clamping; closefrom(3) with only fd 0, 1, and 2
+ * open should not be a usage error. From a close_range() perspective,
+ * close_range(3, ~0U, 0) in the same scenario should also likely not
+ * be a usage error as all fd above 3 are in-fact already closed.
+ */
+ if (highfd < lowfd) {
+ ret = EINVAL;
+ goto out;
+ }
+ /* Clamped to [lowfd, fd_lastfile] */
+ highfd = MIN(highfd, fdp->fd_lastfile);
+ for (fd = lowfd; fd <= highfd; fd++) {
+ if (fdp->fd_ofiles[fd].fde_file != NULL) {
+ FILEDESC_SUNLOCK(fdp);
+ (void)kern_close(td, fd);
+ FILEDESC_SLOCK(fdp);
+ }
+ }
+out:
+ FILEDESC_SUNLOCK(fdp);
+ return (ret);
+}
+
+#ifndef _SYS_SYSPROTO_H_
+struct close_range_args {
+ u_int lowfd;
+ u_int highfd;
+ int flags;
+};
+#endif
+int
+sys_close_range(struct thread *td, struct close_range_args *uap)
+{
+
+ /* No flags currently defined */
+ if (uap->flags != 0)
+ return (EINVAL);
+ return (kern_close_range(td, uap->lowfd, uap->highfd));
+}
+
/*
* Close open file descriptors.
*/
@@ -1325,28 +1376,16 @@ struct closefrom_args {
int
sys_closefrom(struct thread *td, struct closefrom_args *uap)
{
- struct filedesc *fdp;
- int fd;
+ u_int lowfd;
- fdp = td->td_proc->p_fd;
AUDIT_ARG_FD(uap->lowfd);
/*
* Treat negative starting file descriptor values identical to
* closefrom(0) which closes all files.
*/
- if (uap->lowfd < 0)
- uap->lowfd = 0;
- FILEDESC_SLOCK(fdp);
- for (fd = uap->lowfd; fd <= fdp->fd_lastfile; fd++) {
- if (fdp->fd_ofiles[fd].fde_file != NULL) {
- FILEDESC_SUNLOCK(fdp);
- (void)kern_close(td, fd);
- FILEDESC_SLOCK(fdp);
- }
- }
- FILEDESC_SUNLOCK(fdp);
- return (0);
+ lowfd = MAX(0, uap->lowfd);
+ return (kern_close_range(td, lowfd, ~0U));
}
#if defined(COMPAT_43)
diff --git a/sys/kern/syscalls.master b/sys/kern/syscalls.master
index 8a61610daf067..ec8e03b65ee98 100644
--- a/sys/kern/syscalls.master
+++ b/sys/kern/syscalls.master
@@ -3227,6 +3227,13 @@
int flags
);
}
+575 AUE_NULL STD {
+ int close_range(
+ u_int lowfd,
+ u_int highfd,
+ int flags
+ );
+ }
; Please copy any additions and changes to the following compatability tables:
; sys/compat/freebsd32/syscalls.master
diff --git a/sys/sys/syscallsubr.h b/sys/sys/syscallsubr.h
index 5a7f2169e74d1..1593ef2c49415 100644
--- a/sys/sys/syscallsubr.h
+++ b/sys/sys/syscallsubr.h
@@ -105,6 +105,7 @@ int kern_clock_settime(struct thread *td, clockid_t clock_id,
struct timespec *ats);
void kern_thread_cputime(struct thread *targettd, struct timespec *ats);
void kern_process_cputime(struct proc *targetp, struct timespec *ats);
+int kern_close_range(struct thread *td, u_int lowfd, u_int highfd);
int kern_close(struct thread *td, int fd);
int kern_connectat(struct thread *td, int dirfd, int fd,
struct sockaddr *sa);
diff --git a/tests/sys/file/closefrom_test.c b/tests/sys/file/closefrom_test.c
index 78cfeecae0261..7ce93415519e7 100644
--- a/tests/sys/file/closefrom_test.c
+++ b/tests/sys/file/closefrom_test.c
@@ -146,7 +146,7 @@ main(void)
pid_t pid;
int fd, i, start;
- printf("1..15\n");
+ printf("1..19\n");
/* We better start up with fd's 0, 1, and 2 open. */
start = devnull();
@@ -271,5 +271,43 @@ main(void)
fail("closefrom", "highest fd %d", fd);
ok("closefrom");
+ /* Chew up another 8 fd */
+ for (i = 0; i < 8; i++)
+ (void)devnull();
+ fd = highest_fd();
+ start = fd - 7;
+
+ /* close_range() a hole in the middle */
+ close_range(start + 3, start + 5, 0);
+ for (i = start + 3; i < start + 6; ++i) {
+ if (close(i) == 0 || errno != EBADF) {
+ --i;
+ break;
+ }
+ }
+ if (i != start + 6)
+ fail("close_range", "failed to close at %d in %d - %d", i + 1,
+ start + 3, start + 6);
+ ok("close_range");
+
+ /* close_range from the middle of the hole */
+ close_range(start + 4, start + 6, 0);
+ if ((i = highest_fd()) != fd)
+ fail("close_range", "highest fd %d", i);
+ ok("close_range");
+
+ /* close_range to the end; effectively closefrom(2) */
+ close_range(start + 3, ~0L, 0);
+ if ((i = highest_fd()) != start + 2)
+ fail("close_range", "highest fd %d", i);
+ ok("close_range");
+
+ /* Now close the rest */
+ close_range(start, start + 4, 0);
+ fd = highest_fd();
+ if (fd != 3)
+ fail("close_range", "highest fd %d", fd);
+ ok("close_range");
+
return (0);
}