git: dd532ad13371 - main - fts: reduce fd usage by storing fts_dirfd on directory entries only
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Mon, 10 Aug 2026 16:15:15 UTC
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;