The branch stable/14 has been updated by kib:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=69b6dc0b920baaf7bd12b83df08ff78155052261

commit 69b6dc0b920baaf7bd12b83df08ff78155052261
Author:     Konstantin Belousov <k...@freebsd.org>
AuthorDate: 2025-02-20 23:32:22 +0000
Commit:     Konstantin Belousov <k...@freebsd.org>
CommitDate: 2025-03-04 04:19:08 +0000

    libc/gen: split user-visible opendir()-like functions into separate source 
files
    
    (cherry picked from commit d40daefca64750c1076822bdbd3c409a9519f513)
---
 lib/libc/gen/Makefile.inc               |   2 +
 lib/libc/gen/fdopendir.c                |  56 ++++++
 lib/libc/gen/opendir.c                  | 312 +----------------------------
 lib/libc/gen/opendir2.c                 | 340 ++++++++++++++++++++++++++++++++
 lib/libc/gen/telldir.h                  |   1 +
 libexec/rtld-elf/rtld-libc/Makefile.inc |   2 +-
 6 files changed, 401 insertions(+), 312 deletions(-)

diff --git a/lib/libc/gen/Makefile.inc b/lib/libc/gen/Makefile.inc
index 332edfcd6695..f9f6f3139379 100644
--- a/lib/libc/gen/Makefile.inc
+++ b/lib/libc/gen/Makefile.inc
@@ -54,6 +54,7 @@ SRCS+=        __getosreldate.c \
        exec.c \
        exect.c \
        fdevname.c \
+       fdopendir.c \
        feature_present.c \
        fmtcheck.c \
        fmtmsg.c \
@@ -112,6 +113,7 @@ SRCS+=      __getosreldate.c \
        nlist.c \
        nrand48.c \
        opendir.c \
+       opendir2.c \
        pause.c \
        pmadvise.c \
        popen.c \
