/* Close a range of file descriptors. Linux version. Copyright (C) 2021-2024 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 . */ #include #include #include #include #if !__ASSUME_CLOSE_RANGE /* Fallback code: iterates over /proc/self/fd, closing each file descriptor that fall on the criteria. If DIRFD_FALLBACK is set, a failure on /proc/self/fd open will trigger a fallback that tries to close a file descriptor before proceed. */ _Bool __closefrom_fallback (int from, _Bool dirfd_fallback) { int dirfd = __open_nocancel (FD_TO_FILENAME_PREFIX, O_RDONLY | O_DIRECTORY, 0); if (dirfd == -1) { /* Return if procfs can not be opened for some reason. */ if ((errno != EMFILE && errno != ENFILE && errno != ENOMEM) || !dirfd_fallback) return false; /* The closefrom should work even when process can't open new files. */ for (int i = from; i < INT_MAX; i++) { int r = __close_nocancel (i); if (r == 0 || (r == -1 && errno != EBADF)) break; } dirfd = __open_nocancel (FD_TO_FILENAME_PREFIX, O_RDONLY | O_DIRECTORY, 0); if (dirfd == -1) return false; } char buffer[1024]; bool ret = false; while (true) { ssize_t ret = __getdents64 (dirfd, buffer, sizeof (buffer)); if (ret == -1) goto err; else if (ret == 0) break; /* If any file descriptor is closed it resets the /proc/self position read again from the start (to obtain any possible kernel update). */ 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; /* We ignore close errors because EBADF, EINTR, and EIO means the descriptor has been released. */ __close_nocancel (fd); closed = true; } if (closed && __lseek (dirfd, 0, SEEK_SET) < 0) goto err; } ret = true; err: __close_nocancel (dirfd); return ret; } #endif