]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/test/test-chase.c
4f6e006d5e1d4ae213b5011871ee4e62ade5dfee
[thirdparty/systemd.git] / src / test / test-chase.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <stdlib.h>
4 #include <unistd.h>
5
6 #include "alloc-util.h"
7 #include "argv-util.h"
8 #include "chase.h"
9 #include "dirent-util.h"
10 #include "fd-util.h"
11 #include "fs-util.h"
12 #include "id128-util.h"
13 #include "mkdir.h"
14 #include "path-util.h"
15 #include "random-util.h"
16 #include "rm-rf.h"
17 #include "stat-util.h"
18 #include "string-util.h"
19 #include "tests.h"
20 #include "tmpfile-util.h"
21 #include "user-util.h"
22
23 static const char *arg_test_dir = NULL;
24
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;
27
28 log_debug("/* %s(path=%s, root=%s) */", __func__, path, strnull(root));
29
30 assert_se(chase(path, root, CHASE_EXTRACT_FILENAME, &ret1, NULL) > 0);
31 ASSERT_STREQ(ret1, expected);
32
33 assert_se(chase(path, root, 0, &ret2, NULL) > 0);
34 ASSERT_OK(chase_extract_filename(ret2, root, &fname));
35 ASSERT_STREQ(fname, expected);
36 }
37
38 TEST(chase) {
39 _cleanup_free_ char *result = NULL, *pwd = NULL;
40 _cleanup_close_ int pfd = -EBADF;
41 char *temp;
42 const char *top, *p, *pslash, *q, *qslash;
43 struct stat st;
44 int r;
45
46 temp = strjoina(arg_test_dir ?: "/tmp", "/test-chase.XXXXXX");
47 assert_se(mkdtemp(temp));
48
49 top = strjoina(temp, "/top");
50 ASSERT_OK(mkdir(top, 0700));
51
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");
56 goto cleanup;
57 };
58
59 p = strjoina(top, "/dotdot");
60 ASSERT_OK(symlink("..", p));
61
62 p = strjoina(top, "/dotdota");
63 ASSERT_OK(symlink("../a", p));
64
65 p = strjoina(temp, "/a");
66 ASSERT_OK(symlink("b", p));
67
68 p = strjoina(temp, "/b");
69 ASSERT_OK(symlink("/usr", p));
70
71 p = strjoina(temp, "/start");
72 ASSERT_OK(symlink("top/dot/dotdota", p));
73
74 /* Paths that use symlinks underneath the "root" */
75
76 r = chase(p, NULL, 0, &result, NULL);
77 assert_se(r > 0);
78 assert_se(path_equal(result, "/usr"));
79 result = mfree(result);
80
81 r = chase(p, "/.//../../../", 0, &result, NULL);
82 assert_se(r > 0);
83 assert_se(path_equal(result, "/usr"));
84 result = mfree(result);
85
86 pslash = strjoina(p, "/");
87 r = chase(pslash, NULL, 0, &result, NULL);
88 assert_se(r > 0);
89 assert_se(path_equal(result, "/usr/"));
90 result = mfree(result);
91
92 r = chase(p, temp, 0, &result, NULL);
93 assert_se(r == -ENOENT);
94
95 r = chase(pslash, temp, 0, &result, NULL);
96 assert_se(r == -ENOENT);
97
98 q = strjoina(temp, "/usr");
99
100 r = chase(p, temp, CHASE_NONEXISTENT, &result, NULL);
101 assert_se(r == 0);
102 assert_se(path_equal(result, q));
103 result = mfree(result);
104
105 qslash = strjoina(q, "/");
106
107 r = chase(pslash, temp, CHASE_NONEXISTENT, &result, NULL);
108 assert_se(r == 0);
109 assert_se(path_equal(result, qslash));
110 result = mfree(result);
111
112 ASSERT_OK(mkdir(q, 0700));
113
114 r = chase(p, temp, 0, &result, NULL);
115 assert_se(r > 0);
116 assert_se(path_equal(result, q));
117 result = mfree(result);
118
119 r = chase(pslash, temp, 0, &result, NULL);
120 assert_se(r > 0);
121 assert_se(path_equal(result, qslash));
122 result = mfree(result);
123
124 p = strjoina(temp, "/slash");
125 assert_se(symlink("/", p) >= 0);
126
127 r = chase(p, NULL, 0, &result, NULL);
128 assert_se(r > 0);
129 assert_se(path_equal(result, "/"));
130 result = mfree(result);
131
132 r = chase(p, temp, 0, &result, NULL);
133 assert_se(r > 0);
134 assert_se(path_equal(result, temp));
135 result = mfree(result);
136
137 /* Tests for CHASE_EXTRACT_FILENAME and chase_extract_filename() */
138
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");
145
146 p = strjoina(temp, "/slash");
147 test_chase_extract_filename_one(p, NULL, ".");
148 test_chase_extract_filename_one(p, temp, ".");
149
150 /* Paths that would "escape" outside of the "root" */
151
152 p = strjoina(temp, "/6dots");
153 ASSERT_OK(symlink("../../..", p));
154
155 r = chase(p, temp, 0, &result, NULL);
156 assert_se(r > 0 && path_equal(result, temp));
157 result = mfree(result);
158
159 p = strjoina(temp, "/6dotsusr");
160 ASSERT_OK(symlink("../../../usr", p));
161
162 r = chase(p, temp, 0, &result, NULL);
163 assert_se(r > 0 && path_equal(result, q));
164 result = mfree(result);
165
166 p = strjoina(temp, "/top/8dotsusr");
167 ASSERT_OK(symlink("../../../../usr", p));
168
169 r = chase(p, temp, 0, &result, NULL);
170 assert_se(r > 0 && path_equal(result, q));
171 result = mfree(result);
172
173 /* Paths that contain repeated slashes */
174
175 p = strjoina(temp, "/slashslash");
176 ASSERT_OK(symlink("///usr///", p));
177
178 r = chase(p, NULL, 0, &result, NULL);
179 assert_se(r > 0);
180 assert_se(path_equal(result, "/usr"));
181 ASSERT_STREQ(result, "/usr"); /* we guarantee that we drop redundant slashes */
182 result = mfree(result);
183
184 r = chase(p, temp, 0, &result, NULL);
185 assert_se(r > 0);
186 assert_se(path_equal(result, q));
187 result = mfree(result);
188
189 /* Paths underneath the "root" with different UIDs while using CHASE_SAFE */
190
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));
195
196 q = strjoina(temp, "/user/root");
197 ASSERT_OK(mkdir(q, 0755));
198
199 p = strjoina(q, "/link");
200 ASSERT_OK(symlink("/", p));
201
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);
206
207 /* Allow this when the user-owned directories are all in the "root". */
208 r = chase(p, q, CHASE_SAFE, &result, NULL);
209 assert_se(r > 0);
210 result = mfree(result);
211 }
212
213 /* Paths using . */
214
215 r = chase("/etc/./.././", NULL, 0, &result, NULL);
216 assert_se(r > 0);
217 assert_se(path_equal(result, "/"));
218 result = mfree(result);
219
220 r = chase("/etc/./.././", "/etc", 0, &result, NULL);
221 assert_se(r > 0 && path_equal(result, "/etc"));
222 result = mfree(result);
223
224 r = chase("/../.././//../../etc", NULL, 0, &result, NULL);
225 assert_se(r > 0);
226 ASSERT_STREQ(result, "/etc");
227 result = mfree(result);
228
229 r = chase("/../.././//../../test-chase.fsldajfl", NULL, CHASE_NONEXISTENT, &result, NULL);
230 assert_se(r == 0);
231 ASSERT_STREQ(result, "/test-chase.fsldajfl");
232 result = mfree(result);
233
234 r = chase("/../.././//../../etc", "/", CHASE_PREFIX_ROOT, &result, NULL);
235 assert_se(r > 0);
236 ASSERT_STREQ(result, "/etc");
237 result = mfree(result);
238
239 r = chase("/../.././//../../test-chase.fsldajfl", "/", CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &result, NULL);
240 assert_se(r == 0);
241 ASSERT_STREQ(result, "/test-chase.fsldajfl");
242 result = mfree(result);
243
244 r = chase("/.path/with/dot", temp, CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &result, NULL);
245 ASSERT_OK(r);
246 q = strjoina(temp, "/.path/with/dot");
247 ASSERT_STREQ(result, q);
248 result = mfree(result);
249
250 r = chase("/etc/machine-id/foo", NULL, 0, &result, NULL);
251 assert_se(IN_SET(r, -ENOTDIR, -ENOENT));
252 result = mfree(result);
253
254 /* Path that loops back to self */
255
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);
260
261 /* Path which doesn't exist */
262
263 p = strjoina(temp, "/idontexist");
264 r = chase(p, NULL, 0, &result, NULL);
265 assert_se(r == -ENOENT);
266
267 r = chase(p, NULL, CHASE_NONEXISTENT, &result, NULL);
268 assert_se(r == 0);
269 assert_se(path_equal(result, p));
270 result = mfree(result);
271
272 p = strjoina(temp, "/idontexist/meneither");
273 r = chase(p, NULL, 0, &result, NULL);
274 assert_se(r == -ENOENT);
275
276 r = chase(p, NULL, CHASE_NONEXISTENT, &result, NULL);
277 assert_se(r == 0);
278 assert_se(path_equal(result, p));
279 result = mfree(result);
280
281 /* Relative paths */
282
283 ASSERT_OK(safe_getcwd(&pwd));
284
285 ASSERT_OK(chdir(temp));
286
287 p = "this/is/a/relative/path";
288 r = chase(p, NULL, CHASE_NONEXISTENT, &result, NULL);
289 assert_se(r == 0);
290
291 p = strjoina(temp, "/", p);
292 assert_se(path_equal(result, p));
293 result = mfree(result);
294
295 p = "this/is/a/relative/path";
296 r = chase(p, temp, CHASE_NONEXISTENT, &result, NULL);
297 assert_se(r == 0);
298
299 p = strjoina(temp, "/", p);
300 assert_se(path_equal(result, p));
301 result = mfree(result);
302
303 assert_se(chdir(pwd) >= 0);
304
305 /* Path which doesn't exist, but contains weird stuff */
306
307 p = strjoina(temp, "/idontexist/..");
308 r = chase(p, NULL, 0, &result, NULL);
309 assert_se(r == -ENOENT);
310
311 r = chase(p, NULL, CHASE_NONEXISTENT, &result, NULL);
312 assert_se(r == -ENOENT);
313
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);
320
321 if (geteuid() == 0 && !userns_has_single_user()) {
322 p = strjoina(temp, "/priv1");
323 ASSERT_OK(mkdir(p, 0755));
324
325 q = strjoina(p, "/priv2");
326 ASSERT_OK(mkdir(q, 0755));
327
328 ASSERT_OK(chase(q, NULL, CHASE_SAFE, NULL, NULL));
329
330 ASSERT_OK(chown(q, UID_NOBODY, GID_NOBODY));
331 ASSERT_OK(chase(q, NULL, CHASE_SAFE, NULL, NULL));
332
333 ASSERT_OK(chown(p, UID_NOBODY, GID_NOBODY));
334 ASSERT_OK(chase(q, NULL, CHASE_SAFE, NULL, NULL));
335
336 assert_se(chown(q, 0, 0) >= 0);
337 assert_se(chase(q, NULL, CHASE_SAFE, NULL, NULL) == -ENOLINK);
338
339 ASSERT_OK(rmdir(q));
340 ASSERT_OK(symlink("/etc/passwd", q));
341 assert_se(chase(q, NULL, CHASE_SAFE, NULL, NULL) == -ENOLINK);
342
343 assert_se(chown(p, 0, 0) >= 0);
344 ASSERT_OK(chase(q, NULL, CHASE_SAFE, NULL, NULL));
345 }
346
347 p = strjoina(temp, "/machine-id-test");
348 ASSERT_OK(symlink("/usr/../etc/./machine-id", p));
349
350 r = chase(p, NULL, 0, NULL, &pfd);
351 if (r != -ENOENT && sd_id128_get_machine(NULL) >= 0) {
352 _cleanup_close_ int fd = -EBADF;
353 sd_id128_t a, b;
354
355 ASSERT_OK(pfd);
356
357 fd = fd_reopen(pfd, O_RDONLY|O_CLOEXEC);
358 ASSERT_OK(fd);
359 safe_close(pfd);
360
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));
364 }
365
366 assert_se(lstat(p, &st) >= 0);
367 r = chase_and_unlink(p, NULL, 0, 0, &result);
368 assert_se(r == 0);
369 assert_se(path_equal(result, p));
370 result = mfree(result);
371 assert_se(lstat(p, &st) == -1 && errno == ENOENT);
372
373 /* Test CHASE_NOFOLLOW */
374
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);
379 ASSERT_OK(r);
380 ASSERT_OK(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);
386
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);
393 ASSERT_OK(r);
394 ASSERT_OK(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);
400
401 /* Test CHASE_STEP */
402
403 p = strjoina(temp, "/start");
404 r = chase(p, NULL, CHASE_STEP, &result, NULL);
405 assert_se(r == 0);
406 p = strjoina(temp, "/top/dot/dotdota");
407 ASSERT_STREQ(p, result);
408 result = mfree(result);
409
410 r = chase(p, NULL, CHASE_STEP, &result, NULL);
411 assert_se(r == 0);
412 p = strjoina(temp, "/top/dotdota");
413 ASSERT_STREQ(p, result);
414 result = mfree(result);
415
416 r = chase(p, NULL, CHASE_STEP, &result, NULL);
417 assert_se(r == 0);
418 p = strjoina(temp, "/top/../a");
419 ASSERT_STREQ(p, result);
420 result = mfree(result);
421
422 r = chase(p, NULL, CHASE_STEP, &result, NULL);
423 assert_se(r == 0);
424 p = strjoina(temp, "/a");
425 ASSERT_STREQ(p, result);
426 result = mfree(result);
427
428 r = chase(p, NULL, CHASE_STEP, &result, NULL);
429 assert_se(r == 0);
430 p = strjoina(temp, "/b");
431 ASSERT_STREQ(p, result);
432 result = mfree(result);
433
434 r = chase(p, NULL, CHASE_STEP, &result, NULL);
435 assert_se(r == 0);
436 ASSERT_STREQ("/usr", result);
437 result = mfree(result);
438
439 r = chase("/usr", NULL, CHASE_STEP, &result, NULL);
440 assert_se(r > 0);
441 ASSERT_STREQ("/usr", result);
442 result = mfree(result);
443
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);
449 assert_se(r > 0);
450 assert_se(path_equal(path_startswith(result, p), "usr"));
451 result = mfree(result);
452
453 /* Test CHASE_PROHIBIT_SYMLINKS */
454
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);
461
462 cleanup:
463 ASSERT_OK(rm_rf(temp, REMOVE_ROOT|REMOVE_PHYSICAL));
464 }
465
466 TEST(chaseat) {
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;
472 struct stat st;
473 const char *p;
474
475 ASSERT_OK(tfd = mkdtemp_open(NULL, 0, &t));
476
477 /* Test that AT_FDCWD with CHASE_AT_RESOLVE_IN_ROOT resolves against / and not the current working
478 * directory. */
479
480 ASSERT_OK(symlinkat("/usr", tfd, "abc"));
481
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);
486
487 /* If the file descriptor points to the root directory, the result will be absolute. */
488
489 fd = open("/", O_CLOEXEC | O_DIRECTORY | O_PATH);
490 ASSERT_OK(fd);
491
492 ASSERT_OK(chaseat(fd, p, 0, &result, NULL));
493 ASSERT_STREQ(result, "/usr");
494 result = mfree(result);
495
496 ASSERT_OK(chaseat(fd, p, CHASE_AT_RESOLVE_IN_ROOT, &result, NULL));
497 ASSERT_STREQ(result, "/usr");
498 result = mfree(result);
499
500 fd = safe_close(fd);
501
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. */
504
505 ASSERT_OK(chaseat(tfd, "abc", 0, &result, NULL));
506 ASSERT_STREQ(result, "/usr");
507 result = mfree(result);
508
509 ASSERT_OK(chaseat(tfd, "/abc", 0, &result, NULL));
510 ASSERT_STREQ(result, "/usr");
511 result = mfree(result);
512
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);
515
516 ASSERT_OK(chaseat(tfd, "abc", CHASE_AT_RESOLVE_IN_ROOT | CHASE_NONEXISTENT, &result, NULL));
517 ASSERT_STREQ(result, "usr");
518 result = mfree(result);
519
520 ASSERT_OK(chaseat(tfd, "/abc", CHASE_AT_RESOLVE_IN_ROOT | CHASE_NONEXISTENT, &result, NULL));
521 ASSERT_STREQ(result, "usr");
522 result = mfree(result);
523
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. */
526
527 ASSERT_OK(fd = openat(tfd, "def", O_CREAT|O_CLOEXEC, 0700));
528 fd = safe_close(fd);
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);
536
537 /* Valid directory file descriptor without CHASE_AT_RESOLVE_IN_ROOT should resolve symlinks against
538 * host's root. */
539 assert_se(chaseat(tfd, "/qed", 0, NULL, NULL) == -ENOENT);
540
541 /* Test CHASE_PARENT */
542
543 ASSERT_OK(fd = open_mkdir_at(tfd, "chase", O_CLOEXEC, 0755));
544 ASSERT_OK(symlinkat("/def", fd, "parent"));
545 fd = safe_close(fd);
546
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. */
550
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");
554 fd = safe_close(fd);
555 result = mfree(result);
556
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");
560 fd = safe_close(fd);
561 result = mfree(result);
562
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");
566 fd = safe_close(fd);
567 result = mfree(result);
568
569 ASSERT_OK(chaseat(tfd, "/", CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT, &result, NULL));
570 ASSERT_STREQ(result, ".");
571 result = mfree(result);
572
573 assert_se(chaseat(tfd, ".", CHASE_PARENT|CHASE_AT_RESOLVE_IN_ROOT, &result, NULL));
574 ASSERT_STREQ(result, ".");
575 result = mfree(result);
576
577 /* Test CHASE_MKDIR_0755 */
578
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);
584
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);
590
591 assert_se(chaseat(tfd, "i/../p", CHASE_MKDIR_0755|CHASE_NONEXISTENT, NULL, NULL) == -ENOENT);
592
593 /* Test CHASE_EXTRACT_FILENAME */
594
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");
598 fd = safe_close(fd);
599 result = mfree(result);
600
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");
604 fd = safe_close(fd);
605 result = mfree(result);
606
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);
610
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);
614
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);
618
619 /* Test chase_and_openat() */
620
621 fd = chase_and_openat(tfd, "o/p/e/n/f/i/l/e", CHASE_MKDIR_0755, O_CREAT|O_EXCL|O_CLOEXEC, NULL);
622 ASSERT_OK(fd);
623 ASSERT_OK(fd_verify_regular(fd));
624 fd = safe_close(fd);
625
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);
627 ASSERT_OK(fd);
628 ASSERT_OK(fd_verify_directory(fd));
629 fd = safe_close(fd);
630
631 fd = chase_and_openat(tfd, NULL, CHASE_PARENT|CHASE_EXTRACT_FILENAME, O_PATH|O_DIRECTORY|O_CLOEXEC, &result);
632 ASSERT_OK(fd);
633 ASSERT_STREQ(result, ".");
634 fd = safe_close(fd);
635 result = mfree(result);
636
637 /* Test chase_and_openatdir() */
638
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);
644
645 /* Test chase_and_statat() */
646
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);
651
652 /* Test chase_and_accessat() */
653
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);
657
658 /* Test chase_and_fopenat_unlocked() */
659
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);
662 assert_se(feof(f));
663 f = safe_fclose(f);
664 ASSERT_STREQ(result, "o/p/e/n/f/i/l/e");
665 result = mfree(result);
666
667 /* Test chase_and_unlinkat() */
668
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);
672
673 /* Test chase_and_open_parent_at() */
674
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");
678 fd = safe_close(fd);
679 result = mfree(result);
680
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");
684 fd = safe_close(fd);
685 result = mfree(result);
686
687 ASSERT_OK(fd = chase_and_open_parent_at(tfd, "/", CHASE_AT_RESOLVE_IN_ROOT, &result));
688 ASSERT_STREQ(result, ".");
689 fd = safe_close(fd);
690 result = mfree(result);
691
692 ASSERT_OK(fd = chase_and_open_parent_at(tfd, ".", CHASE_AT_RESOLVE_IN_ROOT, &result));
693 ASSERT_STREQ(result, ".");
694 fd = safe_close(fd);
695 result = mfree(result);
696 }
697
698 TEST(chaseat_prefix_root) {
699 _cleanup_free_ char *cwd = NULL, *ret = NULL, *expected = NULL;
700
701 ASSERT_OK(safe_getcwd(&cwd));
702
703 ASSERT_OK(chaseat_prefix_root("/hoge", NULL, &ret));
704 ASSERT_STREQ(ret, "/hoge");
705
706 ret = mfree(ret);
707
708 ASSERT_OK(chaseat_prefix_root("/hoge", "a/b/c", &ret));
709 ASSERT_STREQ(ret, "/hoge");
710
711 ret = mfree(ret);
712
713 ASSERT_OK(chaseat_prefix_root("hoge", "/a/b//./c///", &ret));
714 ASSERT_STREQ(ret, "/a/b/c/hoge");
715
716 ret = mfree(ret);
717
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);
721
722 ret = mfree(ret);
723 expected = mfree(expected);
724
725 ASSERT_OK(chaseat_prefix_root("./hoge/aaa/../././b", "/a/b//./c///", &ret));
726 ASSERT_STREQ(ret, "/a/b/c/hoge/aaa/../././b");
727
728 ret = mfree(ret);
729
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);
733 }
734
735 TEST(trailing_dot_dot) {
736 _cleanup_free_ char *path = NULL, *fdpath = NULL;
737 _cleanup_close_ int fd = -EBADF;
738
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, "/"));
743
744 path = mfree(path);
745 fdpath = mfree(fdpath);
746 fd = safe_close(fd);
747
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"));
756
757 assert_se(path_equal(path, expected1));
758 ASSERT_OK(fd_get_path(fd, &fdpath));
759 assert_se(path_equal(fdpath, expected2));
760 }
761
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()));
765
766 _cleanup_close_ int fd = -EBADF;
767 ASSERT_OK(chase(p, NULL, CHASE_PREFIX_ROOT|CHASE_MKDIR_0755, NULL, &fd));
768
769 ASSERT_OK_EQ(inode_same_at(AT_FDCWD, p, fd, NULL, AT_EMPTY_PATH), 1);
770
771 _cleanup_close_ int fd2 = -EBADF;
772 ASSERT_OK(chase(p, p, CHASE_PREFIX_ROOT|CHASE_MKDIR_0755, NULL, &fd2));
773
774 _cleanup_free_ char *pp = ASSERT_PTR(path_join(p, p));
775
776 ASSERT_OK_EQ(inode_same_at(AT_FDCWD, pp, fd2, NULL, AT_EMPTY_PATH), 1);
777
778 _cleanup_free_ char *f = NULL;
779 ASSERT_OK(path_extract_directory(p, &f));
780
781 _cleanup_free_ char *ff = NULL;
782 ASSERT_OK(path_extract_directory(f, &ff));
783
784 _cleanup_free_ char *fff = NULL;
785 ASSERT_OK(path_extract_directory(ff, &fff));
786
787 ASSERT_OK(rm_rf(fff, REMOVE_PHYSICAL));
788 }
789
790 static int intro(void) {
791 arg_test_dir = saved_argv[1];
792 return EXIT_SUCCESS;
793 }
794
795 DEFINE_TEST_MAIN_WITH_INTRO(LOG_INFO, intro);