]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/test/test-path-util.c
fed829f1f7200f5e4cc36e4a9a58f47b2ba08d53
[thirdparty/systemd.git] / src / test / test-path-util.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright 2013 Zbigniew Jędrzejewski-Szmek
6
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.
11
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.
16
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/>.
19 ***/
20
21 #include <stdio.h>
22 #include <unistd.h>
23
24 #include "alloc-util.h"
25 #include "fd-util.h"
26 #include "macro.h"
27 #include "mount-util.h"
28 #include "path-util.h"
29 #include "rm-rf.h"
30 #include "stat-util.h"
31 #include "string-util.h"
32 #include "strv.h"
33 #include "util.h"
34
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); \
40 }
41
42 static void test_path(void) {
43 _cleanup_close_ int fd = -1;
44
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);
50
51 test_path_compare("/goo/boo", "/goo//boo", 0);
52 test_path_compare("//goo/boo", "/goo/boo//", 0);
53
54 test_path_compare("/", "///", 0);
55
56 test_path_compare("/x", "x/", 1);
57 test_path_compare("x/", "/", -1);
58
59 test_path_compare("/x/./y", "x/y", 1);
60 test_path_compare("x/.y", "x/y", -1);
61
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);
68
69 assert_se(path_is_absolute("/"));
70 assert_se(!path_is_absolute("./"));
71
72 assert_se(is_path("/dir"));
73 assert_se(is_path("a/b"));
74 assert_se(!is_path("."));
75
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.../"), ""));
80
81 fd = open("/", O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOCTTY);
82 assert_se(fd >= 0);
83 assert_se(fd_is_mount_point(fd, "/", 0) > 0);
84
85 {
86 char p1[] = "aaa/bbb////ccc";
87 char p2[] = "//aaa/.////ccc";
88 char p3[] = "/./";
89
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), "/./"));
93 }
94
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"));
100
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"));
106 }
107
108 static void test_path_equal_root(void) {
109 /* Nail down the details of how path_equal("/", ...) works. */
110
111 assert_se(path_equal("/", "/"));
112 assert_se(path_equal("/", "//"));
113
114 assert_se(!path_equal("/", "/./"));
115 assert_se(!path_equal("/", "/../"));
116
117 assert_se(!path_equal("/", "/.../"));
118
119 /* Make sure that files_same works as expected. */
120
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);
125
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);
130
131 assert_se(files_same("/", "/.../", 0) == -ENOENT);
132 assert_se(files_same("/", "/.../", AT_SYMLINK_NOFOLLOW) == -ENOENT);
133
134 /* The same for path_equal_or_files_same. */
135
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));
140
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));
145
146 assert_se(!path_equal_or_files_same("/", "/.../", 0));
147 assert_se(!path_equal_or_files_same("/", "/.../", AT_SYMLINK_NOFOLLOW));
148 }
149
150 static void test_find_binary(const char *self) {
151 char *p;
152
153 assert_se(find_binary("/bin/sh", &p) == 0);
154 puts(p);
155 assert_se(path_equal(p, "/bin/sh"));
156 free(p);
157
158 assert_se(find_binary(self, &p) == 0);
159 puts(p);
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));
163 free(p);
164
165 assert_se(find_binary("sh", &p) == 0);
166 puts(p);
167 assert_se(endswith(p, "/sh"));
168 assert_se(path_is_absolute(p));
169 free(p);
170
171 assert_se(find_binary("xxxx-xxxx", &p) == -ENOENT);
172 assert_se(find_binary("/some/dir/xxxx-xxxx", &p) == -ENOENT);
173 }
174
175 static void test_prefixes(void) {
176 static const char* values[] = { "/a/b/c/d", "/a/b/c", "/a/b", "/a", "", NULL};
177 unsigned i;
178 char s[PATH_MAX];
179 bool b;
180
181 i = 0;
182 PATH_FOREACH_PREFIX_MORE(s, "/a/b/c/d") {
183 log_error("---%s---", s);
184 assert_se(streq(s, values[i++]));
185 }
186 assert_se(values[i] == NULL);
187
188 i = 1;
189 PATH_FOREACH_PREFIX(s, "/a/b/c/d") {
190 log_error("---%s---", s);
191 assert_se(streq(s, values[i++]));
192 }
193 assert_se(values[i] == NULL);
194
195 i = 0;
196 PATH_FOREACH_PREFIX_MORE(s, "////a////b////c///d///////")
197 assert_se(streq(s, values[i++]));
198 assert_se(values[i] == NULL);
199
200 i = 1;
201 PATH_FOREACH_PREFIX(s, "////a////b////c///d///////")
202 assert_se(streq(s, values[i++]));
203 assert_se(values[i] == NULL);
204
205 PATH_FOREACH_PREFIX(s, "////")
206 assert_not_reached("Wut?");
207
208 b = false;
209 PATH_FOREACH_PREFIX_MORE(s, "////") {
210 assert_se(!b);
211 assert_se(streq(s, ""));
212 b = true;
213 }
214 assert_se(b);
215
216 PATH_FOREACH_PREFIX(s, "")
217 assert_not_reached("wut?");
218
219 b = false;
220 PATH_FOREACH_PREFIX_MORE(s, "") {
221 assert_se(!b);
222 assert_se(streq(s, ""));
223 b = true;
224 }
225 }
226
227 static void test_path_join(void) {
228
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)); \
233 }
234
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/");
240
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, "/");
246 }
247
248 static void test_fsck_exists(void) {
249 /* Ensure we use a sane default for PATH. */
250 unsetenv("PATH");
251
252 /* fsck.minix is provided by util-linux and will probably exist. */
253 assert_se(fsck_exists("minix") == 1);
254
255 assert_se(fsck_exists("AbCdE") == 0);
256 assert_se(fsck_exists("/../bin/") == 0);
257 }
258
259 static void test_make_relative(void) {
260 char *result;
261
262 assert_se(path_make_relative("some/relative/path", "/some/path", &result) < 0);
263 assert_se(path_make_relative("/some/path", "some/relative/path", &result) < 0);
264 assert_se(path_make_relative("/some/dotdot/../path", "/some/path", &result) < 0);
265
266 #define test(from_dir, to_path, expected) { \
267 _cleanup_free_ char *z = NULL; \
268 path_make_relative(from_dir, to_path, &z); \
269 assert_se(streq(z, expected)); \
270 }
271
272 test("/", "/", ".");
273 test("/", "/some/path", "some/path");
274 test("/some/path", "/some/path", ".");
275 test("/some/path", "/some/path/in/subdir", "in/subdir");
276 test("/some/path", "/", "../..");
277 test("/some/path", "/some/other/path", "../other/path");
278 test("/some/path/./dot", "/some/further/path", "../../further/path");
279 test("//extra/////slashes///won't////fool///anybody//", "////extra///slashes////are/just///fine///", "../../../are/just/fine");
280 }
281
282 static void test_strv_resolve(void) {
283 char tmp_dir[] = "/tmp/test-path-util-XXXXXX";
284 _cleanup_strv_free_ char **search_dirs = NULL;
285 _cleanup_strv_free_ char **absolute_dirs = NULL;
286 char **d;
287
288 assert_se(mkdtemp(tmp_dir) != NULL);
289
290 search_dirs = strv_new("/dir1", "/dir2", "/dir3", NULL);
291 assert_se(search_dirs);
292 STRV_FOREACH(d, search_dirs) {
293 char *p = strappend(tmp_dir, *d);
294 assert_se(p);
295 assert_se(strv_push(&absolute_dirs, p) == 0);
296 }
297
298 assert_se(mkdir(absolute_dirs[0], 0700) == 0);
299 assert_se(mkdir(absolute_dirs[1], 0700) == 0);
300 assert_se(symlink("dir2", absolute_dirs[2]) == 0);
301
302 path_strv_resolve(search_dirs, tmp_dir);
303 assert_se(streq(search_dirs[0], "/dir1"));
304 assert_se(streq(search_dirs[1], "/dir2"));
305 assert_se(streq(search_dirs[2], "/dir2"));
306
307 assert_se(rm_rf(tmp_dir, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
308 }
309
310 static void test_path_startswith(void) {
311 const char *p;
312
313 p = path_startswith("/foo/bar/barfoo/", "/foo");
314 assert_se(streq_ptr(p, "bar/barfoo/"));
315
316 p = path_startswith("/foo/bar/barfoo/", "/foo/");
317 assert_se(streq_ptr(p, "bar/barfoo/"));
318
319 p = path_startswith("/foo/bar/barfoo/", "/");
320 assert_se(streq_ptr(p, "foo/bar/barfoo/"));
321
322 p = path_startswith("/foo/bar/barfoo/", "////");
323 assert_se(streq_ptr(p, "foo/bar/barfoo/"));
324
325 p = path_startswith("/foo/bar/barfoo/", "/foo//bar/////barfoo///");
326 assert_se(streq_ptr(p, ""));
327
328 p = path_startswith("/foo/bar/barfoo/", "/foo/bar/barfoo////");
329 assert_se(streq_ptr(p, ""));
330
331 p = path_startswith("/foo/bar/barfoo/", "/foo/bar///barfoo/");
332 assert_se(streq_ptr(p, ""));
333
334 p = path_startswith("/foo/bar/barfoo/", "/foo////bar/barfoo/");
335 assert_se(streq_ptr(p, ""));
336
337 p = path_startswith("/foo/bar/barfoo/", "////foo/bar/barfoo/");
338 assert_se(streq_ptr(p, ""));
339
340 p = path_startswith("/foo/bar/barfoo/", "/foo/bar/barfoo");
341 assert_se(streq_ptr(p, ""));
342
343 assert_se(!path_startswith("/foo/bar/barfoo/", "/foo/bar/barfooa/"));
344 assert_se(!path_startswith("/foo/bar/barfoo/", "/foo/bar/barfooa"));
345 assert_se(!path_startswith("/foo/bar/barfoo/", ""));
346 assert_se(!path_startswith("/foo/bar/barfoo/", "/bar/foo"));
347 assert_se(!path_startswith("/foo/bar/barfoo/", "/f/b/b/"));
348 }
349
350 static void test_prefix_root_one(const char *r, const char *p, const char *expected) {
351 _cleanup_free_ char *s = NULL;
352 const char *t;
353
354 assert_se(s = prefix_root(r, p));
355 assert_se(streq_ptr(s, expected));
356
357 t = prefix_roota(r, p);
358 assert_se(t);
359 assert_se(streq_ptr(t, expected));
360 }
361
362 static void test_prefix_root(void) {
363 test_prefix_root_one("/", "/foo", "/foo");
364 test_prefix_root_one(NULL, "/foo", "/foo");
365 test_prefix_root_one("", "/foo", "/foo");
366 test_prefix_root_one("///", "/foo", "/foo");
367 test_prefix_root_one("/", "////foo", "/foo");
368 test_prefix_root_one(NULL, "////foo", "/foo");
369
370 test_prefix_root_one("/foo", "/bar", "/foo/bar");
371 test_prefix_root_one("/foo", "bar", "/foo/bar");
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 }
377
378 static void test_file_in_same_dir(void) {
379 char *t;
380
381 t = file_in_same_dir("/", "a");
382 assert_se(streq(t, "/a"));
383 free(t);
384
385 t = file_in_same_dir("/", "/a");
386 assert_se(streq(t, "/a"));
387 free(t);
388
389 t = file_in_same_dir("", "a");
390 assert_se(streq(t, "a"));
391 free(t);
392
393 t = file_in_same_dir("a/", "a");
394 assert_se(streq(t, "a/a"));
395 free(t);
396
397 t = file_in_same_dir("bar/foo", "bar");
398 assert_se(streq(t, "bar/bar"));
399 free(t);
400 }
401
402 static void test_filename_is_valid(void) {
403 char foo[FILENAME_MAX+2];
404 int i;
405
406 assert_se(!filename_is_valid(""));
407 assert_se(!filename_is_valid("/bar/foo"));
408 assert_se(!filename_is_valid("/"));
409 assert_se(!filename_is_valid("."));
410 assert_se(!filename_is_valid(".."));
411
412 for (i=0; i<FILENAME_MAX+1; i++)
413 foo[i] = 'a';
414 foo[FILENAME_MAX+1] = '\0';
415
416 assert_se(!filename_is_valid(foo));
417
418 assert_se(filename_is_valid("foo_bar-333"));
419 assert_se(filename_is_valid("o.o"));
420 }
421
422 static void test_hidden_or_backup_file(void) {
423 assert_se(hidden_or_backup_file(".hidden"));
424 assert_se(hidden_or_backup_file("..hidden"));
425 assert_se(!hidden_or_backup_file("hidden."));
426
427 assert_se(hidden_or_backup_file("backup~"));
428 assert_se(hidden_or_backup_file(".backup~"));
429
430 assert_se(hidden_or_backup_file("lost+found"));
431 assert_se(hidden_or_backup_file("aquota.user"));
432 assert_se(hidden_or_backup_file("aquota.group"));
433
434 assert_se(hidden_or_backup_file("test.rpmnew"));
435 assert_se(hidden_or_backup_file("test.dpkg-old"));
436 assert_se(hidden_or_backup_file("test.dpkg-remove"));
437 assert_se(hidden_or_backup_file("test.swp"));
438
439 assert_se(!hidden_or_backup_file("test.rpmnew."));
440 assert_se(!hidden_or_backup_file("test.dpkg-old.foo"));
441 }
442
443 static void test_systemd_installation_has_version(const char *path) {
444 int r;
445 const unsigned versions[] = {0, 231, atoi(PACKAGE_VERSION), 999};
446 unsigned i;
447
448 for (i = 0; i < ELEMENTSOF(versions); i++) {
449 r = systemd_installation_has_version(path, versions[i]);
450 assert_se(r >= 0);
451 log_info("%s has systemd >= %u: %s",
452 path ?: "Current installation", versions[i], yes_no(r));
453 }
454 }
455
456 static void test_skip_dev_prefix(void) {
457
458 assert_se(streq(skip_dev_prefix("/"), "/"));
459 assert_se(streq(skip_dev_prefix("/dev"), ""));
460 assert_se(streq(skip_dev_prefix("/dev/"), ""));
461 assert_se(streq(skip_dev_prefix("/dev/foo"), "foo"));
462 assert_se(streq(skip_dev_prefix("/dev/foo/bar"), "foo/bar"));
463 assert_se(streq(skip_dev_prefix("//dev"), ""));
464 assert_se(streq(skip_dev_prefix("//dev//"), ""));
465 assert_se(streq(skip_dev_prefix("/dev///foo"), "foo"));
466 assert_se(streq(skip_dev_prefix("///dev///foo///bar"), "foo///bar"));
467 assert_se(streq(skip_dev_prefix("//foo"), "//foo"));
468 assert_se(streq(skip_dev_prefix("foo"), "foo"));
469 }
470
471 int main(int argc, char **argv) {
472 log_set_max_level(LOG_DEBUG);
473 log_parse_environment();
474 log_open();
475
476 test_path();
477 test_path_equal_root();
478 test_find_binary(argv[0]);
479 test_prefixes();
480 test_path_join();
481 test_fsck_exists();
482 test_make_relative();
483 test_strv_resolve();
484 test_path_startswith();
485 test_prefix_root();
486 test_file_in_same_dir();
487 test_filename_is_valid();
488 test_hidden_or_backup_file();
489 test_skip_dev_prefix();
490
491 test_systemd_installation_has_version(argv[1]); /* NULL is OK */
492
493 return 0;
494 }