]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/test/test-fd-util.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
4 #include <sys/eventfd.h>
8 #include "alloc-util.h"
9 #include "data-fd-util.h"
14 #include "memory-util.h"
15 #include "missing_syscall.h"
17 #include "mount-util.h"
18 #include "namespace-util.h"
19 #include "path-util.h"
20 #include "process-util.h"
21 #include "random-util.h"
22 #include "rlimit-util.h"
24 #include "seccomp-util.h"
25 #include "serialize.h"
26 #include "stat-util.h"
27 #include "string-util.h"
29 #include "tmpfile-util.h"
33 _cleanup_(unlink_tempfilep
) char name0
[] = "/tmp/test-close-many.XXXXXX";
34 _cleanup_(unlink_tempfilep
) char name1
[] = "/tmp/test-close-many.XXXXXX";
35 _cleanup_(unlink_tempfilep
) char name2
[] = "/tmp/test-close-many.XXXXXX";
37 fds
[0] = mkostemp_safe(name0
);
38 fds
[1] = mkostemp_safe(name1
);
39 fds
[2] = mkostemp_safe(name2
);
43 assert_se(fcntl(fds
[0], F_GETFD
) == -1);
44 assert_se(fcntl(fds
[1], F_GETFD
) == -1);
45 assert_se(fcntl(fds
[2], F_GETFD
) >= 0);
51 _cleanup_(unlink_tempfilep
) char name
[] = "/tmp/test-test-close_nointr.XXXXXX";
54 fd
= mkostemp_safe(name
);
56 assert_se(close_nointr(fd
) >= 0);
57 assert_se(close_nointr(fd
) < 0);
61 _cleanup_close_pair_
int p
[2];
62 _cleanup_close_
int a
, b
, c
;
64 assert_se(pipe2(p
, O_CLOEXEC
) >= 0);
65 assert_se((a
= fcntl(p
[0], F_DUPFD
, 3)) >= 0);
66 assert_se((b
= open("/dev/null", O_RDONLY
|O_CLOEXEC
)) >= 0);
67 assert_se((c
= fcntl(a
, F_DUPFD
, 3)) >= 0);
69 assert_se(same_fd(p
[0], p
[0]) > 0);
70 assert_se(same_fd(p
[1], p
[1]) > 0);
71 assert_se(same_fd(a
, a
) > 0);
72 assert_se(same_fd(b
, b
) > 0);
74 assert_se(same_fd(a
, p
[0]) > 0);
75 assert_se(same_fd(p
[0], a
) > 0);
76 assert_se(same_fd(c
, p
[0]) > 0);
77 assert_se(same_fd(p
[0], c
) > 0);
78 assert_se(same_fd(a
, c
) > 0);
79 assert_se(same_fd(c
, a
) > 0);
81 assert_se(same_fd(p
[0], p
[1]) == 0);
82 assert_se(same_fd(p
[1], p
[0]) == 0);
83 assert_se(same_fd(p
[0], b
) == 0);
84 assert_se(same_fd(b
, p
[0]) == 0);
85 assert_se(same_fd(p
[1], a
) == 0);
86 assert_se(same_fd(a
, p
[1]) == 0);
87 assert_se(same_fd(p
[1], b
) == 0);
88 assert_se(same_fd(b
, p
[1]) == 0);
90 assert_se(same_fd(a
, b
) == 0);
91 assert_se(same_fd(b
, a
) == 0);
94 TEST(open_serialization_fd
) {
95 _cleanup_close_
int fd
= -EBADF
;
97 fd
= open_serialization_fd("test");
100 assert_se(write(fd
, "test\n", 5) == 5);
103 TEST(fd_move_above_stdio
) {
104 int original_stdin
, new_fd
;
106 original_stdin
= fcntl(0, F_DUPFD
, 3);
107 assert_se(original_stdin
>= 3);
108 assert_se(close_nointr(0) != EBADF
);
110 new_fd
= open("/dev/null", O_RDONLY
);
111 assert_se(new_fd
== 0);
113 new_fd
= fd_move_above_stdio(new_fd
);
114 assert_se(new_fd
>= 3);
116 assert_se(dup(original_stdin
) == 0);
117 assert_se(close_nointr(original_stdin
) != EBADF
);
118 assert_se(close_nointr(new_fd
) != EBADF
);
121 TEST(rearrange_stdio
) {
125 r
= safe_fork("rearrange", FORK_WAIT
|FORK_LOG
, &pid
);
129 _cleanup_free_
char *path
= NULL
;
134 safe_close(STDERR_FILENO
); /* Let's close an fd < 2, to make it more interesting */
136 assert_se(rearrange_stdio(-EBADF
, -EBADF
, -EBADF
) >= 0);
138 assert_se(fd_get_path(STDIN_FILENO
, &path
) >= 0);
139 assert_se(path_equal(path
, "/dev/null"));
142 assert_se(fd_get_path(STDOUT_FILENO
, &path
) >= 0);
143 assert_se(path_equal(path
, "/dev/null"));
146 assert_se(fd_get_path(STDOUT_FILENO
, &path
) >= 0);
147 assert_se(path_equal(path
, "/dev/null"));
150 safe_close(STDIN_FILENO
);
151 safe_close(STDOUT_FILENO
);
152 safe_close(STDERR_FILENO
);
156 assert_se(pipe(pair
) >= 0);
157 assert_se(pair
[0] == 0);
158 assert_se(pair
[1] == 1);
159 assert_se(fd_move_above_stdio(0) == 3);
161 assert_se(open("/dev/full", O_WRONLY
|O_CLOEXEC
) == 0);
162 assert_se(acquire_data_fd("foobar", 6, 0) == 2);
164 assert_se(rearrange_stdio(2, 0, 1) >= 0);
166 assert_se(write(1, "x", 1) < 0 && errno
== ENOSPC
);
167 assert_se(write(2, "z", 1) == 1);
168 assert_se(read(3, buffer
, sizeof(buffer
)) == 1);
169 assert_se(buffer
[0] == 'z');
170 assert_se(read(0, buffer
, sizeof(buffer
)) == 6);
171 assert_se(memcmp(buffer
, "foobar", 6) == 0);
173 assert_se(rearrange_stdio(-EBADF
, 1, 2) >= 0);
174 assert_se(write(1, "a", 1) < 0 && errno
== ENOSPC
);
175 assert_se(write(2, "y", 1) == 1);
176 assert_se(read(3, buffer
, sizeof(buffer
)) == 1);
177 assert_se(buffer
[0] == 'y');
179 assert_se(fd_get_path(0, &path
) >= 0);
180 assert_se(path_equal(path
, "/dev/null"));
188 log_info("nr-open: %i", read_nr_open());
191 static size_t validate_fds(
198 /* Validates that fds in the specified array are one of the following three:
200 * 1. < 0 (test is skipped) or
201 * 2. opened (if 'opened' param is true) or
202 * 3. closed (if 'opened' param is false)
205 for (size_t i
= 0; i
< n_fds
; i
++) {
210 assert_se(fcntl(fds
[i
], F_GETFD
) >= 0);
212 assert_se(fcntl(fds
[i
], F_GETFD
) < 0 && errno
== EBADF
);
217 return c
; /* Return number of fds >= 0 in the array */
220 static void test_close_all_fds_inner(void) {
221 _cleanup_free_
int *fds
= NULL
, *keep
= NULL
;
222 size_t n_fds
, n_keep
;
225 log_info("/* %s */", __func__
);
227 rlimit_nofile_bump(-1);
229 max_fd
= get_max_fd();
230 assert_se(max_fd
> 10);
233 /* If the worst fallback is activated we need to iterate through all possible fds, hence,
234 * let's lower the limit a small bit, so that we don't run for too long. Yes, this undoes the
235 * rlimit_nofile_bump() call above partially. */
237 (void) setrlimit_closest(RLIMIT_NOFILE
, &(struct rlimit
) { 7000, 7000 });
241 /* Try to use 5000 fds, but when we can't bump the rlimit to make that happen use the whole limit minus 10 */
242 n_fds
= MIN(((size_t) max_fd
& ~1U) - 10U, 5000U);
243 assert_se((n_fds
& 1U) == 0U); /* make sure even number of fds */
245 /* Allocate the determined number of fds, always two at a time */
246 assert_se(fds
= new(int, n_fds
));
247 for (size_t i
= 0; i
< n_fds
; i
+= 2)
248 assert_se(pipe2(fds
+ i
, O_CLOEXEC
) >= 0);
250 /* Validate this worked */
251 assert_se(validate_fds(true, fds
, n_fds
) == n_fds
);
253 /* Randomized number of fds to keep, but at most every second */
254 n_keep
= (random_u64() % (n_fds
/ 2));
256 /* Now randomly select a number of fds from the array above to keep */
257 assert_se(keep
= new(int, n_keep
));
258 for (size_t k
= 0; k
< n_keep
; k
++) {
262 p
= random_u64() % n_fds
;
264 keep
[k
] = TAKE_FD(fds
[p
]);
270 /* Check that all fds from both arrays are still open, and test how many in each are >= 0 */
271 assert_se(validate_fds(true, fds
, n_fds
) == n_fds
- n_keep
);
272 assert_se(validate_fds(true, keep
, n_keep
) == n_keep
);
274 /* Close logging fd first, so that we don't confuse it by closing its fd */
276 log_set_open_when_needed(true);
279 /* Close all but the ones to keep */
280 assert_se(close_all_fds(keep
, n_keep
) >= 0);
282 assert_se(validate_fds(false, fds
, n_fds
) == n_fds
- n_keep
);
283 assert_se(validate_fds(true, keep
, n_keep
) == n_keep
);
285 /* Close everything else too! */
286 assert_se(close_all_fds(NULL
, 0) >= 0);
288 assert_se(validate_fds(false, fds
, n_fds
) == n_fds
- n_keep
);
289 assert_se(validate_fds(false, keep
, n_keep
) == n_keep
);
291 log_set_open_when_needed(false);
295 static int seccomp_prohibit_close_range(void) {
296 #if HAVE_SECCOMP && defined(__SNR_close_range)
297 _cleanup_(seccomp_releasep
) scmp_filter_ctx seccomp
= NULL
;
300 r
= seccomp_init_for_arch(&seccomp
, SCMP_ARCH_NATIVE
, SCMP_ACT_ALLOW
);
302 return log_warning_errno(r
, "Failed to acquire seccomp context, ignoring: %m");
304 r
= seccomp_rule_add_exact(
306 SCMP_ACT_ERRNO(EPERM
),
307 SCMP_SYS(close_range
),
310 return log_warning_errno(r
, "Failed to add close_range() rule, ignoring: %m");
312 r
= seccomp_load(seccomp
);
314 return log_warning_errno(r
, "Failed to apply close_range() restrictions, ignoring: %m");
318 return log_warning_errno(SYNTHETIC_ERRNO(EOPNOTSUPP
), "Seccomp support or close_range() syscall definition not available.");
322 TEST(close_all_fds
) {
325 /* Runs the test four times. Once as is. Once with close_range() syscall blocked via seccomp, once
326 * with /proc/ overmounted, and once with the combination of both. This should trigger all fallbacks
327 * in the close_range_all() function. */
329 r
= safe_fork("(caf-plain)", FORK_CLOSE_ALL_FDS
|FORK_DEATHSIG
|FORK_LOG
|FORK_WAIT
, NULL
);
331 test_close_all_fds_inner();
337 return (void) log_tests_skipped("Lacking privileges for test with close_range() blocked and /proc/ overmounted");
339 r
= safe_fork("(caf-noproc)", FORK_CLOSE_ALL_FDS
|FORK_DEATHSIG
|FORK_LOG
|FORK_WAIT
|FORK_NEW_MOUNTNS
|FORK_MOUNTNS_SLAVE
, NULL
);
341 r
= mount_nofollow_verbose(LOG_WARNING
, "tmpfs", "/proc", "tmpfs", 0, NULL
);
343 log_notice("Overmounting /proc/ didn't work, skipping close_all_fds() with masked /proc/.");
345 test_close_all_fds_inner();
350 if (!is_seccomp_available())
351 return (void) log_tests_skipped("Seccomp not available");
353 r
= safe_fork("(caf-seccomp)", FORK_CLOSE_ALL_FDS
|FORK_DEATHSIG
|FORK_LOG
|FORK_WAIT
, NULL
);
355 r
= seccomp_prohibit_close_range();
357 log_notice("Applying seccomp filter didn't work, skipping close_all_fds() test with masked close_range().");
359 test_close_all_fds_inner();
365 r
= safe_fork("(caf-scnp)", FORK_CLOSE_ALL_FDS
|FORK_DEATHSIG
|FORK_LOG
|FORK_WAIT
|FORK_NEW_MOUNTNS
|FORK_MOUNTNS_SLAVE
, NULL
);
367 r
= seccomp_prohibit_close_range();
369 log_notice("Applying seccomp filter didn't work, skipping close_all_fds() test with masked close_range().");
371 r
= mount_nofollow_verbose(LOG_WARNING
, "tmpfs", "/proc", "tmpfs", 0, NULL
);
373 log_notice("Overmounting /proc/ didn't work, skipping close_all_fds() with masked /proc/.");
375 test_close_all_fds_inner();
378 test_close_all_fds_inner();
384 TEST(format_proc_fd_path
) {
385 assert_se(streq_ptr(FORMAT_PROC_FD_PATH(0), "/proc/self/fd/0"));
386 assert_se(streq_ptr(FORMAT_PROC_FD_PATH(1), "/proc/self/fd/1"));
387 assert_se(streq_ptr(FORMAT_PROC_FD_PATH(2), "/proc/self/fd/2"));
388 assert_se(streq_ptr(FORMAT_PROC_FD_PATH(3), "/proc/self/fd/3"));
389 assert_se(streq_ptr(FORMAT_PROC_FD_PATH(2147483647), "/proc/self/fd/2147483647"));
393 _cleanup_close_
int fd1
= -EBADF
, fd2
= -EBADF
;
394 struct stat st1
, st2
;
397 /* Test this with a directory */
398 fd1
= open("/proc", O_DIRECTORY
|O_PATH
|O_CLOEXEC
);
401 assert_se(fstat(fd1
, &st1
) >= 0);
402 assert_se(S_ISDIR(st1
.st_mode
));
404 fl
= fcntl(fd1
, F_GETFL
);
406 assert_se(FLAGS_SET(fl
, O_DIRECTORY
));
407 assert_se(FLAGS_SET(fl
, O_PATH
));
409 /* fd_reopen() with O_NOFOLLOW will systematically fail, since it is implemented via a symlink in /proc/self/fd/ */
410 assert_se(fd_reopen(fd1
, O_RDONLY
|O_CLOEXEC
|O_NOFOLLOW
) == -ELOOP
);
411 assert_se(fd_reopen(fd1
, O_RDONLY
|O_CLOEXEC
|O_DIRECTORY
|O_NOFOLLOW
) == -ELOOP
);
413 fd2
= fd_reopen(fd1
, O_RDONLY
|O_DIRECTORY
|O_CLOEXEC
); /* drop the O_PATH */
416 assert_se(fstat(fd2
, &st2
) >= 0);
417 assert_se(S_ISDIR(st2
.st_mode
));
418 assert_se(stat_inode_same(&st1
, &st2
));
420 fl
= fcntl(fd2
, F_GETFL
);
422 assert_se(FLAGS_SET(fl
, O_DIRECTORY
));
423 assert_se(!FLAGS_SET(fl
, O_PATH
));
427 fd1
= fd_reopen(fd2
, O_DIRECTORY
|O_PATH
|O_CLOEXEC
); /* reacquire the O_PATH */
430 assert_se(fstat(fd1
, &st1
) >= 0);
431 assert_se(S_ISDIR(st1
.st_mode
));
432 assert_se(stat_inode_same(&st1
, &st2
));
434 fl
= fcntl(fd1
, F_GETFL
);
436 assert_se(FLAGS_SET(fl
, O_DIRECTORY
));
437 assert_se(FLAGS_SET(fl
, O_PATH
));
441 /* And now, test this with a file. */
442 fd1
= open("/proc/version", O_PATH
|O_CLOEXEC
);
445 assert_se(fstat(fd1
, &st1
) >= 0);
446 assert_se(S_ISREG(st1
.st_mode
));
448 fl
= fcntl(fd1
, F_GETFL
);
450 assert_se(!FLAGS_SET(fl
, O_DIRECTORY
));
451 assert_se(FLAGS_SET(fl
, O_PATH
));
453 assert_se(fd_reopen(fd1
, O_RDONLY
|O_DIRECTORY
|O_CLOEXEC
) == -ENOTDIR
);
454 fd2
= fd_reopen(fd1
, O_RDONLY
|O_CLOEXEC
); /* drop the O_PATH */
457 assert_se(fstat(fd2
, &st2
) >= 0);
458 assert_se(S_ISREG(st2
.st_mode
));
459 assert_se(stat_inode_same(&st1
, &st2
));
461 fl
= fcntl(fd2
, F_GETFL
);
463 assert_se(!FLAGS_SET(fl
, O_DIRECTORY
));
464 assert_se(!FLAGS_SET(fl
, O_PATH
));
468 assert_se(fd_reopen(fd2
, O_DIRECTORY
|O_PATH
|O_CLOEXEC
) == -ENOTDIR
);
469 fd1
= fd_reopen(fd2
, O_PATH
|O_CLOEXEC
); /* reacquire the O_PATH */
472 assert_se(fstat(fd1
, &st1
) >= 0);
473 assert_se(S_ISREG(st1
.st_mode
));
474 assert_se(stat_inode_same(&st1
, &st2
));
476 fl
= fcntl(fd1
, F_GETFL
);
478 assert_se(!FLAGS_SET(fl
, O_DIRECTORY
));
479 assert_se(FLAGS_SET(fl
, O_PATH
));
481 /* Also check the right error is generated if the fd is already closed */
483 assert_se(fd_reopen(fd1
, O_RDONLY
|O_CLOEXEC
) == -EBADF
);
486 /* Validate what happens if we reopen a symlink */
487 fd1
= open("/proc/self", O_PATH
|O_CLOEXEC
|O_NOFOLLOW
);
489 assert_se(fstat(fd1
, &st1
) >= 0);
490 assert_se(S_ISLNK(st1
.st_mode
));
492 fd2
= fd_reopen(fd1
, O_PATH
|O_CLOEXEC
);
494 assert_se(fstat(fd2
, &st2
) >= 0);
495 assert_se(S_ISLNK(st2
.st_mode
));
496 assert_se(stat_inode_same(&st1
, &st2
));
497 fd2
= safe_close(fd2
);
499 /* So here's the thing: if we have an O_PATH fd to a symlink, we *cannot* convert it to a regular fd
500 * with that. i.e. you cannot have the VFS follow a symlink pinned via an O_PATH fd. */
501 assert_se(fd_reopen(fd1
, O_RDONLY
|O_CLOEXEC
) == -ELOOP
);
504 TEST(fd_reopen_condition
) {
505 _cleanup_close_
int fd1
= -EBADF
, fd3
= -EBADF
;
508 /* Open without O_PATH */
509 fd1
= open("/usr/", O_RDONLY
|O_DIRECTORY
|O_CLOEXEC
);
512 fl
= fcntl(fd1
, F_GETFL
);
513 assert_se(FLAGS_SET(fl
, O_DIRECTORY
));
514 assert_se(!FLAGS_SET(fl
, O_PATH
));
516 fd2
= fd_reopen_condition(fd1
, O_DIRECTORY
, O_DIRECTORY
|O_PATH
, &fd3
);
517 assert_se(fd2
== fd1
);
520 /* Switch on O_PATH */
521 fd2
= fd_reopen_condition(fd1
, O_DIRECTORY
|O_PATH
, O_DIRECTORY
|O_PATH
, &fd3
);
522 assert_se(fd2
!= fd1
);
523 assert_se(fd3
== fd2
);
525 fl
= fcntl(fd2
, F_GETFL
);
526 assert_se(FLAGS_SET(fl
, O_DIRECTORY
));
527 assert_se(FLAGS_SET(fl
, O_PATH
));
529 close_and_replace(fd1
, fd3
);
531 fd2
= fd_reopen_condition(fd1
, O_DIRECTORY
|O_PATH
, O_DIRECTORY
|O_PATH
, &fd3
);
532 assert_se(fd2
== fd1
);
535 /* Switch off O_PATH again */
536 fd2
= fd_reopen_condition(fd1
, O_DIRECTORY
, O_DIRECTORY
|O_PATH
, &fd3
);
537 assert_se(fd2
!= fd1
);
538 assert_se(fd3
== fd2
);
540 fl
= fcntl(fd2
, F_GETFL
);
541 assert_se(FLAGS_SET(fl
, O_DIRECTORY
));
542 assert_se(!FLAGS_SET(fl
, O_PATH
));
544 close_and_replace(fd1
, fd3
);
546 fd2
= fd_reopen_condition(fd1
, O_DIRECTORY
, O_DIRECTORY
|O_PATH
, &fd3
);
547 assert_se(fd2
== fd1
);
552 _cleanup_close_
int fd1
= -EBADF
, fd2
= -EBADF
;
553 int array
[2] = PIPE_EBADF
, i
= 0;
555 assert_se(fd1
== -EBADF
);
556 assert_se(fd2
== -EBADF
);
558 fd1
= eventfd(0, EFD_CLOEXEC
);
562 assert_se(fd1
== -EBADF
);
565 assert_se(array
[0] == -EBADF
);
566 assert_se(array
[1] == -EBADF
);
568 array
[0] = TAKE_FD(fd2
);
569 assert_se(fd1
== -EBADF
);
570 assert_se(fd2
== -EBADF
);
571 assert_se(array
[0] >= 0);
572 assert_se(array
[1] == -EBADF
);
574 array
[1] = TAKE_FD(array
[i
]);
575 assert_se(array
[0] == -EBADF
);
576 assert_se(array
[1] >= 0);
579 array
[0] = TAKE_FD(*(array
+ i
));
580 assert_se(array
[0] >= 0);
581 assert_se(array
[1] == -EBADF
);
584 fd1
= TAKE_FD(array
[i
]);
586 assert_se(array
[0] == -EBADF
);
587 assert_se(array
[1] == -EBADF
);
590 TEST(dir_fd_is_root
) {
591 _cleanup_close_
int fd
= -EBADF
;
594 assert_se(dir_fd_is_root_or_cwd(AT_FDCWD
) > 0);
596 assert_se((fd
= open("/", O_CLOEXEC
|O_PATH
|O_DIRECTORY
|O_NOFOLLOW
)) >= 0);
597 assert_se(dir_fd_is_root(fd
) > 0);
598 assert_se(dir_fd_is_root_or_cwd(fd
) > 0);
602 assert_se((fd
= open("/usr", O_CLOEXEC
|O_PATH
|O_DIRECTORY
|O_NOFOLLOW
)) >= 0);
603 assert_se(dir_fd_is_root(fd
) == 0);
604 assert_se(dir_fd_is_root_or_cwd(fd
) == 0);
606 r
= detach_mount_namespace();
608 return (void) log_tests_skipped_errno(r
, "Failed to detach mount namespace");
610 _cleanup_(rm_rf_physical_and_freep
) char *tmp
= NULL
;
611 _cleanup_free_
char *x
= NULL
, *y
= NULL
;
613 assert_se(mkdtemp_malloc("/tmp/test-mkdir-XXXXXX", &tmp
) >= 0);
614 assert_se(x
= path_join(tmp
, "x"));
615 assert_se(y
= path_join(tmp
, "x/y"));
616 assert_se(mkdir_p(y
, 0755) >= 0);
617 assert_se(mount_nofollow_verbose(LOG_DEBUG
, x
, y
, NULL
, MS_BIND
, NULL
) >= 0);
621 assert_se((fd
= open(tmp
, O_CLOEXEC
|O_PATH
|O_DIRECTORY
|O_NOFOLLOW
)) >= 0);
622 assert_se(dir_fd_is_root(fd
) == 0);
623 assert_se(dir_fd_is_root_or_cwd(fd
) == 0);
627 assert_se((fd
= open(x
, O_CLOEXEC
|O_PATH
|O_DIRECTORY
|O_NOFOLLOW
)) >= 0);
628 assert_se(dir_fd_is_root(fd
) == 0);
629 assert_se(dir_fd_is_root_or_cwd(fd
) == 0);
633 assert_se((fd
= open(y
, O_CLOEXEC
|O_PATH
|O_DIRECTORY
|O_NOFOLLOW
)) >= 0);
634 assert_se(dir_fd_is_root(fd
) == 0);
635 assert_se(dir_fd_is_root_or_cwd(fd
) == 0);
639 _cleanup_(rm_rf_physical_and_freep
) char *t
= NULL
;
640 _cleanup_close_
int tfd
= -EBADF
, fd
= -EBADF
;
641 _cleanup_free_
char *p
= NULL
, *q
= NULL
, *saved_cwd
= NULL
;
643 tfd
= mkdtemp_open(NULL
, O_PATH
, &t
);
645 assert_se(fd_get_path(tfd
, &p
) >= 0);
646 assert_se(streq(p
, t
));
650 assert_se(safe_getcwd(&saved_cwd
) >= 0);
651 assert_se(chdir(t
) >= 0);
653 assert_se(fd_get_path(AT_FDCWD
, &p
) >= 0);
654 assert_se(streq(p
, t
));
658 assert_se(q
= path_join(t
, "regular"));
659 assert_se(touch(q
) >= 0);
660 assert_se(mkdirat_parents(tfd
, "subdir/symlink", 0755) >= 0);
661 assert_se(symlinkat("../regular", tfd
, "subdir/symlink") >= 0);
662 assert_se(symlinkat("subdir", tfd
, "symdir") >= 0);
664 fd
= openat(tfd
, "regular", O_CLOEXEC
|O_PATH
);
666 assert_se(fd_get_path(fd
, &p
) >= 0);
667 assert_se(streq(p
, q
));
672 fd
= openat(AT_FDCWD
, "regular", O_CLOEXEC
|O_PATH
);
674 assert_se(fd_get_path(fd
, &p
) >= 0);
675 assert_se(streq(p
, q
));
680 fd
= openat(tfd
, "subdir/symlink", O_CLOEXEC
|O_PATH
);
682 assert_se(fd_verify_regular(fd
) >= 0);
683 assert_se(fd_get_path(fd
, &p
) >= 0);
684 assert_se(streq(p
, q
));
689 fd
= openat(AT_FDCWD
, "subdir/symlink", O_CLOEXEC
|O_PATH
);
691 assert_se(fd_verify_regular(fd
) >= 0);
692 assert_se(fd_get_path(fd
, &p
) >= 0);
693 assert_se(streq(p
, q
));
698 fd
= openat(tfd
, "symdir//./symlink", O_CLOEXEC
|O_PATH
);
700 assert_se(fd_verify_regular(fd
) >= 0);
701 assert_se(fd_get_path(fd
, &p
) >= 0);
702 assert_se(streq(p
, q
));
707 fd
= openat(AT_FDCWD
, "symdir//./symlink", O_CLOEXEC
|O_PATH
);
709 assert_se(fd_verify_regular(fd
) >= 0);
710 assert_se(fd_get_path(fd
, &p
) >= 0);
711 assert_se(streq(p
, q
));
717 assert_se(q
= path_join(t
, "subdir/symlink"));
718 fd
= openat(tfd
, "subdir/symlink", O_CLOEXEC
|O_PATH
|O_NOFOLLOW
);
720 assert_se(fd_verify_regular(fd
) == -ELOOP
);
721 assert_se(fd_get_path(fd
, &p
) >= 0);
722 assert_se(streq(p
, q
));
727 fd
= openat(AT_FDCWD
, "subdir/symlink", O_CLOEXEC
|O_PATH
|O_NOFOLLOW
);
729 assert_se(fd_verify_regular(fd
) == -ELOOP
);
730 assert_se(fd_get_path(fd
, &p
) >= 0);
731 assert_se(streq(p
, q
));
736 fd
= openat(tfd
, "symdir//./symlink", O_CLOEXEC
|O_PATH
|O_NOFOLLOW
);
738 assert_se(fd_verify_regular(fd
) == -ELOOP
);
739 assert_se(fd_get_path(fd
, &p
) >= 0);
740 assert_se(streq(p
, q
));
745 fd
= openat(AT_FDCWD
, "symdir//./symlink", O_CLOEXEC
|O_PATH
|O_NOFOLLOW
);
747 assert_se(fd_verify_regular(fd
) == -ELOOP
);
748 assert_se(fd_get_path(fd
, &p
) >= 0);
749 assert_se(streq(p
, q
));
751 assert_se(chdir(saved_cwd
) >= 0);
754 DEFINE_TEST_MAIN(LOG_DEBUG
);