1 /* compile: xcrun -sdk macosx.internal clang -ldarwintest -o iopolicy iopolicy.c -g -Weverything */
2
3 #include <darwintest.h>
4 #include <darwintest_utils.h>
5 #include <darwintest_multiprocess.h>
6 #include <errno.h>
7 #include <stdint.h>
8 #include <stdlib.h>
9 #include <unistd.h>
10 #include <sys/attr.h>
11 #include <sys/event.h>
12 #include <sys/resource.h>
13
14 #ifndef IOPOL_TYPE_VFS_DISALLOW_RW_FOR_O_EVTONLY
15 #define IOPOL_TYPE_VFS_DISALLOW_RW_FOR_O_EVTONLY 10
16 #endif
17
18 #ifndef IOPOL_VFS_DISALLOW_RW_FOR_O_EVTONLY_OFF
19 #define IOPOL_VFS_DISALLOW_RW_FOR_O_EVTONLY_OFF 0
20 #endif
21
22 #ifndef IOPOL_VFS_DISALLOW_RW_FOR_O_EVTONLY_ON
23 #define IOPOL_VFS_DISALLOW_RW_FOR_O_EVTONLY_ON 1
24 #endif
25
26 T_GLOBAL_META(
27 T_META_NAMESPACE("xnu.vfs.iopolicy"),
28 T_META_RADAR_COMPONENT_NAME("xnu"),
29 T_META_RADAR_COMPONENT_VERSION("vfs"),
30 T_META_CHECK_LEAKS(false),
31 T_META_TAG_VM_PREFERRED);
32
33 #define TEST_FILE "testfile"
34
35 static char g_testfile[MAXPATHLEN];
36 static char g_testdata[1024];
37
38 static void
exit_cleanup(void)39 exit_cleanup(void)
40 {
41 (void)remove(g_testfile);
42 }
43
44 T_DECL(iopol_type_vfs_disallow_rw_for_o_evtonly,
45 "test IOPOL_TYPE_VFS_DISALLOW_RW_FOR_O_EVTONLY policy")
46 {
47 char attrbuf[256];
48 struct attrlist attrlist;
49 struct kevent vnode_kevent;
50 struct timespec kevent_timeout;
51 const char *tmpdir = dt_tmpdir();
52 void *mapped;
53 int err, fd, kq;
54
55 T_SETUPBEGIN;
56
57 atexit(exit_cleanup);
58
59 T_ASSERT_POSIX_ZERO(chdir(tmpdir),
60 "Setup: changing to tmpdir: %s", tmpdir);
61
62 snprintf(g_testfile, MAXPATHLEN, "%s/%s", tmpdir, TEST_FILE);
63
64 T_WITH_ERRNO;
65 fd = open(g_testfile, O_CREAT | O_RDWR, 0666);
66 T_ASSERT_NE(fd, -1, "Create test file: %s", g_testfile);
67
68 T_WITH_ERRNO;
69 err = (int)write(fd, g_testfile, sizeof(g_testfile));
70 T_ASSERT_NE(err, -1, "Write: %s", g_testfile);
71
72 T_ASSERT_POSIX_SUCCESS(close(fd), "Close test file: %s", g_testfile);
73
74 kq = kqueue();
75 T_ASSERT_NE(kq, -1, "Create kqueue");
76
77 T_SETUPEND;
78
79 T_WITH_ERRNO;
80 err = setiopolicy_np(IOPOL_TYPE_VFS_DISALLOW_RW_FOR_O_EVTONLY,
81 IOPOL_SCOPE_THREAD, IOPOL_VFS_DISALLOW_RW_FOR_O_EVTONLY_ON);
82 T_ASSERT_TRUE((err == -1) && (errno == EINVAL),
83 "setiopolicy_np(IOPOL_TYPE_VFS_DISALLOW_RW_FOR_O_EVTONLY, IOPOL_SCOPE_THREAD, 1)");
84
85 T_WITH_ERRNO;
86 err = setiopolicy_np(IOPOL_TYPE_VFS_DISALLOW_RW_FOR_O_EVTONLY,
87 IOPOL_SCOPE_PROCESS, IOPOL_VFS_DISALLOW_RW_FOR_O_EVTONLY_ON);
88 T_ASSERT_NE(err, -1,
89 "setiopolicy_np(IOPOL_TYPE_VFS_DISALLOW_RW_FOR_O_EVTONLY, IOPOL_SCOPE_PROCESS, 1)");
90
91 T_WITH_ERRNO;
92 err = getiopolicy_np(IOPOL_TYPE_VFS_DISALLOW_RW_FOR_O_EVTONLY,
93 IOPOL_SCOPE_PROCESS);
94 T_ASSERT_EQ(err, IOPOL_VFS_DISALLOW_RW_FOR_O_EVTONLY_ON,
95 "getiopolicy_np(IOPOL_TYPE_VFS_DISALLOW_RW_FOR_O_EVTONLY, IOPOL_SCOPE_PROCESS)");
96
97 T_WITH_ERRNO;
98 err = getiopolicy_np(IOPOL_TYPE_VFS_HFS_CASE_SENSITIVITY,
99 IOPOL_SCOPE_THREAD);
100 T_ASSERT_TRUE((err == -1) && (errno == EINVAL),
101 "getiopolicy_np(IOPOL_TYPE_VFS_HFS_CASE_SENSITIVITY, IOPOL_SCOPE_THREAD)");
102
103 T_WITH_ERRNO;
104 err = setiopolicy_np(IOPOL_TYPE_VFS_DISALLOW_RW_FOR_O_EVTONLY,
105 IOPOL_SCOPE_PROCESS, IOPOL_VFS_DISALLOW_RW_FOR_O_EVTONLY_OFF);
106 T_ASSERT_TRUE((err == -1) && (errno == EINVAL),
107 "setiopolicy_np(IOPOL_TYPE_VFS_DISALLOW_RW_FOR_O_EVTONLY, IOPOL_SCOPE_PROCESS, 0)");
108
109 T_WITH_ERRNO;
110 fd = open(g_testfile, O_RDWR | O_EVTONLY, 0666);
111 T_ASSERT_NE(fd, -1, "Open test fi1e in 'O_RDW|O_EVTONLY': %s", g_testfile);
112
113 T_WITH_ERRNO;
114 err = (int)write(fd, g_testdata, sizeof(g_testdata));
115 T_ASSERT_TRUE((err == -1) && (errno == EBADF),
116 "Trying to write: %s", g_testfile);
117
118 T_WITH_ERRNO;
119 err = (int)read(fd, g_testdata, sizeof(g_testdata));
120 T_ASSERT_TRUE((err == -1) && (errno == EBADF),
121 "Trying to read: %s", g_testfile);
122
123 T_WITH_ERRNO;
124 mapped = mmap(NULL, sizeof(g_testdata), PROT_READ | PROT_WRITE, MAP_SHARED,
125 fd, 0);
126 T_ASSERT_TRUE((err == -1) && (errno == EACCES),
127 "Trying to mmaped read/write: %s", g_testfile);
128
129 T_ASSERT_POSIX_SUCCESS(close(fd), "Close test file: %s", g_testfile);
130
131 T_WITH_ERRNO;
132 fd = open(g_testfile, O_EVTONLY, 0666);
133 T_ASSERT_NE(fd, -1, "Open test fi1e in 'O_EVTONLY': %s", g_testfile);
134
135 T_WITH_ERRNO;
136 err = (int)read(fd, g_testdata, sizeof(g_testdata));
137 T_ASSERT_TRUE((err == -1) && (errno == EBADF),
138 "Trying to read: %s", g_testfile);
139
140 T_WITH_ERRNO;
141 memset(&attrlist, 0, sizeof(attrlist));
142 attrlist.bitmapcount = ATTR_BIT_MAP_COUNT;
143 attrlist.commonattr = (ATTR_CMN_OBJTYPE | ATTR_CMN_OBJID | ATTR_CMN_MODTIME);
144
145 err = fgetattrlist(fd, &attrlist, &attrbuf, sizeof(attrbuf), 0);
146 T_ASSERT_NE(err, -1, "Perform getattrlist: %s", g_testfile);
147
148 kevent_timeout.tv_sec = kevent_timeout.tv_nsec = 0;
149 EV_SET(&vnode_kevent, fd, EVFILT_VNODE, (EV_ADD | EV_ENABLE | EV_CLEAR),
150 NOTE_WRITE, 0, (void *)g_testfile);
151 err = kevent(kq, &vnode_kevent, 1, NULL, 0, &kevent_timeout);
152 T_ASSERT_GE(err, 0, "Register vnode event on kq: %d", kq);
153
154 kevent_timeout.tv_sec = 2;
155 kevent_timeout.tv_nsec = 0;
156 EV_SET(&vnode_kevent, fd, EVFILT_VNODE, EV_CLEAR, 0, 0, 0);
157
158 err = kevent(kq, NULL, 0, &vnode_kevent, 1, &kevent_timeout);
159 T_ASSERT_NE(err, -1, "Listen for vnode event on kq: %d", kq);
160
161 T_ASSERT_POSIX_SUCCESS(close(fd), "Close test file: %s", g_testfile);
162 }
163