1  /* Close a range of file descriptors.  Linux version.
       2     Copyright (C) 2021-2023 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
      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