* Adhemerval Zanella via Libc-alpha:
> diff --git a/io/Versions b/io/Versions
> index 49c4d2d40a..fa33452881 100644
> --- a/io/Versions
> +++ b/io/Versions
> @@ -135,6 +135,7 @@ libc {
> GLIBC_2.33 {
> stat; stat64; fstat; fstat64; lstat; lstat64; fstatat; fstatat64;
> mknod; mknodat;
> + closefrom;
> }
Needs to be GLIBC_2.34 now. Copyright years need adjusting, too.
> diff --git a/io/closefrom.c b/io/closefrom.c
> new file mode 100644
> index 0000000000..7833935b16
> --- /dev/null
> +++ b/io/closefrom.c
> +void
> +__closefrom (int lowfd)
> +{
> + int maxfd = __getdtablesize ();
> + if (maxfd == -1)
> + __fortify_fail ("closefrom failed to get the file descriptor table size");
> +
> + for (int i = 0; i < maxfd; i++)
> + if (i >= lowfd)
> + __close_nocancel_nostatus (i);
> +}
Still okay for Hurd.
> +weak_alias (__closefrom, closefrom)
> diff --git a/io/tst-closefrom.c b/io/tst-closefrom.c
> new file mode 100644
> index 0000000000..6f6fbd270f
> --- /dev/null
> +++ b/io/tst-closefrom.c
> +#define NFDS 100
> +
> +static int
> +open_multiple_temp_files (void)
> +{
> + /* Check if the temporary file descriptor has no no gaps. */
> + int lowfd = xopen ("/dev/null", O_RDONLY, 0600);
> + for (int i = 1; i <= NFDS; i++)
> + TEST_COMPARE (xopen ("/dev/null", O_RDONLY, 0600),
> + lowfd + i);
> + return lowfd;
> +}
Spurious line wrap.
> +
> +static int
> +closefrom_test (void)
> +{
> + struct support_descriptors *descrs = support_descriptors_list ();
> +
> + int lowfd = open_multiple_temp_files ();
> +
> + const int maximum_fd = lowfd + NFDS;
> + const int half_fd = maximum_fd / 2;
> + const int gap = maximum_fd / 4;
See the other message about the half_fd initialization.
> +/* Check if closefrom works even when no new file descriptors can be
> + created. */
> +static int
> +closefrom_test_file_desc_limit (void)
> +{
> + int max_fd = NFDS;
> + {
> + struct rlimit rl;
> + if (getrlimit (RLIMIT_NOFILE, &rl) == -1)
> + FAIL_EXIT1 ("getrlimit (RLIMIT_NOFILE): %m");
> +
> + max_fd = (rl.rlim_cur < max_fd ? rl.rlim_cur : max_fd);
> + rl.rlim_cur = max_fd;
> +
> + if (setrlimit (RLIMIT_NOFILE, &rl) == 1)
> + FAIL_EXIT1 ("setrlimit (RLIMIT_NOFILE): %m");
> + }
> +
> + /* Exhauste the file descriptor limit. */
> + int lowfd = xopen ("/dev/null", O_RDONLY, 0600);
> + for (;;)
> + {
> + int fd = open ("/dev/null", O_RDONLY, 0600);
> + if (fd == -1)
> + {
> + if (errno != EMFILE)
> + FAIL_EXIT1 ("create_temp_file: %m");
Wrong error message.
Maybe add TEST_VERIFY_EXIT (fd < max_fd) to the loop? I believe the
setrlimit call will ensure that.
> diff --git a/manual/llio.texi b/manual/llio.texi
> index ceb18ac89a..777993d207 100644
> --- a/manual/llio.texi
> +++ b/manual/llio.texi
> @@ -321,6 +321,14 @@ The maximum number of file descriptors is controlled by the
> @end table
> @end deftypefun
>
> +@deftypefun void closefrom (int @var{lowfd})
> +@standards{GNU, unistd.h}
> +@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{@acsfd{}}}
> +
> +The function @code{closefrom} closes all file descriptors large or equal
> +then @var{lowfd}. This function is similar to call @code{close} in specified
> +file descriptor range.
> +@end deftypefun
“larger than or equal to @var{lowfd}”
“@code{close} applied to the specified file descriptor range”
Please also mention that already-closed descriptors are ignored.
> diff --git a/sysdeps/unix/sysv/linux/closefrom.c b/sysdeps/unix/sysv/linux/closefrom.c
> new file mode 100644
> index 0000000000..ba98fccd39
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/closefrom.c
> +void
> +__closefrom (int lowfd)
> +{
> + int l = MAX (0, lowfd);
> +
> + int r = __close_range (l, ~0U, 0);
> + if (r == 0)
> + return;
> +
> + if (!__closefrom_fallback (l))
> + __fortify_fail ("closefrom failed to close a file descriptor");
> +}
This ignores EPERM. I guess that's okay.
> diff --git a/sysdeps/unix/sysv/linux/closefrom_fallback.c b/sysdeps/unix/sysv/linux/closefrom_fallback.c
> new file mode 100644
> index 0000000000..78182bc5f0
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/closefrom_fallback.c
> @@ -0,0 +1,93 @@
> +/* Fallback code: iterates over /proc/self/fd, closing each file descriptor
> + that fall on the criteria. */
> +_Bool
> +__closefrom_fallback (int from)
> +{
> + bool ret = false;
> +
> + int dirfd = __open_nocancel (FD_TO_FILENAME_PREFIX, O_RDONLY | O_DIRECTORY,
> + 0);
> + if (dirfd == -1)
> + {
> + /* The closefrom should work even when process can't open new files.
> + In this case it loops over RLIMIT_NOFILE / rlim_cur until it frees
> + a file descriptor to iterate over /proc. */
> + if (errno == ENOENT)
> + goto err;
> +
> + int maxfd = __getdtablesize ();
> + for (int i = from; i < maxfd; i++)
> + if (__close_nocancel (i) == 0)
> + break;
This should check for errno != EBADF. EINTR, EIO etc. are okay as well
because a descriptor has been released.
I wouldn't mind iterating up to INT_MAX, removing the __getdtablesize
call. It may take a minute or two, but it removes some of the error
scenarios.
> +
> + dirfd = __open_nocancel (FD_TO_FILENAME_PREFIX, O_RDONLY | O_DIRECTORY,
> + 0);
> + if (dirfd == -1)
> + goto err;
> + }
> +
> + char buffer[1024];
> + while (true)
> + {
> + ssize_t ret = __getdents64 (dirfd, buffer, sizeof (buffer));
> + if (ret == -1)
> + goto err;
> + else if (ret == 0)
> + break;
> +
> + bool closed = false;
Maybe add a comment here about restarting on close?
> + char *begin = buffer, *end = buffer + ret;
> + while (begin != end)
> + {
> + unsigned short int d_reclen;
> + memcpy (&d_reclen, begin + offsetof (struct dirent64, d_reclen),
> + sizeof (d_reclen));
> + const char *dname = begin + offsetof (struct dirent64, d_name);
> + begin += d_reclen;
> +
> + if (dname[0] == '.')
> + continue;
> +
> + int fd = 0;
> + for (const char *s = dname; (unsigned int) (*s) - '0' < 10; s++)
> + fd = 10 * fd + (*s - '0');
Hmm. I had to think a bit about it, but it seems okay whether *s is
signed or not.
> + if (fd == dirfd || fd < from)
> + continue;
> +
> + __close_nocancel (fd);
Maybe add a comment why we aren't adding an error here?
> + closed = true;
> + }
> +
> + if (closed)
> + __lseek (dirfd, 0, SEEK_SET);
> + }
Missing error check?
> + ret = true;
> +err:
> + __close_nocancel (dirfd);
> + return ret;
> +}
The algorithm looks fine to me. Thanks for adding the restart-on-close
part.
Thanks,
Florian
On 09/03/2021 07:23, Florian Weimer wrote:
> * Adhemerval Zanella via Libc-alpha:
>
>> diff --git a/io/Versions b/io/Versions
>> index 49c4d2d40a..fa33452881 100644
>> --- a/io/Versions
>> +++ b/io/Versions
>> @@ -135,6 +135,7 @@ libc {
>> GLIBC_2.33 {
>> stat; stat64; fstat; fstat64; lstat; lstat64; fstatat; fstatat64;
>> mknod; mknodat;
>> + closefrom;
>> }
>
> Needs to be GLIBC_2.34 now. Copyright years need adjusting, too.
Ack.
>
>> diff --git a/io/closefrom.c b/io/closefrom.c
>> new file mode 100644
>> index 0000000000..7833935b16
>> --- /dev/null
>> +++ b/io/closefrom.c
>
>> +void
>> +__closefrom (int lowfd)
>> +{
>> + int maxfd = __getdtablesize ();
>> + if (maxfd == -1)
>> + __fortify_fail ("closefrom failed to get the file descriptor table size");
>> +
>> + for (int i = 0; i < maxfd; i++)
>> + if (i >= lowfd)
>> + __close_nocancel_nostatus (i);
>> +}
>
> Still okay for Hurd.
>
>> +weak_alias (__closefrom, closefrom)
>> diff --git a/io/tst-closefrom.c b/io/tst-closefrom.c
>> new file mode 100644
>> index 0000000000..6f6fbd270f
>> --- /dev/null
>> +++ b/io/tst-closefrom.c
>
>> +#define NFDS 100
>> +
>> +static int
>> +open_multiple_temp_files (void)
>> +{
>> + /* Check if the temporary file descriptor has no no gaps. */
>> + int lowfd = xopen ("/dev/null", O_RDONLY, 0600);
>> + for (int i = 1; i <= NFDS; i++)
>> + TEST_COMPARE (xopen ("/dev/null", O_RDONLY, 0600),
>> + lowfd + i);
>> + return lowfd;
>> +}
>
> Spurious line wrap.
Ack.
>
>> +
>> +static int
>> +closefrom_test (void)
>> +{
>> + struct support_descriptors *descrs = support_descriptors_list ();
>> +
>> + int lowfd = open_multiple_temp_files ();
>> +
>> + const int maximum_fd = lowfd + NFDS;
>> + const int half_fd = maximum_fd / 2;
>> + const int gap = maximum_fd / 4;
>
> See the other message about the half_fd initialization.
Ack.
>
>> +/* Check if closefrom works even when no new file descriptors can be
>> + created. */
>> +static int
>> +closefrom_test_file_desc_limit (void)
>> +{
>> + int max_fd = NFDS;
>> + {
>> + struct rlimit rl;
>> + if (getrlimit (RLIMIT_NOFILE, &rl) == -1)
>> + FAIL_EXIT1 ("getrlimit (RLIMIT_NOFILE): %m");
>> +
>> + max_fd = (rl.rlim_cur < max_fd ? rl.rlim_cur : max_fd);
>> + rl.rlim_cur = max_fd;
>> +
>> + if (setrlimit (RLIMIT_NOFILE, &rl) == 1)
>> + FAIL_EXIT1 ("setrlimit (RLIMIT_NOFILE): %m");
>> + }
>> +
>> + /* Exhauste the file descriptor limit. */
>> + int lowfd = xopen ("/dev/null", O_RDONLY, 0600);
>> + for (;;)
>> + {
>> + int fd = open ("/dev/null", O_RDONLY, 0600);
>> + if (fd == -1)
>> + {
>> + if (errno != EMFILE)
>> + FAIL_EXIT1 ("create_temp_file: %m");
>
> Wrong error message.
>
> Maybe add TEST_VERIFY_EXIT (fd < max_fd) to the loop? I believe the
> setrlimit call will ensure that.
Ack.
>
>> diff --git a/manual/llio.texi b/manual/llio.texi
>> index ceb18ac89a..777993d207 100644
>> --- a/manual/llio.texi
>> +++ b/manual/llio.texi
>> @@ -321,6 +321,14 @@ The maximum number of file descriptors is controlled by the
>> @end table
>> @end deftypefun
>>
>> +@deftypefun void closefrom (int @var{lowfd})
>> +@standards{GNU, unistd.h}
>> +@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{@acsfd{}}}
>> +
>> +The function @code{closefrom} closes all file descriptors large or equal
>> +then @var{lowfd}. This function is similar to call @code{close} in specified
>> +file descriptor range.
>> +@end deftypefun
>
> “larger than or equal to @var{lowfd}”
>
> “@code{close} applied to the specified file descriptor range”
>
> Please also mention that already-closed descriptors are ignored.
Ack.
>
>> diff --git a/sysdeps/unix/sysv/linux/closefrom.c b/sysdeps/unix/sysv/linux/closefrom.c
>> new file mode 100644
>> index 0000000000..ba98fccd39
>> --- /dev/null
>> +++ b/sysdeps/unix/sysv/linux/closefrom.c
>> +void
>> +__closefrom (int lowfd)
>> +{
>> + int l = MAX (0, lowfd);
>> +
>> + int r = __close_range (l, ~0U, 0);
>> + if (r == 0)
>> + return;
>> +
>> + if (!__closefrom_fallback (l))
>> + __fortify_fail ("closefrom failed to close a file descriptor");
>> +}
>
> This ignores EPERM. I guess that's okay.
Ack.
>
>> diff --git a/sysdeps/unix/sysv/linux/closefrom_fallback.c b/sysdeps/unix/sysv/linux/closefrom_fallback.c
>> new file mode 100644
>> index 0000000000..78182bc5f0
>> --- /dev/null
>> +++ b/sysdeps/unix/sysv/linux/closefrom_fallback.c
>> @@ -0,0 +1,93 @@
>
>> +/* Fallback code: iterates over /proc/self/fd, closing each file descriptor
>> + that fall on the criteria. */
>> +_Bool
>> +__closefrom_fallback (int from)
>> +{
>> + bool ret = false;
>> +
>> + int dirfd = __open_nocancel (FD_TO_FILENAME_PREFIX, O_RDONLY | O_DIRECTORY,
>> + 0);
>> + if (dirfd == -1)
>> + {
>> + /* The closefrom should work even when process can't open new files.
>> + In this case it loops over RLIMIT_NOFILE / rlim_cur until it frees
>> + a file descriptor to iterate over /proc. */
>> + if (errno == ENOENT)
>> + goto err;
>> +
>> + int maxfd = __getdtablesize ();
>> + for (int i = from; i < maxfd; i++)
>> + if (__close_nocancel (i) == 0)
>> + break;
>
> This should check for errno != EBADF. EINTR, EIO etc. are okay as well
> because a descriptor has been released.
Ack.
>
> I wouldn't mind iterating up to INT_MAX, removing the __getdtablesize
> call. It may take a minute or two, but it removes some of the error
> scenarios.
Ok, I think it should be ok (the __getdtablesize call should be issue
only when open fails).
>
>> +
>> + dirfd = __open_nocancel (FD_TO_FILENAME_PREFIX, O_RDONLY | O_DIRECTORY,
>> + 0);
>> + if (dirfd == -1)
>> + goto err;
>> + }
>> +
>> + char buffer[1024];
>> + while (true)
>> + {
>> + ssize_t ret = __getdents64 (dirfd, buffer, sizeof (buffer));
>> + if (ret == -1)
>> + goto err;
>> + else if (ret == 0)
>> + break;
>> +
>> + bool closed = false;
>
> Maybe add a comment here about restarting on close?
Ack.
>
>> + char *begin = buffer, *end = buffer + ret;
>> + while (begin != end)
>> + {
>> + unsigned short int d_reclen;
>> + memcpy (&d_reclen, begin + offsetof (struct dirent64, d_reclen),
>> + sizeof (d_reclen));
>> + const char *dname = begin + offsetof (struct dirent64, d_name);
>> + begin += d_reclen;
>> +
>> + if (dname[0] == '.')
>> + continue;
>> +
>> + int fd = 0;
>> + for (const char *s = dname; (unsigned int) (*s) - '0' < 10; s++)
>> + fd = 10 * fd + (*s - '0');
>
> Hmm. I had to think a bit about it, but it seems okay whether *s is
> signed or not.>
>> + if (fd == dirfd || fd < from)
>> + continue;
>> +
>> + __close_nocancel (fd);
>
> Maybe add a comment why we aren't adding an error here?
Ack.
>
>> + closed = true;
>> + }
>> +
>> + if (closed)
>> + __lseek (dirfd, 0, SEEK_SET);
>> + }
>
> Missing error check?
Ack, lseek failure should be reported.
>
>> + ret = true;
>> +err:
>> + __close_nocancel (dirfd);
>> + return ret;
>> +}
>
> The algorithm looks fine to me. Thanks for adding the restart-on-close
> part.
>
> Thanks,
> Florian
>
@@ -30,6 +30,10 @@ Major new features:
* On Linux, the close_range function has been added.
+* The function closefrom has been added. It closes all file descriptors
+ greater than given integer. This function is a GNU extension, although it
+ also present in other systems.
+
Deprecated and removed features, and other changes affecting compatibility:
* The mallinfo function is marked deprecated. Callers should call
@@ -153,6 +153,7 @@ extern int __brk (void *__addr) attribute_hidden;
extern int __close (int __fd);
libc_hidden_proto (__close)
extern int __libc_close (int __fd);
+extern _Bool __closefrom_fallback (int __lowfd) attribute_hidden;
extern ssize_t __read (int __fd, void *__buf, size_t __nbytes);
libc_hidden_proto (__read)
extern ssize_t __write (int __fd, const void *__buf, size_t __n);
@@ -55,7 +55,8 @@ routines := \
posix_fadvise posix_fadvise64 \
posix_fallocate posix_fallocate64 \
sendfile sendfile64 copy_file_range \
- utimensat futimens file_change_detection
+ utimensat futimens file_change_detection \
+ closefrom
others := pwd
test-srcs := ftwtest
@@ -69,7 +70,7 @@ tests := test-utime test-stat test-stat2 test-lfs tst-getcwd \
tst-fts tst-fts-lfs tst-open-tmpfile \
tst-copy_file_range tst-getcwd-abspath tst-lockf \
tst-ftw-lnk tst-file_change_detection tst-lchmod \
- tst-ftw-bz26353
+ tst-ftw-bz26353 tst-closefrom
# Likewise for statx, but we do not need static linking here.
tests-internal += tst-statx
@@ -135,6 +135,7 @@ libc {
GLIBC_2.33 {
stat; stat64; fstat; fstat64; lstat; lstat64; fstatat; fstatat64;
mknod; mknodat;
+ closefrom;
}
GLIBC_PRIVATE {
__libc_fcntl64;
new file mode 100644
@@ -0,0 +1,34 @@
+/* Close a range of file descriptors.
+ Copyright (C) 2020 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <not-cancel.h>
+
+void
+__closefrom (int lowfd)
+{
+ int maxfd = __getdtablesize ();
+ if (maxfd == -1)
+ __fortify_fail ("closefrom failed to get the file descriptor table size");
+
+ for (int i = 0; i < maxfd; i++)
+ if (i >= lowfd)
+ __close_nocancel_nostatus (i);
+}
+weak_alias (__closefrom, closefrom)
new file mode 100644
@@ -0,0 +1,152 @@
+/* Smoke test for the closefrom.
+ Copyright (C) 2020 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/resource.h>
+#include <unistd.h>
+
+#include <support/check.h>
+#include <support/descriptors.h>
+#include <support/xunistd.h>
+
+#include <array_length.h>
+
+#define NFDS 100
+
+static int
+open_multiple_temp_files (void)
+{
+ /* Check if the temporary file descriptor has no no gaps. */
+ int lowfd = xopen ("/dev/null", O_RDONLY, 0600);
+ for (int i = 1; i <= NFDS; i++)
+ TEST_COMPARE (xopen ("/dev/null", O_RDONLY, 0600),
+ lowfd + i);
+ return lowfd;
+}
+
+static int
+closefrom_test (void)
+{
+ struct support_descriptors *descrs = support_descriptors_list ();
+
+ int lowfd = open_multiple_temp_files ();
+
+ const int maximum_fd = lowfd + NFDS;
+ const int half_fd = maximum_fd / 2;
+ const int gap = maximum_fd / 4;
+
+ /* Close half of the descriptors and check result. */
+ closefrom (half_fd);
+
+ for (int i = half_fd; i <= maximum_fd; i++)
+ {
+ TEST_COMPARE (fcntl (i, F_GETFL), -1);
+ TEST_COMPARE (errno, EBADF);
+ }
+ for (int i = 0; i < half_fd; i++)
+ TEST_VERIFY (fcntl (i, F_GETFL) > -1);
+
+ /* Create some gaps, close up to a threshold, and check result. */
+ xclose (lowfd + 35);
+ xclose (lowfd + 38);
+ xclose (lowfd + 42);
+ xclose (lowfd + 46);
+
+ /* Close half of the descriptors and check result. */
+ closefrom (gap);
+ for (int i = gap + 1; i < maximum_fd; i++)
+ {
+ TEST_COMPARE (fcntl (i, F_GETFL), -1);
+ TEST_COMPARE (errno, EBADF);
+ }
+ for (int i = 0; i < gap; i++)
+ TEST_VERIFY (fcntl (i, F_GETFL) > -1);
+
+ /* Close the remmaining but the last one. */
+ closefrom (lowfd + 1);
+ for (int i = lowfd + 1; i <= maximum_fd; i++)
+ {
+ TEST_COMPARE (fcntl (i, F_GETFL), -1);
+ TEST_COMPARE (errno, EBADF);
+ }
+ TEST_VERIFY (fcntl (lowfd, F_GETFL) > -1);
+
+ /* Close the last one. */
+ closefrom (lowfd);
+ TEST_COMPARE (fcntl (lowfd, F_GETFL), -1);
+ TEST_COMPARE (errno, EBADF);
+
+ /* Double check by check the /proc. */
+ support_descriptors_check (descrs);
+ support_descriptors_free (descrs);
+
+ return 0;
+}
+
+/* Check if closefrom works even when no new file descriptors can be
+ created. */
+static int
+closefrom_test_file_desc_limit (void)
+{
+ int max_fd = NFDS;
+ {
+ struct rlimit rl;
+ if (getrlimit (RLIMIT_NOFILE, &rl) == -1)
+ FAIL_EXIT1 ("getrlimit (RLIMIT_NOFILE): %m");
+
+ max_fd = (rl.rlim_cur < max_fd ? rl.rlim_cur : max_fd);
+ rl.rlim_cur = max_fd;
+
+ if (setrlimit (RLIMIT_NOFILE, &rl) == 1)
+ FAIL_EXIT1 ("setrlimit (RLIMIT_NOFILE): %m");
+ }
+
+ /* Exhauste the file descriptor limit. */
+ int lowfd = xopen ("/dev/null", O_RDONLY, 0600);
+ for (;;)
+ {
+ int fd = open ("/dev/null", O_RDONLY, 0600);
+ if (fd == -1)
+ {
+ if (errno != EMFILE)
+ FAIL_EXIT1 ("create_temp_file: %m");
+ break;
+ }
+ }
+
+ closefrom (lowfd);
+ for (int i = lowfd; i < NFDS; i++)
+ {
+ TEST_COMPARE (fcntl (i, F_GETFL), -1);
+ TEST_COMPARE (errno, EBADF);
+ }
+
+ return 0;
+}
+
+static int
+do_test (void)
+{
+ closefrom_test ();
+ closefrom_test_file_desc_limit ();
+
+ return 0;
+}
+
+#include <support/test-driver.c>
@@ -321,6 +321,14 @@ The maximum number of file descriptors is controlled by the
@end table
@end deftypefun
+@deftypefun void closefrom (int @var{lowfd})
+@standards{GNU, unistd.h}
+@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{@acsfd{}}}
+
+The function @code{closefrom} closes all file descriptors large or equal
+then @var{lowfd}. This function is similar to call @code{close} in specified
+file descriptor range.
+@end deftypefun
@node I/O Primitives
@section Input and Output Primitives
@@ -352,6 +352,12 @@ extern __off64_t lseek64 (int __fd, __off64_t __offset, int __whence)
__THROW. */
extern int close (int __fd);
+#ifdef __USE_MISC
+/* Close all open file descriptors greater than or equal to LOWFD.
+ Negative LOWFD is clamped to 0. */
+extern void closefrom (int __lowfd) __THROW;
+#endif
+
/* Read NBYTES into BUF from FD. Return the
number read, -1 for errors or 0 for EOF.
@@ -2191,6 +2191,7 @@ GLIBC_2.32 thrd_current F
GLIBC_2.32 thrd_equal F
GLIBC_2.32 thrd_sleep F
GLIBC_2.32 thrd_yield F
+GLIBC_2.33 closefrom F
GLIBC_2.33 fstat F
GLIBC_2.33 fstat64 F
GLIBC_2.33 fstatat F
@@ -61,7 +61,7 @@ sysdep_routines += adjtimex clone umount umount2 readahead sysctl \
open_by_handle_at mlock2 pkey_mprotect pkey_set pkey_get \
timerfd_gettime timerfd_settime prctl \
process_vm_readv process_vm_writev clock_adjtime \
- time64-support pselect32 close_range
+ time64-support pselect32 close_range closefrom_fallback
CFLAGS-gethostid.c = -fexceptions
CFLAGS-tee.c = -fexceptions -fasynchronous-unwind-tables
@@ -2161,6 +2161,7 @@ GLIBC_2.32 sigdescr_np F
GLIBC_2.32 strerrordesc_np F
GLIBC_2.32 strerrorname_np F
GLIBC_2.33 close_range F
+GLIBC_2.33 closefrom F
GLIBC_2.33 fstat F
GLIBC_2.33 fstat64 F
GLIBC_2.33 fstatat F
@@ -2243,6 +2243,7 @@ GLIBC_2.32 sigdescr_np F
GLIBC_2.32 strerrordesc_np F
GLIBC_2.32 strerrorname_np F
GLIBC_2.33 close_range F
+GLIBC_2.33 closefrom F
GLIBC_2.33 fstat F
GLIBC_2.33 fstat64 F
GLIBC_2.33 fstatat F
@@ -1921,6 +1921,7 @@ GLIBC_2.32 write F
GLIBC_2.32 writev F
GLIBC_2.32 wscanf F
GLIBC_2.33 close_range F
+GLIBC_2.33 closefrom F
GLIBC_2.33 fstat F
GLIBC_2.33 fstat64 F
GLIBC_2.33 fstatat F
@@ -144,6 +144,7 @@ GLIBC_2.32 sigabbrev_np F
GLIBC_2.32 sigdescr_np F
GLIBC_2.32 strerrordesc_np F
GLIBC_2.32 strerrorname_np F
+GLIBC_2.33 closefrom F
GLIBC_2.33 fstat F
GLIBC_2.33 fstat64 F
GLIBC_2.33 fstatat F
@@ -142,6 +142,7 @@ GLIBC_2.32 sigdescr_np F
GLIBC_2.32 strerrordesc_np F
GLIBC_2.32 strerrorname_np F
GLIBC_2.33 close_range F
+GLIBC_2.33 closefrom F
GLIBC_2.33 fstat F
GLIBC_2.33 fstat64 F
GLIBC_2.33 fstatat F
new file mode 100644
@@ -0,0 +1,35 @@
+/* Close a range of file descriptors. Linux version.
+ Copyright (C) 2020 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include <sys/param.h>
+#include <unistd.h>
+
+void
+__closefrom (int lowfd)
+{
+ int l = MAX (0, lowfd);
+
+ int r = __close_range (l, ~0U, 0);
+ if (r == 0)
+ return;
+
+ if (!__closefrom_fallback (l))
+ __fortify_fail ("closefrom failed to close a file descriptor");
+}
+weak_alias (__closefrom, closefrom)
new file mode 100644
@@ -0,0 +1,93 @@
+/* Close a range of file descriptors. Linux version.
+ Copyright (C) 2020 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <arch-fd_to_filename.h>
+#include <dirent.h>
+#include <not-cancel.h>
+#include <stdbool.h>
+
+/* Fallback code: iterates over /proc/self/fd, closing each file descriptor
+ that fall on the criteria. */
+_Bool
+__closefrom_fallback (int from)
+{
+ bool ret = false;
+
+ int dirfd = __open_nocancel (FD_TO_FILENAME_PREFIX, O_RDONLY | O_DIRECTORY,
+ 0);
+ if (dirfd == -1)
+ {
+ /* The closefrom should work even when process can't open new files.
+ In this case it loops over RLIMIT_NOFILE / rlim_cur until it frees
+ a file descriptor to iterate over /proc. */
+ if (errno == ENOENT)
+ goto err;
+
+ int maxfd = __getdtablesize ();
+ for (int i = from; i < maxfd; i++)
+ if (__close_nocancel (i) == 0)
+ break;
+
+ dirfd = __open_nocancel (FD_TO_FILENAME_PREFIX, O_RDONLY | O_DIRECTORY,
+ 0);
+ if (dirfd == -1)
+ goto err;
+ }
+
+ char buffer[1024];
+ while (true)
+ {
+ ssize_t ret = __getdents64 (dirfd, buffer, sizeof (buffer));
+ if (ret == -1)
+ goto err;
+ else if (ret == 0)
+ break;
+
+ bool closed = false;
+ char *begin = buffer, *end = buffer + ret;
+ while (begin != end)
+ {
+ unsigned short int d_reclen;
+ memcpy (&d_reclen, begin + offsetof (struct dirent64, d_reclen),
+ sizeof (d_reclen));
+ const char *dname = begin + offsetof (struct dirent64, d_name);
+ begin += d_reclen;
+
+ if (dname[0] == '.')
+ continue;
+
+ int fd = 0;
+ for (const char *s = dname; (unsigned int) (*s) - '0' < 10; s++)
+ fd = 10 * fd + (*s - '0');
+
+ if (fd == dirfd || fd < from)
+ continue;
+
+ __close_nocancel (fd);
+ closed = true;
+ }
+
+ if (closed)
+ __lseek (dirfd, 0, SEEK_SET);
+ }
+
+ ret = true;
+err:
+ __close_nocancel (dirfd);
+ return ret;
+}
@@ -2105,6 +2105,7 @@ GLIBC_2.32 sigdescr_np F
GLIBC_2.32 strerrordesc_np F
GLIBC_2.32 strerrorname_np F
GLIBC_2.33 close_range F
+GLIBC_2.33 closefrom F
GLIBC_2.33 fstat F
GLIBC_2.33 fstat64 F
GLIBC_2.33 fstatat F
@@ -2064,6 +2064,7 @@ GLIBC_2.32 sigdescr_np F
GLIBC_2.32 strerrordesc_np F
GLIBC_2.32 strerrorname_np F
GLIBC_2.33 close_range F
+GLIBC_2.33 closefrom F
GLIBC_2.33 fstat F
GLIBC_2.33 fstat64 F
GLIBC_2.33 fstatat F
@@ -2230,6 +2230,7 @@ GLIBC_2.32 sigdescr_np F
GLIBC_2.32 strerrordesc_np F
GLIBC_2.32 strerrorname_np F
GLIBC_2.33 close_range F
+GLIBC_2.33 closefrom F
GLIBC_2.33 fstat F
GLIBC_2.33 fstat64 F
GLIBC_2.33 fstatat F
@@ -2096,6 +2096,7 @@ GLIBC_2.32 sigdescr_np F
GLIBC_2.32 strerrordesc_np F
GLIBC_2.32 strerrorname_np F
GLIBC_2.33 close_range F
+GLIBC_2.33 closefrom F
GLIBC_2.33 fstat F
GLIBC_2.33 fstat64 F
GLIBC_2.33 fstatat F
@@ -145,6 +145,7 @@ GLIBC_2.32 sigabbrev_np F
GLIBC_2.32 sigdescr_np F
GLIBC_2.32 strerrordesc_np F
GLIBC_2.32 strerrorname_np F
+GLIBC_2.33 closefrom F
GLIBC_2.33 fstat F
GLIBC_2.33 fstat64 F
GLIBC_2.33 fstatat F
@@ -2176,6 +2176,7 @@ GLIBC_2.32 sigdescr_np F
GLIBC_2.32 strerrordesc_np F
GLIBC_2.32 strerrorname_np F
GLIBC_2.33 close_range F
+GLIBC_2.33 closefrom F
GLIBC_2.33 fstat F
GLIBC_2.33 fstat64 F
GLIBC_2.33 fstatat F
@@ -2156,6 +2156,7 @@ GLIBC_2.32 sigdescr_np F
GLIBC_2.32 strerrordesc_np F
GLIBC_2.32 strerrorname_np F
GLIBC_2.33 close_range F
+GLIBC_2.33 closefrom F
GLIBC_2.33 fstat F
GLIBC_2.33 fstat64 F
GLIBC_2.33 fstatat F
@@ -2152,6 +2152,7 @@ GLIBC_2.32 sigabbrev_np F
GLIBC_2.32 sigdescr_np F
GLIBC_2.32 strerrordesc_np F
GLIBC_2.32 strerrorname_np F
+GLIBC_2.33 closefrom F
GLIBC_2.33 fstat F
GLIBC_2.33 fstat64 F
GLIBC_2.33 fstatat F
@@ -2147,6 +2147,7 @@ GLIBC_2.32 sigdescr_np F
GLIBC_2.32 strerrordesc_np F
GLIBC_2.32 strerrorname_np F
GLIBC_2.33 close_range F
+GLIBC_2.33 closefrom F
GLIBC_2.33 fstat F
GLIBC_2.33 fstat64 F
GLIBC_2.33 fstatat F
@@ -2144,6 +2144,7 @@ GLIBC_2.32 sigabbrev_np F
GLIBC_2.32 sigdescr_np F
GLIBC_2.32 strerrordesc_np F
GLIBC_2.32 strerrorname_np F
+GLIBC_2.33 closefrom F
GLIBC_2.33 fstat F
GLIBC_2.33 fstat64 F
GLIBC_2.33 fstatat F
@@ -2153,6 +2153,7 @@ GLIBC_2.32 sigdescr_np F
GLIBC_2.32 strerrordesc_np F
GLIBC_2.32 strerrorname_np F
GLIBC_2.33 close_range F
+GLIBC_2.33 closefrom F
GLIBC_2.33 fstat F
GLIBC_2.33 fstat64 F
GLIBC_2.33 fstatat F
@@ -2147,6 +2147,7 @@ GLIBC_2.32 sigdescr_np F
GLIBC_2.32 strerrordesc_np F
GLIBC_2.32 strerrorname_np F
GLIBC_2.33 close_range F
+GLIBC_2.33 closefrom F
GLIBC_2.33 fstat F
GLIBC_2.33 fstat64 F
GLIBC_2.33 fstatat F
@@ -2194,6 +2194,7 @@ GLIBC_2.32 sigdescr_np F
GLIBC_2.32 strerrordesc_np F
GLIBC_2.32 strerrorname_np F
GLIBC_2.33 close_range F
+GLIBC_2.33 closefrom F
GLIBC_2.33 fstat F
GLIBC_2.33 fstat64 F
GLIBC_2.33 fstatat F
@@ -2203,6 +2203,7 @@ GLIBC_2.32 sigdescr_np F
GLIBC_2.32 strerrordesc_np F
GLIBC_2.32 strerrorname_np F
GLIBC_2.33 close_range F
+GLIBC_2.33 closefrom F
GLIBC_2.33 fstat F
GLIBC_2.33 fstat64 F
GLIBC_2.33 fstatat F
@@ -2235,6 +2235,7 @@ GLIBC_2.32 sigabbrev_np F
GLIBC_2.32 sigdescr_np F
GLIBC_2.32 strerrordesc_np F
GLIBC_2.32 strerrorname_np F
+GLIBC_2.33 closefrom F
GLIBC_2.33 fstat F
GLIBC_2.33 fstat64 F
GLIBC_2.33 fstatat F
@@ -2066,6 +2066,7 @@ GLIBC_2.32 sigdescr_np F
GLIBC_2.32 strerrordesc_np F
GLIBC_2.32 strerrorname_np F
GLIBC_2.33 close_range F
+GLIBC_2.33 closefrom F
GLIBC_2.33 fstat F
GLIBC_2.33 fstat64 F
GLIBC_2.33 fstatat F
@@ -2356,6 +2356,7 @@ GLIBC_2.32 sigdescr_np F
GLIBC_2.32 strerrordesc_np F
GLIBC_2.32 strerrorname_np F
GLIBC_2.33 close_range F
+GLIBC_2.33 closefrom F
GLIBC_2.33 fstat F
GLIBC_2.33 fstat64 F
GLIBC_2.33 fstatat F
@@ -597,6 +597,7 @@ GLIBC_2.33 clone F
GLIBC_2.33 close F
GLIBC_2.33 close_range F
GLIBC_2.33 closedir F
+GLIBC_2.33 closefrom F
GLIBC_2.33 closelog F
GLIBC_2.33 confstr F
GLIBC_2.33 connect F
@@ -2123,6 +2123,7 @@ GLIBC_2.32 sigdescr_np F
GLIBC_2.32 strerrordesc_np F
GLIBC_2.32 strerrorname_np F
GLIBC_2.33 close_range F
+GLIBC_2.33 closefrom F
GLIBC_2.33 fstat F
GLIBC_2.33 fstat64 F
GLIBC_2.33 fstatat F
@@ -2201,6 +2201,7 @@ GLIBC_2.32 sigdescr_np F
GLIBC_2.32 strerrordesc_np F
GLIBC_2.32 strerrorname_np F
GLIBC_2.33 close_range F
+GLIBC_2.33 closefrom F
GLIBC_2.33 fstat F
GLIBC_2.33 fstat64 F
GLIBC_2.33 fstatat F
@@ -2102,6 +2102,7 @@ GLIBC_2.32 sigdescr_np F
GLIBC_2.32 strerrordesc_np F
GLIBC_2.32 strerrorname_np F
GLIBC_2.33 close_range F
+GLIBC_2.33 closefrom F
GLIBC_2.33 fstat F
GLIBC_2.33 fstat64 F
GLIBC_2.33 fstatat F
@@ -2070,6 +2070,7 @@ GLIBC_2.32 sigabbrev_np F
GLIBC_2.32 sigdescr_np F
GLIBC_2.32 strerrordesc_np F
GLIBC_2.32 strerrorname_np F
+GLIBC_2.33 closefrom F
GLIBC_2.33 fstat F
GLIBC_2.33 fstat64 F
GLIBC_2.33 fstatat F
@@ -2068,6 +2068,7 @@ GLIBC_2.32 sigdescr_np F
GLIBC_2.32 strerrordesc_np F
GLIBC_2.32 strerrorname_np F
GLIBC_2.33 close_range F
+GLIBC_2.33 closefrom F
GLIBC_2.33 fstat F
GLIBC_2.33 fstat64 F
GLIBC_2.33 fstatat F
@@ -2192,6 +2192,7 @@ GLIBC_2.32 sigdescr_np F
GLIBC_2.32 strerrordesc_np F
GLIBC_2.32 strerrorname_np F
GLIBC_2.33 close_range F
+GLIBC_2.33 closefrom F
GLIBC_2.33 fstat F
GLIBC_2.33 fstat64 F
GLIBC_2.33 fstatat F
@@ -2119,6 +2119,7 @@ GLIBC_2.32 sigdescr_np F
GLIBC_2.32 strerrordesc_np F
GLIBC_2.32 strerrorname_np F
GLIBC_2.33 close_range F
+GLIBC_2.33 closefrom F
GLIBC_2.33 fstat F
GLIBC_2.33 fstat64 F
GLIBC_2.33 fstatat F
@@ -2077,6 +2077,7 @@ GLIBC_2.32 sigdescr_np F
GLIBC_2.32 strerrordesc_np F
GLIBC_2.32 strerrorname_np F
GLIBC_2.33 close_range F
+GLIBC_2.33 closefrom F
GLIBC_2.33 fstat F
GLIBC_2.33 fstat64 F
GLIBC_2.33 fstatat F
@@ -2174,6 +2174,7 @@ GLIBC_2.32 sigdescr_np F
GLIBC_2.32 strerrordesc_np F
GLIBC_2.32 strerrorname_np F
GLIBC_2.33 close_range F
+GLIBC_2.33 closefrom F
GLIBC_2.33 fstat F
GLIBC_2.33 fstat64 F
GLIBC_2.33 fstatat F