The branch main has been updated by des: URL: https://cgit.FreeBSD.org/src/commit/?id=8d02b7190d3b926118fafc6a70a80676c349e186
commit 8d02b7190d3b926118fafc6a70a80676c349e186 Author: Dag-Erling Smørgrav <d...@freebsd.org> AuthorDate: 2025-07-02 10:22:16 +0000 Commit: Dag-Erling Smørgrav <d...@freebsd.org> CommitDate: 2025-07-02 10:22:29 +0000 fts: Add test cases for unreadable directories. Sponsored by: Klara, Inc. Reviewed by: kevans Differential Revision: https://reviews.freebsd.org/D51098 --- lib/libc/tests/gen/Makefile | 9 ++++ lib/libc/tests/gen/fts_misc_test.c | 78 ++++++++++++++++++++++++++++++++ lib/libc/tests/gen/fts_options_test.c | 84 +++++------------------------------ lib/libc/tests/gen/fts_test.h | 81 +++++++++++++++++++++++++++++++++ 4 files changed, 180 insertions(+), 72 deletions(-) diff --git a/lib/libc/tests/gen/Makefile b/lib/libc/tests/gen/Makefile index b7df4b1d037b..a967ad5ddf91 100644 --- a/lib/libc/tests/gen/Makefile +++ b/lib/libc/tests/gen/Makefile @@ -10,6 +10,7 @@ ATF_TESTS_C+= fpclassify2_test .if ${COMPILER_FEATURES:Mblocks} ATF_TESTS_C+= fts_blocks_test .endif +ATF_TESTS_C+= fts_misc_test ATF_TESTS_C+= fts_options_test ATF_TESTS_C+= ftw_test ATF_TESTS_C+= getentropy_test @@ -104,6 +105,14 @@ TEST_METADATA.setdomainname_test+= is_exclusive=true TESTS_SUBDIRS= execve TESTS_SUBDIRS+= posix_spawn +# Tests that require address sanitizer +.if ${COMPILER_FEATURES:Masan} +.for t in scandir_test realpath2_test +CFLAGS.${t}.c+= -fsanitize=address +LDFLAGS.${t}+= -fsanitize=address +.endfor +.endif + # Tests that require blocks support .for t in fts_blocks_test glob_blocks_test scandir_blocks_test CFLAGS.${t}.c+= -fblocks diff --git a/lib/libc/tests/gen/fts_misc_test.c b/lib/libc/tests/gen/fts_misc_test.c new file mode 100644 index 000000000000..91640078f63c --- /dev/null +++ b/lib/libc/tests/gen/fts_misc_test.c @@ -0,0 +1,78 @@ +/*- + * Copyright (c) 2025 Klara, Inc. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <sys/stat.h> + +#include <fcntl.h> +#include <fts.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <atf-c.h> + +#include "fts_test.h" + +ATF_TC(fts_unrdir); +ATF_TC_HEAD(fts_unrdir, tc) +{ + atf_tc_set_md_var(tc, "descr", "unreadable directories"); + atf_tc_set_md_var(tc, "require.user", "unprivileged"); +} +ATF_TC_BODY(fts_unrdir, tc) +{ + ATF_REQUIRE_EQ(0, mkdir("dir", 0755)); + ATF_REQUIRE_EQ(0, mkdir("dir/unr", 0100)); + ATF_REQUIRE_EQ(0, mkdir("dir/unx", 0400)); + fts_test(tc, &(struct fts_testcase){ + (char *[]){ "dir", NULL }, + FTS_PHYSICAL, + (struct fts_expect[]){ + { FTS_D, "dir", "dir" }, + { FTS_D, "unr", "unr" }, + { FTS_DNR, "unr", "unr" }, + { FTS_D, "unx", "unx" }, + { FTS_DP, "unx", "unx" }, + { FTS_DP, "dir", "dir" }, + { 0 } + }, + }); +} + +ATF_TC(fts_unrdir_nochdir); +ATF_TC_HEAD(fts_unrdir_nochdir, tc) +{ + atf_tc_set_md_var(tc, "descr", "unreadable directories (nochdir)"); + atf_tc_set_md_var(tc, "require.user", "unprivileged"); +} +ATF_TC_BODY(fts_unrdir_nochdir, tc) +{ + ATF_REQUIRE_EQ(0, mkdir("dir", 0755)); + ATF_REQUIRE_EQ(0, mkdir("dir/unr", 0100)); + ATF_REQUIRE_EQ(0, mkdir("dir/unx", 0400)); + fts_test(tc, &(struct fts_testcase){ + (char *[]){ "dir", NULL }, + FTS_PHYSICAL | FTS_NOCHDIR, + (struct fts_expect[]){ + { FTS_D, "dir", "dir" }, + { FTS_D, "unr", "dir/unr" }, + { FTS_DNR, "unr", "dir/unr" }, + { FTS_D, "unx", "dir/unx" }, + { FTS_DP, "unx", "dir/unx" }, + { FTS_DP, "dir", "dir" }, + { 0 } + }, + }); +} + +ATF_TP_ADD_TCS(tp) +{ + fts_check_debug(); + ATF_TP_ADD_TC(tp, fts_unrdir); + ATF_TP_ADD_TC(tp, fts_unrdir_nochdir); + return (atf_no_error()); +} diff --git a/lib/libc/tests/gen/fts_options_test.c b/lib/libc/tests/gen/fts_options_test.c index c80474b70ac7..fc3015138a49 100644 --- a/lib/libc/tests/gen/fts_options_test.c +++ b/lib/libc/tests/gen/fts_options_test.c @@ -15,17 +15,7 @@ #include <atf-c.h> -struct fts_expect { - int fts_info; - const char *fts_name; - const char *fts_accpath; -}; - -struct fts_testcase { - char **paths; - int fts_options; - struct fts_expect *fts_expect; -}; +#include "fts_test.h" static char *all_paths[] = { "dir", @@ -37,20 +27,12 @@ static char *all_paths[] = { NULL }; -/* shorter name for dead links */ -#define FTS_DL FTS_SLNONE - -/* are we being debugged? */ -static bool debug; - /* * Prepare the files and directories we will be inspecting. */ static void fts_options_prepare(const struct atf_tc *tc) { - debug = !getenv("__RUNNING_INSIDE_ATF_RUN") && - isatty(STDERR_FILENO); ATF_REQUIRE_EQ(0, mkdir("dir", 0755)); ATF_REQUIRE_EQ(0, close(creat("file", 0644))); ATF_REQUIRE_EQ(0, close(creat("dir/file", 0644))); @@ -60,49 +42,6 @@ fts_options_prepare(const struct atf_tc *tc) ATF_REQUIRE_EQ(0, symlink("noent", "dead")); } -/* - * Lexical order for reproducability. - */ -static int -fts_options_compar(const FTSENT * const *a, const FTSENT * const *b) -{ - return (strcmp((*a)->fts_name, (*b)->fts_name)); -} - -/* - * Run FTS with the specified paths and options and verify that it - * produces the expected result in the correct order. - */ -static void -fts_options_test(const struct atf_tc *tc, const struct fts_testcase *fts_tc) -{ - FTS *fts; - FTSENT *ftse; - const struct fts_expect *expect = fts_tc->fts_expect; - long level = 0; - - fts = fts_open(fts_tc->paths, fts_tc->fts_options, fts_options_compar); - ATF_REQUIRE_MSG(fts != NULL, "fts_open(): %m"); - while ((ftse = fts_read(fts)) != NULL && expect->fts_name != NULL) { - if (expect->fts_info == FTS_DP) - level--; - if (debug) { - fprintf(stderr, "%2ld %2d %s\n", level, - ftse->fts_info, ftse->fts_name); - } - ATF_CHECK_STREQ(expect->fts_name, ftse->fts_name); - ATF_CHECK_STREQ(expect->fts_accpath, ftse->fts_accpath); - ATF_CHECK_INTEQ(expect->fts_info, ftse->fts_info); - ATF_CHECK_INTEQ(level, ftse->fts_level); - if (expect->fts_info == FTS_D) - level++; - expect++; - } - ATF_CHECK_EQ(NULL, ftse); - ATF_CHECK_EQ(NULL, expect->fts_name); - ATF_REQUIRE_EQ_MSG(0, fts_close(fts), "fts_close(): %m"); -} - ATF_TC(fts_options_logical); ATF_TC_HEAD(fts_options_logical, tc) { @@ -111,7 +50,7 @@ ATF_TC_HEAD(fts_options_logical, tc) ATF_TC_BODY(fts_options_logical, tc) { fts_options_prepare(tc); - fts_options_test(tc, &(struct fts_testcase){ + fts_test(tc, &(struct fts_testcase){ all_paths, FTS_LOGICAL, (struct fts_expect[]){ @@ -162,7 +101,7 @@ ATF_TC_BODY(fts_options_logical_nostat, tc) */ atf_tc_expect_fail("FTS_LOGICAL nullifies FTS_NOSTAT"); fts_options_prepare(tc); - fts_options_test(tc, &(struct fts_testcase){ + fts_test(tc, &(struct fts_testcase){ all_paths, FTS_LOGICAL | FTS_NOSTAT, (struct fts_expect[]){ @@ -203,7 +142,7 @@ ATF_TC_HEAD(fts_options_logical_seedot, tc) ATF_TC_BODY(fts_options_logical_seedot, tc) { fts_options_prepare(tc); - fts_options_test(tc, &(struct fts_testcase){ + fts_test(tc, &(struct fts_testcase){ all_paths, FTS_LOGICAL | FTS_SEEDOT, (struct fts_expect[]){ @@ -252,7 +191,7 @@ ATF_TC_HEAD(fts_options_physical, tc) ATF_TC_BODY(fts_options_physical, tc) { fts_options_prepare(tc); - fts_options_test(tc, &(struct fts_testcase){ + fts_test(tc, &(struct fts_testcase){ all_paths, FTS_PHYSICAL, (struct fts_expect[]){ @@ -278,7 +217,7 @@ ATF_TC_HEAD(fts_options_physical_nochdir, tc) ATF_TC_BODY(fts_options_physical_nochdir, tc) { fts_options_prepare(tc); - fts_options_test(tc, &(struct fts_testcase){ + fts_test(tc, &(struct fts_testcase){ all_paths, FTS_PHYSICAL | FTS_NOCHDIR, (struct fts_expect[]){ @@ -304,7 +243,7 @@ ATF_TC_HEAD(fts_options_physical_comfollow, tc) ATF_TC_BODY(fts_options_physical_comfollow, tc) { fts_options_prepare(tc); - fts_options_test(tc, &(struct fts_testcase){ + fts_test(tc, &(struct fts_testcase){ all_paths, FTS_PHYSICAL | FTS_COMFOLLOW, (struct fts_expect[]){ @@ -333,7 +272,7 @@ ATF_TC_HEAD(fts_options_physical_comfollowdir, tc) ATF_TC_BODY(fts_options_physical_comfollowdir, tc) { fts_options_prepare(tc); - fts_options_test(tc, &(struct fts_testcase){ + fts_test(tc, &(struct fts_testcase){ all_paths, FTS_PHYSICAL | FTS_COMFOLLOWDIR, (struct fts_expect[]){ @@ -362,7 +301,7 @@ ATF_TC_HEAD(fts_options_physical_nostat, tc) ATF_TC_BODY(fts_options_physical_nostat, tc) { fts_options_prepare(tc); - fts_options_test(tc, &(struct fts_testcase){ + fts_test(tc, &(struct fts_testcase){ all_paths, FTS_PHYSICAL | FTS_NOSTAT, (struct fts_expect[]){ @@ -388,7 +327,7 @@ ATF_TC_HEAD(fts_options_physical_nostat_type, tc) ATF_TC_BODY(fts_options_physical_nostat_type, tc) { fts_options_prepare(tc); - fts_options_test(tc, &(struct fts_testcase){ + fts_test(tc, &(struct fts_testcase){ all_paths, FTS_PHYSICAL | FTS_NOSTAT_TYPE, (struct fts_expect[]){ @@ -414,7 +353,7 @@ ATF_TC_HEAD(fts_options_physical_seedot, tc) ATF_TC_BODY(fts_options_physical_seedot, tc) { fts_options_prepare(tc); - fts_options_test(tc, &(struct fts_testcase){ + fts_test(tc, &(struct fts_testcase){ all_paths, FTS_PHYSICAL | FTS_SEEDOT, (struct fts_expect[]){ @@ -440,6 +379,7 @@ ATF_TC_BODY(fts_options_physical_seedot, tc) ATF_TP_ADD_TCS(tp) { + fts_check_debug(); ATF_TP_ADD_TC(tp, fts_options_logical); ATF_TP_ADD_TC(tp, fts_options_logical_nostat); ATF_TP_ADD_TC(tp, fts_options_logical_seedot); diff --git a/lib/libc/tests/gen/fts_test.h b/lib/libc/tests/gen/fts_test.h new file mode 100644 index 000000000000..b3f15050f265 --- /dev/null +++ b/lib/libc/tests/gen/fts_test.h @@ -0,0 +1,81 @@ +/*- + * Copyright (c) 2025 Klara, Inc. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#ifndef FTS_TEST_H_INCLUDED +#define FTS_TEST_H_INCLUDED + +struct fts_expect { + int fts_info; + const char *fts_name; + const char *fts_accpath; +}; + +struct fts_testcase { + char **paths; + int fts_options; + struct fts_expect *fts_expect; +}; + +/* shorter name for dead links */ +#define FTS_DL FTS_SLNONE + +/* are we being debugged? */ +static bool fts_test_debug; + +/* + * Set debug flag if appropriate. + */ +static void +fts_check_debug(void) +{ + fts_test_debug = !getenv("__RUNNING_INSIDE_ATF_RUN") && + isatty(STDERR_FILENO); +} + +/* + * Lexical order for reproducability. + */ +static int +fts_lexical_compar(const FTSENT * const *a, const FTSENT * const *b) +{ + return (strcmp((*a)->fts_name, (*b)->fts_name)); +} + +/* + * Run FTS with the specified paths and options and verify that it + * produces the expected result in the correct order. + */ +static void +fts_test(const struct atf_tc *tc, const struct fts_testcase *fts_tc) +{ + FTS *fts; + FTSENT *ftse; + const struct fts_expect *expect = fts_tc->fts_expect; + long level = 0; + + fts = fts_open(fts_tc->paths, fts_tc->fts_options, fts_lexical_compar); + ATF_REQUIRE_MSG(fts != NULL, "fts_open(): %m"); + while ((ftse = fts_read(fts)) != NULL && expect->fts_name != NULL) { + if (expect->fts_info == FTS_DP || expect->fts_info == FTS_DNR) + level--; + if (fts_test_debug) { + fprintf(stderr, "%2ld %2d %s\n", level, + ftse->fts_info, ftse->fts_name); + } + ATF_CHECK_STREQ(expect->fts_name, ftse->fts_name); + ATF_CHECK_STREQ(expect->fts_accpath, ftse->fts_accpath); + ATF_CHECK_INTEQ(expect->fts_info, ftse->fts_info); + ATF_CHECK_INTEQ(level, ftse->fts_level); + if (expect->fts_info == FTS_D) + level++; + expect++; + } + ATF_CHECK_EQ(NULL, ftse); + ATF_CHECK_EQ(NULL, expect->fts_name); + ATF_REQUIRE_EQ_MSG(0, fts_close(fts), "fts_close(): %m"); +} + +#endif /* FTS_TEST_H_INCLUDED */