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