1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2013 Zbigniew Jędrzejewski-Szmek
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/>.
24 #include "alloc-util.h"
27 #include "mount-util.h"
28 #include "path-util.h"
30 #include "stat-util.h"
31 #include "string-util.h"
35 #define test_path_compare(a, b, result) { \
36 assert_se(path_compare(a, b) == result); \
37 assert_se(path_compare(b, a) == -result); \
38 assert_se(path_equal(a, b) == !result); \
39 assert_se(path_equal(b, a) == !result); \
42 static void test_path(void) {
43 _cleanup_close_ int fd = -1;
45 test_path_compare("/goo", "/goo", 0);
46 test_path_compare("/goo", "/goo", 0);
47 test_path_compare("//goo", "/goo", 0);
48 test_path_compare("//goo/////", "/goo", 0);
49 test_path_compare("goo/////", "goo", 0);
51 test_path_compare("/goo/boo", "/goo//boo", 0);
52 test_path_compare("//goo/boo", "/goo/boo//", 0);
54 test_path_compare("/", "///", 0);
56 test_path_compare("/x", "x/", 1);
57 test_path_compare("x/", "/", -1);
59 test_path_compare("/x/./y", "x/y", 1);
60 test_path_compare("x/.y", "x/y", -1);
62 test_path_compare("foo", "/foo", -1);
63 test_path_compare("/foo", "/foo/bar", -1);
64 test_path_compare("/foo/aaa", "/foo/b", -1);
65 test_path_compare("/foo/aaa", "/foo/b/a", -1);
66 test_path_compare("/foo/a", "/foo/aaa", -1);
67 test_path_compare("/foo/a/b", "/foo/aaa", -1);
69 assert_se(path_is_absolute("/"));
70 assert_se(!path_is_absolute("./"));
72 assert_se(is_path("/dir"));
73 assert_se(is_path("a/b"));
74 assert_se(!is_path("."));
76 assert_se(streq(basename("./aa/bb/../file.da."), "file.da."));
77 assert_se(streq(basename("/aa///.file"), ".file"));
78 assert_se(streq(basename("/aa///file..."), "file..."));
79 assert_se(streq(basename("file.../"), ""));
81 fd = open("/", O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOCTTY);
83 assert_se(fd_is_mount_point(fd, "/", 0) > 0);
86 char p1[] = "aaa/bbb////ccc";
87 char p2[] = "//aaa/.////ccc";
90 assert_se(path_equal(path_kill_slashes(p1), "aaa/bbb/ccc"));
91 assert_se(path_equal(path_kill_slashes(p2), "/aaa/./ccc"));
92 assert_se(path_equal(path_kill_slashes(p3), "/./"));
95 assert_se(PATH_IN_SET("/bin", "/", "/bin", "/foo"));
96 assert_se(PATH_IN_SET("/bin", "/bin"));
97 assert_se(PATH_IN_SET("/bin", "/foo/bar", "/bin"));
98 assert_se(PATH_IN_SET("/", "/", "/", "/foo/bar"));
99 assert_se(!PATH_IN_SET("/", "/abc", "/def"));
101 assert_se(path_equal_ptr(NULL, NULL));
102 assert_se(path_equal_ptr("/a", "/a"));
103 assert_se(!path_equal_ptr("/a", "/b"));
104 assert_se(!path_equal_ptr("/a", NULL));
105 assert_se(!path_equal_ptr(NULL, "/a"));
108 static void test_path_equal_root(void) {
109 /* Nail down the details of how path_equal("/", ...) works. */
111 assert_se(path_equal("/", "/"));
112 assert_se(path_equal("/", "//"));
114 assert_se(!path_equal("/", "/./"));
115 assert_se(!path_equal("/", "/../"));
117 assert_se(!path_equal("/", "/.../"));
119 /* Make sure that files_same works as expected. */
121 assert_se(files_same("/", "/", 0) > 0);
122 assert_se(files_same("/", "/", AT_SYMLINK_NOFOLLOW) > 0);
123 assert_se(files_same("/", "//", 0) > 0);
124 assert_se(files_same("/", "//", AT_SYMLINK_NOFOLLOW) > 0);
126 assert_se(files_same("/", "/./", 0) > 0);
127 assert_se(files_same("/", "/./", AT_SYMLINK_NOFOLLOW) > 0);
128 assert_se(files_same("/", "/../", 0) > 0);
129 assert_se(files_same("/", "/../", AT_SYMLINK_NOFOLLOW) > 0);
131 assert_se(files_same("/", "/.../", 0) == -ENOENT);
132 assert_se(files_same("/", "/.../", AT_SYMLINK_NOFOLLOW) == -ENOENT);
134 /* The same for path_equal_or_files_same. */
136 assert_se(path_equal_or_files_same("/", "/", 0));
137 assert_se(path_equal_or_files_same("/", "/", AT_SYMLINK_NOFOLLOW));
138 assert_se(path_equal_or_files_same("/", "//", 0));
139 assert_se(path_equal_or_files_same("/", "//", AT_SYMLINK_NOFOLLOW));
141 assert_se(path_equal_or_files_same("/", "/./", 0));
142 assert_se(path_equal_or_files_same("/", "/./", AT_SYMLINK_NOFOLLOW));
143 assert_se(path_equal_or_files_same("/", "/../", 0));
144 assert_se(path_equal_or_files_same("/", "/../", AT_SYMLINK_NOFOLLOW));
146 assert_se(!path_equal_or_files_same("/", "/.../", 0));
147 assert_se(!path_equal_or_files_same("/", "/.../", AT_SYMLINK_NOFOLLOW));
150 static void test_find_binary(const char *self) {
153 assert_se(find_binary("/bin/sh", &p) == 0);
155 assert_se(path_equal(p, "/bin/sh"));
158 assert_se(find_binary(self, &p) == 0);
160 /* libtool might prefix the binary name with "lt-" */
161 assert_se(endswith(p, "/lt-test-path-util") || endswith(p, "/test-path-util"));
162 assert_se(path_is_absolute(p));
165 assert_se(find_binary("sh", &p) == 0);
167 assert_se(endswith(p, "/sh"));
168 assert_se(path_is_absolute(p));
171 assert_se(find_binary("xxxx-xxxx", &p) == -ENOENT);
172 assert_se(find_binary("/some/dir/xxxx-xxxx", &p) == -ENOENT);
175 static void test_prefixes(void) {
176 static const char* values[] = { "/a/b/c/d", "/a/b/c", "/a/b", "/a", "", NULL};
182 PATH_FOREACH_PREFIX_MORE(s, "/a/b/c/d") {
183 log_error("---%s---", s);
184 assert_se(streq(s, values[i++]));
186 assert_se(values[i] == NULL);
189 PATH_FOREACH_PREFIX(s, "/a/b/c/d") {
190 log_error("---%s---", s);
191 assert_se(streq(s, values[i++]));
193 assert_se(values[i] == NULL);
196 PATH_FOREACH_PREFIX_MORE(s, "////a////b////c///d///////")
197 assert_se(streq(s, values[i++]));
198 assert_se(values[i] == NULL);
201 PATH_FOREACH_PREFIX(s, "////a////b////c///d///////")
202 assert_se(streq(s, values[i++]));
203 assert_se(values[i] == NULL);
205 PATH_FOREACH_PREFIX(s, "////")
206 assert_not_reached("Wut?");
209 PATH_FOREACH_PREFIX_MORE(s, "////") {
211 assert_se(streq(s, ""));
216 PATH_FOREACH_PREFIX(s, "")
217 assert_not_reached("wut?");
220 PATH_FOREACH_PREFIX_MORE(s, "") {
222 assert_se(streq(s, ""));
227 static void test_path_join(void) {
229 #define test_join(root, path, rest, expected) { \
230 _cleanup_free_ char *z = NULL; \
231 z = path_join(root, path, rest); \
232 assert_se(streq(z, expected)); \
235 test_join("/root", "/a/b", "/c", "/root/a/b/c");
236 test_join("/root", "a/b", "c", "/root/a/b/c");
237 test_join("/root", "/a/b", "c", "/root/a/b/c");
238 test_join("/root", "/", "c", "/root/c");
239 test_join("/root", "/", NULL, "/root/");
241 test_join(NULL, "/a/b", "/c", "/a/b/c");
242 test_join(NULL, "a/b", "c", "a/b/c");
243 test_join(NULL, "/a/b", "c", "/a/b/c");
244 test_join(NULL, "/", "c", "/c");
245 test_join(NULL, "/", NULL, "/");
248 #if 0 /// UNNEEDED by elogind
249 static void test_fsck_exists(void) {
250 /* Ensure we use a sane default for PATH. */
253 /* fsck.minix is provided by util-linux and will probably exist. */
254 assert_se(fsck_exists("minix") == 1);
256 assert_se(fsck_exists("AbCdE") == 0);
257 assert_se(fsck_exists("/../bin/") == 0);
260 static void test_make_relative(void) {
263 assert_se(path_make_relative("some/relative/path", "/some/path", &result) < 0);
264 assert_se(path_make_relative("/some/path", "some/relative/path", &result) < 0);
265 assert_se(path_make_relative("/some/dotdot/../path", "/some/path", &result) < 0);
267 #define test(from_dir, to_path, expected) { \
268 _cleanup_free_ char *z = NULL; \
269 path_make_relative(from_dir, to_path, &z); \
270 assert_se(streq(z, expected)); \
274 test("/", "/some/path", "some/path");
275 test("/some/path", "/some/path", ".");
276 test("/some/path", "/some/path/in/subdir", "in/subdir");
277 test("/some/path", "/", "../..");
278 test("/some/path", "/some/other/path", "../other/path");
279 test("/some/path/./dot", "/some/further/path", "../../further/path");
280 test("//extra/////slashes///won't////fool///anybody//", "////extra///slashes////are/just///fine///", "../../../are/just/fine");
284 static void test_strv_resolve(void) {
285 char tmp_dir[] = "/tmp/test-path-util-XXXXXX";
286 _cleanup_strv_free_ char **search_dirs = NULL;
287 _cleanup_strv_free_ char **absolute_dirs = NULL;
290 assert_se(mkdtemp(tmp_dir) != NULL);
292 search_dirs = strv_new("/dir1", "/dir2", "/dir3", NULL);
293 assert_se(search_dirs);
294 STRV_FOREACH(d, search_dirs) {
295 char *p = strappend(tmp_dir, *d);
297 assert_se(strv_push(&absolute_dirs, p) == 0);
300 assert_se(mkdir(absolute_dirs[0], 0700) == 0);
301 assert_se(mkdir(absolute_dirs[1], 0700) == 0);
302 assert_se(symlink("dir2", absolute_dirs[2]) == 0);
304 path_strv_resolve(search_dirs, tmp_dir);
305 assert_se(streq(search_dirs[0], "/dir1"));
306 assert_se(streq(search_dirs[1], "/dir2"));
307 assert_se(streq(search_dirs[2], "/dir2"));
309 assert_se(rm_rf(tmp_dir, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
312 static void test_path_startswith(void) {
315 p = path_startswith("/foo/bar/barfoo/", "/foo");
316 assert_se(streq_ptr(p, "bar/barfoo/"));
318 p = path_startswith("/foo/bar/barfoo/", "/foo/");
319 assert_se(streq_ptr(p, "bar/barfoo/"));
321 p = path_startswith("/foo/bar/barfoo/", "/");
322 assert_se(streq_ptr(p, "foo/bar/barfoo/"));
324 p = path_startswith("/foo/bar/barfoo/", "////");
325 assert_se(streq_ptr(p, "foo/bar/barfoo/"));
327 p = path_startswith("/foo/bar/barfoo/", "/foo//bar/////barfoo///");
328 assert_se(streq_ptr(p, ""));
330 p = path_startswith("/foo/bar/barfoo/", "/foo/bar/barfoo////");
331 assert_se(streq_ptr(p, ""));
333 p = path_startswith("/foo/bar/barfoo/", "/foo/bar///barfoo/");
334 assert_se(streq_ptr(p, ""));
336 p = path_startswith("/foo/bar/barfoo/", "/foo////bar/barfoo/");
337 assert_se(streq_ptr(p, ""));
339 p = path_startswith("/foo/bar/barfoo/", "////foo/bar/barfoo/");
340 assert_se(streq_ptr(p, ""));
342 p = path_startswith("/foo/bar/barfoo/", "/foo/bar/barfoo");
343 assert_se(streq_ptr(p, ""));
345 assert_se(!path_startswith("/foo/bar/barfoo/", "/foo/bar/barfooa/"));
346 assert_se(!path_startswith("/foo/bar/barfoo/", "/foo/bar/barfooa"));
347 assert_se(!path_startswith("/foo/bar/barfoo/", ""));
348 assert_se(!path_startswith("/foo/bar/barfoo/", "/bar/foo"));
349 assert_se(!path_startswith("/foo/bar/barfoo/", "/f/b/b/"));
352 static void test_prefix_root_one(const char *r, const char *p, const char *expected) {
353 _cleanup_free_ char *s = NULL;
356 assert_se(s = prefix_root(r, p));
357 assert_se(streq_ptr(s, expected));
359 t = prefix_roota(r, p);
361 assert_se(streq_ptr(t, expected));
364 static void test_prefix_root(void) {
365 test_prefix_root_one("/", "/foo", "/foo");
366 test_prefix_root_one(NULL, "/foo", "/foo");
367 test_prefix_root_one("", "/foo", "/foo");
368 test_prefix_root_one("///", "/foo", "/foo");
369 test_prefix_root_one("/", "////foo", "/foo");
370 test_prefix_root_one(NULL, "////foo", "/foo");
372 test_prefix_root_one("/foo", "/bar", "/foo/bar");
373 test_prefix_root_one("/foo", "bar", "/foo/bar");
374 test_prefix_root_one("foo", "bar", "foo/bar");
375 test_prefix_root_one("/foo/", "/bar", "/foo/bar");
376 test_prefix_root_one("/foo/", "//bar", "/foo/bar");
377 test_prefix_root_one("/foo///", "//bar", "/foo/bar");
380 static void test_file_in_same_dir(void) {
383 t = file_in_same_dir("/", "a");
384 assert_se(streq(t, "/a"));
387 t = file_in_same_dir("/", "/a");
388 assert_se(streq(t, "/a"));
391 t = file_in_same_dir("", "a");
392 assert_se(streq(t, "a"));
395 t = file_in_same_dir("a/", "a");
396 assert_se(streq(t, "a/a"));
399 t = file_in_same_dir("bar/foo", "bar");
400 assert_se(streq(t, "bar/bar"));
404 static void test_last_path_component(void) {
405 assert_se(streq(last_path_component("a/b/c"), "c"));
406 assert_se(streq(last_path_component("a/b/c/"), "c/"));
407 assert_se(streq(last_path_component("/"), "/"));
408 assert_se(streq(last_path_component("//"), "/"));
409 assert_se(streq(last_path_component("///"), "/"));
410 assert_se(streq(last_path_component("."), "."));
411 assert_se(streq(last_path_component("./."), "."));
412 assert_se(streq(last_path_component("././"), "./"));
413 assert_se(streq(last_path_component("././/"), ".//"));
414 assert_se(streq(last_path_component("/foo/a"), "a"));
415 assert_se(streq(last_path_component("/foo/a/"), "a/"));
416 assert_se(streq(last_path_component(""), ""));
419 static void test_filename_is_valid(void) {
420 char foo[FILENAME_MAX+2];
423 assert_se(!filename_is_valid(""));
424 assert_se(!filename_is_valid("/bar/foo"));
425 assert_se(!filename_is_valid("/"));
426 assert_se(!filename_is_valid("."));
427 assert_se(!filename_is_valid(".."));
429 for (i=0; i<FILENAME_MAX+1; i++)
431 foo[FILENAME_MAX+1] = '\0';
433 assert_se(!filename_is_valid(foo));
435 assert_se(filename_is_valid("foo_bar-333"));
436 assert_se(filename_is_valid("o.o"));
439 static void test_hidden_or_backup_file(void) {
440 assert_se(hidden_or_backup_file(".hidden"));
441 assert_se(hidden_or_backup_file("..hidden"));
442 assert_se(!hidden_or_backup_file("hidden."));
444 assert_se(hidden_or_backup_file("backup~"));
445 assert_se(hidden_or_backup_file(".backup~"));
447 assert_se(hidden_or_backup_file("lost+found"));
448 assert_se(hidden_or_backup_file("aquota.user"));
449 assert_se(hidden_or_backup_file("aquota.group"));
451 assert_se(hidden_or_backup_file("test.rpmnew"));
452 assert_se(hidden_or_backup_file("test.dpkg-old"));
453 assert_se(hidden_or_backup_file("test.dpkg-remove"));
454 assert_se(hidden_or_backup_file("test.swp"));
456 assert_se(!hidden_or_backup_file("test.rpmnew."));
457 assert_se(!hidden_or_backup_file("test.dpkg-old.foo"));
460 #if 0 /// UNNEEDED by elogind
461 static void test_systemd_installation_has_version(const char *path) {
463 const unsigned versions[] = {0, 231, atoi(PACKAGE_VERSION), 999};
466 for (i = 0; i < ELEMENTSOF(versions); i++) {
467 r = systemd_installation_has_version(path, versions[i]);
469 log_info("%s has systemd >= %u: %s",
470 path ?: "Current installation", versions[i], yes_no(r));
475 static void test_skip_dev_prefix(void) {
477 assert_se(streq(skip_dev_prefix("/"), "/"));
478 assert_se(streq(skip_dev_prefix("/dev"), ""));
479 assert_se(streq(skip_dev_prefix("/dev/"), ""));
480 assert_se(streq(skip_dev_prefix("/dev/foo"), "foo"));
481 assert_se(streq(skip_dev_prefix("/dev/foo/bar"), "foo/bar"));
482 assert_se(streq(skip_dev_prefix("//dev"), ""));
483 assert_se(streq(skip_dev_prefix("//dev//"), ""));
484 assert_se(streq(skip_dev_prefix("/dev///foo"), "foo"));
485 assert_se(streq(skip_dev_prefix("///dev///foo///bar"), "foo///bar"));
486 assert_se(streq(skip_dev_prefix("//foo"), "//foo"));
487 assert_se(streq(skip_dev_prefix("foo"), "foo"));
490 int main(int argc, char **argv) {
491 log_set_max_level(LOG_DEBUG);
492 log_parse_environment();
496 test_path_equal_root();
497 test_find_binary(argv[0]);
500 #if 0 /// UNNEEDED by elogind
502 test_make_relative();
505 test_path_startswith();
507 test_file_in_same_dir();
508 test_last_path_component();
509 test_filename_is_valid();
510 test_hidden_or_backup_file();
511 test_skip_dev_prefix();
513 #if 0 /// UNNEEDED by elogind
514 test_systemd_installation_has_version(argv[1]); /* NULL is OK */