git: da2025a0e894 - main - fts: Add FTS_COMFOLLOWDIR and FTS_NOSTAT_TYPE.
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Thu, 08 May 2025 14:29:42 UTC
The branch main has been updated by des: URL: https://cgit.FreeBSD.org/src/commit/?id=da2025a0e89455fb646b542b53d5a4ddaa2acbe0 commit da2025a0e89455fb646b542b53d5a4ddaa2acbe0 Author: Dag-Erling Smørgrav <des@FreeBSD.org> AuthorDate: 2025-05-08 14:28:51 +0000 Commit: Dag-Erling Smørgrav <des@FreeBSD.org> CommitDate: 2025-05-08 14:29:15 +0000 fts: Add FTS_COMFOLLOWDIR and FTS_NOSTAT_TYPE. MFC after: never Relnotes: yes Sponsored by: Klara, Inc. Reviewed by: kevans, imp Differential Revision: https://reviews.freebsd.org/D50233 --- include/fts.h | 6 +++++- lib/libc/gen/fts.3 | 22 +++++++++++++++++++++- lib/libc/gen/fts.c | 45 ++++++++++++++++++++++++++++++++++++++------- 3 files changed, 64 insertions(+), 9 deletions(-) diff --git a/include/fts.h b/include/fts.h index f2c40b854ffb..479905bda463 100644 --- a/include/fts.h +++ b/include/fts.h @@ -65,7 +65,11 @@ typedef struct { #define FTS_SEEDOT 0x000020 /* return dot and dot-dot */ #define FTS_XDEV 0x000040 /* don't cross devices */ #define FTS_WHITEOUT 0x000080 /* return whiteout information */ -#define FTS_OPTIONMASK 0x0000ff /* valid user option mask */ + /* 0x0100 is FTS_NAMEONLY below */ + /* 0x0200 was previously FTS_STOP */ +#define FTS_COMFOLLOWDIR 0x00400 /* like COMFOLLOW but directories only */ +#define FTS_NOSTAT_TYPE 0x000800 /* like NOSTAT but use d_type */ +#define FTS_OPTIONMASK 0x000cff /* valid user option mask */ /* valid only for fts_children() */ #define FTS_NAMEONLY 0x000100 /* child names only */ diff --git a/lib/libc/gen/fts.3 b/lib/libc/gen/fts.3 index 3007b773ec55..0c32bb0ebdb6 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 April 17, 2025 +.Dd May 7, 2025 .Dt FTS 3 .Os .Sh NAME @@ -394,6 +394,10 @@ This option causes any symbolic link specified as a root path to be followed immediately whether or not .Dv FTS_LOGICAL is also specified. +.It Dv FTS_COMFOLLOWDIR +This option is similar to +.Dv FTS_COMFOLLOW , +but only follows symbolic links to directories. .It Dv FTS_LOGICAL This option causes the .Nm @@ -449,6 +453,15 @@ field to and leave the contents of the .Fa statp field undefined. +.It Dv FTS_NOSTAT_TYPE +This option is similar to +.Dv FTS_NOSTAT , +but attempts to populate +.Fa fts_info +based on information from the +.Fa d_type +field of +.Vt struct dirent . .It Dv FTS_PHYSICAL This option causes the .Nm @@ -820,6 +833,13 @@ functions were introduced in principally to provide for alternative interfaces to the .Nm functionality using different data structures. +Blocks support and the +.Dv FTS_COMFOLLOWDIR +and +.Dv FTS_NOSTAT +options were added in +.Fx 15.0 +based on similar functionality in macOS. .Sh BUGS The .Fn fts_open diff --git a/lib/libc/gen/fts.c b/lib/libc/gen/fts.c index a55b4a6e2981..fa3ee9cc4c83 100644 --- a/lib/libc/gen/fts.c +++ b/lib/libc/gen/fts.c @@ -126,6 +126,10 @@ __fts_open(FTS *sp, char * const *argv) if (ISSET(FTS_LOGICAL)) SET(FTS_NOCHDIR); + /* NOSTAT_TYPE implies NOSTAT */ + if (ISSET(FTS_NOSTAT_TYPE)) + SET(FTS_NOSTAT); + /* * Start out with 1K of path space, and enough, in any case, * to hold the user's paths. @@ -149,7 +153,9 @@ __fts_open(FTS *sp, char * const *argv) p->fts_level = FTS_ROOTLEVEL; p->fts_parent = parent; p->fts_accpath = p->fts_name; - p->fts_info = fts_stat(sp, p, ISSET(FTS_COMFOLLOW), -1); + p->fts_info = fts_stat(sp, p, + ISSET(FTS_COMFOLLOWDIR) ? -1 : ISSET(FTS_COMFOLLOW), + -1); /* Command-line "." and ".." are real directories. */ if (p->fts_info == FTS_DOT) @@ -904,6 +910,25 @@ mem1: saved_errno = errno; p->fts_info == FTS_DC || p->fts_info == FTS_DOT)) --nlinks; } + if (p->fts_info == FTS_NSOK && ISSET(FTS_NOSTAT_TYPE)) { + switch (dp->d_type) { + case DT_FIFO: + case DT_CHR: + case DT_BLK: + case DT_SOCK: + p->fts_info = FTS_DEFAULT; + break; + case DT_REG: + p->fts_info = FTS_F; + break; + case DT_LNK: + p->fts_info = FTS_SL; + break; + case DT_WHT: + p->fts_info = FTS_W; + break; + } + } /* We walk in directory order so "ls -f" doesn't get upset. */ p->fts_link = NULL; @@ -980,7 +1005,7 @@ fts_stat(FTS *sp, FTSENT *p, int follow, int dfd) dev_t dev; ino_t ino; struct stat *sbp, sb; - int saved_errno; + int ret, saved_errno; const char *path; if (dfd == -1) { @@ -1003,19 +1028,25 @@ fts_stat(FTS *sp, FTSENT *p, int follow, int dfd) } /* - * If doing a logical walk, or application requested FTS_FOLLOW, do - * a stat(2). If that fails, check for a non-existent symlink. If - * fail, set the errno from the stat call. + * If doing a logical walk, or caller requested FTS_COMFOLLOW, do + * a full stat(2). If that fails, do an lstat(2) to check for a + * non-existent symlink. If that fails, set the errno from the + * stat(2) call. + * + * As a special case, if stat(2) succeeded but the target is not a + * directory and follow is negative (indicating FTS_COMFOLLOWDIR + * rather than FTS_COMFOLLOW), we also revert to lstat(2). */ if (ISSET(FTS_LOGICAL) || follow) { - if (fstatat(dfd, path, sbp, 0)) { + if ((ret = fstatat(dfd, path, sbp, 0)) != 0 || + (follow < 0 && !S_ISDIR(sbp->st_mode))) { saved_errno = errno; if (fstatat(dfd, path, sbp, AT_SYMLINK_NOFOLLOW)) { p->fts_errno = saved_errno; goto err; } errno = 0; - if (S_ISLNK(sbp->st_mode)) + if (ret != 0 && S_ISLNK(sbp->st_mode)) return (FTS_SLNONE); } } else if (fstatat(dfd, path, sbp, AT_SYMLINK_NOFOLLOW)) {