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