]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - tools/regression/security/open_to_operation/open_to_operation.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / tools / regression / security / open_to_operation / open_to_operation.c
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  */
120 static const int file_modes[] = { O_RDONLY, O_WRONLY, O_RDWR,
121     O_RDONLY | O_TRUNC, O_WRONLY | O_TRUNC, O_RDWR | O_TRUNC };
122 static const int file_modes_count = sizeof(file_modes) / sizeof(int);
123
124 static const int dir_modes[] = { O_RDONLY };
125 static const int dir_modes_count = sizeof(dir_modes) / sizeof(int);
126
127 static int testnum;
128 static int aio_present;
129
130 static void
131 ok_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
142 static void
143 notok_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  */
158 static void
159 try_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
181 static void
182 check_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
202 static void
203 check_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
228 static void
229 check_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
254 static void
255 check_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
278 static void
279 check_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
302 static void
303 check_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
326 static void
327 check_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
350 static void
351 check_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
374 static void
375 check_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
400 static void
401 check_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
425 static void
426 check_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
450 static void
451 check_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
474 static void
475 check_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
587 static void
588 check_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
611 static void
612 check_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
635 static void
636 check_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
675 static void
676 check_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  */
724 typedef ssize_t (*write_fn)(int d, const void *buf, size_t nbytes);
725
726 static ssize_t
727 writev_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
736 static ssize_t
737 pwrite_wrapper(int d, const void *buf, size_t nbytes)
738 {
739
740         return (pwrite(d, buf, nbytes, 0));
741 }
742
743 static ssize_t
744 pwritev_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
753 static ssize_t
754 aio_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
770 static void
771 check_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  */
810 typedef ssize_t (*read_fn)(int d, void *buf, size_t nbytes);
811
812 static ssize_t
813 readv_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
822 static ssize_t
823 pread_wrapper(int d, void *buf, size_t nbytes)
824 {
825
826         return (pread(d, buf, nbytes, 0));
827 }
828
829 static ssize_t
830 preadv_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
839 static ssize_t
840 aio_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
856 static void
857 check_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
891 static void
892 check_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
937 static void
938 check_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
979 static void
980 check_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
1025 static void
1026 check_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
1071 int
1072 main(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 }