(root)/
glibc-2.38/
support/
support-open-dev-null-range.c
       1  /* Return a range of open file descriptors.
       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 <errno.h>
      20  #include <fcntl.h>
      21  #include <support/support.h>
      22  #include <support/check.h>
      23  #include <support/xunistd.h>
      24  #include <stdlib.h>
      25  #include <sys/resource.h>
      26  
      27  static void
      28  increase_nofile (void)
      29  {
      30    struct rlimit rl;
      31    if (getrlimit (RLIMIT_NOFILE, &rl) == -1)
      32      FAIL_EXIT1 ("getrlimit (RLIMIT_NOFILE): %m");
      33  
      34    rl.rlim_cur += 128;
      35  
      36    if (setrlimit (RLIMIT_NOFILE, &rl) == 1)
      37      FAIL_EXIT1 ("setrlimit (RLIMIT_NOFILE): %m");
      38  }
      39  
      40  static int
      41  open_dev_null (int flags, mode_t mode)
      42  {
      43    int fd = open64 ("/dev/null", flags, mode);
      44    if (fd >= 0)
      45      return fd;
      46  
      47    if (fd < 0 && errno != EMFILE)
      48      FAIL_EXIT1 ("open64 (\"/dev/null\", 0x%x, 0%o): %m", flags, mode);
      49  
      50    increase_nofile ();
      51  
      52    return xopen ("/dev/null", flags, mode);
      53  }
      54  
      55  struct range
      56  {
      57    int lowfd;
      58    size_t len;
      59  };
      60  
      61  struct range_list
      62  {
      63    size_t total;
      64    size_t used;
      65    struct range *ranges;
      66  };
      67  
      68  static void
      69  range_init (struct range_list *r)
      70  {
      71    r->total = 8;
      72    r->used = 0;
      73    r->ranges = xmalloc (r->total * sizeof (struct range));
      74  }
      75  
      76  static void
      77  range_add (struct range_list *r, int lowfd, size_t len)
      78  {
      79    if (r->used == r->total)
      80      {
      81        r->total *= 2;
      82        r->ranges = xrealloc (r->ranges, r->total * sizeof (struct range));
      83      }
      84    r->ranges[r->used].lowfd = lowfd;
      85    r->ranges[r->used].len = len;
      86    r->used++;
      87  }
      88  
      89  static void
      90  range_close (struct range_list *r)
      91  {
      92    for (size_t i = 0; i < r->used; i++)
      93      {
      94        int minfd = r->ranges[i].lowfd;
      95        int maxfd = r->ranges[i].lowfd + r->ranges[i].len;
      96        for (int fd = minfd; fd < maxfd; fd++)
      97  	xclose (fd);
      98      }
      99    free (r->ranges);
     100  }
     101  
     102  int
     103  support_open_dev_null_range (int num, int flags, mode_t mode)
     104  {
     105    /* We keep track of the ranges that hit an already opened descriptor, so
     106       we close them after we get a working range.  */
     107    struct range_list rl;
     108    range_init (&rl);
     109  
     110    int lowfd = open_dev_null (flags, mode);
     111    int prevfd = lowfd;
     112    while (true)
     113      {
     114        int i = 1;
     115        for (; i < num; i++)
     116  	{
     117  	  int fd = open_dev_null (flags, mode);
     118  	  if (fd != lowfd + i)
     119  	    {
     120  	      range_add (&rl, lowfd, prevfd - lowfd + 1);
     121  
     122  	      prevfd = lowfd = fd;
     123  	      break;
     124  	    }
     125  	  prevfd = fd;
     126  	}
     127        if (i == num)
     128  	break;
     129      }
     130  
     131    range_close (&rl);
     132  
     133    return lowfd;
     134  }