1/*-
2 * Copyright (c) 2008 Robert N. M. Watson
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27/*-
28 * This regression test attempts to confirm that the flags used at open-time
29 * for a file descriptor properly limit system calls that should be affected
30 * by those flags.  Currently:
31 *
32 * System call                    Policy                      Tested
33 * __acl_aclcheck_fd(2)           any                         no
34 * __acl_delete_fd(2)             any                         no
35 * __acl_get_fd(2)                any                         no
36 * __acl_set_fd(2)                any                         no
37 * aio_fsync(2)                   any                         no
38 * aio_read(2)                    O_RDONLY or O_RDWR          yes
39 * aio_write(2)                   O_WRONLY or O_RDWR          yes
40 * dup(2)                         any                         yes
41 * dup2(2)                        any                         yes
42 * extattr_delete_fd(2)           O_WRONLY or O_RDWR          no
43 * extattr_get_fd(2)              O_RDONLY or O_RDWR          no
44 * extattr_list_fd(2)             O_RDONLY or O_RDWR          no
45 * extattr_set_fd(2)              O_WRONLY or O_RDWR          no
46 * fchdir(2)                      any directory               yes
47 * fchflags(2)                    any                         yes
48 * fchmod(2)                      any                         yes
49 * fchown(2)                      any                         yes
50 * flock(2)                       any                         yes
51 * fpathconf(2)                   any                         yes
52 * fstat(2)                       any                         yes
53 * fstatfs(2)                     any                         yes
54 * fsync(2)                       any                         yes
55 * ftruncate(2)                   O_WRONLY or O_RDWR          yes
56 * futimes(2)                     any                         yes
57 * getdents(2)                    O_RDONLY directory          yes
58 * lseek(2)                       any                         yes
59 * mmap(2) PROT_READ              O_RDONLY or O_RDWR          yes
60 * mmap(2) PROT_WRITE             O_WRONLY or O_RDWR          yes
61 * mmap(2) PROT_WRITE + MAP_PRIV  O_RDONLY or O_RDWR          yes
62 * mmap(2) PROT_EXEC              O_RDONLY or O_RDWR          yes
63 * pread(2)                       O_RDONLY or O_RDWR          yes
64 * preadv(2)                      O_RDONLY or O_RDWR          yes
65 * pwrite(2)                      O_WRONLY or O_RDWR          yes
66 * pwritev(2)                     O_WRONLY or O_RDWR          yes
67 * read(2)                        O_RDONLY or O_RDWR          yes
68 * readv(2)                       O_RDONLY or O_RDWR          yes
69 * sendfile(2)                    O_RDONLY or O_RDWR on file  yes
70 * write(2)                       O_WRONLY or O_RDWR          yes
71 * writev(2)                      O_WRONLY or O_RDWR          yes
72 *
73 * These checks do not verify that original permissions would allow the
74 * operation or that open is properly impacted by permissions, just that once
75 * a file descriptor is held, open-time limitations are implemented.
76 *
77 * We do, however, test that directories cannot be opened as writable.
78 *
79 * XXXRW: Arguably we should also test combinations of bits to mmap(2).
80 *
81 * XXXRW: Should verify mprotect() remapping limits.
82 *
83 * XXXRW: kqueue(2)/kevent(2), poll(2), select(2)
84 *
85 * XXXRW: oaio_read(2), oaio_write(2), freebsd6_*(2).
86 *
87 * XXXRW: __mac*(2)
88 *
89 * XXXRW: message queue and shared memory fds?
90 */
91
92#include <sys/param.h>
93#include <sys/mman.h>
94#include <sys/mount.h>
95#include <sys/socket.h>
96#include <sys/stat.h>
97#include <sys/sysctl.h>
98#include <sys/uio.h>
99
100#include <aio.h>
101#include <dirent.h>
102#include <err.h>
103#include <errno.h>
104#include <fcntl.h>
105#include <limits.h>
106#include <stdio.h>
107#include <stdlib.h>
108#include <string.h>
109#include <unistd.h>
110
111#define	PERM_FILE	0644		/* Allow read, write.  Someday exec? */
112#define	PERM_DIR	0755		/* Allow read, write, exec. */
113
114/*
115 * Modes to try all tests with.
116 */
117static const int file_modes[] = { O_RDONLY, O_WRONLY, O_RDWR,
118    O_RDONLY | O_TRUNC, O_WRONLY | O_TRUNC, O_RDWR | O_TRUNC };
119static const int file_modes_count = nitems(file_modes);
120
121static const int dir_modes[] = { O_RDONLY };
122static const int dir_modes_count = nitems(dir_modes);
123
124static int testnum;
125static int aio_present;
126
127static void
128ok_mode(const char *testname, const char *comment, int mode)
129{
130
131	testnum++;
132	if (comment == NULL)
133		printf("ok %d - %s # mode 0x%x\n", testnum, testname, mode);
134	else
135		printf("ok %d - %s # mode 0x%x - %s\n", testnum, testname,
136		    mode, comment);
137}
138
139static void
140notok_mode(const char *testname, const char *comment, int mode)
141{
142
143	testnum++;
144	if (comment == NULL)
145		printf("not ok %d - %s # mode 0x%x\n", testnum, testname,
146		    mode);
147	else
148		printf("not ok %d - %s # mode 0x%x - %s\n", testnum, testname,
149		    mode, comment);
150}
151
152/*
153 * Before we get started, confirm that we can't open directories writable.
154 */
155static void
156try_directory_open(const char *testname, const char *directory,
157    int mode, int expected_errno)
158{
159	int dfd;
160
161	dfd = open(directory, mode);
162	if (dfd >= 0) {
163		if (expected_errno)
164			notok_mode(testname, "opened", mode);
165		else
166			ok_mode(testname, NULL, mode);
167		close(dfd);
168	} else {
169		if (expected_errno && expected_errno == errno)
170			ok_mode(testname, NULL, mode);
171		else if (expected_errno != 0)
172			notok_mode(testname, "wrong errno", mode);
173		else
174			notok_mode(testname, "failed", mode);
175	}
176}
177
178static void
179check_directory_open_modes(const char *directory, const int *modes,
180    int modes_count)
181{
182	int expected_errno, i, mode;
183
184	/*
185	 * Directories should only open with O_RDONLY.  Notice that we use
186	 * file_modes and not dirmodes.
187	 */
188	for (i = 0; i < modes_count; i++) {
189		mode = modes[i];
190		if (mode == O_RDONLY)
191			expected_errno = 0;
192		else
193			expected_errno = EISDIR;
194		try_directory_open(__func__, directory, mode,
195		    expected_errno);
196	}
197}
198
199static void
200check_dup(const char *testname, const char *path, const int *modes,
201    int modes_count)
202{
203	int dfd, fd, i, mode;
204
205	/*
206	 * dup() should work regardless of open mode.
207	 */
208	for (i = 0; i < modes_count; i++) {
209		mode = modes[i];
210		fd = open(path, mode);
211		if (fd < 0) {
212			notok_mode(testname, "open", mode);
213			continue;
214		}
215		dfd = dup(fd);
216		if (dfd >= 0) {
217			ok_mode(testname, NULL, mode);
218			close(dfd);
219		} else
220			notok_mode(testname, NULL, mode);
221		close(fd);
222	}
223}
224
225static void
226check_dup2(const char *testname, const char *path, const int *modes,
227    int modes_count)
228{
229	int dfd, fd, i, mode;
230
231	/*
232	 * dup2() should work regardless of open mode.
233	 */
234	for (i = 0; i < modes_count; i++) {
235		mode = modes[i];
236		fd = open(path, mode);
237		if (fd < 0) {
238			notok_mode(testname, "open", mode);
239			continue;
240		}
241		dfd = dup2(fd, 500);	/* Arbitrary but high number. */
242		if (dfd >= 0) {
243			ok_mode(testname, NULL, mode);
244			close(dfd);
245		} else
246			notok_mode(testname, NULL, mode);
247		close(fd);
248	}
249}
250
251static void
252check_fchdir(const char *testname, const char *path, const int *modes,
253    int modes_count)
254{
255	int fd, i, mode;
256
257	/*
258	 * fchdir() should work regardless of open mode.
259	 */
260	for (i = 0; i < modes_count; i++) {
261		mode = modes[i];
262		fd = open(path, mode);
263		if (fd < 0) {
264			notok_mode(testname, "open", mode);
265			continue;
266		}
267		if (fchdir(fd) == 0)
268			ok_mode(testname, NULL, mode);
269		else
270			notok_mode(testname, "failed", mode);
271		close(fd);
272	}
273}
274
275static void
276check_fchflags(const char *testname, const char *path, const int *modes,
277    int modes_count)
278{
279	int fd, i, mode;
280
281	/*
282	 * fchflags() should work regardless of open mode.
283	 */
284	for (i = 0; i < modes_count; i++) {
285		mode = modes[i];
286		fd = open(path, mode);
287		if (fd < 0) {
288			notok_mode(testname, "open", mode);
289			continue;
290		}
291		if (fchflags(fd, UF_NODUMP) == 0)
292			ok_mode(testname, NULL, mode);
293		else
294			notok_mode(testname, "failed", mode);
295		close(fd);
296	}
297}
298
299static void
300check_fchmod(const char *testname, const char *path, int setmode,
301    const int *modes, int modes_count)
302{
303	int fd, i, mode;
304
305	/*
306	 * fchmod() should work regardless of open mode.
307	 */
308	for (i = 0; i < modes_count; i++) {
309		mode = modes[i];
310		fd = open(path, mode);
311		if (fd < 0) {
312			notok_mode(testname, "open", mode);
313			continue;
314		}
315		if (fchmod(fd, setmode) == 0)
316			ok_mode(testname, NULL, mode);
317		else
318			notok_mode(testname, "failed", mode);
319		close(fd);
320	}
321}
322
323static void
324check_fchown(const char *testname, const char *path, const int *modes,
325    int modes_count)
326{
327	int fd, i, mode;
328
329	/*
330	 * fchown() should work regardless of open mode.
331	 */
332	for (i = 0; i < modes_count; i++) {
333		mode = modes[i];
334		fd = open(path, mode);
335		if (fd < 0) {
336			notok_mode(testname, "open", mode);
337			continue;
338		}
339		if (fchown(fd, -1, -1) == 0)
340			ok_mode(testname, NULL, mode);
341		else
342			notok_mode(testname, "failed", mode);
343		close(fd);
344	}
345}
346
347static void
348check_flock(const char *testname, const char *path, const int *modes,
349    int modes_count)
350{
351	int fd, i, mode;
352
353	/*
354	 * flock() should work regardless of open mode.
355	 */
356	for (i = 0; i < modes_count; i++) {
357		mode = modes[i];
358		fd = open(path, mode);
359		if (fd < 0) {
360			notok_mode(testname, "open", mode);
361			continue;
362		}
363		if (flock(fd, LOCK_EX) == 0)
364			ok_mode(testname, NULL, mode);
365		else
366			notok_mode(testname, "failed", mode);
367		close(fd);
368	}
369}
370
371static void
372check_fpathconf(const char *testname, const char *path, const int *modes,
373    int modes_count)
374{
375	int fd, i, mode;
376	long l;
377
378	/*
379	 * fpathconf() should work regardless of open mode.
380	 */
381	for (i = 0; i < modes_count; i++) {
382		mode = modes[i];
383		fd = open(path, mode);
384		if (fd < 0) {
385			notok_mode(testname, "open", mode);
386			continue;
387		}
388		l = fpathconf(fd, _PC_FILESIZEBITS);
389		if (l >= 0)
390			ok_mode(testname, NULL, mode);
391		else
392			notok_mode(testname, "failed", mode);
393		close(fd);
394	}
395}
396
397static void
398check_fstat(const char *testname, const char *path, const int *modes,
399    int modes_count)
400{
401	struct stat sb;
402	int fd, i, mode;
403
404	/*
405	 * fstat() should work regardless of open mode.
406	 */
407	for (i = 0; i < modes_count; i++) {
408		mode = modes[i];
409		fd = open(path, mode);
410		if (fd < 0) {
411			notok_mode(testname, "open", mode);
412			continue;
413		}
414		if (fstat(fd, &sb) == 0)
415			ok_mode(testname, NULL, mode);
416		else
417			notok_mode(testname, "failed", mode);
418		close(fd);
419	}
420}
421
422static void
423check_fstatfs(const char *testname, const char *path, const int *modes,
424    int modes_count)
425{
426	struct statfs statfs;
427	int fd, i, mode;
428
429	/*
430	 * fstatfs() should work regardless of open mode.
431	 */
432	for (i = 0; i < modes_count; i++) {
433		mode = modes[i];
434		fd = open(path, mode);
435		if (fd < 0) {
436			notok_mode(testname, "open", mode);
437			continue;
438		}
439		if (fstatfs(fd, &statfs) == 0)
440			ok_mode(testname, NULL, mode);
441		else
442			notok_mode(testname, "failed", mode);
443		close(fd);
444	}
445}
446
447static void
448check_fsync(const char *testname, const char *path, const int *modes,
449    int modes_count)
450{
451	int fd, i, mode;
452
453	/*
454	 * fstatfs() should work regardless of open mode.
455	 */
456	for (i = 0; i < modes_count; i++) {
457		mode = modes[i];
458		fd = open(path, mode);
459		if (fd < 0) {
460			notok_mode(testname, "open", mode);
461			continue;
462		}
463		if (fsync(fd) == 0)
464			ok_mode(testname, NULL, mode);
465		else
466			notok_mode(testname, "failed", mode);
467		close(fd);
468	}
469}
470
471static void
472check_ftruncate(const char *testname, const char *path, const int *modes,
473    int modes_count)
474{
475	struct stat sb;
476	int fd, i, mode;
477
478	/*
479	 * ftruncate() should work as long as long as (mode & O_ACCMODE) is
480	 * O_RDWR or O_WRONLY.
481	 *
482	 * Directories should never be writable, so this test should always
483	 * pass for directories...
484	 */
485	for (i = 0; i < modes_count; i++) {
486		mode = modes[i];
487		fd = open(path, mode);
488		if (fd < 0) {
489			notok_mode(testname, "open", mode);
490			notok_mode(testname, "truncate1 skipped", mode);
491			notok_mode(testname, "truncate2 skipped", mode);
492			notok_mode(testname, "truncate3 skipped", mode);
493			continue;
494		}
495		if (fstat(fd, &sb) < 0) {
496			notok_mode(testname, "fstat", mode);
497			notok_mode(testname, "truncate1 skipped", mode);
498			notok_mode(testname, "truncate2 skipped", mode);
499			notok_mode(testname, "truncate3 skipped", mode);
500			close(fd);
501			continue;
502		}
503		ok_mode(testname, "setup", mode);
504
505		/* Truncate to grow file. */
506		if (ftruncate(fd, sb.st_size + 1) == 0) {
507			if (((mode & O_ACCMODE) == O_WRONLY) ||
508			    ((mode & O_ACCMODE) == O_RDWR))
509				ok_mode(testname, "truncate1 succeeded",
510				    mode);
511			else {
512				notok_mode(testname, "truncate1 succeeded",
513				    mode);
514				notok_mode(testname, "truncate2 skipped",
515				    mode);
516				notok_mode(testname, "truncate3 skipped",
517				    mode);
518				close(fd);
519				continue;
520			}
521		} else {
522			if (((mode & O_ACCMODE) == O_WRONLY) ||
523			    ((mode & O_ACCMODE) == O_RDWR)) {
524				notok_mode(testname, "truncate1 failed",
525				    mode);
526				notok_mode(testname, "truncate2 skipped",
527				    mode);
528				notok_mode(testname, "truncate3 skipped",
529				    mode);
530				close(fd);
531				continue;
532			} else
533				ok_mode(testname, "truncate1 failed", mode);
534		}
535
536		/* Truncate to same size. */
537		if (ftruncate(fd, sb.st_size + 1) == 0) {
538			if (((mode & O_ACCMODE) == O_WRONLY) ||
539			    ((mode & O_ACCMODE) == O_RDWR))
540				ok_mode(testname, "truncate2 succeeded",
541				    mode);
542			else {
543				notok_mode(testname, "truncate2 succeeded",
544				    mode);
545				notok_mode(testname, "truncate3 skipped",
546				    mode);
547				close(fd);
548				continue;
549			}
550		} else {
551			if (((mode & O_ACCMODE) == O_WRONLY) ||
552			    ((mode & O_ACCMODE) == O_RDWR)) {
553				notok_mode(testname, "truncate2 failed",
554				    mode);
555				notok_mode(testname, "truncate3 skipped",
556				    mode);
557				close(fd);
558				continue;
559			} else
560				ok_mode(testname, "truncate2 failed", mode);
561		}
562
563		/* Truncate to shrink. */
564		if (ftruncate(fd, sb.st_size) == 0) {
565			if (((mode & O_ACCMODE) == O_WRONLY) ||
566			    ((mode & O_ACCMODE) == O_RDWR))
567				ok_mode(testname, "truncate3 succeeded",
568				    mode);
569			else
570				notok_mode(testname, "truncate3 succeeded",
571				    mode);
572		} else {
573			if (((mode & O_ACCMODE) == O_WRONLY) ||
574			    ((mode & O_ACCMODE) == O_RDWR))
575				notok_mode(testname, "truncate3 failed",
576				    mode);
577			else
578				ok_mode(testname, "truncate3 failed", mode);
579		}
580		close(fd);
581	}
582}
583
584static void
585check_futimes(const char *testname, const char *path, const int *modes,
586    int modes_count)
587{
588	int fd, i, mode;
589
590	/*
591	 * futimes() should work regardless of open mode.
592	 */
593	for (i = 0; i < modes_count; i++) {
594		mode = modes[i];
595		fd = open(path, mode);
596		if (fd < 0) {
597			notok_mode(testname, "open", mode);
598			continue;
599		}
600		if (futimes(fd, NULL) == 0)
601			ok_mode(testname, NULL, mode);
602		else
603			notok_mode(testname, "failed", mode);
604		close(fd);
605	}
606}
607
608static void
609check_lseek(const char *testname, const char *path, const int *modes,
610    int modes_count)
611{
612	int fd, i, mode;
613
614	/*
615	 * lseek() should work regardless of open mode.
616	 */
617	for (i = 0; i < modes_count; i++) {
618		mode = modes[i];
619		fd = open(path, mode);
620		if (fd < 0) {
621			notok_mode(testname, "open", mode);
622			continue;
623		}
624		if (lseek(fd, 100, SEEK_SET) == 100)
625			ok_mode(testname, NULL, mode);
626		else
627			notok_mode(testname, "failed", mode);
628		close(fd);
629	}
630}
631
632static void
633check_getdents(const char *testname, const char *path, int isdir,
634    const int *modes, int modes_count)
635{
636	int fd, i, mode;
637	char buf[8192];
638
639	/*
640	 * getdents() should always work on directories and never on files,
641	 * assuming directories are always opened for read (which they are).
642	 */
643	for (i = 0; i < modes_count; i++) {
644		mode = modes[i];
645		fd = open(path, mode);
646		if (fd < 0) {
647			notok_mode(testname, "open", mode);
648			continue;
649		}
650		if (getdents(fd, buf, sizeof(buf)) >= 0) {
651			if (isdir && ((mode & O_ACCMODE) == O_RDONLY))
652				ok_mode(testname, "directory succeeded",
653				    mode);
654			else if (isdir)
655				notok_mode(testname, "directory succeeded",
656				    mode);
657			else
658				notok_mode(testname, "file succeeded", mode);
659		} else {
660			if (isdir && ((mode & O_ACCMODE) == O_RDONLY))
661				notok_mode(testname, "directory failed",
662				    mode);
663			else if (isdir)
664				ok_mode(testname, "directory failed", mode);
665			else
666				ok_mode(testname, "file failed", mode);
667		}
668		close(fd);
669	}
670}
671
672static void
673check_sendfile(const char *testname, const char *path, int isdir,
674    const int *modes, int modes_count)
675{
676	int fd, i, mode, sv[2];
677	off_t sent;
678
679	/*
680	 * sendfile() should work only on files, and only when the access mode
681	 * is O_RDONLY or O_RDWR.
682	 */
683	for (i = 0; i < modes_count; i++) {
684		mode = modes[i];
685		fd = open(path, mode);
686		if (fd < 0) {
687			notok_mode(testname, "open", mode);
688			continue;
689		}
690		if (socketpair(PF_LOCAL, SOCK_STREAM, 0, sv) < 0) {
691			notok_mode(testname, "socketpair", mode);
692			continue;
693		}
694		if (sendfile(fd, sv[0], 0, 1, NULL, &sent, 0) == 0) {
695			if (isdir)
696				notok_mode(testname, "directory succeeded",
697				    mode);
698			else if (((mode & O_ACCMODE) == O_RDONLY) ||
699			    ((mode & O_ACCMODE) == O_RDWR))
700				ok_mode(testname, "succeeded", mode);
701			else
702				notok_mode(testname, "succeeded", mode);
703		} else {
704			if (isdir)
705				ok_mode(testname, "directory failed", mode);
706			else if (((mode & O_ACCMODE) == O_RDONLY) ||
707			    ((mode & O_ACCMODE) == O_RDWR))
708				notok_mode(testname, "failed", mode);
709			else
710				ok_mode(testname, "failed", mode);
711		}
712		close(sv[0]);
713		close(sv[1]);
714		close(fd);
715	}
716}
717
718/*
719 * Various functions write, so just make write-like wrappers for them.
720 */
721typedef ssize_t (*write_fn)(int d, const void *buf, size_t nbytes);
722
723static ssize_t
724writev_wrapper(int d, const void *buf, size_t nbytes)
725{
726	struct iovec iov;
727
728	iov.iov_base = (void *)buf;
729	iov.iov_len = nbytes;
730	return (writev(d, &iov, 1));
731}
732
733static ssize_t
734pwrite_wrapper(int d, const void *buf, size_t nbytes)
735{
736
737	return (pwrite(d, buf, nbytes, 0));
738}
739
740static ssize_t
741pwritev_wrapper(int d, const void *buf, size_t nbytes)
742{
743	struct iovec iov;
744
745	iov.iov_base = (void *)buf;
746	iov.iov_len = nbytes;
747	return (pwritev(d, &iov, 1, 0));
748}
749
750static ssize_t
751aio_write_wrapper(int d, const void *buf, size_t nbytes)
752{
753	struct aiocb aiocb;
754	struct aiocb const *aiocb_array[] = { &aiocb };
755
756	bzero(&aiocb, sizeof(aiocb));
757	aiocb.aio_fildes = d;
758	aiocb.aio_buf = (void *)buf;
759	aiocb.aio_nbytes = nbytes;
760	if (aio_write(&aiocb) < 0)
761		return (-1);
762	aiocb_array[0] = &aiocb;
763	if (aio_suspend(aiocb_array, 1, NULL) < 0)
764		return (-1);
765	return (aio_return(&aiocb));
766}
767
768static void
769check_write(const char *testname, write_fn fn, const char *path,
770    const int *modes, int modes_count)
771{
772	int fd, i, mode;
773	char ch;
774
775	/*
776	 * write() should never succeed for directories, but especially
777	 * because they can only be opened read-only.  write() on files
778	 * should succeed for O_WRONLY and O_RDWR descriptors.
779	 */
780
781	for (i = 0; i < modes_count; i++) {
782		mode = modes[i];
783		fd = open(path, mode);
784		if (fd < 0) {
785			notok_mode(testname, "open", mode);
786			continue;
787		}
788		if (fn(fd, &ch, sizeof(ch)) < 0) {
789			if ((mode & O_ACCMODE) == O_WRONLY ||
790			    (mode & O_ACCMODE) == O_RDWR)
791				notok_mode(testname, "write failed", mode);
792			else
793				ok_mode(testname, "write failed", mode);
794		} else {
795			if (!((mode & O_ACCMODE) == O_WRONLY ||
796			    (mode & O_ACCMODE) == O_RDWR))
797				notok_mode(testname, "write succeeded", mode);
798			else
799				ok_mode(testname, "write succeeded", mode);
800		}
801		close(fd);
802	}
803}
804
805/*
806 * Various functions read, so just make read-like wrappers for them.
807 */
808typedef ssize_t (*read_fn)(int d, void *buf, size_t nbytes);
809
810static ssize_t
811readv_wrapper(int d, void *buf, size_t nbytes)
812{
813	struct iovec iov;
814
815	iov.iov_base = buf;
816	iov.iov_len = nbytes;
817	return (readv(d, &iov, 1));
818}
819
820static ssize_t
821pread_wrapper(int d, void *buf, size_t nbytes)
822{
823
824	return (pread(d, buf, nbytes, 0));
825}
826
827static ssize_t
828preadv_wrapper(int d, void *buf, size_t nbytes)
829{
830	struct iovec iov;
831
832	iov.iov_base = buf;
833	iov.iov_len = nbytes;
834	return (preadv(d, &iov, 1, 0));
835}
836
837static ssize_t
838aio_read_wrapper(int d, void *buf, size_t nbytes)
839{
840	struct aiocb aiocb;
841	struct aiocb const *aiocb_array[] = { &aiocb };
842
843	bzero(&aiocb, sizeof(aiocb));
844	aiocb.aio_fildes = d;
845	aiocb.aio_buf = buf;
846	aiocb.aio_nbytes = nbytes;
847	if (aio_read(&aiocb) < 0)
848		return (-1);
849	if (aio_suspend(aiocb_array, 1, NULL) < 0)
850		return (-1);
851	return (aio_return(&aiocb));
852}
853
854static void
855check_read(const char *testname, read_fn fn, const char *path,
856    const int *modes, int modes_count)
857{
858	int fd, i, mode;
859	char ch;
860
861	/*
862	 * read() should (generally) succeeded on directories.  read() on
863	 * files should succeed for O_RDONLY and O_RDWR descriptors.
864	 */
865	for (i = 0; i < modes_count; i++) {
866		mode = modes[i];
867		fd = open(path, mode);
868		if (fd < 0) {
869			notok_mode(testname, "open", mode);
870			continue;
871		}
872		if (fn(fd, &ch, sizeof(ch)) < 0) {
873			if ((mode & O_ACCMODE) == O_RDONLY ||
874			    (mode & O_ACCMODE) == O_RDWR)
875				notok_mode(testname, "read failed", mode);
876			else
877				ok_mode(testname, "read failed", mode);
878		} else {
879			if (!((mode & O_ACCMODE) == O_RDONLY ||
880			    (mode & O_ACCMODE) == O_RDWR))
881				notok_mode(testname, "read succeeded", mode);
882			else
883				ok_mode(testname, "read succeeded", mode);
884		}
885		close(fd);
886	}
887}
888
889static void
890check_mmap_read(const char *testname, const char *path, int isdir,
891    const int *modes, int modes_count)
892{
893	int fd, i, mode;
894	char *addr;
895
896	/*
897	 * mmap() read should fail for directories (ideally?) but succeed for
898	 * O_RDONLY and O_RDWR file descriptors.
899	 */
900	for (i = 0; i < modes_count; i++) {
901		mode = modes[i];
902		fd = open(path, mode);
903		if (fd < 0) {
904			notok_mode(testname, "open", mode);
905			continue;
906		}
907		addr = mmap(NULL, getpagesize(), PROT_READ, MAP_SHARED, fd,
908		    0);
909		if (addr == MAP_FAILED) {
910			if (isdir)
911				ok_mode(testname, "mmap dir failed", mode);
912			else if ((mode & O_ACCMODE) == O_RDONLY ||
913			    (mode & O_ACCMODE) == O_RDWR)
914				notok_mode(testname, "mmap file failed",
915				    mode);
916			else
917				ok_mode(testname, "mmap file failed", mode);
918		} else {
919			if (isdir)
920				notok_mode(testname, "mmap dir succeeded",
921				    mode);
922			else if ((mode & O_ACCMODE) == O_RDONLY ||
923			    (mode & O_ACCMODE) == O_RDWR)
924				ok_mode(testname, "mmap file succeeded",
925				    mode);
926			else
927				notok_mode(testname, "mmap file succeeded",
928				    mode);
929			(void)munmap(addr, getpagesize());
930		}
931		close(fd);
932	}
933}
934
935static void
936check_mmap_write(const char *testname, const char *path, const int *modes,
937    int modes_count)
938{
939	int fd, i, mode;
940	char *addr;
941
942	/*
943	 * mmap() will always fail for directories (ideally) as they are
944	 * always open O_RDONLY.  Check for O_WRONLY or O_RDWR to permit a
945	 * write mapping.  This variant does a MAP_SHARED mapping, but we
946	 * are also interested in MAP_PRIVATE.
947	 */
948	for (i = 0; i < modes_count; i++) {
949		mode = modes[i];
950		fd = open(path, mode);
951		if (fd < 0) {
952			notok_mode(testname, "open", mode);
953			continue;
954		}
955		addr = mmap(NULL, getpagesize(), PROT_WRITE, MAP_SHARED, fd,
956		    0);
957		if (addr == MAP_FAILED) {
958			if ((mode & O_ACCMODE) == O_WRONLY ||
959			    (mode & O_ACCMODE) == O_RDWR)
960				notok_mode(testname, "mmap failed",
961				    mode);
962			else
963				ok_mode(testname, "mmap failed", mode);
964		} else {
965			if ((mode & O_ACCMODE) == O_WRONLY ||
966			    (mode & O_ACCMODE) == O_RDWR)
967				ok_mode(testname, "mmap succeeded",
968				    mode);
969			else
970				notok_mode(testname, "mmap succeeded", mode);
971			(void)munmap(addr, getpagesize());
972		}
973		close(fd);
974	}
975}
976
977static void
978check_mmap_exec(const char *testname, const char *path, int isdir,
979    const int *modes, int modes_count)
980{
981	int fd, i, mode;
982	char *addr;
983
984	/*
985	 * mmap() exec should fail for directories (ideally?) but succeed for
986	 * O_RDONLY and O_RDWR file descriptors.
987	 */
988	for (i = 0; i < modes_count; i++) {
989		mode = modes[i];
990		fd = open(path, mode);
991		if (fd < 0) {
992			notok_mode(testname, "open", mode);
993			continue;
994		}
995		addr = mmap(NULL, getpagesize(), PROT_EXEC, MAP_SHARED, fd,
996		    0);
997		if (addr == MAP_FAILED) {
998			if (isdir)
999				ok_mode(testname, "mmap dir failed", mode);
1000			else if ((mode & O_ACCMODE) == O_RDONLY ||
1001			    (mode & O_ACCMODE) == O_RDWR)
1002				notok_mode(testname, "mmap file failed",
1003				    mode);
1004			else
1005				ok_mode(testname, "mmap file failed", mode);
1006		} else {
1007			if (isdir)
1008				notok_mode(testname, "mmap dir succeeded",
1009				    mode);
1010			else
1011				ok_mode(testname, "mmap file succeeded",
1012				    mode);
1013			(void)munmap(addr, getpagesize());
1014		}
1015		close(fd);
1016	}
1017}
1018
1019static void
1020check_mmap_write_private(const char *testname, const char *path, int isdir,
1021    const int *modes, int modes_count)
1022{
1023	int fd, i, mode;
1024	char *addr;
1025
1026	/*
1027	 * mmap() write private should succeed for readable descriptors
1028	 * except for directories.
1029	 */
1030	for (i = 0; i < modes_count; i++) {
1031		mode = modes[i];
1032		fd = open(path, mode);
1033		if (fd < 0) {
1034			notok_mode(testname, "open", mode);
1035			continue;
1036		}
1037		addr = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE,
1038		    MAP_PRIVATE, fd, 0);
1039		if (addr == MAP_FAILED) {
1040			if (isdir)
1041				ok_mode(testname, "mmap dir failed", mode);
1042			else if ((mode & O_ACCMODE) == O_RDONLY ||
1043			    (mode & O_ACCMODE) == O_RDWR)
1044				notok_mode(testname, "mmap file failed",
1045				    mode);
1046			else
1047				ok_mode(testname, "mmap file failed", mode);
1048		} else {
1049			if (isdir)
1050				notok_mode(testname, "mmap dir succeeded",
1051				    mode);
1052			else if ((mode & O_ACCMODE) == O_RDONLY ||
1053			    (mode & O_ACCMODE) == O_RDWR)
1054				ok_mode(testname, "mmap file succeeded",
1055				    mode);
1056			else
1057				notok_mode(testname, "mmap file succeeded",
1058				    mode);
1059			(void)munmap(addr, getpagesize());
1060		}
1061		close(fd);
1062	}
1063}
1064
1065int
1066main(void)
1067{
1068	char dir_path[PATH_MAX], file_path[PATH_MAX];
1069	int dummy, fd;
1070	size_t size;
1071
1072	aio_present = 0;
1073	size = sizeof(dummy);
1074	if (sysctlbyname("vfs.aio", &dummy, &size, NULL, 0) < 0) {
1075		if (errno == EISDIR)
1076			aio_present = 1;
1077	}
1078
1079	strlcpy(dir_path, "/tmp/open-dir.XXXXXXXXXXX", sizeof(dir_path));
1080	if (mkdtemp(dir_path) == NULL)
1081		err(1, "mkdtemp");
1082	if (chmod(dir_path, PERM_DIR) < 0) {
1083		warn("chmod %s", dir_path);
1084		(void)rmdir(dir_path);
1085		exit(1);
1086	}
1087	strlcpy(file_path, "/tmp/open-file.XXXXXXXXXXX", sizeof(file_path));
1088	fd = mkstemp(file_path);
1089	if (fd < 0) {
1090		warn("mkstemp");
1091		(void)rmdir(dir_path);
1092		exit(1);
1093	}
1094	close(fd);
1095	if (chmod(file_path, PERM_FILE) < 0) {
1096		warn("chmod %s", file_path);
1097		(void)unlink(file_path);
1098		(void)rmdir(dir_path);
1099		exit(1);
1100	}
1101	check_directory_open_modes(dir_path, file_modes, file_modes_count);
1102
1103	check_dup("check_dup_dir", dir_path, dir_modes, dir_modes_count);
1104	check_dup("check_dup_file", file_path, file_modes, file_modes_count);
1105
1106	check_dup2("check_dup2_dir", dir_path, dir_modes, dir_modes_count);
1107	check_dup2("check_dup2_file", file_path, file_modes,
1108	    file_modes_count);
1109
1110	check_fchdir("check_fchdir", dir_path, dir_modes, dir_modes_count);
1111
1112	check_fchflags("check_fchflags_dir", dir_path, dir_modes,
1113	    dir_modes_count);
1114	check_fchflags("check_fchflags_file", file_path, file_modes,
1115	    file_modes_count);
1116
1117	check_fchmod("check_fchmod_dir", dir_path, PERM_DIR, dir_modes,
1118	    dir_modes_count);
1119	check_fchmod("check_fchmod_file", file_path, PERM_FILE, file_modes,
1120	    file_modes_count);
1121
1122	check_fchown("check_fchown_dir", dir_path, dir_modes,
1123	    dir_modes_count);
1124	check_fchown("check_fchown_file", file_path, file_modes,
1125	    file_modes_count);
1126
1127	check_flock("check_flock_dir", dir_path, dir_modes, dir_modes_count);
1128	check_flock("check_flock_file", file_path, file_modes,
1129	    file_modes_count);
1130
1131	check_fpathconf("check_fpathconf_dir", dir_path, dir_modes,
1132	    dir_modes_count);
1133	check_fpathconf("check_fpathconf_file", file_path, file_modes,
1134	    file_modes_count);
1135
1136	check_fstat("check_fstat_dir", dir_path, dir_modes, dir_modes_count);
1137	check_fstat("check_fstat_file", file_path, file_modes,
1138	    file_modes_count);
1139
1140	check_fstatfs("check_fstatfs_dir", dir_path, dir_modes,
1141	    dir_modes_count);
1142	check_fstatfs("check_fstatfs_file", file_path, file_modes,
1143	    file_modes_count);
1144
1145	check_fsync("check_fsync_dir", dir_path, dir_modes, dir_modes_count);
1146	check_fsync("check_fsync_file", file_path, file_modes,
1147	    file_modes_count);
1148
1149	check_ftruncate("check_ftruncate_dir", dir_path, dir_modes,
1150	    dir_modes_count);
1151	check_ftruncate("check_ftruncate_file", file_path, file_modes,
1152	    file_modes_count);
1153
1154	check_futimes("check_futimes_dir", dir_path, dir_modes,
1155	    dir_modes_count);
1156	check_futimes("check_futimes_file", file_path, file_modes,
1157	    file_modes_count);
1158
1159	check_lseek("check_lseek_dir", dir_path, dir_modes, dir_modes_count);
1160	check_lseek("check_lseek_file", file_path, file_modes,
1161	    file_modes_count);
1162
1163	check_getdents("check_getdents_dir", dir_path, 1, dir_modes,
1164	    dir_modes_count);
1165	check_getdents("check_getdents_file", file_path, 0, file_modes,
1166	    file_modes_count);
1167
1168	check_sendfile("check_sendfile_dir", dir_path, 1, dir_modes,
1169	    dir_modes_count);
1170	check_sendfile("check_sendfile_file", file_path, 0, file_modes,
1171	    file_modes_count);
1172
1173	check_write("check_write_dir", write, dir_path, dir_modes,
1174	    dir_modes_count);
1175	check_write("check_write_file", write, file_path, file_modes,
1176	    file_modes_count);
1177
1178	check_write("check_writev_dir", writev_wrapper, dir_path, dir_modes,
1179	    dir_modes_count);
1180	check_write("check_writev_file", writev_wrapper, file_path,
1181	    file_modes, file_modes_count);
1182
1183	check_write("check_pwrite_dir", pwrite_wrapper, dir_path, dir_modes,
1184	    dir_modes_count);
1185	check_write("check_pwrite_file", pwrite_wrapper, file_path,
1186	    file_modes, file_modes_count);
1187
1188	check_write("check_pwritev_dir", pwritev_wrapper, dir_path,
1189	    dir_modes, dir_modes_count);
1190	check_write("check_pwritev_file", pwritev_wrapper, file_path,
1191	    file_modes, file_modes_count);
1192
1193	if (aio_present) {
1194		check_write("check_aio_write_dir", aio_write_wrapper,
1195		    dir_path, dir_modes, dir_modes_count);
1196		check_write("check_aio_write_file", aio_write_wrapper,
1197		    file_path, file_modes, file_modes_count);
1198	}
1199
1200	check_read("check_read_dir", read, dir_path, dir_modes,
1201	    dir_modes_count);
1202	check_read("check_read_file", read, file_path, file_modes,
1203	    file_modes_count);
1204
1205	check_read("check_readv_dir", readv_wrapper, dir_path, dir_modes,
1206	    dir_modes_count);
1207	check_read("check_readv_file", readv_wrapper, file_path,
1208	    file_modes, file_modes_count);
1209
1210	check_read("check_pread_dir", pread_wrapper, dir_path, dir_modes,
1211	    dir_modes_count);
1212	check_read("check_pread_file", pread_wrapper, file_path,
1213	    file_modes, file_modes_count);
1214
1215	check_read("check_preadv_dir", preadv_wrapper, dir_path,
1216	    dir_modes, dir_modes_count);
1217	check_read("check_preadv_file", preadv_wrapper, file_path,
1218	    file_modes, file_modes_count);
1219
1220	if (aio_present) {
1221		check_read("check_aio_read_dir", aio_read_wrapper, dir_path,
1222		    dir_modes, dir_modes_count);
1223		check_read("check_aio_read_file", aio_read_wrapper,
1224		    file_path, file_modes, file_modes_count);
1225	}
1226
1227	check_mmap_read("check_mmap_read_dir", dir_path, 1, dir_modes,
1228	    dir_modes_count);
1229	check_mmap_read("check_mmap_read_file", file_path, 0, file_modes,
1230	    file_modes_count);
1231
1232	check_mmap_write("check_mmap_write_dir", dir_path, dir_modes,
1233	    dir_modes_count);
1234	check_mmap_write("check_mmap_write_file", file_path, file_modes,
1235	    file_modes_count);
1236
1237	check_mmap_exec("check_mmap_exec_dir", dir_path, 1, dir_modes,
1238	    dir_modes_count);
1239	check_mmap_exec("check_mmap_exec_file", file_path, 0, file_modes,
1240	    file_modes_count);
1241
1242	check_mmap_write_private("check_mmap_write_private_dir", dir_path, 1,
1243	    dir_modes, dir_modes_count);
1244	check_mmap_write_private("check_mmap_write_private_file", file_path,
1245	    0, file_modes, file_modes_count);
1246
1247	(void)unlink(file_path);
1248	(void)rmdir(dir_path);
1249	exit(0);
1250}
1251