1194262Sjhb/*-
2282067Sngie * Copyright (c) 2009 Hudson River Trading LLC
3194262Sjhb * Written by: John H. Baldwin <jhb@FreeBSD.org>
4194262Sjhb * All rights reserved.
5194262Sjhb *
6194262Sjhb * Redistribution and use in source and binary forms, with or without
7194262Sjhb * modification, are permitted provided that the following conditions
8194262Sjhb * are met:
9194262Sjhb * 1. Redistributions of source code must retain the above copyright
10194262Sjhb *    notice, this list of conditions and the following disclaimer.
11194262Sjhb * 2. Redistributions in binary form must reproduce the above copyright
12194262Sjhb *    notice, this list of conditions and the following disclaimer in the
13194262Sjhb *    documentation and/or other materials provided with the distribution.
14194262Sjhb *
15194262Sjhb * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16194262Sjhb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17194262Sjhb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18194262Sjhb * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19194262Sjhb * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20194262Sjhb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21194262Sjhb * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22194262Sjhb * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23194262Sjhb * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24194262Sjhb * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25194262Sjhb * SUCH DAMAGE.
26194262Sjhb */
27194262Sjhb
28194262Sjhb#include <sys/cdefs.h>
29194262Sjhb__FBSDID("$FreeBSD: releng/10.3/tests/sys/file/closefrom_test.c 282067 2015-04-27 08:15:17Z ngie $");
30194262Sjhb
31194262Sjhb/*
32194262Sjhb * Regression tests for the closefrom(2) system call.
33194262Sjhb */
34194262Sjhb
35194262Sjhb#include <sys/param.h>
36194262Sjhb#include <sys/mman.h>
37194262Sjhb#include <sys/user.h>
38194262Sjhb#include <sys/wait.h>
39194262Sjhb#include <errno.h>
40194262Sjhb#include <fcntl.h>
41194262Sjhb#include <libutil.h>
42281432Sngie#include <paths.h>
43194262Sjhb#include <stdarg.h>
44194262Sjhb#include <stdio.h>
45194262Sjhb#include <stdlib.h>
46194262Sjhb#include <string.h>
47194262Sjhb#include <unistd.h>
48194262Sjhb
49194262Sjhbstruct shared_info {
50194262Sjhb	int	failed;
51194262Sjhb	char	tag[64];
52194262Sjhb	char	message[0];
53194262Sjhb};
54194262Sjhb
55194262Sjhbstatic int test = 1;
56194262Sjhb
57194262Sjhbstatic void
58194262Sjhbok(const char *descr)
59194262Sjhb{
60194262Sjhb
61194262Sjhb	printf("ok %d - %s\n", test, descr);
62194262Sjhb	test++;
63194262Sjhb}
64194262Sjhb
65194262Sjhbstatic void
66194262Sjhbfail(const char *descr, const char *fmt, ...)
67194262Sjhb{
68194262Sjhb	va_list ap;
69194262Sjhb
70194262Sjhb	printf("not ok %d - %s", test, descr);
71194262Sjhb	test++;
72194262Sjhb	if (fmt) {
73194262Sjhb		va_start(ap, fmt);
74194262Sjhb		printf(" # ");
75194262Sjhb		vprintf(fmt, ap);
76194262Sjhb		va_end(ap);
77194262Sjhb	}
78194262Sjhb	printf("\n");
79194262Sjhb	exit(1);
80194262Sjhb}
81194262Sjhb
82194262Sjhb#define	fail_err(descr)		fail((descr), "%s", strerror(errno))
83194262Sjhb
84194262Sjhbstatic void
85194262Sjhbcok(struct shared_info *info, const char *descr)
86194262Sjhb{
87194262Sjhb
88194262Sjhb	info->failed = 0;
89194262Sjhb	strlcpy(info->tag, descr, sizeof(info->tag));
90194262Sjhb	exit(0);
91194262Sjhb}
92194262Sjhb
93194262Sjhbstatic void
94194262Sjhbcfail(struct shared_info *info, const char *descr, const char *fmt, ...)
95194262Sjhb{
96194262Sjhb	va_list ap;
97194262Sjhb
98194262Sjhb	info->failed = 1;
99194262Sjhb	strlcpy(info->tag, descr, sizeof(info->tag));
100194262Sjhb	if (fmt) {
101194262Sjhb		va_start(ap, fmt);
102194262Sjhb		vsprintf(info->message, fmt, ap);
103194262Sjhb		va_end(ap);
104194262Sjhb	}
105194262Sjhb	exit(0);
106194262Sjhb}
107194262Sjhb
108194262Sjhb#define	cfail_err(info, descr)	cfail((info), (descr), "%s", strerror(errno))
109194262Sjhb
110194262Sjhb/*
111194262Sjhb * Use kinfo_getfile() to fetch the list of file descriptors and figure out
112194262Sjhb * the highest open file descriptor.
113194262Sjhb */
114194262Sjhbstatic int
115194262Sjhbhighest_fd(void)
116194262Sjhb{
117194262Sjhb	struct kinfo_file *kif;
118194262Sjhb	int cnt, i, highest;
119194262Sjhb
120194262Sjhb	kif = kinfo_getfile(getpid(), &cnt);
121194262Sjhb	if (kif == NULL)
122194262Sjhb		fail_err("kinfo_getfile");
123194262Sjhb	highest = INT_MIN;
124194262Sjhb	for (i = 0; i < cnt; i++)
125194262Sjhb		if (kif[i].kf_fd > highest)
126194262Sjhb			highest = kif[i].kf_fd;
127194262Sjhb	free(kif);
128194262Sjhb	return (highest);
129194262Sjhb}
130194262Sjhb
131194262Sjhbstatic int
132194262Sjhbdevnull(void)
133194262Sjhb{
134194262Sjhb	int fd;
135194262Sjhb
136281432Sngie	fd = open(_PATH_DEVNULL, O_RDONLY);
137194262Sjhb	if (fd < 0)
138281432Sngie		fail_err("open(\" "_PATH_DEVNULL" \")");
139194262Sjhb	return (fd);
140194262Sjhb}
141194262Sjhb
142194262Sjhbint
143281432Sngiemain(void)
144194262Sjhb{
145194262Sjhb	struct shared_info *info;
146194262Sjhb	pid_t pid;
147281432Sngie	int fd, i, start;
148194262Sjhb
149194262Sjhb	printf("1..15\n");
150194262Sjhb
151194262Sjhb	/* We better start up with fd's 0, 1, and 2 open. */
152281432Sngie	start = devnull();
153281432Sngie	if (start == -1)
154281432Sngie		fail("open", "bad descriptor %d", start);
155194262Sjhb	ok("open");
156194262Sjhb
157194262Sjhb	/* Make sure highest_fd() works. */
158194262Sjhb	fd = highest_fd();
159281432Sngie	if (start != fd)
160281432Sngie		fail("highest_fd", "bad descriptor %d != %d", start, fd);
161194262Sjhb	ok("highest_fd");
162194262Sjhb
163194262Sjhb	/* Try to use closefrom() for just closing fd 3. */
164281432Sngie	closefrom(start + 1);
165194262Sjhb	fd = highest_fd();
166281432Sngie	if (fd != start)
167194262Sjhb		fail("closefrom", "highest fd %d", fd);
168194262Sjhb	ok("closefrom");
169194262Sjhb
170194262Sjhb	/* Eat up 16 descriptors. */
171194262Sjhb	for (i = 0; i < 16; i++)
172194262Sjhb		(void)devnull();
173194262Sjhb	fd = highest_fd();
174281432Sngie	if (fd != start + 16)
175194262Sjhb		fail("open 16", "highest fd %d", fd);
176194262Sjhb	ok("open 16");
177194262Sjhb
178194262Sjhb	/* Close half of them. */
179194262Sjhb	closefrom(11);
180194262Sjhb	fd = highest_fd();
181194262Sjhb	if (fd != 10)
182194262Sjhb		fail("closefrom", "highest fd %d", fd);
183194262Sjhb	ok("closefrom");
184194262Sjhb
185194262Sjhb	/* Explicitly close descriptors 6 and 8 to create holes. */
186194262Sjhb	if (close(6) < 0 || close(8) < 0)
187194262Sjhb		fail_err("close2 ");
188194262Sjhb	ok("close 2");
189194262Sjhb
190194262Sjhb	/* Verify that close on 6 and 8 fails with EBADF. */
191194262Sjhb	if (close(6) == 0)
192194262Sjhb		fail("close(6)", "did not fail");
193194262Sjhb	if (errno != EBADF)
194194262Sjhb		fail_err("close(6)");
195194262Sjhb	ok("close(6)");
196194262Sjhb	if (close(8) == 0)
197194262Sjhb		fail("close(8)", "did not fail");
198194262Sjhb	if (errno != EBADF)
199194262Sjhb		fail_err("close(8)");
200194262Sjhb	ok("close(8)");
201194262Sjhb
202194262Sjhb	/* Close from 4 on. */
203194262Sjhb	closefrom(4);
204194262Sjhb	fd = highest_fd();
205194262Sjhb	if (fd != 3)
206194262Sjhb		fail("closefrom", "highest fd %d", fd);
207194262Sjhb	ok("closefrom");
208194262Sjhb
209194262Sjhb	/* Allocate a small SHM region for IPC with our child. */
210194262Sjhb	info = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE, MAP_ANON |
211194262Sjhb	    MAP_SHARED, -1, 0);
212194262Sjhb	if (info == MAP_FAILED)
213194262Sjhb		fail_err("mmap");
214194262Sjhb	ok("mmap");
215194262Sjhb
216194262Sjhb	/* Fork a child process to test closefrom(0). */
217194262Sjhb	pid = fork();
218194262Sjhb	if (pid < 0)
219194262Sjhb		fail_err("fork");
220194262Sjhb	if (pid == 0) {
221194262Sjhb		/* Child. */
222194262Sjhb		closefrom(0);
223194262Sjhb		fd = highest_fd();
224194262Sjhb		if (fd >= 0)
225194262Sjhb			cfail(info, "closefrom(0)", "highest fd %d", fd);
226194262Sjhb		cok(info, "closefrom(0)");
227194262Sjhb	}
228194262Sjhb	if (wait(NULL) < 0)
229194262Sjhb		fail_err("wait");
230194262Sjhb	if (info->failed)
231194262Sjhb		fail(info->tag, "%s", info->message);
232194262Sjhb	ok(info->tag);
233194262Sjhb
234194262Sjhb	/* Fork a child process to test closefrom(-1). */
235194262Sjhb	pid = fork();
236194262Sjhb	if (pid < 0)
237194262Sjhb		fail_err("fork");
238194262Sjhb	if (pid == 0) {
239194262Sjhb		/* Child. */
240194262Sjhb		closefrom(-1);
241194262Sjhb		fd = highest_fd();
242194262Sjhb		if (fd >= 0)
243194262Sjhb			cfail(info, "closefrom(-1)", "highest fd %d", fd);
244194262Sjhb		cok(info, "closefrom(-1)");
245194262Sjhb	}
246194262Sjhb	if (wait(NULL) < 0)
247194262Sjhb		fail_err("wait");
248194262Sjhb	if (info->failed)
249194262Sjhb		fail(info->tag, "%s", info->message);
250194262Sjhb	ok(info->tag);
251194262Sjhb
252194262Sjhb	/* Dup stdout to 6. */
253194262Sjhb	if (dup2(1, 6) < 0)
254194262Sjhb		fail_err("dup2");
255194262Sjhb	fd = highest_fd();
256194262Sjhb	if (fd != 6)
257194262Sjhb		fail("dup2", "highest fd %d", fd);
258194262Sjhb	ok("dup2");
259194262Sjhb
260194262Sjhb	/* Do a closefrom() starting in a hole. */
261194262Sjhb	closefrom(4);
262194262Sjhb	fd = highest_fd();
263194262Sjhb	if (fd != 3)
264194262Sjhb		fail("closefrom", "highest fd %d", fd);
265194262Sjhb	ok("closefrom");
266194262Sjhb
267194262Sjhb	/* Do a closefrom() beyond our highest open fd. */
268194262Sjhb	closefrom(32);
269194262Sjhb	fd = highest_fd();
270194262Sjhb	if (fd != 3)
271194262Sjhb		fail("closefrom", "highest fd %d", fd);
272194262Sjhb	ok("closefrom");
273281432Sngie
274194262Sjhb	return (0);
275194262Sjhb}
276