1/*-
2 * Copyright (c) 2005 Robert N. M. Watson
3 * Copyright (c) 2012 Jilles Tjoelker
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 * $FreeBSD: releng/10.2/tests/sys/fifo/fifo_misc.c 281450 2015-04-12 06:18:24Z ngie $
28 */
29
30#include <sys/types.h>
31#include <sys/event.h>
32#include <sys/filio.h>
33#include <sys/stat.h>
34#include <sys/time.h>
35
36#include <err.h>
37#include <errno.h>
38#include <fcntl.h>
39#include <limits.h>
40#include <stdio.h>
41#include <stdlib.h>
42#include <string.h>
43#include <unistd.h>
44
45/*
46 * Regression test for piddling details of fifos.
47 */
48
49/*
50 * All activity occurs within a temporary directory created early in the
51 * test.
52 */
53static char	temp_dir[PATH_MAX];
54
55static void __unused
56atexit_temp_dir(void)
57{
58
59	rmdir(temp_dir);
60}
61
62static void
63makefifo(const char *fifoname, const char *testname)
64{
65
66	if (mkfifo(fifoname, 0700) < 0)
67		err(-1, "%s: makefifo: mkfifo: %s", testname, fifoname);
68}
69
70static void
71cleanfifo(const char *fifoname, int fd1, int fd2)
72{
73
74	if (fd1 != -1)
75		close(fd1);
76	if (fd2 != -1)
77		close(fd2);
78	(void)unlink(fifoname);
79}
80
81static int
82openfifo(const char *fifoname, int *reader_fdp, int *writer_fdp)
83{
84	int error, fd1, fd2;
85
86	fd1 = open(fifoname, O_RDONLY | O_NONBLOCK);
87	if (fd1 < 0)
88		return (-1);
89	fd2 = open(fifoname, O_WRONLY | O_NONBLOCK);
90	if (fd2 < 0) {
91		error = errno;
92		close(fd1);
93		errno = error;
94		return (-1);
95	}
96	*reader_fdp = fd1;
97	*writer_fdp = fd2;
98
99	return (0);
100}
101
102/*
103 * POSIX does not allow lseek(2) on fifos, so we expect ESPIPE as a result.
104 */
105static void
106test_lseek(void)
107{
108	int reader_fd, writer_fd;
109
110	makefifo("testfifo", __func__);
111
112	if (openfifo("testfifo", &reader_fd, &writer_fd) < 0) {
113		warn("%s: openfifo", __func__);
114		cleanfifo("testfifo", -1, -1);
115		exit(-1);
116	}
117
118	if (lseek(reader_fd, 1, SEEK_CUR) >= 0) {
119		warnx("%s: lseek succeeded instead of returning ESPIPE",
120		    __func__);
121		cleanfifo("testfifo", reader_fd, writer_fd);
122		exit(-1);
123	}
124	if (errno != ESPIPE) {
125		warn("%s: lseek returned instead of ESPIPE", __func__);
126		cleanfifo("testfifo", reader_fd, writer_fd);
127		exit(-1);
128	}
129
130	cleanfifo("testfifo", reader_fd, writer_fd);
131}
132
133/*
134 * truncate(2) on FIFO should silently return success.
135 */
136static void
137test_truncate(void)
138{
139
140	makefifo("testfifo", __func__);
141
142	if (truncate("testfifo", 1024) != 0) {
143		warn("%s: truncate", __func__);
144		cleanfifo("testfifo", -1, -1);
145		exit(-1);
146	}
147
148	cleanfifo("testfifo", -1, -1);
149}
150
151static int
152test_ioctl_setclearflag(int fd, int flag, const char *testname,
153    const char *fdname, const char *flagname)
154{
155	int i;
156
157	i = 1;
158	if (ioctl(fd, flag, &i) < 0) {
159		warn("%s:%s: ioctl(%s, %s, 1)", testname, __func__, fdname,
160		    flagname);
161		cleanfifo("testfifo", -1, -1);
162		exit(-1);
163	}
164
165	i = 0;
166	if (ioctl(fd, flag, &i) < 0) {
167		warn("%s:%s: ioctl(%s, %s, 0)", testname, __func__, fdname,
168		    flagname);
169		cleanfifo("testfifo", -1, -1);
170		exit(-1);
171	}
172
173	return (0);
174}
175
176/*
177 * Test that various ioctls can be issued against the file descriptor.  We
178 * don't currently test the semantics of these changes here.
179 */
180static void
181test_ioctl(void)
182{
183	int reader_fd, writer_fd;
184
185	makefifo("testfifo", __func__);
186
187	if (openfifo("testfifo", &reader_fd, &writer_fd) < 0) {
188		warn("%s: openfifo", __func__);
189		cleanfifo("testfifo", -1, -1);
190		exit(-1);
191	}
192
193	/*
194	 * Set and remove the non-blocking I/O flag.
195	 */
196	if (test_ioctl_setclearflag(reader_fd, FIONBIO, __func__,
197	    "reader_fd", "FIONBIO") < 0) {
198		cleanfifo("testfifo", reader_fd, writer_fd);
199		exit(-1);
200	}
201
202	if (test_ioctl_setclearflag(writer_fd, FIONBIO, __func__,
203	    "writer_fd", "FIONBIO") < 0) {
204		cleanfifo("testfifo", reader_fd, writer_fd);
205		exit(-1);
206	}
207
208	/*
209	 * Set and remove the async I/O flag.
210	 */
211	if (test_ioctl_setclearflag(reader_fd, FIOASYNC, __func__,
212	    "reader_fd", "FIOASYNC") < 0) {
213		cleanfifo("testfifo", reader_fd, writer_fd);
214		exit(-1);
215	}
216
217	if (test_ioctl_setclearflag(writer_fd, FIOASYNC, __func__,
218	    "writer_fd", "FIONASYNC") < 0) {
219		cleanfifo("testfifo", reader_fd, writer_fd);
220		exit(-1);
221	}
222
223	cleanfifo("testfifo", reader_fd, writer_fd);
224}
225
226/*
227 * fchmod(2)/fchown(2) on FIFO should work.
228 */
229static void
230test_chmodchown(void)
231{
232	struct stat sb;
233	int reader_fd, writer_fd;
234	uid_t u;
235	gid_t g;
236
237	makefifo("testfifo", __func__);
238
239	if (openfifo("testfifo", &reader_fd, &writer_fd) < 0) {
240		warn("%s: openfifo", __func__);
241		cleanfifo("testfifo", -1, -1);
242		exit(-1);
243	}
244
245	if (fchmod(reader_fd, 0666) != 0) {
246		warn("%s: fchmod", __func__);
247		cleanfifo("testfifo", reader_fd, writer_fd);
248		exit(-1);
249	}
250
251	if (stat("testfifo", &sb) != 0) {
252		warn("%s: stat", __func__);
253		cleanfifo("testfifo", reader_fd, writer_fd);
254		exit(-1);
255	}
256
257	if ((sb.st_mode & 0777) != 0666) {
258		warnx("%s: stat chmod result", __func__);
259		cleanfifo("testfifo", reader_fd, writer_fd);
260		exit(-1);
261	}
262
263	if (fstat(writer_fd, &sb) != 0) {
264		warn("%s: fstat", __func__);
265		cleanfifo("testfifo", reader_fd, writer_fd);
266		exit(-1);
267	}
268
269	if ((sb.st_mode & 0777) != 0666) {
270		warnx("%s: fstat chmod result", __func__);
271		cleanfifo("testfifo", reader_fd, writer_fd);
272		exit(-1);
273	}
274
275	if (fchown(reader_fd, -1, -1) != 0) {
276		warn("%s: fchown 1", __func__);
277		cleanfifo("testfifo", reader_fd, writer_fd);
278		exit(-1);
279	}
280
281	u = geteuid();
282	if (u == 0)
283		u = 1;
284	g = getegid();
285	if (fchown(reader_fd, u, g) != 0) {
286		warn("%s: fchown 2", __func__);
287		cleanfifo("testfifo", reader_fd, writer_fd);
288		exit(-1);
289	}
290	if (stat("testfifo", &sb) != 0) {
291		warn("%s: stat", __func__);
292		cleanfifo("testfifo", reader_fd, writer_fd);
293		exit(-1);
294	}
295
296	if (sb.st_uid != u || sb.st_gid != g) {
297		warnx("%s: stat chown result", __func__);
298		cleanfifo("testfifo", reader_fd, writer_fd);
299		exit(-1);
300	}
301
302	if (fstat(writer_fd, &sb) != 0) {
303		warn("%s: fstat", __func__);
304		cleanfifo("testfifo", reader_fd, writer_fd);
305		exit(-1);
306	}
307
308	if (sb.st_uid != u || sb.st_gid != g) {
309		warnx("%s: fstat chown result", __func__);
310		cleanfifo("testfifo", reader_fd, writer_fd);
311		exit(-1);
312	}
313
314	cleanfifo("testfifo", -1, -1);
315}
316
317int
318main(void)
319{
320
321	strcpy(temp_dir, "fifo_misc.XXXXXXXXXXX");
322	if (mkdtemp(temp_dir) == NULL)
323		err(-1, "mkdtemp");
324	atexit(atexit_temp_dir);
325
326	if (chdir(temp_dir) < 0)
327		err(-1, "chdir %s", temp_dir);
328
329	test_lseek();
330	test_truncate();
331	test_ioctl();
332	test_chmodchown();
333
334	return (0);
335}
336