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