version 1.8, 2015/02/07 07:53:01 |
version 1.12, 2016/10/18 23:58:12 |
Line 59 static void fts_load(FTS *, FTSENT *); |
|
Line 59 static void fts_load(FTS *, FTSENT *); |
|
static size_t fts_maxarglen(char * const *); |
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 FTSENT *fts_sort(FTS *, FTSENT *, int); |
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 |
#ifndef O_DIRECTORY |
Line 69 static int fts_safe_changedir(FTS *, FTSENT *, int, c |
|
Line 69 static int fts_safe_changedir(FTS *, FTSENT *, int, c |
|
#ifndef O_CLOEXEC |
#ifndef O_CLOEXEC |
#define O_CLOEXEC 0 |
#define O_CLOEXEC 0 |
#endif |
#endif |
|
#ifndef PATH_MAX |
|
#define PATH_MAX 4096 |
|
#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, |
|
int (*compar)(const FTSENT **, const FTSENT **)) |
{ |
{ |
FTS *sp; |
FTS *sp; |
FTSENT *p, *root; |
FTSENT *p, *root; |
int nitems; |
int nitems; |
FTSENT *parent, *tmp; |
FTSENT *parent, *tmp; |
size_t len; |
|
|
|
/* Options check. */ |
/* Options check. */ |
if (options & ~FTS_OPTIONMASK) { |
if (options & ~FTS_OPTIONMASK) { |
Line 91 fts_open(char * const *argv, int options, void *dummy) |
|
Line 92 fts_open(char * const *argv, int options, void *dummy) |
|
return (NULL); |
return (NULL); |
} |
} |
|
|
|
/* At least one path must be specified. */ |
|
if (*argv == NULL) { |
|
errno = EINVAL; |
|
return (NULL); |
|
} |
|
|
/* Allocate/initialize the stream */ |
/* Allocate/initialize the stream */ |
if ((sp = calloc(1, sizeof(FTS))) == NULL) |
if ((sp = calloc(1, sizeof(FTS))) == NULL) |
return (NULL); |
return (NULL); |
|
sp->fts_compar = compar; |
sp->fts_options = options; |
sp->fts_options = options; |
|
|
/* |
/* |
Line 110 fts_open(char * const *argv, int options, void *dummy) |
|
Line 118 fts_open(char * const *argv, int options, void *dummy) |
|
|
|
/* Allocate/initialize root(s). */ |
/* Allocate/initialize root(s). */ |
for (root = NULL, nitems = 0; *argv; ++argv, ++nitems) { |
for (root = NULL, nitems = 0; *argv; ++argv, ++nitems) { |
/* Don't allow zero-length paths. */ |
if ((p = fts_alloc(sp, *argv, strlen(*argv))) == NULL) |
if ((len = strlen(*argv)) == 0) { |
|
errno = ENOENT; |
|
goto mem3; |
goto mem3; |
} |
|
|
|
if ((p = fts_alloc(sp, *argv, len)) == NULL) |
|
goto mem3; |
|
p->fts_level = FTS_ROOTLEVEL; |
p->fts_level = FTS_ROOTLEVEL; |
p->fts_parent = parent; |
p->fts_parent = parent; |
p->fts_accpath = p->fts_name; |
p->fts_accpath = p->fts_name; |
Line 127 fts_open(char * const *argv, int options, void *dummy) |
|
Line 129 fts_open(char * const *argv, int options, void *dummy) |
|
if (p->fts_info == FTS_DOT) |
if (p->fts_info == FTS_DOT) |
p->fts_info = FTS_D; |
p->fts_info = FTS_D; |
|
|
p->fts_link = NULL; |
/* |
if (root == NULL) |
* If comparison routine supplied, traverse in sorted |
tmp = root = p; |
* order; otherwise traverse in the order specified. |
else { |
*/ |
tmp->fts_link = p; |
if (compar) { |
tmp = p; |
p->fts_link = root; |
|
root = p; |
|
} else { |
|
p->fts_link = NULL; |
|
if (root == NULL) |
|
tmp = root = p; |
|
else { |
|
tmp->fts_link = p; |
|
tmp = p; |
|
} |
} |
} |
} |
} |
|
if (compar && nitems > 1) |
|
root = fts_sort(sp, root, nitems); |
|
|
/* |
/* |
* Allocate a dummy pointer and make fts_read think that we've just |
* Allocate a dummy pointer and make fts_read think that we've just |
Line 146 fts_open(char * const *argv, int options, void *dummy) |
|
Line 159 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 | O_CLOEXEC)) < 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 213 fts_close(FTS *sp) |
|
Line 214 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_array); |
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 274 fts_read(FTS *sp) |
|
Line 264 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 352 name: t = sp->fts_path + NAPPEND(p->fts_parent); |
|
Line 324 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 379 name: t = sp->fts_path + NAPPEND(p->fts_parent); |
|
Line 334 name: t = sp->fts_path + NAPPEND(p->fts_parent); |
|
* semantics to fts using fts_set. An error return is allowed for similar |
* semantics to fts using fts_set. An error return is allowed for similar |
* reasons. |
* reasons. |
*/ |
*/ |
/* ARGSUSED */ |
|
int |
int |
fts_set(FTS *sp, FTSENT *p, int instr) |
fts_set(FTS *sp, FTSENT *p, int instr) |
{ |
{ |
Line 414 fts_build(FTS *sp) |
|
Line 368 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, doadjust; |
int nitems, level, doadjust; |
int saved_errno; |
int saved_errno; |
char *cp; |
char *cp; |
|
|
Line 432 fts_build(FTS *sp) |
|
Line 386 fts_build(FTS *sp) |
|
} |
} |
|
|
/* |
/* |
* 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)) { |
|
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 396 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 506 fts_build(FTS *sp) |
|
Line 432 fts_build(FTS *sp) |
|
* structures already allocated. |
* structures already allocated. |
*/ |
*/ |
mem1: saved_errno = errno; |
mem1: saved_errno = errno; |
if (p) |
free(p); |
free(p); |
|
fts_lfree(head); |
fts_lfree(head); |
(void)closedir(dirp); |
(void)closedir(dirp); |
cur->fts_info = FTS_ERR; |
cur->fts_info = FTS_ERR; |
Line 518 mem1: saved_errno = errno; |
|
Line 443 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 466 mem1: saved_errno = errno; |
|
return (NULL); |
return (NULL); |
} |
} |
|
|
if (cderrno) { |
/* Build a file name for fts_stat to stat. */ |
p->fts_info = FTS_NS; |
p->fts_accpath = p->fts_path; |
p->fts_errno = cderrno; |
memmove(cp, p->fts_name, p->fts_namelen + 1); |
p->fts_accpath = cur->fts_accpath; |
/* Stat it. */ |
} else { |
p->fts_info = fts_stat(sp, p); |
/* 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); |
|
} |
|
|
|
/* 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; |
Line 581 mem1: saved_errno = errno; |
|
Line 496 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; |
return (NULL); |
return (NULL); |
} |
} |
|
|
|
/* Sort the entries. */ |
|
if (sp->fts_compar && nitems > 1) |
|
head = fts_sort(sp, head, nitems); |
return (head); |
return (head); |
} |
} |
|
|
Line 664 fts_stat(FTS *sp, FTSENT *p) |
|
Line 566 fts_stat(FTS *sp, FTSENT *p) |
|
} |
} |
|
|
static FTSENT * |
static FTSENT * |
|
fts_sort(FTS *sp, FTSENT *head, int nitems) |
|
{ |
|
FTSENT **ap, *p; |
|
|
|
/* |
|
* Construct an array of pointers to the structures and call qsort(3). |
|
* Reassemble the array in the order returned by qsort. If unable to |
|
* sort for memory reasons, return the directory entries in their |
|
* current order. Allocate enough space for the current needs plus |
|
* 40 so don't realloc one entry at a time. |
|
*/ |
|
if (nitems > sp->fts_nitems) { |
|
struct _ftsent **a; |
|
|
|
sp->fts_nitems = nitems + 40; |
|
if ((a = reallocarray(sp->fts_array, |
|
sp->fts_nitems, sizeof(FTSENT *))) == NULL) { |
|
free(sp->fts_array); |
|
sp->fts_array = NULL; |
|
sp->fts_nitems = 0; |
|
return (head); |
|
} |
|
sp->fts_array = a; |
|
} |
|
for (ap = sp->fts_array, p = head; p; p = p->fts_link) |
|
*ap++ = p; |
|
qsort(sp->fts_array, nitems, sizeof(FTSENT *), sp->fts_compar); |
|
for (head = *(ap = sp->fts_array); --nitems; ++ap) |
|
ap[0]->fts_link = ap[1]; |
|
ap[0]->fts_link = NULL; |
|
return (head); |
|
} |
|
|
|
static FTSENT * |
fts_alloc(FTS *sp, const char *name, size_t namelen) |
fts_alloc(FTS *sp, const char *name, size_t namelen) |
{ |
{ |
FTSENT *p; |
FTSENT *p; |
Line 714 fts_palloc(FTS *sp, size_t more) |
|
Line 650 fts_palloc(FTS *sp, size_t more) |
|
*/ |
*/ |
more += 256; |
more += 256; |
if (sp->fts_pathlen + more < sp->fts_pathlen) { |
if (sp->fts_pathlen + more < sp->fts_pathlen) { |
if (sp->fts_path) |
free(sp->fts_path); |
free(sp->fts_path); |
|
sp->fts_path = NULL; |
sp->fts_path = NULL; |
errno = ENAMETOOLONG; |
errno = ENAMETOOLONG; |
return (1); |
return (1); |
Line 723 fts_palloc(FTS *sp, size_t more) |
|
Line 658 fts_palloc(FTS *sp, size_t more) |
|
sp->fts_pathlen += more; |
sp->fts_pathlen += more; |
p = realloc(sp->fts_path, sp->fts_pathlen); |
p = realloc(sp->fts_path, sp->fts_pathlen); |
if (p == NULL) { |
if (p == NULL) { |
if (sp->fts_path) |
free(sp->fts_path); |
free(sp->fts_path); |
|
sp->fts_path = NULL; |
sp->fts_path = NULL; |
return (1); |
return (1); |
} |
} |
Line 769 fts_maxarglen(char * const *argv) |
|
Line 703 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|O_DIRECTORY|O_CLOEXEC)) < 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 |