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