Sync fts(3) with FreeBSD.
authorPeter Avalos <pavalos@theshell.com>
Fri, 27 Feb 2009 08:26:46 +0000 (03:26 -0500)
committerPeter Avalos <pavalos@theshell.com>
Tue, 7 Apr 2009 07:09:33 +0000 (21:09 -1000)
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
bin/ls/ls.c
include/fts.h
lib/libc/gen/fts.3
lib/libc/gen/fts.c
usr.bin/find/find.c
usr.sbin/mtree/create.c

index 074de96..e382e1f 100644 (file)
@@ -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;
 
index d6ddc86..3bde884 100644 (file)
@@ -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;
 
index 6fcc012..4bf6d19 100644 (file)
@@ -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 <sys/cdefs.h>
 
 __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_ */
index d35b619..10592b9 100644 (file)
@@ -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.
 .\" 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
 .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.
index a09537b..a8e24b0 100644 (file)
  * 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.
  * 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 <sys/param.h>
+#include <sys/mount.h>
 #include <sys/stat.h>
 
 #include <dirent.h>
 #include <unistd.h>
 #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);
+}
index 2e146ef..0a9b9a4 100644 (file)
@@ -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));
index 9a1382b..870c434 100644 (file)
@@ -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)) {