diff --git a/lib/libc/gen/fdopendir.c b/lib/libc/gen/fdopendir.c
new file mode 100644
index 000000000000..67c0766b6d83
--- /dev/null
+++ b/lib/libc/gen/fdopendir.c
@@ -0,0 +1,56 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1983, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "namespace.h"
+#include <sys/param.h>
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "un-namespace.h"
+
+#include "gen-private.h"
+#include "telldir.h"
+
+/*
+ * Open a directory with existing file descriptor.
+ */
+DIR *
+fdopendir(int fd)
+{
+
+       if (_fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
+               return (NULL);
+       return (__opendir_common(fd, DTF_HIDEW | DTF_NODUP, true));
+}
diff --git a/lib/libc/gen/opendir.c b/lib/libc/gen/opendir.c
index a3864b0939fc..df288d416a2f 100644
--- a/lib/libc/gen/opendir.c
+++ b/lib/libc/gen/opendir.c
@@ -45,8 +45,6 @@ __SCCSID("@(#)opendir.c       8.8 (Berkeley) 5/1/95");
 #include "gen-private.h"
 #include "telldir.h"
 
-static DIR * __opendir_common(int, int, bool);
-
 /*
  * Open a directory.
  */
@@ -54,313 +52,5 @@ DIR *
 opendir(const char *name)
 {
 
-       return (__opendir2(name, DTF_HIDEW|DTF_NODUP));
-}
-
-/*
- * Open a directory with existing file descriptor.
- */
-DIR *
-fdopendir(int fd)
-{
-
-       if (_fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
-               return (NULL);
-       return (__opendir_common(fd, DTF_HIDEW|DTF_NODUP, true));
-}
-
-DIR *
-__opendir2(const char *name, int flags)
-{
-       int fd;
-       DIR *dir;
-       int saved_errno;
-
-       if ((flags & (__DTF_READALL | __DTF_SKIPREAD)) != 0)
-               return (NULL);
-       if ((fd = _open(name,
-           O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC)) == -1)
-               return (NULL);
-
-       dir = __opendir_common(fd, flags, false);
-       if (dir == NULL) {
-               saved_errno = errno;
-               _close(fd);
-               errno = saved_errno;
-       }
-       return (dir);
-}
-
-static int
-opendir_compar(const void *p1, const void *p2)
-{
-
-       return (strcmp((*(const struct dirent * const *)p1)->d_name,
-           (*(const struct dirent * const *)p2)->d_name));
-}
-
-/*
- * For a directory at the top of a unionfs stack, the entire directory's
- * contents are read and cached locally until the next call to rewinddir().
- * For the fdopendir() case, the initial seek position must be preserved.
- * For rewinddir(), the full directory should always be re-read from the
- * beginning.
- *
- * If an error occurs, the existing buffer and state of 'dirp' is left
- * unchanged.
- */
-bool
-_filldir(DIR *dirp, bool use_current_pos)
-{
-       struct dirent **dpv;
-       char *buf, *ddptr, *ddeptr;
-       off_t pos;
-       int fd2, incr, len, n, saved_errno, space;
-       
-       len = 0;
-       space = 0;
-       buf = NULL;
-       ddptr = NULL;
-
-       /*
-        * Use the system page size if that is a multiple of DIRBLKSIZ.
-        * Hopefully this can be a big win someday by allowing page
-        * trades to user space to be done by _getdirentries().
-        */
-       incr = getpagesize();
-       if ((incr % DIRBLKSIZ) != 0) 
-               incr = DIRBLKSIZ;
-
-       /*
-        * The strategy here is to read all the directory
-        * entries into a buffer, sort the buffer, and
-        * remove duplicate entries by setting the inode
-        * number to zero.
-        *
-        * We reopen the directory because _getdirentries()
-        * on a MNT_UNION mount modifies the open directory,
-        * making it refer to the lower directory after the
-        * upper directory's entries are exhausted.
-        * This would otherwise break software that uses
-        * the directory descriptor for fchdir or *at
-        * functions, such as fts.c.
-        */
-       if ((fd2 = _openat(dirp->dd_fd, ".", O_RDONLY | O_CLOEXEC)) == -1)
-               return (false);
-
-       if (use_current_pos) {
-               pos = lseek(dirp->dd_fd, 0, SEEK_CUR);
-               if (pos == -1 || lseek(fd2, pos, SEEK_SET) == -1) {
-                       saved_errno = errno;
-                       _close(fd2);
-                       errno = saved_errno;
-                       return (false);
-               }
-       }
-
-       do {
-               /*
-                * Always make at least DIRBLKSIZ bytes
-                * available to _getdirentries
-                */
-               if (space < DIRBLKSIZ) {
-                       space += incr;
-                       len += incr;
-                       buf = reallocf(buf, len);
-                       if (buf == NULL) {
-                               saved_errno = errno;
-                               _close(fd2);
-                               errno = saved_errno;
-                               return (false);
-                       }
-                       ddptr = buf + (len - space);
-               }
-
-               n = _getdirentries(fd2, ddptr, space, &dirp->dd_seek);
-               if (n > 0) {
-                       ddptr += n;
-                       space -= n;
-               }
-               if (n < 0) {
-                       saved_errno = errno;
-                       _close(fd2);
-                       errno = saved_errno;
-                       return (false);
-               }
-       } while (n > 0);
-       _close(fd2);
-
-       ddeptr = ddptr;
-
-       /*
-        * There is now a buffer full of (possibly) duplicate
-        * names.
-        */
-       dirp->dd_buf = buf;
-
-       /*
-        * Go round this loop twice...
-        *
-        * Scan through the buffer, counting entries.
-        * On the second pass, save pointers to each one.
-        * Then sort the pointers and remove duplicate names.
-        */
-       for (dpv = NULL;;) {
-               n = 0;
-               ddptr = buf;
-               while (ddptr < ddeptr) {
-                       struct dirent *dp;
-
-                       dp = (struct dirent *) ddptr;
-                       if ((long)dp & 03L)
-                               break;
-                       if ((dp->d_reclen <= 0) ||
-                           (dp->d_reclen > (ddeptr + 1 - ddptr)))
-                               break;
-                       ddptr += dp->d_reclen;
-                       if (dp->d_fileno) {
-                               if (dpv)
-                                       dpv[n] = dp;
-                               n++;
-                       }
-               }
-
-               if (dpv) {
-                       struct dirent *xp;
-
-                       /*
-                        * This sort must be stable.
-                        */
-                       mergesort(dpv, n, sizeof(*dpv), opendir_compar);
-
-                       dpv[n] = NULL;
-                       xp = NULL;
-
-                       /*
-                        * Scan through the buffer in sort order,
-                        * zapping the inode number of any
-                        * duplicate names.
-                        */
-                       for (n = 0; dpv[n]; n++) {
-                               struct dirent *dp = dpv[n];
-
-                               if ((xp == NULL) ||
-                                   strcmp(dp->d_name, xp->d_name)) {
-                                       xp = dp;
-                               } else {
-                                       dp->d_fileno = 0;
-                               }
-                               if (dp->d_type == DT_WHT &&
-                                   (dirp->dd_flags & DTF_HIDEW))
-                                       dp->d_fileno = 0;
-                       }
-
-                       free(dpv);
-                       break;
-               } else {
-                       dpv = malloc((n+1) * sizeof(struct dirent *));
-                       if (dpv == NULL)
-                               break;
-               }
-       }
-
-       dirp->dd_len = len;
-       dirp->dd_size = ddptr - dirp->dd_buf;
-       return (true);
-}
-
-static bool
-is_unionstack(int fd)
-{
-       int unionstack;
-
-       unionstack = _fcntl(fd, F_ISUNIONSTACK, 0);
-       if (unionstack != -1)
-               return (unionstack);
-
-       /*
-        * Should not happen unless running on a kernel without the op,
-        * but no use rendering the system useless in such a case.
-        */
-       return (0);
-}
-
-/*
- * Common routine for opendir(3), __opendir2(3) and fdopendir(3).
- */
-static DIR *
-__opendir_common(int fd, int flags, bool use_current_pos)
-{
-       DIR *dirp;
-       int incr;
-       int saved_errno;
-       bool unionstack;
-
-       if ((dirp = malloc(sizeof(DIR) + sizeof(struct _telldir))) == NULL)
-               return (NULL);
-
-       dirp->dd_buf = NULL;
-       dirp->dd_fd = fd;
-       dirp->dd_flags = flags;
-       dirp->dd_loc = 0;
-       dirp->dd_lock = NULL;
-       dirp->dd_td = (struct _telldir *)((char *)dirp + sizeof(DIR));
-       LIST_INIT(&dirp->dd_td->td_locq);
-       dirp->dd_td->td_loccnt = 0;
-       dirp->dd_compat_de = NULL;
-
-       /*
-        * Use the system page size if that is a multiple of DIRBLKSIZ.
-        * Hopefully this can be a big win someday by allowing page
-        * trades to user space to be done by _getdirentries().
-        */
-       incr = getpagesize();
-       if ((incr % DIRBLKSIZ) != 0) 
-               incr = DIRBLKSIZ;
-
-       /*
-        * Determine whether this directory is the top of a union stack.
-        */
-       unionstack = false;
-       if (flags & DTF_NODUP) {
-               unionstack = is_unionstack(fd);
-       }
-
-       if (unionstack) {
-               if (!_filldir(dirp, use_current_pos))
-                       goto fail;
-               dirp->dd_flags |= __DTF_READALL;
-       } else {
-               dirp->dd_len = incr;
-               dirp->dd_buf = malloc(dirp->dd_len);
-               if (dirp->dd_buf == NULL)
-                       goto fail;
-               if (use_current_pos) {
-                       /*
-                        * Read the first batch of directory entries
-                        * to prime dd_seek.  This also checks if the
-                        * fd passed to fdopendir() is a directory.
-                        */
-                       dirp->dd_size = _getdirentries(dirp->dd_fd,
-                           dirp->dd_buf, dirp->dd_len, &dirp->dd_seek);
-                       if (dirp->dd_size < 0) {
-                               if (errno == EINVAL)
-                                       errno = ENOTDIR;
-                               goto fail;
-                       }
-                       dirp->dd_flags |= __DTF_SKIPREAD;
-               } else {
-                       dirp->dd_size = 0;
-                       dirp->dd_seek = 0;
-               }
-       }
-
-       return (dirp);
-
-fail:
-       saved_errno = errno;
-       free(dirp->dd_buf);
-       free(dirp);
-       errno = saved_errno;
-       return (NULL);
+       return (__opendir2(name, DTF_HIDEW | DTF_NODUP));
 }
diff --git a/lib/libc/gen/opendir2.c b/lib/libc/gen/opendir2.c
new file mode 100644
index 000000000000..30a9030693e4
--- /dev/null
+++ b/lib/libc/gen/opendir2.c
@@ -0,0 +1,340 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1983, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "namespace.h"
+#include <sys/param.h>
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "un-namespace.h"
+
+#include "gen-private.h"
+#include "telldir.h"
+
+DIR *
+__opendir2(const char *name, int flags)
+{
+       int fd;
+       DIR *dir;
+       int saved_errno;
+
+       if ((flags & (__DTF_READALL | __DTF_SKIPREAD)) != 0)
+               return (NULL);
+       if ((fd = _open(name,
+           O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC)) == -1)
+               return (NULL);
+
+       dir = __opendir_common(fd, flags, false);
+       if (dir == NULL) {
+               saved_errno = errno;
+               _close(fd);
+               errno = saved_errno;
+       }
+       return (dir);
+}
+
+static int
+opendir_compar(const void *p1, const void *p2)
+{
+
+       return (strcmp((*(const struct dirent * const *)p1)->d_name,
+           (*(const struct dirent * const *)p2)->d_name));
+}
+
+/*
+ * For a directory at the top of a unionfs stack, the entire directory's
+ * contents are read and cached locally until the next call to rewinddir().
+ * For the fdopendir() case, the initial seek position must be preserved.
+ * For rewinddir(), the full directory should always be re-read from the
+ * beginning.
+ *
+ * If an error occurs, the existing buffer and state of 'dirp' is left
+ * unchanged.
+ */
+bool
+_filldir(DIR *dirp, bool use_current_pos)
+{
+       struct dirent **dpv;
+       char *buf, *ddptr, *ddeptr;
+       off_t pos;
+       int fd2, incr, len, n, saved_errno, space;
+
+       len = 0;
+       space = 0;
+       buf = NULL;
+       ddptr = NULL;
+
+       /*
+        * Use the system page size if that is a multiple of DIRBLKSIZ.
+        * Hopefully this can be a big win someday by allowing page
+        * trades to user space to be done by _getdirentries().
+        */
+       incr = getpagesize();
+       if ((incr % DIRBLKSIZ) != 0)
+               incr = DIRBLKSIZ;
+
+       /*
+        * The strategy here is to read all the directory
+        * entries into a buffer, sort the buffer, and
+        * remove duplicate entries by setting the inode
+        * number to zero.
+        *
+        * We reopen the directory because _getdirentries()
+        * on a MNT_UNION mount modifies the open directory,
+        * making it refer to the lower directory after the
+        * upper directory's entries are exhausted.
+        * This would otherwise break software that uses
+        * the directory descriptor for fchdir or *at
+        * functions, such as fts.c.
+        */
+       if ((fd2 = _openat(dirp->dd_fd, ".", O_RDONLY | O_CLOEXEC)) == -1)
+               return (false);
+
+       if (use_current_pos) {
+               pos = lseek(dirp->dd_fd, 0, SEEK_CUR);
+               if (pos == -1 || lseek(fd2, pos, SEEK_SET) == -1) {
+                       saved_errno = errno;
+                       _close(fd2);
+                       errno = saved_errno;
+                       return (false);
+               }
+       }
+
+       do {
+               /*
+                * Always make at least DIRBLKSIZ bytes
+                * available to _getdirentries
+                */
+               if (space < DIRBLKSIZ) {
+                       space += incr;
+                       len += incr;
+                       buf = reallocf(buf, len);
+                       if (buf == NULL) {
+                               saved_errno = errno;
+                               _close(fd2);
+                               errno = saved_errno;
+                               return (false);
+                       }
+                       ddptr = buf + (len - space);
+               }
+
+               n = _getdirentries(fd2, ddptr, space, &dirp->dd_seek);
+               if (n > 0) {
+                       ddptr += n;
+                       space -= n;
+               }
+               if (n < 0) {
+                       saved_errno = errno;
+                       _close(fd2);
+                       errno = saved_errno;
+                       return (false);
+               }
+       } while (n > 0);
+       _close(fd2);
+
+       ddeptr = ddptr;
+
+       /*
+        * There is now a buffer full of (possibly) duplicate
+        * names.
+        */
+       dirp->dd_buf = buf;
+
+       /*
+        * Go round this loop twice...
+        *
+        * Scan through the buffer, counting entries.
+        * On the second pass, save pointers to each one.
+        * Then sort the pointers and remove duplicate names.
+        */
+       for (dpv = NULL;;) {
+               n = 0;
+               ddptr = buf;
+               while (ddptr < ddeptr) {
+                       struct dirent *dp;
+
+                       dp = (struct dirent *) ddptr;
+                       if ((long)dp & 03L)
+                               break;
+                       if ((dp->d_reclen <= 0) ||
+                           (dp->d_reclen > (ddeptr + 1 - ddptr)))
+                               break;
+                       ddptr += dp->d_reclen;
+                       if (dp->d_fileno) {
+                               if (dpv)
+                                       dpv[n] = dp;
+                               n++;
+                       }
+               }
+
+               if (dpv) {
+                       struct dirent *xp;
+
+                       /*
+                        * This sort must be stable.
+                        */
+                       mergesort(dpv, n, sizeof(*dpv), opendir_compar);
+
+                       dpv[n] = NULL;
+                       xp = NULL;
+
+                       /*
+                        * Scan through the buffer in sort order,
+                        * zapping the inode number of any
+                        * duplicate names.
+                        */
+                       for (n = 0; dpv[n]; n++) {
+                               struct dirent *dp = dpv[n];
+
+                               if ((xp == NULL) ||
+                                   strcmp(dp->d_name, xp->d_name)) {
+                                       xp = dp;
+                               } else {
+                                       dp->d_fileno = 0;
+                               }
+                               if (dp->d_type == DT_WHT &&
+                                   (dirp->dd_flags & DTF_HIDEW))
+                                       dp->d_fileno = 0;
+                       }
+
+                       free(dpv);
+                       break;
+               } else {
+                       dpv = malloc((n+1) * sizeof(struct dirent *));
+                       if (dpv == NULL)
+                               break;
+               }
+       }
+
+       dirp->dd_len = len;
+       dirp->dd_size = ddptr - dirp->dd_buf;
+       return (true);
+}
+
+static bool
+is_unionstack(int fd)
+{
+       int unionstack;
+
+       unionstack = _fcntl(fd, F_ISUNIONSTACK, 0);
+       if (unionstack != -1)
+               return (unionstack);
+
+       /*
+        * Should not happen unless running on a kernel without the op,
+        * but no use rendering the system useless in such a case.
+        */
+       return (0);
+}
+
+/*
+ * Common routine for opendir(3), __opendir2(3) and fdopendir(3).
+ */
+DIR *
+__opendir_common(int fd, int flags, bool use_current_pos)
+{
+       DIR *dirp;
+       int incr;
+       int saved_errno;
+       bool unionstack;
+
+       if ((dirp = malloc(sizeof(DIR) + sizeof(struct _telldir))) == NULL)
+               return (NULL);
+
+       dirp->dd_buf = NULL;
+       dirp->dd_fd = fd;
+       dirp->dd_flags = flags;
+       dirp->dd_loc = 0;
+       dirp->dd_lock = NULL;
+       dirp->dd_td = (struct _telldir *)((char *)dirp + sizeof(DIR));
+       LIST_INIT(&dirp->dd_td->td_locq);
+       dirp->dd_td->td_loccnt = 0;
+       dirp->dd_compat_de = NULL;
+
+       /*
+        * Use the system page size if that is a multiple of DIRBLKSIZ.
+        * Hopefully this can be a big win someday by allowing page
+        * trades to user space to be done by _getdirentries().
+        */
+       incr = getpagesize();
+       if ((incr % DIRBLKSIZ) != 0)
+               incr = DIRBLKSIZ;
+
+       /*
+        * Determine whether this directory is the top of a union stack.
+        */
+       unionstack = false;
+       if (flags & DTF_NODUP) {
+               unionstack = is_unionstack(fd);
+       }
+
+       if (unionstack) {
+               if (!_filldir(dirp, use_current_pos))
+                       goto fail;
+               dirp->dd_flags |= __DTF_READALL;
+       } else {
+               dirp->dd_len = incr;
+               dirp->dd_buf = malloc(dirp->dd_len);
+               if (dirp->dd_buf == NULL)
+                       goto fail;
+               if (use_current_pos) {
+                       /*
+                        * Read the first batch of directory entries
+                        * to prime dd_seek.  This also checks if the
+                        * fd passed to fdopendir() is a directory.
+                        */
+                       dirp->dd_size = _getdirentries(dirp->dd_fd,
+                           dirp->dd_buf, dirp->dd_len, &dirp->dd_seek);
+                       if (dirp->dd_size < 0) {
+                               if (errno == EINVAL)
+                                       errno = ENOTDIR;
+                               goto fail;
+                       }
+                       dirp->dd_flags |= __DTF_SKIPREAD;
+               } else {
+                       dirp->dd_size = 0;
+                       dirp->dd_seek = 0;
+               }
+       }
+
+       return (dirp);
+
+fail:
+       saved_errno = errno;
+       free(dirp->dd_buf);
+       free(dirp);
+       errno = saved_errno;
+       return (NULL);
+}
diff --git a/lib/libc/gen/telldir.h b/lib/libc/gen/telldir.h
index aafa6ac71b1e..6d113491e819 100644
--- a/lib/libc/gen/telldir.h
+++ b/lib/libc/gen/telldir.h
@@ -103,6 +103,7 @@ struct dirent       *_readdir_unlocked(DIR *, int);
 void           _reclaim_telldir(DIR *);
 void           _seekdir(DIR *, long);
 void           _fixtelldir(DIR *dirp, long oldseek, long oldloc);
+DIR            *__opendir_common(int, int, bool);
 
 #define        RDU_SKIP        0x0001
 #define        RDU_SHORT       0x0002
diff --git a/libexec/rtld-elf/rtld-libc/Makefile.inc 
b/libexec/rtld-elf/rtld-libc/Makefile.inc
index 91eb6f5cabec..8a9480bc746e 100644
--- a/libexec/rtld-elf/rtld-libc/Makefile.inc
+++ b/libexec/rtld-elf/rtld-libc/Makefile.inc
@@ -17,7 +17,7 @@ CFLAGS+=      -I${SRCTOP}/libexec/rtld-elf/rtld-libc
 # redirects all calls to interposed functions to use the non-interposed version
 # instead.
 .PATH: ${LIBC_SRCTOP}/gen
-SRCS+= opendir.c closedir.c readdir.c telldir.c
+SRCS+= fdopendir.c opendir.c opendir2.c closedir.c readdir.c telldir.c
 
 # Avoid further dependencies by providing simple implementations of libc
 # functions such as __error(), etc.

Reply via email to