From 59b616d3950abca6b6dbb402817f7beb7bb084a5 Mon Sep 17 00:00:00 2001 From: Peter Avalos Date: Fri, 27 Feb 2009 03:26:46 -0500 Subject: [PATCH] Sync fts(3) with FreeBSD. Here's some of the relevant commit logs from FreeBSD: FTSENT now avoids the use of the struct hack, thereby allowing future extensions to the structure (e.g., for extended attributes) without rebreaking the ABI. FTSENT now contains a pointer to the parent stream, which fts_compar() can then take advantage of, avoiding the undefined behavior previously warned about. As a consequence of this change, the prototype of the comparison function passed to fts_open() has changed to reflect the required amount of constness for its use. All callers in the tree are updated to use the correct prototype. Comparison functions can now make use of the new parent pointer to access the new stream-specific private data pointer, which is intended to assist creation of reentrant library routines which use fts(3) internally. The FTS_NOSTAT option is an optimisation that reduces the number of stat(2) calls by keeping an eye of the number of links a directory has. It assumes that each subdirectory will have a hard link to its parent, to represent the ".." node, and stops calling stat(2) when all links are accounted for in a given directory. This assumption is really only valid for UNIX-like filesystems: A concrete example is NTFS. The NTFS "i-node" does contain a link count, but most/all directories have a link count between 0 and 2 inclusive. The end result is that find on an NTFS volume won't actually traverse the entire hierarchy of the directories passed to it. (Those with a link count of two are not traversed at all) The fix checks the "UFSness" of the filesystem before enabling the optimisation. In fts_build(), if we try to chdir and fail (e.g. due to lack of search permission), try to continue in FTS_DONTCHDIR mode. Of course this won't work for long paths, but we can't descend more than one pathname component beyond the directory anyway if we lack search permission. Our fts(3) API, as inherited from 4.4BSD, suffers from integer fields in FTS and FTSENT structs being too narrow. In addition, the narrow types creep from there into fts.c. As a result, fts(3) consumers, e.g., find(1) or rm(1), can't handle file trees an ordinary user can create, which can have security implications. Fix a file descriptor leak. --- bin/cp/cp.c | 4 +- bin/ls/ls.c | 4 +- include/fts.h | 46 ++++---- lib/libc/gen/fts.3 | 88 +++++++++++---- lib/libc/gen/fts.c | 241 +++++++++++++++++++++++++++------------- usr.bin/find/find.c | 4 +- usr.sbin/mtree/create.c | 4 +- 7 files changed, 265 insertions(+), 126 deletions(-) diff --git a/bin/cp/cp.c b/bin/cp/cp.c index 074de9612c..e382e1fc53 100644 --- a/bin/cp/cp.c +++ b/bin/cp/cp.c @@ -83,7 +83,7 @@ volatile sig_atomic_t info; enum op { FILE_TO_FILE, FILE_TO_DIR, DIR_TO_DNE }; static int copy (char **, enum op, int); -static int mastercmp (const FTSENT **, const FTSENT **); +static int mastercmp (const FTSENT * const *, const FTSENT * const *); static void siginfo (int); int @@ -475,7 +475,7 @@ copy(char **argv, enum op type, int fts_options) * files first reduces seeking. */ static int -mastercmp(const FTSENT **a, const FTSENT **b) +mastercmp(const FTSENT * const *a, const FTSENT * const *b) { int a_info, b_info; diff --git a/bin/ls/ls.c b/bin/ls/ls.c index d6ddc8662b..3bde884b18 100644 --- a/bin/ls/ls.c +++ b/bin/ls/ls.c @@ -83,7 +83,7 @@ } while(0) static void display(const FTSENT *, FTSENT *); -static int mastercmp(const FTSENT **, const FTSENT **); +static int mastercmp(const FTSENT * const *, const FTSENT * const *); static void traverse(int, char **, int); static void (*printfcn)(const DISPLAY *); @@ -786,7 +786,7 @@ display(const FTSENT *p, FTSENT *list) * All other levels use the sort function. Error entries remain unsorted. */ static int -mastercmp(const FTSENT **a, const FTSENT **b) +mastercmp(const FTSENT * const *a, const FTSENT * const *b) { int a_info, b_info; diff --git a/include/fts.h b/include/fts.h index 6fcc012c02..4bf6d19834 100644 --- a/include/fts.h +++ b/include/fts.h @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * @(#)fts.h 8.3 (Berkeley) 8/14/94 - * $FreeBSD: src/include/fts.h,v 1.3.6.1 2001/08/02 22:34:17 assar Exp $ + * $FreeBSD: src/include/fts.h,v 1.12 2008/01/26 17:09:40 yar Exp $ * $DragonFly: src/include/fts.h,v 1.3 2003/11/14 01:01:43 dillon Exp $ */ @@ -45,10 +45,10 @@ typedef struct { dev_t fts_dev; /* starting device # */ char *fts_path; /* path for this descent */ int fts_rfd; /* fd for root */ - int fts_pathlen; /* sizeof(path) */ - int fts_nitems; /* elements in the sort array */ + size_t fts_pathlen; /* sizeof(path) */ + size_t fts_nitems; /* elements in the sort array */ int (*fts_compar) /* compare function */ - (const struct _ftsent **, const struct _ftsent **); + (const struct _ftsent * const *, const struct _ftsent * const *); #define FTS_COMFOLLOW 0x001 /* follow command line symlinks */ #define FTS_LOGICAL 0x002 /* logical walk */ @@ -63,20 +63,22 @@ typedef struct { #define FTS_NAMEONLY 0x100 /* (private) child names only */ #define FTS_STOP 0x200 /* (private) unrecoverable error */ int fts_options; /* fts_open options, global flags */ + void *fts_clientptr; /* thunk for sort function */ } FTS; typedef struct _ftsent { struct _ftsent *fts_cycle; /* cycle node */ struct _ftsent *fts_parent; /* parent directory */ struct _ftsent *fts_link; /* next file in directory */ - long fts_number; /* local numeric value */ - void *fts_pointer; /* local address value */ + long long fts_number; /* local numeric value */ +#define fts_bignum fts_number /* XXX non-std, should go away */ + void *fts_pointer; /* local address value */ char *fts_accpath; /* access path */ char *fts_path; /* root path */ int fts_errno; /* errno for this node */ int fts_symfd; /* fd for symlink */ - u_short fts_pathlen; /* strlen(fts_path) */ - u_short fts_namelen; /* strlen(fts_name) */ + size_t fts_pathlen; /* strlen(fts_path) */ + size_t fts_namelen; /* strlen(fts_name) */ ino_t fts_ino; /* inode */ dev_t fts_dev; /* device */ @@ -84,7 +86,7 @@ typedef struct _ftsent { #define FTS_ROOTPARENTLEVEL -1 #define FTS_ROOTLEVEL 0 - short fts_level; /* depth (-1 to N) */ + long fts_level; /* depth (-1 to N) */ #define FTS_D 1 /* preorder directory */ #define FTS_DC 2 /* directory that causes cycles */ @@ -100,32 +102,38 @@ typedef struct _ftsent { #define FTS_SL 12 /* symbolic link */ #define FTS_SLNONE 13 /* symbolic link without target */ #define FTS_W 14 /* whiteout object */ - u_short fts_info; /* user flags for FTSENT structure */ + int fts_info; /* user status for FTSENT structure */ #define FTS_DONTCHDIR 0x01 /* don't chdir .. to the parent */ #define FTS_SYMFOLLOW 0x02 /* followed a symlink to get here */ #define FTS_ISW 0x04 /* this is a whiteout object */ - u_short fts_flags; /* private flags for FTSENT structure */ + unsigned fts_flags; /* private flags for FTSENT structure */ #define FTS_AGAIN 1 /* read node again */ #define FTS_FOLLOW 2 /* follow symbolic link */ #define FTS_NOINSTR 3 /* no instructions */ #define FTS_SKIP 4 /* discard node */ - u_short fts_instr; /* fts_set() instructions */ + int fts_instr; /* fts_set() instructions */ struct stat *fts_statp; /* stat(2) information */ - char fts_name[1]; /* file name */ + char *fts_name; /* file name */ + FTS *fts_fts; /* back pointer to main FTS */ } FTSENT; #include __BEGIN_DECLS -FTSENT *fts_children (FTS *, int); -int fts_close (FTS *); -FTS *fts_open (char * const *, int, - int (*)(const FTSENT **, const FTSENT **)); -FTSENT *fts_read (FTS *); -int fts_set (FTS *, FTSENT *, int); +FTSENT *fts_children(FTS *, int); +int fts_close(FTS *); +void *fts_get_clientptr(FTS *); +#define fts_get_clientptr(fts) ((fts)->fts_clientptr) +FTS *fts_get_stream(FTSENT *); +#define fts_get_stream(ftsent) ((ftsent)->fts_fts) +FTS *fts_open(char * const *, int, + int (*)(const FTSENT * const *, const FTSENT * const *)); +FTSENT *fts_read(FTS *); +int fts_set(FTS *, FTSENT *, int); +void fts_set_clientptr(FTS *, void *); __END_DECLS #endif /* !_FTS_H_ */ diff --git a/lib/libc/gen/fts.3 b/lib/libc/gen/fts.3 index d35b619297..10592b9405 100644 --- a/lib/libc/gen/fts.3 +++ b/lib/libc/gen/fts.3 @@ -9,10 +9,6 @@ .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software developed by the University of -.\" California, Berkeley and its contributors. .\" 4. Neither the name of the University nor the names of its contributors .\" may be used to endorse or promote products derived from this software .\" without specific prior written permission. @@ -30,10 +26,10 @@ .\" SUCH DAMAGE. .\" .\" @(#)fts.3 8.5 (Berkeley) 4/16/94 -.\" $FreeBSD: src/lib/libc/gen/fts.3,v 1.7.2.6 2003/03/13 18:05:37 trhodes Exp $ +.\" $FreeBSD: src/lib/libc/gen/fts.3,v 1.24 2008/01/26 17:09:40 yar Exp $ .\" $DragonFly: src/lib/libc/gen/fts.3,v 1.4 2007/07/30 22:11:32 swildner Exp $ .\" -.Dd September 15, 2002 +.Dd January 26, 2008 .Dt FTS 3 .Os .Sh NAME @@ -46,13 +42,19 @@ .In sys/stat.h .In fts.h .Ft FTS * -.Fn fts_open "char * const *path_argv" "int options" "int (*compar)(const FTSENT **, const FTSENT **)" +.Fn fts_open "char * const *path_argv" "int options" "int (*compar)(const FTSENT * const *, const FTSENT * const *)" .Ft FTSENT * .Fn fts_read "FTS *ftsp" .Ft FTSENT * .Fn fts_children "FTS *ftsp" "int options" .Ft int .Fn fts_set "FTS *ftsp" "FTSENT *f" "int options" +.Ft void +.Fn fts_set_clientptr "FTS *ftsp" "void *clientdata" +.Ft void * +.Fn fts_get_clientptr "FTS *ftsp" +.Ft FTS * +.Fn fts_get_stream "FTSENT *f" .Ft int .Fn fts_close "FTS *ftsp" .Sh DESCRIPTION @@ -90,37 +92,57 @@ prune and/or re-visit portions of the hierarchy. Two structures are defined (and typedef'd) in the include file .In fts.h . The first is -.Fa FTS , +.Vt FTS , the structure that represents the file hierarchy itself. The second is -.Fa FTSENT , +.Vt FTSENT , the structure that represents a file in the file hierarchy. Normally, an -.Fa FTSENT +.Vt FTSENT structure is returned for every file in the file hierarchy. In this manual page, .Dq file and -.Dq Fa FTSENT No structure +.Dq Vt FTSENT No structure are generally interchangeable. +.Pp +The +.Vt FTS +structure contains space for a single pointer, which may be used to +store application data or per-hierarchy state. +The +.Fn fts_set_clientptr +and +.Fn fts_get_clientptr +functions may be used to set and retrieve this pointer. +This is likely to be useful only when accessed from the sort +comparison function, which can determine the original +.Vt FTS +stream of its arguments using the +.Fn fts_get_stream +function. +The two +.Li get +functions are also available as macros of the same name. +.Pp The -.Fa FTSENT +.Vt FTSENT structure contains at least the following fields, which are described in greater detail below: .Bd -literal typedef struct _ftsent { - u_short fts_info; /* flags for FTSENT structure */ + int fts_info; /* status for FTSENT structure */ char *fts_accpath; /* access path */ char *fts_path; /* root path */ - u_short fts_pathlen; /* strlen(fts_path) */ + size_t fts_pathlen; /* strlen(fts_path) */ char *fts_name; /* file name */ - u_short fts_namelen; /* strlen(fts_name) */ - short fts_level; /* depth (\-1 to N) */ + size_t fts_namelen; /* strlen(fts_name) */ + long fts_level; /* depth (\-1 to N) */ int fts_errno; /* file errno */ - long fts_number; /* local numeric value */ + long long fts_number; /* local numeric value */ void *fts_pointer; /* local address value */ struct ftsent *fts_parent; /* parent directory */ struct ftsent *fts_link; /* next file structure */ @@ -177,7 +199,7 @@ A directory being visited in post-order. The contents of the .Vt FTSENT structure will be unchanged from when -it was returned in pre-order, i.e. with the +it was returned in pre-order, i.e., with the .Fa fts_info field set to .Dv FTS_D . @@ -281,11 +303,12 @@ It is initialized to A pointer to the .Vt FTSENT structure referencing the file in the hierarchy -immediately above the current file, i.e. the directory of which this +immediately above the current file, i.e., the directory of which this file is a member. A parent structure for the initial entry point is provided as well, however, only the .Fa fts_level , +.Fa fts_bignum , .Fa fts_number and .Fa fts_pointer @@ -347,6 +370,13 @@ The .Fa fts_name field is always .Dv NUL Ns -terminated . +.Pp +Note that the use of +.Fa fts_bignum +is mutually exclusive with the use of +.Fa fts_number +or +.Fa fts_pointer . .Sh FTS_OPEN The .Fn fts_open @@ -576,7 +606,7 @@ has not yet been called for a hierarchy, .Fn fts_children will return a pointer to the files in the logical directory specified to .Fn fts_open , -i.e. the arguments specified to +i.e., the arguments specified to .Fn fts_open . Otherwise, if the .Vt FTSENT @@ -753,10 +783,20 @@ The options were invalid. .Xr find 1 , .Xr chdir 2 , .Xr stat 2 , +.Xr ftw 3 , .Xr qsort 3 -.Sh STANDARDS +.Sh HISTORY The .Nm -utility is expected to be included in a future -.St -p1003.1-88 -revision. +interface was first introduced in +.Bx 4.4 . +The +.Fn fts_get_clientptr , +.Fn fts_get_stream , +and +.Fn fts_set_clientptr +functions were introduced in +.Fx 5.0 , +principally to provide for alternative interfaces to the +.Nm +functionality using different data structures. diff --git a/lib/libc/gen/fts.c b/lib/libc/gen/fts.c index a09537b331..a8e24b05ff 100644 --- a/lib/libc/gen/fts.c +++ b/lib/libc/gen/fts.c @@ -10,10 +10,6 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. @@ -30,17 +26,15 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * + * @(#)fts.c 8.6 (Berkeley) 8/14/94 * $OpenBSD: fts.c,v 1.22 1999/10/03 19:22:22 millert Exp $ - * - * $FreeBSD: src/lib/libc/gen/fts.c,v 1.14.2.4 2001/06/01 22:00:34 kris Exp $ + * $FreeBSD: src/lib/libc/gen/fts.c,v 1.30 2009/03/04 03:30:21 das Exp $ * $DragonFly: src/lib/libc/gen/fts.c,v 1.7 2005/11/13 00:07:42 swildner Exp $ - * - * @(#)fts.c 8.6 (Berkeley) 8/14/94 - * $FreeBSD: src/lib/libc/gen/fts.c,v 1.14.2.4 2001/06/01 22:00:34 kris Exp $ */ #include "namespace.h" #include +#include #include #include @@ -52,16 +46,17 @@ #include #include "un-namespace.h" -static FTSENT *fts_alloc(FTS *, const char *, int); +static FTSENT *fts_alloc(FTS *, const char *, size_t); static FTSENT *fts_build(FTS *, int); static void fts_lfree(FTSENT *); static void fts_load(FTS *, FTSENT *); static size_t fts_maxarglen(char * const *); static void fts_padjust(FTS *, FTSENT *); static int fts_palloc(FTS *, size_t); -static FTSENT *fts_sort(FTS *, FTSENT *, int); -static u_short fts_stat(FTS *, FTSENT *, int); +static FTSENT *fts_sort(FTS *, FTSENT *, size_t); +static int fts_stat(FTS *, FTSENT *, int); static int fts_safe_changedir(FTS *, FTSENT *, int, const char *); +static int fts_ufslinks(FTS *, const FTSENT *); #define ISDOT(a) (a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2]))) @@ -76,10 +71,40 @@ static int fts_safe_changedir(FTS *, FTSENT *, int, const char *); #define BNAMES 2 /* fts_children, names only */ #define BREAD 3 /* fts_read */ +/* + * Internal representation of an FTS, including extra implementation + * details. The FTS returned from fts_open points to this structure's + * ftsp_fts member (and can be cast to an _fts_private as required) + */ +struct _fts_private { + FTS ftsp_fts; + struct statfs ftsp_statfs; + dev_t ftsp_dev; + int ftsp_linksreliable; +}; + +/* + * The "FTS_NOSTAT" option can avoid a lot of calls to stat(2) if it + * knows that a directory could not possibly have subdirectories. This + * is decided by looking at the link count: a subdirectory would + * increment its parent's link count by virtue of its own ".." entry. + * This assumption only holds for UFS-like filesystems that implement + * links and directories this way, so we must punt for others. + */ + +static const char *ufslike_filesystems[] = { + "ufs", + "nfs", + "nfs4", + "ext2fs", + 0 +}; + FTS * fts_open(char * const *argv, int options, - int (*compar)(const FTSENT **, const FTSENT **)) + int (*compar)(const FTSENT * const *, const FTSENT * const *)) { + struct _fts_private *priv; FTS *sp; FTSENT *p, *root; FTSENT *parent, *tmp; @@ -91,10 +116,11 @@ fts_open(char * const *argv, int options, return (NULL); } - /* Allocate/initialize the stream */ - if ((sp = malloc((u_int)sizeof(FTS))) == NULL) + /* Allocate/initialize the stream. */ + if ((priv = malloc(sizeof(*priv))) == NULL) return (NULL); - memset(sp, 0, sizeof(FTS)); + memset(priv, 0, sizeof(*priv)); + sp = &priv->ftsp_fts; sp->fts_compar = compar; sp->fts_options = options; @@ -344,7 +370,7 @@ fts_read(FTS *sp) if (fts_safe_changedir(sp, p, -1, p->fts_accpath)) { p->fts_errno = errno; p->fts_flags |= FTS_DONTCHDIR; - for (p = sp->fts_child; p != NULL; + for (p = sp->fts_child; p != NULL; p = p->fts_link) p->fts_accpath = p->fts_parent->fts_accpath; @@ -440,7 +466,7 @@ name: t = sp->fts_path + NAPPEND(p->fts_parent); } _close(p->fts_symfd); } else if (!(p->fts_flags & FTS_DONTCHDIR) && - fts_safe_changedir(sp, p->fts_parent, -1, "..")) { + fts_safe_changedir(sp, p->fts_parent, -1, "..")) { SET(FTS_STOP); return (NULL); } @@ -527,12 +553,42 @@ fts_children(FTS *sp, int instr) if ((fd = _open(".", O_RDONLY, 0)) < 0) return (NULL); sp->fts_child = fts_build(sp, instr); - if (fchdir(fd)) + if (fchdir(fd)) { + _close(fd); return (NULL); + } _close(fd); return (sp->fts_child); } +#ifndef fts_get_clientptr +#error "fts_get_clientptr not defined" +#endif + +void * +(fts_get_clientptr)(FTS *sp) +{ + + return (fts_get_clientptr(sp)); +} + +#ifndef fts_get_stream +#error "fts_get_stream not defined" +#endif + +FTS * +(fts_get_stream)(FTSENT *p) +{ + return (fts_get_stream(p)); +} + +void +fts_set_clientptr(FTS *sp, void *clientptr) +{ + + sp->fts_clientptr = clientptr; +} + /* * This is the tricky part -- do not casually change *anything* in here. The * idea is to build the linked list of entries that are used by fts_children @@ -552,13 +608,14 @@ fts_build(FTS *sp, int type) { struct dirent *dp; FTSENT *p, *head; - int nitems; FTSENT *cur, *tail; DIR *dirp; void *oldaddr; - int cderrno, descend, len, level, maxlen, nlinks, oflag, saved_errno, - nostat, doadjust; char *cp; + int cderrno, descend, oflag, saved_errno, nostat, doadjust; + long level; + long nlinks; /* has to be signed because -1 is a magic value */ + size_t dnamlen, len, maxlen, nitems; /* Set current node pointer. */ cur = sp->fts_cur; @@ -569,9 +626,9 @@ fts_build(FTS *sp, int type) */ #ifdef FTS_WHITEOUT if (ISSET(FTS_WHITEOUT)) - oflag = DTF_NODUP|DTF_REWIND; + oflag = DTF_NODUP | DTF_REWIND; else - oflag = DTF_HIDEW|DTF_NODUP|DTF_REWIND; + oflag = DTF_HIDEW | DTF_NODUP | DTF_REWIND; #else #define __opendir2(path, flag) opendir(path) #endif @@ -593,7 +650,10 @@ fts_build(FTS *sp, int type) /* Be quiet about nostat, GCC. */ nostat = 0; } else if (ISSET(FTS_NOSTAT) && ISSET(FTS_PHYSICAL)) { - nlinks = cur->fts_nlink - (ISSET(FTS_SEEDOT) ? 0 : 2); + if (fts_ufslinks(sp, cur)) + nlinks = cur->fts_nlink - (ISSET(FTS_SEEDOT) ? 0 : 2); + else + nlinks = -1; nostat = 1; } else { nlinks = -1; @@ -628,8 +688,6 @@ fts_build(FTS *sp, int type) cur->fts_flags |= FTS_DONTCHDIR; descend = 0; cderrno = errno; - closedir(dirp); - dirp = NULL; } else descend = 1; } else @@ -661,14 +719,15 @@ fts_build(FTS *sp, int type) /* Read the directory, attaching each entry to the `link' pointer. */ doadjust = 0; for (head = tail = NULL, nitems = 0; dirp && (dp = readdir(dirp));) { + dnamlen = dp->d_namlen; if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name)) continue; - if ((p = fts_alloc(sp, dp->d_name, (int)dp->d_namlen)) == NULL) + if ((p = fts_alloc(sp, dp->d_name, dnamlen)) == NULL) goto mem1; - if (dp->d_namlen >= maxlen) { /* include space for NUL */ + if (dnamlen >= maxlen) { /* include space for NUL */ oldaddr = sp->fts_path; - if (fts_palloc(sp, dp->d_namlen + len + 1)) { + if (fts_palloc(sp, dnamlen + len + 1)) { /* * No more memory for path or structures. Save * errno, free up the current structure and the @@ -693,24 +752,9 @@ mem1: saved_errno = errno; maxlen = sp->fts_pathlen - len; } - if (len + dp->d_namlen >= USHRT_MAX) { - /* - * In an FTSENT, fts_pathlen is a u_short so it is - * possible to wraparound here. If we do, free up - * the current structure and the structures already - * allocated, then error out with ENAMETOOLONG. - */ - free(p); - fts_lfree(head); - closedir(dirp); - cur->fts_info = FTS_ERR; - SET(FTS_STOP); - errno = ENAMETOOLONG; - return (NULL); - } p->fts_level = level; p->fts_parent = sp->fts_cur; - p->fts_pathlen = len + dp->d_namlen; + p->fts_pathlen = len + dnamlen; #ifdef FTS_WHITEOUT if (dp->d_type == DT_WHT) @@ -808,7 +852,7 @@ mem1: saved_errno = errno; return (head); } -static u_short +static int fts_stat(FTS *sp, FTSENT *p, int follow) { FTSENT *t; @@ -821,10 +865,10 @@ fts_stat(FTS *sp, FTSENT *p, int follow) sbp = ISSET(FTS_NOSTAT) ? &sb : p->fts_statp; #ifdef FTS_WHITEOUT - /* check for whiteout */ + /* Check for whiteout. */ if (p->fts_flags & FTS_ISW) { if (sbp != &sb) { - memset(sbp, '\0', sizeof (*sbp)); + memset(sbp, '\0', sizeof(*sbp)); sbp->st_mode = S_IFWHT; } return (FTS_W); @@ -888,8 +932,23 @@ err: memset(sbp, 0, sizeof(struct stat)); return (FTS_DEFAULT); } +/* + * The comparison function takes pointers to pointers to FTSENT structures. + * Qsort wants a comparison function that takes pointers to void. + * (Both with appropriate levels of const-poisoning, of course!) + * Use a trampoline function to deal with the difference. + */ +static int +fts_compar(const void *a, const void *b) +{ + FTS *parent; + + parent = (*(const FTSENT * const *)a)->fts_fts; + return (*parent->fts_compar)(a, b); +} + static FTSENT * -fts_sort(FTS *sp, FTSENT *head, int nitems) +fts_sort(FTS *sp, FTSENT *head, size_t nitems) { FTSENT **ap, *p; @@ -910,8 +969,7 @@ fts_sort(FTS *sp, FTSENT *head, int nitems) } for (ap = sp->fts_array, p = head; p; p = p->fts_link) *ap++ = p; - qsort(sp->fts_array, nitems, sizeof(FTSENT *), - (int (*)(const void *, const void *))sp->fts_compar); + qsort(sp->fts_array, nitems, sizeof(FTSENT *), fts_compar); for (head = *(ap = sp->fts_array); --nitems; ++ap) ap[0]->fts_link = ap[1]; ap[0]->fts_link = NULL; @@ -919,31 +977,41 @@ fts_sort(FTS *sp, FTSENT *head, int nitems) } static FTSENT * -fts_alloc(FTS *sp, const char *name, int namelen) +fts_alloc(FTS *sp, const char *name, size_t namelen) { FTSENT *p; size_t len; + struct ftsent_withstat { + FTSENT ent; + struct stat statbuf; + }; + /* * The file name is a variable length array and no stat structure is * necessary if the user has set the nostat bit. Allocate the FTSENT * structure, the file name and the stat structure in one chunk, but - * be careful that the stat structure is reasonably aligned. Since the - * fts_name field is declared to be of size 1, the fts_name pointer is - * namelen + 2 before the first possible address of the stat structure. + * be careful that the stat structure is reasonably aligned. */ - len = sizeof(FTSENT) + namelen; - if (!ISSET(FTS_NOSTAT)) - len += sizeof(struct stat) + ALIGNBYTES; + if (ISSET(FTS_NOSTAT)) + len = sizeof(FTSENT) + namelen + 1; + else + len = sizeof(struct ftsent_withstat) + namelen + 1; + if ((p = malloc(len)) == NULL) return (NULL); + if (ISSET(FTS_NOSTAT)) { + p->fts_name = (char *)(p + 1); + p->fts_statp = NULL; + } else { + p->fts_name = (char *)((struct ftsent_withstat *)p + 1); + p->fts_statp = &((struct ftsent_withstat *)p)->statbuf; + } + /* Copy the name and guarantee NUL termination. */ - memmove(p->fts_name, name, namelen); + memcpy(p->fts_name, name, namelen); p->fts_name[namelen] = '\0'; - - if (!ISSET(FTS_NOSTAT)) - p->fts_statp = (struct stat *)ALIGN(p->fts_name + namelen + 2); p->fts_namelen = namelen; p->fts_path = sp->fts_path; p->fts_errno = 0; @@ -951,6 +1019,7 @@ fts_alloc(FTS *sp, const char *name, int namelen) p->fts_instr = FTS_NOINSTR; p->fts_number = 0; p->fts_pointer = NULL; + p->fts_fts = sp; return (p); } @@ -977,18 +1046,6 @@ fts_palloc(FTS *sp, size_t more) { sp->fts_pathlen += more + 256; - /* - * Check for possible wraparound. In an FTS, fts_pathlen is - * a signed int but in an FTSENT it is an unsigned short. - * We limit fts_pathlen to USHRT_MAX to be safe in both cases. - */ - if (sp->fts_pathlen < 0 || sp->fts_pathlen >= USHRT_MAX) { - if (sp->fts_path) - free(sp->fts_path); - sp->fts_path = NULL; - errno = ENAMETOOLONG; - return (1); - } sp->fts_path = reallocf(sp->fts_path, sp->fts_pathlen); return (sp->fts_path == NULL); } @@ -1065,3 +1122,37 @@ bail: errno = oerrno; return (ret); } + +/* + * Check if the filesystem for "ent" has UFS-style links. + */ +static int +fts_ufslinks(FTS *sp, const FTSENT *ent) +{ + struct _fts_private *priv; + const char **cpp; + + priv = (struct _fts_private *)sp; + /* + * If this node's device is different from the previous, grab + * the filesystem information, and decide on the reliability + * of the link information from this filesystem for stat(2) + * avoidance. + */ + if (priv->ftsp_dev != ent->fts_dev) { + if (statfs(ent->fts_path, &priv->ftsp_statfs) != -1) { + priv->ftsp_dev = ent->fts_dev; + priv->ftsp_linksreliable = 0; + for (cpp = ufslike_filesystems; *cpp; cpp++) { + if (strcmp(priv->ftsp_statfs.f_fstypename, + *cpp) == 0) { + priv->ftsp_linksreliable = 1; + break; + } + } + } else { + priv->ftsp_linksreliable = 0; + } + } + return (priv->ftsp_linksreliable); +} diff --git a/usr.bin/find/find.c b/usr.bin/find/find.c index 2e146efb86..0a9b9a4f9d 100644 --- a/usr.bin/find/find.c +++ b/usr.bin/find/find.c @@ -51,7 +51,7 @@ #include "find.h" -static int find_compare(const FTSENT **s1, const FTSENT **s2); +static int find_compare(const FTSENT * const *s1, const FTSENT * const *s2); /* * find_compare -- @@ -60,7 +60,7 @@ static int find_compare(const FTSENT **s1, const FTSENT **s2); * order within each directory. */ static int -find_compare(const FTSENT **s1, const FTSENT **s2) +find_compare(const FTSENT * const *s1, const FTSENT * const *s2) { return (strcoll((*s1)->fts_name, (*s2)->fts_name)); diff --git a/usr.sbin/mtree/create.c b/usr.sbin/mtree/create.c index 9a1382b5df..870c434bec 100644 --- a/usr.sbin/mtree/create.c +++ b/usr.sbin/mtree/create.c @@ -69,7 +69,7 @@ static uid_t uid; static mode_t mode; static u_long flags = 0xffffffff; -static int dsort(const FTSENT **, const FTSENT **); +static int dsort(const FTSENT * const *, const FTSENT * const *); static void output(int, int *, const char *, ...); static int statd(FTS *, FTSENT *, uid_t *, gid_t *, mode_t *, u_long *); @@ -378,7 +378,7 @@ statd(FTS *t, FTSENT *parent, uid_t *puid, gid_t *pgid, mode_t *pmode, } static int -dsort(const FTSENT **a, const FTSENT **b) +dsort(const FTSENT * const *a, const FTSENT * const *b) { if (S_ISDIR((*a)->fts_statp->st_mode)) { -- 2.41.0