]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/test/test-path-util.c
util-lib: split our string related calls from util.[ch] into its own file string...
[thirdparty/systemd.git] / src / test / test-path-util.c
CommitLineData
76877b46
ZJS
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2013 Zbigniew Jędrzejewski-Szmek
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
a696dbef 22#include <stdio.h>
5d409034 23#include <sys/mount.h>
07630cea 24#include <unistd.h>
a696dbef 25
76877b46 26#include "macro.h"
07630cea 27#include "path-util.h"
c6878637 28#include "rm-rf.h"
07630cea
LP
29#include "string-util.h"
30#include "strv.h"
31#include "util.h"
76877b46 32
2230852b
MS
33#define test_path_compare(a, b, result) { \
34 assert_se(path_compare(a, b) == result); \
35 assert_se(path_compare(b, a) == -result); \
36 assert_se(path_equal(a, b) == !result); \
37 assert_se(path_equal(b, a) == !result); \
38 }
76877b46
ZJS
39
40static void test_path(void) {
3f72b427
LP
41 _cleanup_close_ int fd = -1;
42
2230852b
MS
43 test_path_compare("/goo", "/goo", 0);
44 test_path_compare("/goo", "/goo", 0);
45 test_path_compare("//goo", "/goo", 0);
46 test_path_compare("//goo/////", "/goo", 0);
47 test_path_compare("goo/////", "goo", 0);
76877b46 48
2230852b
MS
49 test_path_compare("/goo/boo", "/goo//boo", 0);
50 test_path_compare("//goo/boo", "/goo/boo//", 0);
76877b46 51
2230852b 52 test_path_compare("/", "///", 0);
76877b46 53
2230852b
MS
54 test_path_compare("/x", "x/", 1);
55 test_path_compare("x/", "/", -1);
76877b46 56
2230852b
MS
57 test_path_compare("/x/./y", "x/y", 1);
58 test_path_compare("x/.y", "x/y", -1);
59
60 test_path_compare("foo", "/foo", -1);
61 test_path_compare("/foo", "/foo/bar", -1);
62 test_path_compare("/foo/aaa", "/foo/b", -1);
63 test_path_compare("/foo/aaa", "/foo/b/a", -1);
64 test_path_compare("/foo/a", "/foo/aaa", -1);
65 test_path_compare("/foo/a/b", "/foo/aaa", -1);
76877b46
ZJS
66
67 assert_se(path_is_absolute("/"));
68 assert_se(!path_is_absolute("./"));
69
70 assert_se(is_path("/dir"));
71 assert_se(is_path("a/b"));
72 assert_se(!is_path("."));
73
2b6bf07d
ZJS
74 assert_se(streq(basename("./aa/bb/../file.da."), "file.da."));
75 assert_se(streq(basename("/aa///.file"), ".file"));
76 assert_se(streq(basename("/aa///file..."), "file..."));
77 assert_se(streq(basename("file.../"), ""));
76877b46 78
a696dbef 79#define test_parent(x, y) { \
c8b32e11 80 _cleanup_free_ char *z = NULL; \
a696dbef
ZJS
81 int r = path_get_parent(x, &z); \
82 printf("expected: %s\n", y ? y : "error"); \
83 printf("actual: %s\n", r<0 ? "error" : z); \
84 assert_se((y==NULL) ^ (r==0)); \
85 assert_se(y==NULL || path_equal(z, y)); \
76877b46
ZJS
86 }
87
88 test_parent("./aa/bb/../file.da.", "./aa/bb/..");
89 test_parent("/aa///.file", "/aa///");
90 test_parent("/aa///file...", "/aa///");
a696dbef 91 test_parent("file.../", NULL);
76877b46 92
3f72b427
LP
93 fd = open("/", O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOCTTY);
94 assert_se(fd >= 0);
5d409034 95 assert_se(fd_is_mount_point(fd, "/", 0) > 0);
76877b46
ZJS
96
97 {
98 char p1[] = "aaa/bbb////ccc";
99 char p2[] = "//aaa/.////ccc";
100 char p3[] = "/./";
101
8d95631e
FB
102 assert_se(path_equal(path_kill_slashes(p1), "aaa/bbb/ccc"));
103 assert_se(path_equal(path_kill_slashes(p2), "/aaa/./ccc"));
104 assert_se(path_equal(path_kill_slashes(p3), "/./"));
76877b46
ZJS
105 }
106}
107
85eca92e 108static void test_find_binary(const char *self) {
c9d954b2
ZJS
109 char *p;
110
85eca92e 111 assert_se(find_binary("/bin/sh", &p) == 0);
c9d954b2 112 puts(p);
85eca92e 113 assert_se(path_equal(p, "/bin/sh"));
c9d954b2
ZJS
114 free(p);
115
85eca92e 116 assert_se(find_binary(self, &p) == 0);
c9d954b2 117 puts(p);
8d95631e
FB
118 assert_se(endswith(p, "/test-path-util"));
119 assert_se(path_is_absolute(p));
c9d954b2
ZJS
120 free(p);
121
85eca92e 122 assert_se(find_binary("sh", &p) == 0);
c9d954b2 123 puts(p);
8d95631e
FB
124 assert_se(endswith(p, "/sh"));
125 assert_se(path_is_absolute(p));
c9d954b2
ZJS
126 free(p);
127
85eca92e
LP
128 assert_se(find_binary("xxxx-xxxx", &p) == -ENOENT);
129 assert_se(find_binary("/some/dir/xxxx-xxxx", &p) == -ENOENT);
c9d954b2
ZJS
130}
131
fecffe5d 132static void test_prefixes(void) {
e203f7c3
LP
133 static const char* values[] = { "/a/b/c/d", "/a/b/c", "/a/b", "/a", "", NULL};
134 unsigned i;
fecffe5d 135 char s[PATH_MAX];
e203f7c3 136 bool b;
fecffe5d 137
e203f7c3
LP
138 i = 0;
139 PATH_FOREACH_PREFIX_MORE(s, "/a/b/c/d") {
fecffe5d
LP
140 log_error("---%s---", s);
141 assert_se(streq(s, values[i++]));
142 }
e203f7c3 143 assert_se(values[i] == NULL);
fecffe5d 144
e203f7c3
LP
145 i = 1;
146 PATH_FOREACH_PREFIX(s, "/a/b/c/d") {
147 log_error("---%s---", s);
148 assert_se(streq(s, values[i++]));
149 }
fecffe5d
LP
150 assert_se(values[i] == NULL);
151
152 i = 0;
e203f7c3 153 PATH_FOREACH_PREFIX_MORE(s, "////a////b////c///d///////")
fecffe5d 154 assert_se(streq(s, values[i++]));
e203f7c3 155 assert_se(values[i] == NULL);
fecffe5d 156
e203f7c3
LP
157 i = 1;
158 PATH_FOREACH_PREFIX(s, "////a////b////c///d///////")
159 assert_se(streq(s, values[i++]));
fecffe5d
LP
160 assert_se(values[i] == NULL);
161
162 PATH_FOREACH_PREFIX(s, "////")
e203f7c3
LP
163 assert_not_reached("Wut?");
164
165 b = false;
166 PATH_FOREACH_PREFIX_MORE(s, "////") {
167 assert_se(!b);
fecffe5d 168 assert_se(streq(s, ""));
e203f7c3
LP
169 b = true;
170 }
171 assert_se(b);
fecffe5d
LP
172
173 PATH_FOREACH_PREFIX(s, "")
174 assert_not_reached("wut?");
175
e203f7c3
LP
176 b = false;
177 PATH_FOREACH_PREFIX_MORE(s, "") {
8d95631e
FB
178 assert_se(!b);
179 assert_se(streq(s, ""));
e203f7c3
LP
180 b = true;
181 }
fecffe5d
LP
182}
183
0c6ea3a4 184static void test_path_join(void) {
59ae3a95
TA
185
186#define test_join(root, path, rest, expected) { \
187 _cleanup_free_ char *z = NULL; \
188 z = path_join(root, path, rest); \
189 assert_se(streq(z, expected)); \
190 }
191
192 test_join("/root", "/a/b", "/c", "/root/a/b/c");
193 test_join("/root", "a/b", "c", "/root/a/b/c");
194 test_join("/root", "/a/b", "c", "/root/a/b/c");
bc854dc7 195 test_join("/root", "/", "c", "/root/c");
59ae3a95
TA
196 test_join("/root", "/", NULL, "/root/");
197
198 test_join(NULL, "/a/b", "/c", "/a/b/c");
199 test_join(NULL, "a/b", "c", "a/b/c");
200 test_join(NULL, "/a/b", "c", "/a/b/c");
bc854dc7 201 test_join(NULL, "/", "c", "/c");
59ae3a95 202 test_join(NULL, "/", NULL, "/");
0c6ea3a4
ZJS
203}
204
eb66db55
MG
205static void test_fsck_exists(void) {
206 /* Ensure we use a sane default for PATH. */
207 unsetenv("PATH");
208
209 /* fsck.minix is provided by util-linux and will probably exist. */
85eca92e 210 assert_se(fsck_exists("minix") == 1);
eb66db55 211
85eca92e
LP
212 assert_se(fsck_exists("AbCdE") == 0);
213 assert_se(fsck_exists("/../bin/") == 0);
eb66db55
MG
214}
215
6b56a651
TK
216static void test_make_relative(void) {
217 char *result;
218
219 assert_se(path_make_relative("some/relative/path", "/some/path", &result) < 0);
220 assert_se(path_make_relative("/some/path", "some/relative/path", &result) < 0);
221
59ae3a95
TA
222#define test(from_dir, to_path, expected) { \
223 _cleanup_free_ char *z = NULL; \
224 path_make_relative(from_dir, to_path, &z); \
225 assert_se(streq(z, expected)); \
6b56a651
TK
226 }
227
228 test("/", "/", ".");
229 test("/", "/some/path", "some/path");
230 test("/some/path", "/some/path", ".");
231 test("/some/path", "/some/path/in/subdir", "in/subdir");
232 test("/some/path", "/", "../..");
233 test("/some/path", "/some/other/path", "../other/path");
234 test("//extra/////slashes///won't////fool///anybody//", "////extra///slashes////are/just///fine///", "../../../are/just/fine");
235}
236
3e8a78c8
MM
237static void test_strv_resolve(void) {
238 char tmp_dir[] = "/tmp/test-path-util-XXXXXX";
239 _cleanup_strv_free_ char **search_dirs = NULL;
240 _cleanup_strv_free_ char **absolute_dirs = NULL;
241 char **d;
242
243 assert_se(mkdtemp(tmp_dir) != NULL);
244
245 search_dirs = strv_new("/dir1", "/dir2", "/dir3", NULL);
246 assert_se(search_dirs);
247 STRV_FOREACH(d, search_dirs) {
248 char *p = strappend(tmp_dir, *d);
249 assert_se(p);
250 assert_se(strv_push(&absolute_dirs, p) == 0);
251 }
252
253 assert_se(mkdir(absolute_dirs[0], 0700) == 0);
254 assert_se(mkdir(absolute_dirs[1], 0700) == 0);
255 assert_se(symlink("dir2", absolute_dirs[2]) == 0);
256
257 path_strv_resolve(search_dirs, tmp_dir);
258 assert_se(streq(search_dirs[0], "/dir1"));
259 assert_se(streq(search_dirs[1], "/dir2"));
260 assert_se(streq(search_dirs[2], "/dir2"));
261
c6878637 262 assert_se(rm_rf(tmp_dir, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
3e8a78c8
MM
263}
264
5895b62f
RC
265static void test_path_startswith(void) {
266 assert_se(path_startswith("/foo/bar/barfoo/", "/foo"));
267 assert_se(path_startswith("/foo/bar/barfoo/", "/foo/"));
268 assert_se(path_startswith("/foo/bar/barfoo/", "/"));
269 assert_se(path_startswith("/foo/bar/barfoo/", "////"));
270 assert_se(path_startswith("/foo/bar/barfoo/", "/foo//bar/////barfoo///"));
271 assert_se(path_startswith("/foo/bar/barfoo/", "/foo/bar/barfoo////"));
272 assert_se(path_startswith("/foo/bar/barfoo/", "/foo/bar///barfoo/"));
273 assert_se(path_startswith("/foo/bar/barfoo/", "/foo////bar/barfoo/"));
274 assert_se(path_startswith("/foo/bar/barfoo/", "////foo/bar/barfoo/"));
275 assert_se(path_startswith("/foo/bar/barfoo/", "/foo/bar/barfoo"));
276
277 assert_se(!path_startswith("/foo/bar/barfoo/", "/foo/bar/barfooa/"));
278 assert_se(!path_startswith("/foo/bar/barfoo/", "/foo/bar/barfooa"));
279 assert_se(!path_startswith("/foo/bar/barfoo/", ""));
280 assert_se(!path_startswith("/foo/bar/barfoo/", "/bar/foo"));
281 assert_se(!path_startswith("/foo/bar/barfoo/", "/f/b/b/"));
282}
283
1d13f648
LP
284static void test_prefix_root_one(const char *r, const char *p, const char *expected) {
285 _cleanup_free_ char *s = NULL;
286 const char *t;
287
288 assert_se(s = prefix_root(r, p));
289 assert_se(streq_ptr(s, expected));
290
291 t = prefix_roota(r, p);
292 assert_se(t);
293 assert_se(streq_ptr(t, expected));
294}
295
296static void test_prefix_root(void) {
297 test_prefix_root_one("/", "/foo", "/foo");
298 test_prefix_root_one(NULL, "/foo", "/foo");
299 test_prefix_root_one("", "/foo", "/foo");
300 test_prefix_root_one("///", "/foo", "/foo");
301 test_prefix_root_one("/", "////foo", "/foo");
302 test_prefix_root_one(NULL, "////foo", "/foo");
303
304 test_prefix_root_one("/foo", "/bar", "/foo/bar");
305 test_prefix_root_one("/foo", "bar", "/foo/bar");
306 test_prefix_root_one("foo", "bar", "foo/bar");
307 test_prefix_root_one("/foo/", "/bar", "/foo/bar");
308 test_prefix_root_one("/foo/", "//bar", "/foo/bar");
309 test_prefix_root_one("/foo///", "//bar", "/foo/bar");
310}
311
5d409034 312static void test_path_is_mount_point(void) {
36908eb8 313 int fd;
5d409034
MP
314 char tmp_dir[] = "/tmp/test-path-is-mount-point-XXXXXX";
315 _cleanup_free_ char *file1 = NULL, *file2 = NULL, *link1 = NULL, *link2 = NULL;
36908eb8
MP
316 _cleanup_free_ char *dir1 = NULL, *dir1file = NULL, *dirlink1 = NULL, *dirlink1file = NULL;
317 _cleanup_free_ char *dir2 = NULL, *dir2file = NULL;
5d409034 318
e26d6ce5
MP
319 assert_se(path_is_mount_point("/", AT_SYMLINK_FOLLOW) > 0);
320 assert_se(path_is_mount_point("/", 0) > 0);
5d409034 321
e26d6ce5
MP
322 assert_se(path_is_mount_point("/proc", AT_SYMLINK_FOLLOW) > 0);
323 assert_se(path_is_mount_point("/proc", 0) > 0);
5d409034 324
e26d6ce5
MP
325 assert_se(path_is_mount_point("/proc/1", AT_SYMLINK_FOLLOW) == 0);
326 assert_se(path_is_mount_point("/proc/1", 0) == 0);
5d409034 327
e26d6ce5
MP
328 assert_se(path_is_mount_point("/sys", AT_SYMLINK_FOLLOW) > 0);
329 assert_se(path_is_mount_point("/sys", 0) > 0);
5d409034 330
36908eb8
MP
331 /* we'll create a hierarchy of different kinds of dir/file/link
332 * layouts:
333 *
334 * <tmp>/file1, <tmp>/file2
335 * <tmp>/link1 -> file1, <tmp>/link2 -> file2
336 * <tmp>/dir1/
337 * <tmp>/dir1/file
338 * <tmp>/dirlink1 -> dir1
339 * <tmp>/dirlink1file -> dirlink1/file
340 * <tmp>/dir2/
341 * <tmp>/dir2/file
342 */
343
5d409034
MP
344 /* file mountpoints */
345 assert_se(mkdtemp(tmp_dir) != NULL);
346 file1 = path_join(NULL, tmp_dir, "file1");
347 assert_se(file1);
348 file2 = path_join(NULL, tmp_dir, "file2");
349 assert_se(file2);
350 fd = open(file1, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0664);
351 assert_se(fd > 0);
352 close(fd);
353 fd = open(file2, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0664);
354 assert_se(fd > 0);
355 close(fd);
356 link1 = path_join(NULL, tmp_dir, "link1");
357 assert_se(link1);
358 assert_se(symlink("file1", link1) == 0);
359 link2 = path_join(NULL, tmp_dir, "link2");
360 assert_se(link1);
361 assert_se(symlink("file2", link2) == 0);
362
e26d6ce5
MP
363 assert_se(path_is_mount_point(file1, AT_SYMLINK_FOLLOW) == 0);
364 assert_se(path_is_mount_point(file1, 0) == 0);
365 assert_se(path_is_mount_point(link1, AT_SYMLINK_FOLLOW) == 0);
366 assert_se(path_is_mount_point(link1, 0) == 0);
5d409034 367
36908eb8
MP
368 /* directory mountpoints */
369 dir1 = path_join(NULL, tmp_dir, "dir1");
370 assert_se(dir1);
371 assert_se(mkdir(dir1, 0755) == 0);
372 dirlink1 = path_join(NULL, tmp_dir, "dirlink1");
373 assert_se(dirlink1);
374 assert_se(symlink("dir1", dirlink1) == 0);
375 dirlink1file = path_join(NULL, tmp_dir, "dirlink1file");
376 assert_se(dirlink1file);
377 assert_se(symlink("dirlink1/file", dirlink1file) == 0);
378 dir2 = path_join(NULL, tmp_dir, "dir2");
379 assert_se(dir2);
380 assert_se(mkdir(dir2, 0755) == 0);
381
382 assert_se(path_is_mount_point(dir1, AT_SYMLINK_FOLLOW) == 0);
383 assert_se(path_is_mount_point(dir1, 0) == 0);
384 assert_se(path_is_mount_point(dirlink1, AT_SYMLINK_FOLLOW) == 0);
385 assert_se(path_is_mount_point(dirlink1, 0) == 0);
386
387 /* file in subdirectory mountpoints */
388 dir1file = path_join(NULL, dir1, "file");
389 assert_se(dir1file);
390 fd = open(dir1file, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0664);
391 assert_se(fd > 0);
392 close(fd);
393
394 assert_se(path_is_mount_point(dir1file, AT_SYMLINK_FOLLOW) == 0);
395 assert_se(path_is_mount_point(dir1file, 0) == 0);
396 assert_se(path_is_mount_point(dirlink1file, AT_SYMLINK_FOLLOW) == 0);
397 assert_se(path_is_mount_point(dirlink1file, 0) == 0);
398
399 /* these tests will only work as root */
5d409034 400 if (mount(file1, file2, NULL, MS_BIND, NULL) >= 0) {
36908eb8
MP
401 int rt, rf, rlt, rlf, rl1t, rl1f;
402
403 /* files */
404 /* capture results in vars, to avoid dangling mounts on failure */
e26d6ce5
MP
405 rf = path_is_mount_point(file2, 0);
406 rt = path_is_mount_point(file2, AT_SYMLINK_FOLLOW);
407 rlf = path_is_mount_point(link2, 0);
408 rlt = path_is_mount_point(link2, AT_SYMLINK_FOLLOW);
5d409034
MP
409
410 assert_se(umount(file2) == 0);
411
412 assert_se(rf == 1);
413 assert_se(rt == 1);
414 assert_se(rlf == 0);
415 assert_se(rlt == 1);
36908eb8
MP
416
417 /* dirs */
418 dir2file = path_join(NULL, dir2, "file");
419 assert_se(dir2file);
420 fd = open(dir2file, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0664);
421 assert_se(fd > 0);
422 close(fd);
423
424 assert_se(mount(dir2, dir1, NULL, MS_BIND, NULL) >= 0);
425
426 rf = path_is_mount_point(dir1, 0);
427 rt = path_is_mount_point(dir1, AT_SYMLINK_FOLLOW);
428 rlf = path_is_mount_point(dirlink1, 0);
429 rlt = path_is_mount_point(dirlink1, AT_SYMLINK_FOLLOW);
430 /* its parent is a mount point, but not /file itself */
431 rl1f = path_is_mount_point(dirlink1file, 0);
432 rl1t = path_is_mount_point(dirlink1file, AT_SYMLINK_FOLLOW);
433
434 assert_se(umount(dir1) == 0);
435
436 assert_se(rf == 1);
437 assert_se(rt == 1);
438 assert_se(rlf == 0);
439 assert_se(rlt == 1);
440 assert_se(rl1f == 0);
441 assert_se(rl1t == 0);
442
5d409034
MP
443 } else
444 printf("Skipping bind mount file test: %m\n");
445
446 assert_se(rm_rf(tmp_dir, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
447}
448
7f076504 449int main(int argc, char **argv) {
76877b46 450 test_path();
85eca92e 451 test_find_binary(argv[0]);
fecffe5d 452 test_prefixes();
0c6ea3a4 453 test_path_join();
eb66db55 454 test_fsck_exists();
6b56a651 455 test_make_relative();
3e8a78c8 456 test_strv_resolve();
5895b62f 457 test_path_startswith();
1d13f648 458 test_prefix_root();
5d409034 459 test_path_is_mount_point();
5895b62f 460
76877b46
ZJS
461 return 0;
462}