diff --git a/include/dirent.h b/include/dirent.h --- a/include/dirent.h +++ b/include/dirent.h @@ -131,6 +131,11 @@ int (^)(const struct dirent **, const struct dirent **)); #endif #endif +#if __BSD_VISIBLE +int scandirat(int, const char *, struct dirent ***, + int (*)(const struct dirent *), int (*)(const struct dirent **, + const struct dirent **)); +#endif #if __XSI_VISIBLE void seekdir(DIR *, long); long telldir(DIR *); 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 @@ -493,7 +493,9 @@ rand48.3 seed48.3 \ rand48.3 srand48.3 MLINKS+=recv.2 recvmmsg.2 -MLINKS+=scandir.3 alphasort.3 +MLINKS+=scandir.3 alphasort.3 \ + scandir.3 scandirat.3 \ + scandir.3 scandir_b.3 MLINKS+=sem_open.3 sem_close.3 \ sem_open.3 sem_unlink.3 MLINKS+=sem_wait.3 sem_trywait.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 @@ -439,6 +439,7 @@ posix_spawn_file_actions_addchdir_np; posix_spawn_file_actions_addclosefrom_np; posix_spawn_file_actions_addfchdir_np; + scandirat; sched_getaffinity; sched_setaffinity; sched_getcpu; 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 @@ -28,11 +28,13 @@ .\" @(#)scandir.3 8.1 (Berkeley) 6/4/93 .\" $FreeBSD$ .\" -.Dd January 3, 2010 +.Dd August 23, 2022 .Dt SCANDIR 3 .Os .Sh NAME .Nm scandir , +.Nm scandirat , +.Nm scandir_b , .Nm alphasort .Nd scan a directory .Sh LIBRARY @@ -40,9 +42,27 @@ .Sh SYNOPSIS .In dirent.h .Ft int -.Fn scandir "const char *dirname" "struct dirent ***namelist" "int \*(lp*select\*(rp\*(lpconst struct dirent *\*(rp" "int \*(lp*compar\*(rp\*(lpconst struct dirent **, const struct dirent **\*(rp" +.Fo scandir +.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 +.Fo scandirat +.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 scandir_b "const char *dirname" "struct dirent ***namelist" "int \*(lp*select\^(rp\*(lpconst struct dirent *\*(rp" "int \*(lp^compar\*(rp\*(lpconst struct dirent **, const struct dirent **\*(rp" +.Fo scandir_b +.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" .Sh DESCRIPTION @@ -91,6 +111,36 @@ by freeing each pointer in the array and then the array itself. .Pp The +.Fn scandirat +function is similar to +.Fn scandir , +but takes an additional +.Fa dirfd +argument. +If the supplied +.Fa dirname +is absolute, the function behavior is identical to that of +.Fn scandir . +If +.Fa dirname +is relative, +.Fa dirfd +must be a valid file descriptor referencing a directory, in +which case the +.Fa dirname +lookup is relative to the directory referenced by +.Fa dirfd . +If +.Fa dirfd +has the special value +.Va AT_FDCWD , +then the current process directory is used as the base for +relative lookups. +See +.Xr openat 2 +for additional details. +.Pp +The .Fn scandir_b function behaves in the same way as .Fn scandir , @@ -103,6 +153,7 @@ .Xr malloc 3 cannot allocate enough memory to hold all the data structures. .Sh SEE ALSO +.Xr openat 2 , .Xr directory 3 , .Xr malloc 3 , .Xr qsort 3 , @@ -115,3 +166,7 @@ .Fn alphasort functions appeared in .Bx 4.2 . +The +.Fn scandirat +function was added in +.Fx 14.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 @@ -42,8 +42,10 @@ #include "namespace.h" #include +#include #include #include +#include #include "un-namespace.h" #ifdef I_AM_SCANDIR_B @@ -64,22 +66,18 @@ static int alphasort_thunk(void *thunk, const void *p1, const void *p2); #endif -int +static int #ifdef I_AM_SCANDIR_B -scandir_b(const char *dirname, struct dirent ***namelist, select_block select, +scandir_b_dirp(DIR *dirp, struct dirent ***namelist, select_block select, dcomp_block dcomp) #else -scandir(const char *dirname, struct dirent ***namelist, +scandir_dirp(DIR *dirp, struct dirent ***namelist, int (*select)(const struct dirent *), int (*dcomp)(const struct dirent **, - const struct dirent **)) + const struct dirent **)) #endif { struct dirent *d, *p, **names = NULL; size_t arraysz, numitems; - DIR *dirp; - - if ((dirp = opendir(dirname)) == NULL) - return(-1); numitems = 0; arraysz = 32; /* initial estimate of the array size */ @@ -138,7 +136,50 @@ return (-1); } +int +#ifdef I_AM_SCANDIR_B +scandir_b(const char *dirname, struct dirent ***namelist, select_block select, + dcomp_block dcomp) +#else +scandir(const char *dirname, struct dirent ***namelist, + int (*select)(const struct dirent *), int (*dcomp)(const struct dirent **, + const struct dirent **)) +#endif +{ + DIR *dirp; + + dirp = opendir(dirname); + if (dirp == NULL) + return (-1); + return ( +#ifdef I_AM_SCANDIR_B + scandir_b_dirp +#else + scandir_dirp +#endif + (dirp, namelist, select, dcomp)); +} + #ifndef I_AM_SCANDIR_B +int +scandirat(int dirfd, const char *dirname, struct dirent ***namelist, + int (*select)(const struct dirent *), int (*dcomp)(const struct dirent **, + const struct dirent **)) +{ + DIR *dirp; + int fd; + + 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)); +} + /* * Alphabetic order comparison routine for those who want it. * POSIX 2008 requires that alphasort() uses strcoll().