From e0515084fd8989fba4768be221031a7d38a9e1d0 Mon Sep 17 00:00:00 2001 From: Jitendra Bhati Date: Fri, 12 Jun 2026 22:37:55 +0530 Subject: [PATCH 1/2] fts: refactor to use fd-relative operations internally Replace all _open(".", O_RDONLY|O_CLOEXEC) calls with _openat(ROOTFD(sp), ".", O_RDONLY|O_CLOEXEC) in __fts_open(), fts_read(), and fts_children(). Add ftsp_rootfd to struct _fts_private to hold the root file descriptor, and FTSP()/ROOTFD() macros for clean access to it. fts_open() initialises ftsp_rootfd to AT_FDCWD which preserves existing behaviour. This is a preparatory change for fts_openat() which will allow callers to provide a pre-opened directory fd, enabling fts(3) traversal inside Capsicum capability mode. Sponsored by: Google LLC (GSoC 2026) --- lib/libc/gen/fts.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/lib/libc/gen/fts.c b/lib/libc/gen/fts.c index e8063ecb646edd..392dc87e2e1dbf 100644 --- a/lib/libc/gen/fts.c +++ b/lib/libc/gen/fts.c @@ -81,6 +81,8 @@ static int fts_ufslinks(FTS *, const FTSENT *); #define SET(opt) (sp->fts_options |= (opt)) #define FCHDIR(sp, fd) (!ISSET(FTS_NOCHDIR) && fchdir(fd)) +#define FTSP(sp) ((struct _fts_private *)(sp)) +#define ROOTFD(sp) (FTSP(sp)->ftsp_rootfd) /* fts_build flags */ #define BCHILD 1 /* fts_children */ @@ -97,6 +99,7 @@ struct _fts_private { struct statfs ftsp_statfs; dev_t ftsp_dev; int ftsp_linksreliable; + int ftsp_rootfd; }; /* @@ -212,9 +215,9 @@ __fts_open(FTS *sp, char * const *argv) * descriptor we run anyway, just more slowly. */ if (!ISSET(FTS_NOCHDIR) && - (sp->fts_rfd = _open(".", O_RDONLY | O_CLOEXEC, 0)) < 0) + (sp->fts_rfd = _openat(ROOTFD(sp), ".", O_RDONLY | + O_CLOEXEC, 0)) < 0) SET(FTS_NOCHDIR); - return (sp); mem3: fts_lfree(root); @@ -246,6 +249,7 @@ fts_open(char * const *argv, int options, /* Allocate/initialize the stream. */ if ((priv = calloc(1, sizeof(*priv))) == NULL) return (NULL); + priv->ftsp_rootfd = AT_FDCWD; sp = &priv->ftsp_fts; sp->fts_compar = compar; sp->fts_options = options; @@ -439,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 = _open(".", O_RDONLY | O_CLOEXEC, - 0)) < 0) { + if ((p->fts_symfd = _openat(ROOTFD(sp), ".", + O_RDONLY | O_CLOEXEC, 0)) < 0) { p->fts_errno = errno; p->fts_info = FTS_ERR; } else @@ -532,7 +536,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 = - _open(".", O_RDONLY | O_CLOEXEC, 0)) < 0) { + _openat(ROOTFD(sp), ".", O_RDONLY | + O_CLOEXEC, 0)) < 0) { p->fts_errno = errno; p->fts_info = FTS_ERR; } else @@ -671,7 +676,7 @@ fts_children(FTS *sp, int instr) ISSET(FTS_NOCHDIR)) return (sp->fts_child = fts_build(sp, instr)); - if ((fd = _open(".", O_RDONLY | O_CLOEXEC, 0)) < 0) + if ((fd = _openat(ROOTFD(sp), ".", O_RDONLY | O_CLOEXEC, 0)) < 0) return (NULL); sp->fts_child = fts_build(sp, instr); serrno = (sp->fts_child == NULL) ? errno : 0; From 6cf9dd91fa4ccf7085fa33906cc62617a8f8699f Mon Sep 17 00:00:00 2001 From: Jitendra Bhati Date: Sat, 13 Jun 2026 16:06:59 +0530 Subject: [PATCH 2/2] fts: fix _openat() usage in mid-traversal CWD saves ROOTFD(sp) is only correct for the initial open in __fts_open(). After fts has internally chdir'd, ROOTFD(sp) points to the wrong directory. Use AT_FDCWD for the mid-traversal CWD saves in fts_read() and fts_children() which is equivalent to the original _open('.', ...) behaviour. Sponsored by: Google LLC (GSoC 2026) --- lib/libc/gen/fts.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/libc/gen/fts.c b/lib/libc/gen/fts.c index 392dc87e2e1dbf..d48753cdbe487e 100644 --- a/lib/libc/gen/fts.c +++ b/lib/libc/gen/fts.c @@ -443,7 +443,7 @@ 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 = _openat(ROOTFD(sp), ".", + if ((p->fts_symfd = _openat(AT_FDCWD, ".", O_RDONLY | O_CLOEXEC, 0)) < 0) { p->fts_errno = errno; p->fts_info = FTS_ERR; @@ -536,8 +536,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 = - _openat(ROOTFD(sp), ".", O_RDONLY | - O_CLOEXEC, 0)) < 0) { + _openat(AT_FDCWD, ".", O_RDONLY | + O_CLOEXEC, 0)) < 0) { p->fts_errno = errno; p->fts_info = FTS_ERR; } else @@ -676,7 +676,7 @@ fts_children(FTS *sp, int instr) ISSET(FTS_NOCHDIR)) return (sp->fts_child = fts_build(sp, instr)); - if ((fd = _openat(ROOTFD(sp), ".", O_RDONLY | O_CLOEXEC, 0)) < 0) + if ((fd = _openat(AT_FDCWD, ".", O_RDONLY | O_CLOEXEC, 0)) < 0) return (NULL); sp->fts_child = fts_build(sp, instr); serrno = (sp->fts_child == NULL) ? errno : 0;