The branch main has been updated by asomers:

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

commit dd532ad13371dcabc07d05052a7a256fc83c6ead
Author:     Jitendra Bhati <[email protected]>
AuthorDate: 2026-08-08 00:31:09 +0000
Commit:     Alan Somers <[email protected]>
CommitDate: 2026-08-10 16:14:16 +0000

    fts: reduce fd usage by storing fts_dirfd on directory entries only
    
    Previously fts_build() called _dup(_dirfd(dirp)) for every child
    entry, holding N simultaneous fds for a directory with N children.
    
    Redefine fts_dirfd: instead of a fd for the entry's parent
    directory, it is now a fd for the entry itself, set only for
    directory entries. One dup per directory in fts_build() instead
    of one per child.  Close fts_dirfd during the directory post-order
    visit, before advancing to its sibling.
    
    To access a file using fd-relative operations, callers should use
    openat(ent->fts_parent->fts_dirfd, ent->fts_name, ...) instead of
    openat(ent->fts_dirfd, ent->fts_name, ...).  The fd is valid until the
    directory's post-order visit (FTS_DP).
    
    Reported by:    Mark Johnston <[email protected]>
    Fixes:          4bd01d6ae016 (fts: refactor to use fd-relative operations)
    Sponsored by:   Google LLC (GSoC 2026)
    Reviewed by:    asomers
    Pull Request:   https://github.com/freebsd/freebsd-src/pull/2360
---
 include/fts.h      |  2 +-
 lib/libc/gen/fts.3 | 32 +++++++++++++++++++-------------
 lib/libc/gen/fts.c | 23 ++++++++++++++---------
 3 files changed, 34 insertions(+), 23 deletions(-)

