xref: /freebsd/lib/libc/tests/gen/scandir_test.c (revision 62e0f12f5104585b7346fee183e5c667b39ddbad)
1deeebfdeSDag-Erling Smørgrav /*-
2deeebfdeSDag-Erling Smørgrav  * Copyright (c) 2025 Klara, Inc.
3deeebfdeSDag-Erling Smørgrav  *
4deeebfdeSDag-Erling Smørgrav  * SPDX-License-Identifier: BSD-2-Clause
5deeebfdeSDag-Erling Smørgrav  */
6deeebfdeSDag-Erling Smørgrav 
7deeebfdeSDag-Erling Smørgrav #include <sys/stat.h>
8deeebfdeSDag-Erling Smørgrav 
9deeebfdeSDag-Erling Smørgrav #include <dirent.h>
10*62e0f12fSDag-Erling Smørgrav #include <errno.h>
11deeebfdeSDag-Erling Smørgrav #include <fcntl.h>
12*62e0f12fSDag-Erling Smørgrav #include <stdio.h>
13deeebfdeSDag-Erling Smørgrav #include <stdlib.h>
14deeebfdeSDag-Erling Smørgrav 
15deeebfdeSDag-Erling Smørgrav #include <atf-c.h>
16deeebfdeSDag-Erling Smørgrav 
17deeebfdeSDag-Erling Smørgrav static void
scandir_prepare(const struct atf_tc * tc)18deeebfdeSDag-Erling Smørgrav scandir_prepare(const struct atf_tc *tc)
19deeebfdeSDag-Erling Smørgrav {
20deeebfdeSDag-Erling Smørgrav 	ATF_REQUIRE_EQ(0, mkdir("dir", 0755));
21deeebfdeSDag-Erling Smørgrav 	ATF_REQUIRE_EQ(0, mkdir("dir/dir", 0755));
22deeebfdeSDag-Erling Smørgrav 	ATF_REQUIRE_EQ(0, close(creat("dir/file", 0644)));
23deeebfdeSDag-Erling Smørgrav 	ATF_REQUIRE_EQ(0, symlink("file", "dir/link"));
24deeebfdeSDag-Erling Smørgrav 	ATF_REQUIRE_EQ(0, mkdir("dir/skip", 0755));
25deeebfdeSDag-Erling Smørgrav }
26deeebfdeSDag-Erling Smørgrav 
27deeebfdeSDag-Erling Smørgrav static void
scandir_verify(const struct atf_tc * tc,int n,struct dirent ** namelist)28deeebfdeSDag-Erling Smørgrav scandir_verify(const struct atf_tc *tc, int n, struct dirent **namelist)
29deeebfdeSDag-Erling Smørgrav {
30deeebfdeSDag-Erling Smørgrav 	ATF_REQUIRE_EQ_MSG(5, n, "return value is %d", n);
31deeebfdeSDag-Erling Smørgrav 	ATF_CHECK_STREQ("link", namelist[0]->d_name);
32deeebfdeSDag-Erling Smørgrav 	ATF_CHECK_STREQ("file", namelist[1]->d_name);
33deeebfdeSDag-Erling Smørgrav 	ATF_CHECK_STREQ("dir", namelist[2]->d_name);
34deeebfdeSDag-Erling Smørgrav 	ATF_CHECK_STREQ("..", namelist[3]->d_name);
35deeebfdeSDag-Erling Smørgrav 	ATF_CHECK_STREQ(".", namelist[4]->d_name);
36deeebfdeSDag-Erling Smørgrav }
37deeebfdeSDag-Erling Smørgrav 
38deeebfdeSDag-Erling Smørgrav static int
scandir_select(const struct dirent * ent)39deeebfdeSDag-Erling Smørgrav scandir_select(const struct dirent *ent)
40deeebfdeSDag-Erling Smørgrav {
41deeebfdeSDag-Erling Smørgrav 	return (strcmp(ent->d_name, "skip") != 0);
42deeebfdeSDag-Erling Smørgrav }
43deeebfdeSDag-Erling Smørgrav 
44deeebfdeSDag-Erling Smørgrav static int
scandir_compare(const struct dirent ** a,const struct dirent ** b)45deeebfdeSDag-Erling Smørgrav scandir_compare(const struct dirent **a, const struct dirent **b)
46deeebfdeSDag-Erling Smørgrav {
47deeebfdeSDag-Erling Smørgrav 	return (strcmp((*b)->d_name, (*a)->d_name));
48deeebfdeSDag-Erling Smørgrav }
49deeebfdeSDag-Erling Smørgrav 
50deeebfdeSDag-Erling Smørgrav ATF_TC(scandir_test);
ATF_TC_HEAD(scandir_test,tc)51deeebfdeSDag-Erling Smørgrav ATF_TC_HEAD(scandir_test, tc)
52deeebfdeSDag-Erling Smørgrav {
53deeebfdeSDag-Erling Smørgrav 	atf_tc_set_md_var(tc, "descr", "Test scandir()");
54deeebfdeSDag-Erling Smørgrav }
ATF_TC_BODY(scandir_test,tc)55deeebfdeSDag-Erling Smørgrav ATF_TC_BODY(scandir_test, tc)
56deeebfdeSDag-Erling Smørgrav {
57deeebfdeSDag-Erling Smørgrav 	struct dirent **namelist = NULL;
58deeebfdeSDag-Erling Smørgrav 	int i, ret;
59deeebfdeSDag-Erling Smørgrav 
60deeebfdeSDag-Erling Smørgrav 	scandir_prepare(tc);
61deeebfdeSDag-Erling Smørgrav 	ret = scandir("dir", &namelist, scandir_select, scandir_compare);
62deeebfdeSDag-Erling Smørgrav 	scandir_verify(tc, ret, namelist);
63deeebfdeSDag-Erling Smørgrav 	for (i = 0; i < ret; i++)
64deeebfdeSDag-Erling Smørgrav 		free(namelist[i]);
65deeebfdeSDag-Erling Smørgrav 	free(namelist);
66deeebfdeSDag-Erling Smørgrav }
67deeebfdeSDag-Erling Smørgrav 
680a5b763dSDag-Erling Smørgrav ATF_TC(fdscandir_test);
ATF_TC_HEAD(fdscandir_test,tc)690a5b763dSDag-Erling Smørgrav ATF_TC_HEAD(fdscandir_test, tc)
70deeebfdeSDag-Erling Smørgrav {
710a5b763dSDag-Erling Smørgrav 	atf_tc_set_md_var(tc, "descr", "Test fdscandir()");
72deeebfdeSDag-Erling Smørgrav }
ATF_TC_BODY(fdscandir_test,tc)730a5b763dSDag-Erling Smørgrav ATF_TC_BODY(fdscandir_test, tc)
74deeebfdeSDag-Erling Smørgrav {
75deeebfdeSDag-Erling Smørgrav 	struct dirent **namelist = NULL;
76deeebfdeSDag-Erling Smørgrav 	int fd, i, ret;
77deeebfdeSDag-Erling Smørgrav 
78deeebfdeSDag-Erling Smørgrav 	scandir_prepare(tc);
79deeebfdeSDag-Erling Smørgrav 	ATF_REQUIRE((fd = open("dir", O_DIRECTORY | O_RDONLY)) >= 0);
800a5b763dSDag-Erling Smørgrav 	ret = fdscandir(fd, &namelist, scandir_select, scandir_compare);
81deeebfdeSDag-Erling Smørgrav 	scandir_verify(tc, ret, namelist);
82deeebfdeSDag-Erling Smørgrav 	for (i = 0; i < ret; i++)
83deeebfdeSDag-Erling Smørgrav 		free(namelist[i]);
84deeebfdeSDag-Erling Smørgrav 	free(namelist);
85deeebfdeSDag-Erling Smørgrav 	ATF_REQUIRE_EQ(0, close(fd));
86deeebfdeSDag-Erling Smørgrav }
87deeebfdeSDag-Erling Smørgrav 
88deeebfdeSDag-Erling Smørgrav ATF_TC(scandirat_test);
ATF_TC_HEAD(scandirat_test,tc)89deeebfdeSDag-Erling Smørgrav ATF_TC_HEAD(scandirat_test, tc)
90deeebfdeSDag-Erling Smørgrav {
91deeebfdeSDag-Erling Smørgrav 	atf_tc_set_md_var(tc, "descr", "Test scandirat()");
92deeebfdeSDag-Erling Smørgrav }
ATF_TC_BODY(scandirat_test,tc)93deeebfdeSDag-Erling Smørgrav ATF_TC_BODY(scandirat_test, tc)
94deeebfdeSDag-Erling Smørgrav {
95deeebfdeSDag-Erling Smørgrav 	struct dirent **namelist = NULL;
96deeebfdeSDag-Erling Smørgrav 	int fd, i, ret;
97deeebfdeSDag-Erling Smørgrav 
98deeebfdeSDag-Erling Smørgrav 	scandir_prepare(tc);
99deeebfdeSDag-Erling Smørgrav 	ATF_REQUIRE((fd = open("dir", O_DIRECTORY | O_SEARCH)) >= 0);
100deeebfdeSDag-Erling Smørgrav 	ret = scandirat(fd, ".", &namelist, scandir_select, scandir_compare);
101deeebfdeSDag-Erling Smørgrav 	scandir_verify(tc, ret, namelist);
102deeebfdeSDag-Erling Smørgrav 	for (i = 0; i < ret; i++)
103deeebfdeSDag-Erling Smørgrav 		free(namelist[i]);
104deeebfdeSDag-Erling Smørgrav 	free(namelist);
105deeebfdeSDag-Erling Smørgrav 	ATF_REQUIRE_EQ(0, close(fd));
106deeebfdeSDag-Erling Smørgrav }
107deeebfdeSDag-Erling Smørgrav 
108c187b673SDag-Erling Smørgrav static int
scandir_none(const struct dirent * ent __unused)109c187b673SDag-Erling Smørgrav scandir_none(const struct dirent *ent __unused)
110c187b673SDag-Erling Smørgrav {
111c187b673SDag-Erling Smørgrav 	return (0);
112c187b673SDag-Erling Smørgrav }
113c187b673SDag-Erling Smørgrav 
114c187b673SDag-Erling Smørgrav ATF_TC(scandir_none);
ATF_TC_HEAD(scandir_none,tc)115c187b673SDag-Erling Smørgrav ATF_TC_HEAD(scandir_none, tc)
116c187b673SDag-Erling Smørgrav {
117c187b673SDag-Erling Smørgrav 	atf_tc_set_md_var(tc, "descr",
118c187b673SDag-Erling Smørgrav 	    "Test scandir() when no entries are selected");
119c187b673SDag-Erling Smørgrav }
ATF_TC_BODY(scandir_none,tc)120c187b673SDag-Erling Smørgrav ATF_TC_BODY(scandir_none, tc)
121c187b673SDag-Erling Smørgrav {
122c187b673SDag-Erling Smørgrav 	struct dirent **namelist = NULL;
123c187b673SDag-Erling Smørgrav 
124c187b673SDag-Erling Smørgrav 	ATF_REQUIRE_EQ(0, scandir(".", &namelist, scandir_none, alphasort));
125c187b673SDag-Erling Smørgrav 	ATF_REQUIRE(namelist);
126c187b673SDag-Erling Smørgrav 	free(namelist);
127c187b673SDag-Erling Smørgrav }
128c187b673SDag-Erling Smørgrav 
129*62e0f12fSDag-Erling Smørgrav /*
130*62e0f12fSDag-Erling Smørgrav  * Test that scandir() propagates errors from readdir(): we create a
131*62e0f12fSDag-Erling Smørgrav  * directory with enough entries that it can't be read in a single
132*62e0f12fSDag-Erling Smørgrav  * getdirentries() call, then abuse the selection callback to close the
133*62e0f12fSDag-Erling Smørgrav  * file descriptor scandir() is using after the first call, causing the
134*62e0f12fSDag-Erling Smørgrav  * next one to fail, and verify that readdir() returns an error instead of
135*62e0f12fSDag-Erling Smørgrav  * a partial result.  We make two passes, one in which nothing was
136*62e0f12fSDag-Erling Smørgrav  * selected before the error occurred, and one in which everything was.
137*62e0f12fSDag-Erling Smørgrav  */
138*62e0f12fSDag-Erling Smørgrav static int scandir_error_count;
139*62e0f12fSDag-Erling Smørgrav static int scandir_error_fd;
140*62e0f12fSDag-Erling Smørgrav static int scandir_error_select_return;
141*62e0f12fSDag-Erling Smørgrav 
142*62e0f12fSDag-Erling Smørgrav static int
scandir_error_select(const struct dirent * ent __unused)143*62e0f12fSDag-Erling Smørgrav scandir_error_select(const struct dirent *ent __unused)
144*62e0f12fSDag-Erling Smørgrav {
145*62e0f12fSDag-Erling Smørgrav 	if (scandir_error_count++ == 0)
146*62e0f12fSDag-Erling Smørgrav 		close(scandir_error_fd);
147*62e0f12fSDag-Erling Smørgrav 	return (scandir_error_select_return);
148*62e0f12fSDag-Erling Smørgrav }
149*62e0f12fSDag-Erling Smørgrav 
150*62e0f12fSDag-Erling Smørgrav ATF_TC(scandir_error);
ATF_TC_HEAD(scandir_error,tc)151*62e0f12fSDag-Erling Smørgrav ATF_TC_HEAD(scandir_error, tc)
152*62e0f12fSDag-Erling Smørgrav {
153*62e0f12fSDag-Erling Smørgrav 	atf_tc_set_md_var(tc, "descr",
154*62e0f12fSDag-Erling Smørgrav 	    "Test that scandir() propagates errors from readdir()");
155*62e0f12fSDag-Erling Smørgrav }
ATF_TC_BODY(scandir_error,tc)156*62e0f12fSDag-Erling Smørgrav ATF_TC_BODY(scandir_error, tc)
157*62e0f12fSDag-Erling Smørgrav {
158*62e0f12fSDag-Erling Smørgrav 	char path[16];
159*62e0f12fSDag-Erling Smørgrav 	struct dirent **namelist = NULL;
160*62e0f12fSDag-Erling Smørgrav 	int fd, i, ret;
161*62e0f12fSDag-Erling Smørgrav 
162*62e0f12fSDag-Erling Smørgrav 	ATF_REQUIRE_EQ(0, mkdir("dir", 0755));
163*62e0f12fSDag-Erling Smørgrav 	for (i = 0; i < 1024; i++) {
164*62e0f12fSDag-Erling Smørgrav 		snprintf(path, sizeof(path), "dir/%04x", i);
165*62e0f12fSDag-Erling Smørgrav 		ATF_REQUIRE_EQ(0, symlink(path + 4, path));
166*62e0f12fSDag-Erling Smørgrav 	}
167*62e0f12fSDag-Erling Smørgrav 
168*62e0f12fSDag-Erling Smørgrav 	/* first pass, select nothing */
169*62e0f12fSDag-Erling Smørgrav 	ATF_REQUIRE((fd = open("dir", O_DIRECTORY | O_RDONLY)) >= 0);
170*62e0f12fSDag-Erling Smørgrav 	scandir_error_count = 0;
171*62e0f12fSDag-Erling Smørgrav 	scandir_error_fd = fd;
172*62e0f12fSDag-Erling Smørgrav 	scandir_error_select_return = 0;
173*62e0f12fSDag-Erling Smørgrav 	ret = fdscandir(fd, &namelist, scandir_error_select, NULL);
174*62e0f12fSDag-Erling Smørgrav 	ATF_CHECK_EQ(-1, ret);
175*62e0f12fSDag-Erling Smørgrav 	ATF_CHECK_ERRNO(EBADF, ret < 0);
176*62e0f12fSDag-Erling Smørgrav 	ATF_CHECK_EQ(NULL, namelist);
177*62e0f12fSDag-Erling Smørgrav 
178*62e0f12fSDag-Erling Smørgrav 	/* second pass, select everything */
179*62e0f12fSDag-Erling Smørgrav 	ATF_REQUIRE((fd = open("dir", O_DIRECTORY | O_RDONLY)) >= 0);
180*62e0f12fSDag-Erling Smørgrav 	scandir_error_count = 0;
181*62e0f12fSDag-Erling Smørgrav 	scandir_error_fd = fd;
182*62e0f12fSDag-Erling Smørgrav 	scandir_error_select_return = 1;
183*62e0f12fSDag-Erling Smørgrav 	ret = fdscandir(fd, &namelist, scandir_error_select, NULL);
184*62e0f12fSDag-Erling Smørgrav 	ATF_CHECK_EQ(-1, ret);
185*62e0f12fSDag-Erling Smørgrav 	ATF_CHECK_ERRNO(EBADF, ret < 0);
186*62e0f12fSDag-Erling Smørgrav 	ATF_CHECK_EQ(NULL, namelist);
187*62e0f12fSDag-Erling Smørgrav }
188*62e0f12fSDag-Erling Smørgrav 
ATF_TP_ADD_TCS(tp)189deeebfdeSDag-Erling Smørgrav ATF_TP_ADD_TCS(tp)
190deeebfdeSDag-Erling Smørgrav {
191deeebfdeSDag-Erling Smørgrav 	ATF_TP_ADD_TC(tp, scandir_test);
1920a5b763dSDag-Erling Smørgrav 	ATF_TP_ADD_TC(tp, fdscandir_test);
193deeebfdeSDag-Erling Smørgrav 	ATF_TP_ADD_TC(tp, scandirat_test);
194c187b673SDag-Erling Smørgrav 	ATF_TP_ADD_TC(tp, scandir_none);
195*62e0f12fSDag-Erling Smørgrav 	ATF_TP_ADD_TC(tp, scandir_error);
196deeebfdeSDag-Erling Smørgrav 	return (atf_no_error());
197deeebfdeSDag-Erling Smørgrav }
198