1 /* Close a range of file descriptors. Linux version.
2 Copyright (C) 2021-2022 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
18
19 #include <arch-fd_to_filename.h>
20 #include <dirent.h>
21 #include <not-cancel.h>
22 #include <stdbool.h>
23
24 #if !__ASSUME_CLOSE_RANGE
25
26 /* Fallback code: iterates over /proc/self/fd, closing each file descriptor
27 that fall on the criteria. If DIRFD_FALLBACK is set, a failure on
28 /proc/self/fd open will trigger a fallback that tries to close a file
29 descriptor before proceed. */
30 _Bool
__closefrom_fallback(int from,_Bool dirfd_fallback)31 __closefrom_fallback (int from, _Bool dirfd_fallback)
32 {
33 int dirfd = __open_nocancel (FD_TO_FILENAME_PREFIX, O_RDONLY | O_DIRECTORY,
34 0);
35 if (dirfd == -1)
36 {
37 /* Return if procfs can not be opened for some reason. */
38 if ((errno != EMFILE && errno != ENFILE && errno != ENOMEM)
39 || !dirfd_fallback)
40 return false;
41
42 /* The closefrom should work even when process can't open new files. */
43 for (int i = from; i < INT_MAX; i++)
44 {
45 int r = __close_nocancel (i);
46 if (r == 0 || (r == -1 && errno != EBADF))
47 break;
48 }
49
50 dirfd = __open_nocancel (FD_TO_FILENAME_PREFIX, O_RDONLY | O_DIRECTORY,
51 0);
52 if (dirfd == -1)
53 return false;
54 }
55
56 char buffer[1024];
57 bool ret = false;
58 while (true)
59 {
60 ssize_t ret = __getdents64 (dirfd, buffer, sizeof (buffer));
61 if (ret == -1)
62 goto err;
63 else if (ret == 0)
64 break;
65
66 /* If any file descriptor is closed it resets the /proc/self position
67 read again from the start (to obtain any possible kernel update). */
68 bool closed = false;
69 char *begin = buffer, *end = buffer + ret;
70 while (begin != end)
71 {
72 unsigned short int d_reclen;
73 memcpy (&d_reclen, begin + offsetof (struct dirent64, d_reclen),
74 sizeof (d_reclen));
75 const char *dname = begin + offsetof (struct dirent64, d_name);
76 begin += d_reclen;
77
78 if (dname[0] == '.')
79 continue;
80
81 int fd = 0;
82 for (const char *s = dname; (unsigned int) (*s) - '0' < 10; s++)
83 fd = 10 * fd + (*s - '0');
84
85 if (fd == dirfd || fd < from)
86 continue;
87
88 /* We ignore close errors because EBADF, EINTR, and EIO means the
89 descriptor has been released. */
90 __close_nocancel (fd);
91 closed = true;
92 }
93
94 if (closed && __lseek (dirfd, 0, SEEK_SET) < 0)
95 goto err;
96 }
97
98 ret = true;
99 err:
100 __close_nocancel (dirfd);
101 return ret;
102 }
103
104 #endif
105