Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 10 Aug 2026 16:15:15 +0000
From:      "Alan Somers" <asomers@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Cc:        "Jitendra Bhati" <bhatijitendra2022@gmail.com>
Subject:   git: dd532ad13371 - main - fts: reduce fd usage by storing fts_dirfd on directory entries only
Message-ID:  <6a79f913.370c9.29e788fe@gitrepo.freebsd.org>

index | next in thread | raw e-mail

The branch main has been updated by asomers:

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

commit dd532ad13371dcabc07d05052a7a256fc83c6ead
Author:     Jitendra Bhati <bhatijitendra2022@gmail.com>
AuthorDate: 2026-08-08 00:31:09 +0000
Commit:     Alan Somers <asomers@FreeBSD.org>
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 <markj@FreeBSD.org>
    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;


home | help

Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?6a79f913.370c9.29e788fe>