new file mode 100644
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2026 Yocto Project; see
+ * guts/COPYRIGHT for information.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * static FTSENT *
+ * wrap_fts_children(FTS *ftsp, int options) {
+ * FTSENT * rc = NULL;
+ */
+
+ rc = real_fts_children(ftsp, options);
+
+ /* glibc's fts_children calls fstatat internally using glibc-private
+ * symbols, which bypass pseudo's LD_PRELOAD wrappers. We need to
+ * re-stat each entry through pseudo so that pseudo-tracked
+ * ownership and permissions are visible to callers.
+ */
+ FTSENT *p;
+ for (p = rc; p != NULL; p = p->fts_link) {
+ if (p->fts_statp && p->fts_path) {
+ pseudo_msg_t *msg;
+ int save_errno = errno;
+ const char *fts_rpath;
+ PSEUDO_STATBUF buf64;
+
+ switch (p->fts_info) {
+ case FTS_F:
+ case FTS_D:
+ case FTS_SL:
+ case FTS_SLNONE:
+ case FTS_DEFAULT:
+ /* See fts_read.c: fts_path may already contain
+ * the chroot prefix from fts_open's path resolution.
+ * Use it directly in that case.
+ */
+ if (p->fts_path[0] == '/' && pseudo_chroot_len &&
+ !memcmp(p->fts_path, pseudo_chroot, pseudo_chroot_len) &&
+ (p->fts_path[pseudo_chroot_len] == '/' ||
+ p->fts_path[pseudo_chroot_len] == '\0')) {
+ fts_rpath = p->fts_path;
+ } else {
+ fts_rpath = PSEUDO_ROOT_PATH(AT_FDCWD, p->fts_path, AT_SYMLINK_NOFOLLOW);
+ }
+ if (fts_rpath) {
+ pseudo_stat64_from32(&buf64, p->fts_statp);
+ msg = pseudo_client_op(OP_STAT, 0, -1, -1, fts_rpath, &buf64);
+ if (msg && msg->result == RESULT_SUCCEED) {
+ pseudo_stat_msg(&buf64, msg);
+ pseudo_stat32_from64(p->fts_statp, &buf64);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ errno = save_errno;
+ }
+ }
+
+/* return rc;
+ * }
+ */
new file mode 100644
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2026 Yocto Project; see
+ * guts/COPYRIGHT for information.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ * static FTSENT *
+ * wrap_fts_read(FTS *ftsp) {
+ * FTSENT * rc = NULL;
+ */
+
+ rc = real_fts_read(ftsp);
+
+ /* glibc's fts_read calls fstatat internally using glibc-private
+ * symbols, which bypass pseudo's LD_PRELOAD wrappers. We need to
+ * re-stat the entry through pseudo so that pseudo-tracked
+ * ownership and permissions are visible to callers.
+ */
+ if (rc && rc->fts_statp && rc->fts_accpath) {
+ pseudo_msg_t *msg;
+ int save_errno = errno;
+ const char *fts_rpath;
+ PSEUDO_STATBUF buf64;
+
+ switch (rc->fts_info) {
+ case FTS_F:
+ case FTS_D:
+ case FTS_DP:
+ case FTS_SL:
+ case FTS_SLNONE:
+ case FTS_DEFAULT:
+ /* fts_open passes real (chroot-resolved) paths to real_fts_open,
+ * so fts_path is already a real filesystem path. We must use
+ * it directly without PSEUDO_ROOT_PATH, which would
+ * incorrectly prepend the chroot prefix a second time.
+ *
+ * However, for the non-chroot case (or when fts was given
+ * relative paths that are still relative in fts_path),
+ * we still need PSEUDO_ROOT_PATH to make them absolute.
+ */
+ if (rc->fts_path[0] == '/' && pseudo_chroot_len &&
+ !memcmp(rc->fts_path, pseudo_chroot, pseudo_chroot_len) &&
+ (rc->fts_path[pseudo_chroot_len] == '/' ||
+ rc->fts_path[pseudo_chroot_len] == '\0')) {
+ /* Already a real path with chroot prefix */
+ fts_rpath = rc->fts_path;
+ } else {
+ fts_rpath = PSEUDO_ROOT_PATH(AT_FDCWD, rc->fts_path, AT_SYMLINK_NOFOLLOW);
+ }
+ if (fts_rpath) {
+ pseudo_stat64_from32(&buf64, rc->fts_statp);
+ msg = pseudo_client_op(OP_STAT, 0, -1, -1, fts_rpath, &buf64);
+ if (msg && msg->result == RESULT_SUCCEED) {
+ pseudo_stat_msg(&buf64, msg);
+ pseudo_stat32_from64(rc->fts_statp, &buf64);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ errno = save_errno;
+ }
+
+/* return rc;
+ * }
+ */
@@ -13,6 +13,8 @@ int access(const char *path, int mode);
int faccessat(int dirfd, const char *path, int mode, int flags);
int faccessat2(int dirfd, const char *path, int mode, int flags);
FTS *fts_open(char * const *path_argv, int options, int (*compar)(const FTSENT **, const FTSENT **)); /* inode64=1 */
+FTSENT *fts_read(FTS *ftsp); /* noignore_path=1, inode64=1 */
+FTSENT *fts_children(FTS *ftsp, int options); /* noignore_path=1, inode64=1 */
int ftw(const char *path, int (*fn)(const char *, const struct stat *, int), int nopenfd);
int nftw(const char *path, int (*fn)(const char *, const struct stat *, int, struct FTW *), int nopenfd, int flag);
int glob(const char *pattern, int flags, int (*errfunc)(const char *, int), glob_t *pglob);