aboutsummaryrefslogtreecommitdiff
path: root/lib/libc/tests/gen
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libc/tests/gen')
-rw-r--r--lib/libc/tests/gen/Makefile30
-rw-r--r--lib/libc/tests/gen/Makefile.depend1
-rw-r--r--lib/libc/tests/gen/arc4random_test.c29
-rw-r--r--lib/libc/tests/gen/dir2_test.c3
-rw-r--r--lib/libc/tests/gen/dlopen_empty_test.c3
-rw-r--r--lib/libc/tests/gen/execve/Makefile2
-rw-r--r--lib/libc/tests/gen/execve/Makefile.depend1
-rw-r--r--lib/libc/tests/gen/fmtcheck_test.c3
-rw-r--r--lib/libc/tests/gen/fmtmsg_test.c3
-rw-r--r--lib/libc/tests/gen/fnmatch_test.c84
-rw-r--r--lib/libc/tests/gen/fnmatch_testcases.h3
-rw-r--r--lib/libc/tests/gen/fpclassify2_test.c2
-rw-r--r--lib/libc/tests/gen/fts_blocks_test.c68
-rw-r--r--lib/libc/tests/gen/fts_misc_test.c78
-rw-r--r--lib/libc/tests/gen/fts_options_test.c394
-rw-r--r--lib/libc/tests/gen/fts_test.h81
-rw-r--r--lib/libc/tests/gen/ftw_test.c3
-rw-r--r--lib/libc/tests/gen/getentropy_test.c16
-rw-r--r--lib/libc/tests/gen/getmntinfo_test.c3
-rw-r--r--lib/libc/tests/gen/glob2_test.c78
-rw-r--r--lib/libc/tests/gen/glob_blocks_test.c62
-rw-r--r--lib/libc/tests/gen/makecontext_test.c5
-rw-r--r--lib/libc/tests/gen/opendir_test.c145
-rw-r--r--lib/libc/tests/gen/popen_test.c3
-rw-r--r--lib/libc/tests/gen/posix_spawn/Makefile2
-rw-r--r--lib/libc/tests/gen/posix_spawn/Makefile.depend1
-rw-r--r--lib/libc/tests/gen/posix_spawn_test.c53
-rw-r--r--lib/libc/tests/gen/realpath2_test.c3
-rw-r--r--lib/libc/tests/gen/scandir_blocks_test.c118
-rw-r--r--lib/libc/tests/gen/scandir_test.c195
-rw-r--r--lib/libc/tests/gen/sig2str_test.c213
-rw-r--r--lib/libc/tests/gen/sigsetops_test.c5
-rwxr-xr-xlib/libc/tests/gen/spawnp_enoexec.sh1
-rw-r--r--lib/libc/tests/gen/test-fnmatch.c3
-rw-r--r--lib/libc/tests/gen/wordexp_test.c29
35 files changed, 1648 insertions, 75 deletions
diff --git a/lib/libc/tests/gen/Makefile b/lib/libc/tests/gen/Makefile
index a808f6617ec7..8c2151105209 100644
--- a/lib/libc/tests/gen/Makefile
+++ b/lib/libc/tests/gen/Makefile
@@ -1,5 +1,3 @@
-# $FreeBSD$
-
.include <bsd.own.mk>
ATF_TESTS_C+= arc4random_test
@@ -9,14 +7,28 @@ ATF_TESTS_C+= fmtcheck2_test
ATF_TESTS_C+= fmtmsg_test
ATF_TESTS_C+= fnmatch2_test
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
ATF_TESTS_C+= getmntinfo_test
ATF_TESTS_C+= glob2_test
+.if ${COMPILER_FEATURES:Mblocks}
+ATF_TESTS_C+= glob_blocks_test
+.endif
ATF_TESTS_C+= makecontext_test
+ATF_TESTS_C+= opendir_test
ATF_TESTS_C+= popen_test
ATF_TESTS_C+= posix_spawn_test
ATF_TESTS_C+= realpath2_test
+ATF_TESTS_C+= scandir_test
+.if ${COMPILER_FEATURES:Mblocks}
+ATF_TESTS_C+= scandir_blocks_test
+.endif
+ATF_TESTS_C+= sig2str_test
ATF_TESTS_C+= sigsetops_test
ATF_TESTS_C+= wordexp_test
@@ -94,6 +106,20 @@ 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
+LIBADD.${t}+= BlocksRuntime
+.endfor
+
# The old testcase name
TEST_FNMATCH= test-fnmatch
CLEANFILES+= ${GEN_SH_CASE_TESTCASES}
diff --git a/lib/libc/tests/gen/Makefile.depend b/lib/libc/tests/gen/Makefile.depend
index a359c100b4e2..33d10e940cd0 100644
--- a/lib/libc/tests/gen/Makefile.depend
+++ b/lib/libc/tests/gen/Makefile.depend
@@ -1,4 +1,3 @@
-# $FreeBSD$
# Autogenerated - do NOT edit!
DIRDEPS = \
diff --git a/lib/libc/tests/gen/arc4random_test.c b/lib/libc/tests/gen/arc4random_test.c
index a28db303e88c..79139f29f8fa 100644
--- a/lib/libc/tests/gen/arc4random_test.c
+++ b/lib/libc/tests/gen/arc4random_test.c
@@ -1,5 +1,6 @@
/*-
* Copyright (c) 2011 David Schultz
+ * Copyright (c) 2024 Robert Clausecker <fuz@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -24,14 +25,12 @@
* SUCH DAMAGE.
*/
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <errno.h>
#include <stdio.h>
+#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
@@ -80,10 +79,34 @@ ATF_TC_BODY(test_arc4random, tc)
"sequences are the same");
}
+/*
+ * Test whether arc4random_uniform() returns a number below the given threshold.
+ * Test with various thresholds.
+ */
+ATF_TC_WITHOUT_HEAD(test_arc4random_uniform);
+ATF_TC_BODY(test_arc4random_uniform, tc)
+{
+ size_t i, j;
+ static const uint32_t thresholds[] = {
+ 1, 2, 3, 4, 5, 10, 100, 1000,
+ INT32_MAX, (uint32_t)INT32_MAX + 1,
+ UINT32_MAX - 1000000000, UINT32_MAX - 1000000, UINT32_MAX - 1, 0
+ };
+
+ for (i = 0; thresholds[i] != 0; i++)
+ for (j = 0; j < 10000; j++)
+ ATF_CHECK(arc4random_uniform(thresholds[i]) < thresholds[i]);
+
+ /* for a threshold of zero, just check that we get zero every time */
+ for (j = 0; j < 1000; j++)
+ ATF_CHECK_EQ(0, arc4random_uniform(0));
+}
+
ATF_TP_ADD_TCS(tp)
{
ATF_TP_ADD_TC(tp, test_arc4random);
+ ATF_TP_ADD_TC(tp, test_arc4random_uniform);
return (atf_no_error());
}
diff --git a/lib/libc/tests/gen/dir2_test.c b/lib/libc/tests/gen/dir2_test.c
index 3322a5e5086c..4ec5a1759d06 100644
--- a/lib/libc/tests/gen/dir2_test.c
+++ b/lib/libc/tests/gen/dir2_test.c
@@ -31,9 +31,6 @@
* opendir, readdir, seekdir, telldir, closedir, etc
*/
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
#include <dirent.h>
#include <fcntl.h>
#include <stdio.h>
diff --git a/lib/libc/tests/gen/dlopen_empty_test.c b/lib/libc/tests/gen/dlopen_empty_test.c
index 42f9269a10b2..6fb3bf8d8343 100644
--- a/lib/libc/tests/gen/dlopen_empty_test.c
+++ b/lib/libc/tests/gen/dlopen_empty_test.c
@@ -24,9 +24,6 @@
* SUCH DAMAGE.
*/
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
#include <sys/stat.h>
#include <dlfcn.h>
#include <errno.h>
diff --git a/lib/libc/tests/gen/execve/Makefile b/lib/libc/tests/gen/execve/Makefile
index 5e8bc6fb7640..7bf911d1cb47 100644
--- a/lib/libc/tests/gen/execve/Makefile
+++ b/lib/libc/tests/gen/execve/Makefile
@@ -1,5 +1,3 @@
-# $FreeBSD$
-
.include <bsd.own.mk>
NETBSD_ATF_TESTS_C= execve_test
diff --git a/lib/libc/tests/gen/execve/Makefile.depend b/lib/libc/tests/gen/execve/Makefile.depend
index 10e58b789640..e89a5c52c82a 100644
--- a/lib/libc/tests/gen/execve/Makefile.depend
+++ b/lib/libc/tests/gen/execve/Makefile.depend
@@ -1,4 +1,3 @@
-# $FreeBSD$
# Autogenerated - do NOT edit!
DIRDEPS = \
diff --git a/lib/libc/tests/gen/fmtcheck_test.c b/lib/libc/tests/gen/fmtcheck_test.c
index 3e180c9ca2e6..27a12217e81e 100644
--- a/lib/libc/tests/gen/fmtcheck_test.c
+++ b/lib/libc/tests/gen/fmtcheck_test.c
@@ -28,9 +28,6 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
#include <sys/param.h>
#include <err.h>
#include <stdio.h>
diff --git a/lib/libc/tests/gen/fmtmsg_test.c b/lib/libc/tests/gen/fmtmsg_test.c
index aa3ca19425fe..30a5156cdcc8 100644
--- a/lib/libc/tests/gen/fmtmsg_test.c
+++ b/lib/libc/tests/gen/fmtmsg_test.c
@@ -24,9 +24,6 @@
* SUCH DAMAGE.
*/
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
#include <sys/param.h>
#include <sys/wait.h>
#include <err.h>
diff --git a/lib/libc/tests/gen/fnmatch_test.c b/lib/libc/tests/gen/fnmatch_test.c
index 8d9ead2eff91..0ff7400a4a4f 100644
--- a/lib/libc/tests/gen/fnmatch_test.c
+++ b/lib/libc/tests/gen/fnmatch_test.c
@@ -24,11 +24,9 @@
* SUCH DAMAGE.
*/
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
#include <sys/param.h>
#include <errno.h>
+#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -179,10 +177,90 @@ ATF_TC_BODY(fnmatch_test, tc)
}
+ATF_TC(fnmatch_characterclass);
+ATF_TC_HEAD(fnmatch_characterclass, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test fnmatch with character classes");
+}
+
+ATF_TC_BODY(fnmatch_characterclass, tc)
+{
+ ATF_CHECK(fnmatch("[[:alnum:]]", "a", 0) == 0);
+ ATF_CHECK(fnmatch("[[:cntrl:]]", "\a", 0) == 0);
+ ATF_CHECK(fnmatch("[[:lower:]]", "a", 0) == 0);
+ ATF_CHECK(fnmatch("[[:space:]]", " ", 0) == 0);
+ ATF_CHECK(fnmatch("[[:alpha:]]", "a", 0) == 0);
+ ATF_CHECK(fnmatch("[[:digit:]]", "0", 0) == 0);
+ ATF_CHECK(fnmatch("[[:print:]]", "a", 0) == 0);
+ ATF_CHECK(fnmatch("[[:upper:]]", "A", 0) == 0);
+ ATF_CHECK(fnmatch("[[:blank:]]", " ", 0) == 0);
+ ATF_CHECK(fnmatch("[[:graph:]]", "a", 0) == 0);
+ ATF_CHECK(fnmatch("[[:punct:]]", ".", 0) == 0);
+ ATF_CHECK(fnmatch("[[:xdigit:]]", "f", 0) == 0);
+
+ /*
+ * POSIX.1, section 9.3.5. states that '[:' and ':]'
+ * should be interpreted as character classes symbol only
+ * when part of a bracket expression.
+ */
+ ATF_CHECK(fnmatch("[:alnum:]", "a", 0) == 0);
+ ATF_CHECK(fnmatch("[:alnum:]", ":", 0) == 0);
+ ATF_CHECK(fnmatch("[:alnum:]", "1", 0) != 0);
+}
+
+ATF_TC(fnmatch_collsym);
+ATF_TC_HEAD(fnmatch_collsym, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test fnmatch with collating symbols");
+}
+
+ATF_TC_BODY(fnmatch_collsym, tc)
+{
+ setlocale(LC_ALL, "cs_CZ.UTF-8");
+ ATF_CHECK(fnmatch("[ch]", "ch", 0) != 0);
+ ATF_CHECK(fnmatch("[[.ch.]]", "ch", 0) == 0);
+ ATF_CHECK(fnmatch("[[.ch.]]h", "chh", 0) == 0);
+
+ /*
+ * POSIX.1, section 9.3.5. states that '[.' and '.]'
+ * should be interpreted as a collating symbol only
+ * when part of a bracket expression.
+ */
+ ATF_CHECK(fnmatch("[.ch.]", "c", 0) == 0);
+ ATF_CHECK(fnmatch("[.ch.]", "h", 0) == 0);
+ ATF_CHECK(fnmatch("[.ch.]", ".", 0) == 0);
+}
+
+ATF_TC(fnmatch_equivclass);
+ATF_TC_HEAD(fnmatch_equivclass, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test fnmatch with equivalence classes");
+}
+
+ATF_TC_BODY(fnmatch_equivclass, tc)
+{
+ setlocale(LC_ALL, "en_US.UTF-8");
+ ATF_CHECK(fnmatch("[[=a=]]b", "ab", 0) == 0);
+ ATF_CHECK(fnmatch("[[=a=]]b", "Ab", 0) == 0);
+ ATF_CHECK(fnmatch("[[=à=]]b", "ab", 0) == 0);
+ ATF_CHECK(fnmatch("[[=a=]]b", "àb", 0) == 0);
+
+ /*
+ * POSIX.1, section 9.3.5. states that '[=' and '=]'
+ * should be interpreted as an equivalence class only
+ * when part of a bracket expression.
+ */
+ ATF_CHECK(fnmatch("[=a=]b", "=b", 0) == 0);
+ ATF_CHECK(fnmatch("[=a=]b", "ab", 0) == 0);
+}
+
ATF_TP_ADD_TCS(tp)
{
ATF_TP_ADD_TC(tp, fnmatch_test);
+ ATF_TP_ADD_TC(tp, fnmatch_collsym);
+ ATF_TP_ADD_TC(tp, fnmatch_characterclass);
+ ATF_TP_ADD_TC(tp, fnmatch_equivclass);
return (atf_no_error());
}
diff --git a/lib/libc/tests/gen/fnmatch_testcases.h b/lib/libc/tests/gen/fnmatch_testcases.h
index 996e13c7756b..196160a4801b 100644
--- a/lib/libc/tests/gen/fnmatch_testcases.h
+++ b/lib/libc/tests/gen/fnmatch_testcases.h
@@ -24,9 +24,6 @@
* SUCH DAMAGE.
*/
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
#include <fnmatch.h>
struct testcase {
diff --git a/lib/libc/tests/gen/fpclassify2_test.c b/lib/libc/tests/gen/fpclassify2_test.c
index a6bb1df3c9f2..45180ac5be42 100644
--- a/lib/libc/tests/gen/fpclassify2_test.c
+++ b/lib/libc/tests/gen/fpclassify2_test.c
@@ -22,8 +22,6 @@
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
- *
- * $FreeBSD$
*/
#include <math.h>
diff --git a/lib/libc/tests/gen/fts_blocks_test.c b/lib/libc/tests/gen/fts_blocks_test.c
new file mode 100644
index 000000000000..f020dd8dea45
--- /dev/null
+++ b/lib/libc/tests/gen/fts_blocks_test.c
@@ -0,0 +1,68 @@
+/*-
+ * Copyright (c) 2025 Klara, Inc.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/stat.h>
+
+#include <fcntl.h>
+#include <fts.h>
+
+#include <atf-c.h>
+
+/*
+ * Create two directories with three files each in lexicographical order,
+ * then call FTS with a sort block that sorts in reverse lexicographical
+ * order. This has the least chance of getting a false positive due to
+ * differing file system semantics. UFS will return the files in the
+ * order they were created while ZFS will sort them lexicographically; in
+ * both cases, the order we expect is the reverse.
+ */
+ATF_TC(fts_blocks_test);
+ATF_TC_HEAD(fts_blocks_test, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test FTS with a block in lieu of a comparison function");
+}
+ATF_TC_BODY(fts_blocks_test, tc)
+{
+ char *args[] = {
+ "bar", "foo", NULL
+ };
+ char *paths[] = {
+ "foo", "z", "y", "x", "foo",
+ "bar", "c", "b", "a", "bar",
+ NULL
+ };
+ char **expect = paths;
+ FTS *fts;
+ FTSENT *ftse;
+
+ ATF_REQUIRE_EQ(0, mkdir("bar", 0755));
+ ATF_REQUIRE_EQ(0, close(creat("bar/a", 0644)));
+ ATF_REQUIRE_EQ(0, close(creat("bar/b", 0644)));
+ ATF_REQUIRE_EQ(0, close(creat("bar/c", 0644)));
+ ATF_REQUIRE_EQ(0, mkdir("foo", 0755));
+ ATF_REQUIRE_EQ(0, close(creat("foo/x", 0644)));
+ ATF_REQUIRE_EQ(0, close(creat("foo/y", 0644)));
+ ATF_REQUIRE_EQ(0, close(creat("foo/z", 0644)));
+ fts = fts_open_b(args, 0,
+ ^(const FTSENT * const *a, const FTSENT * const *b) {
+ return (strcmp((*b)->fts_name, (*a)->fts_name));
+ });
+ ATF_REQUIRE_MSG(fts != NULL, "fts_open_b(): %m");
+ while ((ftse = fts_read(fts)) != NULL && *expect != NULL) {
+ ATF_CHECK_STREQ(*expect, ftse->fts_name);
+ expect++;
+ }
+ ATF_CHECK_EQ(NULL, ftse);
+ ATF_CHECK_EQ(NULL, *expect);
+ ATF_REQUIRE_EQ_MSG(0, fts_close(fts), "fts_close(): %m");
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, fts_blocks_test);
+ return (atf_no_error());
+}
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
new file mode 100644
index 000000000000..fc3015138a49
--- /dev/null
+++ b/lib/libc/tests/gen/fts_options_test.c
@@ -0,0 +1,394 @@
+/*-
+ * 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"
+
+static char *all_paths[] = {
+ "dir",
+ "dirl",
+ "file",
+ "filel",
+ "dead",
+ "noent",
+ NULL
+};
+
+/*
+ * Prepare the files and directories we will be inspecting.
+ */
+static void
+fts_options_prepare(const struct atf_tc *tc)
+{
+ ATF_REQUIRE_EQ(0, mkdir("dir", 0755));
+ ATF_REQUIRE_EQ(0, close(creat("file", 0644)));
+ ATF_REQUIRE_EQ(0, close(creat("dir/file", 0644)));
+ ATF_REQUIRE_EQ(0, symlink("..", "dir/up"));
+ ATF_REQUIRE_EQ(0, symlink("dir", "dirl"));
+ ATF_REQUIRE_EQ(0, symlink("file", "filel"));
+ ATF_REQUIRE_EQ(0, symlink("noent", "dead"));
+}
+
+ATF_TC(fts_options_logical);
+ATF_TC_HEAD(fts_options_logical, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "FTS_LOGICAL");
+}
+ATF_TC_BODY(fts_options_logical, tc)
+{
+ fts_options_prepare(tc);
+ fts_test(tc, &(struct fts_testcase){
+ all_paths,
+ FTS_LOGICAL,
+ (struct fts_expect[]){
+ { FTS_DL, "dead", "dead" },
+ { FTS_D, "dir", "dir" },
+ { FTS_F, "file", "dir/file" },
+ { FTS_D, "up", "dir/up" },
+ { FTS_DL, "dead", "dir/up/dead" },
+ { FTS_DC, "dir", "dir/up/dir" },
+ { FTS_DC, "dirl", "dir/up/dirl" },
+ { FTS_F, "file", "dir/up/file" },
+ { FTS_F, "filel", "dir/up/filel" },
+ { FTS_DP, "up", "dir/up" },
+ { FTS_DP, "dir", "dir" },
+ { FTS_D, "dirl", "dirl" },
+ { FTS_F, "file", "dirl/file" },
+ { FTS_D, "up", "dirl/up" },
+ { FTS_DL, "dead", "dirl/up/dead" },
+ { FTS_DC, "dir", "dirl/up/dir" },
+ { FTS_DC, "dirl", "dirl/up/dirl" },
+ { FTS_F, "file", "dirl/up/file" },
+ { FTS_F, "filel", "dirl/up/filel" },
+ { FTS_DP, "up", "dirl/up" },
+ { FTS_DP, "dirl", "dirl" },
+ { FTS_F, "file", "file" },
+ { FTS_F, "filel", "filel" },
+ { FTS_NS, "noent", "noent" },
+ { 0 }
+ },
+ });
+}
+
+ATF_TC(fts_options_logical_nostat);
+ATF_TC_HEAD(fts_options_logical_nostat, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "FTS_LOGICAL | FTS_NOSTAT");
+}
+ATF_TC_BODY(fts_options_logical_nostat, tc)
+{
+ /*
+ * While FTS_LOGICAL is not documented as being incompatible with
+ * FTS_NOSTAT, and FTS does not clear FTS_NOSTAT if FTS_LOGICAL is
+ * set, FTS_LOGICAL effectively nullifies FTS_NOSTAT by overriding
+ * the follow check in fts_stat(). In theory, FTS could easily be
+ * changed to only stat links (to check what they point to) in the
+ * FTS_LOGICAL | FTS_NOSTAT case, which would produce a different
+ * result here, so keep the test around in case that ever happens.
+ */
+ atf_tc_expect_fail("FTS_LOGICAL nullifies FTS_NOSTAT");
+ fts_options_prepare(tc);
+ fts_test(tc, &(struct fts_testcase){
+ all_paths,
+ FTS_LOGICAL | FTS_NOSTAT,
+ (struct fts_expect[]){
+ { FTS_DL, "dead", "dead" },
+ { FTS_D, "dir", "dir" },
+ { FTS_NSOK, "file", "dir/file" },
+ { FTS_D, "up", "dir/up" },
+ { FTS_DL, "dead", "dir/up/dead" },
+ { FTS_DC, "dir", "dir/up/dir" },
+ { FTS_DC, "dirl", "dir/up/dirl" },
+ { FTS_NSOK, "file", "dir/up/file" },
+ { FTS_NSOK, "filel", "dir/up/filel" },
+ { FTS_DP, "up", "dir/up" },
+ { FTS_DP, "dir", "dir" },
+ { FTS_D, "dirl", "dirl" },
+ { FTS_NSOK, "file", "dirl/file" },
+ { FTS_D, "up", "dirl/up" },
+ { FTS_DL, "dead", "dirl/up/dead" },
+ { FTS_DC, "dir", "dirl/up/dir" },
+ { FTS_DC, "dirl", "dirl/up/dirl" },
+ { FTS_NSOK, "file", "dirl/up/file" },
+ { FTS_NSOK, "filel", "dirl/up/filel" },
+ { FTS_DP, "up", "dirl/up" },
+ { FTS_DP, "dirl", "dirl" },
+ { FTS_F, "file", "file" },
+ { FTS_F, "filel", "filel" },
+ { FTS_NS, "noent", "noent" },
+ { 0 }
+ },
+ });
+}
+
+ATF_TC(fts_options_logical_seedot);
+ATF_TC_HEAD(fts_options_logical_seedot, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "FTS_LOGICAL | FTS_SEEDOT");
+}
+ATF_TC_BODY(fts_options_logical_seedot, tc)
+{
+ fts_options_prepare(tc);
+ fts_test(tc, &(struct fts_testcase){
+ all_paths,
+ FTS_LOGICAL | FTS_SEEDOT,
+ (struct fts_expect[]){
+ { FTS_DL, "dead", "dead" },
+ { FTS_D, "dir", "dir" },
+ { FTS_DOT, ".", "dir/." },
+ { FTS_DOT, "..", "dir/.." },
+ { FTS_F, "file", "dir/file" },
+ { FTS_D, "up", "dir/up" },
+ { FTS_DOT, ".", "dir/up/." },
+ { FTS_DOT, "..", "dir/up/.." },
+ { FTS_DL, "dead", "dir/up/dead" },
+ { FTS_DC, "dir", "dir/up/dir" },
+ { FTS_DC, "dirl", "dir/up/dirl" },
+ { FTS_F, "file", "dir/up/file" },
+ { FTS_F, "filel", "dir/up/filel" },
+ { FTS_DP, "up", "dir/up" },
+ { FTS_DP, "dir", "dir" },
+ { FTS_D, "dirl", "dirl" },
+ { FTS_DOT, ".", "dirl/." },
+ { FTS_DOT, "..", "dirl/.." },
+ { FTS_F, "file", "dirl/file" },
+ { FTS_D, "up", "dirl/up" },
+ { FTS_DOT, ".", "dirl/up/." },
+ { FTS_DOT, "..", "dirl/up/.." },
+ { FTS_DL, "dead", "dirl/up/dead" },
+ { FTS_DC, "dir", "dirl/up/dir" },
+ { FTS_DC, "dirl", "dirl/up/dirl" },
+ { FTS_F, "file", "dirl/up/file" },
+ { FTS_F, "filel", "dirl/up/filel" },
+ { FTS_DP, "up", "dirl/up" },
+ { FTS_DP, "dirl", "dirl" },
+ { FTS_F, "file", "file" },
+ { FTS_F, "filel", "filel" },
+ { FTS_NS, "noent", "noent" },
+ { 0 }
+ },
+ });
+}
+
+ATF_TC(fts_options_physical);
+ATF_TC_HEAD(fts_options_physical, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "FTS_PHYSICAL");
+}
+ATF_TC_BODY(fts_options_physical, tc)
+{
+ fts_options_prepare(tc);
+ fts_test(tc, &(struct fts_testcase){
+ all_paths,
+ FTS_PHYSICAL,
+ (struct fts_expect[]){
+ { FTS_SL, "dead", "dead" },
+ { FTS_D, "dir", "dir" },
+ { FTS_F, "file", "file" },
+ { FTS_SL, "up", "up" },
+ { FTS_DP, "dir", "dir" },
+ { FTS_SL, "dirl", "dirl" },
+ { FTS_F, "file", "file" },
+ { FTS_SL, "filel", "filel" },
+ { FTS_NS, "noent", "noent" },
+ { 0 }
+ },
+ });
+}
+
+ATF_TC(fts_options_physical_nochdir);
+ATF_TC_HEAD(fts_options_physical_nochdir, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "FTS_PHYSICAL | FTS_NOCHDIR");
+}
+ATF_TC_BODY(fts_options_physical_nochdir, tc)
+{
+ fts_options_prepare(tc);
+ fts_test(tc, &(struct fts_testcase){
+ all_paths,
+ FTS_PHYSICAL | FTS_NOCHDIR,
+ (struct fts_expect[]){
+ { FTS_SL, "dead", "dead" },
+ { FTS_D, "dir", "dir" },
+ { FTS_F, "file", "dir/file" },
+ { FTS_SL, "up", "dir/up" },
+ { FTS_DP, "dir", "dir" },
+ { FTS_SL, "dirl", "dirl" },
+ { FTS_F, "file", "file" },
+ { FTS_SL, "filel", "filel" },
+ { FTS_NS, "noent", "noent" },
+ { 0 }
+ },
+ });
+}
+
+ATF_TC(fts_options_physical_comfollow);
+ATF_TC_HEAD(fts_options_physical_comfollow, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "FTS_PHYSICAL | FTS_COMFOLLOW");
+}
+ATF_TC_BODY(fts_options_physical_comfollow, tc)
+{
+ fts_options_prepare(tc);
+ fts_test(tc, &(struct fts_testcase){
+ all_paths,
+ FTS_PHYSICAL | FTS_COMFOLLOW,
+ (struct fts_expect[]){
+ { FTS_DL, "dead", "dead" },
+ { FTS_D, "dir", "dir" },
+ { FTS_F, "file", "file" },
+ { FTS_SL, "up", "up" },
+ { FTS_DP, "dir", "dir" },
+ { FTS_D, "dirl", "dirl" },
+ { FTS_F, "file", "file" },
+ { FTS_SL, "up", "up" },
+ { FTS_DP, "dirl", "dirl" },
+ { FTS_F, "file", "file" },
+ { FTS_F, "filel", "filel" },
+ { FTS_NS, "noent", "noent" },
+ { 0 }
+ },
+ });
+}
+
+ATF_TC(fts_options_physical_comfollowdir);
+ATF_TC_HEAD(fts_options_physical_comfollowdir, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "FTS_PHYSICAL | FTS_COMFOLLOWDIR");
+}
+ATF_TC_BODY(fts_options_physical_comfollowdir, tc)
+{
+ fts_options_prepare(tc);
+ fts_test(tc, &(struct fts_testcase){
+ all_paths,
+ FTS_PHYSICAL | FTS_COMFOLLOWDIR,
+ (struct fts_expect[]){
+ { FTS_DL, "dead", "dead" },
+ { FTS_D, "dir", "dir" },
+ { FTS_F, "file", "file" },
+ { FTS_SL, "up", "up" },
+ { FTS_DP, "dir", "dir" },
+ { FTS_D, "dirl", "dirl" },
+ { FTS_F, "file", "file" },
+ { FTS_SL, "up", "up" },
+ { FTS_DP, "dirl", "dirl" },
+ { FTS_F, "file", "file" },
+ { FTS_SL, "filel", "filel" },
+ { FTS_NS, "noent", "noent" },
+ { 0 }
+ },
+ });
+}
+
+ATF_TC(fts_options_physical_nostat);
+ATF_TC_HEAD(fts_options_physical_nostat, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "FTS_PHYSICAL | FTS_NOSTAT");
+}
+ATF_TC_BODY(fts_options_physical_nostat, tc)
+{
+ fts_options_prepare(tc);
+ fts_test(tc, &(struct fts_testcase){
+ all_paths,
+ FTS_PHYSICAL | FTS_NOSTAT,
+ (struct fts_expect[]){
+ { FTS_SL, "dead", "dead" },
+ { FTS_D, "dir", "dir" },
+ { FTS_NSOK, "file", "file" },
+ { FTS_NSOK, "up", "up" },
+ { FTS_DP, "dir", "dir" },
+ { FTS_SL, "dirl", "dirl" },
+ { FTS_F, "file", "file" },
+ { FTS_SL, "filel", "filel" },
+ { FTS_NS, "noent", "noent" },
+ { 0 }
+ },
+ });
+}
+
+ATF_TC(fts_options_physical_nostat_type);
+ATF_TC_HEAD(fts_options_physical_nostat_type, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "FTS_PHYSICAL | FTS_NOSTAT_TYPE");
+}
+ATF_TC_BODY(fts_options_physical_nostat_type, tc)
+{
+ fts_options_prepare(tc);
+ fts_test(tc, &(struct fts_testcase){
+ all_paths,
+ FTS_PHYSICAL | FTS_NOSTAT_TYPE,
+ (struct fts_expect[]){
+ { FTS_SL, "dead", "dead" },
+ { FTS_D, "dir", "dir" },
+ { FTS_F, "file", "file" },
+ { FTS_SL, "up", "up" },
+ { FTS_DP, "dir", "dir" },
+ { FTS_SL, "dirl", "dirl" },
+ { FTS_F, "file", "file" },
+ { FTS_SL, "filel", "filel" },
+ { FTS_NS, "noent", "noent" },
+ { 0 }
+ },
+ });
+}
+
+ATF_TC(fts_options_physical_seedot);
+ATF_TC_HEAD(fts_options_physical_seedot, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "FTS_PHYSICAL | FTS_SEEDOT");
+}
+ATF_TC_BODY(fts_options_physical_seedot, tc)
+{
+ fts_options_prepare(tc);
+ fts_test(tc, &(struct fts_testcase){
+ all_paths,
+ FTS_PHYSICAL | FTS_SEEDOT,
+ (struct fts_expect[]){
+ { FTS_SL, "dead", "dead" },
+ { FTS_D, "dir", "dir" },
+ { FTS_DOT, ".", "." },
+ { FTS_DOT, "..", ".." },
+ { FTS_F, "file", "file" },
+ { FTS_SL, "up", "up" },
+ { FTS_DP, "dir", "dir" },
+ { FTS_SL, "dirl", "dirl" },
+ { FTS_F, "file", "file" },
+ { FTS_SL, "filel", "filel" },
+ { FTS_NS, "noent", "noent" },
+ { 0 }
+ },
+ });
+}
+
+/*
+ * TODO: Add tests for FTS_XDEV and FTS_WHITEOUT
+ */
+
+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);
+ ATF_TP_ADD_TC(tp, fts_options_physical);
+ ATF_TP_ADD_TC(tp, fts_options_physical_nochdir);
+ ATF_TP_ADD_TC(tp, fts_options_physical_comfollow);
+ ATF_TP_ADD_TC(tp, fts_options_physical_comfollowdir);
+ ATF_TP_ADD_TC(tp, fts_options_physical_nostat);
+ ATF_TP_ADD_TC(tp, fts_options_physical_nostat_type);
+ ATF_TP_ADD_TC(tp, fts_options_physical_seedot);
+ return (atf_no_error());
+}
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 */
diff --git a/lib/libc/tests/gen/ftw_test.c b/lib/libc/tests/gen/ftw_test.c
index b120f01ff4f1..3d2cf3446dee 100644
--- a/lib/libc/tests/gen/ftw_test.c
+++ b/lib/libc/tests/gen/ftw_test.c
@@ -28,9 +28,6 @@
* Limited test program for nftw() as specified by IEEE Std. 1003.1-2008.
*/
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
#include <sys/wait.h>
#include <err.h>
#include <errno.h>
diff --git a/lib/libc/tests/gen/getentropy_test.c b/lib/libc/tests/gen/getentropy_test.c
index 1290e6e50529..6ac9d5678ea6 100644
--- a/lib/libc/tests/gen/getentropy_test.c
+++ b/lib/libc/tests/gen/getentropy_test.c
@@ -1,5 +1,5 @@
/*-
- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ * SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2018 Conrad Meyer <cem@FreeBSD.org>
* All rights reserved.
@@ -26,11 +26,9 @@
* SUCH DAMAGE.
*/
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
#include <sys/param.h>
#include <errno.h>
+#include <limits.h>
#include <signal.h>
#include <unistd.h>
@@ -65,13 +63,13 @@ ATF_TC_BODY(getentropy_sizes, tc)
char buf[512];
ATF_REQUIRE_EQ(getentropy(buf, sizeof(buf)), -1);
- ATF_REQUIRE_EQ(errno, EIO);
- ATF_REQUIRE_EQ(getentropy(buf, 257), -1);
- ATF_REQUIRE_EQ(errno, EIO);
+ ATF_REQUIRE_EQ(errno, EINVAL);
+ ATF_REQUIRE_EQ(getentropy(buf, GETENTROPY_MAX + 1), -1);
+ ATF_REQUIRE_EQ(errno, EINVAL);
/* Smaller sizes always succeed: */
- ATF_REQUIRE_EQ(getentropy(buf, 256), 0);
- ATF_REQUIRE_EQ(getentropy(buf, 128), 0);
+ ATF_REQUIRE_EQ(getentropy(buf, GETENTROPY_MAX), 0);
+ ATF_REQUIRE_EQ(getentropy(buf, GETENTROPY_MAX / 2), 0);
ATF_REQUIRE_EQ(getentropy(buf, 0), 0);
}
diff --git a/lib/libc/tests/gen/getmntinfo_test.c b/lib/libc/tests/gen/getmntinfo_test.c
index 183fa84ed0e6..06e1091d8a15 100644
--- a/lib/libc/tests/gen/getmntinfo_test.c
+++ b/lib/libc/tests/gen/getmntinfo_test.c
@@ -28,9 +28,6 @@
* Limited test program for getmntinfo(3), a non-standard BSDism.
*/
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
#include <sys/param.h>
#include <sys/mount.h>
#include <sys/ucred.h>
diff --git a/lib/libc/tests/gen/glob2_test.c b/lib/libc/tests/gen/glob2_test.c
index dfcce0eed3da..ff1b36b830b8 100644
--- a/lib/libc/tests/gen/glob2_test.c
+++ b/lib/libc/tests/gen/glob2_test.c
@@ -24,13 +24,13 @@
* SUCH DAMAGE.
*/
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
#include <sys/param.h>
+#include <sys/stat.h>
+
#include <errno.h>
#include <fcntl.h>
#include <glob.h>
+#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -105,10 +105,80 @@ ATF_TC_BODY(glob_pathological_test, tc)
}
}
+ATF_TC(glob_period);
+ATF_TC_HEAD(glob_period, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test behaviour when matching files that start with a period"
+ "(documented in the glob(3) CAVEATS section).");
+}
+ATF_TC_BODY(glob_period, tc)
+{
+ int i;
+ glob_t g;
+
+ atf_utils_create_file(".test", "");
+ glob(".", 0, NULL, &g);
+ ATF_REQUIRE_MSG(g.gl_matchc == 1,
+ "glob(3) shouldn't match files starting with a period when using '.'");
+ for (i = 0; i < g.gl_matchc; i++)
+ printf("%s\n", g.gl_pathv[i]);
+ glob(".*", 0, NULL, &g);
+ ATF_REQUIRE_MSG(g.gl_matchc == 3 && strcmp(g.gl_pathv[2], ".test") == 0,
+ "glob(3) should match files starting with a period when using '.*'");
+}
+
+static bool glob_callback_invoked;
+
+static int
+errfunc(const char *path, int err)
+{
+ ATF_CHECK_STREQ(path, "test/");
+ ATF_CHECK(err == EACCES);
+ glob_callback_invoked = true;
+ /* Suppress EACCES errors. */
+ return (0);
+}
+
+ATF_TC(glob_callback);
+ATF_TC_HEAD(glob_callback, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test ability of callback function to suppress errors");
+ atf_tc_set_md_var(tc, "require.user", "unprivileged");
+}
+ATF_TC_BODY(glob_callback, tc)
+{
+ glob_t g;
+ int rv;
+
+ ATF_REQUIRE_EQ(0, mkdir("test", 0755));
+ ATF_REQUIRE_EQ(0, symlink("foo", "test/foo"));
+ ATF_REQUIRE_EQ(0, chmod("test", 0));
+
+ glob_callback_invoked = false;
+ rv = glob("test/*", 0, errfunc, &g);
+ ATF_CHECK_MSG(glob_callback_invoked,
+ "glob(3) failed to invoke callback function");
+ ATF_CHECK_EQ_MSG(GLOB_NOMATCH, rv,
+ "callback function failed to suppress EACCES");
+ globfree(&g);
+
+ /* GLOB_ERR should ignore the suppressed error. */
+ glob_callback_invoked = false;
+ rv = glob("test/*", GLOB_ERR, errfunc, &g);
+ ATF_CHECK_MSG(glob_callback_invoked,
+ "glob(3) failed to invoke callback function");
+ ATF_CHECK_EQ_MSG(GLOB_ABORTED, rv,
+ "GLOB_ERR didn't override callback function");
+ globfree(&g);
+}
+
ATF_TP_ADD_TCS(tp)
{
ATF_TP_ADD_TC(tp, glob_pathological_test);
-
+ ATF_TP_ADD_TC(tp, glob_period);
+ ATF_TP_ADD_TC(tp, glob_callback);
return (atf_no_error());
}
diff --git a/lib/libc/tests/gen/glob_blocks_test.c b/lib/libc/tests/gen/glob_blocks_test.c
new file mode 100644
index 000000000000..629b90bee762
--- /dev/null
+++ b/lib/libc/tests/gen/glob_blocks_test.c
@@ -0,0 +1,62 @@
+/*-
+ * Copyright (c) 2025 Klara, Inc.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <glob.h>
+#include <stdbool.h>
+
+#include <atf-c.h>
+
+ATF_TC(glob_b_callback);
+ATF_TC_HEAD(glob_b_callback, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test ability of callback block to suppress errors");
+ atf_tc_set_md_var(tc, "require.user", "unprivileged");
+}
+ATF_TC_BODY(glob_b_callback, tc)
+{
+ static bool glob_callback_invoked;
+ static int (^errblk)(const char *, int) =
+ ^(const char *path, int err) {
+ ATF_CHECK_STREQ(path, "test/");
+ ATF_CHECK(err == EACCES);
+ glob_callback_invoked = true;
+ /* Suppress EACCES errors. */
+ return (0);
+ };
+ glob_t g;
+ int rv;
+
+ ATF_REQUIRE_EQ(0, mkdir("test", 0755));
+ ATF_REQUIRE_EQ(0, symlink("foo", "test/foo"));
+ ATF_REQUIRE_EQ(0, chmod("test", 0));
+
+ glob_callback_invoked = false;
+ rv = glob_b("test/*", 0, errblk, &g);
+ ATF_CHECK_MSG(glob_callback_invoked,
+ "glob(3) failed to invoke callback block");
+ ATF_CHECK_EQ_MSG(GLOB_NOMATCH, rv,
+ "callback function failed to suppress EACCES");
+ globfree(&g);
+
+ /* GLOB_ERR should ignore the suppressed error. */
+ glob_callback_invoked = false;
+ rv = glob_b("test/*", GLOB_ERR, errblk, &g);
+ ATF_CHECK_MSG(glob_callback_invoked,
+ "glob(3) failed to invoke callback block");
+ ATF_CHECK_EQ_MSG(GLOB_ABORTED, rv,
+ "GLOB_ERR didn't override callback block");
+ globfree(&g);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, glob_b_callback);
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/gen/makecontext_test.c b/lib/libc/tests/gen/makecontext_test.c
index 50d835d15cbe..23e3cf85f677 100644
--- a/lib/libc/tests/gen/makecontext_test.c
+++ b/lib/libc/tests/gen/makecontext_test.c
@@ -1,5 +1,5 @@
/*-
- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ * SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2018 John H. Baldwin <jhb@FreeBSD.org>
*
@@ -25,9 +25,6 @@
* SUCH DAMAGE.
*/
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
#include <atf-c.h>
#include <ucontext.h>
diff --git a/lib/libc/tests/gen/opendir_test.c b/lib/libc/tests/gen/opendir_test.c
new file mode 100644
index 000000000000..b7481255654f
--- /dev/null
+++ b/lib/libc/tests/gen/opendir_test.c
@@ -0,0 +1,145 @@
+/*-
+ * Copyright (c) 2025 Klara, Inc.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/stat.h>
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <atf-c.h>
+
+/*
+ * Create a directory with a single subdirectory.
+ */
+static void
+opendir_prepare(const struct atf_tc *tc)
+{
+ ATF_REQUIRE_EQ(0, mkdir("dir", 0755));
+ ATF_REQUIRE_EQ(0, mkdir("dir/subdir", 0755));
+}
+
+/*
+ * Assuming dirp represents the directory created by opendir_prepare(),
+ * verify that readdir() returns what we expected to see there.
+ */
+static void
+opendir_check(const struct atf_tc *tc, DIR *dirp)
+{
+ struct dirent *ent;
+
+ ATF_REQUIRE((ent = readdir(dirp)) != NULL);
+ ATF_CHECK_EQ(1, ent->d_namlen);
+ ATF_CHECK_STREQ(".", ent->d_name);
+ ATF_CHECK_EQ(DT_DIR, ent->d_type);
+ ATF_REQUIRE((ent = readdir(dirp)) != NULL);
+ ATF_CHECK_EQ(2, ent->d_namlen);
+ ATF_CHECK_STREQ("..", ent->d_name);
+ ATF_CHECK_EQ(DT_DIR, ent->d_type);
+ ATF_REQUIRE((ent = readdir(dirp)) != NULL);
+ ATF_CHECK_EQ(sizeof("subdir") - 1, ent->d_namlen);
+ ATF_CHECK_STREQ("subdir", ent->d_name);
+ ATF_CHECK_EQ(DT_DIR, ent->d_type);
+ ATF_CHECK(readdir(dirp) == NULL);
+ ATF_CHECK(readdir(dirp) == NULL);
+}
+
+ATF_TC(opendir_ok);
+ATF_TC_HEAD(opendir_ok, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Open a directory.");
+}
+ATF_TC_BODY(opendir_ok, tc)
+{
+ DIR *dirp;
+
+ opendir_prepare(tc);
+ ATF_REQUIRE((dirp = opendir("dir")) != NULL);
+ opendir_check(tc, dirp);
+ ATF_CHECK_EQ(0, closedir(dirp));
+}
+
+ATF_TC(opendir_fifo);
+ATF_TC_HEAD(opendir_fifo, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Do not hang if given a named pipe.");
+}
+ATF_TC_BODY(opendir_fifo, tc)
+{
+ DIR *dirp;
+ int fd;
+
+ ATF_REQUIRE((fd = mkfifo("fifo", 0644)) >= 0);
+ ATF_REQUIRE_EQ(0, close(fd));
+ ATF_REQUIRE((dirp = opendir("fifo")) == NULL);
+ ATF_CHECK_EQ(ENOTDIR, errno);
+}
+
+ATF_TC(fdopendir_ok);
+ATF_TC_HEAD(fdopendir_ok, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Open a directory from a directory descriptor.");
+}
+ATF_TC_BODY(fdopendir_ok, tc)
+{
+ DIR *dirp;
+ int dd;
+
+ opendir_prepare(tc);
+ ATF_REQUIRE((dd = open("dir", O_DIRECTORY | O_RDONLY)) >= 0);
+ ATF_REQUIRE((dirp = fdopendir(dd)) != NULL);
+ opendir_check(tc, dirp);
+ ATF_CHECK_EQ(dd, fdclosedir(dirp));
+ ATF_CHECK_EQ(0, close(dd));
+}
+
+ATF_TC(fdopendir_ebadf);
+ATF_TC_HEAD(fdopendir_ebadf, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Open a directory from an invalid descriptor.");
+}
+ATF_TC_BODY(fdopendir_ebadf, tc)
+{
+ DIR *dirp;
+ int dd;
+
+ ATF_REQUIRE_EQ(0, mkdir("dir", 0755));
+ ATF_REQUIRE((dd = open("dir", O_DIRECTORY | O_RDONLY)) >= 0);
+ ATF_CHECK_EQ(0, close(dd));
+ ATF_REQUIRE((dirp = fdopendir(dd)) == NULL);
+ ATF_CHECK_EQ(EBADF, errno);
+}
+
+ATF_TC(fdopendir_enotdir);
+ATF_TC_HEAD(fdopendir_enotdir, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Open a directory from a non-directory descriptor.");
+}
+ATF_TC_BODY(fdopendir_enotdir, tc)
+{
+ DIR *dirp;
+ int fd;
+
+ ATF_REQUIRE((fd = open("file", O_CREAT | O_RDWR, 0644)) >= 0);
+ ATF_REQUIRE((dirp = fdopendir(fd)) == NULL);
+ ATF_CHECK_EQ(ENOTDIR, errno);
+ ATF_CHECK_EQ(0, close(fd));
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, opendir_ok);
+ ATF_TP_ADD_TC(tp, fdopendir_ok);
+ ATF_TP_ADD_TC(tp, fdopendir_ebadf);
+ ATF_TP_ADD_TC(tp, fdopendir_enotdir);
+ ATF_TP_ADD_TC(tp, opendir_fifo);
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/gen/popen_test.c b/lib/libc/tests/gen/popen_test.c
index 5c615b7146e9..43eadd380f39 100644
--- a/lib/libc/tests/gen/popen_test.c
+++ b/lib/libc/tests/gen/popen_test.c
@@ -29,9 +29,6 @@
* with BSD extensions.
*/
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
#include <sys/param.h>
#include <sys/wait.h>
#include <errno.h>
diff --git a/lib/libc/tests/gen/posix_spawn/Makefile b/lib/libc/tests/gen/posix_spawn/Makefile
index 21feba9dce05..df428876708b 100644
--- a/lib/libc/tests/gen/posix_spawn/Makefile
+++ b/lib/libc/tests/gen/posix_spawn/Makefile
@@ -1,5 +1,3 @@
-# $FreeBSD$
-
.include <bsd.own.mk>
BINDIR= ${TESTSDIR}
diff --git a/lib/libc/tests/gen/posix_spawn/Makefile.depend b/lib/libc/tests/gen/posix_spawn/Makefile.depend
index 10e58b789640..e89a5c52c82a 100644
--- a/lib/libc/tests/gen/posix_spawn/Makefile.depend
+++ b/lib/libc/tests/gen/posix_spawn/Makefile.depend
@@ -1,4 +1,3 @@
-# $FreeBSD$
# Autogenerated - do NOT edit!
DIRDEPS = \
diff --git a/lib/libc/tests/gen/posix_spawn_test.c b/lib/libc/tests/gen/posix_spawn_test.c
index 46259cbf8cde..22133cf1d59a 100644
--- a/lib/libc/tests/gen/posix_spawn_test.c
+++ b/lib/libc/tests/gen/posix_spawn_test.c
@@ -29,11 +29,11 @@
* IEEE Std. 1003.1-2008.
*/
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
+#include <sys/param.h>
+#include <sys/stat.h>
#include <sys/wait.h>
#include <errno.h>
+#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -41,6 +41,10 @@ __FBSDID("$FreeBSD$");
#include <atf-c.h>
+static const char true_script[] =
+ "#!/usr/bin/env\n"
+ "/usr/bin/true\n";
+
char *myenv[2] = { "answer=42", NULL };
ATF_TC_WITHOUT_HEAD(posix_spawn_simple_test);
@@ -127,6 +131,48 @@ ATF_TC_BODY(posix_spawnp_enoexec_fallback_null_argv0, tc)
ATF_REQUIRE(error == EINVAL);
}
+ATF_TC(posix_spawnp_eacces);
+ATF_TC_HEAD(posix_spawnp_eacces, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Verify EACCES behavior in posix_spawnp");
+ atf_tc_set_md_var(tc, "require.user", "unprivileged");
+}
+ATF_TC_BODY(posix_spawnp_eacces, tc)
+{
+ const struct spawnp_eacces_tc {
+ const char *pathvar;
+ int error_expected;
+ } spawnp_eacces_tests[] = {
+ { ".", EACCES }, /* File exists, but not +x */
+ { "unsearchable", ENOENT }, /* File exists, dir not +x */
+ };
+ char *myargs[2] = { "eacces", NULL };
+ int error;
+
+ error = mkdir("unsearchable", 0755);
+ ATF_REQUIRE(error == 0);
+ error = symlink("/usr/bin/true", "unsearchable/eacces");
+ ATF_REQUIRE(error == 0);
+
+ (void)chmod("unsearchable", 0444);
+
+ /* this will create a non-executable file */
+ atf_utils_create_file("eacces", true_script);
+
+ for (size_t i = 0; i < nitems(spawnp_eacces_tests); i++) {
+ const struct spawnp_eacces_tc *tc = &spawnp_eacces_tests[i];
+ pid_t pid;
+
+ error = setenv("PATH", tc->pathvar, 1);
+ ATF_REQUIRE_EQ(0, error);
+
+ error = posix_spawnp(&pid, myargs[0], NULL, NULL, myargs,
+ myenv);
+ ATF_CHECK_INTEQ_MSG(tc->error_expected, error,
+ "path '%s'", tc->pathvar);
+ }
+}
+
ATF_TP_ADD_TCS(tp)
{
@@ -134,6 +180,7 @@ ATF_TP_ADD_TCS(tp)
ATF_TP_ADD_TC(tp, posix_spawn_no_such_command_negative_test);
ATF_TP_ADD_TC(tp, posix_spawnp_enoexec_fallback);
ATF_TP_ADD_TC(tp, posix_spawnp_enoexec_fallback_null_argv0);
+ ATF_TP_ADD_TC(tp, posix_spawnp_eacces);
return (atf_no_error());
}
diff --git a/lib/libc/tests/gen/realpath2_test.c b/lib/libc/tests/gen/realpath2_test.c
index 82ad7f870697..f89dd99cbb72 100644
--- a/lib/libc/tests/gen/realpath2_test.c
+++ b/lib/libc/tests/gen/realpath2_test.c
@@ -24,9 +24,6 @@
* SUCH DAMAGE.
*/
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
#include <sys/param.h>
#include <errno.h>
#include <fcntl.h>
diff --git a/lib/libc/tests/gen/scandir_blocks_test.c b/lib/libc/tests/gen/scandir_blocks_test.c
new file mode 100644
index 000000000000..b94270bc410e
--- /dev/null
+++ b/lib/libc/tests/gen/scandir_blocks_test.c
@@ -0,0 +1,118 @@
+/*-
+ * Copyright (c) 2025 Klara, Inc.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/stat.h>
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdlib.h>
+
+#include <atf-c.h>
+
+static void
+scandir_blocks_prepare(const struct atf_tc *tc)
+{
+ ATF_REQUIRE_EQ(0, mkdir("dir", 0755));
+ ATF_REQUIRE_EQ(0, mkdir("dir/dir", 0755));
+ ATF_REQUIRE_EQ(0, close(creat("dir/file", 0644)));
+ ATF_REQUIRE_EQ(0, symlink("file", "dir/link"));
+ ATF_REQUIRE_EQ(0, mkdir("dir/skip", 0755));
+}
+
+static void
+scandir_blocks_verify(const struct atf_tc *tc, int n, struct dirent **namelist)
+{
+ ATF_REQUIRE_EQ_MSG(5, n, "return value is %d", n);
+ ATF_CHECK_STREQ("link", namelist[0]->d_name);
+ ATF_CHECK_STREQ("file", namelist[1]->d_name);
+ ATF_CHECK_STREQ("dir", namelist[2]->d_name);
+ ATF_CHECK_STREQ("..", namelist[3]->d_name);
+ ATF_CHECK_STREQ(".", namelist[4]->d_name);
+}
+
+ATF_TC(scandir_b_test);
+ATF_TC_HEAD(scandir_b_test, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test scandir_b()");
+}
+ATF_TC_BODY(scandir_b_test, tc)
+{
+ struct dirent **namelist = NULL;
+ int i, ret;
+
+ scandir_blocks_prepare(tc);
+ ret = scandir_b("dir", &namelist,
+ ^(const struct dirent *ent) {
+ return (strcmp(ent->d_name, "skip") != 0);
+ },
+ ^(const struct dirent **a, const struct dirent **b) {
+ return (strcmp((*b)->d_name, (*a)->d_name));
+ });
+ scandir_blocks_verify(tc, ret, namelist);
+ for (i = 0; i < ret; i++)
+ free(namelist[i]);
+ free(namelist);
+}
+
+ATF_TC(fdscandir_b_test);
+ATF_TC_HEAD(fdscandir_b_test, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test fdscandir_b()");
+}
+ATF_TC_BODY(fdscandir_b_test, tc)
+{
+ struct dirent **namelist = NULL;
+ int fd, i, ret;
+
+ scandir_blocks_prepare(tc);
+ ATF_REQUIRE((fd = open("dir", O_DIRECTORY | O_RDONLY)) >= 0);
+ ret = fdscandir_b(fd, &namelist,
+ ^(const struct dirent *ent) {
+ return (strcmp(ent->d_name, "skip") != 0);
+ },
+ ^(const struct dirent **a, const struct dirent **b) {
+ return (strcmp((*b)->d_name, (*a)->d_name));
+ });
+ scandir_blocks_verify(tc, ret, namelist);
+ for (i = 0; i < ret; i++)
+ free(namelist[i]);
+ free(namelist);
+ ATF_REQUIRE_EQ(0, close(fd));
+}
+
+ATF_TC(scandirat_b_test);
+ATF_TC_HEAD(scandirat_b_test, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test scandirat_b()");
+}
+ATF_TC_BODY(scandirat_b_test, tc)
+{
+ struct dirent **namelist = NULL;
+ int fd, i, ret;
+
+ scandir_blocks_prepare(tc);
+ ATF_REQUIRE((fd = open("dir", O_DIRECTORY | O_SEARCH)) >= 0);
+ ret = scandirat_b(fd, ".", &namelist,
+ ^(const struct dirent *ent) {
+ return (strcmp(ent->d_name, "skip") != 0);
+ },
+ ^(const struct dirent **a, const struct dirent **b) {
+ return (strcmp((*b)->d_name, (*a)->d_name));
+ });
+ scandir_blocks_verify(tc, ret, namelist);
+ for (i = 0; i < ret; i++)
+ free(namelist[i]);
+ free(namelist);
+ ATF_REQUIRE_EQ(0, close(fd));
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, scandir_b_test);
+ ATF_TP_ADD_TC(tp, fdscandir_b_test);
+ ATF_TP_ADD_TC(tp, scandirat_b_test);
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/gen/scandir_test.c b/lib/libc/tests/gen/scandir_test.c
new file mode 100644
index 000000000000..afd25bf7c0b2
--- /dev/null
+++ b/lib/libc/tests/gen/scandir_test.c
@@ -0,0 +1,195 @@
+/*-
+ * Copyright (c) 2025 Klara, Inc.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/stat.h>
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <atf-c.h>
+
+static void
+scandir_prepare(const struct atf_tc *tc)
+{
+ ATF_REQUIRE_EQ(0, mkdir("dir", 0755));
+ ATF_REQUIRE_EQ(0, mkdir("dir/dir", 0755));
+ ATF_REQUIRE_EQ(0, close(creat("dir/file", 0644)));
+ ATF_REQUIRE_EQ(0, symlink("file", "dir/link"));
+ ATF_REQUIRE_EQ(0, mkdir("dir/skip", 0755));
+}
+
+static void
+scandir_verify(const struct atf_tc *tc, int n, struct dirent **namelist)
+{
+ ATF_REQUIRE_EQ_MSG(5, n, "return value is %d", n);
+ ATF_CHECK_STREQ("link", namelist[0]->d_name);
+ ATF_CHECK_STREQ("file", namelist[1]->d_name);
+ ATF_CHECK_STREQ("dir", namelist[2]->d_name);
+ ATF_CHECK_STREQ("..", namelist[3]->d_name);
+ ATF_CHECK_STREQ(".", namelist[4]->d_name);
+}
+
+static int
+scandir_select(const struct dirent *ent)
+{
+ return (strcmp(ent->d_name, "skip") != 0);
+}
+
+static int
+scandir_compare(const struct dirent **a, const struct dirent **b)
+{
+ return (strcmp((*b)->d_name, (*a)->d_name));
+}
+
+ATF_TC(scandir_test);
+ATF_TC_HEAD(scandir_test, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test scandir()");
+}
+ATF_TC_BODY(scandir_test, tc)
+{
+ struct dirent **namelist = NULL;
+ int i, ret;
+
+ scandir_prepare(tc);
+ ret = scandir("dir", &namelist, scandir_select, scandir_compare);
+ scandir_verify(tc, ret, namelist);
+ for (i = 0; i < ret; i++)
+ free(namelist[i]);
+ free(namelist);
+}
+
+ATF_TC(fdscandir_test);
+ATF_TC_HEAD(fdscandir_test, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test fdscandir()");
+}
+ATF_TC_BODY(fdscandir_test, tc)
+{
+ struct dirent **namelist = NULL;
+ int fd, i, ret;
+
+ scandir_prepare(tc);
+ ATF_REQUIRE((fd = open("dir", O_DIRECTORY | O_RDONLY)) >= 0);
+ ret = fdscandir(fd, &namelist, scandir_select, scandir_compare);
+ scandir_verify(tc, ret, namelist);
+ for (i = 0; i < ret; i++)
+ free(namelist[i]);
+ free(namelist);
+ ATF_REQUIRE_EQ(0, close(fd));
+}
+
+ATF_TC(scandirat_test);
+ATF_TC_HEAD(scandirat_test, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test scandirat()");
+}
+ATF_TC_BODY(scandirat_test, tc)
+{
+ struct dirent **namelist = NULL;
+ int fd, i, ret;
+
+ scandir_prepare(tc);
+ ATF_REQUIRE((fd = open("dir", O_DIRECTORY | O_SEARCH)) >= 0);
+ ret = scandirat(fd, ".", &namelist, scandir_select, scandir_compare);
+ scandir_verify(tc, ret, namelist);
+ for (i = 0; i < ret; i++)
+ free(namelist[i]);
+ free(namelist);
+ ATF_REQUIRE_EQ(0, close(fd));
+}
+
+static int
+scandir_none(const struct dirent *ent __unused)
+{
+ return (0);
+}
+
+ATF_TC(scandir_none);
+ATF_TC_HEAD(scandir_none, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test scandir() when no entries are selected");
+}
+ATF_TC_BODY(scandir_none, tc)
+{
+ struct dirent **namelist = NULL;
+
+ ATF_REQUIRE_EQ(0, scandir(".", &namelist, scandir_none, alphasort));
+ ATF_REQUIRE(namelist);
+ free(namelist);
+}
+
+/*
+ * Test that scandir() propagates errors from readdir(): we create a
+ * directory with enough entries that it can't be read in a single
+ * getdirentries() call, then abuse the selection callback to close the
+ * file descriptor scandir() is using after the first call, causing the
+ * next one to fail, and verify that readdir() returns an error instead of
+ * a partial result. We make two passes, one in which nothing was
+ * selected before the error occurred, and one in which everything was.
+ */
+static int scandir_error_count;
+static int scandir_error_fd;
+static int scandir_error_select_return;
+
+static int
+scandir_error_select(const struct dirent *ent __unused)
+{
+ if (scandir_error_count++ == 0)
+ close(scandir_error_fd);
+ return (scandir_error_select_return);
+}
+
+ATF_TC(scandir_error);
+ATF_TC_HEAD(scandir_error, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test that scandir() propagates errors from readdir()");
+}
+ATF_TC_BODY(scandir_error, tc)
+{
+ char path[16];
+ struct dirent **namelist = NULL;
+ int fd, i;
+
+ ATF_REQUIRE_EQ(0, mkdir("dir", 0755));
+ for (i = 0; i < 1024; i++) {
+ snprintf(path, sizeof(path), "dir/%04x", i);
+ ATF_REQUIRE_EQ(0, symlink(path + 4, path));
+ }
+
+ /* first pass, select nothing */
+ ATF_REQUIRE((fd = open("dir", O_DIRECTORY | O_RDONLY)) >= 0);
+ scandir_error_count = 0;
+ scandir_error_fd = fd;
+ scandir_error_select_return = 0;
+ ATF_CHECK_ERRNO(EBADF,
+ fdscandir(fd, &namelist, scandir_error_select, NULL) < 0);
+ ATF_CHECK_EQ(NULL, namelist);
+
+ /* second pass, select everything */
+ ATF_REQUIRE((fd = open("dir", O_DIRECTORY | O_RDONLY)) >= 0);
+ scandir_error_count = 0;
+ scandir_error_fd = fd;
+ scandir_error_select_return = 1;
+ ATF_CHECK_ERRNO(EBADF,
+ fdscandir(fd, &namelist, scandir_error_select, NULL) < 0);
+ ATF_CHECK_EQ(NULL, namelist);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, scandir_test);
+ ATF_TP_ADD_TC(tp, fdscandir_test);
+ ATF_TP_ADD_TC(tp, scandirat_test);
+ ATF_TP_ADD_TC(tp, scandir_none);
+ ATF_TP_ADD_TC(tp, scandir_error);
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/gen/sig2str_test.c b/lib/libc/tests/gen/sig2str_test.c
new file mode 100644
index 000000000000..00b6ebb2349a
--- /dev/null
+++ b/lib/libc/tests/gen/sig2str_test.c
@@ -0,0 +1,213 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2025 Ricardo Branco <rbranco@suse.de>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <atf-c.h>
+
+static void
+test_roundtrip(int signum)
+{
+ char str[SIG2STR_MAX];
+ int sig;
+
+ ATF_REQUIRE_EQ_MSG(sig2str(signum, str), 0,
+ "sig2str(%d) failed", signum);
+ ATF_REQUIRE_EQ_MSG(str2sig(str, &sig), 0,
+ "str2sig(\"%s\") failed", str);
+ ATF_REQUIRE_INTEQ_MSG(sig, signum,
+ "Mismatch: roundtrip conversion gave %d instead of %d",
+ sig, signum);
+}
+
+ATF_TC_WITHOUT_HEAD(sig2str_valid);
+ATF_TC_BODY(sig2str_valid, tc)
+{
+ int sig;
+
+ for (sig = 1; sig < sys_nsig; sig++) {
+ test_roundtrip(sig);
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(sig2str_invalid);
+ATF_TC_BODY(sig2str_invalid, tc)
+{
+ char buf[SIG2STR_MAX];
+
+ ATF_CHECK(sig2str(0, buf) != 0);
+ ATF_CHECK(sig2str(-1, buf) != 0);
+ ATF_CHECK(sig2str(SIGRTMAX + 1, buf) != 0);
+}
+
+ATF_TC_WITHOUT_HEAD(str2sig_rtmin_rtmax);
+ATF_TC_BODY(str2sig_rtmin_rtmax, tc)
+{
+ int sig;
+
+ ATF_CHECK_MSG(str2sig("RTMIN", &sig) == 0,
+ "str2sig(\"RTMIN\") failed");
+ ATF_CHECK_INTEQ_MSG(sig, SIGRTMIN,
+ "RTMIN mapped to %d, expected %d", sig, SIGRTMIN);
+
+ ATF_CHECK_MSG(str2sig("RTMAX", &sig) == 0,
+ "str2sig(\"RTMAX\") failed");
+ ATF_CHECK_INTEQ_MSG(sig, SIGRTMAX,
+ "RTMAX mapped to %d, expected %d", sig, SIGRTMAX);
+
+ ATF_CHECK_MSG(str2sig("RTMIN+1", &sig) == 0,
+ "str2sig(\"RTMIN+1\") failed");
+ ATF_CHECK_INTEQ_MSG(sig, SIGRTMIN + 1,
+ "RTMIN+1 mapped to %d, expected %d", sig, SIGRTMIN + 1);
+
+ ATF_CHECK_MSG(str2sig("RTMAX-1", &sig) == 0,
+ "str2sig(\"RTMAX-1\") failed");
+ ATF_CHECK_INTEQ_MSG(sig, SIGRTMAX - 1,
+ "RTMAX-1 mapped to %d, expected %d", sig, SIGRTMAX - 1);
+}
+
+ATF_TC_WITHOUT_HEAD(str2sig_invalid_rt);
+ATF_TC_BODY(str2sig_invalid_rt, tc)
+{
+ int i, sig;
+
+ const char *invalid[] = {
+ "RTMIN+0",
+ "RTMAX-0",
+ "RTMIN-777",
+ "RTMIN+777",
+ "RTMAX-777",
+ "RTMAX+777",
+ "RTMIN-",
+ "RTMAX-",
+ "RTMIN0",
+ "RTMAX1",
+ "RTMIN+abc",
+ "RTMIN-abc",
+ NULL
+ };
+
+ for (i = 0; invalid[i] != NULL; i++) {
+ ATF_CHECK_MSG(str2sig(invalid[i], &sig) != 0,
+ "str2sig(\"%s\") unexpectedly succeeded", invalid[i]);
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(str2sig_fullname);
+ATF_TC_BODY(str2sig_fullname, tc)
+{
+ char fullname[SIG2STR_MAX + 3];
+ int n, sig;
+
+ for (sig = 1; sig < sys_nsig; sig++) {
+ snprintf(fullname, sizeof(fullname), "SIG%s", sys_signame[sig]);
+
+ ATF_CHECK_MSG(str2sig(fullname, &n) == 0,
+ "str2sig(\"%s\") failed with errno %d (%s)",
+ fullname, errno, strerror(errno));
+
+ ATF_CHECK_INTEQ_MSG(n, sig,
+ "Mismatch: %s = %d, str2sig(\"%s\") = %d",
+ sys_signame[sig], sig, fullname, n);
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(str2sig_lowercase);
+ATF_TC_BODY(str2sig_lowercase, tc)
+{
+ char fullname[SIG2STR_MAX + 3];
+ int n, sig;
+
+ for (sig = 1; sig < sys_nsig; sig++) {
+ snprintf(fullname, sizeof(fullname), "sig%s", sys_signame[sig]);
+ for (size_t i = 3; i < strlen(fullname); i++)
+ fullname[i] = toupper((unsigned char)fullname[i]);
+
+ ATF_CHECK_MSG(str2sig(fullname, &n) == 0,
+ "str2sig(\"%s\") failed with errno %d (%s)",
+ fullname, errno, strerror(errno));
+
+ ATF_CHECK_INTEQ_MSG(n, sig,
+ "Mismatch: %s = %d, str2sig(\"%s\") = %d",
+ sys_signame[sig], sig, fullname, n);
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(str2sig_numeric);
+ATF_TC_BODY(str2sig_numeric, tc)
+{
+ char buf[16];
+ int n, sig;
+
+ for (sig = NSIG; sig < SIGRTMIN; sig++) {
+ snprintf(buf, sizeof(buf), "%d", sig);
+ ATF_CHECK_MSG(str2sig(buf, &n) == 0,
+ "str2sig(\"%s\") failed", buf);
+ ATF_CHECK_INTEQ_MSG(n, sig,
+ "Mismatch: str2sig(\"%s\") = %d, expected %d",
+ buf, n, sig);
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(str2sig_invalid);
+ATF_TC_BODY(str2sig_invalid, tc)
+{
+ const char *invalid[] = {
+ "SIGDOESNOTEXIST",
+ "DOESNOTEXIST",
+ "INTERRUPT",
+ "",
+ "SIG",
+ "123abc",
+ "sig1extra",
+ NULL
+ };
+ int i, sig;
+
+ for (i = 0; invalid[i] != NULL; i++) {
+ ATF_CHECK_MSG(str2sig(invalid[i], &sig) != 0,
+ "str2sig(\"%s\") unexpectedly succeeded", invalid[i]);
+ }
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, sig2str_valid);
+ ATF_TP_ADD_TC(tp, sig2str_invalid);
+ ATF_TP_ADD_TC(tp, str2sig_rtmin_rtmax);
+ ATF_TP_ADD_TC(tp, str2sig_invalid_rt);
+ ATF_TP_ADD_TC(tp, str2sig_fullname);
+ ATF_TP_ADD_TC(tp, str2sig_lowercase);
+ ATF_TP_ADD_TC(tp, str2sig_numeric);
+ ATF_TP_ADD_TC(tp, str2sig_invalid);
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/gen/sigsetops_test.c b/lib/libc/tests/gen/sigsetops_test.c
index ba9abed403b0..a22c4b3f4f59 100644
--- a/lib/libc/tests/gen/sigsetops_test.c
+++ b/lib/libc/tests/gen/sigsetops_test.c
@@ -1,5 +1,5 @@
/*-
- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ * SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2019 Kyle Evans <kevans@FreeBSD.org>
* All rights reserved.
@@ -26,9 +26,6 @@
* SUCH DAMAGE.
*/
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
#include <errno.h>
#include <signal.h>
#include <stdbool.h>
diff --git a/lib/libc/tests/gen/spawnp_enoexec.sh b/lib/libc/tests/gen/spawnp_enoexec.sh
index 02941633e108..1050b7a6f944 100755
--- a/lib/libc/tests/gen/spawnp_enoexec.sh
+++ b/lib/libc/tests/gen/spawnp_enoexec.sh
@@ -1,4 +1,3 @@
-# $FreeBSD$
# Intentionally no interpreter
exit 42
diff --git a/lib/libc/tests/gen/test-fnmatch.c b/lib/libc/tests/gen/test-fnmatch.c
index 7771ab37f0a0..1a6c6ed7efdf 100644
--- a/lib/libc/tests/gen/test-fnmatch.c
+++ b/lib/libc/tests/gen/test-fnmatch.c
@@ -24,9 +24,6 @@
* SUCH DAMAGE.
*/
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
#include <sys/param.h>
#include <stdio.h>
#include <stdlib.h>
diff --git a/lib/libc/tests/gen/wordexp_test.c b/lib/libc/tests/gen/wordexp_test.c
index 3ccc67774c7f..a8b9d5509633 100644
--- a/lib/libc/tests/gen/wordexp_test.c
+++ b/lib/libc/tests/gen/wordexp_test.c
@@ -29,9 +29,6 @@
* IEEE Std. 1003.1-2001.
*/
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
#include <sys/wait.h>
#include <errno.h>
#include <signal.h>
@@ -295,6 +292,31 @@ ATF_TC_BODY(with_SIGCHILD_handler_test, tc)
ATF_REQUIRE(r == 0);
}
+ATF_TC_WITHOUT_HEAD(with_SIGCHILD_ignore_test);
+ATF_TC_BODY(with_SIGCHILD_ignore_test, tc)
+{
+ struct sigaction sa;
+ wordexp_t we;
+ int r;
+
+ /* With SIGCHLD set to ignore so that the kernel auto-reaps zombies. */
+ sa.sa_flags = 0;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_handler = SIG_IGN;
+ r = sigaction(SIGCHLD, &sa, NULL);
+ ATF_REQUIRE(r == 0);
+ r = wordexp("hello world", &we, 0);
+ ATF_REQUIRE(r == 0);
+ ATF_REQUIRE(we.we_wordc == 2);
+ ATF_REQUIRE(strcmp(we.we_wordv[0], "hello") == 0);
+ ATF_REQUIRE(strcmp(we.we_wordv[1], "world") == 0);
+ ATF_REQUIRE(we.we_wordv[2] == NULL);
+ wordfree(&we);
+ sa.sa_handler = SIG_DFL;
+ r = sigaction(SIGCHLD, &sa, NULL);
+ ATF_REQUIRE(r == 0);
+}
+
ATF_TC_WITHOUT_HEAD(with_unused_non_default_IFS_test);
ATF_TC_BODY(with_unused_non_default_IFS_test, tc)
{
@@ -353,6 +375,7 @@ ATF_TP_ADD_TCS(tp)
ATF_TP_ADD_TC(tp, WRDE_BADCHAR_test);
ATF_TP_ADD_TC(tp, WRDE_SYNTAX_test);
ATF_TP_ADD_TC(tp, with_SIGCHILD_handler_test);
+ ATF_TP_ADD_TC(tp, with_SIGCHILD_ignore_test);
ATF_TP_ADD_TC(tp, with_unused_non_default_IFS_test);
ATF_TP_ADD_TC(tp, with_used_non_default_IFS_test);