xref: /illumos-gate/usr/src/test/os-tests/tests/close_range.c (revision 5400c85432c0a689ecefb8c19b8f9861809ca093)
1*5400c854SAndy Fiddaman /*
2*5400c854SAndy Fiddaman  * This file and its contents are supplied under the terms of the
3*5400c854SAndy Fiddaman  * Common Development and Distribution License ("CDDL"), version 1.0.
4*5400c854SAndy Fiddaman  * You may only use this file in accordance with the terms of version
5*5400c854SAndy Fiddaman  * 1.0 of the CDDL.
6*5400c854SAndy Fiddaman  *
7*5400c854SAndy Fiddaman  * A full copy of the text of the CDDL should have accompanied this
8*5400c854SAndy Fiddaman  * source.  A copy of the CDDL is also available via the Internet at
9*5400c854SAndy Fiddaman  * http://www.illumos.org/license/CDDL.
10*5400c854SAndy Fiddaman  */
11*5400c854SAndy Fiddaman 
12*5400c854SAndy Fiddaman /*
13*5400c854SAndy Fiddaman  * Copyright 2026 Oxide Computer Company
14*5400c854SAndy Fiddaman  */
15*5400c854SAndy Fiddaman 
16*5400c854SAndy Fiddaman /*
17*5400c854SAndy Fiddaman  * Tests for close_range(2): range semantics, CLOEXEC/CLOFORK marking,
18*5400c854SAndy Fiddaman  * argument validation and the cancellation of outstanding asynchronous
19*5400c854SAndy Fiddaman  * I/O against descriptors that are closed. We also exercise
20*5400c854SAndy Fiddaman  * closefrom(3C), which is implemented on top of close_range().
21*5400c854SAndy Fiddaman  */
22*5400c854SAndy Fiddaman 
23*5400c854SAndy Fiddaman #include <aio.h>
24*5400c854SAndy Fiddaman #include <err.h>
25*5400c854SAndy Fiddaman #include <errno.h>
26*5400c854SAndy Fiddaman #include <fcntl.h>
27*5400c854SAndy Fiddaman #include <limits.h>
28*5400c854SAndy Fiddaman #include <stdbool.h>
29*5400c854SAndy Fiddaman #include <stdio.h>
30*5400c854SAndy Fiddaman #include <stdlib.h>
31*5400c854SAndy Fiddaman #include <string.h>
32*5400c854SAndy Fiddaman #include <time.h>
33*5400c854SAndy Fiddaman #include <unistd.h>
34*5400c854SAndy Fiddaman #include <sys/time.h>
35*5400c854SAndy Fiddaman 
36*5400c854SAndy Fiddaman static uint_t failures = 0;
37*5400c854SAndy Fiddaman 
38*5400c854SAndy Fiddaman #define	TFAIL(name, fmt, ...)	do {					\
39*5400c854SAndy Fiddaman 	(void) fprintf(stderr, "TEST FAILED: %s: " fmt "\n",		\
40*5400c854SAndy Fiddaman 	    (name), ##__VA_ARGS__);					\
41*5400c854SAndy Fiddaman 	failures++;							\
42*5400c854SAndy Fiddaman } while (0)
43*5400c854SAndy Fiddaman 
44*5400c854SAndy Fiddaman #define	TPASS(name)	(void) printf("TEST PASSED: %s\n", (name))
45*5400c854SAndy Fiddaman 
46*5400c854SAndy Fiddaman /*
47*5400c854SAndy Fiddaman  * The test descriptors are pinned at known numbers, comfortably above
48*5400c854SAndy Fiddaman  * anything the process opens for itself.
49*5400c854SAndy Fiddaman  */
50*5400c854SAndy Fiddaman #define	FD_BASE		50
51*5400c854SAndy Fiddaman #define	FD_LIMIT	70
52*5400c854SAndy Fiddaman 
53*5400c854SAndy Fiddaman static void
54*5400c854SAndy Fiddaman open_fds(void)
55*5400c854SAndy Fiddaman {
56*5400c854SAndy Fiddaman 	int fd;
57*5400c854SAndy Fiddaman 
58*5400c854SAndy Fiddaman 	if ((fd = open("/dev/null", O_RDONLY)) == -1)
59*5400c854SAndy Fiddaman 		err(EXIT_FAILURE, "could not open /dev/null");
60*5400c854SAndy Fiddaman 
61*5400c854SAndy Fiddaman 	for (int i = FD_BASE; i < FD_LIMIT; i++) {
62*5400c854SAndy Fiddaman 		if (dup2(fd, i) != i)
63*5400c854SAndy Fiddaman 			err(EXIT_FAILURE, "could not dup to fd %d", i);
64*5400c854SAndy Fiddaman 	}
65*5400c854SAndy Fiddaman 
66*5400c854SAndy Fiddaman 	(void) close(fd);
67*5400c854SAndy Fiddaman }
68*5400c854SAndy Fiddaman 
69*5400c854SAndy Fiddaman /*
70*5400c854SAndy Fiddaman  * Verify that fd is open and carries precisely the descriptor flags in
71*5400c854SAndy Fiddaman  * 'want', or, if want is -1, that fd is closed.
72*5400c854SAndy Fiddaman  */
73*5400c854SAndy Fiddaman static bool
74*5400c854SAndy Fiddaman check_fd(const char *name, int fd, int want)
75*5400c854SAndy Fiddaman {
76*5400c854SAndy Fiddaman 	int fl = fcntl(fd, F_GETFD);
77*5400c854SAndy Fiddaman 
78*5400c854SAndy Fiddaman 	if (want == -1) {
79*5400c854SAndy Fiddaman 		if (fl != -1) {
80*5400c854SAndy Fiddaman 			TFAIL(name, "fd %d unexpectedly open (flags %#x)",
81*5400c854SAndy Fiddaman 			    fd, fl);
82*5400c854SAndy Fiddaman 			return (false);
83*5400c854SAndy Fiddaman 		}
84*5400c854SAndy Fiddaman 		if (errno != EBADF) {
85*5400c854SAndy Fiddaman 			TFAIL(name, "fd %d: got errno %s, wanted EBADF",
86*5400c854SAndy Fiddaman 			    fd, strerrorname_np(errno));
87*5400c854SAndy Fiddaman 			return (false);
88*5400c854SAndy Fiddaman 		}
89*5400c854SAndy Fiddaman 		return (true);
90*5400c854SAndy Fiddaman 	}
91*5400c854SAndy Fiddaman 
92*5400c854SAndy Fiddaman 	if (fl == -1) {
93*5400c854SAndy Fiddaman 		TFAIL(name, "fd %d unexpectedly closed", fd);
94*5400c854SAndy Fiddaman 		return (false);
95*5400c854SAndy Fiddaman 	}
96*5400c854SAndy Fiddaman 	if ((fl & (FD_CLOEXEC | FD_CLOFORK)) != want) {
97*5400c854SAndy Fiddaman 		TFAIL(name, "fd %d flags %#x, expected %#x", fd, fl, want);
98*5400c854SAndy Fiddaman 		return (false);
99*5400c854SAndy Fiddaman 	}
100*5400c854SAndy Fiddaman 	return (true);
101*5400c854SAndy Fiddaman }
102*5400c854SAndy Fiddaman 
103*5400c854SAndy Fiddaman static bool
104*5400c854SAndy Fiddaman check_range(const char *name, int low, int high, int want)
105*5400c854SAndy Fiddaman {
106*5400c854SAndy Fiddaman 	bool pass = true;
107*5400c854SAndy Fiddaman 
108*5400c854SAndy Fiddaman 	for (int fd = low; fd <= high; fd++) {
109*5400c854SAndy Fiddaman 		if (!check_fd(name, fd, want))
110*5400c854SAndy Fiddaman 			pass = false;
111*5400c854SAndy Fiddaman 	}
112*5400c854SAndy Fiddaman 	return (pass);
113*5400c854SAndy Fiddaman }
114*5400c854SAndy Fiddaman 
115*5400c854SAndy Fiddaman static void
116*5400c854SAndy Fiddaman expect_einval(const char *name, uint_t low, uint_t high, int flags)
117*5400c854SAndy Fiddaman {
118*5400c854SAndy Fiddaman 	int ret = close_range(low, high, flags);
119*5400c854SAndy Fiddaman 
120*5400c854SAndy Fiddaman 	if (ret != -1) {
121*5400c854SAndy Fiddaman 		TFAIL(name, "close_range returned %d, expected failure", ret);
122*5400c854SAndy Fiddaman 		return;
123*5400c854SAndy Fiddaman 	}
124*5400c854SAndy Fiddaman 	if (errno != EINVAL) {
125*5400c854SAndy Fiddaman 		TFAIL(name, "got errno %s, wanted EINVAL",
126*5400c854SAndy Fiddaman 		    strerrorname_np(errno));
127*5400c854SAndy Fiddaman 		return;
128*5400c854SAndy Fiddaman 	}
129*5400c854SAndy Fiddaman 	TPASS(name);
130*5400c854SAndy Fiddaman }
131*5400c854SAndy Fiddaman 
132*5400c854SAndy Fiddaman /*
133*5400c854SAndy Fiddaman  * Render an aio_error() result, or the -1 used here to indicate a
134*5400c854SAndy Fiddaman  * timeout, for a failure message.
135*5400c854SAndy Fiddaman  */
136*5400c854SAndy Fiddaman static const char *
137*5400c854SAndy Fiddaman errname(int e)
138*5400c854SAndy Fiddaman {
139*5400c854SAndy Fiddaman 	const char *n;
140*5400c854SAndy Fiddaman 
141*5400c854SAndy Fiddaman 	if (e == -1)
142*5400c854SAndy Fiddaman 		return ("EINPROGRESS (timed out)");
143*5400c854SAndy Fiddaman 	if (e == 0)
144*5400c854SAndy Fiddaman 		return ("success");
145*5400c854SAndy Fiddaman 	if ((n = strerrorname_np(e)) == NULL)
146*5400c854SAndy Fiddaman 		return ("unknown error");
147*5400c854SAndy Fiddaman 	return (n);
148*5400c854SAndy Fiddaman }
149*5400c854SAndy Fiddaman 
150*5400c854SAndy Fiddaman /*
151*5400c854SAndy Fiddaman  * Create a pipe with the read end at the requested descriptor, returning
152*5400c854SAndy Fiddaman  * the write end.
153*5400c854SAndy Fiddaman  */
154*5400c854SAndy Fiddaman static int
155*5400c854SAndy Fiddaman pipe_at(int rfd)
156*5400c854SAndy Fiddaman {
157*5400c854SAndy Fiddaman 	int p[2];
158*5400c854SAndy Fiddaman 
159*5400c854SAndy Fiddaman 	if (pipe(p) == -1)
160*5400c854SAndy Fiddaman 		err(EXIT_FAILURE, "pipe");
161*5400c854SAndy Fiddaman 	if (dup2(p[0], rfd) != rfd)
162*5400c854SAndy Fiddaman 		err(EXIT_FAILURE, "could not dup pipe to fd %d", rfd);
163*5400c854SAndy Fiddaman 	(void) close(p[0]);
164*5400c854SAndy Fiddaman 	return (p[1]);
165*5400c854SAndy Fiddaman }
166*5400c854SAndy Fiddaman 
167*5400c854SAndy Fiddaman /*
168*5400c854SAndy Fiddaman  * Start an asynchronous read against fd. The pipes used here are kept
169*5400c854SAndy Fiddaman  * empty, so the request stays outstanding until data arrives or it is
170*5400c854SAndy Fiddaman  * cancelled.
171*5400c854SAndy Fiddaman  */
172*5400c854SAndy Fiddaman static void
173*5400c854SAndy Fiddaman start_aio(struct aiocb *cb, int fd, char *buf, size_t bufsz)
174*5400c854SAndy Fiddaman {
175*5400c854SAndy Fiddaman 	(void) memset(cb, 0, sizeof (*cb));
176*5400c854SAndy Fiddaman 	cb->aio_fildes = fd;
177*5400c854SAndy Fiddaman 	cb->aio_buf = buf;
178*5400c854SAndy Fiddaman 	cb->aio_nbytes = bufsz;
179*5400c854SAndy Fiddaman 	if (aio_read(cb) != 0)
180*5400c854SAndy Fiddaman 		err(EXIT_FAILURE, "aio_read(fd %d)", fd);
181*5400c854SAndy Fiddaman }
182*5400c854SAndy Fiddaman 
183*5400c854SAndy Fiddaman /*
184*5400c854SAndy Fiddaman  * Wait for an asynchronous request to leave the in-progress state and
185*5400c854SAndy Fiddaman  * return its final aio_error() value, or -1 if it is still in progress
186*5400c854SAndy Fiddaman  * after five seconds.
187*5400c854SAndy Fiddaman  */
188*5400c854SAndy Fiddaman static int
189*5400c854SAndy Fiddaman await_aio(const struct aiocb *cb)
190*5400c854SAndy Fiddaman {
191*5400c854SAndy Fiddaman 	const struct timespec ts = { .tv_sec = 0, .tv_nsec = MSEC2NSEC(1) };
192*5400c854SAndy Fiddaman 
193*5400c854SAndy Fiddaman 	for (int i = 0; i < 5000; i++) {
194*5400c854SAndy Fiddaman 		int e = aio_error(cb);
195*5400c854SAndy Fiddaman 
196*5400c854SAndy Fiddaman 		if (e != EINPROGRESS)
197*5400c854SAndy Fiddaman 			return (e);
198*5400c854SAndy Fiddaman 		(void) nanosleep(&ts, NULL);
199*5400c854SAndy Fiddaman 	}
200*5400c854SAndy Fiddaman 	return (-1);
201*5400c854SAndy Fiddaman }
202*5400c854SAndy Fiddaman 
203*5400c854SAndy Fiddaman /*
204*5400c854SAndy Fiddaman  * Closing a range must cancel outstanding user-level aio requests
205*5400c854SAndy Fiddaman  * against the descriptors within it, and only those. A range that is
206*5400c854SAndy Fiddaman  * only having descriptor flags set must not disturb aio at all.
207*5400c854SAndy Fiddaman  */
208*5400c854SAndy Fiddaman static void
209*5400c854SAndy Fiddaman run_aio_tests(void)
210*5400c854SAndy Fiddaman {
211*5400c854SAndy Fiddaman 	struct aiocb cb_range, cb_below, cb_flag, cb_from;
212*5400c854SAndy Fiddaman 	char buf_range[4], buf_below[4], buf_flag[4], buf_from[4];
213*5400c854SAndy Fiddaman 	ssize_t rv;
214*5400c854SAndy Fiddaman 	int wfd, e;
215*5400c854SAndy Fiddaman 
216*5400c854SAndy Fiddaman 	(void) pipe_at(80);
217*5400c854SAndy Fiddaman 	wfd = pipe_at(40);
218*5400c854SAndy Fiddaman 	start_aio(&cb_range, 80, buf_range, sizeof (buf_range));
219*5400c854SAndy Fiddaman 	start_aio(&cb_below, 40, buf_below, sizeof (buf_below));
220*5400c854SAndy Fiddaman 
221*5400c854SAndy Fiddaman 	/* Closing a range cancels requests against descriptors within it */
222*5400c854SAndy Fiddaman 	if (close_range(60, UINT_MAX, 0) != 0)
223*5400c854SAndy Fiddaman 		err(EXIT_FAILURE, "close_range(60, UINT_MAX, 0)");
224*5400c854SAndy Fiddaman 	if ((e = await_aio(&cb_range)) != ECANCELED) {
225*5400c854SAndy Fiddaman 		TFAIL("aio-cancel-range", "request finished with %s, "
226*5400c854SAndy Fiddaman 		    "wanted ECANCELED", errname(e));
227*5400c854SAndy Fiddaman 	} else if (aio_return(&cb_range) != -1) {
228*5400c854SAndy Fiddaman 		TFAIL("aio-cancel-range", "aio_return did not report an "
229*5400c854SAndy Fiddaman 		    "error after cancellation");
230*5400c854SAndy Fiddaman 	} else if (check_fd("aio-cancel-range", 80, -1)) {
231*5400c854SAndy Fiddaman 		TPASS("aio-cancel-range");
232*5400c854SAndy Fiddaman 	}
233*5400c854SAndy Fiddaman 
234*5400c854SAndy Fiddaman 	/* Requests against descriptors below the range are untouched */
235*5400c854SAndy Fiddaman 	if (write(wfd, "ok", 2) != 2)
236*5400c854SAndy Fiddaman 		err(EXIT_FAILURE, "write to pipe");
237*5400c854SAndy Fiddaman 	if ((e = await_aio(&cb_below)) != 0) {
238*5400c854SAndy Fiddaman 		TFAIL("aio-below-range", "request finished with %s, "
239*5400c854SAndy Fiddaman 		    "wanted success", errname(e));
240*5400c854SAndy Fiddaman 	} else if ((rv = aio_return(&cb_below)) != 2) {
241*5400c854SAndy Fiddaman 		TFAIL("aio-below-range", "aio_return gave %zd, wanted 2", rv);
242*5400c854SAndy Fiddaman 	} else {
243*5400c854SAndy Fiddaman 		TPASS("aio-below-range");
244*5400c854SAndy Fiddaman 	}
245*5400c854SAndy Fiddaman 
246*5400c854SAndy Fiddaman 	/* Setting descriptor flags must not cancel outstanding aio */
247*5400c854SAndy Fiddaman 	start_aio(&cb_flag, 40, buf_flag, sizeof (buf_flag));
248*5400c854SAndy Fiddaman 	if (close_range(40, 40, CLOSE_RANGE_CLOFORK) != 0)
249*5400c854SAndy Fiddaman 		err(EXIT_FAILURE, "close_range(40, 40, CLOSE_RANGE_CLOFORK)");
250*5400c854SAndy Fiddaman 	if (close_range(40, 40, CLOSE_RANGE_CLOEXEC) != 0)
251*5400c854SAndy Fiddaman 		err(EXIT_FAILURE, "close_range(40, 40, CLOSE_RANGE_CLOEXEC)");
252*5400c854SAndy Fiddaman 	e = aio_error(&cb_flag);
253*5400c854SAndy Fiddaman 	if (write(wfd, "ok", 2) != 2)
254*5400c854SAndy Fiddaman 		err(EXIT_FAILURE, "write to pipe");
255*5400c854SAndy Fiddaman 	if (e != EINPROGRESS) {
256*5400c854SAndy Fiddaman 		TFAIL("aio-mark-keeps", "request no longer in progress "
257*5400c854SAndy Fiddaman 		    "after flag marking: %s", errname(e));
258*5400c854SAndy Fiddaman 	} else if ((e = await_aio(&cb_flag)) != 0) {
259*5400c854SAndy Fiddaman 		TFAIL("aio-mark-keeps", "request finished with %s, "
260*5400c854SAndy Fiddaman 		    "wanted success", errname(e));
261*5400c854SAndy Fiddaman 	} else if ((rv = aio_return(&cb_flag)) != 2) {
262*5400c854SAndy Fiddaman 		TFAIL("aio-mark-keeps", "aio_return gave %zd, wanted 2", rv);
263*5400c854SAndy Fiddaman 	} else if (check_fd("aio-mark-keeps", 40, FD_CLOEXEC | FD_CLOFORK)) {
264*5400c854SAndy Fiddaman 		TPASS("aio-mark-keeps");
265*5400c854SAndy Fiddaman 	}
266*5400c854SAndy Fiddaman 
267*5400c854SAndy Fiddaman 	/* closefrom() cancels through the same path */
268*5400c854SAndy Fiddaman 	(void) pipe_at(80);
269*5400c854SAndy Fiddaman 	start_aio(&cb_from, 80, buf_from, sizeof (buf_from));
270*5400c854SAndy Fiddaman 	closefrom(80);
271*5400c854SAndy Fiddaman 	if ((e = await_aio(&cb_from)) != ECANCELED) {
272*5400c854SAndy Fiddaman 		TFAIL("aio-cancel-closefrom", "request finished with %s, "
273*5400c854SAndy Fiddaman 		    "wanted ECANCELED", errname(e));
274*5400c854SAndy Fiddaman 	} else if (check_fd("aio-cancel-closefrom", 80, -1)) {
275*5400c854SAndy Fiddaman 		TPASS("aio-cancel-closefrom");
276*5400c854SAndy Fiddaman 	}
277*5400c854SAndy Fiddaman }
278*5400c854SAndy Fiddaman 
279*5400c854SAndy Fiddaman int
280*5400c854SAndy Fiddaman main(void)
281*5400c854SAndy Fiddaman {
282*5400c854SAndy Fiddaman 	open_fds();
283*5400c854SAndy Fiddaman 
284*5400c854SAndy Fiddaman 	/* Invalid arguments. An empty high range so a bug cannot bite. */
285*5400c854SAndy Fiddaman 	expect_einval("low-gt-high", 5, 4, 0);
286*5400c854SAndy Fiddaman 	expect_einval("flag-unknown", 1000000, 1000001, 1 << 2);
287*5400c854SAndy Fiddaman 	expect_einval("flag-junk", 1000000, 1000001, ~0);
288*5400c854SAndy Fiddaman 
289*5400c854SAndy Fiddaman 	/* A range beyond every open descriptor succeeds and does nothing */
290*5400c854SAndy Fiddaman 	if (close_range(1000000, UINT_MAX, 0) != 0) {
291*5400c854SAndy Fiddaman 		TFAIL("range-empty", "failed: %s", strerrorname_np(errno));
292*5400c854SAndy Fiddaman 	} else {
293*5400c854SAndy Fiddaman 		TPASS("range-empty");
294*5400c854SAndy Fiddaman 	}
295*5400c854SAndy Fiddaman 
296*5400c854SAndy Fiddaman 	/* A low bound above INT_MAX must not close anything */
297*5400c854SAndy Fiddaman 	if (close_range(0x80000000U, UINT_MAX, 0) != 0) {
298*5400c854SAndy Fiddaman 		TFAIL("low-gt-intmax", "failed: %s",
299*5400c854SAndy Fiddaman 		    strerrorname_np(errno));
300*5400c854SAndy Fiddaman 	} else if (check_range("low-gt-intmax", FD_BASE, FD_LIMIT - 1, 0)) {
301*5400c854SAndy Fiddaman 		TPASS("low-gt-intmax");
302*5400c854SAndy Fiddaman 	}
303*5400c854SAndy Fiddaman 
304*5400c854SAndy Fiddaman 	/* Close the middle of the block and check the neighbours */
305*5400c854SAndy Fiddaman 	if (close_range(55, 59, 0) != 0)
306*5400c854SAndy Fiddaman 		err(EXIT_FAILURE, "close_range(55, 59, 0)");
307*5400c854SAndy Fiddaman 	if (check_range("close-mid", 50, 54, 0) &&
308*5400c854SAndy Fiddaman 	    check_range("close-mid", 55, 59, -1) &&
309*5400c854SAndy Fiddaman 	    check_range("close-mid", 60, 69, 0)) {
310*5400c854SAndy Fiddaman 		TPASS("close-mid");
311*5400c854SAndy Fiddaman 	}
312*5400c854SAndy Fiddaman 
313*5400c854SAndy Fiddaman 	/* Mark ranges with each flag, then both together */
314*5400c854SAndy Fiddaman 	if (close_range(60, 64, CLOSE_RANGE_CLOEXEC) != 0)
315*5400c854SAndy Fiddaman 		err(EXIT_FAILURE, "close_range(CLOSE_RANGE_CLOEXEC)");
316*5400c854SAndy Fiddaman 	if (check_range("mark-cloexec", 60, 64, FD_CLOEXEC) &&
317*5400c854SAndy Fiddaman 	    check_range("mark-cloexec", 65, 69, 0)) {
318*5400c854SAndy Fiddaman 		TPASS("mark-cloexec");
319*5400c854SAndy Fiddaman 	}
320*5400c854SAndy Fiddaman 
321*5400c854SAndy Fiddaman 	if (close_range(65, 67, CLOSE_RANGE_CLOFORK) != 0)
322*5400c854SAndy Fiddaman 		err(EXIT_FAILURE, "close_range(CLOSE_RANGE_CLOFORK)");
323*5400c854SAndy Fiddaman 	if (check_range("mark-clofork", 65, 67, FD_CLOFORK) &&
324*5400c854SAndy Fiddaman 	    check_range("mark-clofork", 68, 69, 0)) {
325*5400c854SAndy Fiddaman 		TPASS("mark-clofork");
326*5400c854SAndy Fiddaman 	}
327*5400c854SAndy Fiddaman 
328*5400c854SAndy Fiddaman 	/* Adding a second flag must preserve the first */
329*5400c854SAndy Fiddaman 	if (close_range(65, 67, CLOSE_RANGE_CLOEXEC) != 0)
330*5400c854SAndy Fiddaman 		err(EXIT_FAILURE, "close_range(add CLOSE_RANGE_CLOEXEC)");
331*5400c854SAndy Fiddaman 	if (check_range("mark-accumulate", 65, 67, FD_CLOEXEC | FD_CLOFORK))
332*5400c854SAndy Fiddaman 		TPASS("mark-accumulate");
333*5400c854SAndy Fiddaman 
334*5400c854SAndy Fiddaman 	if (close_range(68, 69,
335*5400c854SAndy Fiddaman 	    CLOSE_RANGE_CLOEXEC | CLOSE_RANGE_CLOFORK) != 0) {
336*5400c854SAndy Fiddaman 		err(EXIT_FAILURE, "close_range(both flags)");
337*5400c854SAndy Fiddaman 	}
338*5400c854SAndy Fiddaman 	if (check_range("mark-both", 68, 69, FD_CLOEXEC | FD_CLOFORK))
339*5400c854SAndy Fiddaman 		TPASS("mark-both");
340*5400c854SAndy Fiddaman 
341*5400c854SAndy Fiddaman 	/* Marking a range that includes closed descriptors skips them */
342*5400c854SAndy Fiddaman 	if (close_range(55, 64, CLOSE_RANGE_CLOEXEC) != 0)
343*5400c854SAndy Fiddaman 		err(EXIT_FAILURE, "close_range over closed fds");
344*5400c854SAndy Fiddaman 	if (check_range("mark-gaps", 55, 59, -1) &&
345*5400c854SAndy Fiddaman 	    check_range("mark-gaps", 60, 64, FD_CLOEXEC)) {
346*5400c854SAndy Fiddaman 		TPASS("mark-gaps");
347*5400c854SAndy Fiddaman 	}
348*5400c854SAndy Fiddaman 
349*5400c854SAndy Fiddaman 	/* The closefrom() form: everything at or above low goes */
350*5400c854SAndy Fiddaman 	if (close_range(52, UINT_MAX, 0) != 0)
351*5400c854SAndy Fiddaman 		err(EXIT_FAILURE, "close_range(52, UINT_MAX, 0)");
352*5400c854SAndy Fiddaman 	if (check_range("close-from", 50, 51, 0) &&
353*5400c854SAndy Fiddaman 	    check_range("close-from", 52, FD_LIMIT - 1, -1)) {
354*5400c854SAndy Fiddaman 		TPASS("close-from");
355*5400c854SAndy Fiddaman 	}
356*5400c854SAndy Fiddaman 
357*5400c854SAndy Fiddaman 	/* And closefrom(3C) itself, now built on close_range() */
358*5400c854SAndy Fiddaman 	open_fds();
359*5400c854SAndy Fiddaman 	closefrom(FD_BASE + 1);
360*5400c854SAndy Fiddaman 	if (check_fd("closefrom", FD_BASE, 0) &&
361*5400c854SAndy Fiddaman 	    check_range("closefrom", FD_BASE + 1, FD_LIMIT - 1, -1)) {
362*5400c854SAndy Fiddaman 		TPASS("closefrom");
363*5400c854SAndy Fiddaman 	}
364*5400c854SAndy Fiddaman 
365*5400c854SAndy Fiddaman 	run_aio_tests();
366*5400c854SAndy Fiddaman 
367*5400c854SAndy Fiddaman 	if (failures == 0) {
368*5400c854SAndy Fiddaman 		(void) printf("All tests passed\n");
369*5400c854SAndy Fiddaman 		return (EXIT_SUCCESS);
370*5400c854SAndy Fiddaman 	}
371*5400c854SAndy Fiddaman 
372*5400c854SAndy Fiddaman 	(void) fprintf(stderr, "%u test(s) failed\n", failures);
373*5400c854SAndy Fiddaman 	return (EXIT_FAILURE);
374*5400c854SAndy Fiddaman }
375