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 #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   bool ret = false;
34 
35   int dirfd = __open_nocancel (FD_TO_FILENAME_PREFIX, O_RDONLY | O_DIRECTORY,
36                                0);
37   if (dirfd == -1)
38     {
39       /* The closefrom should work even when process can't open new files.  */
40       if (errno == ENOENT || !dirfd_fallback)
41         goto err;
42 
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   while (true)
58     {
59       ssize_t ret = __getdents64 (dirfd, buffer, sizeof (buffer));
60       if (ret == -1)
61         goto err;
62       else if (ret == 0)
63         break;
64 
65       /* If any file descriptor is closed it resets the /proc/self position
66          read again from the start (to obtain any possible kernel update).  */
67       bool closed = false;
68       char *begin = buffer, *end = buffer + ret;
69       while (begin != end)
70         {
71           unsigned short int d_reclen;
72           memcpy (&d_reclen, begin + offsetof (struct dirent64, d_reclen),
73                   sizeof (d_reclen));
74           const char *dname = begin + offsetof (struct dirent64, d_name);
75           begin += d_reclen;
76 
77           if (dname[0] == '.')
78             continue;
79 
80           int fd = 0;
81           for (const char *s = dname; (unsigned int) (*s) - '0' < 10; s++)
82             fd = 10 * fd + (*s - '0');
83 
84           if (fd == dirfd || fd < from)
85             continue;
86 
87           /* We ignore close errors because EBADF, EINTR, and EIO means the
88              descriptor has been released.  */
89           __close_nocancel (fd);
90           closed = true;
91         }
92 
93       if (closed && __lseek (dirfd, 0, SEEK_SET) < 0)
94         goto err;
95     }
96 
97   ret = true;
98 err:
99   __close_nocancel (dirfd);
100   return ret;
101 }
102 
103 #endif
104