git: dd532ad13371 - main - fts: reduce fd usage by storing fts_dirfd on directory entries only

Alan Somers <[email protected]>
Newsgroups gmane.os.freebsd.devel.cvs.src
Message-ID <6a79f913.370c9.29e788fe__23937.1843215608$1786378542$gmane$org@gitrepo.freebsd.org>
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;
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.