]> git.ipfire.org Git - thirdparty/systemd.git/blob - 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
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
22 #include <stdio.h>
23 #include <sys/mount.h>
24 #include <unistd.h>
25
26 #include "macro.h"
27 #include "path-util.h"
28 #include "rm-rf.h"
29 #include "string-util.h"
30 #include "strv.h"
31 #include "util.h"
32
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 }
39
40 static void test_path(void) {
41 _cleanup_close_ int fd = -1;
42
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);
48
49 test_path_compare("/goo/boo", "/goo//boo", 0);
50 test_path_compare("//goo/boo", "/goo/boo//", 0);
51
52 test_path_compare("/", "///", 0);
53
54 test_path_compare("/x", "x/", 1);
55 test_path_compare("x/", "/", -1);
56
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);
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
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.../"), ""));
78
79 #define test_parent(x, y) { \
80 _cleanup_free_ char *z = NULL; \
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)); \
86 }
87
88 test_parent("./aa/bb/../file.da.", "./aa/bb/..");
89 test_parent("/aa///.file", "/aa///");
90 test_parent("/aa///file...", "/aa///");
91 test_parent("file.../", NULL);
92
93 fd = open("/", O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOCTTY);
94 assert_se(fd >= 0);
95 assert_se(fd_is_mount_point(fd, "/", 0) > 0);
96
97 {
98 char p1[] = "aaa/bbb////ccc";
99 char p2[] = "//aaa/.////ccc";
100 char p3[] = "/./";
101
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), "/./"));
105 }
106 }
107
108 static void test_find_binary(const char *self) {
109 char *p;
110
111 assert_se(find_binary("/bin/sh", &p) == 0);
112 puts(p);
113 assert_se(path_equal(p, "/bin/sh"));
114 free(p);
115
116 assert_se(find_binary(self, &p) == 0);
117 puts(p);
118 assert_se(endswith(p, "/test-path-util"));
119 assert_se(path_is_absolute(p));
120 free(p);
121
122 assert_se(find_binary("sh", &p) == 0);
123 puts(p);
124 assert_se(endswith(p, "/sh"));
125 assert_se(path_is_absolute(p));
126 free(p);
127
128 assert_se(find_binary("xxxx-xxxx", &p) == -ENOENT);
129 assert_se(find_binary("/some/dir/xxxx-xxxx", &p) == -ENOENT);
130 }
131
132 static void test_prefixes(void) {
133 static const char* values[] = { "/a/b/c/d", "/a/b/c", "/a/b", "/a", "", NULL};
134 unsigned i;
135 char s[PATH_MAX];
136 bool b;
137
138 i = 0;
139 PATH_FOREACH_PREFIX_MORE(s, "/a/b/c/d") {
140 log_error("---%s---", s);
141 assert_se(streq(s, values[i++]));
142 }
143 assert_se(values[i] == NULL);
144
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 }
150 assert_se(values[i] == NULL);
151
152 i = 0;
153 PATH_FOREACH_PREFIX_MORE(s, "////a////b////c///d///////")
154 assert_se(streq(s, values[i++]));
155 assert_se(values[i] == NULL);
156
157 i = 1;
158 PATH_FOREACH_PREFIX(s, "////a////b////c///d///////")
159 assert_se(streq(s, values[i++]));
160 assert_se(values[i] == NULL);
161
162 PATH_FOREACH_PREFIX(s, "////")
163 assert_not_reached("Wut?");
164
165 b = false;
166 PATH_FOREACH_PREFIX_MORE(s, "////") {
167 assert_se(!b);
168 assert_se(streq(s, ""));
169 b = true;
170 }
171 assert_se(b);
172
173 PATH_FOREACH_PREFIX(s, "")
174 assert_not_reached("wut?");
175
176 b = false;
177 PATH_FOREACH_PREFIX_MORE(s, "") {
178 assert_se(!b);
179 assert_se(streq(s, ""));
180 b = true;
181 }
182 }
183
184 static void test_path_join(void) {
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");
195 test_join("/root", "/", "c", "/root/c");
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");
201 test_join(NULL, "/", "c", "/c");
202 test_join(NULL, "/", NULL, "/");
203 }
204
205 static 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. */
210 assert_se(fsck_exists("minix") == 1);
211
212 assert_se(fsck_exists("AbCdE") == 0);
213 assert_se(fsck_exists("/../bin/") == 0);
214 }
215
216 static 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
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)); \
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
237 static 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
262 assert_se(rm_rf(tmp_dir, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
263 }
264
265 static 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
284 static 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
296 static 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
312 static void test_path_is_mount_point(void) {
313 int fd;
314 char tmp_dir[] = "/tmp/test-path-is-mount-point-XXXXXX";
315 _cleanup_free_ char *file1 = NULL, *file2 = NULL, *link1 = NULL, *link2 = NULL;
316 _cleanup_free_ char *dir1 = NULL, *dir1file = NULL, *dirlink1 = NULL, *dirlink1file = NULL;
317 _cleanup_free_ char *dir2 = NULL, *dir2file = NULL;
318
319 assert_se(path_is_mount_point("/", AT_SYMLINK_FOLLOW) > 0);
320 assert_se(path_is_mount_point("/", 0) > 0);
321
322 assert_se(path_is_mount_point("/proc", AT_SYMLINK_FOLLOW) > 0);
323 assert_se(path_is_mount_point("/proc", 0) > 0);
324
325 assert_se(path_is_mount_point("/proc/1", AT_SYMLINK_FOLLOW) == 0);
326 assert_se(path_is_mount_point("/proc/1", 0) == 0);
327
328 assert_se(path_is_mount_point("/sys", AT_SYMLINK_FOLLOW) > 0);
329 assert_se(path_is_mount_point("/sys", 0) > 0);
330
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
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
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);
367
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 */
400 if (mount(file1, file2, NULL, MS_BIND, NULL) >= 0) {
401 int rt, rf, rlt, rlf, rl1t, rl1f;
402
403 /* files */
404 /* capture results in vars, to avoid dangling mounts on failure */
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);
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);
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
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
449 int main(int argc, char **argv) {
450 test_path();
451 test_find_binary(argv[0]);
452 test_prefixes();
453 test_path_join();
454 test_fsck_exists();
455 test_make_relative();
456 test_strv_resolve();
457 test_path_startswith();
458 test_prefix_root();
459 test_path_is_mount_point();
460
461 return 0;
462 }