From b5a3756551f8632b5b3b3fe02a1e2d8a7c2fc442 Mon Sep 17 00:00:00 2001 From: florian Date: Mon, 15 Apr 2024 15:47:58 +0000 Subject: [PATCH] Add scandirat(3); from freebsd To be used in httpd(8) shortly to prevent toctu issues. This makes __fdopendir internally accessible to avoid unnecessary syscalls in scandirat(3). Suggested & diff by guenther suggested by & OK millert tweak & OK guenther OK tb, jca This rides the libc crank. --- include/dirent.h | 5 ++++- lib/libc/Symbols.list | 1 + lib/libc/gen/opendir.c | 5 ++--- lib/libc/gen/scandir.3 | 37 ++++++++++++++++++++++++++++++++-- lib/libc/gen/scandir.c | 43 ++++++++++++++++++++++++++++++++++------ lib/libc/hidden/dirent.h | 7 ++++++- 6 files changed, 85 insertions(+), 13 deletions(-) diff --git a/include/dirent.h b/include/dirent.h index 61a5e011f05..207db24d5e9 100644 --- a/include/dirent.h +++ b/include/dirent.h @@ -1,4 +1,4 @@ -/* $OpenBSD: dirent.h,v 1.34 2016/09/09 18:12:37 millert Exp $ */ +/* $OpenBSD: dirent.h,v 1.35 2024/04/15 15:47:58 florian Exp $ */ /* $NetBSD: dirent.h,v 1.9 1995/03/26 20:13:37 jtc Exp $ */ /*- @@ -90,6 +90,9 @@ int readdir_r(DIR *__restrict, struct dirent *__restrict, #if __POSIX_VISIBLE >= 200809 int scandir(const char *, struct dirent ***, int (*)(const struct dirent *), int (*)(const struct dirent **, const struct dirent **)); +int scandirat(int, const char *, struct dirent ***, + int (*)(const struct dirent *), + int (*)(const struct dirent **, const struct dirent **)); int alphasort(const struct dirent **, const struct dirent **); #elif __BSD_VISIBLE int scandir(const char *, struct dirent ***, int (*)(struct dirent *), diff --git a/lib/libc/Symbols.list b/lib/libc/Symbols.list index 251760f812e..b1e7e916bbc 100644 --- a/lib/libc/Symbols.list +++ b/lib/libc/Symbols.list @@ -750,6 +750,7 @@ readdir_r readpassphrase rewinddir scandir +scandirat seekdir setclasscontext setdomainname diff --git a/lib/libc/gen/opendir.c b/lib/libc/gen/opendir.c index 71d30da9386..ef198924efb 100644 --- a/lib/libc/gen/opendir.c +++ b/lib/libc/gen/opendir.c @@ -1,4 +1,4 @@ -/* $OpenBSD: opendir.c,v 1.30 2016/09/21 04:38:56 guenther Exp $ */ +/* $OpenBSD: opendir.c,v 1.31 2024/04/15 15:47:58 florian Exp $ */ /* * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. @@ -39,7 +39,6 @@ #include "telldir.h" -static DIR *__fdopendir(int fd); /* * Open a directory specified by name. @@ -89,7 +88,7 @@ fdopendir(int fd) } DEF_WEAK(fdopendir); -static DIR * +DIR * __fdopendir(int fd) { DIR *dirp; diff --git a/lib/libc/gen/scandir.3 b/lib/libc/gen/scandir.3 index 50033134303..fda3a82f702 100644 --- a/lib/libc/gen/scandir.3 +++ b/lib/libc/gen/scandir.3 @@ -1,4 +1,4 @@ -.\" $OpenBSD: scandir.3,v 1.16 2021/06/17 18:18:15 jmc Exp $ +.\" $OpenBSD: scandir.3,v 1.17 2024/04/15 15:47:58 florian Exp $ .\" .\" Copyright (c) 1983, 1991, 1993 .\" The Regents of the University of California. All rights reserved. @@ -27,11 +27,12 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd $Mdocdate: June 17 2021 $ +.Dd $Mdocdate: April 15 2024 $ .Dt SCANDIR 3 .Os .Sh NAME .Nm scandir , +.Nm scandirat , .Nm alphasort .Nd scan a directory .Sh SYNOPSIS @@ -45,6 +46,14 @@ .Fa "int (*compar)(const struct dirent **, const struct dirent **)" .Fc .Ft int +.Fo scandirat +.Fa "int dirfd" +.Fa "const char *dirname" +.Fa "struct dirent ***namelist" +.Fa "int (*select)(const struct dirent *)" +.Fa "int (*compar)(const struct dirent **, const struct dirent **)" +.Fc +.Ft int .Fn alphasort "const struct dirent **d1" "const struct dirent **d2" .Sh DESCRIPTION The @@ -91,6 +100,30 @@ parameter to sort the array alphabetically. The memory allocated for the array can be deallocated with .Xr free 3 , 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 +.Fa dirname +is relative, +.Fa dirfd +must be a valid file descriptor referencing a directory, in which case the +.Fa dirname +lookup is performed 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. .Sh DIAGNOSTICS Returns \-1 if the directory cannot be opened for reading or if .Xr malloc 3 diff --git a/lib/libc/gen/scandir.c b/lib/libc/gen/scandir.c index f767ca503fa..5d62fb7a089 100644 --- a/lib/libc/gen/scandir.c +++ b/lib/libc/gen/scandir.c @@ -1,4 +1,4 @@ -/* $OpenBSD: scandir.c,v 1.22 2024/04/14 11:21:08 florian Exp $ */ +/* $OpenBSD: scandir.c,v 1.23 2024/04/15 15:47:58 florian Exp $ */ /* * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. @@ -39,9 +39,11 @@ #include #include #include +#include #include #include #include +#include #include "telldir.h" #define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b)) @@ -57,8 +59,8 @@ ((sizeof(struct dirent) - sizeof(dp)->d_name) + \ (((dp)->d_namlen + 1 + 3) &~ 3)) -int -scandir(const char *dirname, struct dirent ***namelist, +static int +scandir_dirp(DIR *dirp, struct dirent ***namelist, int (*select)(const struct dirent *), int (*dcomp)(const struct dirent **, const struct dirent **)) { @@ -66,10 +68,7 @@ scandir(const char *dirname, struct dirent ***namelist, size_t nitems = 0; struct stat stb; long arraysz; - DIR *dirp; - if ((dirp = opendir(dirname)) == NULL) - return (-1); if (fstat(dirp->dd_fd, &stb) == -1) goto fail; @@ -140,6 +139,38 @@ fail: return (-1); } +int +scandir(const char *dirname, struct dirent ***namelist, + int (*select)(const struct dirent *), + int (*dcomp)(const struct dirent **, const struct dirent **)) +{ + DIR *dirp; + + if ((dirp = opendir(dirname)) == NULL) + return (-1); + + return (scandir_dirp(dirp, namelist, select, dcomp)); +} + +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 = HIDDEN(openat)(dirfd, dirname, O_RDONLY | O_DIRECTORY | O_CLOEXEC); + if (fd == -1) + return (-1); + dirp = __fdopendir(fd); + if (dirp == NULL) { + HIDDEN(close)(fd); + return (-1); + } + return (scandir_dirp(dirp, namelist, select, dcomp)); +} + /* * Alphabetic order comparison routine for those who want it. */ diff --git a/lib/libc/hidden/dirent.h b/lib/libc/hidden/dirent.h index 1e8398291c8..e683252e016 100644 --- a/lib/libc/hidden/dirent.h +++ b/lib/libc/hidden/dirent.h @@ -1,4 +1,4 @@ -/* $OpenBSD: dirent.h,v 1.1 2015/09/12 13:34:22 guenther Exp $ */ +/* $OpenBSD: dirent.h,v 1.2 2024/04/15 15:47:58 florian Exp $ */ /* * Copyright (c) 2015 Philip Guenther * @@ -20,6 +20,10 @@ #include_next +__BEGIN_HIDDEN_DECLS +DIR *__fdopendir(int fd); +__END_HIDDEN_DECLS + PROTO_DEPRECATED(alphasort); PROTO_NORMAL(closedir); PROTO_NORMAL(dirfd); @@ -30,6 +34,7 @@ PROTO_NORMAL(readdir); PROTO_DEPRECATED(readdir_r); PROTO_DEPRECATED(rewinddir); PROTO_DEPRECATED(scandir); +PROTO_DEPRECATED(scandirat); PROTO_NORMAL(seekdir); PROTO_NORMAL(telldir); -- 2.20.1