version 1.2, 2014/08/11 03:19:39 |
version 1.9, 2015/03/18 19:29:48 |
|
|
#include "config.h" |
#include "config.h" |
|
|
#ifdef HAVE_FTS |
#if HAVE_FTS |
|
|
int dummy; |
int dummy; |
|
|
#else |
#else |
|
|
|
/* $Id$ */ |
/* $OpenBSD$ */ |
/* $OpenBSD$ */ |
|
|
/*- |
/*- |
|
|
* SUCH DAMAGE. |
* SUCH DAMAGE. |
*/ |
*/ |
|
|
#include <sys/param.h> |
|
#include <sys/stat.h> |
#include <sys/stat.h> |
#include <sys/types.h> |
#include <sys/types.h> |
|
|
|
|
#include <unistd.h> |
#include <unistd.h> |
#include "compat_fts.h" |
#include "compat_fts.h" |
|
|
|
#define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b)) |
|
|
static FTSENT *fts_alloc(FTS *, const char *, size_t); |
static FTSENT *fts_alloc(FTS *, const char *, size_t); |
static FTSENT *fts_build(FTS *); |
static FTSENT *fts_build(FTS *); |
static void fts_lfree(FTSENT *); |
static void fts_lfree(FTSENT *); |
Line 58 static size_t fts_maxarglen(char * const *); |
|
Line 60 static size_t fts_maxarglen(char * const *); |
|
static void fts_padjust(FTS *, FTSENT *); |
static void fts_padjust(FTS *, FTSENT *); |
static int fts_palloc(FTS *, size_t); |
static int fts_palloc(FTS *, size_t); |
static unsigned short fts_stat(FTS *, FTSENT *); |
static unsigned short fts_stat(FTS *, FTSENT *); |
static int fts_safe_changedir(FTS *, FTSENT *, int, const char *); |
|
|
|
#define ISDOT(a) (a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2]))) |
#define ISDOT(a) (a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2]))) |
|
#ifndef O_DIRECTORY |
|
#define O_DIRECTORY 0 |
|
#endif |
|
#ifndef O_CLOEXEC |
|
#define O_CLOEXEC 0 |
|
#endif |
|
|
#define CLR(opt) (sp->fts_options &= ~(opt)) |
#define CLR(opt) (sp->fts_options &= ~(opt)) |
#define ISSET(opt) (sp->fts_options & (opt)) |
#define ISSET(opt) (sp->fts_options & (opt)) |
#define SET(opt) (sp->fts_options |= (opt)) |
#define SET(opt) (sp->fts_options |= (opt)) |
|
|
#define FCHDIR(sp, fd) (!ISSET(FTS_NOCHDIR) && fchdir(fd)) |
|
|
|
FTS * |
FTS * |
fts_open(char * const *argv, int options, void *dummy) |
fts_open(char * const *argv, int options, void *dummy) |
{ |
{ |
Line 92 fts_open(char * const *argv, int options, void *dummy) |
|
Line 97 fts_open(char * const *argv, int options, void *dummy) |
|
* Start out with 1K of path space, and enough, in any case, |
* Start out with 1K of path space, and enough, in any case, |
* to hold the user's paths. |
* to hold the user's paths. |
*/ |
*/ |
if (fts_palloc(sp, MAX(fts_maxarglen(argv), PATH_MAX))) |
if (fts_palloc(sp, MAXIMUM(fts_maxarglen(argv), PATH_MAX))) |
goto mem1; |
goto mem1; |
|
|
/* Allocate/initialize root's parent. */ |
/* Allocate/initialize root's parent. */ |
Line 138 fts_open(char * const *argv, int options, void *dummy) |
|
Line 143 fts_open(char * const *argv, int options, void *dummy) |
|
sp->fts_cur->fts_link = root; |
sp->fts_cur->fts_link = root; |
sp->fts_cur->fts_info = FTS_INIT; |
sp->fts_cur->fts_info = FTS_INIT; |
|
|
/* |
|
* If using chdir(2), grab a file descriptor pointing to dot to ensure |
|
* that we can get back here; this could be avoided for some paths, |
|
* but almost certainly not worth the effort. Slashes, symbolic links, |
|
* and ".." are all fairly nasty problems. Note, if we can't get the |
|
* descriptor we run anyway, just more slowly. |
|
*/ |
|
if (!ISSET(FTS_NOCHDIR) && (sp->fts_rfd = open(".", O_RDONLY, 0)) < 0) |
|
SET(FTS_NOCHDIR); |
|
|
|
if (nitems == 0) |
if (nitems == 0) |
free(parent); |
free(parent); |
|
|
|
|
fts_close(FTS *sp) |
fts_close(FTS *sp) |
{ |
{ |
FTSENT *freep, *p; |
FTSENT *freep, *p; |
int rfd, error = 0; |
|
|
|
/* |
/* |
* This still works if we haven't read anything -- the dummy structure |
* This still works if we haven't read anything -- the dummy structure |
Line 204 fts_close(FTS *sp) |
|
Line 198 fts_close(FTS *sp) |
|
free(p); |
free(p); |
} |
} |
|
|
/* Stash the original directory fd if needed. */ |
|
rfd = ISSET(FTS_NOCHDIR) ? -1 : sp->fts_rfd; |
|
|
|
/* Free up child linked list, sort array, path buffer, stream ptr.*/ |
/* Free up child linked list, sort array, path buffer, stream ptr.*/ |
if (sp->fts_child) |
if (sp->fts_child) |
fts_lfree(sp->fts_child); |
fts_lfree(sp->fts_child); |
free(sp->fts_path); |
free(sp->fts_path); |
free(sp); |
free(sp); |
|
|
/* Return to original directory, checking for error. */ |
return (0); |
if (rfd != -1) { |
|
int saved_errno; |
|
error = fchdir(rfd); |
|
saved_errno = errno; |
|
(void)close(rfd); |
|
errno = saved_errno; |
|
} |
|
|
|
return (error); |
|
} |
} |
|
|
/* |
/* |
Line 265 fts_read(FTS *sp) |
|
Line 247 fts_read(FTS *sp) |
|
} |
} |
|
|
/* |
/* |
* Cd to the subdirectory. |
|
* |
|
* If have already read and now fail to chdir, whack the list |
|
* to make the names come out right, and set the parent errno |
|
* so the application will eventually get an error condition. |
|
* Set the FTS_DONTCHDIR flag so that when we logically change |
|
* directories back to the parent we don't do a chdir. |
|
* |
|
* If haven't read do so. If the read fails, fts_build sets |
* If haven't read do so. If the read fails, fts_build sets |
* FTS_STOP or the fts_info field of the node. |
* FTS_STOP or the fts_info field of the node. |
*/ |
*/ |
if (sp->fts_child) { |
if (sp->fts_child) { |
if (fts_safe_changedir(sp, p, -1, p->fts_accpath)) { |
/* nothing */ |
p->fts_errno = errno; |
|
p->fts_flags |= FTS_DONTCHDIR; |
|
for (p = sp->fts_child; p; p = p->fts_link) |
|
p->fts_accpath = |
|
p->fts_parent->fts_accpath; |
|
} |
|
} else if ((sp->fts_child = fts_build(sp)) == NULL) { |
} else if ((sp->fts_child = fts_build(sp)) == NULL) { |
if (ISSET(FTS_STOP)) |
if (ISSET(FTS_STOP)) |
return (NULL); |
return (NULL); |
|
|
* the root of the tree), and load the paths for the next root. |
* the root of the tree), and load the paths for the next root. |
*/ |
*/ |
if (p->fts_level == FTS_ROOTLEVEL) { |
if (p->fts_level == FTS_ROOTLEVEL) { |
if (FCHDIR(sp, sp->fts_rfd)) { |
|
SET(FTS_STOP); |
|
return (NULL); |
|
} |
|
fts_load(sp, p); |
fts_load(sp, p); |
return (sp->fts_cur = p); |
return (sp->fts_cur = p); |
} |
} |
Line 343 name: t = sp->fts_path + NAPPEND(p->fts_parent); |
|
Line 307 name: t = sp->fts_path + NAPPEND(p->fts_parent); |
|
/* NUL terminate the pathname. */ |
/* NUL terminate the pathname. */ |
sp->fts_path[p->fts_pathlen] = '\0'; |
sp->fts_path[p->fts_pathlen] = '\0'; |
|
|
/* |
|
* Return to the parent directory. If at a root node or came through |
|
* a symlink, go back through the file descriptor. Otherwise, cd up |
|
* one directory. |
|
*/ |
|
if (p->fts_level == FTS_ROOTLEVEL) { |
|
if (FCHDIR(sp, sp->fts_rfd)) { |
|
SET(FTS_STOP); |
|
sp->fts_cur = p; |
|
return (NULL); |
|
} |
|
} else if (!(p->fts_flags & FTS_DONTCHDIR) && |
|
fts_safe_changedir(sp, p->fts_parent, -1, "..")) { |
|
SET(FTS_STOP); |
|
sp->fts_cur = p; |
|
return (NULL); |
|
} |
|
p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP; |
p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP; |
return (sp->fts_cur = p); |
return (sp->fts_cur = p); |
} |
} |
Line 405 fts_build(FTS *sp) |
|
Line 352 fts_build(FTS *sp) |
|
DIR *dirp; |
DIR *dirp; |
void *oldaddr; |
void *oldaddr; |
size_t dlen, len, maxlen; |
size_t dlen, len, maxlen; |
int nitems, cderrno, descend, level, nlinks, nostat, doadjust; |
int nitems, level, doadjust; |
int saved_errno; |
int saved_errno; |
char *cp; |
char *cp; |
|
|
Line 423 fts_build(FTS *sp) |
|
Line 370 fts_build(FTS *sp) |
|
} |
} |
|
|
/* |
/* |
* Nlinks is the number of possible entries of type directory in the |
|
* directory if we're cheating on stat calls, 0 if we're not doing |
|
* any stat calls at all, -1 if we're doing stats on everything. |
|
*/ |
|
nlinks = -1; |
|
nostat = 0; |
|
|
|
/* |
|
* If we're going to need to stat anything or we want to descend |
|
* and stay in the directory, chdir. If this fails we keep going, |
|
* but set a flag so we don't chdir after the post-order visit. |
|
* We won't be able to stat anything, but we can still return the |
|
* names themselves. Note, that since fts_read won't be able to |
|
* chdir into the directory, it will have to return different path |
|
* names than before, i.e. "a/b" instead of "b". Since the node |
|
* has already been visited in pre-order, have to wait until the |
|
* post-order visit to return the error. There is a special case |
|
* here, if there was nothing to stat then it's not an error to |
|
* not be able to stat. This is all fairly nasty. If a program |
|
* needed sorted entries or stat information, they had better be |
|
* checking FTS_NS on the returned nodes. |
|
*/ |
|
cderrno = 0; |
|
if (fts_safe_changedir(sp, cur, dirfd(dirp), NULL)) { |
|
if (nlinks) |
|
cur->fts_errno = errno; |
|
cur->fts_flags |= FTS_DONTCHDIR; |
|
descend = 0; |
|
cderrno = errno; |
|
(void)closedir(dirp); |
|
dirp = NULL; |
|
} else |
|
descend = 1; |
|
|
|
/* |
|
* Figure out the max file name length that can be stored in the |
* Figure out the max file name length that can be stored in the |
* current path -- the inner loop allocates more path as necessary. |
* current path -- the inner loop allocates more path as necessary. |
* We really wouldn't have to do the maxlen calculations here, we |
* We really wouldn't have to do the maxlen calculations here, we |
Line 468 fts_build(FTS *sp) |
|
Line 380 fts_build(FTS *sp) |
|
* each new name into the path. |
* each new name into the path. |
*/ |
*/ |
len = NAPPEND(cur); |
len = NAPPEND(cur); |
if (ISSET(FTS_NOCHDIR)) { |
cp = sp->fts_path + len; |
cp = sp->fts_path + len; |
*cp++ = '/'; |
*cp++ = '/'; |
|
} |
|
len++; |
len++; |
maxlen = sp->fts_pathlen - len; |
maxlen = sp->fts_pathlen - len; |
|
|
Line 489 fts_build(FTS *sp) |
|
Line 399 fts_build(FTS *sp) |
|
if (ISDOT(dp->d_name)) |
if (ISDOT(dp->d_name)) |
continue; |
continue; |
|
|
#ifdef HAVE_DIRENT_NAMLEN |
#if HAVE_DIRENT_NAMLEN |
dlen = dp->d_namlen; |
dlen = dp->d_namlen; |
#else |
#else |
dlen = strlen(dp->d_name); |
dlen = strlen(dp->d_name); |
Line 518 mem1: saved_errno = errno; |
|
Line 428 mem1: saved_errno = errno; |
|
/* Did realloc() change the pointer? */ |
/* Did realloc() change the pointer? */ |
if (oldaddr != sp->fts_path) { |
if (oldaddr != sp->fts_path) { |
doadjust = 1; |
doadjust = 1; |
if (ISSET(FTS_NOCHDIR)) |
cp = sp->fts_path + len; |
cp = sp->fts_path + len; |
|
} |
} |
maxlen = sp->fts_pathlen - len; |
maxlen = sp->fts_pathlen - len; |
} |
} |
Line 542 mem1: saved_errno = errno; |
|
Line 451 mem1: saved_errno = errno; |
|
return (NULL); |
return (NULL); |
} |
} |
|
|
if (cderrno) { |
/* Build a file name for fts_stat to stat. */ |
if (nlinks) { |
p->fts_accpath = p->fts_path; |
p->fts_info = FTS_NS; |
memmove(cp, p->fts_name, p->fts_namelen + 1); |
p->fts_errno = cderrno; |
/* Stat it. */ |
} else |
p->fts_info = fts_stat(sp, p); |
p->fts_info = FTS_NSOK; |
|
p->fts_accpath = cur->fts_accpath; |
|
} else if (nlinks == 0 |
|
#ifdef DT_DIR |
|
|| (nostat && |
|
dp->d_type != DT_DIR && dp->d_type != DT_UNKNOWN) |
|
#endif |
|
) { |
|
p->fts_accpath = |
|
ISSET(FTS_NOCHDIR) ? p->fts_path : p->fts_name; |
|
p->fts_info = FTS_NSOK; |
|
} else { |
|
/* Build a file name for fts_stat to stat. */ |
|
if (ISSET(FTS_NOCHDIR)) { |
|
p->fts_accpath = p->fts_path; |
|
memmove(cp, p->fts_name, p->fts_namelen + 1); |
|
} else |
|
p->fts_accpath = p->fts_name; |
|
/* Stat it. */ |
|
p->fts_info = fts_stat(sp, p); |
|
|
|
/* Decrement link count if applicable. */ |
|
if (nlinks > 0 && (p->fts_info == FTS_D || |
|
p->fts_info == FTS_DC || p->fts_info == FTS_DOT)) |
|
--nlinks; |
|
} |
|
|
|
/* We walk in directory order so "ls -f" doesn't get upset. */ |
/* We walk in directory order so "ls -f" doesn't get upset. */ |
p->fts_link = NULL; |
p->fts_link = NULL; |
if (head == NULL) |
if (head == NULL) |
Line 598 mem1: saved_errno = errno; |
|
Line 481 mem1: saved_errno = errno; |
|
* If not changing directories, reset the path back to original |
* If not changing directories, reset the path back to original |
* state. |
* state. |
*/ |
*/ |
if (ISSET(FTS_NOCHDIR)) { |
if (len == sp->fts_pathlen || nitems == 0) |
if (len == sp->fts_pathlen || nitems == 0) |
--cp; |
--cp; |
*cp = '\0'; |
*cp = '\0'; |
|
} |
|
|
|
/* |
|
* If descended after called from fts_children or after called from |
|
* fts_read and nothing found, get back. At the root level we use |
|
* the saved fd; if one of fts_open()'s arguments is a relative path |
|
* to an empty directory, we wind up here with no other way back. If |
|
* can't get back, we're done. |
|
*/ |
|
if (descend && !nitems && |
|
(cur->fts_level == FTS_ROOTLEVEL ? FCHDIR(sp, sp->fts_rfd) : |
|
fts_safe_changedir(sp, cur->fts_parent, -1, ".."))) { |
|
cur->fts_info = FTS_ERR; |
|
SET(FTS_STOP); |
|
return (NULL); |
|
} |
|
|
|
/* If didn't find anything, return NULL. */ |
/* If didn't find anything, return NULL. */ |
if (!nitems) { |
if (!nitems) { |
cur->fts_info = FTS_DP; |
cur->fts_info = FTS_DP; |
Line 786 fts_maxarglen(char * const *argv) |
|
Line 652 fts_maxarglen(char * const *argv) |
|
if ((len = strlen(*argv)) > max) |
if ((len = strlen(*argv)) > max) |
max = len; |
max = len; |
return (max + 1); |
return (max + 1); |
} |
|
|
|
/* |
|
* Change to dir specified by fd or p->fts_accpath without getting |
|
* tricked by someone changing the world out from underneath us. |
|
* Assumes p->fts_dev and p->fts_ino are filled in. |
|
*/ |
|
static int |
|
fts_safe_changedir(FTS *sp, FTSENT *p, int fd, const char *path) |
|
{ |
|
int ret, oerrno, newfd; |
|
struct stat sb; |
|
|
|
newfd = fd; |
|
if (ISSET(FTS_NOCHDIR)) |
|
return (0); |
|
if (fd < 0 && (newfd = open(path, O_RDONLY, 0)) < 0) |
|
return (-1); |
|
if (fstat(newfd, &sb)) { |
|
ret = -1; |
|
goto bail; |
|
} |
|
if (p->fts_dev != sb.st_dev || p->fts_ino != sb.st_ino) { |
|
errno = ENOENT; /* disinformation */ |
|
ret = -1; |
|
goto bail; |
|
} |
|
ret = fchdir(newfd); |
|
bail: |
|
oerrno = errno; |
|
if (fd < 0) |
|
(void)close(newfd); |
|
errno = oerrno; |
|
return (ret); |
|
} |
} |
|
|
#endif |
#endif |