1/* Close a range of file descriptors. Linux version.
2 Copyright (C) 2021 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/* Fallback code: iterates over /proc/self/fd, closing each file descriptor
25 that fall on the criteria. If DIRFD_FALLBACK is set, a failure on
26 /proc/self/fd open will trigger a fallback that tries to close a file
27 descriptor before proceed. */
28_Bool
29__closefrom_fallback (int from, _Bool dirfd_fallback)
30{
31 bool ret = false;
32
33 int dirfd = __open_nocancel (FD_TO_FILENAME_PREFIX, O_RDONLY | O_DIRECTORY,
34 0);
35 if (dirfd == -1)
36 {
37 /* The closefrom should work even when process can't open new files. */
38 if (errno == ENOENT || !dirfd_fallback)
39 goto err;
40
41 for (int i = from; i < INT_MAX; i++)
42 {
43 int r = __close_nocancel (i);
44 if (r == 0 || (r == -1 && errno != EBADF))
45 break;
46 }
47
48 dirfd = __open_nocancel (FD_TO_FILENAME_PREFIX, O_RDONLY | O_DIRECTORY,
49 0);
50 if (dirfd == -1)
51 goto err;
52 }
53
54 char buffer[1024];
55 while (true)
56 {
57 ssize_t ret = __getdents64 (dirfd, buffer, sizeof (buffer));
58 if (ret == -1)
59 goto err;
60 else if (ret == 0)
61 break;
62
63 /* If any file descriptor is closed it resets the /proc/self position
64 read again from the start (to obtain any possible kernel update). */
65 bool closed = false;
66 char *begin = buffer, *end = buffer + ret;
67 while (begin != end)
68 {
69 unsigned short int d_reclen;
70 memcpy (&d_reclen, begin + offsetof (struct dirent64, d_reclen),
71 sizeof (d_reclen));
72 const char *dname = begin + offsetof (struct dirent64, d_name);
73 begin += d_reclen;
74
75 if (dname[0] == '.')
76 continue;
77
78 int fd = 0;
79 for (const char *s = dname; (unsigned int) (*s) - '0' < 10; s++)
80 fd = 10 * fd + (*s - '0');
81
82 if (fd == dirfd || fd < from)
83 continue;
84
85 /* We ignore close errors because EBADF, EINTR, and EIO means the
86 descriptor has been released. */
87 __close_nocancel (fd);
88 closed = true;
89 }
90
91 if (closed && __lseek (dirfd, 0, SEEK_SET) < 0)
92 goto err;
93 }
94
95 ret = true;
96err:
97 __close_nocancel (dirfd);
98 return ret;
99}
100