Branch data Line data Source code
1 : : /* SPDX-License-Identifier: LGPL-2.1+ */
2 : :
3 : : #include <unistd.h>
4 : :
5 : : #include "alloc-util.h"
6 : : #include "fd-util.h"
7 : : #include "fs-util.h"
8 : : #include "id128-util.h"
9 : : #include "macro.h"
10 : : #include "mkdir.h"
11 : : #include "path-util.h"
12 : : #include "rm-rf.h"
13 : : #include "stdio-util.h"
14 : : #include "string-util.h"
15 : : #include "strv.h"
16 : : #include "tests.h"
17 : : #include "tmpfile-util.h"
18 : : #include "umask-util.h"
19 : : #include "user-util.h"
20 : : #include "util.h"
21 : : #include "virt.h"
22 : :
23 : : static const char *arg_test_dir = NULL;
24 : :
25 : 4 : static void test_chase_symlinks(void) {
26 : 4 : _cleanup_free_ char *result = NULL;
27 : : char *temp;
28 : : const char *top, *p, *pslash, *q, *qslash;
29 : : struct stat st;
30 : : int r, pfd;
31 : :
32 [ + - ]: 4 : log_info("/* %s */", __func__);
33 : :
34 [ - + + + : 20 : temp = strjoina(arg_test_dir ?: "/tmp", "/test-chase.XXXXXX");
+ - - + -
+ + + +
- ]
35 [ - + ]: 4 : assert_se(mkdtemp(temp));
36 : :
37 [ + + + - : 20 : top = strjoina(temp, "/top");
- + - + +
+ + - ]
38 [ - + ]: 4 : assert_se(mkdir(top, 0700) >= 0);
39 : :
40 [ + + + - : 20 : p = strjoina(top, "/dot");
- + - + +
+ + - ]
41 [ - + ]: 4 : if (symlink(".", p) < 0) {
42 [ # # # # ]: 0 : assert_se(IN_SET(errno, EINVAL, ENOSYS, ENOTTY, EPERM));
43 : 0 : log_tests_skipped_errno(errno, "symlink() not possible");
44 : 0 : goto cleanup;
45 : : };
46 : :
47 [ + + + - : 20 : p = strjoina(top, "/dotdot");
- + - + +
+ + - ]
48 [ - + ]: 4 : assert_se(symlink("..", p) >= 0);
49 : :
50 [ + + + - : 20 : p = strjoina(top, "/dotdota");
- + - + +
+ + - ]
51 [ - + ]: 4 : assert_se(symlink("../a", p) >= 0);
52 : :
53 [ + + + - : 20 : p = strjoina(temp, "/a");
- + - + +
+ + - ]
54 [ - + ]: 4 : assert_se(symlink("b", p) >= 0);
55 : :
56 [ + + + - : 20 : p = strjoina(temp, "/b");
- + - + +
+ + - ]
57 [ - + ]: 4 : assert_se(symlink("/usr", p) >= 0);
58 : :
59 [ + + + - : 20 : p = strjoina(temp, "/start");
- + - + +
+ + - ]
60 [ - + ]: 4 : assert_se(symlink("top/dot/dotdota", p) >= 0);
61 : :
62 : : /* Paths that use symlinks underneath the "root" */
63 : :
64 : 4 : r = chase_symlinks(p, NULL, 0, &result);
65 [ - + ]: 4 : assert_se(r > 0);
66 [ - + ]: 4 : assert_se(path_equal(result, "/usr"));
67 : 4 : result = mfree(result);
68 : :
69 [ + + + - : 20 : pslash = strjoina(p, "/");
- + - + +
+ + - ]
70 : 4 : r = chase_symlinks(pslash, NULL, 0, &result);
71 [ - + ]: 4 : assert_se(r > 0);
72 [ - + ]: 4 : assert_se(path_equal(result, "/usr/"));
73 : 4 : result = mfree(result);
74 : :
75 : 4 : r = chase_symlinks(p, temp, 0, &result);
76 [ - + ]: 4 : assert_se(r == -ENOENT);
77 : :
78 : 4 : r = chase_symlinks(pslash, temp, 0, &result);
79 [ - + ]: 4 : assert_se(r == -ENOENT);
80 : :
81 [ + + + - : 20 : q = strjoina(temp, "/usr");
- + - + +
+ + - ]
82 : :
83 : 4 : r = chase_symlinks(p, temp, CHASE_NONEXISTENT, &result);
84 [ - + ]: 4 : assert_se(r == 0);
85 [ - + ]: 4 : assert_se(path_equal(result, q));
86 : 4 : result = mfree(result);
87 : :
88 [ + + + - : 20 : qslash = strjoina(q, "/");
- + - + +
+ + - ]
89 : :
90 : 4 : r = chase_symlinks(pslash, temp, CHASE_NONEXISTENT, &result);
91 [ - + ]: 4 : assert_se(r == 0);
92 [ - + ]: 4 : assert_se(path_equal(result, qslash));
93 : 4 : result = mfree(result);
94 : :
95 [ - + ]: 4 : assert_se(mkdir(q, 0700) >= 0);
96 : :
97 : 4 : r = chase_symlinks(p, temp, 0, &result);
98 [ - + ]: 4 : assert_se(r > 0);
99 [ - + ]: 4 : assert_se(path_equal(result, q));
100 : 4 : result = mfree(result);
101 : :
102 : 4 : r = chase_symlinks(pslash, temp, 0, &result);
103 [ - + ]: 4 : assert_se(r > 0);
104 [ - + ]: 4 : assert_se(path_equal(result, qslash));
105 : 4 : result = mfree(result);
106 : :
107 [ + + + - : 20 : p = strjoina(temp, "/slash");
- + - + +
+ + - ]
108 [ - + ]: 4 : assert_se(symlink("/", p) >= 0);
109 : :
110 : 4 : r = chase_symlinks(p, NULL, 0, &result);
111 [ - + ]: 4 : assert_se(r > 0);
112 [ - + ]: 4 : assert_se(path_equal(result, "/"));
113 : 4 : result = mfree(result);
114 : :
115 : 4 : r = chase_symlinks(p, temp, 0, &result);
116 [ - + ]: 4 : assert_se(r > 0);
117 [ - + ]: 4 : assert_se(path_equal(result, temp));
118 : 4 : result = mfree(result);
119 : :
120 : : /* Paths that would "escape" outside of the "root" */
121 : :
122 [ + + + - : 20 : p = strjoina(temp, "/6dots");
- + - + +
+ + - ]
123 [ - + ]: 4 : assert_se(symlink("../../..", p) >= 0);
124 : :
125 : 4 : r = chase_symlinks(p, temp, 0, &result);
126 [ + - - + ]: 4 : assert_se(r > 0 && path_equal(result, temp));
127 : 4 : result = mfree(result);
128 : :
129 [ + + + - : 20 : p = strjoina(temp, "/6dotsusr");
- + - + +
+ + - ]
130 [ - + ]: 4 : assert_se(symlink("../../../usr", p) >= 0);
131 : :
132 : 4 : r = chase_symlinks(p, temp, 0, &result);
133 [ + - - + ]: 4 : assert_se(r > 0 && path_equal(result, q));
134 : 4 : result = mfree(result);
135 : :
136 [ + + + - : 20 : p = strjoina(temp, "/top/8dotsusr");
- + - + +
+ + - ]
137 [ - + ]: 4 : assert_se(symlink("../../../../usr", p) >= 0);
138 : :
139 : 4 : r = chase_symlinks(p, temp, 0, &result);
140 [ + - - + ]: 4 : assert_se(r > 0 && path_equal(result, q));
141 : 4 : result = mfree(result);
142 : :
143 : : /* Paths that contain repeated slashes */
144 : :
145 [ + + + - : 20 : p = strjoina(temp, "/slashslash");
- + - + +
+ + - ]
146 [ - + ]: 4 : assert_se(symlink("///usr///", p) >= 0);
147 : :
148 : 4 : r = chase_symlinks(p, NULL, 0, &result);
149 [ - + ]: 4 : assert_se(r > 0);
150 [ - + ]: 4 : assert_se(path_equal(result, "/usr"));
151 : 4 : result = mfree(result);
152 : :
153 : 4 : r = chase_symlinks(p, temp, 0, &result);
154 [ - + ]: 4 : assert_se(r > 0);
155 [ - + ]: 4 : assert_se(path_equal(result, q));
156 : 4 : result = mfree(result);
157 : :
158 : : /* Paths underneath the "root" with different UIDs while using CHASE_SAFE */
159 : :
160 [ - + ]: 4 : if (geteuid() == 0) {
161 [ # # # # : 0 : p = strjoina(temp, "/user");
# # # # #
# # # ]
162 [ # # ]: 0 : assert_se(mkdir(p, 0755) >= 0);
163 [ # # ]: 0 : assert_se(chown(p, UID_NOBODY, GID_NOBODY) >= 0);
164 : :
165 [ # # # # : 0 : q = strjoina(temp, "/user/root");
# # # # #
# # # ]
166 [ # # ]: 0 : assert_se(mkdir(q, 0755) >= 0);
167 : :
168 [ # # # # : 0 : p = strjoina(q, "/link");
# # # # #
# # # ]
169 [ # # ]: 0 : assert_se(symlink("/", p) >= 0);
170 : :
171 : : /* Fail when user-owned directories contain root-owned subdirectories. */
172 : 0 : r = chase_symlinks(p, temp, CHASE_SAFE, &result);
173 [ # # ]: 0 : assert_se(r == -ENOLINK);
174 : 0 : result = mfree(result);
175 : :
176 : : /* Allow this when the user-owned directories are all in the "root". */
177 : 0 : r = chase_symlinks(p, q, CHASE_SAFE, &result);
178 [ # # ]: 0 : assert_se(r > 0);
179 : 0 : result = mfree(result);
180 : : }
181 : :
182 : : /* Paths using . */
183 : :
184 : 4 : r = chase_symlinks("/etc/./.././", NULL, 0, &result);
185 [ - + ]: 4 : assert_se(r > 0);
186 [ - + ]: 4 : assert_se(path_equal(result, "/"));
187 : 4 : result = mfree(result);
188 : :
189 : 4 : r = chase_symlinks("/etc/./.././", "/etc", 0, &result);
190 [ + - - + ]: 4 : assert_se(r > 0 && path_equal(result, "/etc"));
191 : 4 : result = mfree(result);
192 : :
193 : 4 : r = chase_symlinks("/../.././//../../etc", NULL, 0, &result);
194 [ - + ]: 4 : assert_se(r > 0);
195 [ - + ]: 4 : assert_se(streq(result, "/etc"));
196 : 4 : result = mfree(result);
197 : :
198 : 4 : r = chase_symlinks("/../.././//../../test-chase.fsldajfl", NULL, CHASE_NONEXISTENT, &result);
199 [ - + ]: 4 : assert_se(r == 0);
200 [ - + ]: 4 : assert_se(streq(result, "/test-chase.fsldajfl"));
201 : 4 : result = mfree(result);
202 : :
203 : 4 : r = chase_symlinks("/../.././//../../etc", "/", CHASE_PREFIX_ROOT, &result);
204 [ - + ]: 4 : assert_se(r > 0);
205 [ - + ]: 4 : assert_se(streq(result, "/etc"));
206 : 4 : result = mfree(result);
207 : :
208 : 4 : r = chase_symlinks("/../.././//../../test-chase.fsldajfl", "/", CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &result);
209 [ - + ]: 4 : assert_se(r == 0);
210 [ - + ]: 4 : assert_se(streq(result, "/test-chase.fsldajfl"));
211 : 4 : result = mfree(result);
212 : :
213 : 4 : r = chase_symlinks("/etc/machine-id/foo", NULL, 0, &result);
214 [ - + ]: 4 : assert_se(r == -ENOTDIR);
215 : 4 : result = mfree(result);
216 : :
217 : : /* Path that loops back to self */
218 : :
219 [ + + + - : 20 : p = strjoina(temp, "/recursive-symlink");
- + - + +
+ + - ]
220 [ - + ]: 4 : assert_se(symlink("recursive-symlink", p) >= 0);
221 : 4 : r = chase_symlinks(p, NULL, 0, &result);
222 [ - + ]: 4 : assert_se(r == -ELOOP);
223 : :
224 : : /* Path which doesn't exist */
225 : :
226 [ + + + - : 20 : p = strjoina(temp, "/idontexist");
- + - + +
+ + - ]
227 : 4 : r = chase_symlinks(p, NULL, 0, &result);
228 [ - + ]: 4 : assert_se(r == -ENOENT);
229 : :
230 : 4 : r = chase_symlinks(p, NULL, CHASE_NONEXISTENT, &result);
231 [ - + ]: 4 : assert_se(r == 0);
232 [ - + ]: 4 : assert_se(path_equal(result, p));
233 : 4 : result = mfree(result);
234 : :
235 [ + + + - : 20 : p = strjoina(temp, "/idontexist/meneither");
- + - + +
+ + - ]
236 : 4 : r = chase_symlinks(p, NULL, 0, &result);
237 [ - + ]: 4 : assert_se(r == -ENOENT);
238 : :
239 : 4 : r = chase_symlinks(p, NULL, CHASE_NONEXISTENT, &result);
240 [ - + ]: 4 : assert_se(r == 0);
241 [ - + ]: 4 : assert_se(path_equal(result, p));
242 : 4 : result = mfree(result);
243 : :
244 : : /* Path which doesn't exist, but contains weird stuff */
245 : :
246 [ + + + - : 20 : p = strjoina(temp, "/idontexist/..");
- + - + +
+ + - ]
247 : 4 : r = chase_symlinks(p, NULL, 0, &result);
248 [ - + ]: 4 : assert_se(r == -ENOENT);
249 : :
250 : 4 : r = chase_symlinks(p, NULL, CHASE_NONEXISTENT, &result);
251 [ - + ]: 4 : assert_se(r == -ENOENT);
252 : :
253 [ + + + - : 20 : p = strjoina(temp, "/target");
- + - + +
+ + - ]
254 [ + + + - : 20 : q = strjoina(temp, "/top");
- + - + +
+ + - ]
255 [ - + ]: 4 : assert_se(symlink(q, p) >= 0);
256 [ + + + - : 20 : p = strjoina(temp, "/target/idontexist");
- + - + +
+ + - ]
257 : 4 : r = chase_symlinks(p, NULL, 0, &result);
258 [ - + ]: 4 : assert_se(r == -ENOENT);
259 : :
260 [ - + ]: 4 : if (geteuid() == 0) {
261 [ # # # # : 0 : p = strjoina(temp, "/priv1");
# # # # #
# # # ]
262 [ # # ]: 0 : assert_se(mkdir(p, 0755) >= 0);
263 : :
264 [ # # # # : 0 : q = strjoina(p, "/priv2");
# # # # #
# # # ]
265 [ # # ]: 0 : assert_se(mkdir(q, 0755) >= 0);
266 : :
267 [ # # ]: 0 : assert_se(chase_symlinks(q, NULL, CHASE_SAFE, NULL) >= 0);
268 : :
269 [ # # ]: 0 : assert_se(chown(q, UID_NOBODY, GID_NOBODY) >= 0);
270 [ # # ]: 0 : assert_se(chase_symlinks(q, NULL, CHASE_SAFE, NULL) >= 0);
271 : :
272 [ # # ]: 0 : assert_se(chown(p, UID_NOBODY, GID_NOBODY) >= 0);
273 [ # # ]: 0 : assert_se(chase_symlinks(q, NULL, CHASE_SAFE, NULL) >= 0);
274 : :
275 [ # # ]: 0 : assert_se(chown(q, 0, 0) >= 0);
276 [ # # ]: 0 : assert_se(chase_symlinks(q, NULL, CHASE_SAFE, NULL) == -ENOLINK);
277 : :
278 [ # # ]: 0 : assert_se(rmdir(q) >= 0);
279 [ # # ]: 0 : assert_se(symlink("/etc/passwd", q) >= 0);
280 [ # # ]: 0 : assert_se(chase_symlinks(q, NULL, CHASE_SAFE, NULL) == -ENOLINK);
281 : :
282 [ # # ]: 0 : assert_se(chown(p, 0, 0) >= 0);
283 [ # # ]: 0 : assert_se(chase_symlinks(q, NULL, CHASE_SAFE, NULL) >= 0);
284 : : }
285 : :
286 [ + + + - : 20 : p = strjoina(temp, "/machine-id-test");
- + - + +
+ + - ]
287 [ - + ]: 4 : assert_se(symlink("/usr/../etc/./machine-id", p) >= 0);
288 : :
289 : 4 : pfd = chase_symlinks(p, NULL, CHASE_OPEN, NULL);
290 [ + - ]: 4 : if (pfd != -ENOENT) {
291 : 4 : _cleanup_close_ int fd = -1;
292 : : sd_id128_t a, b;
293 : :
294 [ - + ]: 4 : assert_se(pfd >= 0);
295 : :
296 : 4 : fd = fd_reopen(pfd, O_RDONLY|O_CLOEXEC);
297 [ - + ]: 4 : assert_se(fd >= 0);
298 : 4 : safe_close(pfd);
299 : :
300 [ - + ]: 4 : assert_se(id128_read_fd(fd, ID128_PLAIN, &a) >= 0);
301 [ - + ]: 4 : assert_se(sd_id128_get_machine(&b) >= 0);
302 [ - + ]: 4 : assert_se(sd_id128_equal(a, b));
303 : : }
304 : :
305 : : /* Test CHASE_NOFOLLOW */
306 : :
307 [ + + + - : 20 : p = strjoina(temp, "/target");
- + - + +
+ + - ]
308 [ + + + - : 20 : q = strjoina(temp, "/symlink");
- + - + +
+ + - ]
309 [ - + ]: 4 : assert_se(symlink(p, q) >= 0);
310 : 4 : pfd = chase_symlinks(q, NULL, CHASE_OPEN|CHASE_NOFOLLOW, &result);
311 [ - + ]: 4 : assert_se(pfd > 0);
312 [ - + ]: 4 : assert_se(path_equal(result, q));
313 [ - + ]: 4 : assert_se(fstat(pfd, &st) >= 0);
314 [ - + ]: 4 : assert_se(S_ISLNK(st.st_mode));
315 : 4 : result = mfree(result);
316 : :
317 : : /* s1 -> s2 -> nonexistent */
318 [ + + + - : 20 : q = strjoina(temp, "/s1");
- + - + +
+ + - ]
319 [ - + ]: 4 : assert_se(symlink("s2", q) >= 0);
320 [ + + + - : 20 : p = strjoina(temp, "/s2");
- + - + +
+ + - ]
321 [ - + ]: 4 : assert_se(symlink("nonexistent", p) >= 0);
322 : 4 : pfd = chase_symlinks(q, NULL, CHASE_OPEN|CHASE_NOFOLLOW, &result);
323 [ - + ]: 4 : assert_se(pfd > 0);
324 [ - + ]: 4 : assert_se(path_equal(result, q));
325 [ - + ]: 4 : assert_se(fstat(pfd, &st) >= 0);
326 [ - + ]: 4 : assert_se(S_ISLNK(st.st_mode));
327 : 4 : result = mfree(result);
328 : :
329 : : /* Test CHASE_ONE */
330 : :
331 [ + + + - : 20 : p = strjoina(temp, "/start");
- + - + +
+ + - ]
332 : 4 : r = chase_symlinks(p, NULL, CHASE_STEP, &result);
333 [ - + ]: 4 : assert_se(r == 0);
334 [ + + + - : 20 : p = strjoina(temp, "/top/dot/dotdota");
- + - + +
+ + - ]
335 [ - + ]: 4 : assert_se(streq(p, result));
336 : 4 : result = mfree(result);
337 : :
338 : 4 : r = chase_symlinks(p, NULL, CHASE_STEP, &result);
339 [ - + ]: 4 : assert_se(r == 0);
340 [ + + + - : 20 : p = strjoina(temp, "/top/./dotdota");
- + - + +
+ + - ]
341 [ - + ]: 4 : assert_se(streq(p, result));
342 : 4 : result = mfree(result);
343 : :
344 : 4 : r = chase_symlinks(p, NULL, CHASE_STEP, &result);
345 [ - + ]: 4 : assert_se(r == 0);
346 [ + + + - : 20 : p = strjoina(temp, "/top/../a");
- + - + +
+ + - ]
347 [ - + ]: 4 : assert_se(streq(p, result));
348 : 4 : result = mfree(result);
349 : :
350 : 4 : r = chase_symlinks(p, NULL, CHASE_STEP, &result);
351 [ - + ]: 4 : assert_se(r == 0);
352 [ + + + - : 20 : p = strjoina(temp, "/a");
- + - + +
+ + - ]
353 [ - + ]: 4 : assert_se(streq(p, result));
354 : 4 : result = mfree(result);
355 : :
356 : 4 : r = chase_symlinks(p, NULL, CHASE_STEP, &result);
357 [ - + ]: 4 : assert_se(r == 0);
358 [ + + + - : 20 : p = strjoina(temp, "/b");
- + - + +
+ + - ]
359 [ - + ]: 4 : assert_se(streq(p, result));
360 : 4 : result = mfree(result);
361 : :
362 : 4 : r = chase_symlinks(p, NULL, CHASE_STEP, &result);
363 [ - + ]: 4 : assert_se(r == 0);
364 [ - + ]: 4 : assert_se(streq("/usr", result));
365 : 4 : result = mfree(result);
366 : :
367 : 4 : r = chase_symlinks("/usr", NULL, CHASE_STEP, &result);
368 [ - + ]: 4 : assert_se(r > 0);
369 [ - + ]: 4 : assert_se(streq("/usr", result));
370 : 4 : result = mfree(result);
371 : :
372 : 4 : cleanup:
373 [ - + ]: 4 : assert_se(rm_rf(temp, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
374 : 4 : }
375 : :
376 : 4 : static void test_unlink_noerrno(void) {
377 : : char *name;
378 : : int fd;
379 : :
380 [ + - ]: 4 : log_info("/* %s */", __func__);
381 : :
382 [ - + + + : 20 : name = strjoina(arg_test_dir ?: "/tmp", "/test-close_nointr.XXXXXX");
+ - - + -
+ + + +
- ]
383 : 4 : fd = mkostemp_safe(name);
384 [ - + ]: 4 : assert_se(fd >= 0);
385 [ - + ]: 4 : assert_se(close_nointr(fd) >= 0);
386 : :
387 : : {
388 : 4 : PROTECT_ERRNO;
389 : 4 : errno = 42;
390 [ - + ]: 4 : assert_se(unlink_noerrno(name) >= 0);
391 [ - + ]: 4 : assert_se(errno == 42);
392 [ - + ]: 4 : assert_se(unlink_noerrno(name) < 0);
393 [ - + ]: 4 : assert_se(errno == 42);
394 : : }
395 : 4 : }
396 : :
397 : 4 : static void test_readlink_and_make_absolute(void) {
398 : : const char *tempdir, *name, *name2, *name_alias;
399 : 4 : _cleanup_free_ char *r1 = NULL, *r2 = NULL, *pwd = NULL;
400 : :
401 [ + - ]: 4 : log_info("/* %s */", __func__);
402 : :
403 [ - + + + : 20 : tempdir = strjoina(arg_test_dir ?: "/tmp", "/test-readlink_and_make_absolute");
+ - - + -
+ + + +
- ]
404 [ + + + - : 20 : name = strjoina(tempdir, "/original");
- + - + +
+ + - ]
405 : 4 : name2 = "test-readlink_and_make_absolute/original";
406 [ - + + + : 20 : name_alias = strjoina(arg_test_dir ?: "/tmp", "/test-readlink_and_make_absolute-alias");
+ - - + -
+ + + +
- ]
407 : :
408 [ - + ]: 4 : assert_se(mkdir_safe(tempdir, 0755, getuid(), getgid(), MKDIR_WARN_MODE) >= 0);
409 [ - + ]: 4 : assert_se(touch(name) >= 0);
410 : :
411 [ - + ]: 4 : if (symlink(name, name_alias) < 0) {
412 [ # # # # ]: 0 : assert_se(IN_SET(errno, EINVAL, ENOSYS, ENOTTY, EPERM));
413 : 0 : log_tests_skipped_errno(errno, "symlink() not possible");
414 : : } else {
415 [ - + ]: 4 : assert_se(readlink_and_make_absolute(name_alias, &r1) >= 0);
416 [ - + ]: 4 : assert_se(streq(r1, name));
417 [ - + ]: 4 : assert_se(unlink(name_alias) >= 0);
418 : :
419 [ - + ]: 4 : assert_se(safe_getcwd(&pwd) >= 0);
420 : :
421 [ - + ]: 4 : assert_se(chdir(tempdir) >= 0);
422 [ - + ]: 4 : assert_se(symlink(name2, name_alias) >= 0);
423 [ - + ]: 4 : assert_se(readlink_and_make_absolute(name_alias, &r2) >= 0);
424 [ - + ]: 4 : assert_se(streq(r2, name));
425 [ - + ]: 4 : assert_se(unlink(name_alias) >= 0);
426 : :
427 [ - + ]: 4 : assert_se(chdir(pwd) >= 0);
428 : : }
429 : :
430 [ - + ]: 4 : assert_se(rm_rf(tempdir, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
431 : 4 : }
432 : :
433 : 4 : static void test_get_files_in_directory(void) {
434 : 4 : _cleanup_strv_free_ char **l = NULL, **t = NULL;
435 : :
436 [ - + - + ]: 4 : assert_se(get_files_in_directory(arg_test_dir ?: "/tmp", &l) >= 0);
437 [ - + ]: 4 : assert_se(get_files_in_directory(".", &t) >= 0);
438 [ - + ]: 4 : assert_se(get_files_in_directory(".", NULL) >= 0);
439 : 4 : }
440 : :
441 : 4 : static void test_var_tmp(void) {
442 : 4 : _cleanup_free_ char *tmpdir_backup = NULL, *temp_backup = NULL, *tmp_backup = NULL;
443 : 4 : const char *tmp_dir = NULL, *t;
444 : :
445 [ + - ]: 4 : log_info("/* %s */", __func__);
446 : :
447 : 4 : t = getenv("TMPDIR");
448 [ - + ]: 4 : if (t) {
449 : 0 : tmpdir_backup = strdup(t);
450 [ # # ]: 0 : assert_se(tmpdir_backup);
451 : : }
452 : :
453 : 4 : t = getenv("TEMP");
454 [ - + ]: 4 : if (t) {
455 : 0 : temp_backup = strdup(t);
456 [ # # ]: 0 : assert_se(temp_backup);
457 : : }
458 : :
459 : 4 : t = getenv("TMP");
460 [ - + ]: 4 : if (t) {
461 : 0 : tmp_backup = strdup(t);
462 [ # # ]: 0 : assert_se(tmp_backup);
463 : : }
464 : :
465 [ - + ]: 4 : assert_se(unsetenv("TMPDIR") >= 0);
466 [ - + ]: 4 : assert_se(unsetenv("TEMP") >= 0);
467 [ - + ]: 4 : assert_se(unsetenv("TMP") >= 0);
468 : :
469 [ - + ]: 4 : assert_se(var_tmp_dir(&tmp_dir) >= 0);
470 [ - + ]: 4 : assert_se(streq(tmp_dir, "/var/tmp"));
471 : :
472 [ - + ]: 4 : assert_se(setenv("TMPDIR", "/tmp", true) >= 0);
473 [ - + ]: 4 : assert_se(streq(getenv("TMPDIR"), "/tmp"));
474 : :
475 [ - + ]: 4 : assert_se(var_tmp_dir(&tmp_dir) >= 0);
476 [ - + ]: 4 : assert_se(streq(tmp_dir, "/tmp"));
477 : :
478 [ - + ]: 4 : assert_se(setenv("TMPDIR", "/88_does_not_exist_88", true) >= 0);
479 [ - + ]: 4 : assert_se(streq(getenv("TMPDIR"), "/88_does_not_exist_88"));
480 : :
481 [ - + ]: 4 : assert_se(var_tmp_dir(&tmp_dir) >= 0);
482 [ - + ]: 4 : assert_se(streq(tmp_dir, "/var/tmp"));
483 : :
484 [ - + ]: 4 : if (tmpdir_backup) {
485 [ # # ]: 0 : assert_se(setenv("TMPDIR", tmpdir_backup, true) >= 0);
486 [ # # ]: 0 : assert_se(streq(getenv("TMPDIR"), tmpdir_backup));
487 : : }
488 : :
489 [ - + ]: 4 : if (temp_backup) {
490 [ # # ]: 0 : assert_se(setenv("TEMP", temp_backup, true) >= 0);
491 [ # # ]: 0 : assert_se(streq(getenv("TEMP"), temp_backup));
492 : : }
493 : :
494 [ - + ]: 4 : if (tmp_backup) {
495 [ # # ]: 0 : assert_se(setenv("TMP", tmp_backup, true) >= 0);
496 [ # # ]: 0 : assert_se(streq(getenv("TMP"), tmp_backup));
497 : : }
498 : 4 : }
499 : :
500 : 4 : static void test_dot_or_dot_dot(void) {
501 [ + - ]: 4 : log_info("/* %s */", __func__);
502 : :
503 [ - + ]: 4 : assert_se(!dot_or_dot_dot(NULL));
504 [ - + ]: 4 : assert_se(!dot_or_dot_dot(""));
505 [ - + ]: 4 : assert_se(!dot_or_dot_dot("xxx"));
506 [ - + ]: 4 : assert_se(dot_or_dot_dot("."));
507 [ - + ]: 4 : assert_se(dot_or_dot_dot(".."));
508 [ - + ]: 4 : assert_se(!dot_or_dot_dot(".foo"));
509 [ - + ]: 4 : assert_se(!dot_or_dot_dot("..foo"));
510 : 4 : }
511 : :
512 : 4 : static void test_access_fd(void) {
513 : 4 : _cleanup_(rmdir_and_freep) char *p = NULL;
514 : 4 : _cleanup_close_ int fd = -1;
515 : : const char *a;
516 : :
517 [ + - ]: 4 : log_info("/* %s */", __func__);
518 : :
519 [ - + + + : 20 : a = strjoina(arg_test_dir ?: "/tmp", "/access-fd.XXXXXX");
+ - - + -
+ + + +
- ]
520 [ - + ]: 4 : assert_se(mkdtemp_malloc(a, &p) >= 0);
521 : :
522 : 4 : fd = open(p, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
523 [ - + ]: 4 : assert_se(fd >= 0);
524 : :
525 [ - + ]: 4 : assert_se(access_fd(fd, R_OK) >= 0);
526 [ - + ]: 4 : assert_se(access_fd(fd, F_OK) >= 0);
527 [ - + ]: 4 : assert_se(access_fd(fd, W_OK) >= 0);
528 : :
529 [ - + ]: 4 : assert_se(fchmod(fd, 0000) >= 0);
530 : :
531 [ - + ]: 4 : assert_se(access_fd(fd, F_OK) >= 0);
532 : :
533 [ - + ]: 4 : if (geteuid() == 0) {
534 [ # # ]: 0 : assert_se(access_fd(fd, R_OK) >= 0);
535 [ # # ]: 0 : assert_se(access_fd(fd, W_OK) >= 0);
536 : : } else {
537 [ - + ]: 4 : assert_se(access_fd(fd, R_OK) == -EACCES);
538 [ - + ]: 4 : assert_se(access_fd(fd, W_OK) == -EACCES);
539 : : }
540 : 4 : }
541 : :
542 : 4 : static void test_touch_file(void) {
543 : : uid_t test_uid, test_gid;
544 [ + - ]: 4 : _cleanup_(rm_rf_physical_and_freep) char *p = NULL;
545 : : struct stat st;
546 : : const char *a;
547 : : usec_t test_mtime;
548 : : int r;
549 : :
550 [ + - ]: 4 : log_info("/* %s */", __func__);
551 : :
552 [ + - ]: 4 : test_uid = geteuid() == 0 ? 65534 : getuid();
553 [ + - ]: 4 : test_gid = geteuid() == 0 ? 65534 : getgid();
554 : :
555 : 4 : test_mtime = usec_sub_unsigned(now(CLOCK_REALTIME), USEC_PER_WEEK);
556 : :
557 [ - + + + : 20 : a = strjoina(arg_test_dir ?: "/dev/shm", "/touch-file-XXXXXX");
+ - - + -
+ + + +
- ]
558 [ - + ]: 4 : assert_se(mkdtemp_malloc(a, &p) >= 0);
559 : :
560 [ + + + - : 20 : a = strjoina(p, "/regular");
- + - + +
+ + - ]
561 : 4 : r = touch_file(a, false, test_mtime, test_uid, test_gid, 0640);
562 [ - + ]: 4 : if (r < 0) {
563 [ # # # # ]: 0 : assert_se(IN_SET(r, -EINVAL, -ENOSYS, -ENOTTY, -EPERM));
564 : 0 : log_tests_skipped_errno(errno, "touch_file() not possible");
565 : 0 : return;
566 : : }
567 : :
568 [ - + ]: 4 : assert_se(lstat(a, &st) >= 0);
569 [ - + ]: 4 : assert_se(st.st_uid == test_uid);
570 [ - + ]: 4 : assert_se(st.st_gid == test_gid);
571 [ - + ]: 4 : assert_se(S_ISREG(st.st_mode));
572 [ - + ]: 4 : assert_se((st.st_mode & 0777) == 0640);
573 [ - + ]: 4 : assert_se(timespec_load(&st.st_mtim) == test_mtime);
574 : :
575 [ + + + - : 20 : a = strjoina(p, "/dir");
- + - + +
+ + - ]
576 [ - + ]: 4 : assert_se(mkdir(a, 0775) >= 0);
577 [ - + ]: 4 : assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0);
578 [ - + ]: 4 : assert_se(lstat(a, &st) >= 0);
579 [ - + ]: 4 : assert_se(st.st_uid == test_uid);
580 [ - + ]: 4 : assert_se(st.st_gid == test_gid);
581 [ - + ]: 4 : assert_se(S_ISDIR(st.st_mode));
582 [ - + ]: 4 : assert_se((st.st_mode & 0777) == 0640);
583 [ - + ]: 4 : assert_se(timespec_load(&st.st_mtim) == test_mtime);
584 : :
585 [ + + + - : 20 : a = strjoina(p, "/fifo");
- + - + +
+ + - ]
586 [ - + ]: 4 : assert_se(mkfifo(a, 0775) >= 0);
587 [ - + ]: 4 : assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0);
588 [ - + ]: 4 : assert_se(lstat(a, &st) >= 0);
589 [ - + ]: 4 : assert_se(st.st_uid == test_uid);
590 [ - + ]: 4 : assert_se(st.st_gid == test_gid);
591 [ - + ]: 4 : assert_se(S_ISFIFO(st.st_mode));
592 [ - + ]: 4 : assert_se((st.st_mode & 0777) == 0640);
593 [ - + ]: 4 : assert_se(timespec_load(&st.st_mtim) == test_mtime);
594 : :
595 [ + + + - : 20 : a = strjoina(p, "/sock");
- + - + +
+ + - ]
596 [ - + ]: 4 : assert_se(mknod(a, 0775 | S_IFSOCK, 0) >= 0);
597 [ - + ]: 4 : assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0);
598 [ - + ]: 4 : assert_se(lstat(a, &st) >= 0);
599 [ - + ]: 4 : assert_se(st.st_uid == test_uid);
600 [ - + ]: 4 : assert_se(st.st_gid == test_gid);
601 [ - + ]: 4 : assert_se(S_ISSOCK(st.st_mode));
602 [ - + ]: 4 : assert_se((st.st_mode & 0777) == 0640);
603 [ - + ]: 4 : assert_se(timespec_load(&st.st_mtim) == test_mtime);
604 : :
605 [ - + ]: 4 : if (geteuid() == 0) {
606 [ # # # # : 0 : a = strjoina(p, "/cdev");
# # # # #
# # # ]
607 : 0 : r = mknod(a, 0775 | S_IFCHR, makedev(0, 0));
608 [ # # # # : 0 : if (r < 0 && errno == EPERM && detect_container() > 0) {
# # ]
609 [ # # ]: 0 : log_notice("Running in unprivileged container? Skipping remaining tests in %s", __func__);
610 : 0 : return;
611 : : }
612 [ # # ]: 0 : assert_se(r >= 0);
613 [ # # ]: 0 : assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0);
614 [ # # ]: 0 : assert_se(lstat(a, &st) >= 0);
615 [ # # ]: 0 : assert_se(st.st_uid == test_uid);
616 [ # # ]: 0 : assert_se(st.st_gid == test_gid);
617 [ # # ]: 0 : assert_se(S_ISCHR(st.st_mode));
618 [ # # ]: 0 : assert_se((st.st_mode & 0777) == 0640);
619 [ # # ]: 0 : assert_se(timespec_load(&st.st_mtim) == test_mtime);
620 : :
621 [ # # # # : 0 : a = strjoina(p, "/bdev");
# # # # #
# # # ]
622 [ # # ]: 0 : assert_se(mknod(a, 0775 | S_IFBLK, makedev(0, 0)) >= 0);
623 [ # # ]: 0 : assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0);
624 [ # # ]: 0 : assert_se(lstat(a, &st) >= 0);
625 [ # # ]: 0 : assert_se(st.st_uid == test_uid);
626 [ # # ]: 0 : assert_se(st.st_gid == test_gid);
627 [ # # ]: 0 : assert_se(S_ISBLK(st.st_mode));
628 [ # # ]: 0 : assert_se((st.st_mode & 0777) == 0640);
629 [ # # ]: 0 : assert_se(timespec_load(&st.st_mtim) == test_mtime);
630 : : }
631 : :
632 [ + + + - : 20 : a = strjoina(p, "/lnk");
- + - + +
+ + - ]
633 [ - + ]: 4 : assert_se(symlink("target", a) >= 0);
634 [ - + ]: 4 : assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0);
635 [ - + ]: 4 : assert_se(lstat(a, &st) >= 0);
636 [ - + ]: 4 : assert_se(st.st_uid == test_uid);
637 [ - + ]: 4 : assert_se(st.st_gid == test_gid);
638 [ - + ]: 4 : assert_se(S_ISLNK(st.st_mode));
639 [ - + ]: 4 : assert_se(timespec_load(&st.st_mtim) == test_mtime);
640 : : }
641 : :
642 : 4 : static void test_unlinkat_deallocate(void) {
643 : 4 : _cleanup_free_ char *p = NULL;
644 : 4 : _cleanup_close_ int fd = -1;
645 : : struct stat st;
646 : :
647 [ + - ]: 4 : log_info("/* %s */", __func__);
648 : :
649 [ - + ]: 4 : assert_se(tempfn_random_child(arg_test_dir, "unlink-deallocation", &p) >= 0);
650 : :
651 : 4 : fd = open(p, O_WRONLY|O_CLOEXEC|O_CREAT|O_EXCL, 0600);
652 [ - + ]: 4 : assert_se(fd >= 0);
653 : :
654 [ - + ]: 4 : assert_se(write(fd, "hallo\n", 6) == 6);
655 : :
656 [ - + ]: 4 : assert_se(fstat(fd, &st) >= 0);
657 [ - + ]: 4 : assert_se(st.st_size == 6);
658 [ - + ]: 4 : assert_se(st.st_blocks > 0);
659 [ - + ]: 4 : assert_se(st.st_nlink == 1);
660 : :
661 [ - + ]: 4 : assert_se(unlinkat_deallocate(AT_FDCWD, p, 0) >= 0);
662 : :
663 [ - + ]: 4 : assert_se(fstat(fd, &st) >= 0);
664 [ + - - + ]: 4 : assert_se(IN_SET(st.st_size, 0, 6)); /* depending on whether hole punching worked the size will be 6
665 : : (it worked) or 0 (we had to resort to truncation) */
666 [ - + ]: 4 : assert_se(st.st_blocks == 0);
667 [ - + ]: 4 : assert_se(st.st_nlink == 0);
668 : 4 : }
669 : :
670 : 4 : static void test_fsync_directory_of_file(void) {
671 : 4 : _cleanup_close_ int fd = -1;
672 : :
673 [ + - ]: 4 : log_info("/* %s */", __func__);
674 : :
675 : 4 : fd = open_tmpfile_unlinkable(arg_test_dir, O_RDWR);
676 [ - + ]: 4 : assert_se(fd >= 0);
677 : :
678 [ - + ]: 4 : assert_se(fsync_directory_of_file(fd) >= 0);
679 : 4 : }
680 : :
681 : 4 : static void test_rename_noreplace(void) {
682 : : static const char* const table[] = {
683 : : "/reg",
684 : : "/dir",
685 : : "/fifo",
686 : : "/socket",
687 : : "/symlink",
688 : : NULL
689 : : };
690 : :
691 : 4 : _cleanup_(rm_rf_physical_and_freep) char *z = NULL;
692 : 4 : const char *j = NULL;
693 : : char **a, **b;
694 : :
695 [ + - ]: 4 : log_info("/* %s */", __func__);
696 : :
697 [ - + ]: 4 : if (arg_test_dir)
698 [ # # # # : 0 : j = strjoina(arg_test_dir, "/testXXXXXX");
# # # # #
# # # ]
699 [ - + ]: 4 : assert_se(mkdtemp_malloc(j, &z) >= 0);
700 : :
701 [ + + + - : 20 : j = strjoina(z, table[0]);
- + - + +
+ + - ]
702 [ - + ]: 4 : assert_se(touch(j) >= 0);
703 : :
704 [ + + + - : 20 : j = strjoina(z, table[1]);
- + - + +
+ + - ]
705 [ - + ]: 4 : assert_se(mkdir(j, 0777) >= 0);
706 : :
707 [ + + + - : 20 : j = strjoina(z, table[2]);
- + - + +
+ + - ]
708 : 4 : (void) mkfifo(j, 0777);
709 : :
710 [ + + + - : 20 : j = strjoina(z, table[3]);
- + - + +
+ + - ]
711 : 4 : (void) mknod(j, S_IFSOCK | 0777, 0);
712 : :
713 [ + + + - : 20 : j = strjoina(z, table[4]);
- + - + +
+ + - ]
714 : 4 : (void) symlink("foobar", j);
715 : :
716 [ + - + + ]: 24 : STRV_FOREACH(a, (char**) table) {
717 [ + + + + ]: 24 : _cleanup_free_ char *x = NULL, *y = NULL;
718 : :
719 : 20 : x = strjoin(z, *a);
720 [ - + ]: 20 : assert_se(x);
721 : :
722 [ + + ]: 20 : if (access(x, F_OK) < 0) {
723 [ - + ]: 4 : assert_se(errno == ENOENT);
724 : 4 : continue;
725 : : }
726 : :
727 [ + - + + ]: 96 : STRV_FOREACH(b, (char**) table) {
728 [ - + ]: 80 : _cleanup_free_ char *w = NULL;
729 : :
730 : 80 : w = strjoin(w, *b);
731 [ - + ]: 80 : assert_se(w);
732 : :
733 [ + - ]: 80 : if (access(w, F_OK) < 0) {
734 [ - + ]: 80 : assert_se(errno == ENOENT);
735 : 80 : continue;
736 : : }
737 : :
738 [ # # ]: 0 : assert_se(rename_noreplace(AT_FDCWD, w, AT_FDCWD, y) == -EEXIST);
739 : : }
740 : :
741 : 16 : y = strjoin(z, "/somethingelse");
742 [ - + ]: 16 : assert_se(y);
743 : :
744 [ - + ]: 16 : assert_se(rename_noreplace(AT_FDCWD, x, AT_FDCWD, y) >= 0);
745 [ - + ]: 16 : assert_se(rename_noreplace(AT_FDCWD, y, AT_FDCWD, x) >= 0);
746 : : }
747 : 4 : }
748 : :
749 : 4 : static void test_chmod_and_chown(void) {
750 [ - + ]: 4 : _cleanup_(rm_rf_physical_and_freep) char *d = NULL;
751 [ - + ]: 8 : _unused_ _cleanup_umask_ mode_t u = umask(0000);
752 : : struct stat st;
753 : : const char *p;
754 : :
755 [ + - ]: 4 : if (geteuid() != 0)
756 : 4 : return;
757 : :
758 [ # # ]: 0 : log_info("/* %s */", __func__);
759 : :
760 [ # # ]: 0 : assert_se(mkdtemp_malloc(NULL, &d) >= 0);
761 : :
762 [ # # # # : 0 : p = strjoina(d, "/reg");
# # # # #
# # # ]
763 [ # # ]: 0 : assert_se(mknod(p, S_IFREG | 0123, 0) >= 0);
764 : :
765 [ # # ]: 0 : assert_se(chmod_and_chown(p, S_IFREG | 0321, 1, 2) >= 0);
766 [ # # ]: 0 : assert_se(chmod_and_chown(p, S_IFDIR | 0555, 3, 4) == -EINVAL);
767 : :
768 [ # # ]: 0 : assert_se(lstat(p, &st) >= 0);
769 [ # # ]: 0 : assert_se(S_ISREG(st.st_mode));
770 [ # # ]: 0 : assert_se((st.st_mode & 07777) == 0321);
771 : :
772 [ # # # # : 0 : p = strjoina(d, "/dir");
# # # # #
# # # ]
773 [ # # ]: 0 : assert_se(mkdir(p, 0123) >= 0);
774 : :
775 [ # # ]: 0 : assert_se(chmod_and_chown(p, S_IFDIR | 0321, 1, 2) >= 0);
776 [ # # ]: 0 : assert_se(chmod_and_chown(p, S_IFREG | 0555, 3, 4) == -EINVAL);
777 : :
778 [ # # ]: 0 : assert_se(lstat(p, &st) >= 0);
779 [ # # ]: 0 : assert_se(S_ISDIR(st.st_mode));
780 [ # # ]: 0 : assert_se((st.st_mode & 07777) == 0321);
781 : :
782 [ # # # # : 0 : p = strjoina(d, "/lnk");
# # # # #
# # # ]
783 [ # # ]: 0 : assert_se(symlink("idontexist", p) >= 0);
784 : :
785 [ # # ]: 0 : assert_se(chmod_and_chown(p, S_IFLNK | 0321, 1, 2) >= 0);
786 [ # # ]: 0 : assert_se(chmod_and_chown(p, S_IFREG | 0555, 3, 4) == -EINVAL);
787 [ # # ]: 0 : assert_se(chmod_and_chown(p, S_IFDIR | 0555, 3, 4) == -EINVAL);
788 : :
789 [ # # ]: 0 : assert_se(lstat(p, &st) >= 0);
790 [ # # ]: 0 : assert_se(S_ISLNK(st.st_mode));
791 : : }
792 : :
793 : 4 : int main(int argc, char *argv[]) {
794 : 4 : test_setup_logging(LOG_INFO);
795 : :
796 : 4 : arg_test_dir = argv[1];
797 : :
798 : 4 : test_chase_symlinks();
799 : 4 : test_unlink_noerrno();
800 : 4 : test_readlink_and_make_absolute();
801 : 4 : test_get_files_in_directory();
802 : 4 : test_var_tmp();
803 : 4 : test_dot_or_dot_dot();
804 : 4 : test_access_fd();
805 : 4 : test_touch_file();
806 : 4 : test_unlinkat_deallocate();
807 : 4 : test_fsync_directory_of_file();
808 : 4 : test_rename_noreplace();
809 : 4 : test_chmod_and_chown();
810 : :
811 : 4 : return 0;
812 : : }
|