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