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 1 : static void test_chase_symlinks(void) {
26 1 : _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 1 : log_info("/* %s */", __func__);
33 :
34 5 : temp = strjoina(arg_test_dir ?: "/tmp", "/test-chase.XXXXXX");
35 1 : assert_se(mkdtemp(temp));
36 :
37 5 : top = strjoina(temp, "/top");
38 1 : assert_se(mkdir(top, 0700) >= 0);
39 :
40 5 : p = strjoina(top, "/dot");
41 1 : 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 5 : p = strjoina(top, "/dotdot");
48 1 : assert_se(symlink("..", p) >= 0);
49 :
50 5 : p = strjoina(top, "/dotdota");
51 1 : assert_se(symlink("../a", p) >= 0);
52 :
53 5 : p = strjoina(temp, "/a");
54 1 : assert_se(symlink("b", p) >= 0);
55 :
56 5 : p = strjoina(temp, "/b");
57 1 : assert_se(symlink("/usr", p) >= 0);
58 :
59 5 : p = strjoina(temp, "/start");
60 1 : assert_se(symlink("top/dot/dotdota", p) >= 0);
61 :
62 : /* Paths that use symlinks underneath the "root" */
63 :
64 1 : r = chase_symlinks(p, NULL, 0, &result);
65 1 : assert_se(r > 0);
66 1 : assert_se(path_equal(result, "/usr"));
67 1 : result = mfree(result);
68 :
69 5 : pslash = strjoina(p, "/");
70 1 : r = chase_symlinks(pslash, NULL, 0, &result);
71 1 : assert_se(r > 0);
72 1 : assert_se(path_equal(result, "/usr/"));
73 1 : result = mfree(result);
74 :
75 1 : r = chase_symlinks(p, temp, 0, &result);
76 1 : assert_se(r == -ENOENT);
77 :
78 1 : r = chase_symlinks(pslash, temp, 0, &result);
79 1 : assert_se(r == -ENOENT);
80 :
81 5 : q = strjoina(temp, "/usr");
82 :
83 1 : r = chase_symlinks(p, temp, CHASE_NONEXISTENT, &result);
84 1 : assert_se(r == 0);
85 1 : assert_se(path_equal(result, q));
86 1 : result = mfree(result);
87 :
88 5 : qslash = strjoina(q, "/");
89 :
90 1 : r = chase_symlinks(pslash, temp, CHASE_NONEXISTENT, &result);
91 1 : assert_se(r == 0);
92 1 : assert_se(path_equal(result, qslash));
93 1 : result = mfree(result);
94 :
95 1 : assert_se(mkdir(q, 0700) >= 0);
96 :
97 1 : r = chase_symlinks(p, temp, 0, &result);
98 1 : assert_se(r > 0);
99 1 : assert_se(path_equal(result, q));
100 1 : result = mfree(result);
101 :
102 1 : r = chase_symlinks(pslash, temp, 0, &result);
103 1 : assert_se(r > 0);
104 1 : assert_se(path_equal(result, qslash));
105 1 : result = mfree(result);
106 :
107 5 : p = strjoina(temp, "/slash");
108 1 : assert_se(symlink("/", p) >= 0);
109 :
110 1 : r = chase_symlinks(p, NULL, 0, &result);
111 1 : assert_se(r > 0);
112 1 : assert_se(path_equal(result, "/"));
113 1 : result = mfree(result);
114 :
115 1 : r = chase_symlinks(p, temp, 0, &result);
116 1 : assert_se(r > 0);
117 1 : assert_se(path_equal(result, temp));
118 1 : result = mfree(result);
119 :
120 : /* Paths that would "escape" outside of the "root" */
121 :
122 5 : p = strjoina(temp, "/6dots");
123 1 : assert_se(symlink("../../..", p) >= 0);
124 :
125 1 : r = chase_symlinks(p, temp, 0, &result);
126 1 : assert_se(r > 0 && path_equal(result, temp));
127 1 : result = mfree(result);
128 :
129 5 : p = strjoina(temp, "/6dotsusr");
130 1 : assert_se(symlink("../../../usr", p) >= 0);
131 :
132 1 : r = chase_symlinks(p, temp, 0, &result);
133 1 : assert_se(r > 0 && path_equal(result, q));
134 1 : result = mfree(result);
135 :
136 5 : p = strjoina(temp, "/top/8dotsusr");
137 1 : assert_se(symlink("../../../../usr", p) >= 0);
138 :
139 1 : r = chase_symlinks(p, temp, 0, &result);
140 1 : assert_se(r > 0 && path_equal(result, q));
141 1 : result = mfree(result);
142 :
143 : /* Paths that contain repeated slashes */
144 :
145 5 : p = strjoina(temp, "/slashslash");
146 1 : assert_se(symlink("///usr///", p) >= 0);
147 :
148 1 : r = chase_symlinks(p, NULL, 0, &result);
149 1 : assert_se(r > 0);
150 1 : assert_se(path_equal(result, "/usr"));
151 1 : result = mfree(result);
152 :
153 1 : r = chase_symlinks(p, temp, 0, &result);
154 1 : assert_se(r > 0);
155 1 : assert_se(path_equal(result, q));
156 1 : result = mfree(result);
157 :
158 : /* Paths underneath the "root" with different UIDs while using CHASE_SAFE */
159 :
160 1 : 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 1 : r = chase_symlinks("/etc/./.././", NULL, 0, &result);
185 1 : assert_se(r > 0);
186 1 : assert_se(path_equal(result, "/"));
187 1 : result = mfree(result);
188 :
189 1 : r = chase_symlinks("/etc/./.././", "/etc", 0, &result);
190 1 : assert_se(r > 0 && path_equal(result, "/etc"));
191 1 : result = mfree(result);
192 :
193 1 : r = chase_symlinks("/../.././//../../etc", NULL, 0, &result);
194 1 : assert_se(r > 0);
195 1 : assert_se(streq(result, "/etc"));
196 1 : result = mfree(result);
197 :
198 1 : r = chase_symlinks("/../.././//../../test-chase.fsldajfl", NULL, CHASE_NONEXISTENT, &result);
199 1 : assert_se(r == 0);
200 1 : assert_se(streq(result, "/test-chase.fsldajfl"));
201 1 : result = mfree(result);
202 :
203 1 : r = chase_symlinks("/../.././//../../etc", "/", CHASE_PREFIX_ROOT, &result);
204 1 : assert_se(r > 0);
205 1 : assert_se(streq(result, "/etc"));
206 1 : result = mfree(result);
207 :
208 1 : r = chase_symlinks("/../.././//../../test-chase.fsldajfl", "/", CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &result);
209 1 : assert_se(r == 0);
210 1 : assert_se(streq(result, "/test-chase.fsldajfl"));
211 1 : result = mfree(result);
212 :
213 1 : r = chase_symlinks("/etc/machine-id/foo", NULL, 0, &result);
214 1 : assert_se(r == -ENOTDIR);
215 1 : result = mfree(result);
216 :
217 : /* Path that loops back to self */
218 :
219 5 : p = strjoina(temp, "/recursive-symlink");
220 1 : assert_se(symlink("recursive-symlink", p) >= 0);
221 1 : r = chase_symlinks(p, NULL, 0, &result);
222 1 : assert_se(r == -ELOOP);
223 :
224 : /* Path which doesn't exist */
225 :
226 5 : p = strjoina(temp, "/idontexist");
227 1 : r = chase_symlinks(p, NULL, 0, &result);
228 1 : assert_se(r == -ENOENT);
229 :
230 1 : r = chase_symlinks(p, NULL, CHASE_NONEXISTENT, &result);
231 1 : assert_se(r == 0);
232 1 : assert_se(path_equal(result, p));
233 1 : result = mfree(result);
234 :
235 5 : p = strjoina(temp, "/idontexist/meneither");
236 1 : r = chase_symlinks(p, NULL, 0, &result);
237 1 : assert_se(r == -ENOENT);
238 :
239 1 : r = chase_symlinks(p, NULL, CHASE_NONEXISTENT, &result);
240 1 : assert_se(r == 0);
241 1 : assert_se(path_equal(result, p));
242 1 : result = mfree(result);
243 :
244 : /* Path which doesn't exist, but contains weird stuff */
245 :
246 5 : p = strjoina(temp, "/idontexist/..");
247 1 : r = chase_symlinks(p, NULL, 0, &result);
248 1 : assert_se(r == -ENOENT);
249 :
250 1 : r = chase_symlinks(p, NULL, CHASE_NONEXISTENT, &result);
251 1 : assert_se(r == -ENOENT);
252 :
253 5 : p = strjoina(temp, "/target");
254 5 : q = strjoina(temp, "/top");
255 1 : assert_se(symlink(q, p) >= 0);
256 5 : p = strjoina(temp, "/target/idontexist");
257 1 : r = chase_symlinks(p, NULL, 0, &result);
258 1 : assert_se(r == -ENOENT);
259 :
260 1 : 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 5 : p = strjoina(temp, "/machine-id-test");
287 1 : assert_se(symlink("/usr/../etc/./machine-id", p) >= 0);
288 :
289 1 : pfd = chase_symlinks(p, NULL, CHASE_OPEN, NULL);
290 1 : if (pfd != -ENOENT) {
291 1 : _cleanup_close_ int fd = -1;
292 : sd_id128_t a, b;
293 :
294 1 : assert_se(pfd >= 0);
295 :
296 1 : fd = fd_reopen(pfd, O_RDONLY|O_CLOEXEC);
297 1 : assert_se(fd >= 0);
298 1 : safe_close(pfd);
299 :
300 1 : assert_se(id128_read_fd(fd, ID128_PLAIN, &a) >= 0);
301 1 : assert_se(sd_id128_get_machine(&b) >= 0);
302 1 : assert_se(sd_id128_equal(a, b));
303 : }
304 :
305 : /* Test CHASE_NOFOLLOW */
306 :
307 5 : p = strjoina(temp, "/target");
308 5 : q = strjoina(temp, "/symlink");
309 1 : assert_se(symlink(p, q) >= 0);
310 1 : pfd = chase_symlinks(q, NULL, CHASE_OPEN|CHASE_NOFOLLOW, &result);
311 1 : assert_se(pfd > 0);
312 1 : assert_se(path_equal(result, q));
313 1 : assert_se(fstat(pfd, &st) >= 0);
314 1 : assert_se(S_ISLNK(st.st_mode));
315 1 : result = mfree(result);
316 :
317 : /* s1 -> s2 -> nonexistent */
318 5 : q = strjoina(temp, "/s1");
319 1 : assert_se(symlink("s2", q) >= 0);
320 5 : p = strjoina(temp, "/s2");
321 1 : assert_se(symlink("nonexistent", p) >= 0);
322 1 : pfd = chase_symlinks(q, NULL, CHASE_OPEN|CHASE_NOFOLLOW, &result);
323 1 : assert_se(pfd > 0);
324 1 : assert_se(path_equal(result, q));
325 1 : assert_se(fstat(pfd, &st) >= 0);
326 1 : assert_se(S_ISLNK(st.st_mode));
327 1 : result = mfree(result);
328 :
329 : /* Test CHASE_ONE */
330 :
331 5 : p = strjoina(temp, "/start");
332 1 : r = chase_symlinks(p, NULL, CHASE_STEP, &result);
333 1 : assert_se(r == 0);
334 5 : p = strjoina(temp, "/top/dot/dotdota");
335 1 : assert_se(streq(p, result));
336 1 : result = mfree(result);
337 :
338 1 : r = chase_symlinks(p, NULL, CHASE_STEP, &result);
339 1 : assert_se(r == 0);
340 5 : p = strjoina(temp, "/top/./dotdota");
341 1 : assert_se(streq(p, result));
342 1 : result = mfree(result);
343 :
344 1 : r = chase_symlinks(p, NULL, CHASE_STEP, &result);
345 1 : assert_se(r == 0);
346 5 : p = strjoina(temp, "/top/../a");
347 1 : assert_se(streq(p, result));
348 1 : result = mfree(result);
349 :
350 1 : r = chase_symlinks(p, NULL, CHASE_STEP, &result);
351 1 : assert_se(r == 0);
352 5 : p = strjoina(temp, "/a");
353 1 : assert_se(streq(p, result));
354 1 : result = mfree(result);
355 :
356 1 : r = chase_symlinks(p, NULL, CHASE_STEP, &result);
357 1 : assert_se(r == 0);
358 5 : p = strjoina(temp, "/b");
359 1 : assert_se(streq(p, result));
360 1 : result = mfree(result);
361 :
362 1 : r = chase_symlinks(p, NULL, CHASE_STEP, &result);
363 1 : assert_se(r == 0);
364 1 : assert_se(streq("/usr", result));
365 1 : result = mfree(result);
366 :
367 1 : r = chase_symlinks("/usr", NULL, CHASE_STEP, &result);
368 1 : assert_se(r > 0);
369 1 : assert_se(streq("/usr", result));
370 1 : result = mfree(result);
371 :
372 1 : cleanup:
373 1 : assert_se(rm_rf(temp, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
374 1 : }
375 :
376 1 : static void test_unlink_noerrno(void) {
377 : char *name;
378 : int fd;
379 :
380 1 : log_info("/* %s */", __func__);
381 :
382 5 : name = strjoina(arg_test_dir ?: "/tmp", "/test-close_nointr.XXXXXX");
383 1 : fd = mkostemp_safe(name);
384 1 : assert_se(fd >= 0);
385 1 : assert_se(close_nointr(fd) >= 0);
386 :
387 : {
388 1 : PROTECT_ERRNO;
389 1 : errno = 42;
390 1 : assert_se(unlink_noerrno(name) >= 0);
391 1 : assert_se(errno == 42);
392 1 : assert_se(unlink_noerrno(name) < 0);
393 1 : assert_se(errno == 42);
394 : }
395 1 : }
396 :
397 1 : static void test_readlink_and_make_absolute(void) {
398 : const char *tempdir, *name, *name2, *name_alias;
399 1 : _cleanup_free_ char *r1 = NULL, *r2 = NULL, *pwd = NULL;
400 :
401 1 : log_info("/* %s */", __func__);
402 :
403 5 : tempdir = strjoina(arg_test_dir ?: "/tmp", "/test-readlink_and_make_absolute");
404 5 : name = strjoina(tempdir, "/original");
405 1 : name2 = "test-readlink_and_make_absolute/original";
406 5 : name_alias = strjoina(arg_test_dir ?: "/tmp", "/test-readlink_and_make_absolute-alias");
407 :
408 1 : assert_se(mkdir_safe(tempdir, 0755, getuid(), getgid(), MKDIR_WARN_MODE) >= 0);
409 1 : assert_se(touch(name) >= 0);
410 :
411 1 : 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 1 : assert_se(readlink_and_make_absolute(name_alias, &r1) >= 0);
416 1 : assert_se(streq(r1, name));
417 1 : assert_se(unlink(name_alias) >= 0);
418 :
419 1 : assert_se(safe_getcwd(&pwd) >= 0);
420 :
421 1 : assert_se(chdir(tempdir) >= 0);
422 1 : assert_se(symlink(name2, name_alias) >= 0);
423 1 : assert_se(readlink_and_make_absolute(name_alias, &r2) >= 0);
424 1 : assert_se(streq(r2, name));
425 1 : assert_se(unlink(name_alias) >= 0);
426 :
427 1 : assert_se(chdir(pwd) >= 0);
428 : }
429 :
430 1 : assert_se(rm_rf(tempdir, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
431 1 : }
432 :
433 1 : static void test_get_files_in_directory(void) {
434 1 : _cleanup_strv_free_ char **l = NULL, **t = NULL;
435 :
436 1 : assert_se(get_files_in_directory(arg_test_dir ?: "/tmp", &l) >= 0);
437 1 : assert_se(get_files_in_directory(".", &t) >= 0);
438 1 : assert_se(get_files_in_directory(".", NULL) >= 0);
439 1 : }
440 :
441 1 : static void test_var_tmp(void) {
442 1 : _cleanup_free_ char *tmpdir_backup = NULL, *temp_backup = NULL, *tmp_backup = NULL;
443 1 : const char *tmp_dir = NULL, *t;
444 :
445 1 : log_info("/* %s */", __func__);
446 :
447 1 : t = getenv("TMPDIR");
448 1 : if (t) {
449 0 : tmpdir_backup = strdup(t);
450 0 : assert_se(tmpdir_backup);
451 : }
452 :
453 1 : t = getenv("TEMP");
454 1 : if (t) {
455 0 : temp_backup = strdup(t);
456 0 : assert_se(temp_backup);
457 : }
458 :
459 1 : t = getenv("TMP");
460 1 : if (t) {
461 0 : tmp_backup = strdup(t);
462 0 : assert_se(tmp_backup);
463 : }
464 :
465 1 : assert_se(unsetenv("TMPDIR") >= 0);
466 1 : assert_se(unsetenv("TEMP") >= 0);
467 1 : assert_se(unsetenv("TMP") >= 0);
468 :
469 1 : assert_se(var_tmp_dir(&tmp_dir) >= 0);
470 1 : assert_se(streq(tmp_dir, "/var/tmp"));
471 :
472 1 : assert_se(setenv("TMPDIR", "/tmp", true) >= 0);
473 1 : assert_se(streq(getenv("TMPDIR"), "/tmp"));
474 :
475 1 : assert_se(var_tmp_dir(&tmp_dir) >= 0);
476 1 : assert_se(streq(tmp_dir, "/tmp"));
477 :
478 1 : assert_se(setenv("TMPDIR", "/88_does_not_exist_88", true) >= 0);
479 1 : assert_se(streq(getenv("TMPDIR"), "/88_does_not_exist_88"));
480 :
481 1 : assert_se(var_tmp_dir(&tmp_dir) >= 0);
482 1 : assert_se(streq(tmp_dir, "/var/tmp"));
483 :
484 1 : 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 1 : 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 1 : if (tmp_backup) {
495 0 : assert_se(setenv("TMP", tmp_backup, true) >= 0);
496 0 : assert_se(streq(getenv("TMP"), tmp_backup));
497 : }
498 1 : }
499 :
500 1 : static void test_dot_or_dot_dot(void) {
501 1 : log_info("/* %s */", __func__);
502 :
503 1 : assert_se(!dot_or_dot_dot(NULL));
504 1 : assert_se(!dot_or_dot_dot(""));
505 1 : assert_se(!dot_or_dot_dot("xxx"));
506 1 : assert_se(dot_or_dot_dot("."));
507 1 : assert_se(dot_or_dot_dot(".."));
508 1 : assert_se(!dot_or_dot_dot(".foo"));
509 1 : assert_se(!dot_or_dot_dot("..foo"));
510 1 : }
511 :
512 1 : static void test_access_fd(void) {
513 1 : _cleanup_(rmdir_and_freep) char *p = NULL;
514 1 : _cleanup_close_ int fd = -1;
515 : const char *a;
516 :
517 1 : log_info("/* %s */", __func__);
518 :
519 5 : a = strjoina(arg_test_dir ?: "/tmp", "/access-fd.XXXXXX");
520 1 : assert_se(mkdtemp_malloc(a, &p) >= 0);
521 :
522 1 : fd = open(p, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
523 1 : assert_se(fd >= 0);
524 :
525 1 : assert_se(access_fd(fd, R_OK) >= 0);
526 1 : assert_se(access_fd(fd, F_OK) >= 0);
527 1 : assert_se(access_fd(fd, W_OK) >= 0);
528 :
529 1 : assert_se(fchmod(fd, 0000) >= 0);
530 :
531 1 : assert_se(access_fd(fd, F_OK) >= 0);
532 :
533 1 : 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 1 : assert_se(access_fd(fd, R_OK) == -EACCES);
538 1 : assert_se(access_fd(fd, W_OK) == -EACCES);
539 : }
540 1 : }
541 :
542 1 : static void test_touch_file(void) {
543 : uid_t test_uid, test_gid;
544 1 : _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 1 : log_info("/* %s */", __func__);
551 :
552 1 : test_uid = geteuid() == 0 ? 65534 : getuid();
553 1 : test_gid = geteuid() == 0 ? 65534 : getgid();
554 :
555 1 : test_mtime = usec_sub_unsigned(now(CLOCK_REALTIME), USEC_PER_WEEK);
556 :
557 5 : a = strjoina(arg_test_dir ?: "/dev/shm", "/touch-file-XXXXXX");
558 1 : assert_se(mkdtemp_malloc(a, &p) >= 0);
559 :
560 5 : a = strjoina(p, "/regular");
561 1 : r = touch_file(a, false, test_mtime, test_uid, test_gid, 0640);
562 1 : 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 1 : assert_se(lstat(a, &st) >= 0);
569 1 : assert_se(st.st_uid == test_uid);
570 1 : assert_se(st.st_gid == test_gid);
571 1 : assert_se(S_ISREG(st.st_mode));
572 1 : assert_se((st.st_mode & 0777) == 0640);
573 1 : assert_se(timespec_load(&st.st_mtim) == test_mtime);
574 :
575 5 : a = strjoina(p, "/dir");
576 1 : assert_se(mkdir(a, 0775) >= 0);
577 1 : assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0);
578 1 : assert_se(lstat(a, &st) >= 0);
579 1 : assert_se(st.st_uid == test_uid);
580 1 : assert_se(st.st_gid == test_gid);
581 1 : assert_se(S_ISDIR(st.st_mode));
582 1 : assert_se((st.st_mode & 0777) == 0640);
583 1 : assert_se(timespec_load(&st.st_mtim) == test_mtime);
584 :
585 5 : a = strjoina(p, "/fifo");
586 1 : assert_se(mkfifo(a, 0775) >= 0);
587 1 : assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0);
588 1 : assert_se(lstat(a, &st) >= 0);
589 1 : assert_se(st.st_uid == test_uid);
590 1 : assert_se(st.st_gid == test_gid);
591 1 : assert_se(S_ISFIFO(st.st_mode));
592 1 : assert_se((st.st_mode & 0777) == 0640);
593 1 : assert_se(timespec_load(&st.st_mtim) == test_mtime);
594 :
595 5 : a = strjoina(p, "/sock");
596 1 : assert_se(mknod(a, 0775 | S_IFSOCK, 0) >= 0);
597 1 : assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0);
598 1 : assert_se(lstat(a, &st) >= 0);
599 1 : assert_se(st.st_uid == test_uid);
600 1 : assert_se(st.st_gid == test_gid);
601 1 : assert_se(S_ISSOCK(st.st_mode));
602 1 : assert_se((st.st_mode & 0777) == 0640);
603 1 : assert_se(timespec_load(&st.st_mtim) == test_mtime);
604 :
605 1 : 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 5 : a = strjoina(p, "/lnk");
633 1 : assert_se(symlink("target", a) >= 0);
634 1 : assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0);
635 1 : assert_se(lstat(a, &st) >= 0);
636 1 : assert_se(st.st_uid == test_uid);
637 1 : assert_se(st.st_gid == test_gid);
638 1 : assert_se(S_ISLNK(st.st_mode));
639 1 : assert_se(timespec_load(&st.st_mtim) == test_mtime);
640 : }
641 :
642 1 : static void test_unlinkat_deallocate(void) {
643 1 : _cleanup_free_ char *p = NULL;
644 1 : _cleanup_close_ int fd = -1;
645 : struct stat st;
646 :
647 1 : log_info("/* %s */", __func__);
648 :
649 1 : assert_se(tempfn_random_child(arg_test_dir, "unlink-deallocation", &p) >= 0);
650 :
651 1 : fd = open(p, O_WRONLY|O_CLOEXEC|O_CREAT|O_EXCL, 0600);
652 1 : assert_se(fd >= 0);
653 :
654 1 : assert_se(write(fd, "hallo\n", 6) == 6);
655 :
656 1 : assert_se(fstat(fd, &st) >= 0);
657 1 : assert_se(st.st_size == 6);
658 1 : assert_se(st.st_blocks > 0);
659 1 : assert_se(st.st_nlink == 1);
660 :
661 1 : assert_se(unlinkat_deallocate(AT_FDCWD, p, 0) >= 0);
662 :
663 1 : assert_se(fstat(fd, &st) >= 0);
664 1 : 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 1 : assert_se(st.st_blocks == 0);
667 1 : assert_se(st.st_nlink == 0);
668 1 : }
669 :
670 1 : static void test_fsync_directory_of_file(void) {
671 1 : _cleanup_close_ int fd = -1;
672 :
673 1 : log_info("/* %s */", __func__);
674 :
675 1 : fd = open_tmpfile_unlinkable(arg_test_dir, O_RDWR);
676 1 : assert_se(fd >= 0);
677 :
678 1 : assert_se(fsync_directory_of_file(fd) >= 0);
679 1 : }
680 :
681 1 : 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 1 : _cleanup_(rm_rf_physical_and_freep) char *z = NULL;
692 1 : const char *j = NULL;
693 : char **a, **b;
694 :
695 1 : log_info("/* %s */", __func__);
696 :
697 1 : if (arg_test_dir)
698 0 : j = strjoina(arg_test_dir, "/testXXXXXX");
699 1 : assert_se(mkdtemp_malloc(j, &z) >= 0);
700 :
701 5 : j = strjoina(z, table[0]);
702 1 : assert_se(touch(j) >= 0);
703 :
704 5 : j = strjoina(z, table[1]);
705 1 : assert_se(mkdir(j, 0777) >= 0);
706 :
707 5 : j = strjoina(z, table[2]);
708 1 : (void) mkfifo(j, 0777);
709 :
710 5 : j = strjoina(z, table[3]);
711 1 : (void) mknod(j, S_IFSOCK | 0777, 0);
712 :
713 5 : j = strjoina(z, table[4]);
714 1 : (void) symlink("foobar", j);
715 :
716 6 : STRV_FOREACH(a, (char**) table) {
717 6 : _cleanup_free_ char *x = NULL, *y = NULL;
718 :
719 5 : x = strjoin(z, *a);
720 5 : assert_se(x);
721 :
722 5 : if (access(x, F_OK) < 0) {
723 1 : assert_se(errno == ENOENT);
724 1 : continue;
725 : }
726 :
727 24 : STRV_FOREACH(b, (char**) table) {
728 20 : _cleanup_free_ char *w = NULL;
729 :
730 20 : w = strjoin(w, *b);
731 20 : assert_se(w);
732 :
733 20 : if (access(w, F_OK) < 0) {
734 20 : assert_se(errno == ENOENT);
735 20 : continue;
736 : }
737 :
738 0 : assert_se(rename_noreplace(AT_FDCWD, w, AT_FDCWD, y) == -EEXIST);
739 : }
740 :
741 4 : y = strjoin(z, "/somethingelse");
742 4 : assert_se(y);
743 :
744 4 : assert_se(rename_noreplace(AT_FDCWD, x, AT_FDCWD, y) >= 0);
745 4 : assert_se(rename_noreplace(AT_FDCWD, y, AT_FDCWD, x) >= 0);
746 : }
747 1 : }
748 :
749 1 : static void test_chmod_and_chown(void) {
750 1 : _cleanup_(rm_rf_physical_and_freep) char *d = NULL;
751 2 : _unused_ _cleanup_umask_ mode_t u = umask(0000);
752 : struct stat st;
753 : const char *p;
754 :
755 1 : if (geteuid() != 0)
756 1 : 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 1 : int main(int argc, char *argv[]) {
794 1 : test_setup_logging(LOG_INFO);
795 :
796 1 : arg_test_dir = argv[1];
797 :
798 1 : test_chase_symlinks();
799 1 : test_unlink_noerrno();
800 1 : test_readlink_and_make_absolute();
801 1 : test_get_files_in_directory();
802 1 : test_var_tmp();
803 1 : test_dot_or_dot_dot();
804 1 : test_access_fd();
805 1 : test_touch_file();
806 1 : test_unlinkat_deallocate();
807 1 : test_fsync_directory_of_file();
808 1 : test_rename_noreplace();
809 1 : test_chmod_and_chown();
810 :
811 1 : return 0;
812 : }
|