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