Index: include/fts.h =================================================================== --- include/fts.h +++ include/fts.h @@ -61,6 +61,7 @@ #define FTS_NAMEONLY 0x100 /* (private) child names only */ #define FTS_STOP 0x200 /* (private) unrecoverable error */ +#define FTS_CAPMODE 0x400 /* (private) capability mode */ int fts_options; /* fts_open options, global flags */ void *fts_clientptr; /* thunk for sort function */ } FTS; @@ -75,7 +76,7 @@ char *fts_accpath; /* access path */ char *fts_path; /* root path */ int fts_errno; /* errno for this node */ - int fts_symfd; /* fd for symlink */ + int fts_symfd; /* fd for symlink or capmode */ __size_t fts_pathlen; /* strlen(fts_path) */ __size_t fts_namelen; /* strlen(fts_name) */ @@ -124,6 +125,8 @@ __BEGIN_DECLS FTSENT *fts_children(FTS *, int); int fts_close(FTS *); +FTS *fts_fopen(char * const *, int, int *, + int (*)(const FTSENT * const *, const FTSENT * const *)); void *fts_get_clientptr(FTS *); #define fts_get_clientptr(fts) ((fts)->fts_clientptr) FTS *fts_get_stream(FTSENT *); Index: lib/libc/gen/Symbol.map =================================================================== --- lib/libc/gen/Symbol.map +++ lib/libc/gen/Symbol.map @@ -403,6 +403,7 @@ fts_get_clientptr; fts_get_stream; fts_open; + fts_fopen; fts_read; fts_set; fts_set_clientptr; Index: lib/libc/gen/fts.3 =================================================================== --- lib/libc/gen/fts.3 +++ lib/libc/gen/fts.3 @@ -28,7 +28,7 @@ .\" @(#)fts.3 8.5 (Berkeley) 4/16/94 .\" $FreeBSD$ .\" -.Dd January 12, 2014 +.Dd March 21, 2019 .Dt FTS 3 .Os .Sh NAME @@ -39,6 +39,8 @@ .Sh SYNOPSIS .In fts.h .Ft FTS * +.Fn fts_fopen "char * const *path_argv" "int options" "int *file_descriptors" "int (*compar)(const FTSENT * const *, const FTSENT * const *)" +.Ft FTS * .Fn fts_open "char * const *path_argv" "int options" "int (*compar)(const FTSENT * const *, const FTSENT * const *)" .Ft FTSENT * .Fn fts_read "FTS *ftsp" @@ -62,7 +64,9 @@ file hierarchies. A simple overview is that the .Fn fts_open -function returns a +and +.Fn fts_fopen +functions return a .Dq handle on a file hierarchy, which is then supplied to the other @@ -521,6 +525,18 @@ .Fa path_argv for the root paths, and in the order listed in the directory for everything else. +.Sh FTS_FOPEN +The +.Fn fts_fopen +function works exactly the same as +.Fn fts_open . +It takes an extra array of file descriptors corresponding to each +path passed in the argument. +.Fn fts_fopen +sets the +.Dv FTS_NOCDHIR +option automatically. +It also can be used in capability mode. .Sh FTS_READ The .Fn fts_read @@ -726,8 +742,10 @@ function returns 0 on success, and \-1 if an error occurs. .Sh ERRORS -The function +The functions .Fn fts_open +and +.Fn fts_fopen may fail and set .Va errno for any of the errors specified for the library functions @@ -790,7 +808,11 @@ .Fx 5.0 , principally to provide for alternative interfaces to the .Nm -functionality using different data structures. +functionality using different data structures. The +.Fn fts_fopen +function was introduced in +.Fx 13.0 +to allow the fts interface to be used in capability mode. .Sh BUGS The .Fn fts_open Index: lib/libc/gen/fts.c =================================================================== --- lib/libc/gen/fts.c +++ lib/libc/gen/fts.c @@ -58,6 +58,8 @@ static size_t fts_maxarglen(char * const *); static void fts_padjust(FTS *, FTSENT *); static int fts_palloc(FTS *, size_t); +static FTS *fts_private_open(char * const *, int, int *, + int (*)(const FTSENT * const *, const FTSENT * const *)); static FTSENT *fts_sort(FTS *, FTSENT *, size_t); static int fts_stat(FTS *, FTSENT *, int, int); static int fts_safe_changedir(FTS *, FTSENT *, int, char *); @@ -109,18 +111,40 @@ fts_open(char * const *argv, int options, int (*compar)(const FTSENT * const *, const FTSENT * const *)) { - struct _fts_private *priv; - FTS *sp; - FTSENT *p, *root; - FTSENT *parent, *tmp; - size_t len, nitems; + /* Options check. */ + if (options & ~FTS_OPTIONMASK) { + errno = EINVAL; + return (NULL); + } + + return fts_private_open(argv, options, NULL, compar); +} +FTS * +fts_fopen(char * const *argv, int options, int *fds, + int (*compar)(const FTSENT * const *, const FTSENT * const *)) +{ /* Options check. */ if (options & ~FTS_OPTIONMASK) { errno = EINVAL; return (NULL); } + options |= FTS_NOCHDIR | FTS_CAPMODE; + + return fts_private_open(argv, options, fds, compar); +} + +static FTS * +fts_private_open(char * const *argv, int options, int *fds, + int (*compar)(const FTSENT * const *, const FTSENT * const *)) +{ + struct _fts_private *priv; + FTS *sp; + FTSENT *p, *root; + FTSENT *parent, *tmp; + size_t len, nitems; + /* fts_open() requires at least one path */ if (*argv == NULL) { errno = EINVAL; @@ -161,6 +185,10 @@ p->fts_level = FTS_ROOTLEVEL; p->fts_parent = parent; p->fts_accpath = p->fts_name; + if (fds != NULL) { + p->fts_symfd = _dup(*fds); + fds++; + } p->fts_info = fts_stat(sp, p, ISSET(FTS_COMFOLLOW), -1); /* Command-line "." and ".." are real directories. */ @@ -255,6 +283,8 @@ if (sp->fts_cur) { for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) { freep = p; + if (p->fts_symfd > 0) + _close(p->fts_symfd); p = p->fts_link != NULL ? p->fts_link : p->fts_parent; free(freep); } @@ -647,7 +677,17 @@ #else #define __opendir2(path, flag) opendir(path) #endif - if ((dirp = __opendir2(cur->fts_accpath, oflag)) == NULL) { + if (ISSET(FTS_CAPMODE)) { + if (cur->fts_symfd == -1) { + cur->fts_symfd = _openat(cur->fts_parent->fts_symfd, + cur->fts_name, O_RDONLY); + if (cur->fts_symfd < 0) + goto dirfail; + } + if ((dirp = fdopendir(cur->fts_symfd)) == NULL) + goto dirfail; + } else if ((dirp = __opendir2(cur->fts_accpath, oflag)) == NULL) { +dirfail: if (type == BREAD) { cur->fts_info = FTS_DNR; cur->fts_errno = errno; @@ -819,8 +859,12 @@ } ++nitems; } - if (dirp) - (void)closedir(dirp); + if (dirp) { + if (cur->fts_symfd == -1) + (void)closedir(dirp); + else + (void)fdclosedir(dirp); + } /* * If realloc() changed the address of the path, adjust the @@ -876,10 +920,14 @@ int saved_errno; const char *path; - if (dfd == -1) - path = p->fts_accpath, dfd = AT_FDCWD; - else + if (dfd != -1) path = p->fts_name; + else if (p->fts_symfd != -1) + path = ".", dfd = p->fts_symfd; + else if (p->fts_parent->fts_symfd != -1) + path = p->fts_name, dfd = p->fts_parent->fts_symfd; + else + path = p->fts_accpath, dfd = AT_FDCWD; /* If user needs stat info, stat buffer already allocated. */ sbp = ISSET(FTS_NOSTAT) ? &sb : p->fts_statp; @@ -1036,6 +1084,7 @@ p->fts_namelen = namelen; p->fts_path = sp->fts_path; p->fts_errno = 0; + p->fts_symfd = -1; p->fts_flags = 0; p->fts_instr = FTS_NOINSTR; p->fts_number = 0;