Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F143681466
D50935.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
16 KB
Referenced Files
None
Subscribers
None
D50935.diff
View Options
diff --git a/include/dirent.h b/include/dirent.h
--- a/include/dirent.h
+++ b/include/dirent.h
@@ -130,9 +130,22 @@
#endif
#endif
#if __BSD_VISIBLE
+int fscandir(int, struct dirent ***,
+ int (*)(const struct dirent *), int (*)(const struct dirent **,
+ const struct dirent **));
+#ifdef __BLOCKS__
+int fscandir_b(int, struct dirent ***,
+ int (^)(const struct dirent *),
+ int (^)(const struct dirent **, const struct dirent **));
+#endif
int scandirat(int, const char *, struct dirent ***,
int (*)(const struct dirent *), int (*)(const struct dirent **,
const struct dirent **));
+#ifdef __BLOCKS__
+int scandirat_b(int, const char *, struct dirent ***,
+ int (^)(const struct dirent *),
+ int (^)(const struct dirent **, const struct dirent **));
+#endif
#endif
#if __XSI_VISIBLE
void seekdir(DIR *, long);
diff --git a/lib/libc/gen/Makefile.inc b/lib/libc/gen/Makefile.inc
--- a/lib/libc/gen/Makefile.inc
+++ b/lib/libc/gen/Makefile.inc
@@ -499,8 +499,11 @@
MLINKS+=rtld_get_var.3 \
rtld_set_var.3
MLINKS+=scandir.3 alphasort.3 \
- scandir.3 scandirat.3 \
+ scandir.3 fscandir.3 \
+ scandir.3 fscandir_b.3 \
scandir.3 scandir_b.3 \
+ scandir.3 scandirat.3 \
+ scandir.3 scandirat_b.3 \
scandir.3 versionsort.3
MLINKS+=sem_open.3 sem_close.3 \
sem_open.3 sem_unlink.3
diff --git a/lib/libc/gen/Symbol.map b/lib/libc/gen/Symbol.map
--- a/lib/libc/gen/Symbol.map
+++ b/lib/libc/gen/Symbol.map
@@ -458,11 +458,14 @@
aio_read2;
aio_write2;
execvpe;
+ fscandir;
+ fscandir_b;
fts_open_b;
glob_b;
psiginfo;
rtld_get_var;
rtld_set_var;
+ scandirat_b;
uexterr_gettext;
sig2str;
str2sig;
diff --git a/lib/libc/gen/scandir.3 b/lib/libc/gen/scandir.3
--- a/lib/libc/gen/scandir.3
+++ b/lib/libc/gen/scandir.3
@@ -25,13 +25,16 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd August 31, 2023
+.Dd June 19, 2025
.Dt SCANDIR 3
.Os
.Sh NAME
.Nm scandir ,
+.Nm fscandir ,
.Nm scandirat ,
.Nm scandir_b ,
+.Nm fscandir_b ,
+.Nm fscandirat_b ,
.Nm alphasort ,
.Nm versionsort
.Nd scan a directory
@@ -47,6 +50,13 @@
.Fa "int \*(lp*compar\*(rp\*(lpconst struct dirent **, const struct dirent **\*(rp"
.Fc
.Ft int
+.Fo fscandir
+.Fa "int dirfd"
+.Fa "struct dirent ***namelist"
+.Fa "int \*(lp*select\*(rp\*(lpconst struct dirent *\*(rp"
+.Fa "int \*(lp*compar\*(rp\*(lpconst struct dirent **, const struct dirent **\*(rp"
+.Fc
+.Ft int
.Fo scandirat
.Fa "int dirfd"
.Fa "const char *dirname"
@@ -62,6 +72,21 @@
.Fa "int \*(lp^compar\*(rp\*(lpconst struct dirent **, const struct dirent **\*(rp"
.Fc
.Ft int
+.Fo fscandir_b
+.Fa "int dirfd"
+.Fa "struct dirent ***namelist"
+.Fa "int \*(lp^select\*(rp\*(lpconst struct dirent *\*(rp"
+.Fa "int \*(lp^compar\*(rp\*(lpconst struct dirent **, const struct dirent **\*(rp"
+.Fc
+.Ft int
+.Fo scandirat_b
+.Fa "int dirfd"
+.Fa "const char *dirname"
+.Fa "struct dirent ***namelist"
+.Fa "int \*(lp^select\*(rp\*(lpconst struct dirent *\*(rp"
+.Fa "int \*(lp^compar\*(rp\*(lpconst struct dirent **, const struct dirent **\*(rp"
+.Fc
+.Ft int
.Fn alphasort "const struct dirent **d1" "const struct dirent **d2"
.Ft int
.Fn versionsort "const struct dirent **d1" "const struct dirent **d2"
@@ -118,6 +143,13 @@
by freeing each pointer in the array and then the array itself.
.Pp
The
+.Fn fscandir
+function is similar to
+.Fn scandir ,
+but takes a file descriptor referencing a directory instead of a path.
+The file descriptor is left open on return, regardless of outcome.
+.Pp
+The
.Fn scandirat
function is similar to
.Fn scandir ,
@@ -151,17 +183,37 @@
for additional details.
.Pp
The
-.Fn scandir_b
-function behaves in the same way as
+.Fn scandir_b ,
+.Fn fscandir_b ,
+and
+.Fn scandirat_b
+functions behave in the same way as
.Fn scandir ,
-but takes blocks as arguments instead of function pointers and calls
+.Fn fscandir ,
+and
+.Fn scandirat ,
+respectively,
+but take blocks as arguments instead of function pointers and call
.Fn qsort_b
rather than
.Fn qsort .
.Sh DIAGNOSTICS
-Returns \-1 if the directory cannot be opened for reading or if
+The
+.Fn scandir ,
+.Fn fscandir ,
+.Fn scandirat ,
+.Fn scandir_b ,
+.Fn fscandir_b ,
+and
+.Fn scandirat_b
+functions return the number of directory entries found on succes.
+If the directory cannot be opened for reading, an error occurs
+while reading the directory, or
.Xr malloc 3
-cannot allocate enough memory to hold all the data structures.
+cannot allocate enough memory to hold all the directory entries,
+they return \-1 and set
+.Va errno
+to an appropriate value.
.Sh SEE ALSO
.Xr openat 2 ,
.Xr directory 3 ,
@@ -172,8 +224,25 @@
.Xr dir 5
.Sh STANDARDS
The
+.Fn alphasort
+and
+.Fn scandir
+functions are expected to conform to
+.St -p1003.1-2008 .
+The
+.Fn scandirat
+and
.Fn versionsort
-function is a GNU extension and conforms to no standard.
+functions are GNU extensions and conform to no standard.
+The
+.Fn fscandir ,
+.Fn scandir_b ,
+.Fn fscandir_b ,
+and
+.Fn scandirat_b
+functions are
+.Fx
+extensions.
.Sh HISTORY
The
.Fn scandir
@@ -182,8 +251,19 @@
functions appeared in
.Bx 4.2 .
The
+.Fn scandir_b
+function was added in
+.Fx 11.0 .
+The
.Fn scandirat
and
.Fn versionsort
functions were added in
.Fx 13.2 .
+The
+.Fn fscandir ,
+.Fn fscandir_b ,
+and
+.Fn scandirat_b
+functions were added in
+.Fx 15.0 .
diff --git a/lib/libc/gen/scandir.c b/lib/libc/gen/scandir.c
--- a/lib/libc/gen/scandir.c
+++ b/lib/libc/gen/scandir.c
@@ -38,6 +38,7 @@
#include "namespace.h"
#include <dirent.h>
+#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
@@ -64,7 +65,7 @@
static int
#ifdef I_AM_SCANDIR_B
-scandir_b_dirp(DIR *dirp, struct dirent ***namelist, select_block select,
+scandir_dirp_b(DIR *dirp, struct dirent ***namelist, select_block select,
dcomp_block dcomp)
#else
scandir_dirp(DIR *dirp, struct dirent ***namelist,
@@ -72,14 +73,9 @@
const struct dirent **))
#endif
{
- struct dirent *d, *p, **names = NULL;
- size_t arraysz, numitems;
-
- numitems = 0;
- arraysz = 32; /* initial estimate of the array size */
- names = (struct dirent **)malloc(arraysz * sizeof(struct dirent *));
- if (names == NULL)
- goto fail;
+ struct dirent *d, *p = NULL, **names = NULL, **names2;
+ size_t arraysz = 0, numitems = 0;
+ int serrno;
while ((d = readdir(dirp)) != NULL) {
if (select != NULL && !SELECT(d))
@@ -87,33 +83,27 @@
/*
* Make a minimum size copy of the data
*/
- p = (struct dirent *)malloc(_GENERIC_DIRSIZ(d));
+ p = malloc(_GENERIC_DIRSIZ(d));
if (p == NULL)
goto fail;
p->d_fileno = d->d_fileno;
p->d_type = d->d_type;
p->d_reclen = d->d_reclen;
p->d_namlen = d->d_namlen;
- bcopy(d->d_name, p->d_name, p->d_namlen + 1);
+ memcpy(p->d_name, d->d_name, p->d_namlen + 1);
/*
* Check to make sure the array has space left and
* realloc the maximum size.
*/
if (numitems >= arraysz) {
- struct dirent **names2;
-
- names2 = reallocarray(names, arraysz,
- 2 * sizeof(struct dirent *));
- if (names2 == NULL) {
- free(p);
+ arraysz = arraysz ? arraysz * 2 : 32;
+ names2 = reallocarray(names, arraysz, sizeof(*names));
+ if (names2 == NULL)
goto fail;
- }
names = names2;
- arraysz *= 2;
}
names[numitems++] = p;
}
- closedir(dirp);
if (numitems && dcomp != NULL)
#ifdef I_AM_SCANDIR_B
qsort_b(names, numitems, sizeof(struct dirent *), (void*)dcomp);
@@ -125,10 +115,12 @@
return (numitems);
fail:
+ serrno = errno;
+ free(p);
while (numitems > 0)
free(names[--numitems]);
free(names);
- closedir(dirp);
+ errno = serrno;
return (-1);
}
@@ -143,39 +135,82 @@
#endif
{
DIR *dirp;
+ int ret, serrno;
dirp = opendir(dirname);
if (dirp == NULL)
return (-1);
- return (
+ ret =
#ifdef I_AM_SCANDIR_B
- scandir_b_dirp
+ scandir_dirp_b
#else
scandir_dirp
#endif
- (dirp, namelist, select, dcomp));
+ (dirp, namelist, select, dcomp);
+ serrno = errno;
+ closedir(dirp);
+ errno = serrno;
+ return (ret);
}
-#ifndef I_AM_SCANDIR_B
int
-scandirat(int dirfd, const char *dirname, struct dirent ***namelist,
+#ifdef I_AM_SCANDIR_B
+fscandir_b(int dirfd, struct dirent ***namelist, select_block select,
+ dcomp_block dcomp)
+#else
+fscandir(int dirfd, struct dirent ***namelist,
int (*select)(const struct dirent *), int (*dcomp)(const struct dirent **,
const struct dirent **))
+#endif
{
DIR *dirp;
- int fd;
+ int ret, serrno;
+
+ dirp = fdopendir(dirfd);
+ if (dirp == NULL)
+ return (-1);
+ ret =
+#ifdef I_AM_SCANDIR_B
+ scandir_dirp_b
+#else
+ scandir_dirp
+#endif
+ (dirp, namelist, select, dcomp);
+ serrno = errno;
+ fdclosedir(dirp);
+ errno = serrno;
+ return (ret);
+}
+
+int
+#ifdef I_AM_SCANDIR_B
+scandirat_b(int dirfd, const char *dirname, struct dirent ***namelist,
+ select_block select, dcomp_block dcomp)
+#else
+scandirat(int dirfd, const char *dirname, struct dirent ***namelist,
+ int (*select)(const struct dirent *), int (*dcomp)(const struct dirent **,
+ const struct dirent **))
+#endif
+{
+ int fd, ret, serrno;
fd = _openat(dirfd, dirname, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
if (fd == -1)
return (-1);
- dirp = fdopendir(fd);
- if (dirp == NULL) {
- _close(fd);
- return (-1);
- }
- return (scandir_dirp(dirp, namelist, select, dcomp));
+ ret =
+#ifdef I_AM_SCANDIR_B
+ fscandir_b
+#else
+ fscandir
+#endif
+ (fd, namelist, select, dcomp);
+ serrno = errno;
+ _close(fd);
+ errno = serrno;
+ return (ret);
}
+#ifndef I_AM_SCANDIR_B
/*
* Alphabetic order comparison routine for those who want it.
* POSIX 2008 requires that alphasort() uses strcoll().
diff --git a/lib/libc/tests/gen/Makefile b/lib/libc/tests/gen/Makefile
--- a/lib/libc/tests/gen/Makefile
+++ b/lib/libc/tests/gen/Makefile
@@ -22,6 +22,10 @@
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
@@ -101,7 +105,7 @@
TESTS_SUBDIRS+= posix_spawn
# Tests that require blocks support
-.for t in fts_blocks_test glob_blocks_test
+.for t in fts_blocks_test glob_blocks_test scandir_blocks_test
CFLAGS.${t}.c+= -fblocks
LIBADD.${t}+= BlocksRuntime
.endfor
diff --git a/lib/libc/tests/gen/scandir_blocks_test.c b/lib/libc/tests/gen/scandir_blocks_test.c
new file mode 100644
--- /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(fscandir_b_test);
+ATF_TC_HEAD(fscandir_b_test, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test fscandir_b()");
+}
+ATF_TC_BODY(fscandir_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 = fscandir_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, fscandir_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
--- /dev/null
+++ b/lib/libc/tests/gen/scandir_test.c
@@ -0,0 +1,112 @@
+/*-
+ * 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_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(fscandir_test);
+ATF_TC_HEAD(fscandir_test, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test fscandir()");
+}
+ATF_TC_BODY(fscandir_test, tc)
+{
+ struct dirent **namelist = NULL;
+ int fd, i, ret;
+
+ scandir_prepare(tc);
+ ATF_REQUIRE((fd = open("dir", O_DIRECTORY | O_RDONLY)) >= 0);
+ ret = fscandir(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));
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, scandir_test);
+ ATF_TP_ADD_TC(tp, fscandir_test);
+ ATF_TP_ADD_TC(tp, scandirat_test);
+ return (atf_no_error());
+}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sun, Feb 1, 6:25 AM (17 h, 18 s)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
28295810
Default Alt Text
D50935.diff (16 KB)
Attached To
Mode
D50935: libc: Add fscandir(), fscandir_b(), scandirat_b().
Attached
Detach File
Event Timeline
Log In to Comment