]>
Commit | Line | Data |
---|---|---|
974e3884 A |
1 | #include <fcntl.h> |
2 | #include <stdlib.h> | |
3 | #include <sys/mount.h> | |
4 | #include <sys/param.h> | |
5 | #include <sys/stat.h> | |
6 | #include <System/sys/content_protection.h> | |
7 | #include <TargetConditionals.h> | |
8 | #include <unistd.h> | |
9 | ||
10 | #include <darwintest.h> | |
11 | ||
12 | #define ALLOWED_MKOSTEMP_FLAGS (O_APPEND | O_SHLOCK | O_EXLOCK | O_CLOEXEC) | |
13 | #define FCNTL_GETFL_EXPOSED_OFLAGS (O_APPEND) | |
14 | ||
15 | #define TEMPLATE_BASE "/tmp/mktemp_test." | |
16 | #define TEMPLATE_XS "XXXXXXXX" | |
17 | static const char template[] = TEMPLATE_BASE TEMPLATE_XS; | |
18 | ||
19 | static void test_mkostemp(int oflags); | |
20 | ||
21 | T_DECL(test_mkstemp, "basic mkstemp test") | |
22 | { | |
23 | char path[sizeof(template)]; | |
24 | struct stat path_stat; | |
25 | struct stat fd_stat; | |
26 | ||
27 | memcpy(path, template, sizeof(template)); | |
28 | ||
29 | int fd = mkstemp(path); | |
30 | T_ASSERT_POSIX_SUCCESS(fd, | |
31 | "mkstemp must return a valid fd"); | |
32 | T_ASSERT_TRUE(memcmp(path, TEMPLATE_BASE, strlen(TEMPLATE_BASE)) == 0, | |
33 | "mkstemp must respect the template. template: %s, got: %s", | |
34 | template, path); | |
35 | ||
36 | // stat fd and path, compare those | |
37 | T_ASSERT_POSIX_ZERO(stat(path, &path_stat), | |
38 | "must be able to stat the path"); | |
39 | T_ASSERT_POSIX_ZERO(fstat(fd, &fd_stat), | |
40 | "must be able to fstat the fd"); | |
41 | T_ASSERT_TRUE( | |
42 | (path_stat.st_dev == fd_stat.st_dev) && | |
43 | (path_stat.st_ino == fd_stat.st_ino), | |
44 | "fd does not match the file path"); | |
45 | // verify file attributes | |
46 | T_ASSERT_TRUE(S_ISREG(path_stat.st_mode), | |
47 | "the path must point to a regular file"); | |
48 | T_ASSERT_EQ(0600, (path_stat.st_mode & 0777), | |
49 | "created file must have 0600 permissions"); | |
50 | T_ASSERT_EQ_LLONG(0LL, path_stat.st_size, | |
51 | "created file must be empty"); | |
52 | // unlink and stat again | |
53 | T_ASSERT_POSIX_ZERO(unlink(path), | |
54 | "must be able to unlink the created file"); | |
55 | T_ASSERT_POSIX_ZERO(fstat(fd, &fd_stat), | |
56 | "must be able to stat the fd after unlink"); | |
57 | T_ASSERT_EQ(0, fd_stat.st_nlink, | |
58 | "must not have any hard links to the file after unlink"); | |
59 | // close | |
60 | T_ASSERT_POSIX_ZERO(close(fd), | |
61 | "must be able to close the fd"); | |
62 | } | |
63 | ||
64 | T_DECL(two_mkstemp_calls, "two mkstemp calls return different paths and fds") | |
65 | { | |
66 | char path1[sizeof(template)]; | |
67 | char path2[sizeof(template)]; | |
68 | memcpy(path1, template, sizeof(path1)); | |
69 | memcpy(path2, template, sizeof(path2)); | |
70 | ||
71 | int fd1 = mkostemp(path1, 0); | |
72 | T_ASSERT_POSIX_SUCCESS(fd1, "mkostemp must return a valid fd"); | |
73 | ||
74 | int fd2 = mkostemp(path2, 0); | |
75 | T_ASSERT_POSIX_SUCCESS(fd2, "mkostemp must return a valid fd"); | |
76 | ||
77 | T_ASSERT_NE(fd1, fd2, | |
78 | "two mkostemp calls must return different fds"); | |
79 | T_ASSERT_NE_STR(path1, path2, | |
80 | "two mkostemp calls must return different paths"); | |
81 | ||
82 | T_EXPECT_POSIX_ZERO(unlink(path1), | |
83 | "unlink must succeed for the first file"); | |
84 | T_EXPECT_POSIX_ZERO(unlink(path2), | |
85 | "unlink must succeed for the second file"); | |
86 | T_EXPECT_POSIX_ZERO(close(fd1), | |
87 | "close must succeed for the first fd"); | |
88 | T_EXPECT_POSIX_ZERO(close(fd2), | |
89 | "close must succeed for the second fd"); | |
90 | } | |
91 | ||
92 | T_DECL(test_mktemp, "basic mktemp test") | |
93 | { | |
94 | char path[sizeof(template)]; | |
95 | ||
96 | memcpy(path, template, sizeof(template)); | |
97 | ||
98 | T_ASSERT_EQ_PTR((void *) mktemp(path), (void *) path, | |
99 | "mktemp must return its argument"); | |
100 | ||
101 | T_ASSERT_TRUE(memcmp(path, TEMPLATE_BASE, strlen(TEMPLATE_BASE)) == 0, | |
102 | "mktemp must respect the template. template: %s, got: %s", | |
103 | template, path); | |
104 | } | |
105 | ||
106 | T_DECL(test_mkdtemp, "basic mkdtemp test") | |
107 | { | |
108 | char path[sizeof(template)]; | |
109 | struct stat dstat; | |
110 | ||
111 | memcpy(path, template, sizeof(template)); | |
112 | ||
113 | T_ASSERT_EQ_PTR((void *) mkdtemp(path), (void *) path, | |
114 | "mkdtemp must return its argument"); | |
115 | T_ASSERT_TRUE(memcmp(path, TEMPLATE_BASE, strlen(TEMPLATE_BASE)) == 0, | |
116 | "mkdtemp must respect the template. template: %s, got: %s", | |
117 | template, path); | |
118 | ||
119 | // stat fd and path, compare those | |
120 | T_ASSERT_POSIX_ZERO(stat(path, &dstat), | |
121 | "must be able to stat the path"); | |
122 | // verify file attributes | |
123 | T_ASSERT_TRUE(S_ISDIR(dstat.st_mode), | |
124 | "the path must point to a directory"); | |
125 | T_ASSERT_EQ(0700, (dstat.st_mode & 0777), | |
126 | "created directory must have 0700 permissions"); | |
127 | // cleanup | |
128 | T_ASSERT_POSIX_ZERO(rmdir(path), | |
129 | "must be able to rmdir the created directory"); | |
130 | } | |
131 | ||
132 | T_DECL(mkostemp_no_flags, "mkostemp works with 0 oflags") | |
133 | { | |
134 | test_mkostemp(0); | |
135 | } | |
136 | ||
137 | T_DECL(mkostemp_cloexec, "mkostemp works with O_CLOEXEC") | |
138 | { | |
139 | test_mkostemp(O_CLOEXEC); | |
140 | } | |
141 | ||
142 | T_DECL(mkostemp_append, "mkostemp works with O_APPEND") | |
143 | { | |
144 | test_mkostemp(O_APPEND); | |
145 | } | |
146 | ||
147 | T_DECL(mkostemp_all_flags, "mkostemp works with all allowed flags") | |
148 | { | |
149 | test_mkostemp(ALLOWED_MKOSTEMP_FLAGS); | |
150 | } | |
151 | ||
152 | T_DECL(mkostemp_bad_flags, "mkostemp checks for disallowed flags") | |
153 | { | |
154 | char path[sizeof(template)]; | |
155 | memcpy(path, template, sizeof(path)); | |
156 | ||
157 | T_ASSERT_EQ(-1, mkostemp(path, O_CREAT), "mkostemp must not allow O_CREAT"); | |
158 | T_ASSERT_EQ(-1, mkostemp(path, O_NONBLOCK), "mkostemp must not allow O_NONBLOCK"); | |
159 | } | |
160 | ||
161 | static void | |
162 | test_mkostemp(int oflags) | |
163 | { | |
164 | char path[sizeof(template)]; | |
165 | int fd; | |
166 | ||
167 | T_LOG("testing mkostemp with oflags %#x\n", oflags); | |
168 | ||
169 | memcpy(path, template, sizeof(template)); | |
170 | ||
171 | fd = mkostemp(path, oflags); | |
172 | ||
173 | T_ASSERT_POSIX_SUCCESS(fd, | |
174 | "mkostemp must return a valid fd"); | |
175 | ||
176 | if (oflags & O_CLOEXEC) { | |
177 | T_ASSERT_EQ(FD_CLOEXEC, fcntl(fd, F_GETFD), | |
178 | "O_CLOEXEC must be visible through fcntl GETFD"); | |
179 | } else { | |
180 | T_ASSERT_EQ(0, fcntl(fd, F_GETFD), | |
181 | "must not add fcntl GETFD flags at will"); | |
182 | } | |
183 | ||
184 | T_ASSERT_EQ( | |
185 | (oflags & FCNTL_GETFL_EXPOSED_OFLAGS), | |
186 | (fcntl(fd, F_GETFL) & FCNTL_GETFL_EXPOSED_OFLAGS), | |
187 | "oflags must be visible through fcntl GETFL"); | |
188 | ||
189 | T_EXPECT_POSIX_ZERO(unlink(path), | |
190 | "must be able to unlink the created file"); | |
191 | T_EXPECT_POSIX_ZERO(close(fd), | |
192 | "must be able to close the fd"); | |
193 | } | |
194 | ||
195 | #if TARGET_OS_IPHONE | |
196 | // mkstemp_dprotected_np is marked as __OSX_UNAVAILABLE, so its usage | |
197 | // has to be guarded by target conditionals. Having a testcase that | |
198 | // always does T_SKIP on OS X yields another issue. The compiler starts | |
199 | // demanding to mark the test function definition as noreturn. Just | |
200 | // don't compile the testcase for OS X. | |
201 | T_DECL(test_mkstemp_dprotected_np, "basic mkstemp_dprotected_np test") | |
202 | { | |
203 | char path[sizeof(template)]; | |
204 | int fd; | |
205 | int protection_class; | |
206 | struct statfs sfs; | |
207 | ||
208 | // the test requires /tmp to be HFS | |
209 | T_ASSERT_POSIX_ZERO(statfs("/tmp", &sfs), "must be able to statfs /tmp"); | |
210 | if (strcmp(sfs.f_fstypename, "hfs") != 0) { | |
211 | T_SKIP("Can only test dprotected on a HFS filesystem. Got %s," | |
212 | "skipping the test.", sfs.f_fstypename); | |
213 | } | |
214 | ||
215 | memcpy(path, template, sizeof(template)); | |
216 | // create a file | |
217 | fd = mkstemp_dprotected_np(path, PROTECTION_CLASS_B, 0); | |
218 | T_ASSERT_POSIX_SUCCESS(fd, | |
219 | "mkstemp_dprotected_np must return a valid fd"); | |
220 | T_ASSERT_TRUE(memcmp(path, TEMPLATE_BASE, strlen(TEMPLATE_BASE)) == 0, | |
221 | "mkstemp_dprotected_np must respect the template. template: %s, got: %s", | |
222 | template, path); | |
223 | // verify protection class | |
224 | protection_class = fcntl(fd, F_GETPROTECTIONCLASS); | |
225 | T_WITH_ERRNO; T_ASSERT_EQ(protection_class, PROTECTION_CLASS_B, | |
226 | "must get back the protection class from fcntl"); | |
227 | // cleanup | |
228 | T_ASSERT_POSIX_ZERO(close(fd), | |
229 | "must be able to close the fd"); | |
230 | T_ASSERT_POSIX_ZERO(unlink(path), | |
231 | "must be able to unlink the created file"); | |
232 | } | |
233 | #endif |