1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2010 Lennart Poettering
10 #include "alloc-util.h"
12 //#include "fd-util.h"
15 #include "id128-util.h"
18 #include "path-util.h"
20 #include "stdio-util.h"
21 #include "string-util.h"
23 #include "user-util.h"
26 static void test_chase_symlinks(void) {
27 _cleanup_free_ char *result = NULL;
28 char temp[] = "/tmp/test-chase.XXXXXX";
29 const char *top, *p, *pslash, *q, *qslash;
32 assert_se(mkdtemp(temp));
34 top = strjoina(temp, "/top");
35 assert_se(mkdir(top, 0700) >= 0);
37 p = strjoina(top, "/dot");
38 assert_se(symlink(".", p) >= 0);
40 p = strjoina(top, "/dotdot");
41 assert_se(symlink("..", p) >= 0);
43 p = strjoina(top, "/dotdota");
44 assert_se(symlink("../a", p) >= 0);
46 p = strjoina(temp, "/a");
47 assert_se(symlink("b", p) >= 0);
49 p = strjoina(temp, "/b");
50 assert_se(symlink("/usr", p) >= 0);
52 p = strjoina(temp, "/start");
53 assert_se(symlink("top/dot/dotdota", p) >= 0);
55 /* Paths that use symlinks underneath the "root" */
57 r = chase_symlinks(p, NULL, 0, &result);
59 assert_se(path_equal(result, "/usr"));
60 result = mfree(result);
62 pslash = strjoina(p, "/");
63 r = chase_symlinks(pslash, NULL, 0, &result);
65 assert_se(path_equal(result, "/usr/"));
66 result = mfree(result);
68 r = chase_symlinks(p, temp, 0, &result);
69 assert_se(r == -ENOENT);
71 r = chase_symlinks(pslash, temp, 0, &result);
72 assert_se(r == -ENOENT);
74 q = strjoina(temp, "/usr");
76 r = chase_symlinks(p, temp, CHASE_NONEXISTENT, &result);
78 assert_se(path_equal(result, q));
79 result = mfree(result);
81 qslash = strjoina(q, "/");
83 r = chase_symlinks(pslash, temp, CHASE_NONEXISTENT, &result);
85 assert_se(path_equal(result, qslash));
86 result = mfree(result);
88 assert_se(mkdir(q, 0700) >= 0);
90 r = chase_symlinks(p, temp, 0, &result);
92 assert_se(path_equal(result, q));
93 result = mfree(result);
95 r = chase_symlinks(pslash, temp, 0, &result);
97 assert_se(path_equal(result, qslash));
98 result = mfree(result);
100 p = strjoina(temp, "/slash");
101 assert_se(symlink("/", p) >= 0);
103 r = chase_symlinks(p, NULL, 0, &result);
105 assert_se(path_equal(result, "/"));
106 result = mfree(result);
108 r = chase_symlinks(p, temp, 0, &result);
110 assert_se(path_equal(result, temp));
111 result = mfree(result);
113 /* Paths that would "escape" outside of the "root" */
115 p = strjoina(temp, "/6dots");
116 assert_se(symlink("../../..", p) >= 0);
118 r = chase_symlinks(p, temp, 0, &result);
119 assert_se(r > 0 && path_equal(result, temp));
120 result = mfree(result);
122 p = strjoina(temp, "/6dotsusr");
123 assert_se(symlink("../../../usr", p) >= 0);
125 r = chase_symlinks(p, temp, 0, &result);
126 assert_se(r > 0 && path_equal(result, q));
127 result = mfree(result);
129 p = strjoina(temp, "/top/8dotsusr");
130 assert_se(symlink("../../../../usr", p) >= 0);
132 r = chase_symlinks(p, temp, 0, &result);
133 assert_se(r > 0 && path_equal(result, q));
134 result = mfree(result);
136 /* Paths that contain repeated slashes */
138 p = strjoina(temp, "/slashslash");
139 assert_se(symlink("///usr///", p) >= 0);
141 r = chase_symlinks(p, NULL, 0, &result);
143 assert_se(path_equal(result, "/usr"));
144 result = mfree(result);
146 r = chase_symlinks(p, temp, 0, &result);
148 assert_se(path_equal(result, q));
149 result = mfree(result);
153 r = chase_symlinks("/etc/./.././", NULL, 0, &result);
155 assert_se(path_equal(result, "/"));
156 result = mfree(result);
158 r = chase_symlinks("/etc/./.././", "/etc", 0, &result);
159 assert_se(r > 0 && path_equal(result, "/etc"));
160 result = mfree(result);
162 r = chase_symlinks("/../.././//../../etc", NULL, 0, &result);
164 assert_se(streq(result, "/etc"));
165 result = mfree(result);
167 r = chase_symlinks("/../.././//../../test-chase.fsldajfl", NULL, CHASE_NONEXISTENT, &result);
169 assert_se(streq(result, "/test-chase.fsldajfl"));
170 result = mfree(result);
172 r = chase_symlinks("/../.././//../../etc", "/", CHASE_PREFIX_ROOT, &result);
174 assert_se(streq(result, "/etc"));
175 result = mfree(result);
177 r = chase_symlinks("/../.././//../../test-chase.fsldajfl", "/", CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &result);
179 assert_se(streq(result, "/test-chase.fsldajfl"));
180 result = mfree(result);
182 r = chase_symlinks("/etc/machine-id/foo", NULL, 0, &result);
183 assert_se(r == -ENOTDIR);
184 result = mfree(result);
186 /* Path that loops back to self */
188 p = strjoina(temp, "/recursive-symlink");
189 assert_se(symlink("recursive-symlink", p) >= 0);
190 r = chase_symlinks(p, NULL, 0, &result);
191 assert_se(r == -ELOOP);
193 /* Path which doesn't exist */
195 p = strjoina(temp, "/idontexist");
196 r = chase_symlinks(p, NULL, 0, &result);
197 assert_se(r == -ENOENT);
199 r = chase_symlinks(p, NULL, CHASE_NONEXISTENT, &result);
201 assert_se(path_equal(result, p));
202 result = mfree(result);
204 p = strjoina(temp, "/idontexist/meneither");
205 r = chase_symlinks(p, NULL, 0, &result);
206 assert_se(r == -ENOENT);
208 r = chase_symlinks(p, NULL, CHASE_NONEXISTENT, &result);
210 assert_se(path_equal(result, p));
211 result = mfree(result);
213 /* Path which doesn't exist, but contains weird stuff */
215 p = strjoina(temp, "/idontexist/..");
216 r = chase_symlinks(p, NULL, 0, &result);
217 assert_se(r == -ENOENT);
219 r = chase_symlinks(p, NULL, CHASE_NONEXISTENT, &result);
220 assert_se(r == -ENOENT);
222 p = strjoina(temp, "/target");
223 q = strjoina(temp, "/top");
224 assert_se(symlink(q, p) >= 0);
225 p = strjoina(temp, "/target/idontexist");
226 r = chase_symlinks(p, NULL, 0, &result);
227 assert_se(r == -ENOENT);
229 if (geteuid() == 0) {
230 p = strjoina(temp, "/priv1");
231 assert_se(mkdir(p, 0755) >= 0);
233 q = strjoina(p, "/priv2");
234 assert_se(mkdir(q, 0755) >= 0);
236 assert_se(chase_symlinks(q, NULL, CHASE_SAFE, NULL) >= 0);
238 assert_se(chown(q, UID_NOBODY, GID_NOBODY) >= 0);
239 assert_se(chase_symlinks(q, NULL, CHASE_SAFE, NULL) >= 0);
241 assert_se(chown(p, UID_NOBODY, GID_NOBODY) >= 0);
242 assert_se(chase_symlinks(q, NULL, CHASE_SAFE, NULL) >= 0);
244 assert_se(chown(q, 0, 0) >= 0);
245 assert_se(chase_symlinks(q, NULL, CHASE_SAFE, NULL) == -EPERM);
247 assert_se(rmdir(q) >= 0);
248 assert_se(symlink("/etc/passwd", q) >= 0);
249 assert_se(chase_symlinks(q, NULL, CHASE_SAFE, NULL) == -EPERM);
251 assert_se(chown(p, 0, 0) >= 0);
252 assert_se(chase_symlinks(q, NULL, CHASE_SAFE, NULL) >= 0);
255 p = strjoina(temp, "/machine-id-test");
256 assert_se(symlink("/usr/../etc/./machine-id", p) >= 0);
258 pfd = chase_symlinks(p, NULL, CHASE_OPEN, NULL);
259 if (pfd != -ENOENT) {
260 _cleanup_close_ int fd = -1;
265 fd = fd_reopen(pfd, O_RDONLY|O_CLOEXEC);
269 assert_se(id128_read_fd(fd, ID128_PLAIN, &a) >= 0);
270 assert_se(sd_id128_get_machine(&b) >= 0);
271 assert_se(sd_id128_equal(a, b));
276 p = strjoina(temp, "/start");
277 r = chase_symlinks(p, NULL, CHASE_STEP, &result);
279 p = strjoina(temp, "/top/dot/dotdota");
280 assert_se(streq(p, result));
281 result = mfree(result);
283 r = chase_symlinks(p, NULL, CHASE_STEP, &result);
285 p = strjoina(temp, "/top/./dotdota");
286 assert_se(streq(p, result));
287 result = mfree(result);
289 r = chase_symlinks(p, NULL, CHASE_STEP, &result);
291 p = strjoina(temp, "/top/../a");
292 assert_se(streq(p, result));
293 result = mfree(result);
295 r = chase_symlinks(p, NULL, CHASE_STEP, &result);
297 p = strjoina(temp, "/a");
298 assert_se(streq(p, result));
299 result = mfree(result);
301 r = chase_symlinks(p, NULL, CHASE_STEP, &result);
303 p = strjoina(temp, "/b");
304 assert_se(streq(p, result));
305 result = mfree(result);
307 r = chase_symlinks(p, NULL, CHASE_STEP, &result);
309 assert_se(streq("/usr", result));
310 result = mfree(result);
312 r = chase_symlinks("/usr", NULL, CHASE_STEP, &result);
314 assert_se(streq("/usr", result));
315 result = mfree(result);
317 assert_se(rm_rf(temp, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
320 static void test_unlink_noerrno(void) {
321 char name[] = "/tmp/test-close_nointr.XXXXXX";
324 fd = mkostemp_safe(name);
326 assert_se(close_nointr(fd) >= 0);
331 assert_se(unlink_noerrno(name) >= 0);
332 assert_se(errno == -42);
333 assert_se(unlink_noerrno(name) < 0);
334 assert_se(errno == -42);
338 static void test_readlink_and_make_absolute(void) {
339 char tempdir[] = "/tmp/test-readlink_and_make_absolute";
340 char name[] = "/tmp/test-readlink_and_make_absolute/original";
341 char name2[] = "test-readlink_and_make_absolute/original";
342 char name_alias[] = "/tmp/test-readlink_and_make_absolute-alias";
344 _cleanup_free_ char *pwd = NULL;
346 assert_se(mkdir_safe(tempdir, 0755, getuid(), getgid(), MKDIR_WARN_MODE) >= 0);
347 assert_se(touch(name) >= 0);
349 assert_se(symlink(name, name_alias) >= 0);
350 assert_se(readlink_and_make_absolute(name_alias, &r) >= 0);
351 assert_se(streq(r, name));
353 assert_se(unlink(name_alias) >= 0);
355 assert_se(safe_getcwd(&pwd) >= 0);
357 assert_se(chdir(tempdir) >= 0);
358 assert_se(symlink(name2, name_alias) >= 0);
359 assert_se(readlink_and_make_absolute(name_alias, &r) >= 0);
360 assert_se(streq(r, name));
362 assert_se(unlink(name_alias) >= 0);
364 assert_se(chdir(pwd) >= 0);
366 assert_se(rm_rf(tempdir, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
369 static void test_get_files_in_directory(void) {
370 _cleanup_strv_free_ char **l = NULL, **t = NULL;
372 assert_se(get_files_in_directory("/tmp", &l) >= 0);
373 assert_se(get_files_in_directory(".", &t) >= 0);
374 assert_se(get_files_in_directory(".", NULL) >= 0);
377 #if 0 /// UNNEEDED by elogind
378 static void test_var_tmp(void) {
379 _cleanup_free_ char *tmpdir_backup = NULL, *temp_backup = NULL, *tmp_backup = NULL;
380 const char *tmp_dir = NULL, *t;
382 t = getenv("TMPDIR");
384 tmpdir_backup = strdup(t);
385 assert_se(tmpdir_backup);
390 temp_backup = strdup(t);
391 assert_se(temp_backup);
396 tmp_backup = strdup(t);
397 assert_se(tmp_backup);
400 assert_se(unsetenv("TMPDIR") >= 0);
401 assert_se(unsetenv("TEMP") >= 0);
402 assert_se(unsetenv("TMP") >= 0);
404 assert_se(var_tmp_dir(&tmp_dir) >= 0);
405 assert_se(streq(tmp_dir, "/var/tmp"));
407 assert_se(setenv("TMPDIR", "/tmp", true) >= 0);
408 assert_se(streq(getenv("TMPDIR"), "/tmp"));
410 assert_se(var_tmp_dir(&tmp_dir) >= 0);
411 assert_se(streq(tmp_dir, "/tmp"));
413 assert_se(setenv("TMPDIR", "/88_does_not_exist_88", true) >= 0);
414 assert_se(streq(getenv("TMPDIR"), "/88_does_not_exist_88"));
416 assert_se(var_tmp_dir(&tmp_dir) >= 0);
417 assert_se(streq(tmp_dir, "/var/tmp"));
420 assert_se(setenv("TMPDIR", tmpdir_backup, true) >= 0);
421 assert_se(streq(getenv("TMPDIR"), tmpdir_backup));
425 assert_se(setenv("TEMP", temp_backup, true) >= 0);
426 assert_se(streq(getenv("TEMP"), temp_backup));
430 assert_se(setenv("TMP", tmp_backup, true) >= 0);
431 assert_se(streq(getenv("TMP"), tmp_backup));
436 static void test_dot_or_dot_dot(void) {
437 assert_se(!dot_or_dot_dot(NULL));
438 assert_se(!dot_or_dot_dot(""));
439 assert_se(!dot_or_dot_dot("xxx"));
440 assert_se(dot_or_dot_dot("."));
441 assert_se(dot_or_dot_dot(".."));
442 assert_se(!dot_or_dot_dot(".foo"));
443 assert_se(!dot_or_dot_dot("..foo"));
446 #if 0 /// Uses functions that elogind does not need
447 static void test_access_fd(void) {
448 _cleanup_(rmdir_and_freep) char *p = NULL;
449 _cleanup_close_ int fd = -1;
451 assert_se(mkdtemp_malloc("/tmp/access-fd.XXXXXX", &p) >= 0);
453 fd = open(p, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
456 assert_se(access_fd(fd, R_OK) >= 0);
457 assert_se(access_fd(fd, F_OK) >= 0);
458 assert_se(access_fd(fd, W_OK) >= 0);
460 assert_se(fchmod(fd, 0000) >= 0);
462 assert_se(access_fd(fd, F_OK) >= 0);
464 if (geteuid() == 0) {
465 assert_se(access_fd(fd, R_OK) >= 0);
466 assert_se(access_fd(fd, W_OK) >= 0);
468 assert_se(access_fd(fd, R_OK) == -EACCES);
469 assert_se(access_fd(fd, W_OK) == -EACCES);
473 static void test_touch_file(void) {
474 uid_t test_uid, test_gid;
475 _cleanup_(rm_rf_physical_and_freep) char *p = NULL;
480 test_uid = geteuid() == 0 ? 65534 : getuid();
481 test_gid = geteuid() == 0 ? 65534 : getgid();
483 test_mtime = usec_sub_unsigned(now(CLOCK_REALTIME), USEC_PER_WEEK);
485 assert_se(mkdtemp_malloc("/dev/shm/touch-file-XXXXXX", &p) >= 0);
487 a = strjoina(p, "/regular");
488 assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0);
489 assert_se(lstat(a, &st) >= 0);
490 assert_se(st.st_uid == test_uid);
491 assert_se(st.st_gid == test_gid);
492 assert_se(S_ISREG(st.st_mode));
493 assert_se((st.st_mode & 0777) == 0640);
494 assert_se(timespec_load(&st.st_mtim) == test_mtime);
496 a = strjoina(p, "/dir");
497 assert_se(mkdir(a, 0775) >= 0);
498 assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0);
499 assert_se(lstat(a, &st) >= 0);
500 assert_se(st.st_uid == test_uid);
501 assert_se(st.st_gid == test_gid);
502 assert_se(S_ISDIR(st.st_mode));
503 assert_se((st.st_mode & 0777) == 0640);
504 assert_se(timespec_load(&st.st_mtim) == test_mtime);
506 a = strjoina(p, "/fifo");
507 assert_se(mkfifo(a, 0775) >= 0);
508 assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0);
509 assert_se(lstat(a, &st) >= 0);
510 assert_se(st.st_uid == test_uid);
511 assert_se(st.st_gid == test_gid);
512 assert_se(S_ISFIFO(st.st_mode));
513 assert_se((st.st_mode & 0777) == 0640);
514 assert_se(timespec_load(&st.st_mtim) == test_mtime);
516 a = strjoina(p, "/sock");
517 assert_se(mknod(a, 0775 | S_IFSOCK, 0) >= 0);
518 assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0);
519 assert_se(lstat(a, &st) >= 0);
520 assert_se(st.st_uid == test_uid);
521 assert_se(st.st_gid == test_gid);
522 assert_se(S_ISSOCK(st.st_mode));
523 assert_se((st.st_mode & 0777) == 0640);
524 assert_se(timespec_load(&st.st_mtim) == test_mtime);
526 if (geteuid() == 0) {
527 a = strjoina(p, "/cdev");
528 assert_se(mknod(a, 0775 | S_IFCHR, makedev(0, 0)) >= 0);
529 assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0);
530 assert_se(lstat(a, &st) >= 0);
531 assert_se(st.st_uid == test_uid);
532 assert_se(st.st_gid == test_gid);
533 assert_se(S_ISCHR(st.st_mode));
534 assert_se((st.st_mode & 0777) == 0640);
535 assert_se(timespec_load(&st.st_mtim) == test_mtime);
537 a = strjoina(p, "/bdev");
538 assert_se(mknod(a, 0775 | S_IFBLK, makedev(0, 0)) >= 0);
539 assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0);
540 assert_se(lstat(a, &st) >= 0);
541 assert_se(st.st_uid == test_uid);
542 assert_se(st.st_gid == test_gid);
543 assert_se(S_ISBLK(st.st_mode));
544 assert_se((st.st_mode & 0777) == 0640);
545 assert_se(timespec_load(&st.st_mtim) == test_mtime);
548 a = strjoina(p, "/lnk");
549 assert_se(symlink("target", a) >= 0);
550 assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0);
551 assert_se(lstat(a, &st) >= 0);
552 assert_se(st.st_uid == test_uid);
553 assert_se(st.st_gid == test_gid);
554 assert_se(S_ISLNK(st.st_mode));
555 assert_se((st.st_mode & 0777) == 0640);
556 assert_se(timespec_load(&st.st_mtim) == test_mtime);
559 static void test_unlinkat_deallocate(void) {
560 _cleanup_free_ char *p = NULL;
561 _cleanup_close_ int fd = -1;
564 assert_se(tempfn_random_child(NULL, "unlink-deallocation", &p) >= 0);
566 fd = open(p, O_WRONLY|O_CLOEXEC|O_CREAT|O_EXCL, 0600);
569 assert_se(write(fd, "hallo\n", 6) == 6);
571 assert_se(fstat(fd, &st) >= 0);
572 assert_se(st.st_size == 6);
573 assert_se(st.st_blocks > 0);
574 assert_se(st.st_nlink == 1);
576 assert_se(unlinkat_deallocate(AT_FDCWD, p, 0) >= 0);
578 assert_se(fstat(fd, &st) >= 0);
579 assert_se(IN_SET(st.st_size, 0, 6)); /* depending on whether hole punching worked the size will be 6 (it worked) or 0 (we had to resort to truncation) */
580 assert_se(st.st_blocks == 0);
581 assert_se(st.st_nlink == 0);
585 static void test_fsync_directory_of_file(void) {
586 _cleanup_close_ int fd = -1;
588 fd = open_tmpfile_unlinkable(NULL, O_RDWR);
591 assert_se(fsync_directory_of_file(fd) >= 0);
594 int main(int argc, char *argv[]) {
595 test_unlink_noerrno();
596 test_get_files_in_directory();
597 test_readlink_and_make_absolute();
598 #if 0 /// UNNEEDED by elogind
601 test_chase_symlinks();
602 test_dot_or_dot_dot();
603 #if 0 /// Uses functions that elogind does not need
606 test_unlinkat_deallocate();
608 test_fsync_directory_of_file();