]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/test/test-chase.c
4f6e006d5e1d4ae213b5011871ee4e62ade5dfee
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
6 #include "alloc-util.h"
9 #include "dirent-util.h"
12 #include "id128-util.h"
14 #include "path-util.h"
15 #include "random-util.h"
17 #include "stat-util.h"
18 #include "string-util.h"
20 #include "tmpfile-util.h"
21 #include "user-util.h"
23 static const char *arg_test_dir
= NULL
;
25 static void test_chase_extract_filename_one(const char *path
, const char *root
, const char *expected
) {
26 _cleanup_free_
char *ret1
= NULL
, *ret2
= NULL
, *fname
= NULL
;
28 log_debug("/* %s(path=%s, root=%s) */", __func__
, path
, strnull(root
));
30 assert_se(chase(path
, root
, CHASE_EXTRACT_FILENAME
, &ret1
, NULL
) > 0);
31 ASSERT_STREQ(ret1
, expected
);
33 assert_se(chase(path
, root
, 0, &ret2
, NULL
) > 0);
34 ASSERT_OK(chase_extract_filename(ret2
, root
, &fname
));
35 ASSERT_STREQ(fname
, expected
);
39 _cleanup_free_
char *result
= NULL
, *pwd
= NULL
;
40 _cleanup_close_
int pfd
= -EBADF
;
42 const char *top
, *p
, *pslash
, *q
, *qslash
;
46 temp
= strjoina(arg_test_dir
?: "/tmp", "/test-chase.XXXXXX");
47 assert_se(mkdtemp(temp
));
49 top
= strjoina(temp
, "/top");
50 ASSERT_OK(mkdir(top
, 0700));
52 p
= strjoina(top
, "/dot");
53 if (symlink(".", p
) < 0) {
54 assert_se(IN_SET(errno
, EINVAL
, ENOSYS
, ENOTTY
, EPERM
));
55 log_tests_skipped_errno(errno
, "symlink() not possible");
59 p
= strjoina(top
, "/dotdot");
60 ASSERT_OK(symlink("..", p
));
62 p
= strjoina(top
, "/dotdota");
63 ASSERT_OK(symlink("../a", p
));
65 p
= strjoina(temp
, "/a");
66 ASSERT_OK(symlink("b", p
));
68 p
= strjoina(temp
, "/b");
69 ASSERT_OK(symlink("/usr", p
));
71 p
= strjoina(temp
, "/start");
72 ASSERT_OK(symlink("top/dot/dotdota", p
));
74 /* Paths that use symlinks underneath the "root" */
76 r
= chase(p
, NULL
, 0, &result
, NULL
);
78 assert_se(path_equal(result
, "/usr"));
79 result
= mfree(result
);
81 r
= chase(p
, "/.//../../../", 0, &result
, NULL
);
83 assert_se(path_equal(result
, "/usr"));
84 result
= mfree(result
);
86 pslash
= strjoina(p
, "/");
87 r
= chase(pslash
, NULL
, 0, &result
, NULL
);
89 assert_se(path_equal(result
, "/usr/"));
90 result
= mfree(result
);
92 r
= chase(p
, temp
, 0, &result
, NULL
);
93 assert_se(r
== -ENOENT
);
95 r
= chase(pslash
, temp
, 0, &result
, NULL
);
96 assert_se(r
== -ENOENT
);
98 q
= strjoina(temp
, "/usr");
100 r
= chase(p
, temp
, CHASE_NONEXISTENT
, &result
, NULL
);
102 assert_se(path_equal(result
, q
));
103 result
= mfree(result
);
105 qslash
= strjoina(q
, "/");
107 r
= chase(pslash
, temp
, CHASE_NONEXISTENT
, &result
, NULL
);
109 assert_se(path_equal(result
, qslash
));
110 result
= mfree(result
);
112 ASSERT_OK(mkdir(q
, 0700));
114 r
= chase(p
, temp
, 0, &result
, NULL
);
116 assert_se(path_equal(result
, q
));
117 result
= mfree(result
);
119 r
= chase(pslash
, temp
, 0, &result
, NULL
);
121 assert_se(path_equal(result
, qslash
));
122 result
= mfree(result
);
124 p
= strjoina(temp
, "/slash");
125 assert_se(symlink("/", p
) >= 0);
127 r
= chase(p
, NULL
, 0, &result
, NULL
);
129 assert_se(path_equal(result
, "/"));
130 result
= mfree(result
);
132 r
= chase(p
, temp
, 0, &result
, NULL
);
134 assert_se(path_equal(result
, temp
));
135 result
= mfree(result
);
137 /* Tests for CHASE_EXTRACT_FILENAME and chase_extract_filename() */
139 p
= strjoina(temp
, "/start");
140 pslash
= strjoina(p
, "/");
141 test_chase_extract_filename_one(p
, NULL
, "usr");
142 test_chase_extract_filename_one(pslash
, NULL
, "usr");
143 test_chase_extract_filename_one(p
, temp
, "usr");
144 test_chase_extract_filename_one(pslash
, temp
, "usr");
146 p
= strjoina(temp
, "/slash");
147 test_chase_extract_filename_one(p
, NULL
, ".");
148 test_chase_extract_filename_one(p
, temp
, ".");
150 /* Paths that would "escape" outside of the "root" */
152 p
= strjoina(temp
, "/6dots");
153 ASSERT_OK(symlink("../../..", p
));
155 r
= chase(p
, temp
, 0, &result
, NULL
);
156 assert_se(r
> 0 && path_equal(result
, temp
));
157 result
= mfree(result
);
159 p
= strjoina(temp
, "/6dotsusr");
160 ASSERT_OK(symlink("../../../usr", p
));
162 r
= chase(p
, temp
, 0, &result
, NULL
);
163 assert_se(r
> 0 && path_equal(result
, q
));
164 result
= mfree(result
);
166 p
= strjoina(temp
, "/top/8dotsusr");
167 ASSERT_OK(symlink("../../../../usr", p
));
169 r
= chase(p
, temp
, 0, &result
, NULL
);
170 assert_se(r
> 0 && path_equal(result
, q
));
171 result
= mfree(result
);
173 /* Paths that contain repeated slashes */
175 p
= strjoina(temp
, "/slashslash");
176 ASSERT_OK(symlink("///usr///", p
));
178 r
= chase(p
, NULL
, 0, &result
, NULL
);
180 assert_se(path_equal(result
, "/usr"));
181 ASSERT_STREQ(result
, "/usr"); /* we guarantee that we drop redundant slashes */
182 result
= mfree(result
);
184 r
= chase(p
, temp
, 0, &result
, NULL
);
186 assert_se(path_equal(result
, q
));
187 result
= mfree(result
);
189 /* Paths underneath the "root" with different UIDs while using CHASE_SAFE */
191 if (geteuid() == 0 && !userns_has_single_user()) {
192 p
= strjoina(temp
, "/user");
193 ASSERT_OK(mkdir(p
, 0755));
194 ASSERT_OK(chown(p
, UID_NOBODY
, GID_NOBODY
));
196 q
= strjoina(temp
, "/user/root");
197 ASSERT_OK(mkdir(q
, 0755));
199 p
= strjoina(q
, "/link");
200 ASSERT_OK(symlink("/", p
));
202 /* Fail when user-owned directories contain root-owned subdirectories. */
203 r
= chase(p
, temp
, CHASE_SAFE
, &result
, NULL
);
204 assert_se(r
== -ENOLINK
);
205 result
= mfree(result
);
207 /* Allow this when the user-owned directories are all in the "root". */
208 r
= chase(p
, q
, CHASE_SAFE
, &result
, NULL
);
210 result
= mfree(result
);
215 r
= chase("/etc/./.././", NULL
, 0, &result
, NULL
);
217 assert_se(path_equal(result
, "/"));
218 result
= mfree(result
);
220 r
= chase("/etc/./.././", "/etc", 0, &result
, NULL
);
221 assert_se(r
> 0 && path_equal(result
, "/etc"));
222 result
= mfree(result
);
224 r
= chase("/../.././//../../etc", NULL
, 0, &result
, NULL
);
226 ASSERT_STREQ(result
, "/etc");
227 result
= mfree(result
);
229 r
= chase("/../.././//../../test-chase.fsldajfl", NULL
, CHASE_NONEXISTENT
, &result
, NULL
);
231 ASSERT_STREQ(result
, "/test-chase.fsldajfl");
232 result
= mfree(result
);
234 r
= chase("/../.././//../../etc", "/", CHASE_PREFIX_ROOT
, &result
, NULL
);
236 ASSERT_STREQ(result
, "/etc");
237 result
= mfree(result
);
239 r
= chase("/../.././//../../test-chase.fsldajfl", "/", CHASE_PREFIX_ROOT
|CHASE_NONEXISTENT
, &result
, NULL
);
241 ASSERT_STREQ(result
, "/test-chase.fsldajfl");
242 result
= mfree(result
);
244 r
= chase("/.path/with/dot", temp
, CHASE_PREFIX_ROOT
|CHASE_NONEXISTENT
, &result
, NULL
);
246 q
= strjoina(temp
, "/.path/with/dot");
247 ASSERT_STREQ(result
, q
);
248 result
= mfree(result
);
250 r
= chase("/etc/machine-id/foo", NULL
, 0, &result
, NULL
);
251 assert_se(IN_SET(r
, -ENOTDIR
, -ENOENT
));
252 result
= mfree(result
);
254 /* Path that loops back to self */
256 p
= strjoina(temp
, "/recursive-symlink");
257 ASSERT_OK(symlink("recursive-symlink", p
));
258 r
= chase(p
, NULL
, 0, &result
, NULL
);
259 assert_se(r
== -ELOOP
);
261 /* Path which doesn't exist */
263 p
= strjoina(temp
, "/idontexist");
264 r
= chase(p
, NULL
, 0, &result
, NULL
);
265 assert_se(r
== -ENOENT
);
267 r
= chase(p
, NULL
, CHASE_NONEXISTENT
, &result
, NULL
);
269 assert_se(path_equal(result
, p
));
270 result
= mfree(result
);
272 p
= strjoina(temp
, "/idontexist/meneither");
273 r
= chase(p
, NULL
, 0, &result
, NULL
);
274 assert_se(r
== -ENOENT
);
276 r
= chase(p
, NULL
, CHASE_NONEXISTENT
, &result
, NULL
);
278 assert_se(path_equal(result
, p
));
279 result
= mfree(result
);
283 ASSERT_OK(safe_getcwd(&pwd
));
285 ASSERT_OK(chdir(temp
));
287 p
= "this/is/a/relative/path";
288 r
= chase(p
, NULL
, CHASE_NONEXISTENT
, &result
, NULL
);
291 p
= strjoina(temp
, "/", p
);
292 assert_se(path_equal(result
, p
));
293 result
= mfree(result
);
295 p
= "this/is/a/relative/path";
296 r
= chase(p
, temp
, CHASE_NONEXISTENT
, &result
, NULL
);
299 p
= strjoina(temp
, "/", p
);
300 assert_se(path_equal(result
, p
));
301 result
= mfree(result
);
303 assert_se(chdir(pwd
) >= 0);
305 /* Path which doesn't exist, but contains weird stuff */
307 p
= strjoina(temp
, "/idontexist/..");
308 r
= chase(p
, NULL
, 0, &result
, NULL
);
309 assert_se(r
== -ENOENT
);
311 r
= chase(p
, NULL
, CHASE_NONEXISTENT
, &result
, NULL
);
312 assert_se(r
== -ENOENT
);
314 p
= strjoina(temp
, "/target");
315 q
= strjoina(temp
, "/top");
316 assert_se(symlink(q
, p
) >= 0);
317 p
= strjoina(temp
, "/target/idontexist");
318 r
= chase(p
, NULL
, 0, &result
, NULL
);
319 assert_se(r
== -ENOENT
);
321 if (geteuid() == 0 && !userns_has_single_user()) {
322 p
= strjoina(temp
, "/priv1");
323 ASSERT_OK(mkdir(p
, 0755));
325 q
= strjoina(p
, "/priv2");
326 ASSERT_OK(mkdir(q
, 0755));
328 ASSERT_OK(chase(q
, NULL
, CHASE_SAFE
, NULL
, NULL
));
330 ASSERT_OK(chown(q
, UID_NOBODY
, GID_NOBODY
));
331 ASSERT_OK(chase(q
, NULL
, CHASE_SAFE
, NULL
, NULL
));
333 ASSERT_OK(chown(p
, UID_NOBODY
, GID_NOBODY
));
334 ASSERT_OK(chase(q
, NULL
, CHASE_SAFE
, NULL
, NULL
));
336 assert_se(chown(q
, 0, 0) >= 0);
337 assert_se(chase(q
, NULL
, CHASE_SAFE
, NULL
, NULL
) == -ENOLINK
);
340 ASSERT_OK(symlink("/etc/passwd", q
));
341 assert_se(chase(q
, NULL
, CHASE_SAFE
, NULL
, NULL
) == -ENOLINK
);
343 assert_se(chown(p
, 0, 0) >= 0);
344 ASSERT_OK(chase(q
, NULL
, CHASE_SAFE
, NULL
, NULL
));
347 p
= strjoina(temp
, "/machine-id-test");
348 ASSERT_OK(symlink("/usr/../etc/./machine-id", p
));
350 r
= chase(p
, NULL
, 0, NULL
, &pfd
);
351 if (r
!= -ENOENT
&& sd_id128_get_machine(NULL
) >= 0) {
352 _cleanup_close_
int fd
= -EBADF
;
357 fd
= fd_reopen(pfd
, O_RDONLY
|O_CLOEXEC
);
361 ASSERT_OK(id128_read_fd(fd
, ID128_FORMAT_PLAIN
, &a
));
362 ASSERT_OK(sd_id128_get_machine(&b
));
363 assert_se(sd_id128_equal(a
, b
));
366 assert_se(lstat(p
, &st
) >= 0);
367 r
= chase_and_unlink(p
, NULL
, 0, 0, &result
);
369 assert_se(path_equal(result
, p
));
370 result
= mfree(result
);
371 assert_se(lstat(p
, &st
) == -1 && errno
== ENOENT
);
373 /* Test CHASE_NOFOLLOW */
375 p
= strjoina(temp
, "/target");
376 q
= strjoina(temp
, "/symlink");
377 assert_se(symlink(p
, q
) >= 0);
378 r
= chase(q
, NULL
, CHASE_NOFOLLOW
, &result
, &pfd
);
381 assert_se(path_equal(result
, q
));
382 ASSERT_OK(fstat(pfd
, &st
));
383 assert_se(S_ISLNK(st
.st_mode
));
384 result
= mfree(result
);
385 pfd
= safe_close(pfd
);
387 /* s1 -> s2 -> nonexistent */
388 q
= strjoina(temp
, "/s1");
389 ASSERT_OK(symlink("s2", q
));
390 p
= strjoina(temp
, "/s2");
391 ASSERT_OK(symlink("nonexistent", p
));
392 r
= chase(q
, NULL
, CHASE_NOFOLLOW
, &result
, &pfd
);
395 assert_se(path_equal(result
, q
));
396 ASSERT_OK(fstat(pfd
, &st
));
397 assert_se(S_ISLNK(st
.st_mode
));
398 result
= mfree(result
);
399 pfd
= safe_close(pfd
);
401 /* Test CHASE_STEP */
403 p
= strjoina(temp
, "/start");
404 r
= chase(p
, NULL
, CHASE_STEP
, &result
, NULL
);
406 p
= strjoina(temp
, "/top/dot/dotdota");
407 ASSERT_STREQ(p
, result
);
408 result
= mfree(result
);
410 r
= chase(p
, NULL
, CHASE_STEP
, &result
, NULL
);
412 p
= strjoina(temp
, "/top/dotdota");
413 ASSERT_STREQ(p
, result
);
414 result
= mfree(result
);
416 r
= chase(p
, NULL
, CHASE_STEP
, &result
, NULL
);
418 p
= strjoina(temp
, "/top/../a");
419 ASSERT_STREQ(p
, result
);
420 result
= mfree(result
);
422 r
= chase(p
, NULL
, CHASE_STEP
, &result
, NULL
);
424 p
= strjoina(temp
, "/a");
425 ASSERT_STREQ(p
, result
);
426 result
= mfree(result
);
428 r
= chase(p
, NULL
, CHASE_STEP
, &result
, NULL
);
430 p
= strjoina(temp
, "/b");
431 ASSERT_STREQ(p
, result
);
432 result
= mfree(result
);
434 r
= chase(p
, NULL
, CHASE_STEP
, &result
, NULL
);
436 ASSERT_STREQ("/usr", result
);
437 result
= mfree(result
);
439 r
= chase("/usr", NULL
, CHASE_STEP
, &result
, NULL
);
441 ASSERT_STREQ("/usr", result
);
442 result
= mfree(result
);
444 /* Make sure that symlinks in the "root" path are not resolved, but those below are */
445 p
= strjoina("/etc/..", temp
, "/self");
446 assert_se(symlink(".", p
) >= 0);
447 q
= strjoina(p
, "/top/dot/dotdota");
448 r
= chase(q
, p
, 0, &result
, NULL
);
450 assert_se(path_equal(path_startswith(result
, p
), "usr"));
451 result
= mfree(result
);
453 /* Test CHASE_PROHIBIT_SYMLINKS */
455 assert_se(chase("top/dot", temp
, CHASE_PREFIX_ROOT
|CHASE_PROHIBIT_SYMLINKS
, NULL
, NULL
) == -EREMCHG
);
456 assert_se(chase("top/dot", temp
, CHASE_PREFIX_ROOT
|CHASE_PROHIBIT_SYMLINKS
|CHASE_WARN
, NULL
, NULL
) == -EREMCHG
);
457 assert_se(chase("top/dotdot", temp
, CHASE_PREFIX_ROOT
|CHASE_PROHIBIT_SYMLINKS
, NULL
, NULL
) == -EREMCHG
);
458 assert_se(chase("top/dotdot", temp
, CHASE_PREFIX_ROOT
|CHASE_PROHIBIT_SYMLINKS
|CHASE_WARN
, NULL
, NULL
) == -EREMCHG
);
459 assert_se(chase("top/dot/dot", temp
, CHASE_PREFIX_ROOT
|CHASE_PROHIBIT_SYMLINKS
, NULL
, NULL
) == -EREMCHG
);
460 assert_se(chase("top/dot/dot", temp
, CHASE_PREFIX_ROOT
|CHASE_PROHIBIT_SYMLINKS
|CHASE_WARN
, NULL
, NULL
) == -EREMCHG
);
463 ASSERT_OK(rm_rf(temp
, REMOVE_ROOT
|REMOVE_PHYSICAL
));
467 _cleanup_(rm_rf_physical_and_freep
) char *t
= NULL
;
468 _cleanup_close_
int tfd
= -EBADF
, fd
= -EBADF
;
469 _cleanup_free_
char *result
= NULL
;
470 _cleanup_closedir_
DIR *dir
= NULL
;
471 _cleanup_fclose_
FILE *f
= NULL
;
475 ASSERT_OK(tfd
= mkdtemp_open(NULL
, 0, &t
));
477 /* Test that AT_FDCWD with CHASE_AT_RESOLVE_IN_ROOT resolves against / and not the current working
480 ASSERT_OK(symlinkat("/usr", tfd
, "abc"));
482 p
= strjoina(t
, "/abc");
483 ASSERT_OK(chaseat(AT_FDCWD
, p
, CHASE_AT_RESOLVE_IN_ROOT
, &result
, NULL
));
484 ASSERT_STREQ(result
, "/usr");
485 result
= mfree(result
);
487 /* If the file descriptor points to the root directory, the result will be absolute. */
489 fd
= open("/", O_CLOEXEC
| O_DIRECTORY
| O_PATH
);
492 ASSERT_OK(chaseat(fd
, p
, 0, &result
, NULL
));
493 ASSERT_STREQ(result
, "/usr");
494 result
= mfree(result
);
496 ASSERT_OK(chaseat(fd
, p
, CHASE_AT_RESOLVE_IN_ROOT
, &result
, NULL
));
497 ASSERT_STREQ(result
, "/usr");
498 result
= mfree(result
);
502 /* If the file descriptor does not point to the root directory, the result will be relative
503 * unless the result is outside of the specified file descriptor. */
505 ASSERT_OK(chaseat(tfd
, "abc", 0, &result
, NULL
));
506 ASSERT_STREQ(result
, "/usr");
507 result
= mfree(result
);
509 ASSERT_OK(chaseat(tfd
, "/abc", 0, &result
, NULL
));
510 ASSERT_STREQ(result
, "/usr");
511 result
= mfree(result
);
513 assert_se(chaseat(tfd
, "abc", CHASE_AT_RESOLVE_IN_ROOT
, NULL
, NULL
) == -ENOENT
);
514 assert_se(chaseat(tfd
, "/abc", CHASE_AT_RESOLVE_IN_ROOT
, NULL
, NULL
) == -ENOENT
);
516 ASSERT_OK(chaseat(tfd
, "abc", CHASE_AT_RESOLVE_IN_ROOT
| CHASE_NONEXISTENT
, &result
, NULL
));
517 ASSERT_STREQ(result
, "usr");
518 result
= mfree(result
);
520 ASSERT_OK(chaseat(tfd
, "/abc", CHASE_AT_RESOLVE_IN_ROOT
| CHASE_NONEXISTENT
, &result
, NULL
));
521 ASSERT_STREQ(result
, "usr");
522 result
= mfree(result
);
524 /* Test that absolute path or not are the same when resolving relative to a directory file
525 * descriptor and that we always get a relative path back. */
527 ASSERT_OK(fd
= openat(tfd
, "def", O_CREAT
|O_CLOEXEC
, 0700));
529 ASSERT_OK(symlinkat("/def", tfd
, "qed"));
530 ASSERT_OK(chaseat(tfd
, "qed", CHASE_AT_RESOLVE_IN_ROOT
, &result
, NULL
));
531 ASSERT_STREQ(result
, "def");
532 result
= mfree(result
);
533 ASSERT_OK(chaseat(tfd
, "/qed", CHASE_AT_RESOLVE_IN_ROOT
, &result
, NULL
));
534 ASSERT_STREQ(result
, "def");
535 result
= mfree(result
);
537 /* Valid directory file descriptor without CHASE_AT_RESOLVE_IN_ROOT should resolve symlinks against
539 assert_se(chaseat(tfd
, "/qed", 0, NULL
, NULL
) == -ENOENT
);
541 /* Test CHASE_PARENT */
543 ASSERT_OK(fd
= open_mkdir_at(tfd
, "chase", O_CLOEXEC
, 0755));
544 ASSERT_OK(symlinkat("/def", fd
, "parent"));
547 /* Make sure that when we chase a symlink parent directory, that we chase the parent directory of the
548 * symlink target and not the symlink itself. But if we add CHASE_NOFOLLOW, we get the parent
549 * directory of the symlink itself. */
551 ASSERT_OK(chaseat(tfd
, "chase/parent", CHASE_PARENT
|CHASE_AT_RESOLVE_IN_ROOT
, &result
, &fd
));
552 ASSERT_OK(faccessat(fd
, "def", F_OK
, 0));
553 ASSERT_STREQ(result
, "def");
555 result
= mfree(result
);
557 ASSERT_OK(chaseat(tfd
, "chase/parent", CHASE_AT_RESOLVE_IN_ROOT
|CHASE_PARENT
|CHASE_NOFOLLOW
, &result
, &fd
));
558 ASSERT_OK(faccessat(fd
, "parent", F_OK
, AT_SYMLINK_NOFOLLOW
));
559 ASSERT_STREQ(result
, "chase/parent");
561 result
= mfree(result
);
563 ASSERT_OK(chaseat(tfd
, "chase", CHASE_PARENT
|CHASE_AT_RESOLVE_IN_ROOT
, &result
, &fd
));
564 ASSERT_OK(faccessat(fd
, "chase", F_OK
, 0));
565 ASSERT_STREQ(result
, "chase");
567 result
= mfree(result
);
569 ASSERT_OK(chaseat(tfd
, "/", CHASE_PARENT
|CHASE_AT_RESOLVE_IN_ROOT
, &result
, NULL
));
570 ASSERT_STREQ(result
, ".");
571 result
= mfree(result
);
573 assert_se(chaseat(tfd
, ".", CHASE_PARENT
|CHASE_AT_RESOLVE_IN_ROOT
, &result
, NULL
));
574 ASSERT_STREQ(result
, ".");
575 result
= mfree(result
);
577 /* Test CHASE_MKDIR_0755 */
579 ASSERT_OK(chaseat(tfd
, "m/k/d/i/r", CHASE_MKDIR_0755
|CHASE_NONEXISTENT
, &result
, NULL
));
580 ASSERT_OK(faccessat(tfd
, "m/k/d/i", F_OK
, 0));
581 assert_se(RET_NERRNO(faccessat(tfd
, "m/k/d/i/r", F_OK
, 0)) == -ENOENT
);
582 ASSERT_STREQ(result
, "m/k/d/i/r");
583 result
= mfree(result
);
585 ASSERT_OK(chaseat(tfd
, "m/../q", CHASE_MKDIR_0755
|CHASE_NONEXISTENT
, &result
, NULL
));
586 ASSERT_OK(faccessat(tfd
, "m", F_OK
, 0));
587 assert_se(RET_NERRNO(faccessat(tfd
, "q", F_OK
, 0)) == -ENOENT
);
588 ASSERT_STREQ(result
, "q");
589 result
= mfree(result
);
591 assert_se(chaseat(tfd
, "i/../p", CHASE_MKDIR_0755
|CHASE_NONEXISTENT
, NULL
, NULL
) == -ENOENT
);
593 /* Test CHASE_EXTRACT_FILENAME */
595 ASSERT_OK(chaseat(tfd
, "chase/parent", CHASE_AT_RESOLVE_IN_ROOT
|CHASE_PARENT
|CHASE_NOFOLLOW
|CHASE_EXTRACT_FILENAME
, &result
, &fd
));
596 ASSERT_OK(faccessat(fd
, result
, F_OK
, AT_SYMLINK_NOFOLLOW
));
597 ASSERT_STREQ(result
, "parent");
599 result
= mfree(result
);
601 ASSERT_OK(chaseat(tfd
, "chase", CHASE_PARENT
|CHASE_AT_RESOLVE_IN_ROOT
|CHASE_EXTRACT_FILENAME
, &result
, &fd
));
602 ASSERT_OK(faccessat(fd
, result
, F_OK
, 0));
603 ASSERT_STREQ(result
, "chase");
605 result
= mfree(result
);
607 ASSERT_OK(chaseat(tfd
, "/", CHASE_PARENT
|CHASE_AT_RESOLVE_IN_ROOT
|CHASE_EXTRACT_FILENAME
, &result
, NULL
));
608 ASSERT_STREQ(result
, ".");
609 result
= mfree(result
);
611 ASSERT_OK(chaseat(tfd
, ".", CHASE_PARENT
|CHASE_AT_RESOLVE_IN_ROOT
|CHASE_EXTRACT_FILENAME
, &result
, NULL
));
612 ASSERT_STREQ(result
, ".");
613 result
= mfree(result
);
615 ASSERT_OK(chaseat(tfd
, NULL
, CHASE_PARENT
|CHASE_AT_RESOLVE_IN_ROOT
|CHASE_EXTRACT_FILENAME
, &result
, NULL
));
616 ASSERT_STREQ(result
, ".");
617 result
= mfree(result
);
619 /* Test chase_and_openat() */
621 fd
= chase_and_openat(tfd
, "o/p/e/n/f/i/l/e", CHASE_MKDIR_0755
, O_CREAT
|O_EXCL
|O_CLOEXEC
, NULL
);
623 ASSERT_OK(fd_verify_regular(fd
));
626 fd
= chase_and_openat(tfd
, "o/p/e/n/d/i/r", CHASE_MKDIR_0755
, O_DIRECTORY
|O_CREAT
|O_EXCL
|O_CLOEXEC
, NULL
);
628 ASSERT_OK(fd_verify_directory(fd
));
631 fd
= chase_and_openat(tfd
, NULL
, CHASE_PARENT
|CHASE_EXTRACT_FILENAME
, O_PATH
|O_DIRECTORY
|O_CLOEXEC
, &result
);
633 ASSERT_STREQ(result
, ".");
635 result
= mfree(result
);
637 /* Test chase_and_openatdir() */
639 ASSERT_OK(chase_and_opendirat(tfd
, "o/p/e/n/d/i", 0, &result
, &dir
));
640 FOREACH_DIRENT(de
, dir
, assert_not_reached())
641 ASSERT_STREQ(de
->d_name
, "r");
642 ASSERT_STREQ(result
, "o/p/e/n/d/i");
643 result
= mfree(result
);
645 /* Test chase_and_statat() */
647 ASSERT_OK(chase_and_statat(tfd
, "o/p", 0, &result
, &st
));
648 ASSERT_OK(stat_verify_directory(&st
));
649 ASSERT_STREQ(result
, "o/p");
650 result
= mfree(result
);
652 /* Test chase_and_accessat() */
654 ASSERT_OK(chase_and_accessat(tfd
, "o/p/e", 0, F_OK
, &result
));
655 ASSERT_STREQ(result
, "o/p/e");
656 result
= mfree(result
);
658 /* Test chase_and_fopenat_unlocked() */
660 ASSERT_OK(chase_and_fopenat_unlocked(tfd
, "o/p/e/n/f/i/l/e", 0, "re", &result
, &f
));
661 assert_se(fread(&(char[1]) {}, 1, 1, f
) == 0);
664 ASSERT_STREQ(result
, "o/p/e/n/f/i/l/e");
665 result
= mfree(result
);
667 /* Test chase_and_unlinkat() */
669 ASSERT_OK(chase_and_unlinkat(tfd
, "o/p/e/n/f/i/l/e", 0, 0, &result
));
670 ASSERT_STREQ(result
, "o/p/e/n/f/i/l/e");
671 result
= mfree(result
);
673 /* Test chase_and_open_parent_at() */
675 ASSERT_OK(fd
= chase_and_open_parent_at(tfd
, "chase/parent", CHASE_AT_RESOLVE_IN_ROOT
|CHASE_NOFOLLOW
, &result
));
676 ASSERT_OK(faccessat(fd
, result
, F_OK
, AT_SYMLINK_NOFOLLOW
));
677 ASSERT_STREQ(result
, "parent");
679 result
= mfree(result
);
681 ASSERT_OK(fd
= chase_and_open_parent_at(tfd
, "chase", CHASE_AT_RESOLVE_IN_ROOT
, &result
));
682 ASSERT_OK(faccessat(fd
, result
, F_OK
, 0));
683 ASSERT_STREQ(result
, "chase");
685 result
= mfree(result
);
687 ASSERT_OK(fd
= chase_and_open_parent_at(tfd
, "/", CHASE_AT_RESOLVE_IN_ROOT
, &result
));
688 ASSERT_STREQ(result
, ".");
690 result
= mfree(result
);
692 ASSERT_OK(fd
= chase_and_open_parent_at(tfd
, ".", CHASE_AT_RESOLVE_IN_ROOT
, &result
));
693 ASSERT_STREQ(result
, ".");
695 result
= mfree(result
);
698 TEST(chaseat_prefix_root
) {
699 _cleanup_free_
char *cwd
= NULL
, *ret
= NULL
, *expected
= NULL
;
701 ASSERT_OK(safe_getcwd(&cwd
));
703 ASSERT_OK(chaseat_prefix_root("/hoge", NULL
, &ret
));
704 ASSERT_STREQ(ret
, "/hoge");
708 ASSERT_OK(chaseat_prefix_root("/hoge", "a/b/c", &ret
));
709 ASSERT_STREQ(ret
, "/hoge");
713 ASSERT_OK(chaseat_prefix_root("hoge", "/a/b//./c///", &ret
));
714 ASSERT_STREQ(ret
, "/a/b/c/hoge");
718 ASSERT_OK(chaseat_prefix_root("hoge", "a/b//./c///", &ret
));
719 assert_se(expected
= path_join(cwd
, "a/b/c/hoge"));
720 ASSERT_STREQ(ret
, expected
);
723 expected
= mfree(expected
);
725 ASSERT_OK(chaseat_prefix_root("./hoge/aaa/../././b", "/a/b//./c///", &ret
));
726 ASSERT_STREQ(ret
, "/a/b/c/hoge/aaa/../././b");
730 assert_se(chaseat_prefix_root("./hoge/aaa/../././b", "a/b//./c///", &ret
) >= 0);
731 assert_se(expected
= path_join(cwd
, "a/b/c/hoge/aaa/../././b"));
732 ASSERT_STREQ(ret
, expected
);
735 TEST(trailing_dot_dot
) {
736 _cleanup_free_
char *path
= NULL
, *fdpath
= NULL
;
737 _cleanup_close_
int fd
= -EBADF
;
739 ASSERT_OK(chase("/usr/..", NULL
, CHASE_PARENT
, &path
, &fd
));
740 assert_se(path_equal(path
, "/"));
741 ASSERT_OK(fd_get_path(fd
, &fdpath
));
742 assert_se(path_equal(fdpath
, "/"));
745 fdpath
= mfree(fdpath
);
748 _cleanup_(rm_rf_physical_and_freep
) char *t
= NULL
;
749 ASSERT_OK(mkdtemp_malloc(NULL
, &t
));
750 _cleanup_free_
char *sub
= ASSERT_PTR(path_join(t
, "a/b/c/d"));
751 ASSERT_OK(mkdir_p(sub
, 0700));
752 _cleanup_free_
char *suffixed
= ASSERT_PTR(path_join(sub
, ".."));
753 ASSERT_OK(chase(suffixed
, NULL
, CHASE_PARENT
, &path
, &fd
));
754 _cleanup_free_
char *expected1
= ASSERT_PTR(path_join(t
, "a/b/c"));
755 _cleanup_free_
char *expected2
= ASSERT_PTR(path_join(t
, "a/b"));
757 assert_se(path_equal(path
, expected1
));
758 ASSERT_OK(fd_get_path(fd
, &fdpath
));
759 assert_se(path_equal(fdpath
, expected2
));
762 TEST(use_chase_as_mkdir_p
) {
763 _cleanup_free_
char *p
= NULL
;
764 ASSERT_OK_ERRNO(asprintf(&p
, "/tmp/chasemkdir%" PRIu64
"/a/b/c", random_u64()));
766 _cleanup_close_
int fd
= -EBADF
;
767 ASSERT_OK(chase(p
, NULL
, CHASE_PREFIX_ROOT
|CHASE_MKDIR_0755
, NULL
, &fd
));
769 ASSERT_OK_EQ(inode_same_at(AT_FDCWD
, p
, fd
, NULL
, AT_EMPTY_PATH
), 1);
771 _cleanup_close_
int fd2
= -EBADF
;
772 ASSERT_OK(chase(p
, p
, CHASE_PREFIX_ROOT
|CHASE_MKDIR_0755
, NULL
, &fd2
));
774 _cleanup_free_
char *pp
= ASSERT_PTR(path_join(p
, p
));
776 ASSERT_OK_EQ(inode_same_at(AT_FDCWD
, pp
, fd2
, NULL
, AT_EMPTY_PATH
), 1);
778 _cleanup_free_
char *f
= NULL
;
779 ASSERT_OK(path_extract_directory(p
, &f
));
781 _cleanup_free_
char *ff
= NULL
;
782 ASSERT_OK(path_extract_directory(f
, &ff
));
784 _cleanup_free_
char *fff
= NULL
;
785 ASSERT_OK(path_extract_directory(ff
, &fff
));
787 ASSERT_OK(rm_rf(fff
, REMOVE_PHYSICAL
));
790 static int intro(void) {
791 arg_test_dir
= saved_argv
[1];
795 DEFINE_TEST_MAIN_WITH_INTRO(LOG_INFO
, intro
);