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));
274 assert_se(rm_rf(temp, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
277 static void test_unlink_noerrno(void) {
278 char name[] = "/tmp/test-close_nointr.XXXXXX";
281 fd = mkostemp_safe(name);
283 assert_se(close_nointr(fd) >= 0);
288 assert_se(unlink_noerrno(name) >= 0);
289 assert_se(errno == -42);
290 assert_se(unlink_noerrno(name) < 0);
291 assert_se(errno == -42);
295 static void test_readlink_and_make_absolute(void) {
296 char tempdir[] = "/tmp/test-readlink_and_make_absolute";
297 char name[] = "/tmp/test-readlink_and_make_absolute/original";
298 char name2[] = "test-readlink_and_make_absolute/original";
299 char name_alias[] = "/tmp/test-readlink_and_make_absolute-alias";
301 _cleanup_free_ char *pwd = NULL;
303 assert_se(mkdir_safe(tempdir, 0755, getuid(), getgid(), MKDIR_WARN_MODE) >= 0);
304 assert_se(touch(name) >= 0);
306 assert_se(symlink(name, name_alias) >= 0);
307 assert_se(readlink_and_make_absolute(name_alias, &r) >= 0);
308 assert_se(streq(r, name));
310 assert_se(unlink(name_alias) >= 0);
312 assert_se(safe_getcwd(&pwd) >= 0);
314 assert_se(chdir(tempdir) >= 0);
315 assert_se(symlink(name2, name_alias) >= 0);
316 assert_se(readlink_and_make_absolute(name_alias, &r) >= 0);
317 assert_se(streq(r, name));
319 assert_se(unlink(name_alias) >= 0);
321 assert_se(chdir(pwd) >= 0);
323 assert_se(rm_rf(tempdir, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
326 static void test_get_files_in_directory(void) {
327 _cleanup_strv_free_ char **l = NULL, **t = NULL;
329 assert_se(get_files_in_directory("/tmp", &l) >= 0);
330 assert_se(get_files_in_directory(".", &t) >= 0);
331 assert_se(get_files_in_directory(".", NULL) >= 0);
334 #if 0 /// UNNEEDED by elogind
335 static void test_var_tmp(void) {
336 _cleanup_free_ char *tmpdir_backup = NULL, *temp_backup = NULL, *tmp_backup = NULL;
337 const char *tmp_dir = NULL, *t;
339 t = getenv("TMPDIR");
341 tmpdir_backup = strdup(t);
342 assert_se(tmpdir_backup);
347 temp_backup = strdup(t);
348 assert_se(temp_backup);
353 tmp_backup = strdup(t);
354 assert_se(tmp_backup);
357 assert_se(unsetenv("TMPDIR") >= 0);
358 assert_se(unsetenv("TEMP") >= 0);
359 assert_se(unsetenv("TMP") >= 0);
361 assert_se(var_tmp_dir(&tmp_dir) >= 0);
362 assert_se(streq(tmp_dir, "/var/tmp"));
364 assert_se(setenv("TMPDIR", "/tmp", true) >= 0);
365 assert_se(streq(getenv("TMPDIR"), "/tmp"));
367 assert_se(var_tmp_dir(&tmp_dir) >= 0);
368 assert_se(streq(tmp_dir, "/tmp"));
370 assert_se(setenv("TMPDIR", "/88_does_not_exist_88", true) >= 0);
371 assert_se(streq(getenv("TMPDIR"), "/88_does_not_exist_88"));
373 assert_se(var_tmp_dir(&tmp_dir) >= 0);
374 assert_se(streq(tmp_dir, "/var/tmp"));
377 assert_se(setenv("TMPDIR", tmpdir_backup, true) >= 0);
378 assert_se(streq(getenv("TMPDIR"), tmpdir_backup));
382 assert_se(setenv("TEMP", temp_backup, true) >= 0);
383 assert_se(streq(getenv("TEMP"), temp_backup));
387 assert_se(setenv("TMP", tmp_backup, true) >= 0);
388 assert_se(streq(getenv("TMP"), tmp_backup));
393 static void test_dot_or_dot_dot(void) {
394 assert_se(!dot_or_dot_dot(NULL));
395 assert_se(!dot_or_dot_dot(""));
396 assert_se(!dot_or_dot_dot("xxx"));
397 assert_se(dot_or_dot_dot("."));
398 assert_se(dot_or_dot_dot(".."));
399 assert_se(!dot_or_dot_dot(".foo"));
400 assert_se(!dot_or_dot_dot("..foo"));
403 #if 0 /// Uses functions that elogind does not need
404 static void test_access_fd(void) {
405 _cleanup_(rmdir_and_freep) char *p = NULL;
406 _cleanup_close_ int fd = -1;
408 assert_se(mkdtemp_malloc("/tmp/access-fd.XXXXXX", &p) >= 0);
410 fd = open(p, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
413 assert_se(access_fd(fd, R_OK) >= 0);
414 assert_se(access_fd(fd, F_OK) >= 0);
415 assert_se(access_fd(fd, W_OK) >= 0);
417 assert_se(fchmod(fd, 0000) >= 0);
419 assert_se(access_fd(fd, F_OK) >= 0);
421 if (geteuid() == 0) {
422 assert_se(access_fd(fd, R_OK) >= 0);
423 assert_se(access_fd(fd, W_OK) >= 0);
425 assert_se(access_fd(fd, R_OK) == -EACCES);
426 assert_se(access_fd(fd, W_OK) == -EACCES);
430 static void test_touch_file(void) {
431 uid_t test_uid, test_gid;
432 _cleanup_(rm_rf_physical_and_freep) char *p = NULL;
437 test_uid = geteuid() == 0 ? 65534 : getuid();
438 test_gid = geteuid() == 0 ? 65534 : getgid();
440 test_mtime = usec_sub_unsigned(now(CLOCK_REALTIME), USEC_PER_WEEK);
442 assert_se(mkdtemp_malloc("/dev/shm/touch-file-XXXXXX", &p) >= 0);
444 a = strjoina(p, "/regular");
445 assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0);
446 assert_se(lstat(a, &st) >= 0);
447 assert_se(st.st_uid == test_uid);
448 assert_se(st.st_gid == test_gid);
449 assert_se(S_ISREG(st.st_mode));
450 assert_se((st.st_mode & 0777) == 0640);
451 assert_se(timespec_load(&st.st_mtim) == test_mtime);
453 a = strjoina(p, "/dir");
454 assert_se(mkdir(a, 0775) >= 0);
455 assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0);
456 assert_se(lstat(a, &st) >= 0);
457 assert_se(st.st_uid == test_uid);
458 assert_se(st.st_gid == test_gid);
459 assert_se(S_ISDIR(st.st_mode));
460 assert_se((st.st_mode & 0777) == 0640);
461 assert_se(timespec_load(&st.st_mtim) == test_mtime);
463 a = strjoina(p, "/fifo");
464 assert_se(mkfifo(a, 0775) >= 0);
465 assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0);
466 assert_se(lstat(a, &st) >= 0);
467 assert_se(st.st_uid == test_uid);
468 assert_se(st.st_gid == test_gid);
469 assert_se(S_ISFIFO(st.st_mode));
470 assert_se((st.st_mode & 0777) == 0640);
471 assert_se(timespec_load(&st.st_mtim) == test_mtime);
473 a = strjoina(p, "/sock");
474 assert_se(mknod(a, 0775 | S_IFSOCK, 0) >= 0);
475 assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0);
476 assert_se(lstat(a, &st) >= 0);
477 assert_se(st.st_uid == test_uid);
478 assert_se(st.st_gid == test_gid);
479 assert_se(S_ISSOCK(st.st_mode));
480 assert_se((st.st_mode & 0777) == 0640);
481 assert_se(timespec_load(&st.st_mtim) == test_mtime);
483 if (geteuid() == 0) {
484 a = strjoina(p, "/cdev");
485 assert_se(mknod(a, 0775 | S_IFCHR, makedev(0, 0)) >= 0);
486 assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0);
487 assert_se(lstat(a, &st) >= 0);
488 assert_se(st.st_uid == test_uid);
489 assert_se(st.st_gid == test_gid);
490 assert_se(S_ISCHR(st.st_mode));
491 assert_se((st.st_mode & 0777) == 0640);
492 assert_se(timespec_load(&st.st_mtim) == test_mtime);
494 a = strjoina(p, "/bdev");
495 assert_se(mknod(a, 0775 | S_IFBLK, makedev(0, 0)) >= 0);
496 assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0);
497 assert_se(lstat(a, &st) >= 0);
498 assert_se(st.st_uid == test_uid);
499 assert_se(st.st_gid == test_gid);
500 assert_se(S_ISBLK(st.st_mode));
501 assert_se((st.st_mode & 0777) == 0640);
502 assert_se(timespec_load(&st.st_mtim) == test_mtime);
505 a = strjoina(p, "/lnk");
506 assert_se(symlink("target", a) >= 0);
507 assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0);
508 assert_se(lstat(a, &st) >= 0);
509 assert_se(st.st_uid == test_uid);
510 assert_se(st.st_gid == test_gid);
511 assert_se(S_ISLNK(st.st_mode));
512 assert_se((st.st_mode & 0777) == 0640);
513 assert_se(timespec_load(&st.st_mtim) == test_mtime);
516 static void test_unlinkat_deallocate(void) {
517 _cleanup_free_ char *p = NULL;
518 _cleanup_close_ int fd = -1;
521 assert_se(tempfn_random_child(NULL, "unlink-deallocation", &p) >= 0);
523 fd = open(p, O_WRONLY|O_CLOEXEC|O_CREAT|O_EXCL, 0600);
526 assert_se(write(fd, "hallo\n", 6) == 6);
528 assert_se(fstat(fd, &st) >= 0);
529 assert_se(st.st_size == 6);
530 assert_se(st.st_blocks > 0);
531 assert_se(st.st_nlink == 1);
533 assert_se(unlinkat_deallocate(AT_FDCWD, p, 0) >= 0);
535 assert_se(fstat(fd, &st) >= 0);
536 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) */
537 assert_se(st.st_blocks == 0);
538 assert_se(st.st_nlink == 0);
542 static void test_fsync_directory_of_file(void) {
543 _cleanup_close_ int fd = -1;
545 fd = open_tmpfile_unlinkable(NULL, O_RDWR);
548 assert_se(fsync_directory_of_file(fd) >= 0);
551 int main(int argc, char *argv[]) {
552 test_unlink_noerrno();
553 test_get_files_in_directory();
554 test_readlink_and_make_absolute();
555 #if 0 /// UNNEEDED by elogind
558 test_chase_symlinks();
559 test_dot_or_dot_dot();
560 #if 0 /// Uses functions that elogind does not need
563 test_unlinkat_deallocate();
565 test_fsync_directory_of_file();