summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
authorcf46844 <none@none>2007-01-31 14:03:34 -0800
committercf46844 <none@none>2007-01-31 14:03:34 -0800
commit68a94df11c9810ff7f455e0638cc865a474bd586 (patch)
tree035779972909ad75d549ef8613b66751a6293371 /usr/src
parent3589885c9209c38b62ac67c0611bac09f7dd008f (diff)
downloadillumos-joyent-68a94df11c9810ff7f455e0638cc865a474bd586.tar.gz
PSARC/2006/403 Add two new private interfaces to nftw() for 'find' command
5078524 *find* find is not POSIX.1-2001 compliant 6450771 ftw() fails when it descends to a path that exceeds PATH_MAX
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/cmd/find/find.c9
-rw-r--r--usr/src/head/ftw.h14
-rw-r--r--usr/src/lib/libc/port/gen/_xftw.c233
-rw-r--r--usr/src/lib/libc/port/gen/nftw.c371
4 files changed, 518 insertions, 109 deletions
diff --git a/usr/src/cmd/find/find.c b/usr/src/cmd/find/find.c
index fc608c9d79..a7610cc47e 100644
--- a/usr/src/cmd/find/find.c
+++ b/usr/src/cmd/find/find.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -202,7 +202,7 @@ static mode_t getmode();
static char *gettail();
-static int walkflags = FTW_CHDIR|FTW_PHYS|FTW_ANYERR;
+static int walkflags = FTW_CHDIR|FTW_PHYS|FTW_ANYERR|FTW_NOLOOP;
static struct Node *savetnode;
static struct Node *topnode;
static struct Node *freenode; /* next free node we may use later */
@@ -747,6 +747,11 @@ struct FTW *state;
cmdname, name, strerror(errno));
error = 1;
return (0);
+ } else if (type == FTW_DL) {
+ (void) fprintf(stderr, gettext("%s: cycle detected for %s\n"),
+ cmdname, name);
+ error = 1;
+ return (0);
}
while (np) {
diff --git a/usr/src/head/ftw.h b/usr/src/head/ftw.h
index ba6f535dd7..2ff6c4a243 100644
--- a/usr/src/head/ftw.h
+++ b/usr/src/head/ftw.h
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -43,8 +42,7 @@ extern "C" {
#endif
/*
- * Codes for the third argument to the user-supplied function
- * which is passed as the second argument to ftwalk
+ * Codes for the third argument to the user-supplied function.
*/
#define FTW_F 0 /* file */
@@ -54,9 +52,10 @@ extern "C" {
#define FTW_SL 4 /* symbolic link */
#define FTW_DP 6 /* directory */
#define FTW_SLN 7 /* symbolic link that points to nonexistent file */
+#define FTW_DL 8 /* private interface for find utility */
/*
- * Codes for the fourth argument to ftwalk. You can specify the
+ * Codes for the fourth argument to nftw. You can specify the
* union of these flags.
*/
@@ -66,6 +65,7 @@ extern "C" {
#define FTW_DEPTH 010 /* call descendents before calling the parent */
#define FTW_ANYERR 020 /* return FTW_NS on any stat failure */
#define FTW_HOPTION 040 /* private interface for find utility */
+#define FTW_NOLOOP 0100 /* private interface for find utility */
#if defined(__EXTENSIONS__) || !defined(_XOPEN_SOURCE) || defined(_XPG4_2)
struct FTW
diff --git a/usr/src/lib/libc/port/gen/_xftw.c b/usr/src/lib/libc/port/gen/_xftw.c
index 28433fa78b..09e4d0017b 100644
--- a/usr/src/lib/libc/port/gen/_xftw.c
+++ b/usr/src/lib/libc/port/gen/_xftw.c
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -79,17 +78,11 @@
* go before it starts recycling file descriptors. In general,
* it is necessary to use a file descriptor for each level of the
* tree, but they can be recycled for deep trees by saving the
- * position, closing, re-opening, and seeking. It is possible
- * to start recycling file descriptors by sensing when we have
- * run out, but in general this will not be terribly useful if
- * fn expects to be able to open files. We could also figure out
- * how many file descriptors are available and guarantee a certain
- * number to fn, but we would not know how many to guarantee,
- * and we do not want to impose the extra overhead on a caller who
- * knows how many are available without having to figure it out.
- *
- * It is possible for _xftw to die with a memory fault in the event
- * of a file system so deeply nested that the stack overflows.
+ * position, closing, re-opening, and seeking. In order to descend
+ * to arbitrary depths, _xftw requires 2 file descriptors to be open
+ * during the call to openat(), therefore if the depth argument
+ * is less than 2 _xftw will not use openat(), and it will fail with
+ * ENAMETOOLONG if it descends to a directory that exceeds PATH_MAX.
*/
/*
@@ -103,35 +96,72 @@
#include <sys/feature_tests.h>
#if !defined(_LP64) && _FILE_OFFSET_BITS == 64
+#define fstatat64 _fstatat64
#define lstat64 _lstat64
+#define openat64 _openat64
#define readdir64 _readdir64
#define stat64 _stat64
#else
+#define fstatat _fstatat
#define lstat _lstat
+#define openat _openat
#define readdir _readdir
#define stat _stat
#endif /* !_LP64 && _FILE_OFFSET_BITS == 64 */
+#define close _close
#define closedir _closedir
+#define fdopendir _fdopendir
#define opendir _opendir
#define seekdir _seekdir
+#define strdup _strdup
+#define strtok_r _strtok_r
#define telldir _telldir
#include "lint.h"
#include <sys/types.h>
#include <sys/stat.h>
+#include <fcntl.h>
#include <sys/param.h>
#include <dirent.h>
#include <errno.h>
#include <ftw.h>
#include <string.h>
#include <stdlib.h>
-#include <alloca.h>
+#include <unistd.h>
+
+struct Var {
+ int level;
+ int odepth;
+};
+
+static DIR *nocdopendir(const char *, struct Var *);
+static int nocdstat(const char *, struct stat *, struct Var *, int);
+static const char *get_unrooted(const char *);
+static int fwalk(const char *, int (*)(const char *, const struct stat *, int),
+ int, struct Var *);
+/*ARGSUSED*/
int
_xftw(int ver, const char *path,
int (*fn)(const char *, const struct stat *, int), int depth)
{
+ struct Var var;
+ int rc;
+
+ var.level = 0;
+ var.odepth = depth;
+ rc = fwalk(path, fn, depth, &var);
+ return (rc);
+}
+
+/*
+ * This is the recursive walker.
+ */
+static int
+fwalk(const char *path, int (*fn)(const char *, const struct stat *, int),
+ int depth, struct Var *vp)
+{
size_t n;
int rc;
int save_errno;
@@ -140,19 +170,20 @@ _xftw(int ver, const char *path,
struct stat sb;
struct dirent *direntp;
+ vp->level++;
/*
* Try to get file status.
* If unsuccessful, errno will say why.
* It's ok to have a symbolic link that points to
* non-existing file. In this case, pass FTW_NS
- * to a function instead of aborting _xftw() right away.
+ * to a function instead of aborting fwalk() right away.
*/
- if (stat(path, &sb) < 0) {
+ if (nocdstat(path, &sb, vp, 0) < 0) {
#ifdef S_IFLNK
save_errno = errno;
- if ((lstat(path, &sb) != -1) &&
- ((sb.st_mode & S_IFMT) == S_IFLNK)) {
+ if ((nocdstat(path, &sb, vp, AT_SYMLINK_NOFOLLOW) != -1) &&
+ ((sb.st_mode & S_IFMT) == S_IFLNK)) {
errno = save_errno;
return (*fn)(path, &sb, FTW_NS);
} else {
@@ -174,7 +205,7 @@ _xftw(int ver, const char *path,
*
* Open a file to read the directory
*/
- dirp = opendir(path);
+ dirp = nocdopendir(path, vp);
/*
* Call the user function, telling it whether
@@ -192,13 +223,6 @@ _xftw(int ver, const char *path,
return (rc);
}
- /* Create a prefix to which we will append component names */
- n = strlen(path);
- subpath = alloca(n + MAXNAMELEN + 2);
- (void) strcpy(subpath, path);
- if (subpath[0] != '\0' && subpath[n-1] != '/')
- subpath[n++] = '/';
-
/*
* Read the directory one component at a time.
* We must ignore "." and "..", but other than that,
@@ -211,6 +235,18 @@ _xftw(int ver, const char *path,
strcmp(direntp->d_name, "..") == 0)
continue;
+ /* Create a prefix to which we will append component names */
+ n = strlen(path);
+ subpath = malloc(n + strlen(direntp->d_name) + 2);
+ if (subpath == 0) {
+ (void) closedir(dirp);
+ errno = ENOMEM;
+ return (-1);
+ }
+ (void) strcpy(subpath, path);
+ if (subpath[0] != '\0' && subpath[n-1] != '/')
+ subpath[n++] = '/';
+
/* Append component name to the working path */
(void) strlcpy(&subpath[n], direntp->d_name, MAXNAMELEN);
@@ -220,16 +256,19 @@ _xftw(int ver, const char *path,
*/
if (depth <= 1) {
here = telldir(dirp);
- if (closedir(dirp) < 0)
+ if (closedir(dirp) < 0) {
+ free(subpath);
return (-1);
+ }
}
/*
* Do a recursive call to process the file.
* (watch this, sports fans)
*/
- rc = _xftw(ver, subpath, fn, depth-1);
+ rc = fwalk(subpath, fn, depth-1, vp);
if (rc != 0) {
+ free(subpath);
if (depth > 1)
(void) closedir(dirp);
return (rc);
@@ -239,12 +278,142 @@ _xftw(int ver, const char *path,
* If we closed the file, try to reopen it.
*/
if (depth <= 1) {
- dirp = opendir(path);
- if (dirp == NULL)
+ dirp = nocdopendir(path, vp);
+ if (dirp == NULL) {
+ free(subpath);
return (-1);
+ }
seekdir(dirp, here);
}
+ free(subpath);
}
(void) closedir(dirp);
return (0);
}
+
+/*
+ * Open a directory with an arbitrarily long path name. If the original
+ * depth arg >= 2, use openat() to make sure that it doesn't fail with
+ * ENAMETOOLONG.
+ */
+static DIR *
+nocdopendir(const char *path, struct Var *vp)
+{
+ int fd, cfd;
+ DIR *fdd;
+ char *dirp, *token, *ptr;
+
+ fdd = opendir(path);
+ if ((vp->odepth > 1) && (fdd == NULL) && (errno == ENAMETOOLONG)) {
+ /*
+ * Traverse the path using openat() to get the fd for
+ * fdopendir().
+ */
+ if ((dirp = strdup(path)) == NULL) {
+ errno = ENAMETOOLONG;
+ return (NULL);
+ }
+ if ((token = strtok_r(dirp, "/", &ptr)) != NULL) {
+ if ((fd = openat(AT_FDCWD, dirp, O_RDONLY)) < 0) {
+ (void) free(dirp);
+ errno = ENAMETOOLONG;
+ return (NULL);
+ }
+ while ((token = strtok_r(NULL, "/", &ptr)) != NULL) {
+ if ((cfd = openat(fd, token, O_RDONLY)) < 0) {
+ (void) close(fd);
+ (void) free(dirp);
+ errno = ENAMETOOLONG;
+ return (NULL);
+ }
+ (void) close(fd);
+ fd = cfd;
+ }
+ (void) free(dirp);
+ return (fdopendir(fd));
+ }
+ (void) free(dirp);
+ errno = ENAMETOOLONG;
+ }
+ return (fdd);
+}
+
+/*
+ * Stat a file with an arbitrarily long path name. If we aren't doing a
+ * stat on the arg passed to _xftw() and if the original depth arg >= 2,
+ * use openat() to make sure that it doesn't fail with ENAMETOOLONG.
+ */
+static int
+nocdstat(const char *path, struct stat *statp, struct Var *vp, int sym)
+{
+ int fd, cfd;
+ char *dirp, *token, *ptr;
+ int rc;
+ const char *unrootp;
+ int save_err;
+
+ rc = fstatat(AT_FDCWD, path, statp, sym);
+ if ((vp->level > 1) && (vp->odepth >= 2) && (rc < 0) &&
+ (errno == ENAMETOOLONG)) {
+ /* Traverse path using openat() to get fd for fstatat(). */
+ if ((dirp = strdup(path)) == NULL) {
+ errno = ENAMETOOLONG;
+ return (-1);
+ }
+ if ((token = strtok_r(dirp, "/", &ptr)) != NULL) {
+ if ((fd = openat(AT_FDCWD, dirp, O_RDONLY)) < 0) {
+ (void) free(dirp);
+ errno = ENAMETOOLONG;
+ return (-1);
+ }
+ unrootp = get_unrooted(path);
+ while (((token = strtok_r(NULL, "/", &ptr)) != NULL) &&
+ (strcmp(token, unrootp) != 0)) {
+ if ((cfd = openat(fd, token, O_RDONLY)) < 0) {
+ (void) close(fd);
+ (void) free(dirp);
+ errno = ENAMETOOLONG;
+ return (NULL);
+ }
+ (void) close(fd);
+ fd = cfd;
+ }
+ (void) free(dirp);
+ rc = fstatat(fd, unrootp, statp, sym);
+ save_err = errno;
+ (void) close(fd);
+ errno = save_err;
+ return (rc);
+ }
+ (void) free(dirp);
+ errno = ENAMETOOLONG;
+ }
+ return (rc);
+}
+
+/*
+ * Return pointer basename of path. This routine doesn't remove
+ * trailing slashes, but there won't be any.
+ */
+static const char *
+get_unrooted(const char *path)
+{
+ const char *ptr;
+
+ if (!path || !*path)
+ return (NULL);
+
+ ptr = path + strlen(path);
+ /* find last char in path before any trailing slashes */
+ while (ptr != path && *--ptr == '/')
+ ;
+
+ if (ptr == path) /* all slashes */
+ return (ptr);
+
+ while (ptr != path)
+ if (*--ptr == '/')
+ return (++ptr);
+
+ return (ptr);
+}
diff --git a/usr/src/lib/libc/port/gen/nftw.c b/usr/src/lib/libc/port/gen/nftw.c
index 78fdc109a4..c048664f3b 100644
--- a/usr/src/lib/libc/port/gen/nftw.c
+++ b/usr/src/lib/libc/port/gen/nftw.c
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -21,7 +20,7 @@
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -41,7 +40,7 @@
* nftw visits each file and directory in the tree starting at
* path. It uses the generic directory reading library so it works
* for any file system type. The flags field is used to specify:
- * FTW_PHYS Physical walk, does not follow symblolic links
+ * FTW_PHYS Physical walk, does not follow symbolic links
* Otherwise, nftw will follow links but will not
* walk down any path the crosses itself.
* FTW_MOUNT The walk will not cross a mount point.
@@ -59,6 +58,8 @@
* FTW_HOPTION Use stat the first time the walk
* function is called, regardless of
* whether or not FTW_PHYS is specified.
+ * FTW_NOLOOP Allow find utility to detect infinite loops created
+ * by both symbolic and hard linked directories.
*
* fn is called with four arguments at each file and directory.
* The first argument is the pathname of the object, the second
@@ -78,13 +79,20 @@
* appropriate permission. The stat buffer passed to fn
* is undefined. Stat failure for any reason is
* considered an error and nftw will return -1.
+ * The following value is private, and is used by the find utility:
+ * FTW_DL An infinite loop has been detected.
* The fourth argument is a struct FTW* which contains the depth
* and the offset into pathname to the base name.
* If fn returns nonzero, nftw returns this value to its caller.
*
* depth limits the number of open directories that ftw uses
* before it starts recycling file descriptors. In general,
- * a file descriptor is used for each level.
+ * a file descriptor is used for each level. When FTW_CHDIR isn't set,
+ * in order to descend to arbitrary depths, nftw requires 2 file
+ * descriptors to be open during the call to openat(), therefore if
+ * the depth argument is less than 2 nftw will not use openat(), and
+ * it will fail with ENAMETOOLONG if it descends to a directory that
+ * exceeds PATH_MAX.
*
*/
@@ -94,25 +102,33 @@
#pragma weak nftw64 = _nftw64
#define _nftw _nftw64
#define fstat64 _fstat64
+#define fstatat64 _fstatat64
#define lstat64 _lstat64
+#define openat64 _openat64
#define readdir64 _readdir64
#define stat64 _stat64
#else
#pragma weak nftw = _nftw
#define fstat _fstat
+#define fstatat _fstatat
#define lstat _lstat
+#define openat _openat
#define readdir _readdir
#define stat _stat
#endif /* !_LP64 && _FILE_OFFSET_BITS == 64 */
#define chdir _chdir
+#define close _close
#define closedir _closedir
#define fchdir _fchdir
+#define fdopendir _fdopendir
#define fprintf _fprintf
#define getcwd _getcwd
#define opendir _opendir
#define seekdir _seekdir
+#define strdup _strdup
#define strerror _strerror
+#define strtok_r _strtok_r
#define telldir _telldir
#include "lint.h"
@@ -129,6 +145,8 @@
#include <thread.h>
#include <synch.h>
#include <stdio.h>
+#include <strings.h>
+#include <fcntl.h>
#ifndef PATH_MAX
#define PATH_MAX 1023
@@ -139,17 +157,6 @@
* Putting them into a structure that is passed
* around makes nftw() MT-safe with no locking required.
*/
-struct Var {
- char *fullpath;
- char *tmppath;
- int curflags;
- dev_t cur_mount;
- struct FTW state;
- int walklevel;
- int (*statf)(const char *, struct stat *);
- int (*savedstatf)(const char *, struct stat *);
-};
-
struct Save {
struct Save *last;
DIR *fd;
@@ -159,7 +166,28 @@ struct Save {
ino_t inode;
};
+struct Var {
+ char *home;
+ size_t len;
+ char *fullpath;
+ char *tmppath;
+ int curflags;
+ dev_t cur_mount;
+ struct FTW state;
+ int walklevel;
+ int (*statf)(const char *, struct stat *, struct Save *);
+ int (*savedstatf)(const char *, struct stat *, struct Save *);
+ DIR *(*opendirf)(const char *);
+};
+
static int oldclose(struct Save *);
+static int cdlstat(const char *, struct stat *, struct Save *);
+static int cdstat(const char *, struct stat *, struct Save *);
+static int nocdlstat(const char *, struct stat *, struct Save *);
+static int nocdstat(const char *, struct stat *, struct Save *);
+static DIR *cdopendir(const char *);
+static DIR *nocdopendir(const char *);
+static const char *get_unrooted(const char *);
/*
* This is the recursive walker.
@@ -170,7 +198,7 @@ walk(char *component,
int depth, struct Save *last, struct Var *vp)
{
struct stat statb;
- char *p;
+ char *p, *tmp;
int type;
char *comp;
struct dirent *dir;
@@ -181,6 +209,8 @@ walk(char *component,
int oldbase;
int skip;
struct Save this;
+ size_t base_comp, base_component, base_this_comp, base_last_comp;
+ size_t base_fullpath, base_tmppath;
this.last = last;
this.fd = 0;
@@ -192,22 +222,34 @@ walk(char *component,
if (vp->savedstatf == NULL)
vp->savedstatf = vp->statf;
- if ((vp->walklevel++ == 0) && (vp->curflags & FTW_HOPTION))
- vp->statf = stat;
- else
+ if ((vp->walklevel++ == 0) && (vp->curflags & FTW_HOPTION)) {
+ if (((vp->curflags & FTW_CHDIR) == 0) && (depth >= 2)) {
+ vp->statf = nocdstat;
+ } else {
+ vp->statf = cdstat;
+ }
+ } else {
vp->statf = vp->savedstatf;
+ }
/*
* Determine the type of the component.
*/
- if ((*vp->statf)(comp, &statb) >= 0) {
+ if ((*vp->statf)(comp, &statb, last) >= 0) {
if ((statb.st_mode & S_IFMT) == S_IFDIR) {
type = FTW_D;
if (depth <= 1)
(void) oldclose(last);
- if ((this.fd = opendir(comp)) == 0) {
+ if ((this.fd = (*vp->opendirf)(comp)) == 0) {
if (errno == EMFILE && oldclose(last) &&
- (this.fd = opendir(comp)) != 0) {
+ (this.fd = (*vp->opendirf)(comp)) != 0) {
+ /*
+ * If opendirf fails because there
+ * are OPEN_MAX fd in the calling
+ * process, and we close the oldest
+ * fd, and another opendirf doesn't
+ * fail, depth is set to 1.
+ */
depth = 1;
} else {
type = FTW_DNR;
@@ -250,8 +292,12 @@ walk(char *component,
* Also check the file type, if possible, for symbolic
* link.
*/
- if ((vp->statf == stat) && (lstat(comp, &statb) >= 0) &&
- ((statb.st_mode & S_IFMT) == S_IFLNK)) {
+ if (((vp->statf == cdstat) &&
+ (cdlstat(comp, &statb, last) >= 0) &&
+ ((statb.st_mode & S_IFMT) == S_IFLNK)) ||
+ ((vp->statf == nocdstat) &&
+ (nocdlstat(comp, &statb, last) >= 0) &&
+ ((statb.st_mode & S_IFMT) == S_IFLNK))) {
/*
* Ignore bad symbolic link, let "fn"
@@ -297,10 +343,10 @@ walk(char *component,
if (vp->tmppath[0] != '\0' && component[-1] != '/')
*component++ = '/';
+ *component = 0;
if (vp->curflags & FTW_CHDIR) {
struct stat statb2;
- *component = 0;
/*
* Security check (there is a window between
* (*vp->statf)() and opendir() above).
@@ -324,21 +370,13 @@ walk(char *component,
}
/*
- * If the walk has followed a symbolic link, traverse
- * the walk back to make sure there is not a loop.
- *
- * XXX - may need to look at this
- * There's code to check for cycles, but only for FTW_PHYS
- * (find -L flag). However, all directories should be
- * checked, even if not following links because of hardlinks
- * to directories (not recommended, but can exist).
- *
- * We might have added AVL tree routines here to store and search
- * the inodes and devices, as is done for du/ls/chgrp/chown,
- * but libcmdutils is for for internal use only, so we can't
- * add it to a public libc function (nftw()).
+ * If the walk has followed a symbolic link (FTW_PHYS is not set),
+ * traverse the walk back to make sure there is not a loop.
+ * The find utility (FTW_NOLOOP is set) detects infinite loops
+ * in both symbolic and hard linked directories.
*/
- if ((vp->curflags & FTW_PHYS) == 0) {
+ if ((vp->curflags & FTW_NOLOOP) ||
+ ((vp->curflags & FTW_PHYS) == 0)) {
struct Save *sp = last;
while (sp) {
/*
@@ -346,8 +384,14 @@ walk(char *component,
* is a loop. Get ready to return.
*/
if (sp->dev == statb.st_dev &&
- sp->inode == statb.st_ino)
+ sp->inode == statb.st_ino) {
+ if (vp->curflags & FTW_NOLOOP) {
+ /* private interface for find util */
+ type = FTW_DL;
+ goto fail;
+ }
goto quit;
+ }
sp = sp->last;
}
}
@@ -365,21 +409,58 @@ walk(char *component,
else if (q[1] == '.' && q[2] == 0)
continue;
}
+ if (last != NULL && last->comp != NULL) {
+ base_last_comp = last->comp - vp->home;
+ }
+ base_comp = comp - vp->home;
+ base_component = component - vp->home;
+ if ((strlen(q) + strlen(vp->home) + 1) > vp->len) {
+ /*
+ * When the space needed for vp->home has
+ * exceeded the amount of space that has
+ * been allocated, realloc() more space
+ * and adjust pointers to point to the
+ * (possibly moved) new block for vp->home
+ */
+ base_this_comp = this.comp - vp->home;
+ base_fullpath = vp->fullpath - vp->home;
+ base_tmppath = vp->tmppath - vp->home;
+ vp->len *= 2;
+ tmp = (char *)realloc(vp->home, vp->len);
+ if (tmp == NULL) {
+ rc = -1;
+ goto quit;
+ }
+ vp->home = tmp;
+ comp = vp->home + base_comp;
+ component = vp->home + base_component;
+ this.comp = vp->home + base_this_comp;
+ vp->fullpath = vp->home + base_fullpath;
+ vp->tmppath = vp->home + base_tmppath;
+ if (last != NULL && last->comp != NULL) {
+ last->comp = vp->home + base_last_comp;
+ }
+ }
p = component;
- while (p < &vp->tmppath[PATH_MAX] && *q != '\0')
+ while (*q != '\0')
*p++ = *q++;
*p = '\0';
vp->state.level++;
/* Call walk() recursively. */
rc = walk(p, fn, depth-1, &this, vp);
+ if (last != NULL && last->comp != NULL) {
+ last->comp = vp->home + base_last_comp;
+ }
+ comp = vp->home + base_comp;
+ component = vp->home + base_component;
vp->state.level--;
if (this.fd == 0) {
*component = 0;
if (vp->curflags & FTW_CHDIR) {
this.fd = opendir(".");
} else {
- this.fd = opendir(comp);
+ this.fd = (*vp->opendirf)(comp);
}
if (this.fd == 0) {
rc = -1;
@@ -411,7 +492,7 @@ quit:
}
} else {
if ((cdval = chdir("..")) >= 0) {
- if ((*vp->statf)(".", &statb) < 0 ||
+ if ((*vp->statf)(".", &statb, last) < 0 ||
statb.st_ino != last->inode ||
statb.st_dev != last->dev)
cdval = -1;
@@ -421,14 +502,14 @@ quit:
if (chdir(vp->fullpath) < 0) {
rc = -1;
} else {
- /* Security check */
- if ((vp->curflags & FTW_PHYS) &&
- ((*vp->statf)(".", &statb) < 0 ||
- statb.st_ino != last->inode ||
- statb.st_dev != last->dev)) {
- errno = EAGAIN;
- rc = -1;
- }
+ /* Security check */
+ if ((vp->curflags & FTW_PHYS) &&
+ ((*vp->statf)(".", &statb, last) < 0 ||
+ statb.st_ino != last->inode ||
+ statb.st_dev != last->dev)) {
+ errno = EAGAIN;
+ rc = -1;
+ }
}
}
}
@@ -448,7 +529,6 @@ _nftw(const char *path,
{
struct Var var;
struct stat statb;
- char home[2*(PATH_MAX+1)];
int rc = -1;
char *dp;
char *base;
@@ -456,26 +536,34 @@ _nftw(const char *path,
const char *savepath = path;
int save_errno;
- home[0] = 0;
+ var.walklevel = 0;
+ var.len = 2*(PATH_MAX+1);
+ var.home = (char *)malloc(var.len);
+ if (var.home == NULL)
+ return (-1);
+
+ var.home[0] = 0;
/*
* If the walk is going to change directory before
- * reading it, save current woring directory.
+ * reading it, save current working directory.
*/
if (flags & FTW_CHDIR) {
- if (getcwd(home, PATH_MAX+1) == 0)
+ if (getcwd(var.home, PATH_MAX+1) == 0) {
+ free(var.home);
return (-1);
+ }
}
- endhome = dp = home + strlen(home);
+ endhome = dp = var.home + strlen(var.home);
if (*path == '/')
var.fullpath = dp;
else {
*dp++ = '/';
- var.fullpath = home;
+ var.fullpath = var.home;
}
var.tmppath = dp;
base = dp-1;
- while (*path && dp < &var.tmppath[PATH_MAX]) {
+ while (*path) {
*dp = *path;
if (*dp == '/')
base = dp;
@@ -484,26 +572,45 @@ _nftw(const char *path,
*dp = 0;
var.state.base = (int)(base + 1 - var.tmppath);
if (*path) {
+ free(var.home);
errno = ENAMETOOLONG;
return (-1);
}
var.curflags = flags;
/*
+ * If doing chdir()'s, set var.opendirf to cdopendir.
+ * If not doing chdir()'s and if nftw()'s depth arg >= 2,
+ * set var.opendirf to nocdopendir. In order to
+ * descend to arbitrary depths without doing chdir()'s, nftw()
+ * requires a depth arg >= 2 so that nocdopendir() can use openat()
+ * to traverse the directories. So when not doing
+ * chdir()'s if nftw()'s depth arg <= 1, set var.opendirf to
+ * cdopendir.
* If doing a physical walk (not following symbolic link), set
- * var.statf to lstat(). Otherwise, set var.statf to stat().
+ * var.statf to cdlstat() or nocdlstat(). Otherwise, set var.statf
+ * to cdstat() or nocdstat().
*/
- if ((flags & FTW_PHYS) == 0)
- var.statf = stat;
- else
- var.statf = lstat;
+ if (((flags & FTW_CHDIR) == 0) && (depth >= 2)) {
+ var.opendirf = nocdopendir;
+ if (flags & FTW_PHYS)
+ var.statf = nocdlstat;
+ else
+ var.statf = nocdstat;
+ } else {
+ var.opendirf = cdopendir;
+ if (flags & FTW_PHYS)
+ var.statf = cdlstat;
+ else
+ var.statf = cdstat;
+ }
/*
* If walk is not going to cross a mount point,
* save the current mount point.
*/
if (flags & FTW_MOUNT) {
- if ((*var.statf)(savepath, &statb) >= 0)
+ if ((*var.statf)(savepath, &statb, NULL) >= 0)
var.cur_mount = statb.st_dev;
else
goto done;
@@ -518,18 +625,146 @@ _nftw(const char *path,
save_errno = errno;
errno = 0;
var.savedstatf = NULL;
- var.walklevel = 0;
rc = walk(dp, fn, depth, (struct Save *)0, &var);
if (errno == 0)
errno = save_errno;
done:
*endhome = 0;
if (flags & FTW_CHDIR)
- (void) chdir(home);
+ (void) chdir(var.home);
+ free(var.home);
return (rc);
}
/*
+ * Get stat info on path when FTW_CHDIR is set.
+ */
+/*ARGSUSED1*/
+static int
+cdstat(const char *path, struct stat *statp, struct Save *lp) {
+ return (stat(path, statp));
+}
+
+/*
+ * Get lstat info on path when FTW_CHDIR is set.
+ */
+/*ARGSUSED1*/
+static int
+cdlstat(const char *path, struct stat *statp, struct Save *lp)
+{
+ return (lstat(path, statp));
+}
+
+/*
+ * Get stat info on path when FTW_CHDIR is not set.
+ */
+static int
+nocdstat(const char *path, struct stat *statp, struct Save *lp)
+{
+ const char *unrootp;
+
+ if (lp && lp->fd) {
+ /* get basename of path */
+ unrootp = get_unrooted(path);
+ return (fstatat(lp->fd->dd_fd, unrootp, statp, 0));
+ } else {
+ return (stat(path, statp));
+ }
+}
+
+/*
+ * Get lstat info on path when FTW_CHDIR is not set.
+ */
+static int
+nocdlstat(const char *path, struct stat *statp, struct Save *lp)
+{
+ const char *unrootp;
+
+ if (lp && lp->fd) {
+ /* get basename of path */
+ unrootp = get_unrooted(path);
+ return (fstatat(lp->fd->dd_fd, unrootp, statp,
+ AT_SYMLINK_NOFOLLOW));
+ } else {
+ return (lstat(path, statp));
+ }
+}
+
+/*
+ * Open path directory when FTW_CHDIR is set.
+ *
+ */
+static DIR *
+cdopendir(const char *path)
+{
+ return (opendir(path));
+}
+
+/*
+ * Open path directory when FTW_CHDIR is not set.
+ */
+static DIR *
+nocdopendir(const char *path)
+{
+ int fd, cfd;
+ DIR *fdd;
+ char *dirp, *token, *ptr;
+
+ if (((fdd = opendir(path)) == NULL) && (errno == ENAMETOOLONG)) {
+ if ((dirp = strdup(path)) == NULL) {
+ errno = ENAMETOOLONG;
+ return (NULL);
+ }
+ if ((token = strtok_r(dirp, "/", &ptr)) != NULL) {
+ if ((fd = openat(AT_FDCWD, dirp, O_RDONLY)) < 0) {
+ (void) free(dirp);
+ errno = ENAMETOOLONG;
+ return (NULL);
+ }
+ while ((token = strtok_r(NULL, "/", &ptr)) != NULL) {
+ if ((cfd = openat(fd, token, O_RDONLY)) < 0) {
+ (void) close(fd);
+ (void) free(dirp);
+ errno = ENAMETOOLONG;
+ return (NULL);
+ }
+ (void) close(fd);
+ fd = cfd;
+ }
+ (void) free(dirp);
+ return (fdopendir(fd));
+ }
+ (void) free(dirp);
+ errno = ENAMETOOLONG;
+ }
+ return (fdd);
+}
+
+/* return pointer basename of path, which may contain trailing slashes */
+static const char *
+get_unrooted(const char *path)
+{
+ const char *ptr;
+
+ if (!path || !*path)
+ return (NULL);
+
+ ptr = path + strlen(path);
+ /* find last char in path before any trailing slashes */
+ while (ptr != path && *--ptr == '/')
+ ;
+
+ if (ptr == path) /* all slashes */
+ return (ptr);
+
+ while (ptr != path)
+ if (*--ptr == '/')
+ return (++ptr);
+
+ return (ptr);
+}
+
+/*
* close the oldest directory. It saves the seek offset.
* return value is 0 unless it was unable to close any descriptor
*/