1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2010 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
23 #include "alloc-util.h"
29 #include "path-util.h"
31 #include "string-util.h"
35 static void test_chase_symlinks(void) {
36 _cleanup_free_ char *result = NULL;
37 char temp[] = "/tmp/test-chase.XXXXXX";
38 const char *top, *p, *pslash, *q, *qslash;
41 assert_se(mkdtemp(temp));
43 top = strjoina(temp, "/top");
44 assert_se(mkdir(top, 0700) >= 0);
46 p = strjoina(top, "/dot");
47 assert_se(symlink(".", p) >= 0);
49 p = strjoina(top, "/dotdot");
50 assert_se(symlink("..", p) >= 0);
52 p = strjoina(top, "/dotdota");
53 assert_se(symlink("../a", p) >= 0);
55 p = strjoina(temp, "/a");
56 assert_se(symlink("b", p) >= 0);
58 p = strjoina(temp, "/b");
59 assert_se(symlink("/usr", p) >= 0);
61 p = strjoina(temp, "/start");
62 assert_se(symlink("top/dot/dotdota", p) >= 0);
64 /* Paths that use symlinks underneath the "root" */
66 r = chase_symlinks(p, NULL, 0, &result);
68 assert_se(path_equal(result, "/usr"));
69 result = mfree(result);
71 pslash = strjoina(p, "/");
72 r = chase_symlinks(pslash, NULL, 0, &result);
74 assert_se(path_equal(result, "/usr/"));
75 result = mfree(result);
77 r = chase_symlinks(p, temp, 0, &result);
78 assert_se(r == -ENOENT);
80 r = chase_symlinks(pslash, temp, 0, &result);
81 assert_se(r == -ENOENT);
83 q = strjoina(temp, "/usr");
85 r = chase_symlinks(p, temp, CHASE_NONEXISTENT, &result);
87 assert_se(path_equal(result, q));
88 result = mfree(result);
90 qslash = strjoina(q, "/");
92 r = chase_symlinks(pslash, temp, CHASE_NONEXISTENT, &result);
94 assert_se(path_equal(result, qslash));
95 result = mfree(result);
97 assert_se(mkdir(q, 0700) >= 0);
99 r = chase_symlinks(p, temp, 0, &result);
101 assert_se(path_equal(result, q));
102 result = mfree(result);
104 r = chase_symlinks(pslash, temp, 0, &result);
106 assert_se(path_equal(result, qslash));
107 result = mfree(result);
109 p = strjoina(temp, "/slash");
110 assert_se(symlink("/", p) >= 0);
112 r = chase_symlinks(p, NULL, 0, &result);
114 assert_se(path_equal(result, "/"));
115 result = mfree(result);
117 r = chase_symlinks(p, temp, 0, &result);
119 assert_se(path_equal(result, temp));
120 result = mfree(result);
122 /* Paths that would "escape" outside of the "root" */
124 p = strjoina(temp, "/6dots");
125 assert_se(symlink("../../..", p) >= 0);
127 r = chase_symlinks(p, temp, 0, &result);
128 assert_se(r > 0 && path_equal(result, temp));
129 result = mfree(result);
131 p = strjoina(temp, "/6dotsusr");
132 assert_se(symlink("../../../usr", p) >= 0);
134 r = chase_symlinks(p, temp, 0, &result);
135 assert_se(r > 0 && path_equal(result, q));
136 result = mfree(result);
138 p = strjoina(temp, "/top/8dotsusr");
139 assert_se(symlink("../../../../usr", p) >= 0);
141 r = chase_symlinks(p, temp, 0, &result);
142 assert_se(r > 0 && path_equal(result, q));
143 result = mfree(result);
145 /* Paths that contain repeated slashes */
147 p = strjoina(temp, "/slashslash");
148 assert_se(symlink("///usr///", p) >= 0);
150 r = chase_symlinks(p, NULL, 0, &result);
152 assert_se(path_equal(result, "/usr"));
153 result = mfree(result);
155 r = chase_symlinks(p, temp, 0, &result);
157 assert_se(path_equal(result, q));
158 result = mfree(result);
162 r = chase_symlinks("/etc/./.././", NULL, 0, &result);
164 assert_se(path_equal(result, "/"));
165 result = mfree(result);
167 r = chase_symlinks("/etc/./.././", "/etc", 0, &result);
168 assert_se(r > 0 && path_equal(result, "/etc"));
169 result = mfree(result);
171 r = chase_symlinks("/../.././//../../etc", NULL, 0, &result);
173 assert_se(streq(result, "/etc"));
174 result = mfree(result);
176 r = chase_symlinks("/../.././//../../test-chase.fsldajfl", NULL, CHASE_NONEXISTENT, &result);
178 assert_se(streq(result, "/test-chase.fsldajfl"));
179 result = mfree(result);
181 r = chase_symlinks("/../.././//../../etc", "/", CHASE_PREFIX_ROOT, &result);
183 assert_se(streq(result, "/etc"));
184 result = mfree(result);
186 r = chase_symlinks("/../.././//../../test-chase.fsldajfl", "/", CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &result);
188 assert_se(streq(result, "/test-chase.fsldajfl"));
189 result = mfree(result);
191 r = chase_symlinks("/etc/machine-id/foo", NULL, 0, &result);
192 assert_se(r == -ENOTDIR);
193 result = mfree(result);
195 /* Path that loops back to self */
197 p = strjoina(temp, "/recursive-symlink");
198 assert_se(symlink("recursive-symlink", p) >= 0);
199 r = chase_symlinks(p, NULL, 0, &result);
200 assert_se(r == -ELOOP);
202 /* Path which doesn't exist */
204 p = strjoina(temp, "/idontexist");
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 p = strjoina(temp, "/idontexist/meneither");
214 r = chase_symlinks(p, NULL, 0, &result);
215 assert_se(r == -ENOENT);
217 r = chase_symlinks(p, NULL, CHASE_NONEXISTENT, &result);
219 assert_se(path_equal(result, p));
220 result = mfree(result);
222 /* Path which doesn't exist, but contains weird stuff */
224 p = strjoina(temp, "/idontexist/..");
225 r = chase_symlinks(p, NULL, 0, &result);
226 assert_se(r == -ENOENT);
228 r = chase_symlinks(p, NULL, CHASE_NONEXISTENT, &result);
229 assert_se(r == -ENOENT);
231 p = strjoina(temp, "/target");
232 q = strjoina(temp, "/top");
233 assert_se(symlink(q, p) >= 0);
234 p = strjoina(temp, "/target/idontexist");
235 r = chase_symlinks(p, NULL, 0, &result);
236 assert_se(r == -ENOENT);
238 assert_se(rm_rf(temp, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
241 static void test_unlink_noerrno(void) {
242 char name[] = "/tmp/test-close_nointr.XXXXXX";
245 fd = mkostemp_safe(name);
247 assert_se(close_nointr(fd) >= 0);
252 assert_se(unlink_noerrno(name) >= 0);
253 assert_se(errno == -42);
254 assert_se(unlink_noerrno(name) < 0);
255 assert_se(errno == -42);
259 static void test_readlink_and_make_absolute(void) {
260 char tempdir[] = "/tmp/test-readlink_and_make_absolute";
261 char name[] = "/tmp/test-readlink_and_make_absolute/original";
262 char name2[] = "test-readlink_and_make_absolute/original";
263 char name_alias[] = "/tmp/test-readlink_and_make_absolute-alias";
265 _cleanup_free_ char *pwd = NULL;
267 assert_se(mkdir_safe(tempdir, 0755, getuid(), getgid(), false) >= 0);
268 assert_se(touch(name) >= 0);
270 assert_se(symlink(name, name_alias) >= 0);
271 assert_se(readlink_and_make_absolute(name_alias, &r) >= 0);
272 assert_se(streq(r, name));
274 assert_se(unlink(name_alias) >= 0);
276 assert_se(pwd = get_current_dir_name());
278 assert_se(chdir(tempdir) >= 0);
279 assert_se(symlink(name2, name_alias) >= 0);
280 assert_se(readlink_and_make_absolute(name_alias, &r) >= 0);
281 assert_se(streq(r, name));
283 assert_se(unlink(name_alias) >= 0);
285 assert_se(chdir(pwd) >= 0);
287 assert_se(rm_rf(tempdir, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
290 static void test_get_files_in_directory(void) {
291 _cleanup_strv_free_ char **l = NULL, **t = NULL;
293 assert_se(get_files_in_directory("/tmp", &l) >= 0);
294 assert_se(get_files_in_directory(".", &t) >= 0);
295 assert_se(get_files_in_directory(".", NULL) >= 0);
298 #if 0 /// UNNEEDED by elogind
299 static void test_var_tmp(void) {
300 _cleanup_free_ char *tmpdir_backup = NULL, *temp_backup = NULL, *tmp_backup = NULL;
301 const char *tmp_dir = NULL, *t;
303 t = getenv("TMPDIR");
305 tmpdir_backup = strdup(t);
306 assert_se(tmpdir_backup);
311 temp_backup = strdup(t);
312 assert_se(temp_backup);
317 tmp_backup = strdup(t);
318 assert_se(tmp_backup);
321 assert_se(unsetenv("TMPDIR") >= 0);
322 assert_se(unsetenv("TEMP") >= 0);
323 assert_se(unsetenv("TMP") >= 0);
325 assert_se(var_tmp_dir(&tmp_dir) >= 0);
326 assert_se(streq(tmp_dir, "/var/tmp"));
328 assert_se(setenv("TMPDIR", "/tmp", true) >= 0);
329 assert_se(streq(getenv("TMPDIR"), "/tmp"));
331 assert_se(var_tmp_dir(&tmp_dir) >= 0);
332 assert_se(streq(tmp_dir, "/tmp"));
334 assert_se(setenv("TMPDIR", "/88_does_not_exist_88", true) >= 0);
335 assert_se(streq(getenv("TMPDIR"), "/88_does_not_exist_88"));
337 assert_se(var_tmp_dir(&tmp_dir) >= 0);
338 assert_se(streq(tmp_dir, "/var/tmp"));
341 assert_se(setenv("TMPDIR", tmpdir_backup, true) >= 0);
342 assert_se(streq(getenv("TMPDIR"), tmpdir_backup));
346 assert_se(setenv("TEMP", temp_backup, true) >= 0);
347 assert_se(streq(getenv("TEMP"), temp_backup));
351 assert_se(setenv("TMP", tmp_backup, true) >= 0);
352 assert_se(streq(getenv("TMP"), tmp_backup));
357 static void test_dot_or_dot_dot(void) {
358 assert_se(!dot_or_dot_dot(NULL));
359 assert_se(!dot_or_dot_dot(""));
360 assert_se(!dot_or_dot_dot("xxx"));
361 assert_se(dot_or_dot_dot("."));
362 assert_se(dot_or_dot_dot(".."));
363 assert_se(!dot_or_dot_dot(".foo"));
364 assert_se(!dot_or_dot_dot("..foo"));
367 #if 0 /// Uses functions that elogind does not need
368 static void test_access_fd(void) {
369 _cleanup_(rmdir_and_freep) char *p = NULL;
370 _cleanup_close_ int fd = -1;
372 assert_se(mkdtemp_malloc("/tmp/access-fd.XXXXXX", &p) >= 0);
374 fd = open(p, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
377 assert_se(access_fd(fd, R_OK) >= 0);
378 assert_se(access_fd(fd, F_OK) >= 0);
379 assert_se(access_fd(fd, W_OK) >= 0);
381 assert_se(fchmod(fd, 0000) >= 0);
383 assert_se(access_fd(fd, F_OK) >= 0);
385 if (geteuid() == 0) {
386 assert_se(access_fd(fd, R_OK) >= 0);
387 assert_se(access_fd(fd, W_OK) >= 0);
389 assert_se(access_fd(fd, R_OK) == -EACCES);
390 assert_se(access_fd(fd, W_OK) == -EACCES);
395 static void test_touch_file(void) {
396 uid_t test_uid, test_gid;
397 _cleanup_(rm_rf_physical_and_freep) char *p = NULL;
402 test_uid = geteuid() == 0 ? 65534 : getuid();
403 test_gid = geteuid() == 0 ? 65534 : getgid();
405 test_mtime = usec_sub_unsigned(now(CLOCK_REALTIME), USEC_PER_WEEK);
407 assert_se(mkdtemp_malloc("/dev/shm/touch-file-XXXXXX", &p) >= 0);
409 a = strjoina(p, "/regular");
410 assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0);
411 assert_se(lstat(a, &st) >= 0);
412 assert_se(st.st_uid == test_uid);
413 assert_se(st.st_gid == test_gid);
414 assert_se(S_ISREG(st.st_mode));
415 assert_se((st.st_mode & 0777) == 0640);
416 assert_se(timespec_load(&st.st_mtim) == test_mtime);
418 a = strjoina(p, "/dir");
419 assert_se(mkdir(a, 0775) >= 0);
420 assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0);
421 assert_se(lstat(a, &st) >= 0);
422 assert_se(st.st_uid == test_uid);
423 assert_se(st.st_gid == test_gid);
424 assert_se(S_ISDIR(st.st_mode));
425 assert_se((st.st_mode & 0777) == 0640);
426 assert_se(timespec_load(&st.st_mtim) == test_mtime);
428 a = strjoina(p, "/fifo");
429 assert_se(mkfifo(a, 0775) >= 0);
430 assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0);
431 assert_se(lstat(a, &st) >= 0);
432 assert_se(st.st_uid == test_uid);
433 assert_se(st.st_gid == test_gid);
434 assert_se(S_ISFIFO(st.st_mode));
435 assert_se((st.st_mode & 0777) == 0640);
436 assert_se(timespec_load(&st.st_mtim) == test_mtime);
438 a = strjoina(p, "/sock");
439 assert_se(mknod(a, 0775 | S_IFSOCK, 0) >= 0);
440 assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0);
441 assert_se(lstat(a, &st) >= 0);
442 assert_se(st.st_uid == test_uid);
443 assert_se(st.st_gid == test_gid);
444 assert_se(S_ISSOCK(st.st_mode));
445 assert_se((st.st_mode & 0777) == 0640);
446 assert_se(timespec_load(&st.st_mtim) == test_mtime);
448 if (geteuid() == 0) {
449 a = strjoina(p, "/cdev");
450 assert_se(mknod(a, 0775 | S_IFCHR, makedev(0, 0)) >= 0);
451 assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0);
452 assert_se(lstat(a, &st) >= 0);
453 assert_se(st.st_uid == test_uid);
454 assert_se(st.st_gid == test_gid);
455 assert_se(S_ISCHR(st.st_mode));
456 assert_se((st.st_mode & 0777) == 0640);
457 assert_se(timespec_load(&st.st_mtim) == test_mtime);
459 a = strjoina(p, "/bdev");
460 assert_se(mknod(a, 0775 | S_IFBLK, makedev(0, 0)) >= 0);
461 assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0);
462 assert_se(lstat(a, &st) >= 0);
463 assert_se(st.st_uid == test_uid);
464 assert_se(st.st_gid == test_gid);
465 assert_se(S_ISBLK(st.st_mode));
466 assert_se((st.st_mode & 0777) == 0640);
467 assert_se(timespec_load(&st.st_mtim) == test_mtime);
470 a = strjoina(p, "/lnk");
471 assert_se(symlink("target", a) >= 0);
472 assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0);
473 assert_se(lstat(a, &st) >= 0);
474 assert_se(st.st_uid == test_uid);
475 assert_se(st.st_gid == test_gid);
476 assert_se(S_ISLNK(st.st_mode));
477 assert_se((st.st_mode & 0777) == 0640);
478 assert_se(timespec_load(&st.st_mtim) == test_mtime);
481 int main(int argc, char *argv[]) {
482 test_unlink_noerrno();
483 test_get_files_in_directory();
484 test_readlink_and_make_absolute();
485 #if 0 /// UNNEEDED by elogind
488 test_chase_symlinks();
489 test_dot_or_dot_dot();
490 #if 0 /// Uses functions that elogind does not need