]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/test/test-chase.c
ASSERT_STREQ for simple cases
[thirdparty/systemd.git] / src / test / test-chase.c
CommitLineData
8f22fe32
YW
1/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3#include <unistd.h>
4
5#include "alloc-util.h"
6#include "chase.h"
7#include "dirent-util.h"
8#include "fd-util.h"
9#include "fs-util.h"
10#include "id128-util.h"
11#include "mkdir.h"
12#include "path-util.h"
13#include "rm-rf.h"
14#include "string-util.h"
15#include "tests.h"
16#include "tmpfile-util.h"
17
18static const char *arg_test_dir = NULL;
19
3991f35f
YW
20static void test_chase_extract_filename_one(const char *path, const char *root, const char *expected) {
21 _cleanup_free_ char *ret1 = NULL, *ret2 = NULL, *fname = NULL;
22
23 log_debug("/* %s(path=%s, root=%s) */", __func__, path, strnull(root));
24
25 assert_se(chase(path, root, CHASE_EXTRACT_FILENAME, &ret1, NULL) > 0);
c79e88b3 26 ASSERT_STREQ(ret1, expected);
3991f35f
YW
27
28 assert_se(chase(path, root, 0, &ret2, NULL) > 0);
f9d273e6
UA
29 ASSERT_OK(chase_extract_filename(ret2, root, &fname));
30 ASSERT_STREQ(fname, expected);
3991f35f
YW
31}
32
8f22fe32
YW
33TEST(chase) {
34 _cleanup_free_ char *result = NULL, *pwd = NULL;
35 _cleanup_close_ int pfd = -EBADF;
36 char *temp;
37 const char *top, *p, *pslash, *q, *qslash;
38 struct stat st;
39 int r;
40
41 temp = strjoina(arg_test_dir ?: "/tmp", "/test-chase.XXXXXX");
42 assert_se(mkdtemp(temp));
43
44 top = strjoina(temp, "/top");
f9d273e6 45 ASSERT_OK(mkdir(top, 0700));
8f22fe32
YW
46
47 p = strjoina(top, "/dot");
48 if (symlink(".", p) < 0) {
49 assert_se(IN_SET(errno, EINVAL, ENOSYS, ENOTTY, EPERM));
50 log_tests_skipped_errno(errno, "symlink() not possible");
51 goto cleanup;
52 };
53
54 p = strjoina(top, "/dotdot");
f9d273e6 55 ASSERT_OK(symlink("..", p));
8f22fe32
YW
56
57 p = strjoina(top, "/dotdota");
f9d273e6 58 ASSERT_OK(symlink("../a", p));
8f22fe32
YW
59
60 p = strjoina(temp, "/a");
f9d273e6 61 ASSERT_OK(symlink("b", p));
8f22fe32
YW
62
63 p = strjoina(temp, "/b");
f9d273e6 64 ASSERT_OK(symlink("/usr", p));
8f22fe32
YW
65
66 p = strjoina(temp, "/start");
f9d273e6 67 ASSERT_OK(symlink("top/dot/dotdota", p));
8f22fe32
YW
68
69 /* Paths that use symlinks underneath the "root" */
70
71 r = chase(p, NULL, 0, &result, NULL);
72 assert_se(r > 0);
73 assert_se(path_equal(result, "/usr"));
74 result = mfree(result);
75
83c57d8c
YW
76 r = chase(p, "/.//../../../", 0, &result, NULL);
77 assert_se(r > 0);
78 assert_se(path_equal(result, "/usr"));
79 result = mfree(result);
80
8f22fe32
YW
81 pslash = strjoina(p, "/");
82 r = chase(pslash, NULL, 0, &result, NULL);
83 assert_se(r > 0);
84 assert_se(path_equal(result, "/usr/"));
85 result = mfree(result);
86
87 r = chase(p, temp, 0, &result, NULL);
88 assert_se(r == -ENOENT);
89
90 r = chase(pslash, temp, 0, &result, NULL);
91 assert_se(r == -ENOENT);
92
93 q = strjoina(temp, "/usr");
94
95 r = chase(p, temp, CHASE_NONEXISTENT, &result, NULL);
96 assert_se(r == 0);
97 assert_se(path_equal(result, q));
98 result = mfree(result);
99
100 qslash = strjoina(q, "/");
101
102 r = chase(pslash, temp, CHASE_NONEXISTENT, &result, NULL);
103 assert_se(r == 0);
104 assert_se(path_equal(result, qslash));
105 result = mfree(result);
106
f9d273e6 107 ASSERT_OK(mkdir(q, 0700));
8f22fe32
YW
108
109 r = chase(p, temp, 0, &result, NULL);
110 assert_se(r > 0);
111 assert_se(path_equal(result, q));
112 result = mfree(result);
113
114 r = chase(pslash, temp, 0, &result, NULL);
115 assert_se(r > 0);
116 assert_se(path_equal(result, qslash));
117 result = mfree(result);
118
119 p = strjoina(temp, "/slash");
120 assert_se(symlink("/", p) >= 0);
121
122 r = chase(p, NULL, 0, &result, NULL);
123 assert_se(r > 0);
124 assert_se(path_equal(result, "/"));
125 result = mfree(result);
126
127 r = chase(p, temp, 0, &result, NULL);
128 assert_se(r > 0);
129 assert_se(path_equal(result, temp));
130 result = mfree(result);
131
3991f35f
YW
132 /* Tests for CHASE_EXTRACT_FILENAME and chase_extract_filename() */
133
134 p = strjoina(temp, "/start");
135 pslash = strjoina(p, "/");
136 test_chase_extract_filename_one(p, NULL, "usr");
137 test_chase_extract_filename_one(pslash, NULL, "usr");
138 test_chase_extract_filename_one(p, temp, "usr");
139 test_chase_extract_filename_one(pslash, temp, "usr");
140
141 p = strjoina(temp, "/slash");
142 test_chase_extract_filename_one(p, NULL, ".");
143 test_chase_extract_filename_one(p, temp, ".");
144
8f22fe32
YW
145 /* Paths that would "escape" outside of the "root" */
146
147 p = strjoina(temp, "/6dots");
f9d273e6 148 ASSERT_OK(symlink("../../..", p));
8f22fe32
YW
149
150 r = chase(p, temp, 0, &result, NULL);
151 assert_se(r > 0 && path_equal(result, temp));
152 result = mfree(result);
153
154 p = strjoina(temp, "/6dotsusr");
f9d273e6 155 ASSERT_OK(symlink("../../../usr", p));
8f22fe32
YW
156
157 r = chase(p, temp, 0, &result, NULL);
158 assert_se(r > 0 && path_equal(result, q));
159 result = mfree(result);
160
161 p = strjoina(temp, "/top/8dotsusr");
f9d273e6 162 ASSERT_OK(symlink("../../../../usr", p));
8f22fe32
YW
163
164 r = chase(p, temp, 0, &result, NULL);
165 assert_se(r > 0 && path_equal(result, q));
166 result = mfree(result);
167
168 /* Paths that contain repeated slashes */
169
170 p = strjoina(temp, "/slashslash");
f9d273e6 171 ASSERT_OK(symlink("///usr///", p));
8f22fe32
YW
172
173 r = chase(p, NULL, 0, &result, NULL);
174 assert_se(r > 0);
175 assert_se(path_equal(result, "/usr"));
c79e88b3 176 ASSERT_STREQ(result, "/usr"); /* we guarantee that we drop redundant slashes */
8f22fe32
YW
177 result = mfree(result);
178
179 r = chase(p, temp, 0, &result, NULL);
180 assert_se(r > 0);
181 assert_se(path_equal(result, q));
182 result = mfree(result);
183
184 /* Paths underneath the "root" with different UIDs while using CHASE_SAFE */
185
186 if (geteuid() == 0) {
187 p = strjoina(temp, "/user");
f9d273e6
UA
188 ASSERT_OK(mkdir(p, 0755));
189 ASSERT_OK(chown(p, UID_NOBODY, GID_NOBODY));
8f22fe32
YW
190
191 q = strjoina(temp, "/user/root");
f9d273e6 192 ASSERT_OK(mkdir(q, 0755));
8f22fe32
YW
193
194 p = strjoina(q, "/link");
f9d273e6 195 ASSERT_OK(symlink("/", p));
8f22fe32
YW
196
197 /* Fail when user-owned directories contain root-owned subdirectories. */
198 r = chase(p, temp, CHASE_SAFE, &result, NULL);
199 assert_se(r == -ENOLINK);
200 result = mfree(result);
201
202 /* Allow this when the user-owned directories are all in the "root". */
203 r = chase(p, q, CHASE_SAFE, &result, NULL);
204 assert_se(r > 0);
205 result = mfree(result);
206 }
207
208 /* Paths using . */
209
210 r = chase("/etc/./.././", NULL, 0, &result, NULL);
211 assert_se(r > 0);
212 assert_se(path_equal(result, "/"));
213 result = mfree(result);
214
215 r = chase("/etc/./.././", "/etc", 0, &result, NULL);
216 assert_se(r > 0 && path_equal(result, "/etc"));
217 result = mfree(result);
218
219 r = chase("/../.././//../../etc", NULL, 0, &result, NULL);
220 assert_se(r > 0);
f9d273e6 221 ASSERT_STREQ(result, "/etc");
8f22fe32
YW
222 result = mfree(result);
223
224 r = chase("/../.././//../../test-chase.fsldajfl", NULL, CHASE_NONEXISTENT, &result, NULL);
225 assert_se(r == 0);
f9d273e6 226 ASSERT_STREQ(result, "/test-chase.fsldajfl");
8f22fe32
YW
227 result = mfree(result);
228
229 r = chase("/../.././//../../etc", "/", CHASE_PREFIX_ROOT, &result, NULL);
230 assert_se(r > 0);
f9d273e6 231 ASSERT_STREQ(result, "/etc");
8f22fe32
YW
232 result = mfree(result);
233
234 r = chase("/../.././//../../test-chase.fsldajfl", "/", CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &result, NULL);
235 assert_se(r == 0);
f9d273e6 236 ASSERT_STREQ(result, "/test-chase.fsldajfl");
8f22fe32
YW
237 result = mfree(result);
238
239 r = chase("/etc/machine-id/foo", NULL, 0, &result, NULL);
240 assert_se(IN_SET(r, -ENOTDIR, -ENOENT));
241 result = mfree(result);
242
243 /* Path that loops back to self */
244
245 p = strjoina(temp, "/recursive-symlink");
f9d273e6 246 ASSERT_OK(symlink("recursive-symlink", p));
8f22fe32
YW
247 r = chase(p, NULL, 0, &result, NULL);
248 assert_se(r == -ELOOP);
249
250 /* Path which doesn't exist */
251
252 p = strjoina(temp, "/idontexist");
253 r = chase(p, NULL, 0, &result, NULL);
254 assert_se(r == -ENOENT);
255
256 r = chase(p, NULL, CHASE_NONEXISTENT, &result, NULL);
257 assert_se(r == 0);
258 assert_se(path_equal(result, p));
259 result = mfree(result);
260
261 p = strjoina(temp, "/idontexist/meneither");
262 r = chase(p, NULL, 0, &result, NULL);
263 assert_se(r == -ENOENT);
264
265 r = chase(p, NULL, CHASE_NONEXISTENT, &result, NULL);
266 assert_se(r == 0);
267 assert_se(path_equal(result, p));
268 result = mfree(result);
269
270 /* Relative paths */
271
f9d273e6 272 ASSERT_OK(safe_getcwd(&pwd));
8f22fe32 273
f9d273e6 274 ASSERT_OK(chdir(temp));
8f22fe32
YW
275
276 p = "this/is/a/relative/path";
277 r = chase(p, NULL, CHASE_NONEXISTENT, &result, NULL);
278 assert_se(r == 0);
279
280 p = strjoina(temp, "/", p);
281 assert_se(path_equal(result, p));
282 result = mfree(result);
283
284 p = "this/is/a/relative/path";
285 r = chase(p, temp, CHASE_NONEXISTENT, &result, NULL);
286 assert_se(r == 0);
287
288 p = strjoina(temp, "/", p);
289 assert_se(path_equal(result, p));
290 result = mfree(result);
291
292 assert_se(chdir(pwd) >= 0);
293
294 /* Path which doesn't exist, but contains weird stuff */
295
296 p = strjoina(temp, "/idontexist/..");
297 r = chase(p, NULL, 0, &result, NULL);
298 assert_se(r == -ENOENT);
299
300 r = chase(p, NULL, CHASE_NONEXISTENT, &result, NULL);
301 assert_se(r == -ENOENT);
302
303 p = strjoina(temp, "/target");
304 q = strjoina(temp, "/top");
305 assert_se(symlink(q, p) >= 0);
306 p = strjoina(temp, "/target/idontexist");
307 r = chase(p, NULL, 0, &result, NULL);
308 assert_se(r == -ENOENT);
309
310 if (geteuid() == 0) {
311 p = strjoina(temp, "/priv1");
f9d273e6 312 ASSERT_OK(mkdir(p, 0755));
8f22fe32
YW
313
314 q = strjoina(p, "/priv2");
f9d273e6 315 ASSERT_OK(mkdir(q, 0755));
8f22fe32 316
f9d273e6 317 ASSERT_OK(chase(q, NULL, CHASE_SAFE, NULL, NULL));
8f22fe32 318
f9d273e6
UA
319 ASSERT_OK(chown(q, UID_NOBODY, GID_NOBODY));
320 ASSERT_OK(chase(q, NULL, CHASE_SAFE, NULL, NULL));
8f22fe32 321
f9d273e6
UA
322 ASSERT_OK(chown(p, UID_NOBODY, GID_NOBODY));
323 ASSERT_OK(chase(q, NULL, CHASE_SAFE, NULL, NULL));
8f22fe32
YW
324
325 assert_se(chown(q, 0, 0) >= 0);
326 assert_se(chase(q, NULL, CHASE_SAFE, NULL, NULL) == -ENOLINK);
327
f9d273e6
UA
328 ASSERT_OK(rmdir(q));
329 ASSERT_OK(symlink("/etc/passwd", q));
8f22fe32
YW
330 assert_se(chase(q, NULL, CHASE_SAFE, NULL, NULL) == -ENOLINK);
331
332 assert_se(chown(p, 0, 0) >= 0);
f9d273e6 333 ASSERT_OK(chase(q, NULL, CHASE_SAFE, NULL, NULL));
8f22fe32
YW
334 }
335
336 p = strjoina(temp, "/machine-id-test");
f9d273e6 337 ASSERT_OK(symlink("/usr/../etc/./machine-id", p));
8f22fe32
YW
338
339 r = chase(p, NULL, 0, NULL, &pfd);
340 if (r != -ENOENT && sd_id128_get_machine(NULL) >= 0) {
341 _cleanup_close_ int fd = -EBADF;
342 sd_id128_t a, b;
343
f9d273e6 344 ASSERT_OK(pfd);
8f22fe32
YW
345
346 fd = fd_reopen(pfd, O_RDONLY|O_CLOEXEC);
f9d273e6 347 ASSERT_OK(fd);
8f22fe32
YW
348 safe_close(pfd);
349
f9d273e6
UA
350 ASSERT_OK(id128_read_fd(fd, ID128_FORMAT_PLAIN, &a));
351 ASSERT_OK(sd_id128_get_machine(&b));
8f22fe32
YW
352 assert_se(sd_id128_equal(a, b));
353 }
354
355 assert_se(lstat(p, &st) >= 0);
356 r = chase_and_unlink(p, NULL, 0, 0, &result);
357 assert_se(r == 0);
358 assert_se(path_equal(result, p));
359 result = mfree(result);
360 assert_se(lstat(p, &st) == -1 && errno == ENOENT);
361
362 /* Test CHASE_NOFOLLOW */
363
364 p = strjoina(temp, "/target");
365 q = strjoina(temp, "/symlink");
366 assert_se(symlink(p, q) >= 0);
367 r = chase(q, NULL, CHASE_NOFOLLOW, &result, &pfd);
f9d273e6
UA
368 ASSERT_OK(r);
369 ASSERT_OK(pfd);
8f22fe32 370 assert_se(path_equal(result, q));
f9d273e6 371 ASSERT_OK(fstat(pfd, &st));
8f22fe32
YW
372 assert_se(S_ISLNK(st.st_mode));
373 result = mfree(result);
374 pfd = safe_close(pfd);
375
376 /* s1 -> s2 -> nonexistent */
377 q = strjoina(temp, "/s1");
f9d273e6 378 ASSERT_OK(symlink("s2", q));
8f22fe32 379 p = strjoina(temp, "/s2");
f9d273e6 380 ASSERT_OK(symlink("nonexistent", p));
8f22fe32 381 r = chase(q, NULL, CHASE_NOFOLLOW, &result, &pfd);
f9d273e6
UA
382 ASSERT_OK(r);
383 ASSERT_OK(pfd);
8f22fe32 384 assert_se(path_equal(result, q));
f9d273e6 385 ASSERT_OK(fstat(pfd, &st));
8f22fe32
YW
386 assert_se(S_ISLNK(st.st_mode));
387 result = mfree(result);
388 pfd = safe_close(pfd);
389
390 /* Test CHASE_STEP */
391
392 p = strjoina(temp, "/start");
393 r = chase(p, NULL, CHASE_STEP, &result, NULL);
394 assert_se(r == 0);
395 p = strjoina(temp, "/top/dot/dotdota");
f9d273e6 396 ASSERT_STREQ(p, result);
8f22fe32
YW
397 result = mfree(result);
398
399 r = chase(p, NULL, CHASE_STEP, &result, NULL);
400 assert_se(r == 0);
401 p = strjoina(temp, "/top/dotdota");
f9d273e6 402 ASSERT_STREQ(p, result);
8f22fe32
YW
403 result = mfree(result);
404
405 r = chase(p, NULL, CHASE_STEP, &result, NULL);
406 assert_se(r == 0);
407 p = strjoina(temp, "/top/../a");
f9d273e6 408 ASSERT_STREQ(p, result);
8f22fe32
YW
409 result = mfree(result);
410
411 r = chase(p, NULL, CHASE_STEP, &result, NULL);
412 assert_se(r == 0);
413 p = strjoina(temp, "/a");
f9d273e6 414 ASSERT_STREQ(p, result);
8f22fe32
YW
415 result = mfree(result);
416
417 r = chase(p, NULL, CHASE_STEP, &result, NULL);
418 assert_se(r == 0);
419 p = strjoina(temp, "/b");
f9d273e6 420 ASSERT_STREQ(p, result);
8f22fe32
YW
421 result = mfree(result);
422
423 r = chase(p, NULL, CHASE_STEP, &result, NULL);
424 assert_se(r == 0);
f9d273e6 425 ASSERT_STREQ("/usr", result);
8f22fe32
YW
426 result = mfree(result);
427
428 r = chase("/usr", NULL, CHASE_STEP, &result, NULL);
429 assert_se(r > 0);
f9d273e6 430 ASSERT_STREQ("/usr", result);
8f22fe32
YW
431 result = mfree(result);
432
433 /* Make sure that symlinks in the "root" path are not resolved, but those below are */
434 p = strjoina("/etc/..", temp, "/self");
435 assert_se(symlink(".", p) >= 0);
436 q = strjoina(p, "/top/dot/dotdota");
437 r = chase(q, p, 0, &result, NULL);
438 assert_se(r > 0);
439 assert_se(path_equal(path_startswith(result, p), "usr"));
440 result = mfree(result);
441
442 /* Test CHASE_PROHIBIT_SYMLINKS */
443
444 assert_se(chase("top/dot", temp, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, NULL, NULL) == -EREMCHG);
445 assert_se(chase("top/dot", temp, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS|CHASE_WARN, NULL, NULL) == -EREMCHG);
446 assert_se(chase("top/dotdot", temp, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, NULL, NULL) == -EREMCHG);
447 assert_se(chase("top/dotdot", temp, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS|CHASE_WARN, NULL, NULL) == -EREMCHG);
448 assert_se(chase("top/dot/dot", temp, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, NULL, NULL) == -EREMCHG);
449 assert_se(chase("top/dot/dot", temp, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS|CHASE_WARN, NULL, NULL) == -EREMCHG);
450
451 cleanup:
f9d273e6 452 ASSERT_OK(rm_rf(temp, REMOVE_ROOT|REMOVE_PHYSICAL));
8f22fe32
YW
453}
454
455TEST(chaseat) {
456 _cleanup_(rm_rf_physical_and_freep) char *t = NULL;
457 _cleanup_close_ int tfd = -EBADF, fd = -EBADF;
458 _cleanup_free_ char *result = NULL;
459 _cleanup_closedir_ DIR *dir = NULL;
460 _cleanup_fclose_ FILE *f = NULL;
461 struct stat st;
462 const char *p;
463
f9d273e6 464 ASSERT_OK((tfd = mkdtemp_open(NULL, 0, &t)));
8f22fe32
YW
465
466 /* Test that AT_FDCWD with CHASE_AT_RESOLVE_IN_ROOT resolves against / and not the current working
467 * directory. */
468
f9d273e6 469 ASSERT_OK(symlinkat("/usr", tfd, "abc"));
8f22fe32
YW
470
471 p = strjoina(t, "/abc");
f9d273e6
UA
472 ASSERT_OK(chaseat(AT_FDCWD, p, CHASE_AT_RESOLVE_IN_ROOT, &result, NULL));
473 ASSERT_STREQ(result, "/usr");
8f22fe32
YW
474 result = mfree(result);
475
c0552b35
YW
476 /* If the file descriptor points to the root directory, the result will be absolute. */
477
478 fd = open("/", O_CLOEXEC | O_DIRECTORY | O_PATH);
f9d273e6 479 ASSERT_OK(fd);
c0552b35 480
f9d273e6
UA
481 ASSERT_OK(chaseat(fd, p, 0, &result, NULL));
482 ASSERT_STREQ(result, "/usr");
c0552b35
YW
483 result = mfree(result);
484
f9d273e6
UA
485 ASSERT_OK(chaseat(fd, p, CHASE_AT_RESOLVE_IN_ROOT, &result, NULL));
486 ASSERT_STREQ(result, "/usr");
c0552b35
YW
487 result = mfree(result);
488
489 fd = safe_close(fd);
490
24be89eb
YW
491 /* If the file descriptor does not point to the root directory, the result will be relative
492 * unless the result is outside of the specified file descriptor. */
493
f9d273e6
UA
494 ASSERT_OK(chaseat(tfd, "abc", 0, &result, NULL));
495 ASSERT_STREQ(result, "/usr");
24be89eb
YW
496 result = mfree(result);
497
f9d273e6
UA
498 ASSERT_OK(chaseat(tfd, "/abc", 0, &result, NULL));
499 ASSERT_STREQ(result, "/usr");
24be89eb 500 result = mfree(result);
c0552b35
YW
501
502 assert_se(chaseat(tfd, "abc", CHASE_AT_RESOLVE_IN_ROOT, NULL, NULL) == -ENOENT);
503 assert_se(chaseat(tfd, "/abc", CHASE_AT_RESOLVE_IN_ROOT, NULL, NULL) == -ENOENT);
504
f9d273e6
UA
505 ASSERT_OK(chaseat(tfd, "abc", CHASE_AT_RESOLVE_IN_ROOT | CHASE_NONEXISTENT, &result, NULL));
506 ASSERT_STREQ(result, "usr");
c0552b35
YW
507 result = mfree(result);
508
f9d273e6
UA
509 ASSERT_OK(chaseat(tfd, "/abc", CHASE_AT_RESOLVE_IN_ROOT | CHASE_NONEXISTENT, &result, NULL));
510 ASSERT_STREQ(result, "usr");
c0552b35
YW
511 result = mfree(result);
512
8f22fe32
YW
513 /* Test that absolute path or not are the same when resolving relative to a directory file
514 * descriptor and that we always get a relative path back. */
515
f9d273e6 516 ASSERT_OK(fd = openat(tfd, "def", O_CREAT|O_CLOEXEC, 0700));
8f22fe32 517 fd = safe_close(fd);
f9d273e6
UA
518 ASSERT_OK(symlinkat("/def", tfd, "qed"));
519 ASSERT_OK(chaseat(tfd, "qed", CHASE_AT_RESOLVE_IN_ROOT, &result, NULL));
520 ASSERT_STREQ(result, "def");
8f22fe32 521 result = mfree(result);
f9d273e6
UA
522 ASSERT_OK(chaseat(tfd, "/qed", CHASE_AT_RESOLVE_IN_ROOT, &result, NULL));
523 ASSERT_STREQ(result, "def");
8f22fe32
YW
524 result = mfree(result);
525
526 /* Valid directory file descriptor without CHASE_AT_RESOLVE_IN_ROOT should resolve symlinks against
527 * host's root. */
528 assert_se(chaseat(tfd, "/qed", 0, NULL, NULL) == -ENOENT);
529
530 /* Test CHASE_PARENT */
531
f9d273e6
UA
532 ASSERT_OK((fd = open_mkdir_at(tfd, "chase", O_CLOEXEC, 0755)));
533 ASSERT_OK(symlinkat("/def", fd, "parent"));
8f22fe32
YW
534 fd = safe_close(fd);
535
536 /* Make sure that when we chase a symlink parent directory, that we chase the parent directory of the
537 * symlink target and not the symlink itself. But if we add CHASE_NOFOLLOW, we get the parent
538 * directory of the symlink itself. */
539
f9d273e6
UA
540 ASSERT_OK(chaseat(tfd, "chase/parent", CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT, &result, &fd));
541 ASSERT_OK(faccessat(fd, "def", F_OK, 0));
542 ASSERT_STREQ(result, "def");
8f22fe32
YW
543 fd = safe_close(fd);
544 result = mfree(result);
545
f9d273e6
UA
546 ASSERT_OK(chaseat(tfd, "chase/parent", CHASE_AT_RESOLVE_IN_ROOT|CHASE_PARENT|CHASE_NOFOLLOW, &result, &fd));
547 ASSERT_OK(faccessat(fd, "parent", F_OK, AT_SYMLINK_NOFOLLOW));
548 ASSERT_STREQ(result, "chase/parent");
8f22fe32
YW
549 fd = safe_close(fd);
550 result = mfree(result);
551
f9d273e6
UA
552 ASSERT_OK(chaseat(tfd, "chase", CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT, &result, &fd));
553 ASSERT_OK(faccessat(fd, "chase", F_OK, 0));
554 ASSERT_STREQ(result, "chase");
8f22fe32
YW
555 fd = safe_close(fd);
556 result = mfree(result);
557
f9d273e6
UA
558 ASSERT_OK(chaseat(tfd, "/", CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT, &result, NULL));
559 ASSERT_STREQ(result, ".");
8f22fe32
YW
560 result = mfree(result);
561
f9d273e6
UA
562 assert_se(chaseat(tfd, ".", CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT, &result, NULL));
563 ASSERT_STREQ(result, ".");
8f22fe32
YW
564 result = mfree(result);
565
566 /* Test CHASE_MKDIR_0755 */
567
f9d273e6
UA
568 ASSERT_OK(chaseat(tfd, "m/k/d/i/r", CHASE_MKDIR_0755|CHASE_NONEXISTENT, &result, NULL));
569 ASSERT_OK(faccessat(tfd, "m/k/d/i", F_OK, 0));
8f22fe32 570 assert_se(RET_NERRNO(faccessat(tfd, "m/k/d/i/r", F_OK, 0)) == -ENOENT);
f9d273e6 571 ASSERT_STREQ(result, "m/k/d/i/r");
8f22fe32
YW
572 result = mfree(result);
573
f9d273e6
UA
574 ASSERT_OK(chaseat(tfd, "m/../q", CHASE_MKDIR_0755|CHASE_NONEXISTENT, &result, NULL));
575 ASSERT_OK(faccessat(tfd, "m", F_OK, 0));
8f22fe32 576 assert_se(RET_NERRNO(faccessat(tfd, "q", F_OK, 0)) == -ENOENT);
f9d273e6 577 ASSERT_STREQ(result, "q");
8f22fe32
YW
578 result = mfree(result);
579
4ea0bcb9 580 assert_se(chaseat(tfd, "i/../p", CHASE_MKDIR_0755|CHASE_NONEXISTENT, NULL, NULL) == -ENOENT);
8f22fe32 581
f5dc1adc 582 /* Test CHASE_EXTRACT_FILENAME */
8f22fe32 583
f9d273e6
UA
584 ASSERT_OK(chaseat(tfd, "chase/parent", CHASE_AT_RESOLVE_IN_ROOT|CHASE_PARENT|CHASE_NOFOLLOW|CHASE_EXTRACT_FILENAME, &result, &fd));
585 ASSERT_OK(faccessat(fd, result, F_OK, AT_SYMLINK_NOFOLLOW));
586 ASSERT_STREQ(result, "parent");
8f22fe32
YW
587 fd = safe_close(fd);
588 result = mfree(result);
589
f9d273e6
UA
590 ASSERT_OK(chaseat(tfd, "chase", CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT|CHASE_EXTRACT_FILENAME, &result, &fd));
591 ASSERT_OK(faccessat(fd, result, F_OK, 0));
592 ASSERT_STREQ(result, "chase");
8f22fe32
YW
593 fd = safe_close(fd);
594 result = mfree(result);
595
f9d273e6
UA
596 ASSERT_OK(chaseat(tfd, "/", CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT|CHASE_EXTRACT_FILENAME, &result, NULL));
597 ASSERT_STREQ(result, ".");
8f22fe32
YW
598 result = mfree(result);
599
f9d273e6
UA
600 ASSERT_OK(chaseat(tfd, ".", CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT|CHASE_EXTRACT_FILENAME, &result, NULL));
601 ASSERT_STREQ(result, ".");
8f22fe32
YW
602 result = mfree(result);
603
f9d273e6
UA
604 ASSERT_OK(chaseat(tfd, NULL, CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT|CHASE_EXTRACT_FILENAME, &result, NULL));
605 ASSERT_STREQ(result, ".");
3c8f449c
DDM
606 result = mfree(result);
607
8f22fe32
YW
608 /* Test chase_and_openat() */
609
610 fd = chase_and_openat(tfd, "o/p/e/n/f/i/l/e", CHASE_MKDIR_0755, O_CREAT|O_EXCL|O_CLOEXEC, NULL);
f9d273e6
UA
611 ASSERT_OK(fd);
612 ASSERT_OK(fd_verify_regular(fd));
8f22fe32
YW
613 fd = safe_close(fd);
614
615 fd = chase_and_openat(tfd, "o/p/e/n/d/i/r", CHASE_MKDIR_0755, O_DIRECTORY|O_CREAT|O_EXCL|O_CLOEXEC, NULL);
f9d273e6
UA
616 ASSERT_OK(fd);
617 ASSERT_OK(fd_verify_directory(fd));
8f22fe32
YW
618 fd = safe_close(fd);
619
8b85333c 620 fd = chase_and_openat(tfd, NULL, CHASE_PARENT|CHASE_EXTRACT_FILENAME, O_PATH|O_DIRECTORY|O_CLOEXEC, &result);
f9d273e6
UA
621 ASSERT_OK(fd);
622 ASSERT_STREQ(result, ".");
8b85333c
DDM
623 fd = safe_close(fd);
624 result = mfree(result);
625
8f22fe32
YW
626 /* Test chase_and_openatdir() */
627
f9d273e6 628 ASSERT_OK(chase_and_opendirat(tfd, "o/p/e/n/d/i", 0, &result, &dir));
8f22fe32 629 FOREACH_DIRENT(de, dir, assert_not_reached())
f9d273e6
UA
630 ASSERT_STREQ(de->d_name, "r");
631 ASSERT_STREQ(result, "o/p/e/n/d/i");
8f22fe32
YW
632 result = mfree(result);
633
634 /* Test chase_and_statat() */
635
f9d273e6
UA
636 ASSERT_OK(chase_and_statat(tfd, "o/p", 0, &result, &st));
637 ASSERT_OK(stat_verify_directory(&st));
638 ASSERT_STREQ(result, "o/p");
8f22fe32
YW
639 result = mfree(result);
640
641 /* Test chase_and_accessat() */
642
f9d273e6
UA
643 ASSERT_OK(chase_and_accessat(tfd, "o/p/e", 0, F_OK, &result));
644 ASSERT_STREQ(result, "o/p/e");
8f22fe32
YW
645 result = mfree(result);
646
647 /* Test chase_and_fopenat_unlocked() */
648
f9d273e6 649 ASSERT_OK(chase_and_fopenat_unlocked(tfd, "o/p/e/n/f/i/l/e", 0, "re", &result, &f));
8f22fe32
YW
650 assert_se(fread(&(char[1]) {}, 1, 1, f) == 0);
651 assert_se(feof(f));
652 f = safe_fclose(f);
f9d273e6 653 ASSERT_STREQ(result, "o/p/e/n/f/i/l/e");
8f22fe32
YW
654 result = mfree(result);
655
656 /* Test chase_and_unlinkat() */
657
f9d273e6
UA
658 ASSERT_OK(chase_and_unlinkat(tfd, "o/p/e/n/f/i/l/e", 0, 0, &result));
659 ASSERT_STREQ(result, "o/p/e/n/f/i/l/e");
8f22fe32
YW
660 result = mfree(result);
661
662 /* Test chase_and_open_parent_at() */
663
f9d273e6
UA
664 ASSERT_OK((fd = chase_and_open_parent_at(tfd, "chase/parent", CHASE_AT_RESOLVE_IN_ROOT|CHASE_NOFOLLOW, &result)));
665 ASSERT_OK(faccessat(fd, result, F_OK, AT_SYMLINK_NOFOLLOW));
666 ASSERT_STREQ(result, "parent");
8f22fe32
YW
667 fd = safe_close(fd);
668 result = mfree(result);
669
f9d273e6
UA
670 ASSERT_OK((fd = chase_and_open_parent_at(tfd, "chase", CHASE_AT_RESOLVE_IN_ROOT, &result)));
671 ASSERT_OK(faccessat(fd, result, F_OK, 0));
672 ASSERT_STREQ(result, "chase");
8f22fe32
YW
673 fd = safe_close(fd);
674 result = mfree(result);
675
f9d273e6
UA
676 ASSERT_OK((fd = chase_and_open_parent_at(tfd, "/", CHASE_AT_RESOLVE_IN_ROOT, &result)));
677 ASSERT_STREQ(result, ".");
8f22fe32
YW
678 fd = safe_close(fd);
679 result = mfree(result);
680
f9d273e6
UA
681 ASSERT_OK((fd = chase_and_open_parent_at(tfd, ".", CHASE_AT_RESOLVE_IN_ROOT, &result)));
682 ASSERT_STREQ(result, ".");
8f22fe32
YW
683 fd = safe_close(fd);
684 result = mfree(result);
685}
686
60e761d8
YW
687TEST(chaseat_prefix_root) {
688 _cleanup_free_ char *cwd = NULL, *ret = NULL, *expected = NULL;
689
f9d273e6 690 ASSERT_OK(safe_getcwd(&cwd));
60e761d8 691
f9d273e6
UA
692 ASSERT_OK(chaseat_prefix_root("/hoge", NULL, &ret));
693 ASSERT_STREQ(ret, "/hoge");
60e761d8
YW
694
695 ret = mfree(ret);
696
f9d273e6
UA
697 ASSERT_OK(chaseat_prefix_root("/hoge", "a/b/c", &ret));
698 ASSERT_STREQ(ret, "/hoge");
60e761d8
YW
699
700 ret = mfree(ret);
701
f9d273e6
UA
702 ASSERT_OK(chaseat_prefix_root("hoge", "/a/b//./c///", &ret));
703 ASSERT_STREQ(ret, "/a/b/c/hoge");
60e761d8
YW
704
705 ret = mfree(ret);
706
f9d273e6 707 ASSERT_OK(chaseat_prefix_root("hoge", "a/b//./c///", &ret));
60e761d8 708 assert_se(expected = path_join(cwd, "a/b/c/hoge"));
f9d273e6 709 ASSERT_STREQ(ret, expected);
60e761d8
YW
710
711 ret = mfree(ret);
712 expected = mfree(expected);
713
f9d273e6
UA
714 ASSERT_OK(chaseat_prefix_root("./hoge/aaa/../././b", "/a/b//./c///", &ret));
715 ASSERT_STREQ(ret, "/a/b/c/hoge/aaa/../././b");
60e761d8
YW
716
717 ret = mfree(ret);
718
719 assert_se(chaseat_prefix_root("./hoge/aaa/../././b", "a/b//./c///", &ret) >= 0);
720 assert_se(expected = path_join(cwd, "a/b/c/hoge/aaa/../././b"));
c79e88b3 721 ASSERT_STREQ(ret, expected);
60e761d8
YW
722}
723
9c21cfdd
LP
724TEST(trailing_dot_dot) {
725 _cleanup_free_ char *path = NULL, *fdpath = NULL;
726 _cleanup_close_ int fd = -EBADF;
727
f9d273e6 728 ASSERT_OK(chase("/usr/..", NULL, CHASE_PARENT, &path, &fd));
9c21cfdd 729 assert_se(path_equal(path, "/"));
f9d273e6 730 ASSERT_OK(fd_get_path(fd, &fdpath));
9c21cfdd
LP
731 assert_se(path_equal(fdpath, "/"));
732
733 path = mfree(path);
734 fdpath = mfree(fdpath);
735 fd = safe_close(fd);
736
737 _cleanup_(rm_rf_physical_and_freep) char *t = NULL;
f9d273e6 738 ASSERT_OK(mkdtemp_malloc(NULL, &t));
9c21cfdd 739 _cleanup_free_ char *sub = ASSERT_PTR(path_join(t, "a/b/c/d"));
f9d273e6 740 ASSERT_OK(mkdir_p(sub, 0700));
9c21cfdd 741 _cleanup_free_ char *suffixed = ASSERT_PTR(path_join(sub, ".."));
f9d273e6 742 ASSERT_OK(chase(suffixed, NULL, CHASE_PARENT, &path, &fd));
9c21cfdd
LP
743 _cleanup_free_ char *expected1 = ASSERT_PTR(path_join(t, "a/b/c"));
744 _cleanup_free_ char *expected2 = ASSERT_PTR(path_join(t, "a/b"));
745
746 assert_se(path_equal(path, expected1));
f9d273e6 747 ASSERT_OK(fd_get_path(fd, &fdpath));
9c21cfdd
LP
748 assert_se(path_equal(fdpath, expected2));
749}
750
c0dff420
YW
751static int intro(void) {
752 arg_test_dir = saved_argv[1];
753 return EXIT_SUCCESS;
754}
755
8f22fe32 756DEFINE_TEST_MAIN_WITH_INTRO(LOG_INFO, intro);