1 /* Return a range of open file descriptors.
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 <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
increase_nofile(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
open_dev_null(int flags,mode_t mode)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
range_init(struct range_list * r)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
range_add(struct range_list * r,int lowfd,size_t len)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
range_close(struct range_list * r)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
support_open_dev_null_range(int num,int flags,mode_t mode)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 }
135