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