mirror of
https://github.com/freebsd/freebsd-src.git
synced 2026-10-28 11:25:21 +01:00
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: 4bd01d6ae0 (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
This commit is contained in:
1 parent
99360212c7
commit
dd532ad133
3 files changed
+34
-23
No files matched your search
+1
-1
@@ -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) */
|
||||
|
||||
+19
-13
@@ -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
|
||||
|
||||
+14
-9
@@ -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;
|
||||
|
||||
Reference in new issue
Block a user