Index: include/unistd.h =================================================================== --- include/unistd.h +++ include/unistd.h @@ -326,6 +326,9 @@ int chdir(const char *); int chown(const char *, uid_t, gid_t); int close(int); +#if __BSD_VISIBLE +int close_range(unsigned int, unsigned int, int); +#endif void closefrom(int); int dup(int); int dup2(int, int); Index: lib/libc/sys/Makefile.inc =================================================================== --- lib/libc/sys/Makefile.inc +++ lib/libc/sys/Makefile.inc @@ -375,6 +375,7 @@ 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 Index: lib/libc/sys/Symbol.map =================================================================== --- lib/libc/sys/Symbol.map +++ lib/libc/sys/Symbol.map @@ -403,6 +403,7 @@ FBSD_1.6 { __sysctlbyname; + close_range; copy_file_range; fhlink; fhlinkat; Index: lib/libc/sys/closefrom.2 =================================================================== --- lib/libc/sys/closefrom.2 +++ lib/libc/sys/closefrom.2 @@ -29,7 +29,8 @@ .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 @@ .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 Index: sys/compat/freebsd32/syscalls.master =================================================================== --- sys/compat/freebsd32/syscalls.master +++ sys/compat/freebsd32/syscalls.master @@ -1153,5 +1153,7 @@ 570 AUE_SYSCTL STD { int freebsd32___sysctlbyname(const char *name, \ size_t namelen, void *old, uint32_t *oldlenp, \ void *new, size_t newlen); } +571 AUE_NULL NOPROTO { int close_range(u_int lowfd, u_int highfd, \ + int flags); } ; vim: syntax=off Index: sys/kern/capabilities.conf =================================================================== --- sys/kern/capabilities.conf +++ sys/kern/capabilities.conf @@ -126,6 +126,7 @@ ## Always allow file descriptor close(2). ## close +close_range closefrom ## Index: sys/kern/kern_descrip.c =================================================================== --- sys/kern/kern_descrip.c +++ sys/kern/kern_descrip.c @@ -1256,6 +1256,57 @@ 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); + + /* Clamped to [lowfd, fd_lastfile] */ + highfd = MIN(highfd, fdp->fd_lastfile); + if (highfd < lowfd) { + ret = EINVAL; + goto out; + } + 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); + /* + * We won't sanity check lowfd < highfd here because highfd may be + * clamped down to the highest fd opened, which could be lower than + * lowfd while highfd wouldn't be. Let the implementation validate + * the range of fd and return an error as necessary. + */ + return (kern_close_range(td, uap->lowfd, uap->highfd)); +} + /* * Close open file descriptors. */ @@ -1268,28 +1319,16 @@ 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) Index: sys/kern/syscalls.master =================================================================== --- sys/kern/syscalls.master +++ sys/kern/syscalls.master @@ -3194,6 +3194,13 @@ _In_reads_bytes_opt_(newlen) void *new, size_t newlen); } +571 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 Index: sys/sys/syscallsubr.h =================================================================== --- sys/sys/syscallsubr.h +++ sys/sys/syscallsubr.h @@ -91,6 +91,7 @@ const struct timespec *rqtp, struct timespec *rmtp); int kern_clock_settime(struct thread *td, clockid_t clock_id, 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); Index: tests/sys/file/closefrom_test.c =================================================================== --- tests/sys/file/closefrom_test.c +++ tests/sys/file/closefrom_test.c @@ -146,7 +146,7 @@ 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 @@ 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); }