]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/test/test-chase.c
iovec-util: add exported constant empty but valid (i.e. non-NULL) iovec
[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
7efaab48
DDM
239 r = chase("/.path/with/dot", temp, CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &result, NULL);
240 ASSERT_OK(r);
241 q = strjoina(temp, "/.path/with/dot");
242 ASSERT_STREQ(result, q);
243 result = mfree(result);
244
8f22fe32
YW
245 r = chase("/etc/machine-id/foo", NULL, 0, &result, NULL);
246 assert_se(IN_SET(r, -ENOTDIR, -ENOENT));
247 result = mfree(result);
248
249 /* Path that loops back to self */
250
251 p = strjoina(temp, "/recursive-symlink");
f9d273e6 252 ASSERT_OK(symlink("recursive-symlink", p));
8f22fe32
YW
253 r = chase(p, NULL, 0, &result, NULL);
254 assert_se(r == -ELOOP);
255
256 /* Path which doesn't exist */
257
258 p = strjoina(temp, "/idontexist");
259 r = chase(p, NULL, 0, &result, NULL);
260 assert_se(r == -ENOENT);
261
262 r = chase(p, NULL, CHASE_NONEXISTENT, &result, NULL);
263 assert_se(r == 0);
264 assert_se(path_equal(result, p));
265 result = mfree(result);
266
267 p = strjoina(temp, "/idontexist/meneither");
268 r = chase(p, NULL, 0, &result, NULL);
269 assert_se(r == -ENOENT);
270
271 r = chase(p, NULL, CHASE_NONEXISTENT, &result, NULL);
272 assert_se(r == 0);
273 assert_se(path_equal(result, p));
274 result = mfree(result);
275
276 /* Relative paths */
277
f9d273e6 278 ASSERT_OK(safe_getcwd(&pwd));
8f22fe32 279
f9d273e6 280 ASSERT_OK(chdir(temp));
8f22fe32
YW
281
282 p = "this/is/a/relative/path";
283 r = chase(p, NULL, CHASE_NONEXISTENT, &result, NULL);
284 assert_se(r == 0);
285
286 p = strjoina(temp, "/", p);
287 assert_se(path_equal(result, p));
288 result = mfree(result);
289
290 p = "this/is/a/relative/path";
291 r = chase(p, temp, CHASE_NONEXISTENT, &result, NULL);
292 assert_se(r == 0);
293
294 p = strjoina(temp, "/", p);
295 assert_se(path_equal(result, p));
296 result = mfree(result);
297
298 assert_se(chdir(pwd) >= 0);
299
300 /* Path which doesn't exist, but contains weird stuff */
301
302 p = strjoina(temp, "/idontexist/..");
303 r = chase(p, NULL, 0, &result, NULL);
304 assert_se(r == -ENOENT);
305
306 r = chase(p, NULL, CHASE_NONEXISTENT, &result, NULL);
307 assert_se(r == -ENOENT);
308
309 p = strjoina(temp, "/target");
310 q = strjoina(temp, "/top");
311 assert_se(symlink(q, p) >= 0);
312 p = strjoina(temp, "/target/idontexist");
313 r = chase(p, NULL, 0, &result, NULL);
314 assert_se(r == -ENOENT);
315
316 if (geteuid() == 0) {
317 p = strjoina(temp, "/priv1");
f9d273e6 318 ASSERT_OK(mkdir(p, 0755));
8f22fe32
YW
319
320 q = strjoina(p, "/priv2");
f9d273e6 321 ASSERT_OK(mkdir(q, 0755));
8f22fe32 322
f9d273e6 323 ASSERT_OK(chase(q, NULL, CHASE_SAFE, NULL, NULL));
8f22fe32 324
f9d273e6
UA
325 ASSERT_OK(chown(q, UID_NOBODY, GID_NOBODY));
326 ASSERT_OK(chase(q, NULL, CHASE_SAFE, NULL, NULL));
8f22fe32 327
f9d273e6
UA
328 ASSERT_OK(chown(p, UID_NOBODY, GID_NOBODY));
329 ASSERT_OK(chase(q, NULL, CHASE_SAFE, NULL, NULL));
8f22fe32
YW
330
331 assert_se(chown(q, 0, 0) >= 0);
332 assert_se(chase(q, NULL, CHASE_SAFE, NULL, NULL) == -ENOLINK);
333
f9d273e6
UA
334 ASSERT_OK(rmdir(q));
335 ASSERT_OK(symlink("/etc/passwd", q));
8f22fe32
YW
336 assert_se(chase(q, NULL, CHASE_SAFE, NULL, NULL) == -ENOLINK);
337
338 assert_se(chown(p, 0, 0) >= 0);
f9d273e6 339 ASSERT_OK(chase(q, NULL, CHASE_SAFE, NULL, NULL));
8f22fe32
YW
340 }
341
342 p = strjoina(temp, "/machine-id-test");
f9d273e6 343 ASSERT_OK(symlink("/usr/../etc/./machine-id", p));
8f22fe32
YW
344
345 r = chase(p, NULL, 0, NULL, &pfd);
346 if (r != -ENOENT && sd_id128_get_machine(NULL) >= 0) {
347 _cleanup_close_ int fd = -EBADF;
348 sd_id128_t a, b;
349
f9d273e6 350 ASSERT_OK(pfd);
8f22fe32
YW
351
352 fd = fd_reopen(pfd, O_RDONLY|O_CLOEXEC);
f9d273e6 353 ASSERT_OK(fd);
8f22fe32
YW
354 safe_close(pfd);
355
f9d273e6
UA
356 ASSERT_OK(id128_read_fd(fd, ID128_FORMAT_PLAIN, &a));
357 ASSERT_OK(sd_id128_get_machine(&b));
8f22fe32
YW
358 assert_se(sd_id128_equal(a, b));
359 }
360
361 assert_se(lstat(p, &st) >= 0);
362 r = chase_and_unlink(p, NULL, 0, 0, &result);
363 assert_se(r == 0);
364 assert_se(path_equal(result, p));
365 result = mfree(result);
366 assert_se(lstat(p, &st) == -1 && errno == ENOENT);
367
368 /* Test CHASE_NOFOLLOW */
369
370 p = strjoina(temp, "/target");
371 q = strjoina(temp, "/symlink");
372 assert_se(symlink(p, q) >= 0);
373 r = chase(q, NULL, CHASE_NOFOLLOW, &result, &pfd);
f9d273e6
UA
374 ASSERT_OK(r);
375 ASSERT_OK(pfd);
8f22fe32 376 assert_se(path_equal(result, q));
f9d273e6 377 ASSERT_OK(fstat(pfd, &st));
8f22fe32
YW
378 assert_se(S_ISLNK(st.st_mode));
379 result = mfree(result);
380 pfd = safe_close(pfd);
381
382 /* s1 -> s2 -> nonexistent */
383 q = strjoina(temp, "/s1");
f9d273e6 384 ASSERT_OK(symlink("s2", q));
8f22fe32 385 p = strjoina(temp, "/s2");
f9d273e6 386 ASSERT_OK(symlink("nonexistent", p));
8f22fe32 387 r = chase(q, NULL, CHASE_NOFOLLOW, &result, &pfd);
f9d273e6
UA
388 ASSERT_OK(r);
389 ASSERT_OK(pfd);
8f22fe32 390 assert_se(path_equal(result, q));
f9d273e6 391 ASSERT_OK(fstat(pfd, &st));
8f22fe32
YW
392 assert_se(S_ISLNK(st.st_mode));
393 result = mfree(result);
394 pfd = safe_close(pfd);
395
396 /* Test CHASE_STEP */
397
398 p = strjoina(temp, "/start");
399 r = chase(p, NULL, CHASE_STEP, &result, NULL);
400 assert_se(r == 0);
401 p = strjoina(temp, "/top/dot/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/dotdota");
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, "/top/../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, "/a");
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);
425 p = strjoina(temp, "/b");
f9d273e6 426 ASSERT_STREQ(p, result);
8f22fe32
YW
427 result = mfree(result);
428
429 r = chase(p, NULL, CHASE_STEP, &result, NULL);
430 assert_se(r == 0);
f9d273e6 431 ASSERT_STREQ("/usr", result);
8f22fe32
YW
432 result = mfree(result);
433
434 r = chase("/usr", NULL, CHASE_STEP, &result, NULL);
435 assert_se(r > 0);
f9d273e6 436 ASSERT_STREQ("/usr", result);
8f22fe32
YW
437 result = mfree(result);
438
439 /* Make sure that symlinks in the "root" path are not resolved, but those below are */
440 p = strjoina("/etc/..", temp, "/self");
441 assert_se(symlink(".", p) >= 0);
442 q = strjoina(p, "/top/dot/dotdota");
443 r = chase(q, p, 0, &result, NULL);
444 assert_se(r > 0);
445 assert_se(path_equal(path_startswith(result, p), "usr"));
446 result = mfree(result);
447
448 /* Test CHASE_PROHIBIT_SYMLINKS */
449
450 assert_se(chase("top/dot", temp, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, NULL, NULL) == -EREMCHG);
451 assert_se(chase("top/dot", temp, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS|CHASE_WARN, NULL, NULL) == -EREMCHG);
452 assert_se(chase("top/dotdot", temp, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, NULL, NULL) == -EREMCHG);
453 assert_se(chase("top/dotdot", temp, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS|CHASE_WARN, NULL, NULL) == -EREMCHG);
454 assert_se(chase("top/dot/dot", temp, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, NULL, NULL) == -EREMCHG);
455 assert_se(chase("top/dot/dot", temp, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS|CHASE_WARN, NULL, NULL) == -EREMCHG);
456
457 cleanup:
f9d273e6 458 ASSERT_OK(rm_rf(temp, REMOVE_ROOT|REMOVE_PHYSICAL));
8f22fe32
YW
459}
460
461TEST(chaseat) {
462 _cleanup_(rm_rf_physical_and_freep) char *t = NULL;
463 _cleanup_close_ int tfd = -EBADF, fd = -EBADF;
464 _cleanup_free_ char *result = NULL;
465 _cleanup_closedir_ DIR *dir = NULL;
466 _cleanup_fclose_ FILE *f = NULL;
467 struct stat st;
468 const char *p;
469
f9d273e6 470 ASSERT_OK((tfd = mkdtemp_open(NULL, 0, &t)));
8f22fe32
YW
471
472 /* Test that AT_FDCWD with CHASE_AT_RESOLVE_IN_ROOT resolves against / and not the current working
473 * directory. */
474
f9d273e6 475 ASSERT_OK(symlinkat("/usr", tfd, "abc"));
8f22fe32
YW
476
477 p = strjoina(t, "/abc");
f9d273e6
UA
478 ASSERT_OK(chaseat(AT_FDCWD, p, CHASE_AT_RESOLVE_IN_ROOT, &result, NULL));
479 ASSERT_STREQ(result, "/usr");
8f22fe32
YW
480 result = mfree(result);
481
c0552b35
YW
482 /* If the file descriptor points to the root directory, the result will be absolute. */
483
484 fd = open("/", O_CLOEXEC | O_DIRECTORY | O_PATH);
f9d273e6 485 ASSERT_OK(fd);
c0552b35 486
f9d273e6
UA
487 ASSERT_OK(chaseat(fd, p, 0, &result, NULL));
488 ASSERT_STREQ(result, "/usr");
c0552b35
YW
489 result = mfree(result);
490
f9d273e6
UA
491 ASSERT_OK(chaseat(fd, p, CHASE_AT_RESOLVE_IN_ROOT, &result, NULL));
492 ASSERT_STREQ(result, "/usr");
c0552b35
YW
493 result = mfree(result);
494
495 fd = safe_close(fd);
496
24be89eb
YW
497 /* If the file descriptor does not point to the root directory, the result will be relative
498 * unless the result is outside of the specified file descriptor. */
499
f9d273e6
UA
500 ASSERT_OK(chaseat(tfd, "abc", 0, &result, NULL));
501 ASSERT_STREQ(result, "/usr");
24be89eb
YW
502 result = mfree(result);
503
f9d273e6
UA
504 ASSERT_OK(chaseat(tfd, "/abc", 0, &result, NULL));
505 ASSERT_STREQ(result, "/usr");
24be89eb 506 result = mfree(result);
c0552b35
YW
507
508 assert_se(chaseat(tfd, "abc", CHASE_AT_RESOLVE_IN_ROOT, NULL, NULL) == -ENOENT);
509 assert_se(chaseat(tfd, "/abc", CHASE_AT_RESOLVE_IN_ROOT, NULL, NULL) == -ENOENT);
510
f9d273e6
UA
511 ASSERT_OK(chaseat(tfd, "abc", CHASE_AT_RESOLVE_IN_ROOT | CHASE_NONEXISTENT, &result, NULL));
512 ASSERT_STREQ(result, "usr");
c0552b35
YW
513 result = mfree(result);
514
f9d273e6
UA
515 ASSERT_OK(chaseat(tfd, "/abc", CHASE_AT_RESOLVE_IN_ROOT | CHASE_NONEXISTENT, &result, NULL));
516 ASSERT_STREQ(result, "usr");
c0552b35
YW
517 result = mfree(result);
518
8f22fe32
YW
519 /* Test that absolute path or not are the same when resolving relative to a directory file
520 * descriptor and that we always get a relative path back. */
521
f9d273e6 522 ASSERT_OK(fd = openat(tfd, "def", O_CREAT|O_CLOEXEC, 0700));
8f22fe32 523 fd = safe_close(fd);
f9d273e6
UA
524 ASSERT_OK(symlinkat("/def", tfd, "qed"));
525 ASSERT_OK(chaseat(tfd, "qed", CHASE_AT_RESOLVE_IN_ROOT, &result, NULL));
526 ASSERT_STREQ(result, "def");
8f22fe32 527 result = mfree(result);
f9d273e6
UA
528 ASSERT_OK(chaseat(tfd, "/qed", CHASE_AT_RESOLVE_IN_ROOT, &result, NULL));
529 ASSERT_STREQ(result, "def");
8f22fe32
YW
530 result = mfree(result);
531
532 /* Valid directory file descriptor without CHASE_AT_RESOLVE_IN_ROOT should resolve symlinks against
533 * host's root. */
534 assert_se(chaseat(tfd, "/qed", 0, NULL, NULL) == -ENOENT);
535
536 /* Test CHASE_PARENT */
537
f9d273e6
UA
538 ASSERT_OK((fd = open_mkdir_at(tfd, "chase", O_CLOEXEC, 0755)));
539 ASSERT_OK(symlinkat("/def", fd, "parent"));
8f22fe32
YW
540 fd = safe_close(fd);
541
542 /* Make sure that when we chase a symlink parent directory, that we chase the parent directory of the
543 * symlink target and not the symlink itself. But if we add CHASE_NOFOLLOW, we get the parent
544 * directory of the symlink itself. */
545
f9d273e6
UA
546 ASSERT_OK(chaseat(tfd, "chase/parent", CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT, &result, &fd));
547 ASSERT_OK(faccessat(fd, "def", F_OK, 0));
548 ASSERT_STREQ(result, "def");
8f22fe32
YW
549 fd = safe_close(fd);
550 result = mfree(result);
551
f9d273e6
UA
552 ASSERT_OK(chaseat(tfd, "chase/parent", CHASE_AT_RESOLVE_IN_ROOT|CHASE_PARENT|CHASE_NOFOLLOW, &result, &fd));
553 ASSERT_OK(faccessat(fd, "parent", F_OK, AT_SYMLINK_NOFOLLOW));
554 ASSERT_STREQ(result, "chase/parent");
8f22fe32
YW
555 fd = safe_close(fd);
556 result = mfree(result);
557
f9d273e6
UA
558 ASSERT_OK(chaseat(tfd, "chase", CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT, &result, &fd));
559 ASSERT_OK(faccessat(fd, "chase", F_OK, 0));
560 ASSERT_STREQ(result, "chase");
8f22fe32
YW
561 fd = safe_close(fd);
562 result = mfree(result);
563
f9d273e6
UA
564 ASSERT_OK(chaseat(tfd, "/", CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT, &result, NULL));
565 ASSERT_STREQ(result, ".");
8f22fe32
YW
566 result = mfree(result);
567
f9d273e6
UA
568 assert_se(chaseat(tfd, ".", CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT, &result, NULL));
569 ASSERT_STREQ(result, ".");
8f22fe32
YW
570 result = mfree(result);
571
572 /* Test CHASE_MKDIR_0755 */
573
f9d273e6
UA
574 ASSERT_OK(chaseat(tfd, "m/k/d/i/r", CHASE_MKDIR_0755|CHASE_NONEXISTENT, &result, NULL));
575 ASSERT_OK(faccessat(tfd, "m/k/d/i", F_OK, 0));
8f22fe32 576 assert_se(RET_NERRNO(faccessat(tfd, "m/k/d/i/r", F_OK, 0)) == -ENOENT);
f9d273e6 577 ASSERT_STREQ(result, "m/k/d/i/r");
8f22fe32
YW
578 result = mfree(result);
579
f9d273e6
UA
580 ASSERT_OK(chaseat(tfd, "m/../q", CHASE_MKDIR_0755|CHASE_NONEXISTENT, &result, NULL));
581 ASSERT_OK(faccessat(tfd, "m", F_OK, 0));
8f22fe32 582 assert_se(RET_NERRNO(faccessat(tfd, "q", F_OK, 0)) == -ENOENT);
f9d273e6 583 ASSERT_STREQ(result, "q");
8f22fe32
YW
584 result = mfree(result);
585
4ea0bcb9 586 assert_se(chaseat(tfd, "i/../p", CHASE_MKDIR_0755|CHASE_NONEXISTENT, NULL, NULL) == -ENOENT);
8f22fe32 587
f5dc1adc 588 /* Test CHASE_EXTRACT_FILENAME */
8f22fe32 589
f9d273e6
UA
590 ASSERT_OK(chaseat(tfd, "chase/parent", CHASE_AT_RESOLVE_IN_ROOT|CHASE_PARENT|CHASE_NOFOLLOW|CHASE_EXTRACT_FILENAME, &result, &fd));
591 ASSERT_OK(faccessat(fd, result, F_OK, AT_SYMLINK_NOFOLLOW));
592 ASSERT_STREQ(result, "parent");
8f22fe32
YW
593 fd = safe_close(fd);
594 result = mfree(result);
595
f9d273e6
UA
596 ASSERT_OK(chaseat(tfd, "chase", CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT|CHASE_EXTRACT_FILENAME, &result, &fd));
597 ASSERT_OK(faccessat(fd, result, F_OK, 0));
598 ASSERT_STREQ(result, "chase");
8f22fe32
YW
599 fd = safe_close(fd);
600 result = mfree(result);
601
f9d273e6
UA
602 ASSERT_OK(chaseat(tfd, "/", CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT|CHASE_EXTRACT_FILENAME, &result, NULL));
603 ASSERT_STREQ(result, ".");
8f22fe32
YW
604 result = mfree(result);
605
f9d273e6
UA
606 ASSERT_OK(chaseat(tfd, ".", CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT|CHASE_EXTRACT_FILENAME, &result, NULL));
607 ASSERT_STREQ(result, ".");
8f22fe32
YW
608 result = mfree(result);
609
f9d273e6
UA
610 ASSERT_OK(chaseat(tfd, NULL, CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT|CHASE_EXTRACT_FILENAME, &result, NULL));
611 ASSERT_STREQ(result, ".");
3c8f449c
DDM
612 result = mfree(result);
613
8f22fe32
YW
614 /* Test chase_and_openat() */
615
616 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
617 ASSERT_OK(fd);
618 ASSERT_OK(fd_verify_regular(fd));
8f22fe32
YW
619 fd = safe_close(fd);
620
621 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
622 ASSERT_OK(fd);
623 ASSERT_OK(fd_verify_directory(fd));
8f22fe32
YW
624 fd = safe_close(fd);
625
8b85333c 626 fd = chase_and_openat(tfd, NULL, CHASE_PARENT|CHASE_EXTRACT_FILENAME, O_PATH|O_DIRECTORY|O_CLOEXEC, &result);
f9d273e6
UA
627 ASSERT_OK(fd);
628 ASSERT_STREQ(result, ".");
8b85333c
DDM
629 fd = safe_close(fd);
630 result = mfree(result);
631
8f22fe32
YW
632 /* Test chase_and_openatdir() */
633
f9d273e6 634 ASSERT_OK(chase_and_opendirat(tfd, "o/p/e/n/d/i", 0, &result, &dir));
8f22fe32 635 FOREACH_DIRENT(de, dir, assert_not_reached())
f9d273e6
UA
636 ASSERT_STREQ(de->d_name, "r");
637 ASSERT_STREQ(result, "o/p/e/n/d/i");
8f22fe32
YW
638 result = mfree(result);
639
640 /* Test chase_and_statat() */
641
f9d273e6
UA
642 ASSERT_OK(chase_and_statat(tfd, "o/p", 0, &result, &st));
643 ASSERT_OK(stat_verify_directory(&st));
644 ASSERT_STREQ(result, "o/p");
8f22fe32
YW
645 result = mfree(result);
646
647 /* Test chase_and_accessat() */
648
f9d273e6
UA
649 ASSERT_OK(chase_and_accessat(tfd, "o/p/e", 0, F_OK, &result));
650 ASSERT_STREQ(result, "o/p/e");
8f22fe32
YW
651 result = mfree(result);
652
653 /* Test chase_and_fopenat_unlocked() */
654
f9d273e6 655 ASSERT_OK(chase_and_fopenat_unlocked(tfd, "o/p/e/n/f/i/l/e", 0, "re", &result, &f));
8f22fe32
YW
656 assert_se(fread(&(char[1]) {}, 1, 1, f) == 0);
657 assert_se(feof(f));
658 f = safe_fclose(f);
f9d273e6 659 ASSERT_STREQ(result, "o/p/e/n/f/i/l/e");
8f22fe32
YW
660 result = mfree(result);
661
662 /* Test chase_and_unlinkat() */
663
f9d273e6
UA
664 ASSERT_OK(chase_and_unlinkat(tfd, "o/p/e/n/f/i/l/e", 0, 0, &result));
665 ASSERT_STREQ(result, "o/p/e/n/f/i/l/e");
8f22fe32
YW
666 result = mfree(result);
667
668 /* Test chase_and_open_parent_at() */
669
f9d273e6
UA
670 ASSERT_OK((fd = chase_and_open_parent_at(tfd, "chase/parent", CHASE_AT_RESOLVE_IN_ROOT|CHASE_NOFOLLOW, &result)));
671 ASSERT_OK(faccessat(fd, result, F_OK, AT_SYMLINK_NOFOLLOW));
672 ASSERT_STREQ(result, "parent");
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", CHASE_AT_RESOLVE_IN_ROOT, &result)));
677 ASSERT_OK(faccessat(fd, result, F_OK, 0));
678 ASSERT_STREQ(result, "chase");
8f22fe32
YW
679 fd = safe_close(fd);
680 result = mfree(result);
681
f9d273e6
UA
682 ASSERT_OK((fd = chase_and_open_parent_at(tfd, "/", CHASE_AT_RESOLVE_IN_ROOT, &result)));
683 ASSERT_STREQ(result, ".");
8f22fe32
YW
684 fd = safe_close(fd);
685 result = mfree(result);
686
f9d273e6
UA
687 ASSERT_OK((fd = chase_and_open_parent_at(tfd, ".", CHASE_AT_RESOLVE_IN_ROOT, &result)));
688 ASSERT_STREQ(result, ".");
8f22fe32
YW
689 fd = safe_close(fd);
690 result = mfree(result);
691}
692
60e761d8
YW
693TEST(chaseat_prefix_root) {
694 _cleanup_free_ char *cwd = NULL, *ret = NULL, *expected = NULL;
695
f9d273e6 696 ASSERT_OK(safe_getcwd(&cwd));
60e761d8 697
f9d273e6
UA
698 ASSERT_OK(chaseat_prefix_root("/hoge", NULL, &ret));
699 ASSERT_STREQ(ret, "/hoge");
60e761d8
YW
700
701 ret = mfree(ret);
702
f9d273e6
UA
703 ASSERT_OK(chaseat_prefix_root("/hoge", "a/b/c", &ret));
704 ASSERT_STREQ(ret, "/hoge");
60e761d8
YW
705
706 ret = mfree(ret);
707
f9d273e6
UA
708 ASSERT_OK(chaseat_prefix_root("hoge", "/a/b//./c///", &ret));
709 ASSERT_STREQ(ret, "/a/b/c/hoge");
60e761d8
YW
710
711 ret = mfree(ret);
712
f9d273e6 713 ASSERT_OK(chaseat_prefix_root("hoge", "a/b//./c///", &ret));
60e761d8 714 assert_se(expected = path_join(cwd, "a/b/c/hoge"));
f9d273e6 715 ASSERT_STREQ(ret, expected);
60e761d8
YW
716
717 ret = mfree(ret);
718 expected = mfree(expected);
719
f9d273e6
UA
720 ASSERT_OK(chaseat_prefix_root("./hoge/aaa/../././b", "/a/b//./c///", &ret));
721 ASSERT_STREQ(ret, "/a/b/c/hoge/aaa/../././b");
60e761d8
YW
722
723 ret = mfree(ret);
724
725 assert_se(chaseat_prefix_root("./hoge/aaa/../././b", "a/b//./c///", &ret) >= 0);
726 assert_se(expected = path_join(cwd, "a/b/c/hoge/aaa/../././b"));
c79e88b3 727 ASSERT_STREQ(ret, expected);
60e761d8
YW
728}
729
9c21cfdd
LP
730TEST(trailing_dot_dot) {
731 _cleanup_free_ char *path = NULL, *fdpath = NULL;
732 _cleanup_close_ int fd = -EBADF;
733
f9d273e6 734 ASSERT_OK(chase("/usr/..", NULL, CHASE_PARENT, &path, &fd));
9c21cfdd 735 assert_se(path_equal(path, "/"));
f9d273e6 736 ASSERT_OK(fd_get_path(fd, &fdpath));
9c21cfdd
LP
737 assert_se(path_equal(fdpath, "/"));
738
739 path = mfree(path);
740 fdpath = mfree(fdpath);
741 fd = safe_close(fd);
742
743 _cleanup_(rm_rf_physical_and_freep) char *t = NULL;
f9d273e6 744 ASSERT_OK(mkdtemp_malloc(NULL, &t));
9c21cfdd 745 _cleanup_free_ char *sub = ASSERT_PTR(path_join(t, "a/b/c/d"));
f9d273e6 746 ASSERT_OK(mkdir_p(sub, 0700));
9c21cfdd 747 _cleanup_free_ char *suffixed = ASSERT_PTR(path_join(sub, ".."));
f9d273e6 748 ASSERT_OK(chase(suffixed, NULL, CHASE_PARENT, &path, &fd));
9c21cfdd
LP
749 _cleanup_free_ char *expected1 = ASSERT_PTR(path_join(t, "a/b/c"));
750 _cleanup_free_ char *expected2 = ASSERT_PTR(path_join(t, "a/b"));
751
752 assert_se(path_equal(path, expected1));
f9d273e6 753 ASSERT_OK(fd_get_path(fd, &fdpath));
9c21cfdd
LP
754 assert_se(path_equal(fdpath, expected2));
755}
756
c0dff420
YW
757static int intro(void) {
758 arg_test_dir = saved_argv[1];
759 return EXIT_SUCCESS;
760}
761
8f22fe32 762DEFINE_TEST_MAIN_WITH_INTRO(LOG_INFO, intro);