diff --git a/include/fts.h b/include/fts.h
index 0308b8ff880b..7d01b7a195c7 100644
--- a/include/fts.h
+++ b/include/fts.h
@@ -92,7 +92,7 @@ struct _ftsent {
        char *fts_path;                 /* root path */
        int fts_errno;                  /* errno for this node */
        int fts_symfd;                  /* fd for symlink */
-       int fts_dirfd;                  /* fd for parent directory */
+       int fts_dirfd;                  /* fd for this directory, if a 
directory */
        int __fts_reserved[3];          /* reserved for future use */
        __size_t fts_pathlen;           /* strlen(fts_path) */
        __size_t fts_namelen;           /* strlen(fts_name) */
diff --git a/lib/libc/gen/fts.3 b/lib/libc/gen/fts.3
index d24eb58bb438..eb204a0dd3ac 100644
--- a/lib/libc/gen/fts.3
+++ b/lib/libc/gen/fts.3
@@ -25,7 +25,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd July 7, 2026
+.Dd August 9, 2026
 .Dt FTS 3
 .Os
 .Sh NAME
@@ -316,9 +316,10 @@ file is a member.
 A parent structure for the initial entry point is provided as well,
 however, only the
 .Fa fts_level ,
-.Fa fts_number
-and
+.Fa fts_number ,
 .Fa fts_pointer
+and
+.Fa fts_dirfd
 fields are guaranteed to be initialized.
 .It Fa fts_link
 Upon return from the
@@ -350,19 +351,24 @@ A pointer to
 .Xr stat 2
 information for the file.
 .It Fa fts_dirfd
-A file descriptor open on the parent directory of this entry.
-It may be used with
+A file descriptor open on this directory entry.
+It is set only for directory entries
+.Pq Dv FTS_D
+and is \-1 for all other entry types.
+To access a file using fd-relative operations without relying
+on path-based syscalls, required in
+.Xr capsicum 4
+capability mode, use
+.Fa fts_parent->fts_dirfd
+with
 .Xr openat 2
 and
-.Fa fts_name
-to access the file without relying on path-based operations,
-which is required in
-.Xr capsicum 4
-capability mode.
-The descriptor is valid only until the next call to
-.Fn fts_read
+.Fa fts_name .
+The descriptor is valid until the directory's post-order visit
+.Pq Dv FTS_DP
 and must not be closed by the caller.
-For root-level entries,
+For root-level entries opened with
+.Fn fts_open ,
 .Fa fts_dirfd
 is \-1.
 .El
diff --git a/lib/libc/gen/fts.c b/lib/libc/gen/fts.c
index 5f56d169e076..e388fce550b9 100644
--- a/lib/libc/gen/fts.c
+++ b/lib/libc/gen/fts.c
@@ -443,8 +443,8 @@ fts_read(FTS *sp)
            (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) {
                p->fts_info = fts_stat(sp, p, 1, -1);
                if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {
-                       if ((p->fts_symfd = p->fts_dirfd >= 0 ?
-                           _dup(p->fts_dirfd) :
+                       if ((p->fts_symfd = p->fts_parent->fts_dirfd >= 0 ?
+                           _dup(p->fts_parent->fts_dirfd) :
                            _open(".", O_RDONLY | O_CLOEXEC, 0)) < 0) {
                                p->fts_errno = errno;
                                p->fts_info = FTS_ERR;
@@ -509,6 +509,10 @@ fts_read(FTS *sp)
 
        /* Move to the next node on this level. */
 next:  tmp = p;
+       if (tmp->fts_dirfd >= 0 && tmp->fts_info == FTS_DP) {
+               (void)_close(tmp->fts_dirfd);
+               tmp->fts_dirfd = -1;
+       }
        if ((p = p->fts_link) != NULL) {
                /*
                 * If reached the top, return to the original directory (or
@@ -537,8 +541,8 @@ next:       tmp = p;
                        p->fts_info = fts_stat(sp, p, 1, -1);
                        if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {
                                if ((p->fts_symfd =
-                                   p->fts_dirfd >= 0 ?
-                                   _dup(p->fts_dirfd) :
+                                   p->fts_parent->fts_dirfd >= 0 ?
+                                   _dup(p->fts_parent->fts_dirfd) :
                                    _open(".", O_RDONLY | O_CLOEXEC, 0)) < 0) {
                                        p->fts_errno = errno;
                                        p->fts_info = FTS_ERR;
@@ -680,8 +684,8 @@ fts_children(FTS *sp, int instr)
            ISSET(FTS_NOCHDIR))
                return (sp->fts_child = fts_build(sp, instr));
 
-       if ((fd = sp->fts_cur->fts_dirfd >= 0 ?
-           _dup(sp->fts_cur->fts_dirfd) :
+       if ((fd = sp->fts_cur->fts_parent->fts_dirfd >= 0 ?
+           _dup(sp->fts_cur->fts_parent->fts_dirfd) :
            _open(".", O_RDONLY | O_CLOEXEC, 0)) < 0)
                return (NULL);
        sp->fts_child = fts_build(sp, instr);
@@ -783,6 +787,8 @@ fts_build(FTS *sp, int type)
                return (NULL);
        }
 
+       cur->fts_dirfd = _dup(_dirfd(dirp));
+
        /*
         * In the FTS_PHYSICAL | FTS_NOSTAT case, we want to avoid calling
         * fstat() unnecessarily, but we still need to call it for
@@ -918,7 +924,6 @@ mem1:                               saved_errno = errno;
                }
 
                p->fts_level = level;
-               p->fts_dirfd = _dup(_dirfd(dirp));
                p->fts_parent = sp->fts_cur;
                p->fts_pathlen = len + dnamlen;
 
@@ -1337,8 +1342,8 @@ fts_ufslinks(FTS *sp, const FTSENT *ent)
         * avoidance.
         */
        if (priv->ftsp_dev != ent->fts_dev) {
-               if ((ent->fts_dirfd >= 0 ?
-                   _fstatfs(ent->fts_dirfd, &priv->ftsp_statfs) :
+               if ((ent->fts_parent->fts_dirfd >= 0 ?
+                   _fstatfs(ent->fts_parent->fts_dirfd, &priv->ftsp_statfs) :
                     statfs(ent->fts_path, &priv->ftsp_statfs)) != -1) {
                        priv->ftsp_dev = ent->fts_dev;
                        priv->ftsp_linksreliable = 0;

Reply via email to